diff options
210 files changed, 4875 insertions, 2011 deletions
diff --git a/gcc/testsuite/go.test/test/env.go b/gcc/testsuite/go.test/test/env.go index a4b9d05d87d..3c8e4232838 100644 --- a/gcc/testsuite/go.test/test/env.go +++ b/gcc/testsuite/go.test/test/env.go @@ -4,6 +4,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// Test that the Go environment variables are present and accessible through +// package os and package runtime. + package main import ( @@ -12,18 +15,14 @@ import ( ) func main() { - ga, e0 := os.Getenverror("GOARCH") - if e0 != nil { - print("$GOARCH: ", e0.Error(), "\n") - os.Exit(1) - } + ga := os.Getenv("GOARCH") if ga != runtime.GOARCH { print("$GOARCH=", ga, "!= runtime.GOARCH=", runtime.GOARCH, "\n") os.Exit(1) } - xxx, e1 := os.Getenverror("DOES_NOT_EXIST") - if e1 != os.ENOENV { - print("$DOES_NOT_EXIST=", xxx, "; err = ", e1.Error(), "\n") + xxx := os.Getenv("DOES_NOT_EXIST") + if xxx != "" { + print("$DOES_NOT_EXIST=", xxx, "\n") os.Exit(1) } } diff --git a/gcc/testsuite/go.test/test/fixedbugs/bug262.go b/gcc/testsuite/go.test/test/fixedbugs/bug262.go index f5f2c355322..ebca7905f9a 100644 --- a/gcc/testsuite/go.test/test/fixedbugs/bug262.go +++ b/gcc/testsuite/go.test/test/fixedbugs/bug262.go @@ -7,7 +7,7 @@ package main import ( - "os" + "errors" "strconv" ) @@ -44,7 +44,7 @@ func main() { } mm := make(map[string]error) trace = "" - mm["abc"] = os.EINVAL + mm["abc"] = errors.New("invalid") *i(), mm[f()] = strconv.Atoi(h()) if mm["abc"] != nil || trace != "ifh" { println("BUG1", mm["abc"], trace) diff --git a/libgo/MERGE b/libgo/MERGE index a760ae9c8de..9605a8a9040 100644 --- a/libgo/MERGE +++ b/libgo/MERGE @@ -1,4 +1,4 @@ -43cf9b39b647 +96bd78e7d35e 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 1167a3f7ed0..eb764df0eab 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -504,7 +504,7 @@ runtime1.c: $(srcdir)/runtime/runtime1.goc goc2c mv -f $@.tmp $@ sema.c: $(srcdir)/runtime/sema.goc goc2c - ./goc2c --gcc --go-prefix libgo_runtime $< > $@.tmp + ./goc2c --gcc --go-prefix libgo_sync $< > $@.tmp mv -f $@.tmp $@ sigqueue.c: $(srcdir)/runtime/sigqueue.goc goc2c @@ -847,6 +847,7 @@ go_sync_files = \ go/sync/cond.go \ go/sync/mutex.go \ go/sync/once.go \ + go/sync/runtime.go \ go/sync/rwmutex.go \ go/sync/waitgroup.go @@ -878,6 +879,7 @@ go_time_files = \ go/time/tick.go \ go/time/time.go \ go/time/zoneinfo.go \ + go/time/zoneinfo_read.go \ go/time/zoneinfo_unix.go go_unicode_files = \ @@ -1091,6 +1093,7 @@ go_exp_norm_files = \ go/exp/norm/composition.go \ go/exp/norm/forminfo.go \ go/exp/norm/input.go \ + go/exp/norm/iter.go \ go/exp/norm/normalize.go \ go/exp/norm/readwriter.go \ go/exp/norm/tables.go \ @@ -1132,7 +1135,8 @@ go_go_doc_files = \ go/go/doc/example.go \ go/go/doc/exports.go \ go/go/doc/filter.go \ - go/go/doc/reader.go + go/go/doc/reader.go \ + go/go/doc/synopsis.go go_go_parser_files = \ go/go/parser/interface.go \ go/go/parser/parser.go @@ -1159,7 +1163,6 @@ go_hash_fnv_files = \ go_html_template_files = \ go/html/template/attr.go \ - go/html/template/clone.go \ go/html/template/content.go \ go/html/template/context.go \ go/html/template/css.go \ diff --git a/libgo/Makefile.in b/libgo/Makefile.in index b1d1d4c561e..4604e560c9b 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -1157,6 +1157,7 @@ go_sync_files = \ go/sync/cond.go \ go/sync/mutex.go \ go/sync/once.go \ + go/sync/runtime.go \ go/sync/rwmutex.go \ go/sync/waitgroup.go @@ -1182,6 +1183,7 @@ go_time_files = \ go/time/tick.go \ go/time/time.go \ go/time/zoneinfo.go \ + go/time/zoneinfo_read.go \ go/time/zoneinfo_unix.go go_unicode_files = \ @@ -1427,6 +1429,7 @@ go_exp_norm_files = \ go/exp/norm/composition.go \ go/exp/norm/forminfo.go \ go/exp/norm/input.go \ + go/exp/norm/iter.go \ go/exp/norm/normalize.go \ go/exp/norm/readwriter.go \ go/exp/norm/tables.go \ @@ -1474,7 +1477,8 @@ go_go_doc_files = \ go/go/doc/example.go \ go/go/doc/exports.go \ go/go/doc/filter.go \ - go/go/doc/reader.go + go/go/doc/reader.go \ + go/go/doc/synopsis.go go_go_parser_files = \ go/go/parser/interface.go \ @@ -1508,7 +1512,6 @@ go_hash_fnv_files = \ go_html_template_files = \ go/html/template/attr.go \ - go/html/template/clone.go \ go/html/template/content.go \ go/html/template/context.go \ go/html/template/css.go \ @@ -4318,7 +4321,7 @@ runtime1.c: $(srcdir)/runtime/runtime1.goc goc2c mv -f $@.tmp $@ sema.c: $(srcdir)/runtime/sema.goc goc2c - ./goc2c --gcc --go-prefix libgo_runtime $< > $@.tmp + ./goc2c --gcc --go-prefix libgo_sync $< > $@.tmp mv -f $@.tmp $@ sigqueue.c: $(srcdir)/runtime/sigqueue.goc goc2c diff --git a/libgo/go/bufio/bufio.go b/libgo/go/bufio/bufio.go index 156dddfcf07..6f3b1eec971 100644 --- a/libgo/go/bufio/bufio.go +++ b/libgo/go/bufio/bufio.go @@ -106,9 +106,12 @@ func (b *Reader) Peek(n int) ([]byte, error) { if m > n { m = n } - err := b.readErr() - if m < n && err == nil { - err = ErrBufferFull + var err error + if m < n { + err = b.readErr() + if err == nil { + err = ErrBufferFull + } } return b.buf[b.r : b.r+m], err } diff --git a/libgo/go/bufio/bufio_test.go b/libgo/go/bufio/bufio_test.go index 9aec61ec426..a43cbd23a64 100644 --- a/libgo/go/bufio/bufio_test.go +++ b/libgo/go/bufio/bufio_test.go @@ -539,6 +539,27 @@ func TestPeek(t *testing.T) { if _, err := buf.Peek(1); err != io.EOF { t.Fatalf("want EOF got %v", err) } + + // Test for issue 3022, not exposing a reader's error on a successful Peek. + buf = NewReaderSize(dataAndEOFReader("abcd"), 32) + if s, err := buf.Peek(2); string(s) != "ab" || err != nil { + t.Errorf(`Peek(2) on "abcd", EOF = %q, %v; want "ab", nil`, string(s), err) + } + if s, err := buf.Peek(4); string(s) != "abcd" || err != nil { + t.Errorf(`Peek(4) on "abcd", EOF = %q, %v; want "abcd", nil`, string(s), err) + } + if n, err := buf.Read(p[0:5]); string(p[0:n]) != "abcd" || err != nil { + t.Fatalf("Read after peek = %q, %v; want abcd, EOF", p[0:n], err) + } + if n, err := buf.Read(p[0:1]); string(p[0:n]) != "" || err != io.EOF { + t.Fatalf(`second Read after peek = %q, %v; want "", EOF`, p[0:n], err) + } +} + +type dataAndEOFReader string + +func (r dataAndEOFReader) Read(p []byte) (int, error) { + return copy(p, r), io.EOF } func TestPeekThenUnreadRune(t *testing.T) { diff --git a/libgo/go/bytes/bytes.go b/libgo/go/bytes/bytes.go index e94a0ec5c4f..7d1426fb417 100644 --- a/libgo/go/bytes/bytes.go +++ b/libgo/go/bytes/bytes.go @@ -13,6 +13,7 @@ import ( // Compare returns an integer comparing the two byte arrays lexicographically. // The result will be 0 if a==b, -1 if a < b, and +1 if a > b +// A nil argument is equivalent to an empty slice. func Compare(a, b []byte) int { m := len(a) if m > len(b) { @@ -37,6 +38,7 @@ func Compare(a, b []byte) int { } // Equal returns a boolean reporting whether a == b. +// A nil argument is equivalent to an empty slice. func Equal(a, b []byte) bool func equalPortable(a, b []byte) bool { diff --git a/libgo/go/bytes/bytes_test.go b/libgo/go/bytes/bytes_test.go index 2a1d41b910e..000f235176d 100644 --- a/libgo/go/bytes/bytes_test.go +++ b/libgo/go/bytes/bytes_test.go @@ -46,32 +46,39 @@ type BinOpTest struct { i int } -var comparetests = []BinOpTest{ - {"", "", 0}, - {"a", "", 1}, - {"", "a", -1}, - {"abc", "abc", 0}, - {"ab", "abc", -1}, - {"abc", "ab", 1}, - {"x", "ab", 1}, - {"ab", "x", -1}, - {"x", "a", 1}, - {"b", "x", -1}, +var compareTests = []struct { + a, b []byte + i int +}{ + {[]byte(""), []byte(""), 0}, + {[]byte("a"), []byte(""), 1}, + {[]byte(""), []byte("a"), -1}, + {[]byte("abc"), []byte("abc"), 0}, + {[]byte("ab"), []byte("abc"), -1}, + {[]byte("abc"), []byte("ab"), 1}, + {[]byte("x"), []byte("ab"), 1}, + {[]byte("ab"), []byte("x"), -1}, + {[]byte("x"), []byte("a"), 1}, + {[]byte("b"), []byte("x"), -1}, + // nil tests + {nil, nil, 0}, + {[]byte(""), nil, 0}, + {nil, []byte(""), 0}, + {[]byte("a"), nil, 1}, + {nil, []byte("a"), -1}, } func TestCompare(t *testing.T) { - for _, tt := range comparetests { - a := []byte(tt.a) - b := []byte(tt.b) - cmp := Compare(a, b) + for _, tt := range compareTests { + cmp := Compare(tt.a, tt.b) if cmp != tt.i { t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp) } - eql := Equal(a, b) + eql := Equal(tt.a, tt.b) if eql != (tt.i == 0) { t.Errorf(`Equal(%q, %q) = %v`, tt.a, tt.b, eql) } - eql = EqualPortable(a, b) + eql = EqualPortable(tt.a, tt.b) if eql != (tt.i == 0) { t.Errorf(`EqualPortable(%q, %q) = %v`, tt.a, tt.b, eql) } diff --git a/libgo/go/bytes/example_test.go b/libgo/go/bytes/example_test.go index 0234a012a4e..6fe8cd5a90c 100644 --- a/libgo/go/bytes/example_test.go +++ b/libgo/go/bytes/example_test.go @@ -11,18 +11,18 @@ import ( "os" ) -// Hello world! func ExampleBuffer() { var b Buffer // A Buffer needs no initialization. b.Write([]byte("Hello ")) b.Write([]byte("world!")) b.WriteTo(os.Stdout) + // Output: Hello world! } -// Gophers rule! func ExampleBuffer_reader() { // A Buffer can turn a string or a []byte into an io.Reader. buf := NewBufferString("R29waGVycyBydWxlIQ==") dec := base64.NewDecoder(base64.StdEncoding, buf) io.Copy(os.Stdout, dec) + // Output: Gophers rule! } diff --git a/libgo/go/compress/flate/deflate_test.go b/libgo/go/compress/flate/deflate_test.go index a76e2d930f6..543c5950586 100644 --- a/libgo/go/compress/flate/deflate_test.go +++ b/libgo/go/compress/flate/deflate_test.go @@ -306,6 +306,9 @@ func TestDeflateInflateString(t *testing.T) { t.Error(err) } testToFromWithLimit(t, gold, test.label, test.limit) + if testing.Short() { + break + } } } @@ -363,6 +366,10 @@ func TestWriterDict(t *testing.T) { // See http://code.google.com/p/go/issues/detail?id=2508 func TestRegression2508(t *testing.T) { + if testing.Short() { + t.Logf("test disabled with -short") + return + } w, err := NewWriter(ioutil.Discard, 1) if err != nil { t.Fatalf("NewWriter: %v", err) diff --git a/libgo/go/container/heap/example_test.go b/libgo/go/container/heap/example_test.go index c3b8d94cb2a..2050bc83591 100644 --- a/libgo/go/container/heap/example_test.go +++ b/libgo/go/container/heap/example_test.go @@ -57,11 +57,26 @@ func (pq *PriorityQueue) Pop() interface{} { return item } -// 99:seven 88:five 77:zero 66:nine 55:three 44:two 33:six 22:one 11:four 00:eight -func ExampleInterface() { - // The full code of this example, including the methods that implement - // heap.Interface, is in the file src/pkg/container/heap/example_test.go. +// update is not used by the example but shows how to take the top item from +// the queue, update its priority and value, and put it back. +func (pq *PriorityQueue) update(value string, priority int) { + item := heap.Pop(pq).(*Item) + item.value = value + item.priority = priority + heap.Push(pq, item) +} +// changePriority is not used by the example but shows how to change the +// priority of an arbitrary item. +func (pq *PriorityQueue) changePriority(item *Item, priority int) { + heap.Remove(pq, item.index) + item.priority = priority + heap.Push(pq, item) +} + +// This example pushes 10 items into a PriorityQueue and takes them out in +// order of priority. +func Example() { const nItem = 10 // Random priorities for the items (a permutation of 0..9, times 11)). priorities := [nItem]int{ @@ -85,21 +100,6 @@ func ExampleInterface() { item := heap.Pop(&pq).(*Item) fmt.Printf("%.2d:%s ", item.priority, item.value) } -} - -// update is not used by the example but shows how to take the top item from the queue, -// update its priority and value, and put it back. -func (pq *PriorityQueue) update(value string, priority int) { - item := heap.Pop(pq).(*Item) - item.value = value - item.priority = priority - heap.Push(pq, item) -} - -// changePriority is not used by the example but shows how to change the priority of an arbitrary -// item. -func (pq *PriorityQueue) changePriority(item *Item, priority int) { - heap.Remove(pq, item.index) - item.priority = priority - heap.Push(pq, item) + // Output: + // 99:seven 88:five 77:zero 66:nine 55:three 44:two 33:six 22:one 11:four 00:eight } diff --git a/libgo/go/crypto/dsa/dsa.go b/libgo/go/crypto/dsa/dsa.go index f7c47831790..05766a2f136 100644 --- a/libgo/go/crypto/dsa/dsa.go +++ b/libgo/go/crypto/dsa/dsa.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 dsa implements the Digital Signature Algorithm, as defined in FIPS 186-3 +// Package dsa implements the Digital Signature Algorithm, as defined in FIPS 186-3. package dsa import ( diff --git a/libgo/go/crypto/md5/md5_test.go b/libgo/go/crypto/md5/md5_test.go index b15e4668c32..aae875464f9 100644 --- a/libgo/go/crypto/md5/md5_test.go +++ b/libgo/go/crypto/md5/md5_test.go @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package md5 +package md5_test import ( + "crypto/md5" "fmt" "io" "testing" @@ -52,7 +53,7 @@ var golden = []md5Test{ func TestGolden(t *testing.T) { for i := 0; i < len(golden); i++ { g := golden[i] - c := New() + c := md5.New() for j := 0; j < 3; j++ { if j < 2 { io.WriteString(c, g.in) @@ -69,3 +70,11 @@ func TestGolden(t *testing.T) { } } } + +func ExampleNew() { + h := md5.New() + io.WriteString(h, "The fog is getting thicker!") + io.WriteString(h, "And Leon's getting laaarger!") + fmt.Printf("%x", h.Sum(nil)) + // Output: e2c569be17396eca2a2e3c11578123ed +} diff --git a/libgo/go/crypto/sha1/sha1_test.go b/libgo/go/crypto/sha1/sha1_test.go index c23df6c41e9..2dc14ac9868 100644 --- a/libgo/go/crypto/sha1/sha1_test.go +++ b/libgo/go/crypto/sha1/sha1_test.go @@ -4,9 +4,10 @@ // SHA1 hash algorithm. See RFC 3174. -package sha1 +package sha1_test import ( + "crypto/sha1" "fmt" "io" "testing" @@ -54,7 +55,7 @@ var golden = []sha1Test{ func TestGolden(t *testing.T) { for i := 0; i < len(golden); i++ { g := golden[i] - c := New() + c := sha1.New() for j := 0; j < 3; j++ { if j < 2 { io.WriteString(c, g.in) @@ -71,3 +72,10 @@ func TestGolden(t *testing.T) { } } } + +func ExampleNew() { + h := sha1.New() + io.WriteString(h, "His money is twice tainted: 'taint yours and 'taint mine.") + fmt.Printf("% x", h.Sum(nil)) + // Output: 59 7f 6a 54 00 10 f9 4c 15 d7 18 06 a9 9a 2c 87 10 e7 47 bd +} diff --git a/libgo/go/crypto/tls/handshake_client.go b/libgo/go/crypto/tls/handshake_client.go index 687e5ef11b4..0d7b806ff5b 100644 --- a/libgo/go/crypto/tls/handshake_client.go +++ b/libgo/go/crypto/tls/handshake_client.go @@ -273,7 +273,7 @@ func (c *Conn) clientHandshake() error { masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) - clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */ ) + clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */) clientHash := suite.mac(c.vers, clientMAC) c.out.prepareCipherSpec(c.vers, clientCipher, clientHash) c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) @@ -294,7 +294,7 @@ func (c *Conn) clientHandshake() error { finishedHash.Write(finished.marshal()) c.writeRecord(recordTypeHandshake, finished.marshal()) - serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */ ) + serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */) serverHash := suite.mac(c.vers, serverMAC) c.in.prepareCipherSpec(c.vers, serverCipher, serverHash) c.readRecord(recordTypeChangeCipherSpec) diff --git a/libgo/go/crypto/tls/handshake_server.go b/libgo/go/crypto/tls/handshake_server.go index fb53767f3e0..23ec5587235 100644 --- a/libgo/go/crypto/tls/handshake_server.go +++ b/libgo/go/crypto/tls/handshake_server.go @@ -295,7 +295,7 @@ FindCipherSuite: masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := keysFromPreMasterSecret(c.vers, preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) - clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */ ) + clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */) clientHash := suite.mac(c.vers, clientMAC) c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) c.readRecord(recordTypeChangeCipherSpec) @@ -333,7 +333,7 @@ FindCipherSuite: finishedHash.Write(clientFinished.marshal()) - serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */ ) + serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */) serverHash := suite.mac(c.vers, serverMAC) c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) diff --git a/libgo/go/database/sql/convert.go b/libgo/go/database/sql/convert.go index 4afa2bef753..bfcb03ccf8d 100644 --- a/libgo/go/database/sql/convert.go +++ b/libgo/go/database/sql/convert.go @@ -17,8 +17,8 @@ import ( // subsetTypeArgs takes a slice of arguments from callers of the sql // package and converts them into a slice of the driver package's // "subset types". -func subsetTypeArgs(args []interface{}) ([]interface{}, error) { - out := make([]interface{}, len(args)) +func subsetTypeArgs(args []interface{}) ([]driver.Value, error) { + out := make([]driver.Value, len(args)) for n, arg := range args { var err error out[n], err = driver.DefaultParameterConverter.ConvertValue(arg) diff --git a/libgo/go/database/sql/driver/driver.go b/libgo/go/database/sql/driver/driver.go index b9300776050..7f986b80f2c 100644 --- a/libgo/go/database/sql/driver/driver.go +++ b/libgo/go/database/sql/driver/driver.go @@ -6,21 +6,20 @@ // drivers as used by package sql. // // Most code should use package sql. -// -// Drivers only need to be aware of a subset of Go's types. The sql package -// will convert all types into one of the following: +package driver + +import "errors" + +// A driver Value is a value that drivers must be able to handle. +// A Value is either nil or an instance of one of these types: // // int64 // float64 // bool -// nil // []byte // string [*] everywhere except from Rows.Next. // time.Time -// -package driver - -import "errors" +type Value interface{} // Driver is the interface that must be implemented by a database // driver. @@ -50,11 +49,9 @@ var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented") // first prepare a query, execute the statement, and then close the // statement. // -// All arguments are of a subset type as defined in the package docs. -// // Exec may return ErrSkip. type Execer interface { - Exec(query string, args []interface{}) (Result, error) + Exec(query string, args []Value) (Result, error) } // Conn is a connection to a database. It is not used concurrently @@ -127,18 +124,17 @@ type Stmt interface { NumInput() int // Exec executes a query that doesn't return rows, such - // as an INSERT or UPDATE. The args are all of a subset - // type as defined above. - Exec(args []interface{}) (Result, error) + // as an INSERT or UPDATE. + Exec(args []Value) (Result, error) // Exec executes a query that may return rows, such as a - // SELECT. The args of all of a subset type as defined above. - Query(args []interface{}) (Rows, error) + // SELECT. + Query(args []Value) (Rows, error) } // ColumnConverter may be optionally implemented by Stmt if the // the statement is aware of its own columns' types and can -// convert from any type to a driver subset type. +// convert from any type to a driver Value. type ColumnConverter interface { // ColumnConverter returns a ValueConverter for the provided // column index. If the type of a specific column isn't known @@ -162,12 +158,12 @@ type Rows interface { // the provided slice. The provided slice will be the same // size as the Columns() are wide. // - // The dest slice may be populated with only with values - // of subset types defined above, but excluding string. + // The dest slice may be populated only with + // a driver Value type, but excluding string. // All string values must be converted to []byte. // // Next should return io.EOF when there are no more rows. - Next(dest []interface{}) error + Next(dest []Value) error } // Tx is a transaction. @@ -190,18 +186,19 @@ func (v RowsAffected) RowsAffected() (int64, error) { return int64(v), nil } -// DDLSuccess is a pre-defined Result for drivers to return when a DDL -// command succeeds. -var DDLSuccess ddlSuccess +// ResultNoRows is a pre-defined Result for drivers to return when a DDL +// command (such as a CREATE TABLE) succeeds. It returns an error for both +// LastInsertId and RowsAffected. +var ResultNoRows noRows -type ddlSuccess struct{} +type noRows struct{} -var _ Result = ddlSuccess{} +var _ Result = noRows{} -func (ddlSuccess) LastInsertId() (int64, error) { +func (noRows) LastInsertId() (int64, error) { return 0, errors.New("no LastInsertId available after DDL statement") } -func (ddlSuccess) RowsAffected() (int64, error) { +func (noRows) RowsAffected() (int64, error) { return 0, errors.New("no RowsAffected available after DDL statement") } diff --git a/libgo/go/database/sql/driver/types.go b/libgo/go/database/sql/driver/types.go index ce3c943ead2..3305354dfd0 100644 --- a/libgo/go/database/sql/driver/types.go +++ b/libgo/go/database/sql/driver/types.go @@ -17,28 +17,28 @@ import ( // driver package to provide consistent implementations of conversions // between drivers. The ValueConverters have several uses: // -// * converting from the subset types as provided by the sql package +// * converting from the Value types as provided by the sql package // into a database table's specific column type and making sure it // fits, such as making sure a particular int64 fits in a // table's uint16 column. // // * converting a value as given from the database into one of the -// subset types. +// driver Value types. // -// * by the sql package, for converting from a driver's subset type +// * by the sql package, for converting from a driver's Value type // to a user's type in a scan. type ValueConverter interface { - // ConvertValue converts a value to a restricted subset type. - ConvertValue(v interface{}) (interface{}, error) + // ConvertValue converts a value to a driver Value. + ConvertValue(v interface{}) (Value, error) } -// SubsetValuer is the interface providing the SubsetValue method. +// Valuer is the interface providing the Value method. // -// Types implementing SubsetValuer interface are able to convert -// themselves to one of the driver's allowed subset values. -type SubsetValuer interface { - // SubsetValue returns a driver parameter subset value. - SubsetValue() (interface{}, error) +// Types implementing Valuer interface are able to convert +// themselves to a driver Value. +type Valuer interface { + // Value returns a driver Value. + Value() (Value, error) } // Bool is a ValueConverter that converts input values to bools. @@ -59,7 +59,7 @@ var _ ValueConverter = boolType{} func (boolType) String() string { return "Bool" } -func (boolType) ConvertValue(src interface{}) (interface{}, error) { +func (boolType) ConvertValue(src interface{}) (Value, error) { switch s := src.(type) { case bool: return s, nil @@ -104,7 +104,7 @@ type int32Type struct{} var _ ValueConverter = int32Type{} -func (int32Type) ConvertValue(v interface{}) (interface{}, error) { +func (int32Type) ConvertValue(v interface{}) (Value, error) { rv := reflect.ValueOf(v) switch rv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: @@ -137,7 +137,7 @@ var String stringType type stringType struct{} -func (stringType) ConvertValue(v interface{}) (interface{}, error) { +func (stringType) ConvertValue(v interface{}) (Value, error) { switch v.(type) { case string, []byte: return v, nil @@ -151,7 +151,7 @@ type Null struct { Converter ValueConverter } -func (n Null) ConvertValue(v interface{}) (interface{}, error) { +func (n Null) ConvertValue(v interface{}) (Value, error) { if v == nil { return nil, nil } @@ -164,28 +164,17 @@ type NotNull struct { Converter ValueConverter } -func (n NotNull) ConvertValue(v interface{}) (interface{}, error) { +func (n NotNull) ConvertValue(v interface{}) (Value, error) { if v == nil { return nil, fmt.Errorf("nil value not allowed") } return n.Converter.ConvertValue(v) } -// IsParameterSubsetType reports whether v is of a valid type for a -// parameter. These types are: -// -// int64 -// float64 -// bool -// nil -// []byte -// time.Time -// string -// -// This is the same list as IsScanSubsetType, with the addition of -// string. -func IsParameterSubsetType(v interface{}) bool { - if IsScanSubsetType(v) { +// IsValue reports whether v is a valid Value parameter type. +// Unlike IsScanValue, IsValue permits the string type. +func IsValue(v interface{}) bool { + if IsScanValue(v) { return true } if _, ok := v.(string); ok { @@ -194,18 +183,9 @@ func IsParameterSubsetType(v interface{}) bool { return false } -// IsScanSubsetType reports whether v is of a valid type for a -// value populated by Rows.Next. These types are: -// -// int64 -// float64 -// bool -// nil -// []byte -// time.Time -// -// This is the same list as IsParameterSubsetType, without string. -func IsScanSubsetType(v interface{}) bool { +// IsScanValue reports whether v is a valid Value scan type. +// Unlike IsValue, IsScanValue does not permit the string type. +func IsScanValue(v interface{}) bool { if v == nil { return true } @@ -221,7 +201,7 @@ func IsScanSubsetType(v interface{}) bool { // ColumnConverter. // // DefaultParameterConverter returns the given value directly if -// IsSubsetType(value). Otherwise integer type are converted to +// IsValue(value). Otherwise integer type are converted to // int64, floats to float64, and strings to []byte. Other types are // an error. var DefaultParameterConverter defaultConverter @@ -230,18 +210,18 @@ type defaultConverter struct{} var _ ValueConverter = defaultConverter{} -func (defaultConverter) ConvertValue(v interface{}) (interface{}, error) { - if IsParameterSubsetType(v) { +func (defaultConverter) ConvertValue(v interface{}) (Value, error) { + if IsValue(v) { return v, nil } - if svi, ok := v.(SubsetValuer); ok { - sv, err := svi.SubsetValue() + if svi, ok := v.(Valuer); ok { + sv, err := svi.Value() if err != nil { return nil, err } - if !IsParameterSubsetType(sv) { - return nil, fmt.Errorf("non-subset type %T returned from SubsetValue", sv) + if !IsValue(sv) { + return nil, fmt.Errorf("non-Value type %T returned from Value", sv) } return sv, nil } diff --git a/libgo/go/database/sql/fakedb_test.go b/libgo/go/database/sql/fakedb_test.go index 889e2a25232..fc63f03740a 100644 --- a/libgo/go/database/sql/fakedb_test.go +++ b/libgo/go/database/sql/fakedb_test.go @@ -217,7 +217,7 @@ func (c *fakeConn) Close() error { return nil } -func checkSubsetTypes(args []interface{}) error { +func checkSubsetTypes(args []driver.Value) error { for n, arg := range args { switch arg.(type) { case int64, float64, bool, nil, []byte, string, time.Time: @@ -228,7 +228,7 @@ func checkSubsetTypes(args []interface{}) error { return nil } -func (c *fakeConn) Exec(query string, args []interface{}) (driver.Result, error) { +func (c *fakeConn) Exec(query string, args []driver.Value) (driver.Result, error) { // This is an optional interface, but it's implemented here // just to check that all the args of of the proper types. // ErrSkip is returned so the caller acts as if we didn't @@ -379,7 +379,7 @@ func (s *fakeStmt) Close() error { var errClosed = errors.New("fakedb: statement has been closed") -func (s *fakeStmt) Exec(args []interface{}) (driver.Result, error) { +func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) { if s.closed { return nil, errClosed } @@ -392,12 +392,12 @@ func (s *fakeStmt) Exec(args []interface{}) (driver.Result, error) { switch s.cmd { case "WIPE": db.wipe() - return driver.DDLSuccess, nil + return driver.ResultNoRows, nil case "CREATE": if err := db.createTable(s.table, s.colName, s.colType); err != nil { return nil, err } - return driver.DDLSuccess, nil + return driver.ResultNoRows, nil case "INSERT": return s.execInsert(args) } @@ -405,7 +405,7 @@ func (s *fakeStmt) Exec(args []interface{}) (driver.Result, error) { return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd) } -func (s *fakeStmt) execInsert(args []interface{}) (driver.Result, error) { +func (s *fakeStmt) execInsert(args []driver.Value) (driver.Result, error) { db := s.c.db if len(args) != s.placeholders { panic("error in pkg db; should only get here if size is correct") @@ -441,7 +441,7 @@ func (s *fakeStmt) execInsert(args []interface{}) (driver.Result, error) { return driver.RowsAffected(1), nil } -func (s *fakeStmt) Query(args []interface{}) (driver.Rows, error) { +func (s *fakeStmt) Query(args []driver.Value) (driver.Rows, error) { if s.closed { return nil, errClosed } @@ -548,7 +548,7 @@ func (rc *rowsCursor) Columns() []string { return rc.cols } -func (rc *rowsCursor) Next(dest []interface{}) error { +func (rc *rowsCursor) Next(dest []driver.Value) error { if rc.closed { return errors.New("fakedb: cursor is closed") } diff --git a/libgo/go/database/sql/sql.go b/libgo/go/database/sql/sql.go index f14a98c3cf2..62b551d89b5 100644 --- a/libgo/go/database/sql/sql.go +++ b/libgo/go/database/sql/sql.go @@ -62,8 +62,8 @@ func (ns *NullString) Scan(value interface{}) error { return convertAssign(&ns.String, value) } -// SubsetValue implements the driver SubsetValuer interface. -func (ns NullString) SubsetValue() (interface{}, error) { +// Value implements the driver Valuer interface. +func (ns NullString) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } @@ -88,8 +88,8 @@ func (n *NullInt64) Scan(value interface{}) error { return convertAssign(&n.Int64, value) } -// SubsetValue implements the driver SubsetValuer interface. -func (n NullInt64) SubsetValue() (interface{}, error) { +// Value implements the driver Valuer interface. +func (n NullInt64) Value() (driver.Value, error) { if !n.Valid { return nil, nil } @@ -114,8 +114,8 @@ func (n *NullFloat64) Scan(value interface{}) error { return convertAssign(&n.Float64, value) } -// SubsetValue implements the driver SubsetValuer interface. -func (n NullFloat64) SubsetValue() (interface{}, error) { +// Value implements the driver Valuer interface. +func (n NullFloat64) Value() (driver.Value, error) { if !n.Valid { return nil, nil } @@ -140,8 +140,8 @@ func (n *NullBool) Scan(value interface{}) error { return convertAssign(&n.Bool, value) } -// SubsetValue implements the driver SubsetValuer interface. -func (n NullBool) SubsetValue() (interface{}, error) { +// Value implements the driver Valuer interface. +func (n NullBool) Value() (driver.Value, error) { if !n.Valid { return nil, nil } @@ -523,8 +523,13 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) { } defer tx.releaseConn() + sargs, err := subsetTypeArgs(args) + if err != nil { + return nil, err + } + if execer, ok := ci.(driver.Execer); ok { - resi, err := execer.Exec(query, args) + resi, err := execer.Exec(query, sargs) if err == nil { return result{resi}, nil } @@ -539,11 +544,6 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) { } defer sti.Close() - sargs, err := subsetTypeArgs(args) - if err != nil { - return nil, err - } - resi, err := sti.Exec(sargs) if err != nil { return nil, err @@ -618,19 +618,21 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) { return nil, fmt.Errorf("sql: expected %d arguments, got %d", want, len(args)) } + sargs := make([]driver.Value, len(args)) + // Convert args to subset types. if cc, ok := si.(driver.ColumnConverter); ok { for n, arg := range args { // First, see if the value itself knows how to convert // itself to a driver type. For example, a NullString // struct changing into a string or nil. - if svi, ok := arg.(driver.SubsetValuer); ok { - sv, err := svi.SubsetValue() + if svi, ok := arg.(driver.Valuer); ok { + sv, err := svi.Value() if err != nil { - return nil, fmt.Errorf("sql: argument index %d from SubsetValue: %v", n, err) + return nil, fmt.Errorf("sql: argument index %d from Value: %v", n, err) } - if !driver.IsParameterSubsetType(sv) { - return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from SubsetValue", n, sv) + if !driver.IsValue(sv) { + return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from Value", n, sv) } arg = sv } @@ -642,25 +644,25 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) { // truncated), or that a nil can't go into a NOT NULL // column before going across the network to get the // same error. - args[n], err = cc.ColumnConverter(n).ConvertValue(arg) + sargs[n], err = cc.ColumnConverter(n).ConvertValue(arg) if err != nil { return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err) } - if !driver.IsParameterSubsetType(args[n]) { + if !driver.IsValue(sargs[n]) { return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T", - arg, args[n]) + arg, sargs[n]) } } } else { for n, arg := range args { - args[n], err = driver.DefaultParameterConverter.ConvertValue(arg) + sargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg) if err != nil { return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err) } } } - resi, err := si.Exec(args) + resi, err := si.Exec(sargs) if err != nil { return nil, err } @@ -829,7 +831,7 @@ type Rows struct { rowsi driver.Rows closed bool - lastcols []interface{} + lastcols []driver.Value lasterr error closeStmt *Stmt // if non-nil, statement to Close on close } @@ -846,7 +848,7 @@ func (rs *Rows) Next() bool { return false } if rs.lastcols == nil { - rs.lastcols = make([]interface{}, len(rs.rowsi.Columns())) + rs.lastcols = make([]driver.Value, len(rs.rowsi.Columns())) } rs.lasterr = rs.rowsi.Next(rs.lastcols) if rs.lasterr == io.EOF { diff --git a/libgo/go/debug/dwarf/open.go b/libgo/go/debug/dwarf/open.go index 9543297e189..37a518b6d37 100644 --- a/libgo/go/debug/dwarf/open.go +++ b/libgo/go/debug/dwarf/open.go @@ -31,8 +31,9 @@ type Data struct { } // New returns a new Data object initialized from the given parameters. -// Clients should typically use [TODO(rsc): method to be named later] instead of calling -// New directly. +// Rather than calling this function directly, clients should typically use +// the DWARF method of the File type of the appropriate package debug/elf, +// debug/macho, or debug/pe. // // The []byte arguments are the data from the corresponding debug section // in the object file; for example, for an ELF object, abbrev is the contents of diff --git a/libgo/go/debug/dwarf/testdata/typedef.c b/libgo/go/debug/dwarf/testdata/typedef.c index 664d021ced5..f05f01564ff 100644 --- a/libgo/go/debug/dwarf/testdata/typedef.c +++ b/libgo/go/debug/dwarf/testdata/typedef.c @@ -28,8 +28,13 @@ typedef struct my_struct { volatile int vi; char x : 1; int y : 4; + int z[0]; long long array[40]; + int zz[0]; } t_my_struct; +typedef struct my_struct1 { + int zz [1]; +} t_my_struct1; typedef union my_union { volatile int vi; char x : 1; @@ -65,7 +70,8 @@ t_func_void_of_char *a9; t_func_void_of_void *a10; t_func_void_of_ptr_char_dots *a11; t_my_struct *a12; -t_my_union *a12a; +t_my_struct1 *a12a; +t_my_union *a12b; t_my_enum *a13; t_my_list *a14; t_my_tree *a15; diff --git a/libgo/go/debug/dwarf/testdata/typedef.elf b/libgo/go/debug/dwarf/testdata/typedef.elf Binary files differindex 44df8da9bc7..b2062d2c4bb 100755 --- a/libgo/go/debug/dwarf/testdata/typedef.elf +++ b/libgo/go/debug/dwarf/testdata/typedef.elf diff --git a/libgo/go/debug/dwarf/testdata/typedef.macho b/libgo/go/debug/dwarf/testdata/typedef.macho Binary files differindex 41019c1e146..f75afcccbfc 100644 --- a/libgo/go/debug/dwarf/testdata/typedef.macho +++ b/libgo/go/debug/dwarf/testdata/typedef.macho diff --git a/libgo/go/debug/dwarf/type.go b/libgo/go/debug/dwarf/type.go index 9be66658fe9..4502355022d 100644 --- a/libgo/go/debug/dwarf/type.go +++ b/libgo/go/debug/dwarf/type.go @@ -426,6 +426,8 @@ func (d *Data) Type(off Offset) (Type, error) { t.StructName, _ = e.Val(AttrName).(string) t.Incomplete = e.Val(AttrDeclaration) != nil t.Field = make([]*StructField, 0, 8) + var lastFieldType Type + var lastFieldBitOffset int64 for kid := next(); kid != nil; kid = next() { if kid.Tag == TagMember { f := new(StructField) @@ -444,11 +446,32 @@ func (d *Data) Type(off Offset) (Type, error) { goto Error } } + + haveBitOffset := false f.Name, _ = kid.Val(AttrName).(string) f.ByteSize, _ = kid.Val(AttrByteSize).(int64) - f.BitOffset, _ = kid.Val(AttrBitOffset).(int64) + f.BitOffset, haveBitOffset = kid.Val(AttrBitOffset).(int64) f.BitSize, _ = kid.Val(AttrBitSize).(int64) t.Field = append(t.Field, f) + + bito := f.BitOffset + if !haveBitOffset { + bito = f.ByteOffset * 8 + } + if bito == lastFieldBitOffset && t.Kind != "union" { + // Last field was zero width. Fix array length. + // (DWARF writes out 0-length arrays as if they were 1-length arrays.) + zeroArray(lastFieldType) + } + lastFieldType = f.Type + lastFieldBitOffset = bito + } + } + if t.Kind != "union" { + b, ok := e.Val(AttrByteSize).(int64) + if ok && b*8 == lastFieldBitOffset { + // Final field must be zero width. Fix array length. + zeroArray(lastFieldType) } } @@ -579,3 +602,14 @@ Error: delete(d.typeCache, off) return nil, err } + +func zeroArray(t Type) { + for { + at, ok := t.(*ArrayType) + if !ok { + break + } + at.Count = 0 + t = at.Type + } +} diff --git a/libgo/go/debug/dwarf/type_test.go b/libgo/go/debug/dwarf/type_test.go index b9470a4fcb4..b5b255f6f4a 100644 --- a/libgo/go/debug/dwarf/type_test.go +++ b/libgo/go/debug/dwarf/type_test.go @@ -25,13 +25,22 @@ var typedefTests = map[string]string{ "t_func_void_of_char": "func(char) void", "t_func_void_of_void": "func() void", "t_func_void_of_ptr_char_dots": "func(*char, ...) void", - "t_my_struct": "struct my_struct {vi volatile int@0; x char@4 : 1@7; y int@4 : 4@27; array [40]long long int@8}", + "t_my_struct": "struct my_struct {vi volatile int@0; x char@4 : 1@7; y int@4 : 4@27; z [0]int@8; array [40]long long int@8; zz [0]int@328}", + "t_my_struct1": "struct my_struct1 {zz [1]int@0}", "t_my_union": "union my_union {vi volatile int@0; x char@0 : 1@7; y int@0 : 4@28; array [40]long long int@0}", "t_my_enum": "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}", "t_my_list": "struct list {val short int@0; next *t_my_list@8}", "t_my_tree": "struct tree {left *struct tree@0; right *struct tree@8; val long long unsigned int@16}", } +// As Apple converts gcc to a clang-based front end +// they keep breaking the DWARF output. This map lists the +// conversion from real answer to Apple answer. +var machoBug = map[string]string{ + "func(*char, ...) void": "func(*char) void", + "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}": "enum my_enum {e1=1; e2=2; e3=-5; e4=-1530494976}", +} + func elfData(t *testing.T, name string) *Data { f, err := elf.Open(name) if err != nil { @@ -58,13 +67,13 @@ func machoData(t *testing.T, name string) *Data { return d } -func TestTypedefsELF(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf")) } +func TestTypedefsELF(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf"), "elf") } func TestTypedefsMachO(t *testing.T) { - testTypedefs(t, machoData(t, "testdata/typedef.macho")) + testTypedefs(t, machoData(t, "testdata/typedef.macho"), "macho") } -func testTypedefs(t *testing.T, d *Data) { +func testTypedefs(t *testing.T, d *Data, kind string) { r := d.Reader() seen := make(map[string]bool) for { @@ -93,7 +102,7 @@ func testTypedefs(t *testing.T, d *Data) { t.Errorf("multiple definitions for %s", t1.Name) } seen[t1.Name] = true - if typstr != want { + if typstr != want && (kind != "macho" || typstr != machoBug[want]) { t.Errorf("%s:\n\thave %s\n\twant %s", t1.Name, typstr, want) } } diff --git a/libgo/go/debug/gosym/pclntab_test.go b/libgo/go/debug/gosym/pclntab_test.go index b90181bdc64..b2400bb3ba7 100644 --- a/libgo/go/debug/gosym/pclntab_test.go +++ b/libgo/go/debug/gosym/pclntab_test.go @@ -6,15 +6,37 @@ package gosym import ( "debug/elf" + "fmt" "os" + "os/exec" "runtime" + "strings" "testing" ) +var pclinetestBinary string + func dotest() bool { // For now, only works on ELF platforms. - // TODO: convert to work with new go tool - return false && runtime.GOOS == "linux" && runtime.GOARCH == "amd64" + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + return false + } + if pclinetestBinary != "" { + return true + } + // This command builds pclinetest from pclinetest.asm; + // the resulting binary looks like it was built from pclinetest.s, + // but we have renamed it to keep it away from the go tool. + pclinetestBinary = os.TempDir() + "/pclinetest" + command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -E main -o %s %s.6", + pclinetestBinary, pclinetestBinary, pclinetestBinary) + cmd := exec.Command("sh", "-c", command) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + panic(err) + } + return true } func getTable(t *testing.T) *Table { @@ -149,7 +171,7 @@ func TestPCLine(t *testing.T) { return } - f, tab := crack("_test/pclinetest", t) + f, tab := crack(pclinetestBinary, t) text := f.Section(".text") textdat, err := text.Data() if err != nil { @@ -163,10 +185,13 @@ func TestPCLine(t *testing.T) { file, line, fn := tab.PCToLine(pc) off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g wantLine += int(textdat[off]) + t.Logf("off is %d", off) if fn == nil { t.Errorf("failed to get line of PC %#x", pc) - } else if len(file) < 12 || file[len(file)-12:] != "pclinetest.s" || line != wantLine || fn != sym { - t.Errorf("expected %s:%d (%s) at PC %#x, got %s:%d (%s)", "pclinetest.s", wantLine, sym.Name, pc, file, line, fn.Name) + } else if !strings.HasSuffix(file, "pclinetest.asm") { + t.Errorf("expected %s (%s) at PC %#x, got %s (%s)", "pclinetest.asm", sym.Name, pc, file, fn.Name) + } else if line != wantLine || fn != sym { + t.Errorf("expected :%d (%s) at PC %#x, got :%d (%s)", wantLine, sym.Name, pc, line, fn.Name) } } diff --git a/libgo/go/encoding/gob/decode.go b/libgo/go/encoding/gob/decode.go index 750d623cde2..a0bb985300f 100644 --- a/libgo/go/encoding/gob/decode.go +++ b/libgo/go/encoding/gob/decode.go @@ -464,7 +464,7 @@ func allocate(rtyp reflect.Type, p uintptr, indir int) uintptr { // decodeSingle decodes a top-level value that is not a struct and stores it through p. // Such values are preceded by a zero, making them have the memory layout of a // struct field (although with an illegal field number). -func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uintptr) (err error) { +func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uintptr) { state := dec.newDecoderState(&dec.buf) state.fieldnum = singletonField delta := int(state.decodeUint()) @@ -473,7 +473,7 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uint } instr := &engine.instr[singletonField] if instr.indir != ut.indir { - return errors.New("gob: internal error: inconsistent indirection") + errorf("internal error: inconsistent indirection instr %d ut %d", instr.indir, ut.indir) } ptr := unsafe.Pointer(basep) // offset will be zero if instr.indir > 1 { @@ -481,10 +481,9 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uint } instr.op(instr, state, ptr) dec.freeDecoderState(state) - return nil } -// decodeSingle decodes a top-level struct and stores it through p. +// decodeStruct decodes a top-level struct and stores it through p. // Indir is for the value, not the type. At the time of the call it may // differ from ut.indir, which was computed when the engine was built. // This state cannot arise for decodeSingle, which is called directly @@ -839,11 +838,10 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg } case reflect.Map: - name = "element of " + name keyId := dec.wireType[wireId].MapT.Key elemId := dec.wireType[wireId].MapT.Elem - keyOp, keyIndir := dec.decOpFor(keyId, t.Key(), name, inProgress) - elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), name, inProgress) + keyOp, keyIndir := dec.decOpFor(keyId, t.Key(), "key of "+name, inProgress) + elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), "element of "+name, inProgress) ovfl := overflow(name) op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { up := unsafe.Pointer(p) @@ -1151,7 +1149,7 @@ func (dec *Decoder) compileDec(remoteId typeId, ut *userTypeInfo) (engine *decEn // getDecEnginePtr returns the engine for the specified type. func (dec *Decoder) getDecEnginePtr(remoteId typeId, ut *userTypeInfo) (enginePtr **decEngine, err error) { - rt := ut.base + rt := ut.user decoderMap, ok := dec.decoderCache[rt] if !ok { decoderMap = make(map[typeId]**decEngine) diff --git a/libgo/go/encoding/gob/encoder_test.go b/libgo/go/encoding/gob/encoder_test.go index 9a62cf9c2ad..3bfae30f39a 100644 --- a/libgo/go/encoding/gob/encoder_test.go +++ b/libgo/go/encoding/gob/encoder_test.go @@ -685,3 +685,54 @@ func TestSliceIncompatibility(t *testing.T) { t.Error("expected compatibility error") } } + +// Mutually recursive slices of structs caused problems. +type Bug3 struct { + Num int + Children []*Bug3 +} + +func TestGobPtrSlices(t *testing.T) { + in := []*Bug3{ + &Bug3{1, nil}, + &Bug3{2, nil}, + } + b := new(bytes.Buffer) + err := NewEncoder(b).Encode(&in) + if err != nil { + t.Fatal("encode:", err) + } + + var out []*Bug3 + err = NewDecoder(b).Decode(&out) + if err != nil { + t.Fatal("decode:", err) + } + if !reflect.DeepEqual(in, out) { + t.Fatal("got %v; wanted %v", out, in) + } +} + +// getDecEnginePtr cached engine for ut.base instead of ut.user so we passed +// a *map and then tried to reuse its engine to decode the inner map. +func TestPtrToMapOfMap(t *testing.T) { + Register(make(map[string]interface{})) + subdata := make(map[string]interface{}) + subdata["bar"] = "baz" + data := make(map[string]interface{}) + data["foo"] = subdata + + b := new(bytes.Buffer) + err := NewEncoder(b).Encode(data) + if err != nil { + t.Fatal("encode:", err) + } + var newData map[string]interface{} + err = NewDecoder(b).Decode(&newData) + if err != nil { + t.Fatal("decode:", err) + } + if !reflect.DeepEqual(data, newData) { + t.Fatalf("expected %v got %v", data, newData) + } +} diff --git a/libgo/go/encoding/gob/type.go b/libgo/go/encoding/gob/type.go index 39006efdb2d..0dd7a0a770e 100644 --- a/libgo/go/encoding/gob/type.go +++ b/libgo/go/encoding/gob/type.go @@ -152,6 +152,10 @@ var idToType = make(map[typeId]gobType) var builtinIdToType map[typeId]gobType // set in init() after builtins are established func setTypeId(typ gobType) { + // When building recursive types, someone may get there before us. + if typ.id() != 0 { + return + } nextId++ typ.setId(nextId) idToType[nextId] = typ @@ -346,6 +350,11 @@ func newSliceType(name string) *sliceType { func (s *sliceType) init(elem gobType) { // Set our type id before evaluating the element's, in case it's our own. setTypeId(s) + // See the comments about ids in newTypeObject. Only slices and + // structs have mutual recursion. + if elem.id() == 0 { + setTypeId(elem) + } s.Elem = elem.id() } @@ -503,6 +512,13 @@ func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, err if err != nil { return nil, err } + // Some mutually recursive types can cause us to be here while + // still defining the element. Fix the element type id here. + // We could do this more neatly by setting the id at the start of + // building every type, but that would break binary compatibility. + if gt.id() == 0 { + setTypeId(gt) + } st.Field = append(st.Field, &fieldType{f.Name, gt.id()}) } return st, nil diff --git a/libgo/go/encoding/json/decode.go b/libgo/go/encoding/json/decode.go index 87076b53dc0..110c6fd6238 100644 --- a/libgo/go/encoding/json/decode.go +++ b/libgo/go/encoding/json/decode.go @@ -496,6 +496,12 @@ func (d *decodeState) object(v reflect.Value) { // Pretend this field doesn't exist. continue } + if sf.Anonymous { + // Pretend this field doesn't exist, + // so that we can do a good job with + // these in a later version. + continue + } // First, tag match tagName, _ := parseTag(tag) if tagName == key { @@ -963,3 +969,11 @@ func unquoteBytes(s []byte) (t []byte, ok bool) { } return b[0:w], true } + +// The following is issue 3069. + +// BUG(rsc): This package ignores anonymous (embedded) struct fields +// during encoding and decoding. A future version may assign meaning +// to them. To force an anonymous field to be ignored in all future +// versions of this package, use an explicit `json:"-"` tag in the struct +// definition. diff --git a/libgo/go/encoding/json/decode_test.go b/libgo/go/encoding/json/decode_test.go index 775becfa7c9..0eec586a9bb 100644 --- a/libgo/go/encoding/json/decode_test.go +++ b/libgo/go/encoding/json/decode_test.go @@ -619,3 +619,32 @@ func TestRefUnmarshal(t *testing.T) { t.Errorf("got %+v, want %+v", got, want) } } + +// Test that anonymous fields are ignored. +// We may assign meaning to them later. +func TestAnonymous(t *testing.T) { + type S struct { + T + N int + } + + data, err := Marshal(new(S)) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + want := `{"N":0}` + if string(data) != want { + t.Fatalf("Marshal = %#q, want %#q", string(data), want) + } + + var s S + if err := Unmarshal([]byte(`{"T": 1, "T": {"Y": 1}, "N": 2}`), &s); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + if s.N != 2 { + t.Fatal("Unmarshal: did not set N") + } + if s.T.Y != 0 { + t.Fatal("Unmarshal: did set T.Y") + } +} diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go index 83e73c09cb4..8a794b79bd5 100644 --- a/libgo/go/encoding/json/encode.go +++ b/libgo/go/encoding/json/encode.go @@ -538,6 +538,11 @@ func encodeFields(t reflect.Type) []encodeField { if f.PkgPath != "" { continue } + if f.Anonymous { + // We want to do a better job with these later, + // so for now pretend they don't exist. + continue + } var ef encodeField ef.i = i ef.tag = f.Name diff --git a/libgo/go/encoding/xml/marshal.go b/libgo/go/encoding/xml/marshal.go index a96c523d553..6c3170bdda3 100644 --- a/libgo/go/encoding/xml/marshal.go +++ b/libgo/go/encoding/xml/marshal.go @@ -57,35 +57,14 @@ const ( // if the field value is empty. The empty values are false, 0, any // nil pointer or interface value, and any array, slice, map, or // string of length zero. +// - a non-pointer anonymous struct field is handled as if the +// fields of its value were part of the outer struct. // // If a field uses a tag "a>b>c", then the element c will be nested inside // parent elements a and b. Fields that appear next to each other that name -// the same parent will be enclosed in one XML element. For example: +// the same parent will be enclosed in one XML element. // -// type Result struct { -// XMLName xml.Name `xml:"result"` -// Id int `xml:"id,attr"` -// FirstName string `xml:"person>name>first"` -// LastName string `xml:"person>name>last"` -// Age int `xml:"person>age"` -// Height float `xml:"person>height,omitempty"` -// Married bool `xml:"person>married"` -// } -// -// xml.Marshal(&Result{Id: 13, FirstName: "John", LastName: "Doe", Age: 42}) -// -// would be marshalled as: -// -// <result> -// <person id="13"> -// <name> -// <first>John</first> -// <last>Doe</last> -// </name> -// <age>42</age> -// <married>false</married> -// </person> -// </result> +// See MarshalIndent for an example. // // Marshal will return an error if asked to marshal a channel, function, or map. func Marshal(v interface{}) ([]byte, error) { @@ -96,6 +75,22 @@ func Marshal(v interface{}) ([]byte, error) { return b.Bytes(), nil } +// MarshalIndent works like Marshal, but each XML element begins on a new +// indented line that starts with prefix and is followed by one or more +// copies of indent according to the nesting depth. +func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { + var b bytes.Buffer + enc := NewEncoder(&b) + enc.prefix = prefix + enc.indent = indent + err := enc.marshalValue(reflect.ValueOf(v), nil) + enc.Flush() + if err != nil { + return nil, err + } + return b.Bytes(), nil +} + // An Encoder writes XML data to an output stream. type Encoder struct { printer @@ -103,7 +98,7 @@ type Encoder struct { // NewEncoder returns a new encoder that writes to w. func NewEncoder(w io.Writer) *Encoder { - return &Encoder{printer{bufio.NewWriter(w)}} + return &Encoder{printer{Writer: bufio.NewWriter(w)}} } // Encode writes the XML encoding of v to the stream. @@ -118,8 +113,14 @@ func (enc *Encoder) Encode(v interface{}) error { type printer struct { *bufio.Writer + indent string + prefix string + depth int + indentedIn bool } +// marshalValue writes one or more XML elements representing val. +// If val was obtained from a struct field, finfo must have its details. func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { if !val.IsValid() { return nil @@ -177,6 +178,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { } } + p.writeIndent(1) p.WriteByte('<') p.WriteString(name) @@ -216,6 +218,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { return err } + p.writeIndent(-1) p.WriteByte('<') p.WriteByte('/') p.WriteString(name) @@ -294,6 +297,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { if vf.Len() == 0 { continue } + p.writeIndent(0) p.WriteString("<!--") dashDash := false dashLast := false @@ -352,6 +356,33 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { return nil } +func (p *printer) writeIndent(depthDelta int) { + if len(p.prefix) == 0 && len(p.indent) == 0 { + return + } + if depthDelta < 0 { + p.depth-- + if p.indentedIn { + p.indentedIn = false + return + } + p.indentedIn = false + } + p.WriteByte('\n') + if len(p.prefix) > 0 { + p.WriteString(p.prefix) + } + if len(p.indent) > 0 { + for i := 0; i < p.depth; i++ { + p.WriteString(p.indent) + } + } + if depthDelta > 0 { + p.depth++ + p.indentedIn = true + } +} + type parentStack struct { *printer stack []string @@ -367,20 +398,20 @@ func (s *parentStack) trim(parents []string) { break } } - for i := len(s.stack) - 1; i >= split; i-- { + s.writeIndent(-1) s.WriteString("</") s.WriteString(s.stack[i]) s.WriteByte('>') } - s.stack = parents[:split] } // push adds parent elements to the stack and writes open tags. func (s *parentStack) push(parents []string) { for i := 0; i < len(parents); i++ { - s.WriteString("<") + s.writeIndent(1) + s.WriteByte('<') s.WriteString(parents[i]) s.WriteByte('>') } diff --git a/libgo/go/encoding/xml/read.go b/libgo/go/encoding/xml/read.go index b5a3426a328..c2168242091 100644 --- a/libgo/go/encoding/xml/read.go +++ b/libgo/go/encoding/xml/read.go @@ -25,58 +25,6 @@ import ( // slice, or string. Well-formed data that does not fit into v is // discarded. // -// For example, given these definitions: -// -// type Email struct { -// Where string `xml:",attr"` -// Addr string -// } -// -// type Result struct { -// XMLName xml.Name `xml:"result"` -// Name string -// Phone string -// Email []Email -// Groups []string `xml:"group>value"` -// } -// -// result := Result{Name: "name", Phone: "phone", Email: nil} -// -// unmarshalling the XML input -// -// <result> -// <email where="home"> -// <addr>gre@example.com</addr> -// </email> -// <email where='work'> -// <addr>gre@work.com</addr> -// </email> -// <name>Grace R. Emlin</name> -// <group> -// <value>Friends</value> -// <value>Squash</value> -// </group> -// <address>123 Main Street</address> -// </result> -// -// via Unmarshal(data, &result) is equivalent to assigning -// -// r = Result{ -// xml.Name{Local: "result"}, -// "Grace R. Emlin", // name -// "phone", // no phone given -// []Email{ -// Email{"home", "gre@example.com"}, -// Email{"work", "gre@work.com"}, -// }, -// []string{"Friends", "Squash"}, -// } -// -// Note that the field r.Phone has not been modified and -// that the XML <address> element was discarded. Also, the field -// Groups was assigned considering the element path provided in the -// field tag. -// // Because Unmarshal uses the reflect package, it can only assign // to exported (upper case) fields. Unmarshal uses a case-sensitive // comparison to match XML element names to tag values and struct @@ -133,6 +81,9 @@ import ( // of the above rules and the struct has a field with tag ",any", // unmarshal maps the sub-element to that struct field. // +// * A non-pointer anonymous struct field is handled as if the +// fields of its value were part of the outer struct. +// // * A struct field with tag "-" is never unmarshalled into. // // Unmarshal maps an XML element to a string or []byte by saving the diff --git a/libgo/go/errors/errors_test.go b/libgo/go/errors/errors_test.go index c537eeb6251..63c05d7185b 100644 --- a/libgo/go/errors/errors_test.go +++ b/libgo/go/errors/errors_test.go @@ -5,29 +5,49 @@ package errors_test import ( - . "errors" + "errors" + "fmt" "testing" ) func TestNewEqual(t *testing.T) { // Different allocations should not be equal. - if New("abc") == New("abc") { + if errors.New("abc") == errors.New("abc") { t.Errorf(`New("abc") == New("abc")`) } - if New("abc") == New("xyz") { + if errors.New("abc") == errors.New("xyz") { t.Errorf(`New("abc") == New("xyz")`) } // Same allocation should be equal to itself (not crash). - err := New("jkl") + err := errors.New("jkl") if err != err { t.Errorf(`err != err`) } } func TestErrorMethod(t *testing.T) { - err := New("abc") + err := errors.New("abc") if err.Error() != "abc" { t.Errorf(`New("abc").Error() = %q, want %q`, err.Error(), "abc") } } + +func ExampleNew() { + err := errors.New("emit macho dwarf: elf header corrupted") + if err != nil { + fmt.Print(err) + } + // Output: emit macho dwarf: elf header corrupted +} + +// The fmt package's Errorf function lets us use the package's formatting +// features to create descriptive error messages. +func ExampleNew_errorf() { + const name, id = "bimmler", 17 + err := fmt.Errorf("user %q (id %d) not found", name, id) + if err != nil { + fmt.Print(err) + } + // Output: user "bimmler" (id 17) not found +} diff --git a/libgo/go/exp/inotify/inotify_linux_test.go b/libgo/go/exp/inotify/inotify_linux_test.go index c2160fc6537..d41d66bfacd 100644 --- a/libgo/go/exp/inotify/inotify_linux_test.go +++ b/libgo/go/exp/inotify/inotify_linux_test.go @@ -7,6 +7,7 @@ package inotify import ( + "io/ioutil" "os" "testing" "time" @@ -16,16 +17,19 @@ func TestInotifyEvents(t *testing.T) { // Create an inotify watcher instance and initialize it watcher, err := NewWatcher() if err != nil { - t.Fatalf("NewWatcher() failed: %s", err) + t.Fatalf("NewWatcher failed: %s", err) } - t.Logf("NEEDS TO BE CONVERTED TO NEW GO TOOL") // TODO - return + dir, err := ioutil.TempDir("", "inotify") + if err != nil { + t.Fatalf("TempDir failed: %s", err) + } + defer os.RemoveAll(dir) // Add a watch for "_test" - err = watcher.Watch("_test") + err = watcher.Watch(dir) if err != nil { - t.Fatalf("Watcher.Watch() failed: %s", err) + t.Fatalf("Watch failed: %s", err) } // Receive errors on the error channel on a separate goroutine @@ -35,7 +39,7 @@ func TestInotifyEvents(t *testing.T) { } }() - const testFile string = "_test/TestInotifyEvents.testfile" + testFile := dir + "/TestInotifyEvents.testfile" // Receive events on the event channel on a separate goroutine eventstream := watcher.Event @@ -58,7 +62,7 @@ func TestInotifyEvents(t *testing.T) { // This should add at least one event to the inotify event queue _, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) if err != nil { - t.Fatalf("creating test file failed: %s", err) + t.Fatalf("creating test file: %s", err) } // We expect this event to be received almost immediately, but let's wait 1 s to be sure @@ -95,7 +99,7 @@ func TestInotifyClose(t *testing.T) { t.Fatal("double Close() test failed: second Close() call didn't return") } - err := watcher.Watch("_test") + err := watcher.Watch(os.TempDir()) if err == nil { t.Fatal("expected error on Watch() after Close(), got nil") } diff --git a/libgo/go/exp/norm/composition.go b/libgo/go/exp/norm/composition.go index ccff4670602..2cbe1ac730e 100644 --- a/libgo/go/exp/norm/composition.go +++ b/libgo/go/exp/norm/composition.go @@ -66,6 +66,18 @@ func (rb *reorderBuffer) flush(out []byte) []byte { return out } +// flushCopy copies the normalized segment to buf and resets rb. +// It returns the number of bytes written to buf. +func (rb *reorderBuffer) flushCopy(buf []byte) int { + p := 0 + for i := 0; i < rb.nrune; i++ { + runep := rb.rune[i] + p += copy(buf[p:], rb.byte[runep.pos:runep.pos+runep.size]) + } + rb.reset() + return p +} + // insertOrdered inserts a rune in the buffer, ordered by Canonical Combining Class. // It returns false if the buffer is not large enough to hold the rune. // It is used internally by insert and insertString only. @@ -96,32 +108,41 @@ func (rb *reorderBuffer) insertOrdered(info runeInfo) bool { // insert inserts the given rune in the buffer ordered by CCC. // It returns true if the buffer was large enough to hold the decomposed rune. func (rb *reorderBuffer) insert(src input, i int, info runeInfo) bool { - if info.size == 3 { - if rune := src.hangul(i); rune != 0 { - return rb.decomposeHangul(rune) - } + if rune := src.hangul(i); rune != 0 { + return rb.decomposeHangul(rune) } if info.hasDecomposition() { - dcomp := info.decomposition() - rb.tmpBytes = inputBytes(dcomp) - for i := 0; i < len(dcomp); { - info = rb.f.info(&rb.tmpBytes, i) - pos := rb.nbyte - if !rb.insertOrdered(info) { - return false - } - end := i + int(info.size) - copy(rb.byte[pos:], dcomp[i:end]) - i = end - } - } else { - // insertOrder changes nbyte + return rb.insertDecomposed(info.decomposition()) + } + return rb.insertSingle(src, i, info) +} + +// insertDecomposed inserts an entry in to the reorderBuffer for each rune +// in dcomp. dcomp must be a sequence of decomposed UTF-8-encoded runes. +func (rb *reorderBuffer) insertDecomposed(dcomp []byte) bool { + saveNrune, saveNbyte := rb.nrune, rb.nbyte + rb.tmpBytes = inputBytes(dcomp) + for i := 0; i < len(dcomp); { + info := rb.f.info(&rb.tmpBytes, i) pos := rb.nbyte if !rb.insertOrdered(info) { + rb.nrune, rb.nbyte = saveNrune, saveNbyte return false } - src.copySlice(rb.byte[pos:], i, i+int(info.size)) + i += copy(rb.byte[pos:], dcomp[i:i+int(info.size)]) + } + return true +} + +// insertSingle inserts an entry in the reorderBuffer for the rune at +// position i. info is the runeInfo for the rune at position i. +func (rb *reorderBuffer) insertSingle(src input, i int, info runeInfo) bool { + // insertOrder changes nbyte + pos := rb.nbyte + if !rb.insertOrdered(info) { + return false } + src.copySlice(rb.byte[pos:], i, i+int(info.size)) return true } @@ -182,8 +203,12 @@ const ( jamoLVTCount = 19 * 21 * 28 ) -// Caller must verify that len(b) >= 3. +const hangulUTF8Size = 3 + func isHangul(b []byte) bool { + if len(b) < hangulUTF8Size { + return false + } b0 := b[0] if b0 < hangulBase0 { return false @@ -202,8 +227,10 @@ func isHangul(b []byte) bool { return b1 == hangulEnd1 && b[2] < hangulEnd2 } -// Caller must verify that len(b) >= 3. func isHangulString(b string) bool { + if len(b) < hangulUTF8Size { + return false + } b0 := b[0] if b0 < hangulBase0 { return false @@ -234,6 +261,22 @@ func isHangulWithoutJamoT(b []byte) bool { return c < jamoLVTCount && c%jamoTCount == 0 } +// decomposeHangul writes the decomposed Hangul to buf and returns the number +// of bytes written. len(buf) should be at least 9. +func decomposeHangul(buf []byte, r rune) int { + const JamoUTF8Len = 3 + r -= hangulBase + x := r % jamoTCount + r /= jamoTCount + utf8.EncodeRune(buf, jamoLBase+r/jamoVCount) + utf8.EncodeRune(buf[JamoUTF8Len:], jamoVBase+r%jamoVCount) + if x != 0 { + utf8.EncodeRune(buf[2*JamoUTF8Len:], jamoTBase+x) + return 3 * JamoUTF8Len + } + return 2 * JamoUTF8Len +} + // decomposeHangul algorithmically decomposes a Hangul rune into // its Jamo components. // See http://unicode.org/reports/tr15/#Hangul for details on decomposing Hangul. diff --git a/libgo/go/exp/norm/composition_test.go b/libgo/go/exp/norm/composition_test.go index e32380d7afa..9de9eacfd65 100644 --- a/libgo/go/exp/norm/composition_test.go +++ b/libgo/go/exp/norm/composition_test.go @@ -47,14 +47,14 @@ func runTests(t *testing.T, name string, fm Form, f insertFunc, tests []TestCase } } -func TestFlush(t *testing.T) { +type flushFunc func(rb *reorderBuffer) []byte + +func testFlush(t *testing.T, name string, fn flushFunc) { rb := reorderBuffer{} rb.init(NFC, nil) - out := make([]byte, 0) - - out = rb.flush(out) + out := fn(&rb) if len(out) != 0 { - t.Errorf("wrote bytes on flush of empty buffer. (len(out) = %d)", len(out)) + t.Errorf("%s: wrote bytes on flush of empty buffer. (len(out) = %d)", name, len(out)) } for _, r := range []rune("world!") { @@ -65,16 +65,32 @@ func TestFlush(t *testing.T) { out = rb.flush(out) want := "Hello world!" if string(out) != want { - t.Errorf(`output after flush was "%s"; want "%s"`, string(out), want) + t.Errorf(`%s: output after flush was "%s"; want "%s"`, name, string(out), want) } if rb.nrune != 0 { - t.Errorf("flush: non-null size of info buffer (rb.nrune == %d)", rb.nrune) + t.Errorf("%s: non-null size of info buffer (rb.nrune == %d)", name, rb.nrune) } if rb.nbyte != 0 { - t.Errorf("flush: non-null size of byte buffer (rb.nbyte == %d)", rb.nbyte) + t.Errorf("%s: non-null size of byte buffer (rb.nbyte == %d)", name, rb.nbyte) } } +func flushF(rb *reorderBuffer) []byte { + out := make([]byte, 0) + return rb.flush(out) +} + +func flushCopyF(rb *reorderBuffer) []byte { + out := make([]byte, MaxSegmentSize) + n := rb.flushCopy(out) + return out[:n] +} + +func TestFlush(t *testing.T) { + testFlush(t, "flush", flushF) + testFlush(t, "flushCopy", flushCopyF) +} + var insertTests = []TestCase{ {[]rune{'a'}, []rune{'a'}}, {[]rune{0x300}, []rune{0x300}}, diff --git a/libgo/go/exp/norm/input.go b/libgo/go/exp/norm/input.go index 5c0968ba58c..9c564d67718 100644 --- a/libgo/go/exp/norm/input.go +++ b/libgo/go/exp/norm/input.go @@ -7,7 +7,7 @@ package norm import "unicode/utf8" type input interface { - skipASCII(p int) int + skipASCII(p, max int) int skipNonStarter(p int) int appendSlice(buf []byte, s, e int) []byte copySlice(buf []byte, s, e int) @@ -18,8 +18,8 @@ type input interface { type inputString string -func (s inputString) skipASCII(p int) int { - for ; p < len(s) && s[p] < utf8.RuneSelf; p++ { +func (s inputString) skipASCII(p, max int) int { + for ; p < max && s[p] < utf8.RuneSelf; p++ { } return p } @@ -59,8 +59,8 @@ func (s inputString) hangul(p int) rune { type inputBytes []byte -func (s inputBytes) skipASCII(p int) int { - for ; p < len(s) && s[p] < utf8.RuneSelf; p++ { +func (s inputBytes) skipASCII(p, max int) int { + for ; p < max && s[p] < utf8.RuneSelf; p++ { } return p } diff --git a/libgo/go/exp/norm/iter.go b/libgo/go/exp/norm/iter.go new file mode 100644 index 00000000000..761ba90cdd4 --- /dev/null +++ b/libgo/go/exp/norm/iter.go @@ -0,0 +1,286 @@ +// 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 norm + +const MaxSegmentSize = maxByteBufferSize + +// An Iter iterates over a string or byte slice, while normalizing it +// to a given Form. +type Iter struct { + rb reorderBuffer + info runeInfo // first character saved from previous iteration + next iterFunc // implementation of next depends on form + + p int // current position in input source + outStart int // start of current segment in output buffer + inStart int // start of current segment in input source + maxp int // position in output buffer after which not to start a new segment + maxseg int // for tracking an excess of combining characters + + tccc uint8 + done bool +} + +type iterFunc func(*Iter, []byte) int + +// SetInput initializes i to iterate over src after normalizing it to Form f. +func (i *Iter) SetInput(f Form, src []byte) { + i.rb.init(f, src) + if i.rb.f.composing { + i.next = nextComposed + } else { + i.next = nextDecomposed + } + i.p = 0 + if i.done = len(src) == 0; !i.done { + i.info = i.rb.f.info(i.rb.src, i.p) + } +} + +// SetInputString initializes i to iterate over src after normalizing it to Form f. +func (i *Iter) SetInputString(f Form, src string) { + i.rb.initString(f, src) + if i.rb.f.composing { + i.next = nextComposed + } else { + i.next = nextDecomposed + } + i.p = 0 + if i.done = len(src) == 0; !i.done { + i.info = i.rb.f.info(i.rb.src, i.p) + } +} + +// Pos returns the byte position at which the next call to Next will commence processing. +func (i *Iter) Pos() int { + return i.p +} + +// Done returns true if there is no more input to process. +func (i *Iter) Done() bool { + return i.done +} + +// Next writes f(i.input[i.Pos():n]...) to buffer buf, where n is the +// largest boundary of i.input such that the result fits in buf. +// It returns the number of bytes written to buf. +// len(buf) should be at least MaxSegmentSize. +// Done must be false before calling Next. +func (i *Iter) Next(buf []byte) int { + return i.next(i, buf) +} + +func (i *Iter) initNext(outn, inStart int) { + i.outStart = 0 + i.inStart = inStart + i.maxp = outn - MaxSegmentSize + i.maxseg = MaxSegmentSize +} + +// setStart resets the start of the new segment to the given position. +// It returns true if there is not enough room for the new segment. +func (i *Iter) setStart(outp, inp int) bool { + if outp > i.maxp { + return true + } + i.outStart = outp + i.inStart = inp + i.maxseg = outp + MaxSegmentSize + return false +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +// nextDecomposed is the implementation of Next for forms NFD and NFKD. +func nextDecomposed(i *Iter, out []byte) int { + var outp int + i.initNext(len(out), i.p) +doFast: + inCopyStart, outCopyStart := i.p, outp // invariant xCopyStart <= i.xStart + for { + if sz := int(i.info.size); sz <= 1 { + // ASCII or illegal byte. Either way, advance by 1. + i.p++ + outp++ + max := min(i.rb.nsrc, len(out)-outp+i.p) + if np := i.rb.src.skipASCII(i.p, max); np > i.p { + outp += np - i.p + i.p = np + if i.p >= i.rb.nsrc { + break + } + // ASCII may combine with consecutive runes. + if i.setStart(outp-1, i.p-1) { + i.p-- + outp-- + i.info.size = 1 + break + } + } + } else if d := i.info.decomposition(); d != nil { + i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p) + p := outp + len(d) + if p > i.maxseg && i.setStart(outp, i.p) { + return outp + } + copy(out[outp:], d) + outp = p + i.p += sz + inCopyStart, outCopyStart = i.p, outp + } else if r := i.rb.src.hangul(i.p); r != 0 { + i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p) + for { + outp += decomposeHangul(out[outp:], r) + i.p += hangulUTF8Size + if r = i.rb.src.hangul(i.p); r == 0 { + break + } + if i.setStart(outp, i.p) { + return outp + } + } + inCopyStart, outCopyStart = i.p, outp + } else { + p := outp + sz + if p > i.maxseg && i.setStart(outp, i.p) { + break + } + outp = p + i.p += sz + } + if i.p >= i.rb.nsrc { + break + } + prevCC := i.info.tccc + i.info = i.rb.f.info(i.rb.src, i.p) + if cc := i.info.ccc; cc == 0 { + if i.setStart(outp, i.p) { + break + } + } else if cc < prevCC { + goto doNorm + } + } + if inCopyStart != i.p { + i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p) + } + i.done = i.p >= i.rb.nsrc + return outp +doNorm: + // Insert what we have decomposed so far in the reorderBuffer. + // As we will only reorder, there will always be enough room. + i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p) + if !i.rb.insertDecomposed(out[i.outStart:outp]) { + // Start over to prevent decompositions from crossing segment boundaries. + // This is a rare occurance. + i.p = i.inStart + i.info = i.rb.f.info(i.rb.src, i.p) + } + outp = i.outStart + for { + if !i.rb.insert(i.rb.src, i.p, i.info) { + break + } + if i.p += int(i.info.size); i.p >= i.rb.nsrc { + outp += i.rb.flushCopy(out[outp:]) + i.done = true + return outp + } + i.info = i.rb.f.info(i.rb.src, i.p) + if i.info.ccc == 0 { + break + } + } + // new segment or too many combining characters: exit normalization + if outp += i.rb.flushCopy(out[outp:]); i.setStart(outp, i.p) { + return outp + } + goto doFast +} + +// nextComposed is the implementation of Next for forms NFC and NFKC. +func nextComposed(i *Iter, out []byte) int { + var outp int + i.initNext(len(out), i.p) +doFast: + inCopyStart, outCopyStart := i.p, outp // invariant xCopyStart <= i.xStart + var prevCC uint8 + for { + if !i.info.isYesC() { + goto doNorm + } + if cc := i.info.ccc; cc == 0 { + if i.setStart(outp, i.p) { + break + } + } else if cc < prevCC { + goto doNorm + } + prevCC = i.info.tccc + sz := int(i.info.size) + if sz == 0 { + sz = 1 // illegal rune: copy byte-by-byte + } + p := outp + sz + if p > i.maxseg && i.setStart(outp, i.p) { + break + } + outp = p + i.p += sz + max := min(i.rb.nsrc, len(out)-outp+i.p) + if np := i.rb.src.skipASCII(i.p, max); np > i.p { + outp += np - i.p + i.p = np + if i.p >= i.rb.nsrc { + break + } + // ASCII may combine with consecutive runes. + if i.setStart(outp-1, i.p-1) { + i.p-- + outp-- + i.info = runeInfo{size: 1} + break + } + } + if i.p >= i.rb.nsrc { + break + } + i.info = i.rb.f.info(i.rb.src, i.p) + } + if inCopyStart != i.p { + i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p) + } + i.done = i.p >= i.rb.nsrc + return outp +doNorm: + i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.inStart) + outp, i.p = i.outStart, i.inStart + i.info = i.rb.f.info(i.rb.src, i.p) + for { + if !i.rb.insert(i.rb.src, i.p, i.info) { + break + } + if i.p += int(i.info.size); i.p >= i.rb.nsrc { + i.rb.compose() + outp += i.rb.flushCopy(out[outp:]) + i.done = true + return outp + } + i.info = i.rb.f.info(i.rb.src, i.p) + if i.info.boundaryBefore() { + break + } + } + i.rb.compose() + if outp += i.rb.flushCopy(out[outp:]); i.setStart(outp, i.p) { + return outp + } + goto doFast +} diff --git a/libgo/go/exp/norm/iter_test.go b/libgo/go/exp/norm/iter_test.go new file mode 100644 index 00000000000..f6e8d817251 --- /dev/null +++ b/libgo/go/exp/norm/iter_test.go @@ -0,0 +1,186 @@ +// 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 norm + +import ( + "strings" + "testing" +) + +var iterBufSizes = []int{ + MaxSegmentSize, + 1.5 * MaxSegmentSize, + 2 * MaxSegmentSize, + 3 * MaxSegmentSize, + 100 * MaxSegmentSize, +} + +func doIterNorm(f Form, buf []byte, s string) []byte { + acc := []byte{} + i := Iter{} + i.SetInputString(f, s) + for !i.Done() { + n := i.Next(buf) + acc = append(acc, buf[:n]...) + } + return acc +} + +func runIterTests(t *testing.T, name string, f Form, tests []AppendTest, norm bool) { + for i, test := range tests { + in := test.left + test.right + gold := test.out + if norm { + gold = string(f.AppendString(nil, test.out)) + } + for _, sz := range iterBufSizes { + buf := make([]byte, sz) + out := string(doIterNorm(f, buf, in)) + if len(out) != len(gold) { + const msg = "%s:%d:%d: length is %d; want %d" + t.Errorf(msg, name, i, sz, len(out), len(gold)) + } + if out != gold { + // Find first rune that differs and show context. + ir := []rune(out) + ig := []rune(gold) + for j := 0; j < len(ir) && j < len(ig); j++ { + if ir[j] == ig[j] { + continue + } + if j -= 3; j < 0 { + j = 0 + } + for e := j + 7; j < e && j < len(ir) && j < len(ig); j++ { + const msg = "%s:%d:%d: runeAt(%d) = %U; want %U" + t.Errorf(msg, name, i, sz, j, ir[j], ig[j]) + } + break + } + } + } + } +} + +func rep(r rune, n int) string { + return strings.Repeat(string(r), n) +} + +var iterTests = []AppendTest{ + {"", ascii, ascii}, + {"", txt_all, txt_all}, + {"", "a" + rep(0x0300, MaxSegmentSize/2), "a" + rep(0x0300, MaxSegmentSize/2)}, +} + +var iterTestsD = []AppendTest{ + { // segment overflow on unchanged character + "", + "a" + rep(0x0300, MaxSegmentSize/2) + "\u0316", + "a" + rep(0x0300, MaxSegmentSize/2-1) + "\u0316\u0300", + }, + { // segment overflow on unchanged character + start value + "", + "a" + rep(0x0300, MaxSegmentSize/2+maxCombiningChars+4) + "\u0316", + "a" + rep(0x0300, MaxSegmentSize/2+maxCombiningChars) + "\u0316" + rep(0x300, 4), + }, + { // segment overflow on decomposition + "", + "a" + rep(0x0300, MaxSegmentSize/2-1) + "\u0340", + "a" + rep(0x0300, MaxSegmentSize/2), + }, + { // segment overflow on decomposition + start value + "", + "a" + rep(0x0300, MaxSegmentSize/2-1) + "\u0340" + rep(0x300, maxCombiningChars+4) + "\u0320", + "a" + rep(0x0300, MaxSegmentSize/2-1) + rep(0x300, maxCombiningChars+1) + "\u0320" + rep(0x300, 4), + }, + { // start value after ASCII overflow + "", + rep('a', MaxSegmentSize) + rep(0x300, maxCombiningChars+2) + "\u0320", + rep('a', MaxSegmentSize) + rep(0x300, maxCombiningChars) + "\u0320\u0300\u0300", + }, + { // start value after Hangul overflow + "", + rep(0xAC00, MaxSegmentSize/6) + rep(0x300, maxCombiningChars+2) + "\u0320", + strings.Repeat("\u1100\u1161", MaxSegmentSize/6) + rep(0x300, maxCombiningChars-1) + "\u0320" + rep(0x300, 3), + }, + { // start value after cc=0 + "", + "您您" + rep(0x300, maxCombiningChars+4) + "\u0320", + "您您" + rep(0x300, maxCombiningChars) + "\u0320" + rep(0x300, 4), + }, + { // start value after normalization + "", + "\u0300\u0320a" + rep(0x300, maxCombiningChars+4) + "\u0320", + "\u0320\u0300a" + rep(0x300, maxCombiningChars) + "\u0320" + rep(0x300, 4), + }, +} + +var iterTestsC = []AppendTest{ + { // ordering of non-composing combining characters + "", + "\u0305\u0316", + "\u0316\u0305", + }, + { // segment overflow + "", + "a" + rep(0x0305, MaxSegmentSize/2+4) + "\u0316", + "a" + rep(0x0305, MaxSegmentSize/2-1) + "\u0316" + rep(0x305, 5), + }, +} + +func TestIterNextD(t *testing.T) { + runIterTests(t, "IterNextD1", NFKD, appendTests, true) + runIterTests(t, "IterNextD2", NFKD, iterTests, true) + runIterTests(t, "IterNextD3", NFKD, iterTestsD, false) +} + +func TestIterNextC(t *testing.T) { + runIterTests(t, "IterNextC1", NFKC, appendTests, true) + runIterTests(t, "IterNextC2", NFKC, iterTests, true) + runIterTests(t, "IterNextC3", NFKC, iterTestsC, false) +} + +type SegmentTest struct { + in string + out []string +} + +var segmentTests = []SegmentTest{ + {rep('a', MaxSegmentSize), []string{rep('a', MaxSegmentSize), ""}}, + {rep('a', MaxSegmentSize+2), []string{rep('a', MaxSegmentSize-1), "aaa", ""}}, + {rep('a', MaxSegmentSize) + "\u0300aa", []string{rep('a', MaxSegmentSize-1), "a\u0300", "aa", ""}}, +} + +// Note that, by design, segmentation is equal for composing and decomposing forms. +func TestIterSegmentation(t *testing.T) { + segmentTest(t, "SegmentTestD", NFD, segmentTests) + segmentTest(t, "SegmentTestC", NFC, segmentTests) +} + +func segmentTest(t *testing.T, name string, f Form, tests []SegmentTest) { + iter := Iter{} + for i, tt := range segmentTests { + buf := make([]byte, MaxSegmentSize) + iter.SetInputString(f, tt.in) + for j, seg := range tt.out { + if seg == "" { + if !iter.Done() { + n := iter.Next(buf) + res := string(buf[:n]) + t.Errorf(`%s:%d:%d: expected Done()==true, found segment "%s"`, name, i, j, res) + } + continue + } + if iter.Done() { + t.Errorf("%s:%d:%d: Done()==true, want false", name, i, j) + } + n := iter.Next(buf) + seg = f.String(seg) + if res := string(buf[:n]); res != seg { + t.Errorf(`%s:%d:%d" segment was "%s" (%d); want "%s" (%d)`, name, i, j, res, len(res), seg, len(seg)) + } + } + } +} diff --git a/libgo/go/exp/norm/normalize.go b/libgo/go/exp/norm/normalize.go index 030d900918e..b5cd44abfa0 100644 --- a/libgo/go/exp/norm/normalize.go +++ b/libgo/go/exp/norm/normalize.go @@ -243,7 +243,7 @@ func quickSpan(rb *reorderBuffer, i int) int { lastSegStart := i src, n := rb.src, rb.nsrc for i < n { - if j := src.skipASCII(i); i != j { + if j := src.skipASCII(i, n); i != j { i = j lastSegStart = i - 1 lastCC = 0 @@ -448,11 +448,16 @@ func decomposeToLastBoundary(rb *reorderBuffer, buf []byte) []byte { } // Check that decomposition doesn't result in overflow. if info.hasDecomposition() { - dcomp := info.decomposition() - for i := 0; i < len(dcomp); { - inf := rb.f.info(inputBytes(dcomp), i) - i += int(inf.size) + if isHangul(buf) { + i += int(info.size) n++ + } else { + dcomp := info.decomposition() + for i := 0; i < len(dcomp); { + inf := rb.f.info(inputBytes(dcomp), i) + i += int(inf.size) + n++ + } } } else { n++ diff --git a/libgo/go/exp/norm/normalize_test.go b/libgo/go/exp/norm/normalize_test.go index c7d5e08fca0..8b970598b4d 100644 --- a/libgo/go/exp/norm/normalize_test.go +++ b/libgo/go/exp/norm/normalize_test.go @@ -5,6 +5,7 @@ package norm import ( + "bytes" "strings" "testing" ) @@ -495,15 +496,40 @@ func TestAppend(t *testing.T) { runAppendTests(t, "TestString", NFKC, stringF, appendTests) } +func appendBench(f Form, in []byte) func() { + buf := make([]byte, 0, 4*len(in)) + return func() { + f.Append(buf, in...) + } +} + +func iterBench(f Form, in []byte) func() { + buf := make([]byte, 4*len(in)) + iter := Iter{} + return func() { + iter.SetInput(f, in) + for !iter.Done() { + iter.Next(buf) + } + } +} + +func appendBenchmarks(bm []func(), f Form, in []byte) []func() { + //bm = append(bm, appendBench(f, in)) + bm = append(bm, iterBench(f, in)) + return bm +} + func doFormBenchmark(b *testing.B, inf, f Form, s string) { b.StopTimer() in := inf.Bytes([]byte(s)) - buf := make([]byte, 2*len(in)) - b.SetBytes(int64(len(in))) + bm := appendBenchmarks(nil, f, in) + b.SetBytes(int64(len(in) * len(bm))) b.StartTimer() for i := 0; i < b.N; i++ { - buf = f.Append(buf[0:0], in...) - buf = buf[0:0] + for _, fn := range bm { + fn() + } } } @@ -549,17 +575,21 @@ func BenchmarkNormalizeHangulNFD2NFD(b *testing.B) { doFormBenchmark(b, NFD, NFD, txt_kr) } +var forms = []Form{NFC, NFD, NFKC, NFKD} + func doTextBenchmark(b *testing.B, s string) { b.StopTimer() - b.SetBytes(int64(len(s)) * 4) in := []byte(s) - var buf = make([]byte, 0, 2*len(in)) + bm := []func(){} + for _, f := range forms { + bm = appendBenchmarks(bm, f, in) + } + b.SetBytes(int64(len(s) * len(bm))) b.StartTimer() for i := 0; i < b.N; i++ { - NFC.Append(buf, in...) - NFD.Append(buf, in...) - NFKC.Append(buf, in...) - NFKD.Append(buf, in...) + for _, f := range bm { + f() + } } } @@ -584,6 +614,11 @@ func BenchmarkJapanese(b *testing.B) { func BenchmarkChinese(b *testing.B) { doTextBenchmark(b, txt_cn) } +func BenchmarkOverflow(b *testing.B) { + doTextBenchmark(b, overflow) +} + +var overflow = string(bytes.Repeat([]byte("\u035D"), 4096)) + "\u035B" // Tests sampled from the Canonical ordering tests (Part 2) of // http://unicode.org/Public/UNIDATA/NormalizationTest.txt diff --git a/libgo/go/exp/norm/normregtest.go b/libgo/go/exp/norm/normregtest.go index c2ab25bc99d..507de1ae834 100644 --- a/libgo/go/exp/norm/normregtest.go +++ b/libgo/go/exp/norm/normregtest.go @@ -220,6 +220,17 @@ func cmpIsNormal(t *Test, name string, f norm.Form, test string, result, want bo func doTest(t *Test, f norm.Form, gold, test string) { result := f.Bytes([]byte(test)) cmpResult(t, "Bytes", f, gold, test, string(result)) + sresult := f.String(test) + cmpResult(t, "String", f, gold, test, sresult) + buf := make([]byte, norm.MaxSegmentSize) + acc := []byte{} + i := norm.Iter{} + i.SetInputString(f, test) + for !i.Done() { + n := i.Next(buf) + acc = append(acc, buf[:n]...) + } + cmpResult(t, "Iter.Next", f, gold, test, string(acc)) for i := range test { out := f.Append(f.Bytes([]byte(test[:i])), []byte(test[i:])...) cmpResult(t, fmt.Sprintf(":Append:%d", i), f, gold, test, string(out)) diff --git a/libgo/go/exp/proxy/socks5.go b/libgo/go/exp/proxy/socks5.go index 466e135eb10..62fa5c9296d 100644 --- a/libgo/go/exp/proxy/socks5.go +++ b/libgo/go/exp/proxy/socks5.go @@ -98,9 +98,9 @@ func (s *socks5) Dial(network, addr string) (net.Conn, error) { buf = append(buf, socks5Version) if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { - buf = append(buf, 2, /* num auth methods */ socks5AuthNone, socks5AuthPassword) + buf = append(buf, 2 /* num auth methods */, socks5AuthNone, socks5AuthPassword) } else { - buf = append(buf, 1, /* num auth methods */ socks5AuthNone) + buf = append(buf, 1 /* num auth methods */, socks5AuthNone) } if _, err = conn.Write(buf); err != nil { @@ -139,7 +139,7 @@ func (s *socks5) Dial(network, addr string) (net.Conn, error) { } buf = buf[:0] - buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */ ) + buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */) if ip := net.ParseIP(host); ip != nil { if len(ip) == 4 { diff --git a/libgo/go/exp/terminal/terminal.go b/libgo/go/exp/terminal/terminal.go index c3ba5bde2ee..c1ed0c0c443 100644 --- a/libgo/go/exp/terminal/terminal.go +++ b/libgo/go/exp/terminal/terminal.go @@ -389,12 +389,12 @@ func (t *Terminal) Write(buf []byte) (n int, err error) { // We have a prompt and possibly user input on the screen. We // have to clear it first. - t.move(0, /* up */ 0, /* down */ t.cursorX, /* left */ 0 /* right */ ) + t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */) t.cursorX = 0 t.clearLineToRight() for t.cursorY > 0 { - t.move(1, /* up */ 0, 0, 0) + t.move(1 /* up */, 0, 0, 0) t.cursorY-- t.clearLineToRight() } diff --git a/libgo/go/exp/winfsnotify/winfsnotify_test.go b/libgo/go/exp/winfsnotify/winfsnotify_test.go index 59ac1624a26..4a1929a8397 100644 --- a/libgo/go/exp/winfsnotify/winfsnotify_test.go +++ b/libgo/go/exp/winfsnotify/winfsnotify_test.go @@ -7,6 +7,7 @@ package winfsnotify import ( + "io/ioutil" "os" "testing" "time" @@ -115,7 +116,13 @@ func TestNotifyClose(t *testing.T) { t.Fatal("double Close() test failed: second Close() call didn't return") } - err := watcher.Watch("_test") + dir, err := ioutil.TempDir("", "wininotify") + if err != nil { + t.Fatalf("TempDir failed: %s", err) + } + defer os.RemoveAll(dir) + + err = watcher.Watch(dir) if err == nil { t.Fatal("expected error on Watch() after Close(), got nil") } diff --git a/libgo/go/go/doc/example.go b/libgo/go/go/doc/example.go index 1c23b0d95c3..a7e0e250a2e 100644 --- a/libgo/go/go/doc/example.go +++ b/libgo/go/go/doc/example.go @@ -2,28 +2,31 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Extract example functions from package ASTs. +// Extract example functions from file ASTs. package doc import ( "go/ast" - "go/printer" "go/token" + "regexp" + "sort" "strings" "unicode" "unicode/utf8" ) type Example struct { - Name string // name of the item being demonstrated - Body *printer.CommentedNode // code - Output string // expected output + Name string // name of the item being exemplified + Doc string // example function doc string + Code ast.Node + Comments []*ast.CommentGroup + Output string // expected output } -func Examples(pkg *ast.Package) []*Example { +func Examples(files ...*ast.File) []*Example { var list []*Example - for _, file := range pkg.Files { + for _, file := range files { hasTests := false // file contains tests or benchmarks numDecl := 0 // number of non-import declarations in the file var flist []*Example @@ -45,26 +48,54 @@ func Examples(pkg *ast.Package) []*Example { if !isTest(name, "Example") { continue } + var doc string + if f.Doc != nil { + doc = f.Doc.Text() + } flist = append(flist, &Example{ - Name: name[len("Example"):], - Body: &printer.CommentedNode{ - Node: f.Body, - Comments: file.Comments, - }, - Output: f.Doc.Text(), + Name: name[len("Example"):], + Doc: doc, + Code: f.Body, + Comments: file.Comments, + Output: exampleOutput(f, file.Comments), }) } if !hasTests && numDecl > 1 && len(flist) == 1 { // If this file only has one example function, some // other top-level declarations, and no tests or // benchmarks, use the whole file as the example. - flist[0].Body.Node = file + flist[0].Code = file } list = append(list, flist...) } + sort.Sort(exampleByName(list)) return list } +var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`) + +func exampleOutput(fun *ast.FuncDecl, comments []*ast.CommentGroup) string { + // find the last comment in the function + var last *ast.CommentGroup + for _, cg := range comments { + if cg.Pos() < fun.Pos() { + continue + } + if cg.End() > fun.End() { + break + } + last = cg + } + if last != nil { + // test that it begins with the correct prefix + text := last.Text() + if loc := outputPrefix.FindStringIndex(text); loc != nil { + return strings.TrimSpace(text[loc[1]:]) + } + } + return "" // no suitable comment found +} + // isTest tells whether name looks like a test, example, or benchmark. // It is a Test (say) if there is a character after Test that is not a // lower-case letter. (We don't want Testiness.) @@ -78,3 +109,9 @@ func isTest(name, prefix string) bool { rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) return !unicode.IsLower(rune) } + +type exampleByName []*Example + +func (s exampleByName) Len() int { return len(s) } +func (s exampleByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s exampleByName) Less(i, j int) bool { return s[i].Name < s[j].Name } diff --git a/libgo/go/go/doc/reader.go b/libgo/go/go/doc/reader.go index bdfb294adb0..3558892ebd0 100644 --- a/libgo/go/go/doc/reader.go +++ b/libgo/go/go/doc/reader.go @@ -439,8 +439,10 @@ func (r *reader) readFile(src *ast.File) { // gets to (re-)use the declaration documentation // if there's none associated with the spec itself fake := &ast.GenDecl{ - d.Doc, d.Pos(), token.TYPE, token.NoPos, - []ast.Spec{s}, token.NoPos, + Doc: d.Doc, + TokPos: d.Pos(), + Tok: token.TYPE, + Specs: []ast.Spec{s}, } r.readType(fake, s) } @@ -460,7 +462,7 @@ func (r *reader) readFile(src *ast.File) { // non-empty BUG comment; collect comment without BUG prefix list := append([]*ast.Comment(nil), c.List...) // make a copy list[0].Text = text[m[1]:] - r.bugs = append(r.bugs, (&ast.CommentGroup{list}).Text()) + r.bugs = append(r.bugs, (&ast.CommentGroup{List: list}).Text()) } } } @@ -530,7 +532,7 @@ func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int) _, origRecvIsPtr := newField.Type.(*ast.StarExpr) var typ ast.Expr = ast.NewIdent(recvTypeName) if !embeddedIsPtr && origRecvIsPtr { - typ = &ast.StarExpr{token.NoPos, typ} + typ = &ast.StarExpr{X: typ} } newField.Type = typ diff --git a/libgo/go/go/doc/synopsis.go b/libgo/go/go/doc/synopsis.go new file mode 100644 index 00000000000..2192d78c0cd --- /dev/null +++ b/libgo/go/go/doc/synopsis.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 doc + +import "unicode" + +// firstSentenceLen returns the length of the first sentence in s. +// The sentence ends after the first period followed by space and +// not preceded by exactly one uppercase letter. +// +func firstSentenceLen(s string) int { + var ppp, pp, p rune + for i, q := range s { + if q == '\n' || q == '\r' || q == '\t' { + q = ' ' + } + if q == ' ' && p == '.' && (!unicode.IsUpper(pp) || unicode.IsUpper(ppp)) { + return i + } + ppp, pp, p = pp, p, q + } + return len(s) +} + +// Synopsis returns a cleaned version of the first sentence in s. +// That sentence ends after the first period followed by space and +// not preceded by exactly one uppercase letter. The result string +// has no \n, \r, or \t characters and uses only single spaces between +// words. +// +func Synopsis(s string) string { + n := firstSentenceLen(s) + var b []byte + p := byte(' ') + for i := 0; i < n; i++ { + q := s[i] + if q == '\n' || q == '\r' || q == '\t' { + q = ' ' + } + if q != ' ' || p != ' ' { + b = append(b, q) + p = q + } + } + // remove trailing blank, if any + if n := len(b); n > 0 && p == ' ' { + b = b[0 : n-1] + } + return string(b) +} diff --git a/libgo/go/go/doc/synopsis_test.go b/libgo/go/go/doc/synopsis_test.go new file mode 100644 index 00000000000..dfc6598af47 --- /dev/null +++ b/libgo/go/go/doc/synopsis_test.go @@ -0,0 +1,44 @@ +// 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 doc + +import "testing" + +var tests = []struct { + txt string + fsl int + syn string +}{ + {"", 0, ""}, + {"foo", 3, "foo"}, + {"foo.", 4, "foo."}, + {"foo.bar", 7, "foo.bar"}, + {" foo. ", 6, "foo."}, + {" foo\t bar.\n", 12, "foo bar."}, + {" foo\t bar.\n", 12, "foo bar."}, + {"a b\n\nc\r\rd\t\t", 12, "a b c d"}, + {"a b\n\nc\r\rd\t\t . BLA", 15, "a b c d ."}, + {"Package poems by T.S.Eliot. To rhyme...", 27, "Package poems by T.S.Eliot."}, + {"Package poems by T. S. Eliot. To rhyme...", 29, "Package poems by T. S. Eliot."}, + {"foo implements the foo ABI. The foo ABI is...", 27, "foo implements the foo ABI."}, + {"Package\nfoo. ..", 12, "Package foo."}, + {"P . Q.", 3, "P ."}, + {"P. Q. ", 8, "P. Q."}, + {"Package Καλημέρα κόσμε.", 36, "Package Καλημέρα κόσμε."}, + {"Package こんにちは 世界\n", 31, "Package こんにちは 世界"}, +} + +func TestSynopsis(t *testing.T) { + for _, e := range tests { + fsl := firstSentenceLen(e.txt) + if fsl != e.fsl { + t.Errorf("got fsl = %d; want %d for %q\n", fsl, e.fsl, e.txt) + } + syn := Synopsis(e.txt) + if syn != e.syn { + t.Errorf("got syn = %q; want %q for %q\n", syn, e.syn, e.txt) + } + } +} diff --git a/libgo/go/go/doc/testdata/benchmark.go b/libgo/go/go/doc/testdata/benchmark.go index 0bf567b7c4d..0aded5bb4c7 100644 --- a/libgo/go/go/doc/testdata/benchmark.go +++ b/libgo/go/go/doc/testdata/benchmark.go @@ -16,7 +16,7 @@ var matchBenchmarks = flag.String("test.bench", "", "regular expression to selec var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds") // An internal type but exported because it is cross-package; part of the implementation -// of gotest. +// of go test. type InternalBenchmark struct { Name string F func(b *B) @@ -213,7 +213,7 @@ func (r BenchmarkResult) String() string { } // An internal function but exported because it is cross-package; part of the implementation -// of gotest. +// of go test. func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) { // If no flag was specified, don't run benchmarks. if len(*matchBenchmarks) == 0 { @@ -281,7 +281,7 @@ func (b *B) trimOutput() { } // Benchmark benchmarks a single function. Useful for creating -// custom benchmarks that do not use gotest. +// custom benchmarks that do not use go test. func Benchmark(f func(b *B)) BenchmarkResult { b := &B{ common: common{ diff --git a/libgo/go/go/doc/testdata/testing.1.golden b/libgo/go/go/doc/testdata/testing.1.golden index 1f92f8fe3e1..d26a4685ca0 100644 --- a/libgo/go/go/doc/testdata/testing.1.golden +++ b/libgo/go/go/doc/testdata/testing.1.golden @@ -27,7 +27,7 @@ VARIABLES // The short flag requests that tests run more quickly, but its functionality // is provided by test writers themselves. The testing package is just its // home. The all.bash installation script sets it to make installation more - // efficient, but by default the flag is off so a plain "gotest" will do a + // efficient, but by default the flag is off so a plain "go test" will do a // full test of the package. short = flag.Bool("test.short", false, "run smaller test suite to save time") diff --git a/libgo/go/go/doc/testdata/testing.go b/libgo/go/go/doc/testdata/testing.go index cfe212dc1d7..71c1d1eaf0e 100644 --- a/libgo/go/go/doc/testdata/testing.go +++ b/libgo/go/go/doc/testdata/testing.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package testing provides support for automated testing of Go packages. -// It is intended to be used in concert with the ``gotest'' utility, which automates +// It is intended to be used in concert with the ``go test'' utility, which automates // execution of any function of the form // func TestXxx(*testing.T) // where Xxx can be any alphanumeric string (but the first letter must not be in @@ -12,7 +12,7 @@ // // Functions of the form // func BenchmarkXxx(*testing.B) -// are considered benchmarks, and are executed by gotest when the -test.bench +// are considered benchmarks, and are executed by go test when the -test.bench // flag is provided. // // A sample benchmark function looks like this: @@ -53,7 +53,7 @@ var ( // The short flag requests that tests run more quickly, but its functionality // is provided by test writers themselves. The testing package is just its // home. The all.bash installation script sets it to make installation more - // efficient, but by default the flag is off so a plain "gotest" will do a + // efficient, but by default the flag is off so a plain "go test" will do a // full test of the package. short = flag.Bool("test.short", false, "run smaller test suite to save time") @@ -205,7 +205,7 @@ func (t *T) Parallel() { } // An internal type but exported because it is cross-package; part of the implementation -// of gotest. +// of go test. type InternalTest struct { Name string F func(*T) @@ -227,7 +227,7 @@ func tRunner(t *T, test *InternalTest) { } // An internal function but exported because it is cross-package; part of the implementation -// of gotest. +// of go test. func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) { flag.Parse() parseCpuList() diff --git a/libgo/go/go/parser/parser.go b/libgo/go/go/parser/parser.go index 6a0b61eb48c..c1e61904486 100644 --- a/libgo/go/go/parser/parser.go +++ b/libgo/go/go/parser/parser.go @@ -249,7 +249,7 @@ func (p *parser) consumeComment() (comment *ast.Comment, endline int) { } } - comment = &ast.Comment{p.pos, p.lit} + comment = &ast.Comment{Slash: p.pos, Text: p.lit} p.next0() return @@ -270,7 +270,7 @@ func (p *parser) consumeCommentGroup() (comments *ast.CommentGroup, endline int) } // add comment group to the comments list - comments = &ast.CommentGroup{list} + comments = &ast.CommentGroup{List: list} p.comments = append(p.comments, comments) return @@ -391,7 +391,7 @@ func (p *parser) parseIdent() *ast.Ident { } else { p.expect(token.IDENT) // use expect() error handling } - return &ast.Ident{pos, name, nil} + return &ast.Ident{NamePos: pos, Name: name} } func (p *parser) parseIdentList() (list []*ast.Ident) { @@ -469,7 +469,7 @@ func (p *parser) parseType() ast.Expr { pos := p.pos p.errorExpected(pos, "type") p.next() // make progress - return &ast.BadExpr{pos, p.pos} + return &ast.BadExpr{From: pos, To: p.pos} } return typ @@ -489,7 +489,7 @@ func (p *parser) parseTypeName() ast.Expr { p.next() p.resolve(ident) sel := p.parseIdent() - return &ast.SelectorExpr{ident, sel} + return &ast.SelectorExpr{X: ident, Sel: sel} } return ident @@ -503,7 +503,7 @@ func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr { lbrack := p.expect(token.LBRACK) var len ast.Expr if ellipsisOk && p.tok == token.ELLIPSIS { - len = &ast.Ellipsis{p.pos, nil} + len = &ast.Ellipsis{Ellipsis: p.pos} p.next() } else if p.tok != token.RBRACK { len = p.parseRhs() @@ -511,7 +511,7 @@ func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr { p.expect(token.RBRACK) elt := p.parseType() - return &ast.ArrayType{lbrack, len, elt} + return &ast.ArrayType{Lbrack: lbrack, Len: len, Elt: elt} } func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident { @@ -521,7 +521,7 @@ func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident { if !isIdent { pos := x.Pos() p.errorExpected(pos, "identifier") - ident = &ast.Ident{pos, "_", nil} + ident = &ast.Ident{NamePos: pos, Name: "_"} } idents[i] = ident } @@ -541,7 +541,7 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field { // optional tag var tag *ast.BasicLit if p.tok == token.STRING { - tag = &ast.BasicLit{p.pos, p.tok, p.lit} + tag = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit} p.next() } @@ -557,13 +557,13 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field { if n := len(list); n > 1 || !isTypeName(deref(typ)) { pos := typ.Pos() p.errorExpected(pos, "anonymous field") - typ = &ast.BadExpr{pos, list[n-1].End()} + typ = &ast.BadExpr{From: pos, To: list[n-1].End()} } } p.expectSemi() // call before accessing p.linecomment - field := &ast.Field{doc, idents, typ, tag, p.lineComment} + field := &ast.Field{Doc: doc, Names: idents, Type: typ, Tag: tag, Comment: p.lineComment} p.declare(field, nil, scope, ast.Var, idents...) return field @@ -586,7 +586,14 @@ func (p *parser) parseStructType() *ast.StructType { } rbrace := p.expect(token.RBRACE) - return &ast.StructType{pos, &ast.FieldList{lbrace, list, rbrace}, false} + return &ast.StructType{ + Struct: pos, + Fields: &ast.FieldList{ + Opening: lbrace, + List: list, + Closing: rbrace, + }, + } } func (p *parser) parsePointerType() *ast.StarExpr { @@ -597,7 +604,7 @@ func (p *parser) parsePointerType() *ast.StarExpr { star := p.expect(token.MUL) base := p.parseType() - return &ast.StarExpr{star, base} + return &ast.StarExpr{Star: star, X: base} } func (p *parser) tryVarType(isParam bool) ast.Expr { @@ -607,9 +614,9 @@ func (p *parser) tryVarType(isParam bool) ast.Expr { typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message if typ == nil { p.error(pos, "'...' parameter is missing type") - typ = &ast.BadExpr{pos, p.pos} + typ = &ast.BadExpr{From: pos, To: p.pos} } - return &ast.Ellipsis{pos, typ} + return &ast.Ellipsis{Ellipsis: pos, Elt: typ} } return p.tryIdentOrType(false) } @@ -620,7 +627,7 @@ func (p *parser) parseVarType(isParam bool) ast.Expr { pos := p.pos p.errorExpected(pos, "type") p.next() // make progress - typ = &ast.BadExpr{pos, p.pos} + typ = &ast.BadExpr{From: pos, To: p.pos} } return typ } @@ -661,7 +668,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [ if typ != nil { // IdentifierList Type idents := p.makeIdentList(list) - field := &ast.Field{nil, idents, typ, nil, nil} + field := &ast.Field{Names: idents, Type: typ} params = append(params, field) // Go spec: The scope of an identifier denoting a function // parameter or result variable is the function body. @@ -673,7 +680,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [ for p.tok != token.RPAREN && p.tok != token.EOF { idents := p.parseIdentList() typ := p.parseVarType(ellipsisOk) - field := &ast.Field{nil, idents, typ, nil, nil} + field := &ast.Field{Names: idents, Type: typ} params = append(params, field) // Go spec: The scope of an identifier denoting a function // parameter or result variable is the function body. @@ -708,7 +715,7 @@ func (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) *ast.FieldLi } rparen := p.expect(token.RPAREN) - return &ast.FieldList{lparen, params, rparen} + return &ast.FieldList{Opening: lparen, List: params, Closing: rparen} } func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList { @@ -750,7 +757,7 @@ func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) { scope := ast.NewScope(p.topScope) // function scope params, results := p.parseSignature(scope) - return &ast.FuncType{pos, params, results}, scope + return &ast.FuncType{Func: pos, Params: params, Results: results}, scope } func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field { @@ -767,7 +774,7 @@ func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field { idents = []*ast.Ident{ident} scope := ast.NewScope(nil) // method scope params, results := p.parseSignature(scope) - typ = &ast.FuncType{token.NoPos, params, results} + typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results} } else { // embedded interface typ = x @@ -775,7 +782,7 @@ func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field { } p.expectSemi() // call before accessing p.linecomment - spec := &ast.Field{doc, idents, typ, nil, p.lineComment} + spec := &ast.Field{Doc: doc, Names: idents, Type: typ, Comment: p.lineComment} p.declare(spec, nil, scope, ast.Fun, idents...) return spec @@ -795,7 +802,14 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType { } rbrace := p.expect(token.RBRACE) - return &ast.InterfaceType{pos, &ast.FieldList{lbrace, list, rbrace}, false} + return &ast.InterfaceType{ + Interface: pos, + Methods: &ast.FieldList{ + Opening: lbrace, + List: list, + Closing: rbrace, + }, + } } func (p *parser) parseMapType() *ast.MapType { @@ -809,7 +823,7 @@ func (p *parser) parseMapType() *ast.MapType { p.expect(token.RBRACK) value := p.parseType() - return &ast.MapType{pos, key, value} + return &ast.MapType{Map: pos, Key: key, Value: value} } func (p *parser) parseChanType() *ast.ChanType { @@ -832,7 +846,7 @@ func (p *parser) parseChanType() *ast.ChanType { } value := p.parseType() - return &ast.ChanType{pos, dir, value} + return &ast.ChanType{Begin: pos, Dir: dir, Value: value} } // If the result is an identifier, it is not resolved. @@ -860,7 +874,7 @@ func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr { p.next() typ := p.parseType() rparen := p.expect(token.RPAREN) - return &ast.ParenExpr{lparen, typ, rparen} + return &ast.ParenExpr{Lparen: lparen, X: typ, Rparen: rparen} } // no type found @@ -903,7 +917,7 @@ func (p *parser) parseBody(scope *ast.Scope) *ast.BlockStmt { p.closeScope() rbrace := p.expect(token.RBRACE) - return &ast.BlockStmt{lbrace, list, rbrace} + return &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace} } func (p *parser) parseBlockStmt() *ast.BlockStmt { @@ -917,7 +931,7 @@ func (p *parser) parseBlockStmt() *ast.BlockStmt { p.closeScope() rbrace := p.expect(token.RBRACE) - return &ast.BlockStmt{lbrace, list, rbrace} + return &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace} } // ---------------------------------------------------------------------------- @@ -938,7 +952,7 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr { body := p.parseBody(scope) p.exprLev-- - return &ast.FuncLit{typ, body} + return &ast.FuncLit{Type: typ, Body: body} } // parseOperand may return an expression or a raw type (incl. array @@ -959,7 +973,7 @@ func (p *parser) parseOperand(lhs bool) ast.Expr { return x case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING: - x := &ast.BasicLit{p.pos, p.tok, p.lit} + x := &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit} p.next() return x @@ -970,7 +984,7 @@ func (p *parser) parseOperand(lhs bool) ast.Expr { x := p.parseRhsOrType() // types may be parenthesized: (some type) p.exprLev-- rparen := p.expect(token.RPAREN) - return &ast.ParenExpr{lparen, x, rparen} + return &ast.ParenExpr{Lparen: lparen, X: x, Rparen: rparen} case token.FUNC: return p.parseFuncTypeOrLit() @@ -987,7 +1001,7 @@ func (p *parser) parseOperand(lhs bool) ast.Expr { pos := p.pos p.errorExpected(pos, "operand") p.next() // make progress - return &ast.BadExpr{pos, p.pos} + return &ast.BadExpr{From: pos, To: p.pos} } func (p *parser) parseSelector(x ast.Expr) ast.Expr { @@ -997,7 +1011,7 @@ func (p *parser) parseSelector(x ast.Expr) ast.Expr { sel := p.parseIdent() - return &ast.SelectorExpr{x, sel} + return &ast.SelectorExpr{X: x, Sel: sel} } func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr { @@ -1015,7 +1029,7 @@ func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr { } p.expect(token.RPAREN) - return &ast.TypeAssertExpr{x, typ} + return &ast.TypeAssertExpr{X: x, Type: typ} } func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr { @@ -1041,9 +1055,9 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr { rbrack := p.expect(token.RBRACK) if isSlice { - return &ast.SliceExpr{x, lbrack, low, high, rbrack} + return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: low, High: high, Rbrack: rbrack} } - return &ast.IndexExpr{x, lbrack, low, rbrack} + return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: low, Rbrack: rbrack} } func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr { @@ -1069,7 +1083,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr { p.exprLev-- rparen := p.expectClosing(token.RPAREN, "argument list") - return &ast.CallExpr{fun, lparen, list, ellipsis, rparen} + return &ast.CallExpr{Fun: fun, Lparen: lparen, Args: list, Ellipsis: ellipsis, Rparen: rparen} } func (p *parser) parseElement(keyOk bool) ast.Expr { @@ -1086,7 +1100,7 @@ func (p *parser) parseElement(keyOk bool) ast.Expr { if p.tok == token.COLON { colon := p.pos p.next() - return &ast.KeyValueExpr{x, colon, p.parseElement(false)} + return &ast.KeyValueExpr{Key: x, Colon: colon, Value: p.parseElement(false)} } p.resolve(x) // not a map key } @@ -1123,7 +1137,7 @@ func (p *parser) parseLiteralValue(typ ast.Expr) ast.Expr { } p.exprLev-- rbrace := p.expectClosing(token.RBRACE, "composite literal") - return &ast.CompositeLit{typ, lbrace, elts, rbrace} + return &ast.CompositeLit{Type: typ, Lbrace: lbrace, Elts: elts, Rbrace: rbrace} } // checkExpr checks that x is an expression (and not a type). @@ -1152,7 +1166,7 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr { default: // all other nodes are not proper expressions p.errorExpected(x.Pos(), "expression") - x = &ast.BadExpr{x.Pos(), x.End()} + x = &ast.BadExpr{From: x.Pos(), To: x.End()} } return x } @@ -1215,7 +1229,7 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr { case *ast.ArrayType: if len, isEllipsis := t.Len.(*ast.Ellipsis); isEllipsis { p.error(len.Pos(), "expected array length, found '...'") - x = &ast.BadExpr{x.Pos(), x.End()} + x = &ast.BadExpr{From: x.Pos(), To: x.End()} } } @@ -1247,7 +1261,7 @@ L: pos := p.pos p.next() // make progress p.errorExpected(pos, "selector or type assertion") - x = &ast.BadExpr{pos, p.pos} + x = &ast.BadExpr{From: pos, To: p.pos} } case token.LBRACK: if lhs { @@ -1288,7 +1302,7 @@ func (p *parser) parseUnaryExpr(lhs bool) ast.Expr { pos, op := p.pos, p.tok p.next() x := p.parseUnaryExpr(false) - return &ast.UnaryExpr{pos, op, p.checkExpr(x)} + return &ast.UnaryExpr{OpPos: pos, Op: op, X: p.checkExpr(x)} case token.ARROW: // channel type or receive expression @@ -1297,18 +1311,18 @@ func (p *parser) parseUnaryExpr(lhs bool) ast.Expr { if p.tok == token.CHAN { p.next() value := p.parseType() - return &ast.ChanType{pos, ast.RECV, value} + return &ast.ChanType{Begin: pos, Dir: ast.RECV, Value: value} } x := p.parseUnaryExpr(false) - return &ast.UnaryExpr{pos, token.ARROW, p.checkExpr(x)} + return &ast.UnaryExpr{OpPos: pos, Op: token.ARROW, X: p.checkExpr(x)} case token.MUL: // pointer type or unary "*" expression pos := p.pos p.next() x := p.parseUnaryExpr(false) - return &ast.StarExpr{pos, p.checkExprOrType(x)} + return &ast.StarExpr{Star: pos, X: p.checkExprOrType(x)} } return p.parsePrimaryExpr(lhs) @@ -1330,7 +1344,7 @@ func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr { lhs = false } y := p.parseBinaryExpr(false, prec+1) - x = &ast.BinaryExpr{p.checkExpr(x), pos, op, p.checkExpr(y)} + x = &ast.BinaryExpr{X: p.checkExpr(x), OpPos: pos, Op: op, Y: p.checkExpr(y)} } } @@ -1392,12 +1406,12 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) { if mode == rangeOk && p.tok == token.RANGE && (tok == token.DEFINE || tok == token.ASSIGN) { pos := p.pos p.next() - y = []ast.Expr{&ast.UnaryExpr{pos, token.RANGE, p.parseRhs()}} + y = []ast.Expr{&ast.UnaryExpr{OpPos: pos, Op: token.RANGE, X: p.parseRhs()}} isRange = true } else { y = p.parseRhsList() } - as := &ast.AssignStmt{x, pos, tok, y} + as := &ast.AssignStmt{Lhs: x, TokPos: pos, Tok: tok, Rhs: y} if tok == token.DEFINE { p.shortVarDecl(as, x) } @@ -1418,7 +1432,7 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) { // Go spec: The scope of a label is the body of the function // in which it is declared and excludes the body of any nested // function. - stmt := &ast.LabeledStmt{label, colon, p.parseStmt()} + stmt := &ast.LabeledStmt{Label: label, Colon: colon, Stmt: p.parseStmt()} p.declare(stmt, nil, p.labelScope, ast.Lbl, label) return stmt, false } @@ -1429,24 +1443,24 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) { // before the ':' that caused the problem. Thus, use the (latest) colon // position for error reporting. p.error(colon, "illegal label declaration") - return &ast.BadStmt{x[0].Pos(), colon + 1}, false + return &ast.BadStmt{From: x[0].Pos(), To: colon + 1}, false case token.ARROW: // send statement arrow := p.pos p.next() y := p.parseRhs() - return &ast.SendStmt{x[0], arrow, y}, false + return &ast.SendStmt{Chan: x[0], Arrow: arrow, Value: y}, false case token.INC, token.DEC: // increment or decrement - s := &ast.IncDecStmt{x[0], p.pos, p.tok} + s := &ast.IncDecStmt{X: x[0], TokPos: p.pos, Tok: p.tok} p.next() return s, false } // expression - return &ast.ExprStmt{x[0]}, false + return &ast.ExprStmt{X: x[0]}, false } func (p *parser) parseCallExpr() *ast.CallExpr { @@ -1467,10 +1481,10 @@ func (p *parser) parseGoStmt() ast.Stmt { call := p.parseCallExpr() p.expectSemi() if call == nil { - return &ast.BadStmt{pos, pos + 2} // len("go") + return &ast.BadStmt{From: pos, To: pos + 2} // len("go") } - return &ast.GoStmt{pos, call} + return &ast.GoStmt{Go: pos, Call: call} } func (p *parser) parseDeferStmt() ast.Stmt { @@ -1482,10 +1496,10 @@ func (p *parser) parseDeferStmt() ast.Stmt { call := p.parseCallExpr() p.expectSemi() if call == nil { - return &ast.BadStmt{pos, pos + 5} // len("defer") + return &ast.BadStmt{From: pos, To: pos + 5} // len("defer") } - return &ast.DeferStmt{pos, call} + return &ast.DeferStmt{Defer: pos, Call: call} } func (p *parser) parseReturnStmt() *ast.ReturnStmt { @@ -1501,7 +1515,7 @@ func (p *parser) parseReturnStmt() *ast.ReturnStmt { } p.expectSemi() - return &ast.ReturnStmt{pos, x} + return &ast.ReturnStmt{Return: pos, Results: x} } func (p *parser) parseBranchStmt(tok token.Token) *ast.BranchStmt { @@ -1519,7 +1533,7 @@ func (p *parser) parseBranchStmt(tok token.Token) *ast.BranchStmt { } p.expectSemi() - return &ast.BranchStmt{pos, tok, label} + return &ast.BranchStmt{TokPos: pos, Tok: tok, Label: label} } func (p *parser) makeExpr(s ast.Stmt) ast.Expr { @@ -1530,7 +1544,7 @@ func (p *parser) makeExpr(s ast.Stmt) ast.Expr { return p.checkExpr(es.X) } p.error(s.Pos(), "expected condition, found simple statement") - return &ast.BadExpr{s.Pos(), s.End()} + return &ast.BadExpr{From: s.Pos(), To: s.End()} } func (p *parser) parseIfStmt() *ast.IfStmt { @@ -1572,7 +1586,7 @@ func (p *parser) parseIfStmt() *ast.IfStmt { p.expectSemi() } - return &ast.IfStmt{pos, s, x, body, else_} + return &ast.IfStmt{If: pos, Init: s, Cond: x, Body: body, Else: else_} } func (p *parser) parseTypeList() (list []ast.Expr) { @@ -1612,7 +1626,7 @@ func (p *parser) parseCaseClause(typeSwitch bool) *ast.CaseClause { body := p.parseStmtList() p.closeScope() - return &ast.CaseClause{pos, list, colon, body} + return &ast.CaseClause{Case: pos, List: list, Colon: colon, Body: body} } func isTypeSwitchAssert(x ast.Expr) bool { @@ -1681,13 +1695,13 @@ func (p *parser) parseSwitchStmt() ast.Stmt { } rbrace := p.expect(token.RBRACE) p.expectSemi() - body := &ast.BlockStmt{lbrace, list, rbrace} + body := &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace} if typeSwitch { - return &ast.TypeSwitchStmt{pos, s1, s2, body} + return &ast.TypeSwitchStmt{Switch: pos, Init: s1, Assign: s2, Body: body} } - return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body} + return &ast.SwitchStmt{Switch: pos, Init: s1, Tag: p.makeExpr(s2), Body: body} } func (p *parser) parseCommClause() *ast.CommClause { @@ -1710,7 +1724,7 @@ func (p *parser) parseCommClause() *ast.CommClause { arrow := p.pos p.next() rhs := p.parseRhs() - comm = &ast.SendStmt{lhs[0], arrow, rhs} + comm = &ast.SendStmt{Chan: lhs[0], Arrow: arrow, Value: rhs} } else { // RecvStmt if tok := p.tok; tok == token.ASSIGN || tok == token.DEFINE { @@ -1723,7 +1737,7 @@ func (p *parser) parseCommClause() *ast.CommClause { pos := p.pos p.next() rhs := p.parseRhs() - as := &ast.AssignStmt{lhs, pos, tok, []ast.Expr{rhs}} + as := &ast.AssignStmt{Lhs: lhs, TokPos: pos, Tok: tok, Rhs: []ast.Expr{rhs}} if tok == token.DEFINE { p.shortVarDecl(as, lhs) } @@ -1734,7 +1748,7 @@ func (p *parser) parseCommClause() *ast.CommClause { p.errorExpected(lhs[0].Pos(), "1 expression") // continue with first expression } - comm = &ast.ExprStmt{lhs[0]} + comm = &ast.ExprStmt{X: lhs[0]} } } } else { @@ -1745,7 +1759,7 @@ func (p *parser) parseCommClause() *ast.CommClause { body := p.parseStmtList() p.closeScope() - return &ast.CommClause{pos, comm, colon, body} + return &ast.CommClause{Case: pos, Comm: comm, Colon: colon, Body: body} } func (p *parser) parseSelectStmt() *ast.SelectStmt { @@ -1761,9 +1775,9 @@ func (p *parser) parseSelectStmt() *ast.SelectStmt { } rbrace := p.expect(token.RBRACE) p.expectSemi() - body := &ast.BlockStmt{lbrace, list, rbrace} + body := &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace} - return &ast.SelectStmt{pos, body} + return &ast.SelectStmt{Select: pos, Body: body} } func (p *parser) parseForStmt() ast.Stmt { @@ -1812,16 +1826,30 @@ func (p *parser) parseForStmt() ast.Stmt { key = as.Lhs[0] default: p.errorExpected(as.Lhs[0].Pos(), "1 or 2 expressions") - return &ast.BadStmt{pos, body.End()} + return &ast.BadStmt{From: pos, To: body.End()} } // parseSimpleStmt returned a right-hand side that // is a single unary expression of the form "range x" x := as.Rhs[0].(*ast.UnaryExpr).X - return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, x, body} + return &ast.RangeStmt{ + For: pos, + Key: key, + Value: value, + TokPos: as.TokPos, + Tok: as.Tok, + X: x, + Body: body, + } } // regular for statement - return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body} + return &ast.ForStmt{ + For: pos, + Init: s1, + Cond: p.makeExpr(s2), + Post: s3, + Body: body, + } } func (p *parser) parseStmt() (s ast.Stmt) { @@ -1831,12 +1859,12 @@ func (p *parser) parseStmt() (s ast.Stmt) { switch p.tok { case token.CONST, token.TYPE, token.VAR: - s = &ast.DeclStmt{p.parseDecl()} + s = &ast.DeclStmt{Decl: p.parseDecl()} case - // tokens that may start a top-level expression - token.IDENT, token.INT, token.FLOAT, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operand - token.LBRACK, token.STRUCT, // composite type - token.MUL, token.AND, token.ARROW, token.ADD, token.SUB, token.XOR: // unary operators + // tokens that may start an expression + token.IDENT, token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operands + token.LBRACK, token.STRUCT, // composite types + token.ADD, token.SUB, token.MUL, token.AND, token.XOR, token.ARROW, token.NOT: // unary operators s, _ = p.parseSimpleStmt(labelOk) // because of the required look-ahead, labeled statements are // parsed by parseSimpleStmt - don't expect a semicolon after @@ -1864,17 +1892,17 @@ func (p *parser) parseStmt() (s ast.Stmt) { case token.FOR: s = p.parseForStmt() case token.SEMICOLON: - s = &ast.EmptyStmt{p.pos} + s = &ast.EmptyStmt{Semicolon: p.pos} p.next() case token.RBRACE: // a semicolon may be omitted before a closing "}" - s = &ast.EmptyStmt{p.pos} + s = &ast.EmptyStmt{Semicolon: p.pos} default: // no statement found pos := p.pos p.errorExpected(pos, "statement") p.next() // make progress - s = &ast.BadStmt{pos, p.pos} + s = &ast.BadStmt{From: pos, To: p.pos} } return @@ -1893,7 +1921,7 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec { var ident *ast.Ident switch p.tok { case token.PERIOD: - ident = &ast.Ident{p.pos, ".", nil} + ident = &ast.Ident{NamePos: p.pos, Name: "."} p.next() case token.IDENT: ident = p.parseIdent() @@ -1901,7 +1929,7 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec { var path *ast.BasicLit if p.tok == token.STRING { - path = &ast.BasicLit{p.pos, p.tok, p.lit} + path = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit} p.next() } else { p.expect(token.STRING) // use expect() error handling @@ -1909,7 +1937,12 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec { p.expectSemi() // call before accessing p.linecomment // collect imports - spec := &ast.ImportSpec{doc, ident, path, p.lineComment, token.NoPos} + spec := &ast.ImportSpec{ + Doc: doc, + Name: ident, + Path: path, + Comment: p.lineComment, + } p.imports = append(p.imports, spec) return spec @@ -1933,7 +1966,13 @@ func parseConstSpec(p *parser, doc *ast.CommentGroup, iota int) ast.Spec { // a function begins at the end of the ConstSpec or VarSpec and ends at // the end of the innermost containing block. // (Global identifiers are resolved in a separate phase after parsing.) - spec := &ast.ValueSpec{doc, idents, typ, values, p.lineComment} + spec := &ast.ValueSpec{ + Doc: doc, + Names: idents, + Type: typ, + Values: values, + Comment: p.lineComment, + } p.declare(spec, iota, p.topScope, ast.Con, idents...) return spec @@ -1950,7 +1989,7 @@ func parseTypeSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec { // at the identifier in the TypeSpec and ends at the end of the innermost // containing block. // (Global identifiers are resolved in a separate phase after parsing.) - spec := &ast.TypeSpec{doc, ident, nil, nil} + spec := &ast.TypeSpec{Doc: doc, Name: ident} p.declare(spec, nil, p.topScope, ast.Typ, ident) spec.Type = p.parseType() @@ -1978,7 +2017,13 @@ func parseVarSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec { // a function begins at the end of the ConstSpec or VarSpec and ends at // the end of the innermost containing block. // (Global identifiers are resolved in a separate phase after parsing.) - spec := &ast.ValueSpec{doc, idents, typ, values, p.lineComment} + spec := &ast.ValueSpec{ + Doc: doc, + Names: idents, + Type: typ, + Values: values, + Comment: p.lineComment, + } p.declare(spec, nil, p.topScope, ast.Var, idents...) return spec @@ -2005,7 +2050,14 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen list = append(list, f(p, nil, 0)) } - return &ast.GenDecl{doc, pos, keyword, lparen, list, rparen} + return &ast.GenDecl{ + Doc: doc, + TokPos: pos, + Tok: keyword, + Lparen: lparen, + Specs: list, + Rparen: rparen, + } } func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList { @@ -2018,7 +2070,7 @@ func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList { // must have exactly one receiver if par.NumFields() != 1 { p.errorExpected(par.Opening, "exactly one receiver") - par.List = []*ast.Field{{Type: &ast.BadExpr{par.Opening, par.Closing + 1}}} + par.List = []*ast.Field{{Type: &ast.BadExpr{From: par.Opening, To: par.Closing + 1}}} return par } @@ -2027,7 +2079,7 @@ func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList { base := deref(recv.Type) if _, isIdent := base.(*ast.Ident); !isIdent { p.errorExpected(base.Pos(), "(unqualified) identifier") - par.List = []*ast.Field{{Type: &ast.BadExpr{recv.Pos(), recv.End()}}} + par.List = []*ast.Field{{Type: &ast.BadExpr{From: recv.Pos(), To: recv.End()}}} } return par @@ -2057,7 +2109,17 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl { } p.expectSemi() - decl := &ast.FuncDecl{doc, recv, ident, &ast.FuncType{pos, params, results}, body} + decl := &ast.FuncDecl{ + Doc: doc, + Recv: recv, + Name: ident, + Type: &ast.FuncType{ + Func: pos, + Params: params, + Results: results, + }, + Body: body, + } if recv == nil { // Go spec: The scope of an identifier denoting a constant, type, // variable, or function (but not method) declared at top level @@ -2096,7 +2158,7 @@ func (p *parser) parseDecl() ast.Decl { pos := p.pos p.errorExpected(pos, "declaration") p.next() // make progress - decl := &ast.BadDecl{pos, p.pos} + decl := &ast.BadDecl{From: pos, To: p.pos} return decl } @@ -2155,5 +2217,14 @@ func (p *parser) parseFile() *ast.File { } } - return &ast.File{doc, pos, ident, decls, p.pkgScope, p.imports, p.unresolved[0:i], p.comments} + return &ast.File{ + Doc: doc, + Package: pos, + Name: ident, + Decls: decls, + Scope: p.pkgScope, + Imports: p.imports, + Unresolved: p.unresolved[0:i], + Comments: p.comments, + } } diff --git a/libgo/go/go/printer/nodes.go b/libgo/go/go/printer/nodes.go index 25935fb42bb..cd5e075c16c 100644 --- a/libgo/go/go/printer/nodes.go +++ b/libgo/go/go/printer/nodes.go @@ -87,7 +87,6 @@ const ( commaSep // elements are separated by commas commaTerm // list is optionally terminated by a comma noIndent // no extra indentation in multi-line lists - periodSep // elements are separated by periods ) // Sets multiLine to true if the identifier list spans multiple lines. @@ -133,7 +132,9 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp for i, x := range list { if i > 0 { if mode&commaSep != 0 { - p.print(token.COMMA) + // use position of expression following the comma as + // comma position for correct comment placement + p.print(x.Pos(), token.COMMA) } p.print(blank) } @@ -213,14 +214,18 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp } if i > 0 { - switch { - case mode&commaSep != 0: + needsLinebreak := prevLine < line && prevLine > 0 && line > 0 + if mode&commaSep != 0 { + // use position of expression following the comma as + // comma position for correct comment placement, but + // only if the expression is on the same line + if !needsLinebreak { + p.print(x.Pos()) + } p.print(token.COMMA) - case mode&periodSep != 0: - p.print(token.PERIOD) } - needsBlank := mode&periodSep == 0 // period-separated list elements don't need a blank - if prevLine < line && prevLine > 0 && line > 0 { + needsBlank := true + if needsLinebreak { // lines are broken using newlines so comments remain aligned // unless forceFF is set or there are multiple expressions on // the same line in which case formfeed is used @@ -287,11 +292,18 @@ func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) { parLineBeg = parLineEnd } // separating "," if needed + needsLinebreak := 0 < prevLine && prevLine < parLineBeg if i > 0 { + // use position of parameter following the comma as + // comma position for correct comma placement, but + // only if the next parameter is on the same line + if !needsLinebreak { + p.print(par.Pos()) + } p.print(token.COMMA) } // separator if needed (linebreak or blank) - if 0 < prevLine && prevLine < parLineBeg && p.linebreak(parLineBeg, 0, ws, true) { + if needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) { // break line if the opening "(" or previous parameter ended on a different line ws = ignore *multiLine = true @@ -316,7 +328,7 @@ func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) { // if the closing ")" is on a separate line from the last parameter, // print an additional "," and line break if closing := p.lineFor(fields.Closing); 0 < prevLine && prevLine < closing { - p.print(",") + p.print(token.COMMA) p.linebreak(closing, 0, ignore, true) } // unindent if we indented @@ -374,7 +386,7 @@ func (p *printer) isOneLineFieldList(list []*ast.Field) bool { } func (p *printer) setLineComment(text string) { - p.setComment(&ast.CommentGroup{[]*ast.Comment{{token.NoPos, text}}}) + p.setComment(&ast.CommentGroup{List: []*ast.Comment{{Slash: token.NoPos, Text: text}}}) } func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) { @@ -397,6 +409,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) f := list[0] for i, x := range f.Names { if i > 0 { + // no comments so no need for comma position p.print(token.COMMA, blank) } p.expr(x, ignoreMultiLine) @@ -668,63 +681,6 @@ func isBinary(expr ast.Expr) bool { return ok } -// If the expression contains one or more selector expressions, splits it into -// two expressions at the rightmost period. Writes entire expr to suffix when -// selector isn't found. Rewrites AST nodes for calls, index expressions and -// type assertions, all of which may be found in selector chains, to make them -// parts of the chain. -func splitSelector(expr ast.Expr) (body, suffix ast.Expr) { - switch x := expr.(type) { - case *ast.SelectorExpr: - body, suffix = x.X, x.Sel - return - case *ast.CallExpr: - body, suffix = splitSelector(x.Fun) - if body != nil { - suffix = &ast.CallExpr{suffix, x.Lparen, x.Args, x.Ellipsis, x.Rparen} - return - } - case *ast.IndexExpr: - body, suffix = splitSelector(x.X) - if body != nil { - suffix = &ast.IndexExpr{suffix, x.Lbrack, x.Index, x.Rbrack} - return - } - case *ast.SliceExpr: - body, suffix = splitSelector(x.X) - if body != nil { - suffix = &ast.SliceExpr{suffix, x.Lbrack, x.Low, x.High, x.Rbrack} - return - } - case *ast.TypeAssertExpr: - body, suffix = splitSelector(x.X) - if body != nil { - suffix = &ast.TypeAssertExpr{suffix, x.Type} - return - } - } - suffix = expr - return -} - -// Convert an expression into an expression list split at the periods of -// selector expressions. -func selectorExprList(expr ast.Expr) (list []ast.Expr) { - // split expression - for expr != nil { - var suffix ast.Expr - expr, suffix = splitSelector(expr) - list = append(list, suffix) - } - - // reverse list - for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 { - list[i], list[j] = list[j], list[i] - } - - return -} - // Sets multiLine to true if the expression spans multiple lines. func (p *printer) expr1(expr ast.Expr, prec1, depth int, multiLine *bool) { p.print(expr.Pos()) @@ -798,8 +754,14 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, multiLine *bool) { } case *ast.SelectorExpr: - parts := selectorExprList(expr) - p.exprList(token.NoPos, parts, depth, periodSep, multiLine, token.NoPos) + p.expr1(x.X, token.HighestPrec, depth, multiLine) + p.print(token.PERIOD) + if line := p.lineFor(x.Sel.Pos()); p.pos.IsValid() && p.pos.Line < line { + p.print(indent, newline, x.Sel.Pos(), x.Sel, unindent) + *multiLine = true + } else { + p.print(x.Sel.Pos(), x.Sel) + } case *ast.TypeAssertExpr: p.expr1(x.X, token.HighestPrec, depth, multiLine) @@ -1180,7 +1142,9 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) { p.print(token.FOR, blank) p.expr(s.Key, multiLine) if s.Value != nil { - p.print(token.COMMA, blank) + // use position of value following the comma as + // comma position for correct comment placement + p.print(s.Value.Pos(), token.COMMA, blank) p.expr(s.Value, multiLine) } p.print(blank, s.TokPos, s.Tok, blank, token.RANGE, blank) diff --git a/libgo/go/go/printer/printer.go b/libgo/go/go/printer/printer.go index c9949205e8a..72f65a1d852 100644 --- a/libgo/go/go/printer/printer.go +++ b/libgo/go/go/printer/printer.go @@ -686,9 +686,11 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro } if last != nil { - if last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line { - // the last comment is a /*-style comment and the next item - // follows on the same line: separate with an extra blank + // if the last comment is a /*-style comment and the next item + // follows on the same line but is not a comma or a "closing" + // token, add an extra blank for separation + if last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line && tok != token.COMMA && + tok != token.RPAREN && tok != token.RBRACK && tok != token.RBRACE { p.writeByte(' ', 1) } // ensure that there is a line break after a //-style comment, diff --git a/libgo/go/go/printer/printer_test.go b/libgo/go/go/printer/printer_test.go index fa133cd35f0..2d4f61356c1 100644 --- a/libgo/go/go/printer/printer_test.go +++ b/libgo/go/go/printer/printer_test.go @@ -283,10 +283,10 @@ func fibo(n int) { t.Error("expected offset 1") // error in test } - testComment(t, f, len(src), &ast.Comment{pos, "//-style comment"}) - testComment(t, f, len(src), &ast.Comment{pos, "/*-style comment */"}) - testComment(t, f, len(src), &ast.Comment{pos, "/*-style \n comment */"}) - testComment(t, f, len(src), &ast.Comment{pos, "/*-style comment \n\n\n */"}) + testComment(t, f, len(src), &ast.Comment{Slash: pos, Text: "//-style comment"}) + testComment(t, f, len(src), &ast.Comment{Slash: pos, Text: "/*-style comment */"}) + testComment(t, f, len(src), &ast.Comment{Slash: pos, Text: "/*-style \n comment */"}) + testComment(t, f, len(src), &ast.Comment{Slash: pos, Text: "/*-style comment \n\n\n */"}) } type visitor chan *ast.Ident diff --git a/libgo/go/go/printer/testdata/comments.golden b/libgo/go/go/printer/testdata/comments.golden index e5826eecefe..4c6f1ab8274 100644 --- a/libgo/go/go/printer/testdata/comments.golden +++ b/libgo/go/go/printer/testdata/comments.golden @@ -405,16 +405,17 @@ func _() { } // Some interesting interspersed comments. +// See below for more common cases. func _( /* this */ x /* is */ /* an */ int) { } -func _( /* no params */ ) {} +func _( /* no params */) {} func _() { - f( /* no args */ ) + f( /* no args */) } -func ( /* comment1 */ T /* comment2 */ ) _() {} +func ( /* comment1 */ T /* comment2 */) _() {} func _() { /* one-line functions with comments are formatted as multi-line functions */ } @@ -425,7 +426,7 @@ func _() { } func _() { - _ = []int{0, 1 /* don't introduce a newline after this comment - was issue 1365 */ } + _ = []int{0, 1 /* don't introduce a newline after this comment - was issue 1365 */} } // Test cases from issue 1542: @@ -448,8 +449,9 @@ func _() { _ = a } -// Comments immediately adjacent to punctuation (for which the go/printer -// may only have estimated position information) must remain after the punctuation. +// Comments immediately adjacent to punctuation followed by a newline +// remain after the punctuation (looks better and permits alignment of +// comments). func _() { _ = T{ 1, // comment after comma @@ -479,6 +481,35 @@ func _() { } } +// If there is no newline following punctuation, commas move before the punctuation. +// This way, commas interspersed in lists stay with the respective expression. +func f(x /* comment */, y int, z int /* comment */, u, v, w int /* comment */) { + f(x /* comment */, y) + f(x, /* comment */ + y) + f( + x, /* comment */ + ) +} + +func g( + x int, /* comment */ +) { +} + +type _ struct { + a, b /* comment */, c int +} + +type _ struct { + a, b /* comment */, c int +} + +func _() { + for a /* comment */, b := range x { + } +} + // Print line directives correctly. // The following is a legal line directive. diff --git a/libgo/go/go/printer/testdata/comments.input b/libgo/go/go/printer/testdata/comments.input index 55f6b61f21f..c0f8cca3a92 100644 --- a/libgo/go/go/printer/testdata/comments.input +++ b/libgo/go/go/printer/testdata/comments.input @@ -411,6 +411,7 @@ func _() { // Some interesting interspersed comments. +// See below for more common cases. func _(/* this */x/* is *//* an */ int) { } @@ -453,8 +454,9 @@ func _() { _ = a } -// Comments immediately adjacent to punctuation (for which the go/printer -// may only have estimated position information) must remain after the punctuation. +// Comments immediately adjacent to punctuation followed by a newline +// remain after the punctuation (looks better and permits alignment of +// comments). func _() { _ = T{ 1, // comment after comma @@ -486,6 +488,31 @@ func _() { } } +// If there is no newline following punctuation, commas move before the punctuation. +// This way, commas interspersed in lists stay with the respective expression. +func f(x/* comment */, y int, z int /* comment */, u, v, w int /* comment */) { + f(x /* comment */, y) + f(x /* comment */, + y) + f( + x /* comment */, + ) +} + +func g( + x int /* comment */, +) {} + +type _ struct { + a, b /* comment */, c int +} + +type _ struct { a, b /* comment */, c int } + +func _() { + for a /* comment */, b := range x { + } +} // Print line directives correctly. diff --git a/libgo/go/go/printer/testdata/expressions.golden b/libgo/go/go/printer/testdata/expressions.golden index d0cf24ad6f6..95fdd95ffbb 100644 --- a/libgo/go/go/printer/testdata/expressions.golden +++ b/libgo/go/go/printer/testdata/expressions.golden @@ -545,7 +545,7 @@ func _() { // handle multiline argument list correctly _ = new(T). foo( - 1). + 1). foo(2) _ = new(T).foo( @@ -587,12 +587,12 @@ func _() { _ = new(T). Field. Array[3+ - 4]. + 4]. Table["foo"]. Blob.(*Type). Slices[1:4]. Method(1, 2, - 3). + 3). Thingy _ = a.b.c diff --git a/libgo/go/go/printer/testdata/expressions.raw b/libgo/go/go/printer/testdata/expressions.raw index d7819a3baad..3442ba9b950 100644 --- a/libgo/go/go/printer/testdata/expressions.raw +++ b/libgo/go/go/printer/testdata/expressions.raw @@ -545,7 +545,7 @@ func _() { // handle multiline argument list correctly _ = new(T). foo( - 1). + 1). foo(2) _ = new(T).foo( @@ -587,12 +587,12 @@ func _() { _ = new(T). Field. Array[3+ - 4]. + 4]. Table["foo"]. Blob.(*Type). Slices[1:4]. Method(1, 2, - 3). + 3). Thingy _ = a.b.c diff --git a/libgo/go/go/scanner/scanner.go b/libgo/go/go/scanner/scanner.go index 458e1f9f37a..2395363b0ec 100644 --- a/libgo/go/go/scanner/scanner.go +++ b/libgo/go/go/scanner/scanner.go @@ -2,21 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package scanner implements a scanner for Go source text. Takes a []byte as -// source which can then be tokenized through repeated calls to the Scan -// function. Typical use: -// -// var s scanner.Scanner -// fset := token.NewFileSet() // position information is relative to fset -// file := fset.AddFile(filename, fset.Base(), len(src)) // register file -// s.Init(file, src, nil /* no error handler */, 0) -// for { -// pos, tok, lit := s.Scan() -// if tok == token.EOF { -// break -// } -// // do something here with pos, tok, and lit -// } +// Package scanner implements a scanner for Go source text. +// It takes a []byte as source which can then be tokenized +// through repeated calls to the Scan method. // package scanner diff --git a/libgo/go/html/template/clone.go b/libgo/go/html/template/clone.go deleted file mode 100644 index d0d8ea46733..00000000000 --- a/libgo/go/html/template/clone.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package template - -import ( - "text/template/parse" -) - -// clone clones a template Node. -func clone(n parse.Node) parse.Node { - switch t := n.(type) { - case *parse.ActionNode: - return cloneAction(t) - case *parse.IfNode: - b := new(parse.IfNode) - copyBranch(&b.BranchNode, &t.BranchNode) - return b - case *parse.ListNode: - return cloneList(t) - case *parse.RangeNode: - b := new(parse.RangeNode) - copyBranch(&b.BranchNode, &t.BranchNode) - return b - case *parse.TemplateNode: - return cloneTemplate(t) - case *parse.TextNode: - return cloneText(t) - case *parse.WithNode: - b := new(parse.WithNode) - copyBranch(&b.BranchNode, &t.BranchNode) - return b - } - panic("cloning " + n.String() + " is unimplemented") -} - -// cloneAction returns a deep clone of n. -func cloneAction(n *parse.ActionNode) *parse.ActionNode { - // We use keyless fields because they won't compile if a field is added. - return &parse.ActionNode{n.NodeType, n.Line, clonePipe(n.Pipe)} -} - -// cloneList returns a deep clone of n. -func cloneList(n *parse.ListNode) *parse.ListNode { - if n == nil { - return nil - } - // We use keyless fields because they won't compile if a field is added. - c := parse.ListNode{n.NodeType, make([]parse.Node, len(n.Nodes))} - for i, child := range n.Nodes { - c.Nodes[i] = clone(child) - } - return &c -} - -// clonePipe returns a shallow clone of n. -// The escaper does not modify pipe descendants in place so there's no need to -// clone deeply. -func clonePipe(n *parse.PipeNode) *parse.PipeNode { - if n == nil { - return nil - } - // We use keyless fields because they won't compile if a field is added. - return &parse.PipeNode{n.NodeType, n.Line, n.Decl, n.Cmds} -} - -// cloneTemplate returns a deep clone of n. -func cloneTemplate(n *parse.TemplateNode) *parse.TemplateNode { - // We use keyless fields because they won't compile if a field is added. - return &parse.TemplateNode{n.NodeType, n.Line, n.Name, clonePipe(n.Pipe)} -} - -// cloneText clones the given node sharing its []byte. -func cloneText(n *parse.TextNode) *parse.TextNode { - // We use keyless fields because they won't compile if a field is added. - return &parse.TextNode{n.NodeType, n.Text} -} - -// copyBranch clones src into dst. -func copyBranch(dst, src *parse.BranchNode) { - // We use keyless fields because they won't compile if a field is added. - *dst = parse.BranchNode{ - src.NodeType, - src.Line, - clonePipe(src.Pipe), - cloneList(src.List), - cloneList(src.ElseList), - } -} diff --git a/libgo/go/html/template/clone_test.go b/libgo/go/html/template/clone_test.go index 39788173b99..c612775d4f0 100644 --- a/libgo/go/html/template/clone_test.go +++ b/libgo/go/html/template/clone_test.go @@ -7,86 +7,109 @@ package template import ( "bytes" "testing" + "text/template/parse" ) +func TestAddParseTree(t *testing.T) { + root := Must(New("root").Parse(`{{define "a"}} {{.}} {{template "b"}} {{.}} "></a>{{end}}`)) + tree, err := parse.Parse("t", `{{define "b"}}<a href="{{end}}`, "", "", nil, nil) + if err != nil { + t.Fatal(err) + } + added := Must(root.AddParseTree("b", tree["b"])) + b := new(bytes.Buffer) + err = added.ExecuteTemplate(b, "a", "1>0") + if err != nil { + t.Fatal(err) + } + if got, want := b.String(), ` 1>0 <a href=" 1%3e0 "></a>`; got != want { + t.Errorf("got %q want %q", got, want) + } +} + func TestClone(t *testing.T) { - tests := []struct { - input, want, wantClone string - }{ - { - `Hello, {{if true}}{{"<World>"}}{{end}}!`, - "Hello, <World>!", - "Hello, <World>!", - }, - { - `Hello, {{if false}}{{.X}}{{else}}{{"<World>"}}{{end}}!`, - "Hello, <World>!", - "Hello, <World>!", - }, - { - `Hello, {{with "<World>"}}{{.}}{{end}}!`, - "Hello, <World>!", - "Hello, <World>!", - }, - { - `{{range .}}<p>{{.}}</p>{{end}}`, - "<p>foo</p><p><bar></p><p>baz</p>", - "<p>foo</p><p><bar></p><p>baz</p>", - }, - { - `Hello, {{"<World>" | html}}!`, - "Hello, <World>!", - "Hello, <World>!", - }, - { - `Hello{{if 1}}, World{{else}}{{template "d"}}{{end}}!`, - "Hello, World!", - "Hello, World!", - }, + // The {{.}} will be executed with data "<i>*/" in different contexts. + // In the t0 template, it will be in a text context. + // In the t1 template, it will be in a URL context. + // In the t2 template, it will be in a JavaScript context. + // In the t3 template, it will be in a CSS context. + const tmpl = `{{define "a"}}{{template "lhs"}}{{.}}{{template "rhs"}}{{end}}` + b := new(bytes.Buffer) + + // Create an incomplete template t0. + t0 := Must(New("t0").Parse(tmpl)) + + // Clone t0 as t1. + t1 := Must(t0.Clone()) + Must(t1.Parse(`{{define "lhs"}} <a href=" {{end}}`)) + Must(t1.Parse(`{{define "rhs"}} "></a> {{end}}`)) + + // Execute t1. + b.Reset() + if err := t1.ExecuteTemplate(b, "a", "<i>*/"); err != nil { + t.Fatal(err) + } + if got, want := b.String(), ` <a href=" %3ci%3e*/ "></a> `; got != want { + t.Errorf("t1: got %q want %q", got, want) + } + + // Clone t0 as t2. + t2 := Must(t0.Clone()) + Must(t2.Parse(`{{define "lhs"}} <p onclick="javascript: {{end}}`)) + Must(t2.Parse(`{{define "rhs"}} "></p> {{end}}`)) + + // Execute t2. + b.Reset() + if err := t2.ExecuteTemplate(b, "a", "<i>*/"); err != nil { + t.Fatal(err) + } + if got, want := b.String(), ` <p onclick="javascript: "\u003ci\u003e*/" "></p> `; got != want { + t.Errorf("t2: got %q want %q", got, want) } - for _, test := range tests { - s, err := New("s").Parse(test.input) - if err != nil { - t.Errorf("input=%q: unexpected parse error %v", test.input, err) - } - - d, _ := New("d").Parse(test.input) - // Hack: just replace the root of the tree. - d.text.Root = cloneList(s.text.Root) - - if want, got := s.text.Root.String(), d.text.Root.String(); want != got { - t.Errorf("want %q, got %q", want, got) - } - - err = escapeTemplates(d, "d") - if err != nil { - t.Errorf("%q: failed to escape: %s", test.input, err) - continue - } - - if want, got := "s", s.Name(); want != got { - t.Errorf("want %q, got %q", want, got) - continue - } - if want, got := "d", d.Name(); want != got { - t.Errorf("want %q, got %q", want, got) - continue - } - - data := []string{"foo", "<bar>", "baz"} - - var b bytes.Buffer - d.Execute(&b, data) - if got := b.String(); got != test.wantClone { - t.Errorf("input=%q: want %q, got %q", test.input, test.wantClone, got) - } - - // Make sure escaping d did not affect s. - b.Reset() - s.text.Execute(&b, data) - if got := b.String(); got != test.want { - t.Errorf("input=%q: want %q, got %q", test.input, test.want, got) - } + // Clone t0 as t3, but do not execute t3 yet. + t3 := Must(t0.Clone()) + Must(t3.Parse(`{{define "lhs"}} <style> {{end}}`)) + Must(t3.Parse(`{{define "rhs"}} </style> {{end}}`)) + + // Complete t0. + Must(t0.Parse(`{{define "lhs"}} ( {{end}}`)) + Must(t0.Parse(`{{define "rhs"}} ) {{end}}`)) + + // Clone t0 as t4. Redefining the "lhs" template should fail. + t4 := Must(t0.Clone()) + if _, err := t4.Parse(`{{define "lhs"}} FAIL {{end}}`); err == nil { + t.Error(`redefine "lhs": got nil err want non-nil`) + } + + // Execute t0. + b.Reset() + if err := t0.ExecuteTemplate(b, "a", "<i>*/"); err != nil { + t.Fatal(err) + } + if got, want := b.String(), ` ( <i>*/ ) `; got != want { + t.Errorf("t0: got %q want %q", got, want) + } + + // Clone t0. This should fail, as t0 has already executed. + if _, err := t0.Clone(); err == nil { + t.Error(`t0.Clone(): got nil err want non-nil`) + } + + // Similarly, cloning sub-templates should fail. + if _, err := t0.Lookup("a").Clone(); err == nil { + t.Error(`t0.Lookup("a").Clone(): got nil err want non-nil`) + } + if _, err := t0.Lookup("lhs").Clone(); err == nil { + t.Error(`t0.Lookup("lhs").Clone(): got nil err want non-nil`) + } + + // Execute t3. + b.Reset() + if err := t3.ExecuteTemplate(b, "a", "<i>*/"); err != nil { + t.Fatal(err) + } + if got, want := b.String(), ` <style> ZgotmplZ </style> `; got != want { + t.Errorf("t3: got %q want %q", got, want) } } diff --git a/libgo/go/html/template/content.go b/libgo/go/html/template/content.go index 4de7ccde912..539664f9729 100644 --- a/libgo/go/html/template/content.go +++ b/libgo/go/html/template/content.go @@ -85,6 +85,22 @@ func indirect(a interface{}) interface{} { return v.Interface() } +var ( + errorType = reflect.TypeOf((*error)(nil)).Elem() + fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() +) + +// indirectToStringerOrError returns the value, after dereferencing as many times +// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer +// or error, +func indirectToStringerOrError(a interface{}) interface{} { + v := reflect.ValueOf(a) + for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + return v.Interface() +} + // stringify converts its arguments to a string and the type of the content. // All pointers are dereferenced, as in the text/template package. func stringify(args ...interface{}) (string, contentType) { @@ -107,7 +123,7 @@ func stringify(args ...interface{}) (string, contentType) { } } for i, arg := range args { - args[i] = indirect(arg) + args[i] = indirectToStringerOrError(arg) } return fmt.Sprint(args...), contentTypePlain } diff --git a/libgo/go/html/template/content_test.go b/libgo/go/html/template/content_test.go index c96a521a59c..3c32e5e89cf 100644 --- a/libgo/go/html/template/content_test.go +++ b/libgo/go/html/template/content_test.go @@ -6,6 +6,7 @@ package template import ( "bytes" + "fmt" "strings" "testing" ) @@ -219,3 +220,42 @@ func TestTypedContent(t *testing.T) { } } } + +// Test that we print using the String method. Was issue 3073. +type stringer struct { + v int +} + +func (s *stringer) String() string { + return fmt.Sprintf("string=%d", s.v) +} + +type errorer struct { + v int +} + +func (s *errorer) Error() string { + return fmt.Sprintf("error=%d", s.v) +} + +func TestStringer(t *testing.T) { + s := &stringer{3} + b := new(bytes.Buffer) + tmpl := Must(New("x").Parse("{{.}}")) + if err := tmpl.Execute(b, s); err != nil { + t.Fatal(err) + } + var expect = "string=3" + if b.String() != expect { + t.Errorf("expected %q got %q", expect, b.String()) + } + e := &errorer{7} + b.Reset() + if err := tmpl.Execute(b, e); err != nil { + t.Fatal(err) + } + expect = "error=7" + if b.String() != expect { + t.Errorf("expected %q got %q", expect, b.String()) + } +} diff --git a/libgo/go/html/template/doc.go b/libgo/go/html/template/doc.go index 6fe507abea4..7f60f3b9680 100644 --- a/libgo/go/html/template/doc.go +++ b/libgo/go/html/template/doc.go @@ -17,11 +17,11 @@ Introduction This package wraps package text/template so you can share its template API to parse and execute HTML templates safely. - set, err := new(template.Set).Parse(...) + tmpl, err := template.New("name").Parse(...) // Error checking elided - err = set.Execute(out, "Foo", data) + err = tmpl.Execute(out, "Foo", data) -If successful, set will now be injection-safe. Otherwise, err is an error +If successful, tmpl will now be injection-safe. Otherwise, err is an error defined in the docs for ErrorCode. HTML templates treat data values as plain text which should be encoded so they @@ -172,18 +172,18 @@ This package assumes that template authors are trusted, that Execute's data parameter is not, and seeks to preserve the properties below in the face of untrusted data: -Structure Preservation Property +Structure Preservation Property: "... when a template author writes an HTML tag in a safe templating language, the browser will interpret the corresponding portion of the output as a tag regardless of the values of untrusted data, and similarly for other structures such as attribute boundaries and JS and CSS string boundaries." -Code Effect Property +Code Effect Property: "... only code specified by the template author should run as a result of injecting the template output into a page and all code specified by the template author should run as a result of the same." -Least Surprise Property +Least Surprise Property: "A developer (or code reviewer) familiar with HTML, CSS, and JavaScript, who knows that contextual autoescaping happens should be able to look at a {{.}} and correctly infer what sanitization happens." diff --git a/libgo/go/html/template/escape.go b/libgo/go/html/template/escape.go index c6f723ae4a4..02fa3eaad6b 100644 --- a/libgo/go/html/template/escape.go +++ b/libgo/go/html/template/escape.go @@ -46,30 +46,30 @@ func escapeTemplates(tmpl *Template, names ...string) error { // funcMap maps command names to functions that render their inputs safe. var funcMap = template.FuncMap{ - "exp_template_html_attrescaper": attrEscaper, - "exp_template_html_commentescaper": commentEscaper, - "exp_template_html_cssescaper": cssEscaper, - "exp_template_html_cssvaluefilter": cssValueFilter, - "exp_template_html_htmlnamefilter": htmlNameFilter, - "exp_template_html_htmlescaper": htmlEscaper, - "exp_template_html_jsregexpescaper": jsRegexpEscaper, - "exp_template_html_jsstrescaper": jsStrEscaper, - "exp_template_html_jsvalescaper": jsValEscaper, - "exp_template_html_nospaceescaper": htmlNospaceEscaper, - "exp_template_html_rcdataescaper": rcdataEscaper, - "exp_template_html_urlescaper": urlEscaper, - "exp_template_html_urlfilter": urlFilter, - "exp_template_html_urlnormalizer": urlNormalizer, + "html_template_attrescaper": attrEscaper, + "html_template_commentescaper": commentEscaper, + "html_template_cssescaper": cssEscaper, + "html_template_cssvaluefilter": cssValueFilter, + "html_template_htmlnamefilter": htmlNameFilter, + "html_template_htmlescaper": htmlEscaper, + "html_template_jsregexpescaper": jsRegexpEscaper, + "html_template_jsstrescaper": jsStrEscaper, + "html_template_jsvalescaper": jsValEscaper, + "html_template_nospaceescaper": htmlNospaceEscaper, + "html_template_rcdataescaper": rcdataEscaper, + "html_template_urlescaper": urlEscaper, + "html_template_urlfilter": urlFilter, + "html_template_urlnormalizer": urlNormalizer, } // equivEscapers matches contextual escapers to equivalent template builtins. var equivEscapers = map[string]string{ - "exp_template_html_attrescaper": "html", - "exp_template_html_htmlescaper": "html", - "exp_template_html_nospaceescaper": "html", - "exp_template_html_rcdataescaper": "html", - "exp_template_html_urlescaper": "urlquery", - "exp_template_html_urlnormalizer": "urlquery", + "html_template_attrescaper": "html", + "html_template_htmlescaper": "html", + "html_template_nospaceescaper": "html", + "html_template_rcdataescaper": "html", + "html_template_urlescaper": "urlquery", + "html_template_urlnormalizer": "urlquery", } // escaper collects type inferences about templates and changes needed to make @@ -147,17 +147,17 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context { case stateURL, stateCSSDqStr, stateCSSSqStr, stateCSSDqURL, stateCSSSqURL, stateCSSURL: switch c.urlPart { case urlPartNone: - s = append(s, "exp_template_html_urlfilter") + s = append(s, "html_template_urlfilter") fallthrough case urlPartPreQuery: switch c.state { case stateCSSDqStr, stateCSSSqStr: - s = append(s, "exp_template_html_cssescaper") + s = append(s, "html_template_cssescaper") default: - s = append(s, "exp_template_html_urlnormalizer") + s = append(s, "html_template_urlnormalizer") } case urlPartQueryOrFrag: - s = append(s, "exp_template_html_urlescaper") + s = append(s, "html_template_urlescaper") case urlPartUnknown: return context{ state: stateError, @@ -167,27 +167,27 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context { panic(c.urlPart.String()) } case stateJS: - s = append(s, "exp_template_html_jsvalescaper") + s = append(s, "html_template_jsvalescaper") // A slash after a value starts a div operator. c.jsCtx = jsCtxDivOp case stateJSDqStr, stateJSSqStr: - s = append(s, "exp_template_html_jsstrescaper") + s = append(s, "html_template_jsstrescaper") case stateJSRegexp: - s = append(s, "exp_template_html_jsregexpescaper") + s = append(s, "html_template_jsregexpescaper") case stateCSS: - s = append(s, "exp_template_html_cssvaluefilter") + s = append(s, "html_template_cssvaluefilter") case stateText: - s = append(s, "exp_template_html_htmlescaper") + s = append(s, "html_template_htmlescaper") case stateRCDATA: - s = append(s, "exp_template_html_rcdataescaper") + s = append(s, "html_template_rcdataescaper") case stateAttr: // Handled below in delim check. case stateAttrName, stateTag: c.state = stateAttrName - s = append(s, "exp_template_html_htmlnamefilter") + s = append(s, "html_template_htmlnamefilter") default: if isComment(c.state) { - s = append(s, "exp_template_html_commentescaper") + s = append(s, "html_template_commentescaper") } else { panic("unexpected state " + c.state.String()) } @@ -196,9 +196,9 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context { case delimNone: // No extra-escaping needed for raw text content. case delimSpaceOrTagEnd: - s = append(s, "exp_template_html_nospaceescaper") + s = append(s, "html_template_nospaceescaper") default: - s = append(s, "exp_template_html_attrescaper") + s = append(s, "html_template_attrescaper") } e.editActionNode(n, s) return c @@ -260,22 +260,22 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) { // redundantFuncs[a][b] implies that funcMap[b](funcMap[a](x)) == funcMap[a](x) // for all x. var redundantFuncs = map[string]map[string]bool{ - "exp_template_html_commentescaper": { - "exp_template_html_attrescaper": true, - "exp_template_html_nospaceescaper": true, - "exp_template_html_htmlescaper": true, + "html_template_commentescaper": { + "html_template_attrescaper": true, + "html_template_nospaceescaper": true, + "html_template_htmlescaper": true, }, - "exp_template_html_cssescaper": { - "exp_template_html_attrescaper": true, + "html_template_cssescaper": { + "html_template_attrescaper": true, }, - "exp_template_html_jsregexpescaper": { - "exp_template_html_attrescaper": true, + "html_template_jsregexpescaper": { + "html_template_attrescaper": true, }, - "exp_template_html_jsstrescaper": { - "exp_template_html_attrescaper": true, + "html_template_jsstrescaper": { + "html_template_attrescaper": true, }, - "exp_template_html_urlescaper": { - "exp_template_html_urlnormalizer": true, + "html_template_urlescaper": { + "html_template_urlnormalizer": true, }, } @@ -505,7 +505,7 @@ func (e *escaper) escapeTree(c context, name string, line int) (context, string) dt := e.template(dname) if dt == nil { dt = template.New(dname) - dt.Tree = &parse.Tree{Name: dname, Root: cloneList(t.Root)} + dt.Tree = &parse.Tree{Name: dname, Root: t.Root.CopyList()} e.derived[dname] = dt } t = dt diff --git a/libgo/go/html/template/template.go b/libgo/go/html/template/template.go index 9ffe41413a8..b0bae7a54fb 100644 --- a/libgo/go/html/template/template.go +++ b/libgo/go/html/template/template.go @@ -50,7 +50,7 @@ func (t *Template) Execute(wr io.Writer, data interface{}) (err error) { // ExecuteTemplate applies the template associated with t that has the given // name to the specified data object and writes the output to wr. func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error { - tmpl, err := t.lookupAndEscapeTemplate(wr, name) + tmpl, err := t.lookupAndEscapeTemplate(name) if err != nil { return err } @@ -60,7 +60,7 @@ func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) // lookupAndEscapeTemplate guarantees that the template with the given name // is escaped, or returns an error if it cannot be. It returns the named // template. -func (t *Template) lookupAndEscapeTemplate(wr io.Writer, name string) (tmpl *Template, err error) { +func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err error) { t.nameSpace.mu.Lock() defer t.nameSpace.mu.Unlock() tmpl = t.set[name] @@ -106,14 +106,71 @@ func (t *Template) Parse(src string) (*Template, error) { return t, nil } -// AddParseTree is unimplemented. -func (t *Template) AddParseTree(name string, tree *parse.Tree) error { - return fmt.Errorf("html/template: AddParseTree unimplemented") +// AddParseTree creates a new template with the name and parse tree +// and associates it with t. +// +// It returns an error if t has already been executed. +func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) { + t.nameSpace.mu.Lock() + defer t.nameSpace.mu.Unlock() + if t.escaped { + return nil, fmt.Errorf("html/template: cannot AddParseTree to %q after it has executed", t.Name()) + } + text, err := t.text.AddParseTree(name, tree) + if err != nil { + return nil, err + } + ret := &Template{ + false, + text, + t.nameSpace, + } + t.set[name] = ret + return ret, nil } -// Clone is unimplemented. -func (t *Template) Clone(name string) error { - return fmt.Errorf("html/template: Clone unimplemented") +// Clone returns a duplicate of the template, including all associated +// templates. The actual representation is not copied, but the name space of +// associated templates is, so further calls to Parse in the copy will add +// templates to the copy but not to the original. Clone can be used to prepare +// common templates and use them with variant definitions for other templates +// by adding the variants after the clone is made. +// +// It returns an error if t has already been executed. +func (t *Template) Clone() (*Template, error) { + t.nameSpace.mu.Lock() + defer t.nameSpace.mu.Unlock() + if t.escaped { + return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name()) + } + textClone, err := t.text.Clone() + if err != nil { + return nil, err + } + ret := &Template{ + false, + textClone, + &nameSpace{ + set: make(map[string]*Template), + }, + } + for _, x := range textClone.Templates() { + name := x.Name() + src := t.set[name] + if src == nil || src.escaped { + return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name()) + } + x.Tree = &parse.Tree{ + Name: x.Tree.Name, + Root: x.Tree.Root.CopyList(), + } + ret.set[name] = &Template{ + false, + x, + ret.nameSpace, + } + } + return ret, nil } // New allocates a new HTML template with the given name. diff --git a/libgo/go/image/decode_example_test.go b/libgo/go/image/decode_example_test.go new file mode 100644 index 00000000000..aa5a841c0a5 --- /dev/null +++ b/libgo/go/image/decode_example_test.go @@ -0,0 +1,79 @@ +// 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. + +// This example demonstrates decoding a JPEG image and examining its pixels. +package image_test + +import ( + "fmt" + "image" + "log" + "os" + + // Package image/jpeg is not used explicitly in the code below, + // but is imported for its initialization side-effect, which allows + // image.Decode to understand JPEG formatted images. Uncomment these + // two lines to also understand GIF and PNG images: + // _ "image/gif" + // _ "image/png" + _ "image/jpeg" +) + +func Example() { + // Open the file. + file, err := os.Open("testdata/video-001.jpeg") + if err != nil { + log.Fatal(err) + } + defer file.Close() + + // Decode the image. + m, _, err := image.Decode(file) + if err != nil { + log.Fatal(err) + } + bounds := m.Bounds() + + // Calculate a 16-bin histogram for m's red, green, blue and alpha components. + // + // An image's bounds do not necessarily start at (0, 0), so the two loops start + // at bounds.Min.Y and bounds.Min.X. Looping over Y first and X second is more + // likely to result in better memory access patterns than X first and Y second. + var histogram [16][4]int + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { + for x := bounds.Min.X; x < bounds.Max.X; x++ { + r, g, b, a := m.At(x, y).RGBA() + // A color's RGBA method returns values in the range [0, 65535]. + // Shifting by 12 reduces this to the range [0, 15]. + histogram[r>>12][0]++ + histogram[g>>12][1]++ + histogram[b>>12][2]++ + histogram[a>>12][3]++ + } + } + + // Print the results. + fmt.Printf("%-14s %6s %6s %6s %6s\n", "bin", "red", "green", "blue", "alpha") + for i, x := range histogram { + fmt.Printf("0x%04x-0x%04x: %6d %6d %6d %6d\n", i<<12, (i+1)<<12-1, x[0], x[1], x[2], x[3]) + } + // Output: + // bin red green blue alpha + // 0x0000-0x0fff: 471 819 7596 0 + // 0x1000-0x1fff: 576 2892 726 0 + // 0x2000-0x2fff: 1038 2330 943 0 + // 0x3000-0x3fff: 883 2321 1014 0 + // 0x4000-0x4fff: 501 1295 525 0 + // 0x5000-0x5fff: 302 962 242 0 + // 0x6000-0x6fff: 219 358 150 0 + // 0x7000-0x7fff: 352 281 192 0 + // 0x8000-0x8fff: 3688 216 246 0 + // 0x9000-0x9fff: 2277 237 283 0 + // 0xa000-0xafff: 971 254 357 0 + // 0xb000-0xbfff: 317 306 429 0 + // 0xc000-0xcfff: 203 402 401 0 + // 0xd000-0xdfff: 256 394 241 0 + // 0xe000-0xefff: 378 343 173 0 + // 0xf000-0xffff: 3018 2040 1932 15450 +} diff --git a/libgo/go/image/ycbcr_test.go b/libgo/go/image/ycbcr_test.go index eb8b1950bfe..b2373f79ba3 100644 --- a/libgo/go/image/ycbcr_test.go +++ b/libgo/go/image/ycbcr_test.go @@ -50,6 +50,9 @@ func TestYCbCr(t *testing.T) { testYCbCr(t, r, subsampleRatio, delta) } } + if testing.Short() { + break + } } } diff --git a/libgo/go/io/ioutil/tempfile.go b/libgo/go/io/ioutil/tempfile.go index 645eed6abb8..42d2e675869 100644 --- a/libgo/go/io/ioutil/tempfile.go +++ b/libgo/go/io/ioutil/tempfile.go @@ -49,7 +49,7 @@ func TempFile(dir, prefix string) (f *os.File, err error) { for i := 0; i < 10000; i++ { name := filepath.Join(dir, prefix+nextSuffix()) f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) - if pe, ok := err.(*os.PathError); ok && pe.Err == os.EEXIST { + if os.IsExist(err) { if nconflict++; nconflict > 10 { rand = reseed() } @@ -76,7 +76,7 @@ func TempDir(dir, prefix string) (name string, err error) { for i := 0; i < 10000; i++ { try := filepath.Join(dir, prefix+nextSuffix()) err = os.Mkdir(try, 0700) - if pe, ok := err.(*os.PathError); ok && pe.Err == os.EEXIST { + if os.IsExist(err) { if nconflict++; nconflict > 10 { rand = reseed() } diff --git a/libgo/go/log/syslog/syslog.go b/libgo/go/log/syslog/syslog.go index 3eb5353e9a9..f53310cb0a1 100644 --- a/libgo/go/log/syslog/syslog.go +++ b/libgo/go/log/syslog/syslog.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +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. diff --git a/libgo/go/log/syslog/syslog_test.go b/libgo/go/log/syslog/syslog_test.go index 7f509b3666e..0fd6239059a 100644 --- a/libgo/go/log/syslog/syslog_test.go +++ b/libgo/go/log/syslog/syslog_test.go @@ -1,6 +1,9 @@ // Copyright 2009 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 !windows,!plan9 + package syslog import ( diff --git a/libgo/go/log/syslog/syslog_unix.go b/libgo/go/log/syslog/syslog_unix.go index b1c929ad2fe..46a164dd577 100644 --- a/libgo/go/log/syslog/syslog_unix.go +++ b/libgo/go/log/syslog/syslog_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !windows,!plan9 + package syslog import ( diff --git a/libgo/go/math/big/nat_test.go b/libgo/go/math/big/nat_test.go index 25e39273c0c..7f3f76dc36f 100644 --- a/libgo/go/math/big/nat_test.go +++ b/libgo/go/math/big/nat_test.go @@ -512,6 +512,9 @@ func TestStringPowers(t *testing.T) { t.Errorf("failed at %d ** %d in base %d: %s != %s", b, p, b, xs, xs2) } } + if b >= 3 && testing.Short() { + break + } } } diff --git a/libgo/go/math/rand/rand.go b/libgo/go/math/rand/rand.go index 89552192030..94f84a85fbe 100644 --- a/libgo/go/math/rand/rand.go +++ b/libgo/go/math/rand/rand.go @@ -49,9 +49,10 @@ func (r *Rand) Int() int { } // Int63n returns, as an int64, a non-negative pseudo-random number in [0,n). +// It panics if n <= 0. func (r *Rand) Int63n(n int64) int64 { if n <= 0 { - return 0 + panic("invalid argument to Int63n") } max := int64((1 << 63) - 1 - (1<<63)%uint64(n)) v := r.Int63() @@ -62,9 +63,10 @@ func (r *Rand) Int63n(n int64) int64 { } // Int31n returns, as an int32, a non-negative pseudo-random number in [0,n). +// It panics if n <= 0. func (r *Rand) Int31n(n int32) int32 { if n <= 0 { - return 0 + panic("invalid argument to Int31n") } max := int32((1 << 31) - 1 - (1<<31)%uint32(n)) v := r.Int31() @@ -75,7 +77,11 @@ func (r *Rand) Int31n(n int32) int32 { } // Intn returns, as an int, a non-negative pseudo-random number in [0,n). +// It panics if n <= 0. func (r *Rand) Intn(n int) int { + if n <= 0 { + panic("invalid argument to Intn") + } if n <= 1<<31-1 { return int(r.Int31n(int32(n))) } @@ -125,12 +131,15 @@ func Int31() int32 { return globalRand.Int31() } func Int() int { return globalRand.Int() } // Int63n returns, as an int64, a non-negative pseudo-random number in [0,n). +// It panics if n <= 0. func Int63n(n int64) int64 { return globalRand.Int63n(n) } // Int31n returns, as an int32, a non-negative pseudo-random number in [0,n). +// It panics if n <= 0. func Int31n(n int32) int32 { return globalRand.Int31n(n) } // Intn returns, as an int, a non-negative pseudo-random number in [0,n). +// It panics if n <= 0. func Intn(n int) int { return globalRand.Intn(n) } // Float64 returns, as a float64, a pseudo-random number in [0.0,1.0). diff --git a/libgo/go/math/rand/rand_test.go b/libgo/go/math/rand/rand_test.go index 0ba8f98c496..bbd44e3f8b1 100644 --- a/libgo/go/math/rand/rand_test.go +++ b/libgo/go/math/rand/rand_test.go @@ -141,6 +141,9 @@ func TestNonStandardNormalValues(t *testing.T) { for m := 0.5; m < mmax; m *= 2 { for _, seed := range testSeeds { testNormalDistribution(t, numTestSamples, m, sd, seed) + if testing.Short() { + break + } } } } @@ -191,6 +194,9 @@ func TestNonStandardExponentialValues(t *testing.T) { for rate := 0.05; rate < 10; rate *= 2 { for _, seed := range testSeeds { testExponentialDistribution(t, numTestSamples, rate, seed) + if testing.Short() { + break + } } } } diff --git a/libgo/go/net/dialgoogle_test.go b/libgo/go/net/dialgoogle_test.go index 81750a3d739..14356da4ce3 100644 --- a/libgo/go/net/dialgoogle_test.go +++ b/libgo/go/net/dialgoogle_test.go @@ -14,7 +14,7 @@ import ( ) // If an IPv6 tunnel is running, we can try dialing a real IPv6 address. -var ipv6 = flag.Bool("ipv6", false, "assume ipv6 tunnel is present") +var testIPv6 = flag.Bool("ipv6", false, "assume ipv6 tunnel is present") // fd is already connected to the destination, port 80. // Run an HTTP request to fetch the appropriate page. @@ -130,7 +130,7 @@ func TestDialGoogleIPv6(t *testing.T) { return } // Only run tcp6 if the kernel will take it. - if !*ipv6 || !supportsIPv6 { + if !*testIPv6 || !supportsIPv6 { return } diff --git a/libgo/go/net/fd.go b/libgo/go/net/fd.go index bf0a387775d..ae1bf2614a2 100644 --- a/libgo/go/net/fd.go +++ b/libgo/go/net/fd.go @@ -252,7 +252,9 @@ func (s *pollServer) Run() { } else { netfd := s.LookupFD(fd, mode) if netfd == nil { - print("pollServer: unexpected wakeup for fd=", fd, " mode=", string(mode), "\n") + // This can happen because the WaitFD runs without + // holding s's lock, so there might be a pending wakeup + // for an fd that has been evicted. No harm done. continue } s.WakeFD(netfd, mode, nil) @@ -506,7 +508,7 @@ func (fd *netFD) Write(p []byte) (int, error) { } defer fd.decref() if fd.sysfile == nil { - return 0, os.EINVAL + return 0, syscall.EINVAL } var err error diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go index efd846e5d8c..45f5c2d882f 100644 --- a/libgo/go/net/fd_windows.go +++ b/libgo/go/net/fd_windows.go @@ -335,7 +335,7 @@ func (fd *netFD) Close() error { func (fd *netFD) shutdown(how int) error { if fd == nil || fd.sysfd == syscall.InvalidHandle { - return os.EINVAL + return syscall.EINVAL } err := syscall.Shutdown(fd.sysfd, how) if err != nil { @@ -369,7 +369,7 @@ func (o *readOp) Name() string { func (fd *netFD) Read(buf []byte) (int, error) { if fd == nil { - return 0, os.EINVAL + return 0, syscall.EINVAL } fd.rio.Lock() defer fd.rio.Unlock() @@ -378,7 +378,7 @@ func (fd *netFD) Read(buf []byte) (int, error) { } defer fd.decref() if fd.sysfd == syscall.InvalidHandle { - return 0, os.EINVAL + return 0, syscall.EINVAL } var o readOp o.Init(fd, buf, 'r') @@ -408,7 +408,7 @@ func (o *readFromOp) Name() string { func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) { if fd == nil { - return 0, nil, os.EINVAL + return 0, nil, syscall.EINVAL } if len(buf) == 0 { return 0, nil, nil @@ -447,7 +447,7 @@ func (o *writeOp) Name() string { func (fd *netFD) Write(buf []byte) (int, error) { if fd == nil { - return 0, os.EINVAL + return 0, syscall.EINVAL } fd.wio.Lock() defer fd.wio.Unlock() @@ -478,7 +478,7 @@ func (o *writeToOp) Name() string { func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) { if fd == nil { - return 0, os.EINVAL + return 0, syscall.EINVAL } if len(buf) == 0 { return 0, nil @@ -490,7 +490,7 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) { } defer fd.decref() if fd.sysfd == syscall.InvalidHandle { - return 0, os.EINVAL + return 0, syscall.EINVAL } var o writeToOp o.Init(fd, buf, 'w') @@ -578,10 +578,12 @@ func (fd *netFD) dup() (*os.File, error) { return nil, os.NewSyscallError("dup", syscall.EWINDOWS) } +var errNoSupport = errors.New("address family not supported") + func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) { - return 0, 0, 0, nil, os.EAFNOSUPPORT + return 0, 0, 0, nil, errNoSupport } func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) { - return 0, 0, os.EAFNOSUPPORT + return 0, 0, errNoSupport } diff --git a/libgo/go/net/file.go b/libgo/go/net/file.go index f9546dc930d..c95d16d64e7 100644 --- a/libgo/go/net/file.go +++ b/libgo/go/net/file.go @@ -28,7 +28,7 @@ func newFileFD(f *os.File) (*netFD, error) { switch sa.(type) { default: closesocket(fd) - return nil, os.EINVAL + return nil, syscall.EINVAL case *syscall.SockaddrInet4: family = syscall.AF_INET if proto == syscall.SOCK_DGRAM { @@ -84,7 +84,7 @@ func FileConn(f *os.File) (c Conn, err error) { return newIPConn(fd), nil } fd.Close() - return nil, os.EINVAL + return nil, syscall.EINVAL } // FileListener returns a copy of the network listener corresponding @@ -103,7 +103,7 @@ func FileListener(f *os.File) (l Listener, err error) { return &UnixListener{fd, laddr.Name}, nil } fd.Close() - return nil, os.EINVAL + return nil, syscall.EINVAL } // FilePacketConn returns a copy of the packet network connection @@ -122,5 +122,5 @@ func FilePacketConn(f *os.File) (c PacketConn, err error) { return newUnixConn(fd), nil } fd.Close() - return nil, os.EINVAL + return nil, syscall.EINVAL } diff --git a/libgo/go/net/file_plan9.go b/libgo/go/net/file_plan9.go index 06d7cc89846..04f7ee0401b 100644 --- a/libgo/go/net/file_plan9.go +++ b/libgo/go/net/file_plan9.go @@ -6,6 +6,7 @@ package net import ( "os" + "syscall" ) // FileConn returns a copy of the network connection corresponding to @@ -13,7 +14,7 @@ import ( // finished. Closing c does not affect f, and closing f does not // affect c. func FileConn(f *os.File) (c Conn, err error) { - return nil, os.EPLAN9 + return nil, syscall.EPLAN9 } // FileListener returns a copy of the network listener corresponding @@ -21,7 +22,7 @@ func FileConn(f *os.File) (c Conn, err error) { // when finished. Closing c does not affect l, and closing l does not // affect c. func FileListener(f *os.File) (l Listener, err error) { - return nil, os.EPLAN9 + return nil, syscall.EPLAN9 } // FilePacketConn returns a copy of the packet network connection @@ -29,5 +30,5 @@ func FileListener(f *os.File) (l Listener, err error) { // responsibility to close f when finished. Closing c does not affect // f, and closing f does not affect c. func FilePacketConn(f *os.File) (c PacketConn, err error) { - return nil, os.EPLAN9 + return nil, syscall.EPLAN9 } diff --git a/libgo/go/net/hosts_test.go b/libgo/go/net/hosts_test.go index 1bd00541c6d..064e7e43282 100644 --- a/libgo/go/net/hosts_test.go +++ b/libgo/go/net/hosts_test.go @@ -34,7 +34,7 @@ var hosttests = []hostTest{ func TestLookupStaticHost(t *testing.T) { p := hostsPath - hostsPath = "hosts_testdata" + hostsPath = "testdata/hosts" for i := 0; i < len(hosttests); i++ { tt := hosttests[i] ips := lookupStaticHost(tt.host) diff --git a/libgo/go/net/http/cookie_test.go b/libgo/go/net/http/cookie_test.go index 712350dfcef..1e9186a0581 100644 --- a/libgo/go/net/http/cookie_test.go +++ b/libgo/go/net/http/cookie_test.go @@ -128,6 +128,34 @@ var readSetCookiesTests = []struct { Raw: "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly", }}, }, + { + Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}}, + []*Cookie{{ + Name: ".ASPXAUTH", + Value: "7E3AA", + Path: "/", + Expires: time.Date(2012, 3, 7, 14, 25, 6, 0, time.UTC), + RawExpires: "Wed, 07-Mar-2012 14:25:06 GMT", + HttpOnly: true, + Raw: ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly", + }}, + }, + { + Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}}, + []*Cookie{{ + Name: "ASP.NET_SessionId", + Value: "foo", + Path: "/", + HttpOnly: true, + Raw: "ASP.NET_SessionId=foo; path=/; HttpOnly", + }}, + }, + + // TODO(bradfitz): users have reported seeing this in the + // wild, but do browsers handle it? RFC 6265 just says "don't + // do that" (section 3) and then never mentions header folding + // again. + // Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly, .ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}}, } func toJSON(v interface{}) string { diff --git a/libgo/go/net/http/example_test.go b/libgo/go/net/http/example_test.go new file mode 100644 index 00000000000..2584afc439e --- /dev/null +++ b/libgo/go/net/http/example_test.go @@ -0,0 +1,51 @@ +// 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 http_test + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" +) + +func ExampleHijacker() { + http.HandleFunc("/hijack", func(w http.ResponseWriter, r *http.Request) { + hj, ok := w.(http.Hijacker) + if !ok { + http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError) + return + } + conn, bufrw, err := hj.Hijack() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + // Don't forget to close the connection: + defer conn.Close() + bufrw.WriteString("Now we're speaking raw TCP. Say hi: ") + bufrw.Flush() + s, err := bufrw.ReadString('\n') + if err != nil { + log.Printf("error reading string: %v", err) + return + } + fmt.Fprintf(bufrw, "You said: %q\nBye.\n", s) + bufrw.Flush() + }) +} + +func ExampleGet() { + res, err := http.Get("http://www.google.com/robots.txt") + if err != nil { + log.Fatal(err) + } + robots, err := ioutil.ReadAll(res.Body) + if err != nil { + log.Fatal(err) + } + res.Body.Close() + fmt.Printf("%s", robots) +} diff --git a/libgo/go/net/http/fs_test.go b/libgo/go/net/http/fs_test.go index 143617e95fc..0409008b675 100644 --- a/libgo/go/net/http/fs_test.go +++ b/libgo/go/net/http/fs_test.go @@ -6,6 +6,7 @@ package http_test import ( "bytes" + "errors" "fmt" "io" "io/ioutil" @@ -131,7 +132,7 @@ func TestFileServerCleans(t *testing.T) { ch := make(chan string, 1) fs := FileServer(&testFileSystem{func(name string) (File, error) { ch <- name - return nil, os.ENOENT + return nil, errors.New("file does not exist") }}) tests := []struct { reqPath, openArg string @@ -398,11 +399,15 @@ func TestLinuxSendfile(t *testing.T) { return } - _, err = Get(fmt.Sprintf("http://%s/", ln.Addr())) + res, err := Get(fmt.Sprintf("http://%s/", ln.Addr())) if err != nil { - t.Errorf("http client error: %v", err) - return + t.Fatalf("http client error: %v", err) + } + _, err = io.Copy(ioutil.Discard, res.Body) + if err != nil { + t.Fatalf("client body read error: %v", err) } + res.Body.Close() // Force child to exit cleanly. Get(fmt.Sprintf("http://%s/quit", ln.Addr())) diff --git a/libgo/go/net/http/httputil/persist.go b/libgo/go/net/http/httputil/persist.go index c065ccfb499..32f4662cc0e 100644 --- a/libgo/go/net/http/httputil/persist.go +++ b/libgo/go/net/http/httputil/persist.go @@ -13,12 +13,12 @@ import ( "net" "net/http" "net/textproto" - "os" "sync" ) var ( ErrPersistEOF = &http.ProtocolError{ErrorString: "persistent connection closed"} + ErrClosed = &http.ProtocolError{ErrorString: "connection closed by user"} ErrPipeline = &http.ProtocolError{ErrorString: "pipeline error"} ) @@ -191,7 +191,7 @@ func (sc *ServerConn) Write(req *http.Request, resp *http.Response) error { } if sc.c == nil { // connection closed by user in the meantime defer sc.lk.Unlock() - return os.EBADF + return ErrClosed } c := sc.c if sc.nread <= sc.nwritten { diff --git a/libgo/go/net/http/pprof/pprof.go b/libgo/go/net/http/pprof/pprof.go index 0fe41b7d31b..06fcde1447f 100644 --- a/libgo/go/net/http/pprof/pprof.go +++ b/libgo/go/net/http/pprof/pprof.go @@ -22,9 +22,9 @@ // // go tool pprof http://localhost:6060/debug/pprof/profile // -// Or to look at the thread creation profile: +// Or to view all available profiles: // -// go tool pprof http://localhost:6060/debug/pprof/thread +// go tool pprof http://localhost:6060/debug/pprof/ // // For a study of the facility in action, visit // @@ -36,7 +36,9 @@ import ( "bufio" "bytes" "fmt" + "html/template" "io" + "log" "net/http" "os" "runtime" @@ -47,11 +49,10 @@ import ( ) func init() { + http.Handle("/debug/pprof/", http.HandlerFunc(Index)) http.Handle("/debug/pprof/cmdline", http.HandlerFunc(Cmdline)) http.Handle("/debug/pprof/profile", http.HandlerFunc(Profile)) - http.Handle("/debug/pprof/heap", http.HandlerFunc(Heap)) http.Handle("/debug/pprof/symbol", http.HandlerFunc(Symbol)) - http.Handle("/debug/pprof/thread", http.HandlerFunc(Thread)) } // Cmdline responds with the running program's @@ -62,20 +63,6 @@ func Cmdline(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, strings.Join(os.Args, "\x00")) } -// Heap responds with the pprof-formatted heap profile. -// The package initialization registers it as /debug/pprof/heap. -func Heap(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - pprof.WriteHeapProfile(w) -} - -// Thread responds with the pprof-formatted thread creation profile. -// The package initialization registers it as /debug/pprof/thread. -func Thread(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - pprof.WriteThreadProfile(w) -} - // Profile responds with the pprof-formatted cpu profile. // The package initialization registers it as /debug/pprof/profile. func Profile(w http.ResponseWriter, r *http.Request) { @@ -147,3 +134,61 @@ func Symbol(w http.ResponseWriter, r *http.Request) { w.Write(buf.Bytes()) } + +// Handler returns an HTTP handler that serves the named profile. +func Handler(name string) http.Handler { + return handler(name) +} + +type handler string + +func (name handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + debug, _ := strconv.Atoi(r.FormValue("debug")) + p := pprof.Lookup(string(name)) + if p == nil { + w.WriteHeader(404) + fmt.Fprintf(w, "Unknown profile: %s\n", name) + return + } + p.WriteTo(w, debug) + return +} + +// Index responds with the pprof-formatted profile named by the request. +// For example, "/debug/pprof/heap" serves the "heap" profile. +// Index responds to a request for "/debug/pprof/" with an HTML page +// listing the available profiles. +func Index(w http.ResponseWriter, r *http.Request) { + if strings.HasPrefix(r.URL.Path, "/debug/pprof/") { + name := r.URL.Path[len("/debug/pprof/"):] + if name != "" { + handler(name).ServeHTTP(w, r) + return + } + } + + profiles := pprof.Profiles() + if err := indexTmpl.Execute(w, profiles); err != nil { + log.Print(err) + } +} + +var indexTmpl = template.Must(template.New("index").Parse(`<html> +<head> +<title>/debug/pprof/</title> +</head> +/debug/pprof/<br> +<br> +<body> +profiles:<br> +<table> +{{range .}} +<tr><td align=right>{{.Count}}<td><a href="/debug/pprof/{{.Name}}?debug=1">{{.Name}}</a> +{{end}} +</table> +<br> +<a href="/debug/pprof/goroutine?debug=2">full goroutine stack dump</a><br> +</body> +</html> +`)) diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go index 0bbec53be71..5277657805d 100644 --- a/libgo/go/net/http/request.go +++ b/libgo/go/net/http/request.go @@ -186,7 +186,7 @@ func (r *Request) Cookies() []*Cookie { return readCookies(r.Header, "") } -var ErrNoCookie = errors.New("http: named cookied not present") +var ErrNoCookie = errors.New("http: named cookie not present") // Cookie returns the named cookie provided in the request or // ErrNoCookie if not found. @@ -486,7 +486,7 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) { rawurl = "http://" + rawurl } - if req.URL, err = url.ParseRequest(rawurl); err != nil { + if req.URL, err = url.ParseRequestURI(rawurl); err != nil { return nil, err } diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go index e2860c3edcf..b6a6b4c77d1 100644 --- a/libgo/go/net/http/serve_test.go +++ b/libgo/go/net/http/serve_test.go @@ -245,8 +245,7 @@ func TestServerTimeouts(t *testing.T) { fmt.Fprintf(res, "req=%d", reqNum) }) - const second = 1000000000 /* nanos */ - server := &Server{Handler: handler, ReadTimeout: 0.25 * second, WriteTimeout: 0.25 * second} + server := &Server{Handler: handler, ReadTimeout: 250 * time.Millisecond, WriteTimeout: 250 * time.Millisecond} go server.Serve(l) url := fmt.Sprintf("http://%s/", addr) @@ -277,7 +276,7 @@ func TestServerTimeouts(t *testing.T) { if n != 0 || err != io.EOF { t.Errorf("Read = %v, %v, wanted %v, %v", n, err, 0, io.EOF) } - if latency < 200*time.Millisecond /* fudge from 0.25 above */ { + if latency < 200*time.Millisecond /* fudge from 250 ms above */ { t.Errorf("got EOF after %s, want >= %s", latency, 200*time.Millisecond) } diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go index e715c73cb6e..fa0df54a236 100644 --- a/libgo/go/net/http/server.go +++ b/libgo/go/net/http/server.go @@ -12,7 +12,6 @@ package http import ( "bufio" "bytes" - "crypto/rand" "crypto/tls" "errors" "fmt" @@ -985,6 +984,7 @@ type Server struct { ReadTimeout time.Duration // maximum duration before timing out read of the request WriteTimeout time.Duration // maximum duration before timing out write of the response MaxHeaderBytes int // maximum size of request headers, DefaultMaxHeaderBytes if 0 + TLSConfig *tls.Config // optional TLS config, used by ListenAndServeTLS } // ListenAndServe listens on the TCP network address srv.Addr and then @@ -1121,9 +1121,12 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error { if addr == "" { addr = ":https" } - config := &tls.Config{ - Rand: rand.Reader, - NextProtos: []string{"http/1.1"}, + config := &tls.Config{} + if srv.TLSConfig != nil { + *config = *srv.TLSConfig + } + if config.NextProtos == nil { + config.NextProtos = []string{"http/1.1"} } var err error diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go index a36571a4446..1a629c1727c 100644 --- a/libgo/go/net/http/transport_test.go +++ b/libgo/go/net/http/transport_test.go @@ -648,7 +648,7 @@ func TestTransportPersistConnLeak(t *testing.T) { tr := &Transport{} c := &Client{Transport: tr} - n0 := runtime.Goroutines() + n0 := runtime.NumGoroutine() const numReq = 25 didReqCh := make(chan bool) @@ -669,7 +669,7 @@ func TestTransportPersistConnLeak(t *testing.T) { <-gotReqCh } - nhigh := runtime.Goroutines() + nhigh := runtime.NumGoroutine() // Tell all handlers to unblock and reply. for i := 0; i < numReq; i++ { @@ -685,7 +685,7 @@ func TestTransportPersistConnLeak(t *testing.T) { time.Sleep(100 * time.Millisecond) runtime.GC() runtime.GC() // even more. - nfinal := runtime.Goroutines() + nfinal := runtime.NumGoroutine() growth := nfinal - n0 diff --git a/libgo/go/net/interface_linux.go b/libgo/go/net/interface_linux.go index 21038c629b1..15c2f3781b1 100644 --- a/libgo/go/net/interface_linux.go +++ b/libgo/go/net/interface_linux.go @@ -166,13 +166,13 @@ func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) { return nil, err } } - ifmat4 := parseProcNetIGMP(ifi) - ifmat6 := parseProcNetIGMP6(ifi) + ifmat4 := parseProcNetIGMP("/proc/net/igmp", ifi) + ifmat6 := parseProcNetIGMP6("/proc/net/igmp6", ifi) return append(ifmat4, ifmat6...), nil } -func parseProcNetIGMP(ifi *Interface) []Addr { - fd, err := open("/proc/net/igmp") +func parseProcNetIGMP(path string, ifi *Interface) []Addr { + fd, err := open(path) if err != nil { return nil } @@ -185,23 +185,26 @@ func parseProcNetIGMP(ifi *Interface) []Addr { fd.readLine() // skip first line b := make([]byte, IPv4len) for l, ok := fd.readLine(); ok; l, ok = fd.readLine() { - f := getFields(l) - switch len(f) { - case 4: + f := splitAtBytes(l, " :\r\t\n") + if len(f) < 4 { + continue + } + switch { + case l[0] != ' ' && l[0] != '\t': // new interface line + name = f[1] + case len(f[0]) == 8: if ifi == nil || name == ifi.Name { fmt.Sscanf(f[0], "%08x", &b) ifma := IPAddr{IP: IPv4(b[3], b[2], b[1], b[0])} ifmat = append(ifmat, ifma.toAddr()) } - case 5: - name = f[1] } } return ifmat } -func parseProcNetIGMP6(ifi *Interface) []Addr { - fd, err := open("/proc/net/igmp6") +func parseProcNetIGMP6(path string, ifi *Interface) []Addr { + fd, err := open(path) if err != nil { return nil } @@ -210,7 +213,10 @@ func parseProcNetIGMP6(ifi *Interface) []Addr { var ifmat []Addr b := make([]byte, IPv6len) for l, ok := fd.readLine(); ok; l, ok = fd.readLine() { - f := getFields(l) + f := splitAtBytes(l, " \r\t\n") + if len(f) < 6 { + continue + } if ifi == nil || f[1] == ifi.Name { fmt.Sscanf(f[2], "%32x", &b) ifma := IPAddr{IP: IP{b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]}} diff --git a/libgo/go/net/interface_test.go b/libgo/go/net/interface_test.go index 4ce01dc9061..769414e0eef 100644 --- a/libgo/go/net/interface_test.go +++ b/libgo/go/net/interface_test.go @@ -31,17 +31,17 @@ func TestInterfaces(t *testing.T) { for _, ifi := range ift { ifxi, err := InterfaceByIndex(ifi.Index) if err != nil { - t.Fatalf("InterfaceByIndex(%#q) failed: %v", ifi.Index, err) + t.Fatalf("InterfaceByIndex(%q) failed: %v", ifi.Index, err) } if !sameInterface(ifxi, &ifi) { - t.Fatalf("InterfaceByIndex(%#q) = %v, want %v", ifi.Index, *ifxi, ifi) + t.Fatalf("InterfaceByIndex(%q) = %v, want %v", ifi.Index, *ifxi, ifi) } ifxn, err := InterfaceByName(ifi.Name) if err != nil { - t.Fatalf("InterfaceByName(%#q) failed: %v", ifi.Name, err) + t.Fatalf("InterfaceByName(%q) failed: %v", ifi.Name, err) } if !sameInterface(ifxn, &ifi) { - t.Fatalf("InterfaceByName(%#q) = %v, want %v", ifi.Name, *ifxn, ifi) + t.Fatalf("InterfaceByName(%q) = %v, want %v", ifi.Name, *ifxn, ifi) } t.Logf("%q: flags %q, ifindex %v, mtu %v\n", ifi.Name, ifi.Flags.String(), ifi.Index, ifi.MTU) t.Logf("\thardware address %q", ifi.HardwareAddr.String()) diff --git a/libgo/go/net/ipraw_test.go b/libgo/go/net/ipraw_test.go index f9401c1104e..6136202727c 100644 --- a/libgo/go/net/ipraw_test.go +++ b/libgo/go/net/ipraw_test.go @@ -7,6 +7,7 @@ package net import ( "bytes" "os" + "syscall" "testing" "time" ) @@ -15,7 +16,7 @@ var icmpTests = []struct { net string laddr string raddr string - ipv6 bool + ipv6 bool // test with underlying AF_INET6 socket }{ {"ip4:icmp", "", "127.0.0.1", false}, {"ip6:icmp", "", "::1", true}, @@ -34,15 +35,15 @@ func TestICMP(t *testing.T) { } id := os.Getpid() & 0xffff seqnum++ - echo := newICMPEchoRequest(tt.ipv6, id, seqnum, 128, []byte("Go Go Gadget Ping!!!")) - exchangeICMPEcho(t, tt.net, tt.laddr, tt.raddr, tt.ipv6, echo) + echo := newICMPEchoRequest(tt.net, id, seqnum, 128, []byte("Go Go Gadget Ping!!!")) + exchangeICMPEcho(t, tt.net, tt.laddr, tt.raddr, echo) } } -func exchangeICMPEcho(t *testing.T, net, laddr, raddr string, ipv6 bool, echo []byte) { +func exchangeICMPEcho(t *testing.T, net, laddr, raddr string, echo []byte) { c, err := ListenPacket(net, laddr) if err != nil { - t.Errorf("ListenPacket(%#q, %#q) failed: %v", net, laddr, err) + t.Errorf("ListenPacket(%q, %q) failed: %v", net, laddr, err) return } c.SetDeadline(time.Now().Add(100 * time.Millisecond)) @@ -50,12 +51,12 @@ func exchangeICMPEcho(t *testing.T, net, laddr, raddr string, ipv6 bool, echo [] ra, err := ResolveIPAddr(net, raddr) if err != nil { - t.Errorf("ResolveIPAddr(%#q, %#q) failed: %v", net, raddr, err) + t.Errorf("ResolveIPAddr(%q, %q) failed: %v", net, raddr, err) return } waitForReady := make(chan bool) - go icmpEchoTransponder(t, net, raddr, ipv6, waitForReady) + go icmpEchoTransponder(t, net, raddr, waitForReady) <-waitForReady _, err = c.WriteTo(echo, ra) @@ -71,11 +72,15 @@ func exchangeICMPEcho(t *testing.T, net, laddr, raddr string, ipv6 bool, echo [] t.Errorf("ReadFrom failed: %v", err) return } - if !ipv6 && reply[0] != ICMP4_ECHO_REPLY { - continue - } - if ipv6 && reply[0] != ICMP6_ECHO_REPLY { - continue + switch c.(*IPConn).fd.family { + case syscall.AF_INET: + if reply[0] != ICMP4_ECHO_REPLY { + continue + } + case syscall.AF_INET6: + if reply[0] != ICMP6_ECHO_REPLY { + continue + } } xid, xseqnum := parseICMPEchoReply(echo) rid, rseqnum := parseICMPEchoReply(reply) @@ -87,11 +92,11 @@ func exchangeICMPEcho(t *testing.T, net, laddr, raddr string, ipv6 bool, echo [] } } -func icmpEchoTransponder(t *testing.T, net, raddr string, ipv6 bool, waitForReady chan bool) { +func icmpEchoTransponder(t *testing.T, net, raddr string, waitForReady chan bool) { c, err := Dial(net, raddr) if err != nil { waitForReady <- true - t.Errorf("Dial(%#q, %#q) failed: %v", net, raddr, err) + t.Errorf("Dial(%q, %q) failed: %v", net, raddr, err) return } c.SetDeadline(time.Now().Add(100 * time.Millisecond)) @@ -106,18 +111,23 @@ func icmpEchoTransponder(t *testing.T, net, raddr string, ipv6 bool, waitForRead t.Errorf("Read failed: %v", err) return } - if !ipv6 && echo[0] != ICMP4_ECHO_REQUEST { - continue - } - if ipv6 && echo[0] != ICMP6_ECHO_REQUEST { - continue + switch c.(*IPConn).fd.family { + case syscall.AF_INET: + if echo[0] != ICMP4_ECHO_REQUEST { + continue + } + case syscall.AF_INET6: + if echo[0] != ICMP6_ECHO_REQUEST { + continue + } } break } - if !ipv6 { + switch c.(*IPConn).fd.family { + case syscall.AF_INET: echo[0] = ICMP4_ECHO_REPLY - } else { + case syscall.AF_INET6: echo[0] = ICMP6_ECHO_REPLY } @@ -135,11 +145,15 @@ const ( ICMP6_ECHO_REPLY = 129 ) -func newICMPEchoRequest(ipv6 bool, id, seqnum, msglen int, filler []byte) []byte { - if !ipv6 { +func newICMPEchoRequest(net string, id, seqnum, msglen int, filler []byte) []byte { + afnet, _, _ := parseDialNetwork(net) + switch afnet { + case "ip4": return newICMPv4EchoRequest(id, seqnum, msglen, filler) + case "ip6": + return newICMPv6EchoRequest(id, seqnum, msglen, filler) } - return newICMPv6EchoRequest(id, seqnum, msglen, filler) + return nil } func newICMPv4EchoRequest(id, seqnum, msglen int, filler []byte) []byte { diff --git a/libgo/go/net/iprawsock_plan9.go b/libgo/go/net/iprawsock_plan9.go index 382a4402770..43719fc99cd 100644 --- a/libgo/go/net/iprawsock_plan9.go +++ b/libgo/go/net/iprawsock_plan9.go @@ -7,7 +7,7 @@ package net import ( - "os" + "syscall" "time" ) @@ -17,34 +17,34 @@ type IPConn bool // SetDeadline implements the Conn SetDeadline method. func (c *IPConn) SetDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } // SetReadDeadline implements the Conn SetReadDeadline method. func (c *IPConn) SetReadDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } // SetWriteDeadline implements the Conn SetWriteDeadline method. func (c *IPConn) SetWriteDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } // 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, os.EPLAN9 + return 0, syscall.EPLAN9 } // Write implements the Conn Write method. func (c *IPConn) Write(b []byte) (int, error) { - return 0, os.EPLAN9 + return 0, syscall.EPLAN9 } // Close closes the IP connection. func (c *IPConn) Close() error { - return os.EPLAN9 + return syscall.EPLAN9 } // LocalAddr returns the local network address. @@ -67,12 +67,12 @@ func (c *IPConn) RemoteAddr() Addr { // Timeout() == true after a fixed time limit; see SetDeadline and // SetReadDeadline. func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) { - return 0, nil, os.EPLAN9 + return 0, nil, syscall.EPLAN9 } // ReadFrom implements the PacketConn ReadFrom method. func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) { - return 0, nil, os.EPLAN9 + return 0, nil, syscall.EPLAN9 } // WriteToIP writes a IP packet to addr via c, copying the payload from b. @@ -82,18 +82,18 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) { // see SetDeadline and SetWriteDeadline. // On packet-oriented connections, write timeouts are rare. func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) { - return 0, os.EPLAN9 + return 0, syscall.EPLAN9 } // WriteTo implements the PacketConn WriteTo method. func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) { - return 0, os.EPLAN9 + return 0, syscall.EPLAN9 } // DialIP connects to the remote address raddr on the network protocol netProto, // which must be "ip", "ip4", or "ip6" followed by a colon and a protocol number or name. func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) { - return nil, os.EPLAN9 + return nil, syscall.EPLAN9 } // ListenIP listens for incoming IP packets addressed to the @@ -101,5 +101,5 @@ func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) { // and WriteTo methods can be used to receive and send IP // packets with per-packet addressing. func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) { - return nil, os.EPLAN9 + return nil, syscall.EPLAN9 } diff --git a/libgo/go/net/iprawsock_posix.go b/libgo/go/net/iprawsock_posix.go index c34ffeb121d..9caa86985a5 100644 --- a/libgo/go/net/iprawsock_posix.go +++ b/libgo/go/net/iprawsock_posix.go @@ -66,7 +66,7 @@ func (c *IPConn) Read(b []byte) (int, error) { // Write implements the Conn Write method. func (c *IPConn) Write(b []byte) (int, error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } return c.fd.Write(b) } @@ -74,7 +74,7 @@ func (c *IPConn) Write(b []byte) (int, error) { // Close closes the IP connection. func (c *IPConn) Close() error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } err := c.fd.Close() c.fd = nil @@ -100,7 +100,7 @@ func (c *IPConn) RemoteAddr() Addr { // SetDeadline implements the Conn SetDeadline method. func (c *IPConn) SetDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setDeadline(c.fd, t) } @@ -108,7 +108,7 @@ func (c *IPConn) SetDeadline(t time.Time) error { // SetReadDeadline implements the Conn SetReadDeadline method. func (c *IPConn) SetReadDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setReadDeadline(c.fd, t) } @@ -116,7 +116,7 @@ func (c *IPConn) SetReadDeadline(t time.Time) error { // SetWriteDeadline implements the Conn SetWriteDeadline method. func (c *IPConn) SetWriteDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setWriteDeadline(c.fd, t) } @@ -125,7 +125,7 @@ func (c *IPConn) SetWriteDeadline(t time.Time) error { // receive buffer associated with the connection. func (c *IPConn) SetReadBuffer(bytes int) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setReadBuffer(c.fd, bytes) } @@ -134,7 +134,7 @@ func (c *IPConn) SetReadBuffer(bytes int) error { // transmit buffer associated with the connection. func (c *IPConn) SetWriteBuffer(bytes int) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setWriteBuffer(c.fd, bytes) } @@ -150,7 +150,7 @@ func (c *IPConn) SetWriteBuffer(bytes int) error { // SetReadDeadline. func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) { if !c.ok() { - return 0, nil, os.EINVAL + return 0, nil, syscall.EINVAL } // TODO(cw,rsc): consider using readv if we know the family // type to avoid the header trim/copy @@ -173,7 +173,7 @@ func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) { // ReadFrom implements the PacketConn ReadFrom method. func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) { if !c.ok() { - return 0, nil, os.EINVAL + return 0, nil, syscall.EINVAL } n, uaddr, err := c.ReadFromIP(b) return n, uaddr.toAddr(), err @@ -187,7 +187,7 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) { // On packet-oriented connections, write timeouts are rare. func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } sa, err := addr.sockaddr(c.fd.family) if err != nil { @@ -199,11 +199,11 @@ func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) { // WriteTo implements the PacketConn WriteTo method. func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } a, ok := addr.(*IPAddr) if !ok { - return 0, &OpError{"write", c.fd.net, addr, os.EINVAL} + return 0, &OpError{"write", c.fd.net, addr, syscall.EINVAL} } return c.WriteToIP(b, a) } diff --git a/libgo/go/net/ipsock_plan9.go b/libgo/go/net/ipsock_plan9.go index 597b1277544..eab0bf3e899 100644 --- a/libgo/go/net/ipsock_plan9.go +++ b/libgo/go/net/ipsock_plan9.go @@ -10,6 +10,7 @@ import ( "errors" "io" "os" + "syscall" "time" ) @@ -83,7 +84,7 @@ func (c *plan9Conn) ok() bool { return c != nil && c.ctl != nil } // Read implements the Conn Read method. func (c *plan9Conn) Read(b []byte) (n int, err error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } if c.data == nil { c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) @@ -102,7 +103,7 @@ func (c *plan9Conn) Read(b []byte) (n int, err error) { // Write implements the Conn Write method. func (c *plan9Conn) Write(b []byte) (n int, err error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } if c.data == nil { c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) @@ -116,7 +117,7 @@ func (c *plan9Conn) Write(b []byte) (n int, err error) { // Close closes the connection. func (c *plan9Conn) Close() error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } err := c.ctl.Close() if err != nil { @@ -148,17 +149,17 @@ func (c *plan9Conn) RemoteAddr() Addr { // SetDeadline implements the Conn SetDeadline method. func (c *plan9Conn) SetDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } // SetReadDeadline implements the Conn SetReadDeadline method. func (c *plan9Conn) SetReadDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } // SetWriteDeadline implements the Conn SetWriteDeadline method. func (c *plan9Conn) SetWriteDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string, err error) { @@ -280,7 +281,7 @@ func (l *plan9Listener) Accept() (c Conn, err error) { func (l *plan9Listener) Close() error { if l == nil || l.ctl == nil { - return os.EINVAL + return syscall.EINVAL } return l.ctl.Close() } diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go index 4da18a5061a..4841057d6be 100644 --- a/libgo/go/net/ipsock_posix.go +++ b/libgo/go/net/ipsock_posix.go @@ -105,21 +105,20 @@ type sockaddr interface { } func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { - var oserr error var la, ra syscall.Sockaddr family := favoriteAddrFamily(net, laddr, raddr, mode) if laddr != nil { - if la, oserr = laddr.sockaddr(family); oserr != nil { + if la, err = laddr.sockaddr(family); err != nil { goto Error } } if raddr != nil { - if ra, oserr = raddr.sockaddr(family); oserr != nil { + if ra, err = raddr.sockaddr(family); err != nil { goto Error } } - fd, oserr = socket(net, family, sotype, proto, la, ra, toAddr) - if oserr != nil { + fd, err = socket(net, family, sotype, proto, la, ra, toAddr) + if err != nil { goto Error } return fd, nil @@ -129,7 +128,7 @@ Error: if mode == "listen" { addr = laddr } - return nil, &OpError{mode, net, addr, oserr} + return nil, &OpError{mode, net, addr, err} } func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) { diff --git a/libgo/go/net/lookup_plan9.go b/libgo/go/net/lookup_plan9.go index c0bb9225a7d..b08a9fb98e0 100644 --- a/libgo/go/net/lookup_plan9.go +++ b/libgo/go/net/lookup_plan9.go @@ -7,6 +7,7 @@ package net import ( "errors" "os" + "syscall" ) func query(filename, query string, bufSize int) (res []string, err error) { @@ -71,7 +72,7 @@ func queryDNS(addr string, typ string) (res []string, err error) { func lookupProtocol(name string) (proto int, err error) { // TODO: Implement this - return 0, os.EPLAN9 + return 0, syscall.EPLAN9 } func lookupHost(host string) (addrs []string, err error) { diff --git a/libgo/go/net/lookup_test.go b/libgo/go/net/lookup_test.go index 9a39ca8a1eb..7b9ea844cd4 100644 --- a/libgo/go/net/lookup_test.go +++ b/libgo/go/net/lookup_test.go @@ -8,14 +8,14 @@ package net import ( - "runtime" + "flag" "testing" ) -var avoidMacFirewall = runtime.GOOS == "darwin" +var testExternal = flag.Bool("external", false, "allow use of external networks during test") func TestGoogleSRV(t *testing.T) { - if testing.Short() || avoidMacFirewall { + if testing.Short() || !*testExternal { t.Logf("skipping test to avoid external network") return } @@ -38,7 +38,7 @@ func TestGoogleSRV(t *testing.T) { } func TestGmailMX(t *testing.T) { - if testing.Short() || avoidMacFirewall { + if testing.Short() || !*testExternal { t.Logf("skipping test to avoid external network") return } @@ -52,7 +52,7 @@ func TestGmailMX(t *testing.T) { } func TestGmailTXT(t *testing.T) { - if testing.Short() || avoidMacFirewall { + if testing.Short() || !*testExternal { t.Logf("skipping test to avoid external network") return } @@ -66,7 +66,7 @@ func TestGmailTXT(t *testing.T) { } func TestGoogleDNSAddr(t *testing.T) { - if testing.Short() || avoidMacFirewall { + if testing.Short() || !*testExternal { t.Logf("skipping test to avoid external network") return } diff --git a/libgo/go/net/multicast_test.go b/libgo/go/net/multicast_test.go index f62580de66e..1d760c21051 100644 --- a/libgo/go/net/multicast_test.go +++ b/libgo/go/net/multicast_test.go @@ -5,30 +5,46 @@ package net import ( + "errors" "os" "runtime" + "syscall" "testing" ) -var listenMulticastUDPTests = []struct { +var multicastListenerTests = []struct { net string gaddr *UDPAddr flags Flags - ipv6 bool + ipv6 bool // test with underlying AF_INET6 socket }{ // 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}, - {"udp4", &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}, + + {"udp4", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, FlagUp | FlagLoopback, false}, + {"udp4", &UDPAddr{IPv4(224, 0, 0, 254), 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}, } -func TestListenMulticastUDP(t *testing.T) { +// TestMulticastListener tests both single and double listen to a test +// listener with same address family, same group address and same port. +func TestMulticastListener(t *testing.T) { switch runtime.GOOS { case "netbsd", "openbsd", "plan9", "windows": return @@ -38,112 +54,142 @@ func TestListenMulticastUDP(t *testing.T) { } } - for _, tt := range listenMulticastUDPTests { + for _, tt := range multicastListenerTests { if tt.ipv6 && (!supportsIPv6 || os.Getuid() != 0) { continue } - ift, err := Interfaces() + ifi, err := availMulticastInterface(t, tt.flags) if err != nil { - t.Fatalf("Interfaces failed: %v", err) - } - var ifi *Interface - for _, x := range ift { - if x.Flags&tt.flags == tt.flags { - ifi = &x - break - } - } - if ifi == nil { - t.Logf("an appropriate multicast interface not found") - return + continue } - c, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr) + c1, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr) if err != nil { - t.Fatalf("ListenMulticastUDP failed: %v", err) + t.Fatalf("First ListenMulticastUDP failed: %v", err) } - defer c.Close() // test to listen concurrently across multiple listeners - if !tt.ipv6 { - testIPv4MulticastSocketOptions(t, c.fd, ifi) - } else { - testIPv6MulticastSocketOptions(t, c.fd, ifi) - } - ifmat, err := ifi.MulticastAddrs() + checkMulticastListener(t, err, c1, tt.gaddr) + c2, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr) if err != nil { - t.Fatalf("MulticastAddrs failed: %v", err) - } - var found bool - for _, ifma := range ifmat { - if ifma.(*IPAddr).IP.Equal(tt.gaddr.IP) { - found = true - break - } + t.Fatalf("Second ListenMulticastUDP failed: %v", err) } - if !found { - t.Fatalf("%q not found in RIB", tt.gaddr.String()) + checkMulticastListener(t, err, c2, tt.gaddr) + c2.Close() + switch c1.fd.family { + case syscall.AF_INET: + testIPv4MulticastSocketOptions(t, c1.fd, ifi) + case syscall.AF_INET6: + testIPv6MulticastSocketOptions(t, c1.fd, ifi) } + c1.Close() } } -func TestSimpleListenMulticastUDP(t *testing.T) { +func TestSimpleMulticastListener(t *testing.T) { switch runtime.GOOS { case "plan9": return } - for _, tt := range listenMulticastUDPTests { + for _, tt := range multicastListenerTests { if tt.ipv6 { continue } - tt.flags = FlagUp | FlagMulticast + tt.flags = FlagUp | FlagMulticast // for windows testing + ifi, err := availMulticastInterface(t, tt.flags) + if err != nil { + continue + } + c1, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr) + if err != nil { + t.Fatalf("First ListenMulticastUDP failed: %v", err) + } + checkSimpleMulticastListener(t, err, c1, tt.gaddr) + c2, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr) + if err != nil { + t.Fatalf("Second ListenMulticastUDP failed: %v", err) + } + checkSimpleMulticastListener(t, err, c2, tt.gaddr) + c2.Close() + c1.Close() + } +} + +func checkMulticastListener(t *testing.T, err error, c *UDPConn, gaddr *UDPAddr) { + if !multicastRIBContains(t, gaddr.IP) { + t.Fatalf("%q not found in RIB", gaddr.String()) + } + if c.LocalAddr().String() != gaddr.String() { + t.Fatalf("LocalAddr returns %q, expected %q", c.LocalAddr().String(), gaddr.String()) + } +} + +func checkSimpleMulticastListener(t *testing.T, err error, c *UDPConn, gaddr *UDPAddr) { + if c.LocalAddr().String() != gaddr.String() { + t.Fatalf("LocalAddr returns %q, expected %q", c.LocalAddr().String(), gaddr.String()) + } +} + +func availMulticastInterface(t *testing.T, flags Flags) (*Interface, error) { + var ifi *Interface + if flags != Flags(0) { ift, err := Interfaces() if err != nil { t.Fatalf("Interfaces failed: %v", err) } - var ifi *Interface for _, x := range ift { - if x.Flags&tt.flags == tt.flags { + if x.Flags&flags == flags { ifi = &x break } } if ifi == nil { - t.Logf("an appropriate multicast interface not found") - return + return nil, errors.New("an appropriate multicast interface not found") } - c, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr) + } + return ifi, nil +} + +func multicastRIBContains(t *testing.T, ip IP) bool { + ift, err := Interfaces() + if err != nil { + t.Fatalf("Interfaces failed: %v", err) + } + for _, ifi := range ift { + ifmat, err := ifi.MulticastAddrs() if err != nil { - t.Fatalf("ListenMulticastUDP failed: %v", err) + t.Fatalf("MulticastAddrs failed: %v", err) + } + for _, ifma := range ifmat { + if ifma.(*IPAddr).IP.Equal(ip) { + return true + } } - c.Close() } + return false } func testIPv4MulticastSocketOptions(t *testing.T, fd *netFD, ifi *Interface) { - ifmc, err := ipv4MulticastInterface(fd) + _, err := ipv4MulticastInterface(fd) if err != nil { t.Fatalf("ipv4MulticastInterface failed: %v", err) } - t.Logf("IPv4 multicast interface: %v", ifmc) - err = setIPv4MulticastInterface(fd, ifi) - if err != nil { - t.Fatalf("setIPv4MulticastInterface failed: %v", err) + if ifi != nil { + err = setIPv4MulticastInterface(fd, ifi) + if err != nil { + t.Fatalf("setIPv4MulticastInterface failed: %v", err) + } } - - ttl, err := ipv4MulticastTTL(fd) + _, err = ipv4MulticastTTL(fd) if err != nil { t.Fatalf("ipv4MulticastTTL failed: %v", err) } - t.Logf("IPv4 multicast TTL: %v", ttl) err = setIPv4MulticastTTL(fd, 1) if err != nil { t.Fatalf("setIPv4MulticastTTL failed: %v", err) } - - loop, err := ipv4MulticastLoopback(fd) + _, err = ipv4MulticastLoopback(fd) if err != nil { t.Fatalf("ipv4MulticastLoopback failed: %v", err) } - t.Logf("IPv4 multicast loopback: %v", loop) err = setIPv4MulticastLoopback(fd, false) if err != nil { t.Fatalf("setIPv4MulticastLoopback failed: %v", err) @@ -151,31 +197,28 @@ func testIPv4MulticastSocketOptions(t *testing.T, fd *netFD, ifi *Interface) { } func testIPv6MulticastSocketOptions(t *testing.T, fd *netFD, ifi *Interface) { - ifmc, err := ipv6MulticastInterface(fd) + _, err := ipv6MulticastInterface(fd) if err != nil { t.Fatalf("ipv6MulticastInterface failed: %v", err) } - t.Logf("IPv6 multicast interface: %v", ifmc) - err = setIPv6MulticastInterface(fd, ifi) - if err != nil { - t.Fatalf("setIPv6MulticastInterface failed: %v", err) + if ifi != nil { + err = setIPv6MulticastInterface(fd, ifi) + if err != nil { + t.Fatalf("setIPv6MulticastInterface failed: %v", err) + } } - - hoplim, err := ipv6MulticastHopLimit(fd) + _, err = ipv6MulticastHopLimit(fd) if err != nil { t.Fatalf("ipv6MulticastHopLimit failed: %v", err) } - t.Logf("IPv6 multicast hop limit: %v", hoplim) err = setIPv6MulticastHopLimit(fd, 1) if err != nil { t.Fatalf("setIPv6MulticastHopLimit failed: %v", err) } - - loop, err := ipv6MulticastLoopback(fd) + _, err = ipv6MulticastLoopback(fd) if err != nil { t.Fatalf("ipv6MulticastLoopback failed: %v", err) } - t.Logf("IPv6 multicast loopback: %v", loop) err = setIPv6MulticastLoopback(fd, false) if err != nil { t.Fatalf("setIPv6MulticastLoopback failed: %v", err) diff --git a/libgo/go/net/net.go b/libgo/go/net/net.go index 79d36a2a813..bf242ff8dd6 100644 --- a/libgo/go/net/net.go +++ b/libgo/go/net/net.go @@ -2,8 +2,41 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package net provides a portable interface to Unix networks sockets, -// including TCP/IP, UDP, domain name resolution, and Unix domain sockets. +/* +Package net provides a portable interface for network I/O, including +TCP/IP, UDP, domain name resolution, and Unix domain sockets. + +Although the package provides access to low-level networking +primitives, most clients will need only the basic interface provided +by the Dial, Listen, and Accept functions and the associated +Conn and Listener interfaces. The crypto/tls package uses +the same interfaces and similar Dial and Listen functions. + +The Dial function connects to a server: + + conn, err := net.Dial("tcp", "google.com:80") + if err != nil { + // handle error + } + fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n") + status, err := bufio.NewReader(conn).ReadString('\n') + // ... + +The Listen function creates servers: + + ln, err := net.Listen("tcp", ":8080") + if err != nil { + // handle error + } + for { + conn, err := ln.Accept() + if err != nil { + // handle error + continue + } + go handleConnection(conn) + } +*/ package net // TODO(rsc): @@ -42,21 +75,28 @@ type Conn interface { RemoteAddr() Addr // SetDeadline sets the read and write deadlines associated - // with the connection. + // with the connection. It is equivalent to calling both + // SetReadDeadline and SetWriteDeadline. + // + // A deadline is an absolute time after which I/O operations + // fail with a timeout (see type Error) instead of + // blocking. The deadline applies to all future I/O, not just + // the immediately following call to Read or Write. + // + // An idle timeout can be implemented by repeatedly extending + // the deadline after successful Read or Write calls. + // + // A zero value for t means I/O operations will not time out. SetDeadline(t time.Time) error - // SetReadDeadline sets the deadline for all Read calls to return. - // If the deadline is reached, Read will fail with a timeout - // (see type Error) instead of blocking. + // SetReadDeadline sets the deadline for Read calls. // A zero value for t means Read will not time out. SetReadDeadline(t time.Time) error - // SetWriteDeadline sets the deadline for all Write calls to return. - // If the deadline is reached, Write will fail with a timeout - // (see type Error) instead of blocking. - // A zero value for t means Write will not time out. + // SetWriteDeadline sets the deadline for Write calls. // Even if write times out, it may return n > 0, indicating that // some of the data was successfully written. + // A zero value for t means Write will not time out. SetWriteDeadline(t time.Time) error } diff --git a/libgo/go/net/parse.go b/libgo/go/net/parse.go index 4c4200a49b7..7c87b42f6d9 100644 --- a/libgo/go/net/parse.go +++ b/libgo/go/net/parse.go @@ -67,7 +67,7 @@ func open(name string) (*file, error) { if err != nil { return nil, err } - return &file{fd, make([]byte, 1024)[0:0], false}, nil + return &file{fd, make([]byte, os.Getpagesize())[0:0], false}, nil } func byteIndex(s string, c byte) int { diff --git a/libgo/go/net/rpc/server_test.go b/libgo/go/net/rpc/server_test.go index 8cfa033ccc3..62c7b1e6004 100644 --- a/libgo/go/net/rpc/server_test.go +++ b/libgo/go/net/rpc/server_test.go @@ -387,12 +387,12 @@ func (WriteFailCodec) WriteRequest(*Request, interface{}) error { } func (WriteFailCodec) ReadResponseHeader(*Response) error { - time.Sleep(120 * time.Second) + select {} panic("unreachable") } func (WriteFailCodec) ReadResponseBody(interface{}) error { - time.Sleep(120 * time.Second) + select {} panic("unreachable") } diff --git a/libgo/go/net/server_test.go b/libgo/go/net/server_test.go index 55691493aa9..b9862168153 100644 --- a/libgo/go/net/server_test.go +++ b/libgo/go/net/server_test.go @@ -95,7 +95,7 @@ func doTest(t *testing.T, network, listenaddr, dialaddr string) { t.Logf("Test %q %q %q", network, listenaddr, dialaddr) switch listenaddr { case "", "0.0.0.0", "[::]", "[::ffff:0.0.0.0]": - if testing.Short() || avoidMacFirewall { + if testing.Short() || !*testExternal { t.Logf("skip wildcard listen during short test") return } diff --git a/libgo/go/net/smtp/smtp.go b/libgo/go/net/smtp/smtp.go index f600cc86482..59f6449f0ab 100644 --- a/libgo/go/net/smtp/smtp.go +++ b/libgo/go/net/smtp/smtp.go @@ -50,15 +50,14 @@ func Dial(addr string) (*Client, error) { // server name to be used when authenticating. func NewClient(conn net.Conn, host string) (*Client, error) { text := textproto.NewConn(conn) - _, msg, err := text.ReadResponse(220) + _, _, err := text.ReadResponse(220) if err != nil { text.Close() return nil, err } c := &Client{Text: text, conn: conn, serverName: host} - if strings.Contains(msg, "ESMTP") { - err = c.ehlo() - } else { + err = c.ehlo() + if err != nil { err = c.helo() } return c, err diff --git a/libgo/go/net/smtp/smtp_test.go b/libgo/go/net/smtp/smtp_test.go index ce887820531..c315d185c9d 100644 --- a/libgo/go/net/smtp/smtp_test.go +++ b/libgo/go/net/smtp/smtp_test.go @@ -8,9 +8,11 @@ import ( "bufio" "bytes" "io" + "net" "net/textproto" "strings" "testing" + "time" ) type authTest struct { @@ -59,9 +61,12 @@ type faker struct { io.ReadWriter } -func (f faker) Close() error { - return nil -} +func (f faker) Close() error { return nil } +func (f faker) LocalAddr() net.Addr { return nil } +func (f faker) RemoteAddr() net.Addr { return nil } +func (f faker) SetDeadline(time.Time) error { return nil } +func (f faker) SetReadDeadline(time.Time) error { return nil } +func (f faker) SetWriteDeadline(time.Time) error { return nil } func TestBasic(t *testing.T) { basicServer = strings.Join(strings.Split(basicServer, "\n"), "\r\n") @@ -180,3 +185,87 @@ Goodbye. . QUIT ` + +func TestNewClient(t *testing.T) { + newClientServer = strings.Join(strings.Split(newClientServer, "\n"), "\r\n") + newClientClient = strings.Join(strings.Split(newClientClient, "\n"), "\r\n") + + var cmdbuf bytes.Buffer + bcmdbuf := bufio.NewWriter(&cmdbuf) + out := func() string { + bcmdbuf.Flush() + return cmdbuf.String() + } + var fake faker + fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(newClientServer)), bcmdbuf) + c, err := NewClient(fake, "fake.host") + if err != nil { + t.Fatalf("NewClient: %v\n(after %v)", err, out()) + } + if ok, args := c.Extension("aUtH"); !ok || args != "LOGIN PLAIN" { + t.Fatalf("Expected AUTH supported") + } + if ok, _ := c.Extension("DSN"); ok { + t.Fatalf("Shouldn't support DSN") + } + if err := c.Quit(); err != nil { + t.Fatalf("QUIT failed: %s", err) + } + + actualcmds := out() + if newClientClient != actualcmds { + t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, newClientClient) + } +} + +var newClientServer = `220 hello world +250-mx.google.com at your service +250-SIZE 35651584 +250-AUTH LOGIN PLAIN +250 8BITMIME +221 OK +` + +var newClientClient = `EHLO localhost +QUIT +` + +func TestNewClient2(t *testing.T) { + newClient2Server = strings.Join(strings.Split(newClient2Server, "\n"), "\r\n") + newClient2Client = strings.Join(strings.Split(newClient2Client, "\n"), "\r\n") + + var cmdbuf bytes.Buffer + bcmdbuf := bufio.NewWriter(&cmdbuf) + var fake faker + fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(newClient2Server)), bcmdbuf) + c, err := NewClient(fake, "fake.host") + if err != nil { + t.Fatalf("NewClient: %v", err) + } + if ok, _ := c.Extension("DSN"); ok { + t.Fatalf("Shouldn't support DSN") + } + if err := c.Quit(); err != nil { + t.Fatalf("QUIT failed: %s", err) + } + + bcmdbuf.Flush() + actualcmds := cmdbuf.String() + if newClient2Client != actualcmds { + t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, newClient2Client) + } +} + +var newClient2Server = `220 hello world +502 EH? +250-mx.google.com at your service +250-SIZE 35651584 +250-AUTH LOGIN PLAIN +250 8BITMIME +221 OK +` + +var newClient2Client = `EHLO localhost +HELO localhost +QUIT +` diff --git a/libgo/go/net/sock.go b/libgo/go/net/sock.go index 70064c307ef..dc139f04a25 100644 --- a/libgo/go/net/sock.go +++ b/libgo/go/net/sock.go @@ -33,13 +33,14 @@ func socket(net string, f, t, p int, la, ra syscall.Sockaddr, toAddr func(syscal return nil, err } + var bla syscall.Sockaddr if la != nil { - la, err = listenerSockaddr(s, f, la, toAddr) + bla, err = listenerSockaddr(s, f, la, toAddr) if err != nil { closesocket(s) return nil, err } - err = syscall.Bind(s, la) + err = syscall.Bind(s, bla) if err != nil { closesocket(s) return nil, err @@ -61,7 +62,12 @@ func socket(net string, f, t, p int, la, ra syscall.Sockaddr, toAddr func(syscal } sa, _ := syscall.Getsockname(s) - laddr := toAddr(sa) + var laddr Addr + if la != nil && bla != la { + laddr = toAddr(la) + } else { + laddr = toAddr(sa) + } sa, _ = syscall.Getpeername(s) raddr := toAddr(sa) diff --git a/libgo/go/net/tcpsock_plan9.go b/libgo/go/net/tcpsock_plan9.go index 128766144dd..35f56966eae 100644 --- a/libgo/go/net/tcpsock_plan9.go +++ b/libgo/go/net/tcpsock_plan9.go @@ -7,7 +7,7 @@ package net import ( - "os" + "syscall" "time" ) @@ -19,35 +19,35 @@ type TCPConn struct { // SetDeadline implements the Conn SetDeadline method. func (c *TCPConn) SetDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } // SetReadDeadline implements the Conn SetReadDeadline method. func (c *TCPConn) SetReadDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } // SetWriteDeadline implements the Conn SetWriteDeadline method. func (c *TCPConn) SetWriteDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } // CloseRead shuts down the reading side of the TCP connection. // Most callers should just use Close. func (c *TCPConn) CloseRead() error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } - return os.EPLAN9 + return syscall.EPLAN9 } // CloseWrite shuts down the writing side of the TCP connection. // Most callers should just use Close. func (c *TCPConn) CloseWrite() error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } - return os.EPLAN9 + return syscall.EPLAN9 } // DialTCP connects to the remote address raddr on the network net, diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go index 200ce91566c..e05bc10170e 100644 --- a/libgo/go/net/tcpsock_posix.go +++ b/libgo/go/net/tcpsock_posix.go @@ -9,6 +9,7 @@ package net import ( + "fmt" "io" "os" "syscall" @@ -26,6 +27,11 @@ func sockaddrToTCP(sa syscall.Sockaddr) Addr { return &TCPAddr{sa.Addr[0:], sa.Port} case *syscall.SockaddrInet6: return &TCPAddr{sa.Addr[0:], sa.Port} + default: + if sa != nil { + // Diagnose when we will turn a non-nil sockaddr into a nil. + panic(fmt.Sprintf("unexpected type in sockaddrToTCP: %T", sa)) + } } return nil } @@ -70,7 +76,7 @@ func (c *TCPConn) ok() bool { return c != nil && c.fd != nil } // Read implements the Conn Read method. func (c *TCPConn) Read(b []byte) (n int, err error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } return c.fd.Read(b) } @@ -86,7 +92,7 @@ func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) { // Write implements the Conn Write method. func (c *TCPConn) Write(b []byte) (n int, err error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } return c.fd.Write(b) } @@ -94,7 +100,7 @@ func (c *TCPConn) Write(b []byte) (n int, err error) { // Close closes the TCP connection. func (c *TCPConn) Close() error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } err := c.fd.Close() c.fd = nil @@ -105,7 +111,7 @@ func (c *TCPConn) Close() error { // Most callers should just use Close. func (c *TCPConn) CloseRead() error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return c.fd.CloseRead() } @@ -114,7 +120,7 @@ func (c *TCPConn) CloseRead() error { // Most callers should just use Close. func (c *TCPConn) CloseWrite() error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return c.fd.CloseWrite() } @@ -138,7 +144,7 @@ func (c *TCPConn) RemoteAddr() Addr { // SetDeadline implements the Conn SetDeadline method. func (c *TCPConn) SetDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setDeadline(c.fd, t) } @@ -146,7 +152,7 @@ func (c *TCPConn) SetDeadline(t time.Time) error { // SetReadDeadline implements the Conn SetReadDeadline method. func (c *TCPConn) SetReadDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setReadDeadline(c.fd, t) } @@ -154,7 +160,7 @@ func (c *TCPConn) SetReadDeadline(t time.Time) error { // SetWriteDeadline implements the Conn SetWriteDeadline method. func (c *TCPConn) SetWriteDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setWriteDeadline(c.fd, t) } @@ -163,7 +169,7 @@ func (c *TCPConn) SetWriteDeadline(t time.Time) error { // receive buffer associated with the connection. func (c *TCPConn) SetReadBuffer(bytes int) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setReadBuffer(c.fd, bytes) } @@ -172,7 +178,7 @@ func (c *TCPConn) SetReadBuffer(bytes int) error { // transmit buffer associated with the connection. func (c *TCPConn) SetWriteBuffer(bytes int) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setWriteBuffer(c.fd, bytes) } @@ -190,7 +196,7 @@ func (c *TCPConn) SetWriteBuffer(bytes int) error { // data to be sent and acknowledged. func (c *TCPConn) SetLinger(sec int) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setLinger(c.fd, sec) } @@ -199,7 +205,7 @@ func (c *TCPConn) SetLinger(sec int) error { // keepalive messages on the connection. func (c *TCPConn) SetKeepAlive(keepalive bool) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setKeepAlive(c.fd, keepalive) } @@ -210,7 +216,7 @@ func (c *TCPConn) SetKeepAlive(keepalive bool) error { // that data is sent as soon as possible after a Write. func (c *TCPConn) SetNoDelay(noDelay bool) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setNoDelay(c.fd, noDelay) } @@ -259,6 +265,17 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) { } func selfConnect(fd *netFD) bool { + // The socket constructor can return an fd with raddr nil under certain + // unknown conditions. The errors in the calls there to Getpeername + // are discarded, but we can't catch the problem there because those + // calls are sometimes legally erroneous with a "socket not connected". + // Since this code (selfConnect) is already trying to work around + // a problem, we make sure if this happens we recognize trouble and + // ask the DialTCP routine to try again. + // TODO: try to understand what's really going on. + if fd.laddr == nil || fd.raddr == nil { + return true + } l := fd.laddr.(*TCPAddr) r := fd.raddr.(*TCPAddr) return l.Port == r.Port && l.IP.Equal(r.IP) @@ -294,7 +311,7 @@ func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) { // and the remote address. func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) { if l == nil || l.fd == nil || l.fd.sysfd < 0 { - return nil, os.EINVAL + return nil, syscall.EINVAL } fd, err := l.fd.accept(sockaddrToTCP) if err != nil { @@ -317,7 +334,7 @@ func (l *TCPListener) Accept() (c Conn, err error) { // Already Accepted connections are not closed. func (l *TCPListener) Close() error { if l == nil || l.fd == nil { - return os.EINVAL + return syscall.EINVAL } return l.fd.Close() } @@ -329,7 +346,7 @@ func (l *TCPListener) Addr() Addr { return l.fd.laddr } // A zero time value disables the deadline. func (l *TCPListener) SetDeadline(t time.Time) error { if l == nil || l.fd == nil { - return os.EINVAL + return syscall.EINVAL } return setDeadline(l.fd, t) } diff --git a/libgo/go/net/testdata/hosts b/libgo/go/net/testdata/hosts new file mode 100644 index 00000000000..b601763898b --- /dev/null +++ b/libgo/go/net/testdata/hosts @@ -0,0 +1,12 @@ +255.255.255.255 broadcasthost +127.0.0.2 odin +127.0.0.3 odin # inline comment +::2 odin +127.1.1.1 thor +# aliases +127.1.1.2 ullr ullrhost +# Bogus entries that must be ignored. +123.123.123 loki +321.321.321.321 +# TODO(yvesj): Should we be able to parse this? From a Darwin system. +fe80::1%lo0 localhost diff --git a/libgo/go/net/testdata/igmp b/libgo/go/net/testdata/igmp new file mode 100644 index 00000000000..5f380a2c7db --- /dev/null +++ b/libgo/go/net/testdata/igmp @@ -0,0 +1,24 @@ +Idx Device : Count Querier Group Users Timer Reporter +1 lo : 1 V3 + 010000E0 1 0:00000000 0 +2 eth0 : 2 V2 + FB0000E0 1 0:00000000 1 + 010000E0 1 0:00000000 0 +3 eth1 : 1 V3 + 010000E0 1 0:00000000 0 +4 eth2 : 1 V3 + 010000E0 1 0:00000000 0 +5 eth0.100 : 2 V3 + FB0000E0 1 0:00000000 0 + 010000E0 1 0:00000000 0 +6 eth0.101 : 2 V3 + FB0000E0 1 0:00000000 0 + 010000E0 1 0:00000000 0 +7 eth0.102 : 2 V3 + FB0000E0 1 0:00000000 0 + 010000E0 1 0:00000000 0 +8 eth0.103 : 2 V3 + FB0000E0 1 0:00000000 0 + 010000E0 1 0:00000000 0 +9 device1tap2: 1 V3 + 010000E0 1 0:00000000 0 diff --git a/libgo/go/net/testdata/igmp6 b/libgo/go/net/testdata/igmp6 new file mode 100644 index 00000000000..6cd5a2d4d9d --- /dev/null +++ b/libgo/go/net/testdata/igmp6 @@ -0,0 +1,18 @@ +1 lo ff020000000000000000000000000001 1 0000000C 0 +2 eth0 ff0200000000000000000001ffac891e 1 00000006 0 +2 eth0 ff020000000000000000000000000001 1 0000000C 0 +3 eth1 ff0200000000000000000001ffac8928 2 00000006 0 +3 eth1 ff020000000000000000000000000001 1 0000000C 0 +4 eth2 ff0200000000000000000001ffac8932 2 00000006 0 +4 eth2 ff020000000000000000000000000001 1 0000000C 0 +5 eth0.100 ff0200000000000000000001ffac891e 1 00000004 0 +5 eth0.100 ff020000000000000000000000000001 1 0000000C 0 +6 pan0 ff020000000000000000000000000001 1 0000000C 0 +7 eth0.101 ff0200000000000000000001ffac891e 1 00000004 0 +7 eth0.101 ff020000000000000000000000000001 1 0000000C 0 +8 eth0.102 ff0200000000000000000001ffac891e 1 00000004 0 +8 eth0.102 ff020000000000000000000000000001 1 0000000C 0 +9 eth0.103 ff0200000000000000000001ffac891e 1 00000004 0 +9 eth0.103 ff020000000000000000000000000001 1 0000000C 0 +10 device1tap2 ff0200000000000000000001ff4cc3a3 1 00000004 0 +10 device1tap2 ff020000000000000000000000000001 1 0000000C 0 diff --git a/libgo/go/net/textproto/reader.go b/libgo/go/net/textproto/reader.go index 862cd536c46..125feb3e885 100644 --- a/libgo/go/net/textproto/reader.go +++ b/libgo/go/net/textproto/reader.go @@ -454,10 +454,14 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { // Key ends at first colon; must not have spaces. i := bytes.IndexByte(kv, ':') - if i < 0 || bytes.IndexByte(kv[0:i], ' ') >= 0 { + if i < 0 { return m, ProtocolError("malformed MIME header line: " + string(kv)) } - key := CanonicalMIMEHeaderKey(string(kv[0:i])) + key := string(kv[0:i]) + if strings.Index(key, " ") >= 0 { + key = strings.TrimRight(key, " ") + } + key = CanonicalMIMEHeaderKey(key) // Skip initial spaces in value. i++ // skip colon @@ -503,6 +507,11 @@ MustRewrite: a := []byte(s) upper := true for i, v := range a { + if v == ' ' { + a[i] = '-' + upper = true + continue + } if upper && 'a' <= v && v <= 'z' { a[i] = v + 'A' - 'a' } diff --git a/libgo/go/net/textproto/reader_test.go b/libgo/go/net/textproto/reader_test.go index 4d036914801..7c5d16227ff 100644 --- a/libgo/go/net/textproto/reader_test.go +++ b/libgo/go/net/textproto/reader_test.go @@ -164,6 +164,29 @@ func TestLargeReadMIMEHeader(t *testing.T) { } } +// Test that we read slightly-bogus MIME headers seen in the wild, +// with spaces before colons, and spaces in keys. +func TestReadMIMEHeaderNonCompliant(t *testing.T) { + // Invalid HTTP response header as sent by an Axis security + // camera: (this is handled by IE, Firefox, Chrome, curl, etc.) + r := reader("Foo: bar\r\n" + + "Content-Language: en\r\n" + + "SID : 0\r\n" + + "Audio Mode : None\r\n" + + "Privilege : 127\r\n\r\n") + m, err := r.ReadMIMEHeader() + want := MIMEHeader{ + "Foo": {"bar"}, + "Content-Language": {"en"}, + "Sid": {"0"}, + "Audio-Mode": {"None"}, + "Privilege": {"127"}, + } + if !reflect.DeepEqual(m, want) || err != nil { + t.Fatalf("ReadMIMEHeader =\n%v, %v; want:\n%v", m, err, want) + } +} + type readResponseTest struct { in string inCode int diff --git a/libgo/go/net/textproto/textproto.go b/libgo/go/net/textproto/textproto.go index 317ec72b0cc..ad5840cf7da 100644 --- a/libgo/go/net/textproto/textproto.go +++ b/libgo/go/net/textproto/textproto.go @@ -20,6 +20,9 @@ // // Writer, to write dot-encoded text blocks. // +// Conn, a convenient packaging of Reader, Writer, and Pipeline for use +// with a single network connection. +// package textproto import ( diff --git a/libgo/go/net/udpsock_plan9.go b/libgo/go/net/udpsock_plan9.go index f90a5fe9ab8..4f298a42f87 100644 --- a/libgo/go/net/udpsock_plan9.go +++ b/libgo/go/net/udpsock_plan9.go @@ -9,6 +9,7 @@ package net import ( "errors" "os" + "syscall" "time" ) @@ -20,17 +21,17 @@ type UDPConn struct { // SetDeadline implements the Conn SetDeadline method. func (c *UDPConn) SetDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } // SetReadDeadline implements the Conn SetReadDeadline method. func (c *UDPConn) SetReadDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } // SetWriteDeadline implements the Conn SetWriteDeadline method. func (c *UDPConn) SetWriteDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } // UDP-specific methods. @@ -43,7 +44,7 @@ func (c *UDPConn) SetWriteDeadline(t time.Time) error { // after a fixed time limit; see SetDeadline and SetReadDeadline. func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { if !c.ok() { - return 0, nil, os.EINVAL + return 0, nil, syscall.EINVAL } if c.data == nil { c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) @@ -69,7 +70,7 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { // ReadFrom implements the PacketConn ReadFrom method. func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) { if !c.ok() { - return 0, nil, os.EINVAL + return 0, nil, syscall.EINVAL } return c.ReadFromUDP(b) } @@ -82,7 +83,7 @@ func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) { // On packet-oriented connections, write timeouts are rare. func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } if c.data == nil { c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) @@ -106,11 +107,11 @@ 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) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } a, ok := addr.(*UDPAddr) if !ok { - return 0, &OpError{"write", c.dir, addr, os.EINVAL} + return 0, &OpError{"write", c.dir, addr, syscall.EINVAL} } return c.WriteToUDP(b, a) } @@ -191,5 +192,5 @@ func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err error) { // the interface to join. ListenMulticastUDP uses default // multicast interface if ifi is nil. func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) { - return nil, os.EPLAN9 + return nil, syscall.EPLAN9 } diff --git a/libgo/go/net/udpsock_posix.go b/libgo/go/net/udpsock_posix.go index 6108373568a..1f99dc53867 100644 --- a/libgo/go/net/udpsock_posix.go +++ b/libgo/go/net/udpsock_posix.go @@ -63,7 +63,7 @@ func (c *UDPConn) ok() bool { return c != nil && c.fd != nil } // Read implements the Conn Read method. func (c *UDPConn) Read(b []byte) (int, error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } return c.fd.Read(b) } @@ -71,7 +71,7 @@ func (c *UDPConn) Read(b []byte) (int, error) { // Write implements the Conn Write method. func (c *UDPConn) Write(b []byte) (int, error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } return c.fd.Write(b) } @@ -79,7 +79,7 @@ func (c *UDPConn) Write(b []byte) (int, error) { // Close closes the UDP connection. func (c *UDPConn) Close() error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } err := c.fd.Close() c.fd = nil @@ -105,7 +105,7 @@ func (c *UDPConn) RemoteAddr() Addr { // SetDeadline implements the Conn SetDeadline method. func (c *UDPConn) SetDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setDeadline(c.fd, t) } @@ -113,7 +113,7 @@ func (c *UDPConn) SetDeadline(t time.Time) error { // SetReadDeadline implements the Conn SetReadDeadline method. func (c *UDPConn) SetReadDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setReadDeadline(c.fd, t) } @@ -121,7 +121,7 @@ func (c *UDPConn) SetReadDeadline(t time.Time) error { // SetWriteDeadline implements the Conn SetWriteDeadline method. func (c *UDPConn) SetWriteDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setWriteDeadline(c.fd, t) } @@ -130,7 +130,7 @@ func (c *UDPConn) SetWriteDeadline(t time.Time) error { // receive buffer associated with the connection. func (c *UDPConn) SetReadBuffer(bytes int) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setReadBuffer(c.fd, bytes) } @@ -139,7 +139,7 @@ func (c *UDPConn) SetReadBuffer(bytes int) error { // transmit buffer associated with the connection. func (c *UDPConn) SetWriteBuffer(bytes int) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setWriteBuffer(c.fd, bytes) } @@ -154,7 +154,7 @@ func (c *UDPConn) SetWriteBuffer(bytes int) error { // after a fixed time limit; see SetDeadline and SetReadDeadline. func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { if !c.ok() { - return 0, nil, os.EINVAL + return 0, nil, syscall.EINVAL } n, sa, err := c.fd.ReadFrom(b) switch sa := sa.(type) { @@ -169,7 +169,7 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { // ReadFrom implements the PacketConn ReadFrom method. func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) { if !c.ok() { - return 0, nil, os.EINVAL + return 0, nil, syscall.EINVAL } n, uaddr, err := c.ReadFromUDP(b) return n, uaddr.toAddr(), err @@ -183,7 +183,7 @@ func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) { // On packet-oriented connections, write timeouts are rare. func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } if c.fd.isConnected { return 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected} @@ -198,11 +198,11 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) { // WriteTo implements the PacketConn WriteTo method. func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } a, ok := addr.(*UDPAddr) if !ok { - return 0, &OpError{"write", c.fd.net, addr, os.EINVAL} + return 0, &OpError{"write", c.fd.net, addr, syscall.EINVAL} } return c.WriteToUDP(b, a) } @@ -262,7 +262,7 @@ func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, e return nil, UnknownNetworkError(net) } if gaddr == nil || gaddr.IP == nil { - return nil, &OpError{"listenmulticastudp", "udp", nil, errMissingAddress} + return nil, &OpError{"listenmulticast", net, nil, errMissingAddress} } fd, err := internetSocket(net, gaddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) if err != nil { diff --git a/libgo/go/net/unixsock_plan9.go b/libgo/go/net/unixsock_plan9.go index 1d9d7578f4f..7b4ae6bd116 100644 --- a/libgo/go/net/unixsock_plan9.go +++ b/libgo/go/net/unixsock_plan9.go @@ -7,7 +7,7 @@ package net import ( - "os" + "syscall" "time" ) @@ -19,17 +19,17 @@ type UnixConn bool // Read implements the Conn Read method. func (c *UnixConn) Read(b []byte) (n int, err error) { - return 0, os.EPLAN9 + return 0, syscall.EPLAN9 } // Write implements the Conn Write method. func (c *UnixConn) Write(b []byte) (n int, err error) { - return 0, os.EPLAN9 + return 0, syscall.EPLAN9 } // Close closes the Unix domain connection. func (c *UnixConn) Close() error { - return os.EPLAN9 + return syscall.EPLAN9 } // LocalAddr returns the local network address, a *UnixAddr. @@ -47,28 +47,28 @@ func (c *UnixConn) RemoteAddr() Addr { // SetDeadline implements the Conn SetDeadline method. func (c *UnixConn) SetDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } // SetReadDeadline implements the Conn SetReadDeadline method. func (c *UnixConn) SetReadDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } // SetWriteDeadline implements the Conn SetWriteDeadline method. func (c *UnixConn) SetWriteDeadline(t time.Time) error { - return os.EPLAN9 + return syscall.EPLAN9 } // ReadFrom implements the PacketConn ReadFrom method. func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) { - err = os.EPLAN9 + err = syscall.EPLAN9 return } // WriteTo implements the PacketConn WriteTo method. func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) { - err = os.EPLAN9 + err = syscall.EPLAN9 return } @@ -76,7 +76,7 @@ func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) { // which must be "unix" or "unixgram". If laddr is not nil, it is used // as the local address for the connection. func DialUnix(net string, laddr, raddr *UnixAddr) (c *UnixConn, err error) { - return nil, os.EPLAN9 + return nil, syscall.EPLAN9 } // UnixListener is a Unix domain socket listener. @@ -87,19 +87,19 @@ type UnixListener bool // ListenUnix announces on the Unix domain socket laddr and returns a Unix listener. // Net must be "unix" (stream sockets). func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err error) { - return nil, os.EPLAN9 + return nil, syscall.EPLAN9 } // Accept implements the Accept method in the Listener interface; // it waits for the next call and returns a generic Conn. func (l *UnixListener) Accept() (c Conn, err error) { - return nil, os.EPLAN9 + return nil, syscall.EPLAN9 } // Close stops listening on the Unix address. // Already accepted connections are not closed. func (l *UnixListener) Close() error { - return os.EPLAN9 + return syscall.EPLAN9 } // Addr returns the listener's network address. diff --git a/libgo/go/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go index 10b79668511..3a94cf5c5ad 100644 --- a/libgo/go/net/unixsock_posix.go +++ b/libgo/go/net/unixsock_posix.go @@ -59,8 +59,8 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err f = sockaddrToUnixpacket } - fd, oserr := socket(net, syscall.AF_UNIX, sotype, 0, la, ra, f) - if oserr != nil { + fd, err = socket(net, syscall.AF_UNIX, sotype, 0, la, ra, f) + if err != nil { goto Error } return fd, nil @@ -70,7 +70,7 @@ Error: if mode == "listen" { addr = laddr } - return nil, &OpError{Op: mode, Net: net, Addr: addr, Err: oserr} + return nil, &OpError{Op: mode, Net: net, Addr: addr, Err: err} } func sockaddrToUnix(sa syscall.Sockaddr) Addr { @@ -123,7 +123,7 @@ func (c *UnixConn) ok() bool { return c != nil && c.fd != nil } // Read implements the Conn Read method. func (c *UnixConn) Read(b []byte) (n int, err error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } return c.fd.Read(b) } @@ -131,7 +131,7 @@ func (c *UnixConn) Read(b []byte) (n int, err error) { // Write implements the Conn Write method. func (c *UnixConn) Write(b []byte) (n int, err error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } return c.fd.Write(b) } @@ -139,7 +139,7 @@ func (c *UnixConn) Write(b []byte) (n int, err error) { // Close closes the Unix domain connection. func (c *UnixConn) Close() error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } err := c.fd.Close() c.fd = nil @@ -168,7 +168,7 @@ func (c *UnixConn) RemoteAddr() Addr { // SetDeadline implements the Conn SetDeadline method. func (c *UnixConn) SetDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setDeadline(c.fd, t) } @@ -176,7 +176,7 @@ func (c *UnixConn) SetDeadline(t time.Time) error { // SetReadDeadline implements the Conn SetReadDeadline method. func (c *UnixConn) SetReadDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setReadDeadline(c.fd, t) } @@ -184,7 +184,7 @@ func (c *UnixConn) SetReadDeadline(t time.Time) error { // SetWriteDeadline implements the Conn SetWriteDeadline method. func (c *UnixConn) SetWriteDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setWriteDeadline(c.fd, t) } @@ -193,7 +193,7 @@ func (c *UnixConn) SetWriteDeadline(t time.Time) error { // receive buffer associated with the connection. func (c *UnixConn) SetReadBuffer(bytes int) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setReadBuffer(c.fd, bytes) } @@ -202,7 +202,7 @@ func (c *UnixConn) SetReadBuffer(bytes int) error { // transmit buffer associated with the connection. func (c *UnixConn) SetWriteBuffer(bytes int) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setWriteBuffer(c.fd, bytes) } @@ -216,7 +216,7 @@ func (c *UnixConn) SetWriteBuffer(bytes int) error { // see SetDeadline and SetReadDeadline. func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) { if !c.ok() { - return 0, nil, os.EINVAL + return 0, nil, syscall.EINVAL } n, sa, err := c.fd.ReadFrom(b) switch sa := sa.(type) { @@ -229,7 +229,7 @@ func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) { // ReadFrom implements the PacketConn ReadFrom method. func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) { if !c.ok() { - return 0, nil, os.EINVAL + return 0, nil, syscall.EINVAL } n, uaddr, err := c.ReadFromUnix(b) return n, uaddr.toAddr(), err @@ -243,10 +243,10 @@ func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) { // On packet-oriented connections, write timeouts are rare. func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } if addr.Net != sotypeToNet(c.fd.sotype) { - return 0, os.EAFNOSUPPORT + return 0, syscall.EAFNOSUPPORT } sa := &syscall.SockaddrUnix{Name: addr.Name} return c.fd.WriteTo(b, sa) @@ -255,18 +255,18 @@ func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) { // WriteTo implements the PacketConn WriteTo method. func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } a, ok := addr.(*UnixAddr) if !ok { - return 0, &OpError{"write", c.fd.net, addr, os.EINVAL} + return 0, &OpError{"write", c.fd.net, addr, syscall.EINVAL} } return c.WriteToUnix(b, a) } func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) { if !c.ok() { - return 0, 0, 0, nil, os.EINVAL + return 0, 0, 0, nil, syscall.EINVAL } n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob) switch sa := sa.(type) { @@ -278,11 +278,11 @@ func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAdd func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) { if !c.ok() { - return 0, 0, os.EINVAL + return 0, 0, syscall.EINVAL } if addr != nil { if addr.Net != sotypeToNet(c.fd.sotype) { - return 0, 0, os.EAFNOSUPPORT + return 0, 0, syscall.EAFNOSUPPORT } sa := &syscall.SockaddrUnix{Name: addr.Name} return c.fd.WriteMsg(b, oob, sa) @@ -339,7 +339,7 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) { // and the remote address. func (l *UnixListener) AcceptUnix() (*UnixConn, error) { if l == nil || l.fd == nil { - return nil, os.EINVAL + return nil, syscall.EINVAL } fd, err := l.fd.accept(sockaddrToUnix) if err != nil { @@ -363,7 +363,7 @@ func (l *UnixListener) Accept() (c Conn, err error) { // Already accepted connections are not closed. func (l *UnixListener) Close() error { if l == nil || l.fd == nil { - return os.EINVAL + return syscall.EINVAL } // The operating system doesn't clean up @@ -391,7 +391,7 @@ func (l *UnixListener) Addr() Addr { return l.fd.laddr } // A zero time value disables the deadline. func (l *UnixListener) SetDeadline(t time.Time) (err error) { if l == nil || l.fd == nil { - return os.EINVAL + return syscall.EINVAL } return setDeadline(l.fd, t) } diff --git a/libgo/go/net/url/url.go b/libgo/go/net/url/url.go index a9ce3b31e24..88ff7ebfef3 100644 --- a/libgo/go/net/url/url.go +++ b/libgo/go/net/url/url.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 URL parses URLs and implements query escaping. +// Package url parses URLs and implements query escaping. // See RFC 3986. package url @@ -321,19 +321,28 @@ func split(s string, c byte, cutc bool) (string, string) { } // Parse parses rawurl into a URL structure. -// The string rawurl is assumed not to have a #fragment suffix. -// (Web browsers strip #fragment before sending the URL to a web server.) // The rawurl may be relative or absolute. func Parse(rawurl string) (url *URL, err error) { - return parse(rawurl, false) + // Cut off #frag + u, frag := split(rawurl, '#', true) + if url, err = parse(u, false); err != nil { + return nil, err + } + if frag == "" { + return url, nil + } + if url.Fragment, err = unescape(frag, encodeFragment); err != nil { + return nil, &Error{"parse", rawurl, err} + } + return url, nil } -// ParseRequest parses rawurl into a URL structure. It assumes that -// rawurl was received from an HTTP request, so the rawurl is interpreted +// ParseRequestURI parses rawurl into a URL structure. It assumes that +// rawurl was received in an HTTP request, so the rawurl is interpreted // only as an absolute URI or an absolute path. // The string rawurl is assumed not to have a #fragment suffix. // (Web browsers strip #fragment before sending the URL to a web server.) -func ParseRequest(rawurl string) (url *URL, err error) { +func ParseRequestURI(rawurl string) (url *URL, err error) { return parse(rawurl, true) } @@ -415,22 +424,6 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) { return } -// ParseWithReference is like Parse but allows a trailing #fragment. -func ParseWithReference(rawurlref string) (url *URL, err error) { - // Cut off #frag - rawurl, frag := split(rawurlref, '#', true) - if url, err = Parse(rawurl); err != nil { - return nil, err - } - if frag == "" { - return url, nil - } - if url.Fragment, err = unescape(frag, encodeFragment); err != nil { - return nil, &Error{"parse", rawurlref, err} - } - return url, nil -} - // String reassembles the URL into a valid URL string. func (u *URL) String() string { // TODO: Rewrite to use bytes.Buffer @@ -589,15 +582,15 @@ func (u *URL) IsAbs() bool { return u.Scheme != "" } -// Parse parses a URL in the context of a base URL. The URL in ref +// Parse parses a URL in the context of the receiver. The provided URL // may be relative or absolute. Parse returns nil, err on parse // failure, otherwise its return value is the same as ResolveReference. -func (base *URL) Parse(ref string) (*URL, error) { +func (u *URL) Parse(ref string) (*URL, error) { refurl, err := Parse(ref) if err != nil { return nil, err } - return base.ResolveReference(refurl), nil + return u.ResolveReference(refurl), nil } // ResolveReference resolves a URI reference to an absolute URI from @@ -606,13 +599,13 @@ func (base *URL) Parse(ref string) (*URL, error) { // URL instance, even if the returned URL is identical to either the // base or reference. If ref is an absolute URL, then ResolveReference // ignores base and returns a copy of ref. -func (base *URL) ResolveReference(ref *URL) *URL { +func (u *URL) ResolveReference(ref *URL) *URL { if ref.IsAbs() { url := *ref return &url } // relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ] - url := *base + url := *u url.RawQuery = ref.RawQuery url.Fragment = ref.Fragment if ref.Opaque != "" { @@ -632,7 +625,7 @@ func (base *URL) ResolveReference(ref *URL) *URL { url.Path = ref.Path } else { // The "rel_path" case. - path := resolvePath(base.Path, ref.Path) + path := resolvePath(u.Path, ref.Path) if !strings.HasPrefix(path, "/") { path = "/" + path } diff --git a/libgo/go/net/url/url_test.go b/libgo/go/net/url/url_test.go index 9fe5ff886b7..2d911ed505a 100644 --- a/libgo/go/net/url/url_test.go +++ b/libgo/go/net/url/url_test.go @@ -188,22 +188,6 @@ var urltests = []URLTest{ }, "http://user:password@google.com", }, -} - -var urlnofragtests = []URLTest{ - { - "http://www.google.com/?q=go+language#foo", - &URL{ - Scheme: "http", - Host: "www.google.com", - Path: "/", - RawQuery: "q=go+language#foo", - }, - "", - }, -} - -var urlfragtests = []URLTest{ { "http://www.google.com/?q=go+language#foo", &URL{ @@ -257,12 +241,6 @@ func DoTest(t *testing.T, parse func(string) (*URL, error), name string, tests [ func TestParse(t *testing.T) { DoTest(t, Parse, "Parse", urltests) - DoTest(t, Parse, "Parse", urlnofragtests) -} - -func TestParseWithReference(t *testing.T) { - DoTest(t, ParseWithReference, "ParseWithReference", urltests) - DoTest(t, ParseWithReference, "ParseWithReference", urlfragtests) } const pathThatLooksSchemeRelative = "//not.a.user@not.a.host/just/a/path" @@ -281,16 +259,16 @@ var parseRequestUrlTests = []struct { {"../dir/", false}, } -func TestParseRequest(t *testing.T) { +func TestParseRequestURI(t *testing.T) { for _, test := range parseRequestUrlTests { - _, err := ParseRequest(test.url) + _, err := ParseRequestURI(test.url) valid := err == nil if valid != test.expectedValid { t.Errorf("Expected valid=%v for %q; got %v", test.expectedValid, test.url, valid) } } - url, err := ParseRequest(pathThatLooksSchemeRelative) + url, err := ParseRequestURI(pathThatLooksSchemeRelative) if err != nil { t.Fatalf("Unexpected error %v", err) } @@ -319,9 +297,6 @@ func DoTestString(t *testing.T, parse func(string) (*URL, error), name string, t func TestURLString(t *testing.T) { DoTestString(t, Parse, "Parse", urltests) - DoTestString(t, Parse, "Parse", urlnofragtests) - DoTestString(t, ParseWithReference, "ParseWithReference", urltests) - DoTestString(t, ParseWithReference, "ParseWithReference", urlfragtests) } type EscapeTest struct { @@ -538,7 +513,7 @@ var resolveReferenceTests = []struct { func TestResolveReference(t *testing.T) { mustParse := func(url string) *URL { - u, err := ParseWithReference(url) + u, err := Parse(url) if err != nil { t.Fatalf("Expected URL to parse: %q, got error: %v", url, err) } @@ -589,7 +564,7 @@ func TestResolveReference(t *testing.T) { func TestResolveReferenceOpaque(t *testing.T) { mustParse := func(url string) *URL { - u, err := ParseWithReference(url) + u, err := Parse(url) if err != nil { t.Fatalf("Expected URL to parse: %q, got error: %v", url, err) } diff --git a/libgo/go/old/netchan/netchan_test.go b/libgo/go/old/netchan/netchan_test.go index 53f0f787765..9a7c076d59c 100644 --- a/libgo/go/old/netchan/netchan_test.go +++ b/libgo/go/old/netchan/netchan_test.go @@ -291,6 +291,10 @@ func exportLoopback(exp *Exporter, t *testing.T) { // This test checks that channel operations can proceed // even when other concurrent operations are blocked. func TestIndependentSends(t *testing.T) { + if testing.Short() { + t.Logf("disabled test during -short") + return + } exp, imp := pair(t) exportLoopback(exp, t) @@ -378,6 +382,10 @@ const flowCount = 100 // test flow control from exporter to importer. func TestExportFlowControl(t *testing.T) { + if testing.Short() { + t.Logf("disabled test during -short") + return + } exp, imp := pair(t) sendDone := make(chan bool, 1) @@ -394,6 +402,10 @@ func TestExportFlowControl(t *testing.T) { // test flow control from importer to exporter. func TestImportFlowControl(t *testing.T) { + if testing.Short() { + t.Logf("disabled test during -short") + return + } exp, imp := pair(t) ch := make(chan int) diff --git a/libgo/go/os/dir_plan9.go b/libgo/go/os/dir_plan9.go index f2dc15409db..7fa4c7f4449 100644 --- a/libgo/go/os/dir_plan9.go +++ b/libgo/go/os/dir_plan9.go @@ -10,6 +10,9 @@ import ( "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) { // If this file has no dirinfo, create one. if file.dirinfo == nil { @@ -35,7 +38,7 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) { break } if d.nbuf < syscall.STATFIXLEN { - return result, &PathError{"readdir", file.name, Eshortstat} + return result, &PathError{"readdir", file.name, errShortStat} } } @@ -43,7 +46,7 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) { m, _ := gbit16(d.buf[d.bufp:]) m += 2 if m < syscall.STATFIXLEN { - return result, &PathError{"readdir", file.name, Eshortstat} + return result, &PathError{"readdir", file.name, errShortStat} } dir, e := UnmarshalDir(d.buf[d.bufp : d.bufp+int(m)]) if e != nil { @@ -138,7 +141,7 @@ func UnmarshalDir(b []byte) (d *Dir, err error) { n, b = gbit16(b) if int(n) != len(b) { - return nil, Ebadstat + return nil, errBadStat } d = new(Dir) @@ -155,7 +158,7 @@ func UnmarshalDir(b []byte) (d *Dir, err error) { d.Muid, b = gstring(b) if len(b) != 0 { - return nil, Ebadstat + return nil, errBadStat } return d, nil diff --git a/libgo/go/os/env.go b/libgo/go/os/env.go index 59350510ccf..eb265f24138 100644 --- a/libgo/go/os/env.go +++ b/libgo/go/os/env.go @@ -6,10 +6,7 @@ package os -import ( - "errors" - "syscall" -) +import "syscall" // Expand replaces ${var} or $var in the string based on the mapping function. // Invocations of undefined variables are replaced with the empty string. @@ -77,26 +74,10 @@ func getShellName(s string) (string, int) { return s[:i], i } -// ENOENV is the error indicating that an environment variable does not exist. -var ENOENV = errors.New("no such environment variable") - -// Getenverror retrieves the value of the environment variable named by the key. -// It returns the value and an error, if any. -func Getenverror(key string) (value string, err error) { - if len(key) == 0 { - return "", EINVAL - } - val, found := syscall.Getenv(key) - if !found { - return "", ENOENV - } - return val, nil -} - // Getenv retrieves the value of the environment variable named by the key. // It returns the value, which will be empty if the variable is not present. func Getenv(key string) string { - v, _ := Getenverror(key) + v, _ := syscall.Getenv(key) return v } diff --git a/libgo/go/os/error.go b/libgo/go/os/error.go index 135cdae1f9b..e0b83b5c22c 100644 --- a/libgo/go/os/error.go +++ b/libgo/go/os/error.go @@ -4,6 +4,18 @@ package os +import ( + "errors" +) + +// Portable analogs of some common system call errors. +var ( + ErrInvalid = errors.New("invalid argument") + ErrPermission = errors.New("permission denied") + ErrExist = errors.New("file already exists") + ErrNotExist = errors.New("file does not exist") +) + // PathError records an error and the operation and file path that caused it. type PathError struct { Op string diff --git a/libgo/go/os/error_plan9.go b/libgo/go/os/error_plan9.go index cc847e07743..159d685e7cd 100644 --- a/libgo/go/os/error_plan9.go +++ b/libgo/go/os/error_plan9.go @@ -4,34 +4,38 @@ package os -import ( - "errors" - "syscall" -) +// IsExist returns whether the error is known to report that a file already exists. +func IsExist(err error) bool { + if pe, ok := err.(*PathError); ok { + err = pe.Err + } + return contains(err.Error(), " exists") +} -var ( - Eshortstat = errors.New("stat buffer too small") - Ebadstat = errors.New("malformed stat buffer") - Ebadfd = errors.New("fd out of range or not open") - Ebadarg = errors.New("bad arg in system call") - Enotdir = errors.New("not a directory") - Enonexist = errors.New("file does not exist") - Eexist = errors.New("file already exists") - Eio = errors.New("i/o error") - Eperm = errors.New("permission denied") +// IsNotExist returns whether the error is known to report that a file does not exist. +func IsNotExist(err error) bool { + if pe, ok := err.(*PathError); ok { + err = pe.Err + } + return contains(err.Error(), "does not exist") +} - EINVAL = Ebadarg - ENOTDIR = Enotdir - ENOENT = Enonexist - EEXIST = Eexist - EIO = Eio - EACCES = Eperm - EPERM = Eperm - EISDIR = syscall.EISDIR +// IsPermission returns whether the error is known to report that permission is denied. +func IsPermission(err error) bool { + if pe, ok := err.(*PathError); ok { + err = pe.Err + } + return contains(err.Error(), "permission denied") +} - EBADF = errors.New("bad file descriptor") - ENAMETOOLONG = errors.New("file name too long") - ERANGE = errors.New("math result not representable") - EPIPE = errors.New("Broken Pipe") - EPLAN9 = errors.New("not supported by plan 9") -) +// contains is a local version of strings.Contains. It knows len(sep) > 1. +func contains(s, sep string) bool { + n := len(sep) + c := sep[0] + for i := 0; i+n <= len(s); i++ { + if s[i] == c && s[i:i+n] == sep { + return true + } + } + return false +} diff --git a/libgo/go/os/error_posix.go b/libgo/go/os/error_posix.go index 57c9b6f2786..74b75d11218 100644 --- a/libgo/go/os/error_posix.go +++ b/libgo/go/os/error_posix.go @@ -8,44 +8,29 @@ package os import "syscall" -// Commonly known Unix errors. -var ( - EPERM error = syscall.EPERM - ENOENT error = syscall.ENOENT - ESRCH error = syscall.ESRCH - EINTR error = syscall.EINTR - EIO error = syscall.EIO - E2BIG error = syscall.E2BIG - ENOEXEC error = syscall.ENOEXEC - EBADF error = syscall.EBADF - ECHILD error = syscall.ECHILD - EDEADLK error = syscall.EDEADLK - ENOMEM error = syscall.ENOMEM - EACCES error = syscall.EACCES - EFAULT error = syscall.EFAULT - EBUSY error = syscall.EBUSY - EEXIST error = syscall.EEXIST - EXDEV error = syscall.EXDEV - ENODEV error = syscall.ENODEV - ENOTDIR error = syscall.ENOTDIR - EISDIR error = syscall.EISDIR - EINVAL error = syscall.EINVAL - ENFILE error = syscall.ENFILE - EMFILE error = syscall.EMFILE - ENOTTY error = syscall.ENOTTY - EFBIG error = syscall.EFBIG - ENOSPC error = syscall.ENOSPC - ESPIPE error = syscall.ESPIPE - EROFS error = syscall.EROFS - EMLINK error = syscall.EMLINK - EPIPE error = syscall.EPIPE - EAGAIN error = syscall.EAGAIN - EDOM error = syscall.EDOM - ERANGE error = syscall.ERANGE - EADDRINUSE error = syscall.EADDRINUSE - ECONNREFUSED error = syscall.ECONNREFUSED - ENAMETOOLONG error = syscall.ENAMETOOLONG - EAFNOSUPPORT error = syscall.EAFNOSUPPORT - ETIMEDOUT error = syscall.ETIMEDOUT - ENOTCONN error = syscall.ENOTCONN -) +// IsExist returns whether the error is known to report that a file already exists. +// It is satisfied by ErrExist as well as some syscall errors. +func IsExist(err error) bool { + if pe, ok := err.(*PathError); ok { + err = pe.Err + } + return err == syscall.EEXIST || err == ErrExist +} + +// IsNotExist returns whether the error is known to report that a file does not exist. +// It is satisfied by ErrNotExist as well as some syscall errors. +func IsNotExist(err error) bool { + if pe, ok := err.(*PathError); ok { + err = pe.Err + } + return err == syscall.ENOENT || err == ErrNotExist +} + +// IsPermission returns whether the error is known to report that permission is denied. +// It is satisfied by ErrPermission as well as some syscall errors. +func IsPermission(err error) bool { + if pe, ok := err.(*PathError); ok { + err = pe.Err + } + return err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission +} diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go index fe254672169..ebe92a9fba3 100644 --- a/libgo/go/os/exec/exec.go +++ b/libgo/go/os/exec/exec.go @@ -79,9 +79,9 @@ type Cmd struct { // Process is the underlying process, once started. Process *os.Process - // Waitmsg contains information about an exited process, + // ProcessState contains information about an exited process, // available after a call to Wait or Run. - Waitmsg *os.Waitmsg + ProcessState *os.ProcessState err error // last error (from LookPath, stdin, stdout, stderr) finished bool // when Wait was called @@ -266,11 +266,11 @@ func (c *Cmd) Start() error { // An ExitError reports an unsuccessful exit by a command. type ExitError struct { - *os.Waitmsg + *os.ProcessState } func (e *ExitError) Error() string { - return e.Waitmsg.String() + return e.ProcessState.String() } // Wait waits for the command to exit. @@ -291,8 +291,8 @@ func (c *Cmd) Wait() error { return errors.New("exec: Wait was already called") } c.finished = true - msg, err := c.Process.Wait(0) - c.Waitmsg = msg + state, err := c.Process.Wait() + c.ProcessState = state var copyError error for _ = range c.goroutine { @@ -307,8 +307,8 @@ func (c *Cmd) Wait() error { if err != nil { return err - } else if !msg.Exited() || msg.ExitStatus() != 0 { - return &ExitError{msg} + } else if !state.Success() { + return &ExitError{state} } return copyError diff --git a/libgo/go/os/exec/lp_plan9.go b/libgo/go/os/exec/lp_plan9.go index d88cd0df959..0e229e03ee7 100644 --- a/libgo/go/os/exec/lp_plan9.go +++ b/libgo/go/os/exec/lp_plan9.go @@ -8,6 +8,7 @@ import ( "errors" "os" "strings" + "syscall" ) // ErrNotFound is the error resulting if a path search failed to find an executable file. @@ -21,7 +22,7 @@ func findExecutable(file string) error { if m := d.Mode(); !m.IsDir() && m&0111 != 0 { return nil } - return os.EPERM + return syscall.EPERM } // LookPath searches for an executable binary named file diff --git a/libgo/go/os/exec/lp_unix.go b/libgo/go/os/exec/lp_unix.go index 2d3a919dc6e..21632219972 100644 --- a/libgo/go/os/exec/lp_unix.go +++ b/libgo/go/os/exec/lp_unix.go @@ -23,7 +23,7 @@ func findExecutable(file string) error { if m := d.Mode(); !m.IsDir() && m&0111 != 0 { return nil } - return os.EPERM + return os.ErrPermission } // LookPath searches for an executable binary named file diff --git a/libgo/go/os/exec/lp_windows.go b/libgo/go/os/exec/lp_windows.go index b7efcd68b80..d8351d7e6d3 100644 --- a/libgo/go/os/exec/lp_windows.go +++ b/libgo/go/os/exec/lp_windows.go @@ -19,7 +19,7 @@ func chkStat(file string) error { return err } if d.IsDir() { - return os.EPERM + return os.ErrPermission } return nil } @@ -39,7 +39,7 @@ func findExecutable(file string, exts []string) (string, error) { return f, nil } } - return ``, os.ENOENT + return ``, os.ErrNotExist } // LookPath searches for an executable binary named file diff --git a/libgo/go/os/exec_plan9.go b/libgo/go/os/exec_plan9.go index 08f16b86d54..1c9e2b997f8 100644 --- a/libgo/go/os/exec_plan9.go +++ b/libgo/go/os/exec_plan9.go @@ -8,6 +8,7 @@ import ( "errors" "runtime" "syscall" + "time" ) // StartProcess starts a new process with the program, arguments and attributes @@ -20,18 +21,10 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e Sys: attr.Sys, } - // Create array of integer (system) fds. - intfd := make([]int, len(attr.Files)) - for i, f := range attr.Files { - if f == nil { - intfd[i] = -1 - } else { - intfd[i] = f.Fd() - } + for _, f := range attr.Files { + sysattr.Files = append(sysattr.Files, f.Fd()) } - sysattr.Files = intfd - pid, h, e := syscall.StartProcess(name, argv, sysattr) if e != nil { return nil, &PathError{"fork/exec", name, e} @@ -72,19 +65,13 @@ func (p *Process) Kill() error { return e } -// Waitmsg stores the information about an exited process as reported by Wait. -type Waitmsg struct { - syscall.Waitmsg -} - // Wait waits for the Process to exit or stop, and then returns a -// Waitmsg describing its status and an error, if any. The options -// (WNOHANG etc.) affect the behavior of the Wait call. -func (p *Process) Wait(options int) (w *Waitmsg, err error) { +// ProcessState describing its status and an error, if any. +func (p *Process) Wait() (ps *ProcessState, err error) { var waitmsg syscall.Waitmsg if p.Pid == -1 { - return nil, EINVAL + return nil, ErrInvalid } for true { @@ -100,21 +87,11 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) { } } - return &Waitmsg{waitmsg}, nil -} - -// Wait waits for process pid to exit or stop, and then returns a -// Waitmsg describing its status and an error, if any. The options -// (WNOHANG etc.) affect the behavior of the Wait call. -// Wait is equivalent to calling FindProcess and then Wait -// and Release on the result. -func Wait(pid int, options int) (w *Waitmsg, err error) { - p, e := FindProcess(pid) - if e != nil { - return nil, e + ps = &ProcessState{ + pid: waitmsg.Pid, + status: &waitmsg, } - defer p.Release() - return p.Wait(options) + return ps, nil } // Release releases any resources associated with the Process. @@ -131,9 +108,57 @@ func findProcess(pid int) (p *Process, err error) { return newProcess(pid, 0), nil } -func (w *Waitmsg) String() string { - if w == nil { +// ProcessState stores information about process as reported by Wait. +type ProcessState struct { + pid int // The process's id. + status *syscall.Waitmsg // System-dependent status info. +} + +// Pid returns the process id of the exited process. +func (p *ProcessState) Pid() int { + return p.pid +} + +// Exited returns whether the program has exited. +func (p *ProcessState) Exited() bool { + return p.status.Exited() +} + +// Success reports whether the program exited successfully, +// such as with exit status 0 on Unix. +func (p *ProcessState) Success() bool { + return p.status.ExitStatus() == 0 +} + +// Sys returns system-dependent exit information about +// the process. Convert it to the appropriate underlying +// type, such as *syscall.Waitmsg on Plan 9, to access its contents. +func (p *ProcessState) Sys() interface{} { + return p.status +} + +// SysUsage returns system-dependent resource usage information about +// the exited process. Convert it to the appropriate underlying +// type, such as *syscall.Waitmsg on Plan 9, to access its contents. +func (p *ProcessState) SysUsage() interface{} { + return p.status +} + +// UserTime returns the user CPU time of the exited process and its children. +// It is always reported as 0 on Windows. +func (p *ProcessState) UserTime() time.Duration { + return time.Duration(p.status.Time[0]) * time.Millisecond +} + +// SystemTime returns the system CPU time of the exited process and its children. +// It is always reported as 0 on Windows. +func (p *ProcessState) SystemTime() time.Duration { + return time.Duration(p.status.Time[1]) * time.Millisecond +} + +func (p *ProcessState) String() string { + if p == nil { return "<nil>" } - return "exit status: " + w.Msg + return "exit status: " + p.status.Msg } diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go index 33a689eb045..4a75cb67fb5 100644 --- a/libgo/go/os/exec_posix.go +++ b/libgo/go/os/exec_posix.go @@ -42,32 +42,41 @@ func (p *Process) Kill() error { return p.Signal(Kill) } -// TODO(rsc): Should os implement its own syscall.WaitStatus -// wrapper with the methods, or is exposing the underlying one enough? -// -// TODO(rsc): Certainly need to have Rusage struct, -// since syscall one might have different field types across -// different OS. - -// Waitmsg stores the information about an exited process as reported by Wait. -type Waitmsg struct { - Pid int // The process's id. - syscall.WaitStatus // System-dependent status info. - Rusage *syscall.Rusage // System-dependent resource usage info. +// ProcessState stores information about process as reported by Wait. +type ProcessState struct { + pid int // The process's id. + status syscall.WaitStatus // System-dependent status info. + rusage *syscall.Rusage } -// Wait waits for process pid to exit or stop, and then returns a -// Waitmsg describing its status and an error, if any. The options -// (WNOHANG etc.) affect the behavior of the Wait call. -// Wait is equivalent to calling FindProcess and then Wait -// and Release on the result. -func Wait(pid int, options int) (w *Waitmsg, err error) { - p, e := FindProcess(pid) - if e != nil { - return nil, e - } - defer p.Release() - return p.Wait(options) +// Pid returns the process id of the exited process. +func (p *ProcessState) Pid() int { + return p.pid +} + +// Exited returns whether the program has exited. +func (p *ProcessState) Exited() bool { + return p.status.Exited() +} + +// Success reports whether the program exited successfully, +// such as with exit status 0 on Unix. +func (p *ProcessState) Success() bool { + return p.status.ExitStatus() == 0 +} + +// Sys returns system-dependent exit information about +// the process. Convert it to the appropriate underlying +// type, such as syscall.WaitStatus on Unix, to access its contents. +func (p *ProcessState) Sys() interface{} { + return p.status +} + +// SysUsage returns system-dependent resource usage information about +// the exited process. Convert it to the appropriate underlying +// type, such as *syscall.Rusage on Unix, to access its contents. +func (p *ProcessState) SysUsage() interface{} { + return p.rusage } // Convert i to decimal string. @@ -97,26 +106,26 @@ func itod(i int) string { return string(b[bp:]) } -func (w *Waitmsg) String() string { - if w == nil { +func (p *ProcessState) String() string { + if p == nil { return "<nil>" } - // TODO(austin) Use signal names when possible? + status := p.Sys().(syscall.WaitStatus) res := "" switch { - case w.Exited(): - res = "exit status " + itod(w.ExitStatus()) - case w.Signaled(): - res = "signal " + itod(int(w.Signal())) - case w.Stopped(): - res = "stop signal " + itod(int(w.StopSignal())) - if w.StopSignal() == syscall.SIGTRAP && w.TrapCause() != 0 { - res += " (trap " + itod(w.TrapCause()) + ")" + case status.Exited(): + res = "exit status " + itod(status.ExitStatus()) + case status.Signaled(): + res = "signal " + itod(int(status.Signal())) + case status.Stopped(): + res = "stop signal " + itod(int(status.StopSignal())) + if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 { + res += " (trap " + itod(status.TrapCause()) + ")" } - case w.Continued(): + case status.Continued(): res = "continued" } - if w.CoreDump() { + if status.CoreDump() { res += " (core dumped)" } return res diff --git a/libgo/go/os/exec_unix.go b/libgo/go/os/exec_unix.go index 7fe7c2fe8cd..8d000e9ef15 100644 --- a/libgo/go/os/exec_unix.go +++ b/libgo/go/os/exec_unix.go @@ -10,46 +10,30 @@ import ( "errors" "runtime" "syscall" + "time" ) -// Options for Wait. -const ( - WNOHANG = syscall.WNOHANG // Don't wait if no process has exited. - WSTOPPED = syscall.WSTOPPED // If set, status of stopped subprocesses is also reported. - WUNTRACED = syscall.WUNTRACED // Usually an alias for WSTOPPED. - WRUSAGE = 1 << 20 // Record resource usage. -) - -// WRUSAGE must not be too high a bit, to avoid clashing with Linux's -// WCLONE, WALL, and WNOTHREAD flags, which sit in the top few bits of -// the options - // Wait waits for the Process to exit or stop, and then returns a -// Waitmsg describing its status and an error, if any. The options -// (WNOHANG etc.) affect the behavior of the Wait call. -func (p *Process) Wait(options int) (w *Waitmsg, err error) { +// ProcessState describing its status and an error, if any. +func (p *Process) Wait() (ps *ProcessState, err error) { if p.Pid == -1 { - return nil, EINVAL + return nil, syscall.EINVAL } var status syscall.WaitStatus - var rusage *syscall.Rusage - if options&WRUSAGE != 0 { - rusage = new(syscall.Rusage) - options ^= WRUSAGE - } - pid1, e := syscall.Wait4(p.Pid, &status, options, rusage) + var rusage syscall.Rusage + pid1, e := syscall.Wait4(p.Pid, &status, 0, &rusage) if e != nil { return nil, NewSyscallError("wait", e) } - // With WNOHANG pid is 0 if child has not exited. - if pid1 != 0 && options&WSTOPPED == 0 { + if pid1 != 0 { p.done = true } - w = new(Waitmsg) - w.Pid = pid1 - w.WaitStatus = status - w.Rusage = rusage - return w, nil + ps = &ProcessState{ + pid: pid1, + status: status, + rusage: &rusage, + } + return ps, nil } // Signal sends a signal to the Process. @@ -80,3 +64,13 @@ func findProcess(pid int) (p *Process, err error) { // NOOP for unix. return newProcess(pid, 0), nil } + +// UserTime returns the user CPU time of the exited process and its children. +func (p *ProcessState) UserTime() time.Duration { + return time.Duration(p.rusage.Utime.Nano()) * time.Nanosecond +} + +// SystemTime returns the system CPU time of the exited process and its children. +func (p *ProcessState) SystemTime() time.Duration { + return time.Duration(p.rusage.Stime.Nano()) * time.Nanosecond +} diff --git a/libgo/go/os/exec_windows.go b/libgo/go/os/exec_windows.go index f357a3034b1..dab0dc97571 100644 --- a/libgo/go/os/exec_windows.go +++ b/libgo/go/os/exec_windows.go @@ -8,12 +8,13 @@ import ( "errors" "runtime" "syscall" + "time" "unsafe" ) // Wait waits for the Process to exit or stop, and then returns a -// Waitmsg describing its status and an error, if any. -func (p *Process) Wait(options int) (w *Waitmsg, err error) { +// ProcessState describing its status and an error, if any. +func (p *Process) Wait() (ps *ProcessState, err error) { s, e := syscall.WaitForSingleObject(syscall.Handle(p.handle), syscall.INFINITE) switch s { case syscall.WAIT_OBJECT_0: @@ -29,7 +30,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) { return nil, NewSyscallError("GetExitCodeProcess", e) } p.done = true - return &Waitmsg{p.Pid, syscall.WaitStatus{Status: s, ExitCode: ec}, new(syscall.Rusage)}, nil + return &ProcessState{p.Pid, syscall.WaitStatus{Status: s, ExitCode: ec}, new(syscall.Rusage)}, nil } // Signal sends a signal to the Process. @@ -48,7 +49,7 @@ func (p *Process) Signal(sig Signal) error { // Release releases any resources associated with the Process. func (p *Process) Release() error { if p.handle == uintptr(syscall.InvalidHandle) { - return EINVAL + return syscall.EINVAL } e := syscall.CloseHandle(syscall.Handle(p.handle)) if e != nil { @@ -83,3 +84,15 @@ func init() { Args[i] = string(syscall.UTF16ToString((*v)[:])) } } + +// UserTime returns the user CPU time of the exited process and its children. +// For now, it is always reported as 0 on Windows. +func (p *ProcessState) UserTime() time.Duration { + return 0 +} + +// SystemTime returns the system CPU time of the exited process and its children. +// For now, it is always reported as 0 on Windows. +func (p *ProcessState) SystemTime() time.Duration { + return 0 +} diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go index 85f151e2840..1c3d0172d34 100644 --- a/libgo/go/os/file.go +++ b/libgo/go/os/file.go @@ -7,11 +7,33 @@ // Go-like; failing calls return values of type error rather than error numbers. // Often, more information is available within the error. For example, // if a call that takes a file name fails, such as Open or Stat, the error -// will include failing file name when printed and will be of type *PathError, -// which may be unpacked for more information. +// will include the failing file name when printed and will be of type +// *PathError, which may be unpacked for more information. // // The os interface is intended to be uniform across all operating systems. // Features not generally available appear in the system-specific package syscall. +// +// Here is a simple example, opening a file and reading some of it. +// +// file, err := os.Open("file.go") // For read access. +// if err != nil { +// log.Fatal(err) +// } +// +// If the open fails, the error string will be self-explanatory, like +// +// open file.go: no such file or directory +// +// The file's data can then be read into a slice of bytes. Read and +// Write take their byte counts from the length of the artument slice. +// +// data := make([]byte, 100) +// count, err := file.Read(data) +// if err != nil { +// log.Fatal(err) +// } +// fmt.Printf("read %d bytes: %q\n", count, data[:count]) +// package os import ( @@ -50,12 +72,25 @@ const ( SEEK_END int = 2 // seek relative to the end ) +// LinkError records an error during a link or symlink or rename +// system call and the paths that caused it. +type LinkError struct { + Op string + Old string + New string + Err error +} + +func (e *LinkError) Error() string { + return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error() +} + // Read reads up to len(b) bytes from the File. // It returns the number of bytes read and an error, if any. // EOF is signaled by a zero count with err set to io.EOF. func (f *File) Read(b []byte) (n int, err error) { if f == nil { - return 0, EINVAL + return 0, ErrInvalid } n, e := f.read(b) if n < 0 { @@ -76,7 +111,7 @@ func (f *File) Read(b []byte) (n int, err error) { // At end of file, that error is io.EOF. func (f *File) ReadAt(b []byte, off int64) (n int, err error) { if f == nil { - return 0, EINVAL + return 0, ErrInvalid } for len(b) > 0 { m, e := f.pread(b, off) @@ -99,7 +134,7 @@ func (f *File) ReadAt(b []byte, off int64) (n int, err error) { // Write returns a non-nil error when n != len(b). func (f *File) Write(b []byte) (n int, err error) { if f == nil { - return 0, EINVAL + return 0, ErrInvalid } n, e := f.write(b) if n < 0 { @@ -119,7 +154,7 @@ func (f *File) Write(b []byte) (n int, err error) { // WriteAt returns a non-nil error when n != len(b). func (f *File) WriteAt(b []byte, off int64) (n int, err error) { if f == nil { - return 0, EINVAL + return 0, ErrInvalid } for len(b) > 0 { m, e := f.pwrite(b, off) @@ -153,7 +188,7 @@ func (f *File) Seek(offset int64, whence int) (ret int64, err error) { // an array of bytes. func (f *File) WriteString(s string) (ret int, err error) { if f == nil { - return 0, EINVAL + return 0, ErrInvalid } return f.Write([]byte(s)) } diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go index fed2b809175..cb0e9ef9289 100644 --- a/libgo/go/os/file_plan9.go +++ b/libgo/go/os/file_plan9.go @@ -5,11 +5,14 @@ package os import ( + "errors" "runtime" "syscall" "time" ) +var ErrPlan9 = errors.New("unimplemented on Plan 9") + // File represents an open file descriptor. type File struct { *file @@ -26,19 +29,20 @@ type file struct { } // Fd returns the integer Unix file descriptor referencing the open file. -func (file *File) Fd() int { - if file == nil { - return -1 +func (f *File) Fd() uintptr { + if f == nil { + return ^(uintptr(0)) } - return file.fd + return uintptr(f.fd) } // NewFile returns a new File with the given file descriptor and name. -func NewFile(fd int, name string) *File { - if fd < 0 { +func NewFile(fd uintptr, name string) *File { + fdi := int(fd) + if fdi < 0 { return nil } - f := &File{&file{fd: fd, name: name}} + f := &File{&file{fd: fdi, name: name}} runtime.SetFinalizer(f.file, (*file).close) return f } @@ -128,7 +132,7 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) { } } - return NewFile(fd, name), nil + return NewFile(uintptr(fd), name), nil } // Close closes the File, rendering it unusable for I/O. @@ -139,7 +143,7 @@ func (file *File) Close() error { func (file *file) close() error { if file == nil || file.fd < 0 { - return Ebadfd + return ErrInvalid } var err error syscall.ForkLock.RLock() @@ -202,7 +206,7 @@ func (f *File) Chmod(mode FileMode) error { // of recently written data to disk. func (f *File) Sync() (err error) { if f == nil { - return EINVAL + return ErrInvalid } var d Dir @@ -272,7 +276,6 @@ func Remove(name string) error { } // Rename renames a file. -// If there is an error, it will be of type *PathError. func Rename(oldname, newname string) error { var d Dir d.Null() @@ -330,34 +333,37 @@ func Pipe() (r *File, w *File, err error) { } syscall.ForkLock.RUnlock() - return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil + return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil } // not supported on Plan 9 // Link creates a hard link. +// If there is an error, it will be of type *LinkError. func Link(oldname, newname string) error { - return EPLAN9 + return &LinkError{"link", oldname, newname, ErrPlan9} } +// Symlink creates newname as a symbolic link to oldname. +// If there is an error, it will be of type *LinkError. func Symlink(oldname, newname string) error { - return EPLAN9 + return &LinkError{"symlink", oldname, newname, ErrPlan9} } func Readlink(name string) (string, error) { - return "", EPLAN9 + return "", ErrPlan9 } func Chown(name string, uid, gid int) error { - return EPLAN9 + return ErrPlan9 } func Lchown(name string, uid, gid int) error { - return EPLAN9 + return ErrPlan9 } func (f *File) Chown(uid, gid int) error { - return EPLAN9 + return ErrPlan9 } // TempDir returns the default directory to use for temporary files. diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go index 8d3a00b6c5a..073bd56a471 100644 --- a/libgo/go/os/file_posix.go +++ b/libgo/go/os/file_posix.go @@ -24,20 +24,8 @@ func epipecheck(file *File, e error) { } } -// LinkError records an error during a link or symlink or rename -// system call and the paths that caused it. -type LinkError struct { - Op string - Old string - New string - Err error -} - -func (e *LinkError) Error() string { - return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error() -} - // Link creates newname as a hard link to the oldname file. +// If there is an error, it will be of type *LinkError. func Link(oldname, newname string) error { e := syscall.Link(oldname, newname) if e != nil { @@ -47,6 +35,7 @@ func Link(oldname, newname string) error { } // Symlink creates newname as a symbolic link to oldname. +// If there is an error, it will be of type *LinkError. func Symlink(oldname, newname string) error { e := syscall.Symlink(oldname, newname) if e != nil { @@ -160,7 +149,7 @@ func (f *File) Truncate(size int64) error { // of recently written data to disk. func (f *File) Sync() (err error) { if f == nil { - return EINVAL + return syscall.EINVAL } if e := syscall.Fsync(f.fd); e != nil { return NewSyscallError("fsync", e) diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go index 3d659efe061..a69680cb8c9 100644 --- a/libgo/go/os/file_unix.go +++ b/libgo/go/os/file_unix.go @@ -89,7 +89,7 @@ func (f *File) Close() error { func (file *file) close() error { if file == nil || file.fd < 0 { - return EINVAL + return syscall.EINVAL } var err error if e := syscall.Close(file.fd); e != nil { diff --git a/libgo/go/os/getwd.go b/libgo/go/os/getwd.go index 56836434dbe..81d8fed926e 100644 --- a/libgo/go/os/getwd.go +++ b/libgo/go/os/getwd.go @@ -52,7 +52,7 @@ func Getwd() (pwd string, err error) { pwd = "" for parent := ".."; ; parent = "../" + parent { if len(parent) >= 1024 { // Sanity check - return "", ENAMETOOLONG + return "", syscall.ENAMETOOLONG } fd, err := Open(parent) if err != nil { @@ -74,7 +74,7 @@ func Getwd() (pwd string, err error) { } } fd.Close() - return "", ENOENT + return "", ErrNotExist Found: pd, err := fd.Stat() diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go index 27436dadcda..cff35fcef75 100644 --- a/libgo/go/os/os_test.go +++ b/libgo/go/os/os_test.go @@ -13,6 +13,7 @@ import ( "path/filepath" "runtime" "strings" + "syscall" "testing" "time" ) @@ -538,7 +539,7 @@ func exec(t *testing.T, dir, cmd string, args []string, expect string) { t.Errorf("exec %q returned %q wanted %q", strings.Join(append([]string{cmd}, args...), " "), output, expect) } - p.Wait(0) + p.Wait() } func TestStartProcess(t *testing.T) { @@ -767,7 +768,7 @@ func TestSeek(t *testing.T) { for i, tt := range tests { off, err := f.Seek(tt.in, tt.whence) if off != tt.out || err != nil { - if e, ok := err.(*PathError); ok && e.Err == EINVAL && tt.out > 1<<32 { + if e, ok := err.(*PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 { // Reiserfs rejects the big seeks. // http://code.google.com/p/go/issues/detail?id=91 break @@ -787,17 +788,17 @@ var openErrorTests = []openErrorTest{ { sfdir + "/no-such-file", O_RDONLY, - ENOENT, + syscall.ENOENT, }, { sfdir, O_WRONLY, - EISDIR, + syscall.EISDIR, }, { sfdir + "/" + sfname + "/no-such-file", O_WRONLY, - ENOTDIR, + syscall.ENOTDIR, }, } @@ -850,7 +851,7 @@ func run(t *testing.T, cmd []string) string { var b bytes.Buffer io.Copy(&b, r) - _, err = p.Wait(0) + _, err = p.Wait() if err != nil { t.Fatalf("run hostname Wait: %v", err) } @@ -983,32 +984,31 @@ func TestAppend(t *testing.T) { } func TestStatDirWithTrailingSlash(t *testing.T) { - // Create new dir, in _test so it will get - // cleaned up by make if not by us. - path := "_test/_TestStatDirWithSlash_" - err := MkdirAll(path, 0777) + // Create new temporary directory and arrange to clean it up. + path, err := ioutil.TempDir("", "/_TestStatDirWithSlash_") if err != nil { - t.Fatalf("MkdirAll %q: %s", path, err) + t.Fatalf("TempDir: %s", err) } defer RemoveAll(path) // Stat of path should succeed. _, err = Stat(path) if err != nil { - t.Fatal("stat failed:", err) + t.Fatalf("stat %s failed: %s", path, err) } // Stat of path+"/" should succeed too. - _, err = Stat(path + "/") + path += "/" + _, err = Stat(path) if err != nil { - t.Fatal("stat failed:", err) + t.Fatalf("stat %s failed: %s", path, err) } } -func TestNilWaitmsgString(t *testing.T) { - var w *Waitmsg - s := w.String() +func TestNilProcessStateString(t *testing.T) { + var ps *ProcessState + s := ps.String() if s != "<nil>" { - t.Errorf("(*Waitmsg)(nil).String() = %q, want %q", s, "<nil>") + t.Errorf("(*ProcessState)(nil).String() = %q, want %q", s, "<nil>") } } diff --git a/libgo/go/os/path.go b/libgo/go/os/path.go index e962f3e397b..02a77ec8051 100644 --- a/libgo/go/os/path.go +++ b/libgo/go/os/path.go @@ -4,7 +4,10 @@ package os -import "io" +import ( + "io" + "syscall" +) // MkdirAll creates a directory named path, // along with any necessary parents, and returns nil, @@ -20,7 +23,7 @@ func MkdirAll(path string, perm FileMode) error { if dir.IsDir() { return nil } - return &PathError{"mkdir", path, ENOTDIR} + return &PathError{"mkdir", path, syscall.ENOTDIR} } // Doesn't already exist; make sure parent does. @@ -70,7 +73,7 @@ func RemoveAll(path string) error { // Otherwise, is this a directory we need to recurse into? dir, serr := Lstat(path) if serr != nil { - if serr, ok := serr.(*PathError); ok && (serr.Err == ENOENT || serr.Err == ENOTDIR) { + if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) { return nil } return serr diff --git a/libgo/go/os/path_test.go b/libgo/go/os/path_test.go index 18634ba410e..c1e3fb35436 100644 --- a/libgo/go/os/path_test.go +++ b/libgo/go/os/path_test.go @@ -8,18 +8,18 @@ import ( . "os" "path/filepath" "runtime" + "syscall" "testing" ) func TestMkdirAll(t *testing.T) { - // Create new dir, in _test so it will get - // cleaned up by make if not by us. - path := "_test/_TestMkdirAll_/dir/./dir2" + tmpDir := TempDir() + path := tmpDir + "/_TestMkdirAll_/dir/./dir2" err := MkdirAll(path, 0777) if err != nil { t.Fatalf("MkdirAll %q: %s", path, err) } - defer RemoveAll("_test/_TestMkdirAll_") + defer RemoveAll(tmpDir + "/_TestMkdirAll_") // Already exists, should succeed. err = MkdirAll(path, 0777) @@ -63,7 +63,7 @@ func TestMkdirAll(t *testing.T) { } if runtime.GOOS == "windows" { - path := `_test\_TestMkdirAll_\dir\.\dir2\` + path := tmpDir + `\_TestMkdirAll_\dir\.\dir2\` err := MkdirAll(path, 0777) if err != nil { t.Fatalf("MkdirAll %q: %s", path, err) @@ -72,8 +72,9 @@ func TestMkdirAll(t *testing.T) { } func TestRemoveAll(t *testing.T) { + tmpDir := TempDir() // Work directory. - path := "_test/_TestRemoveAll_" + path := tmpDir + "/_TestRemoveAll_" fpath := path + "/file" dpath := path + "/dir" @@ -170,19 +171,22 @@ func TestMkdirAllWithSymlink(t *testing.T) { return } - err := Mkdir("_test/dir", 0755) + tmpDir := TempDir() + dir := tmpDir + "/dir" + err := Mkdir(dir, 0755) if err != nil { - t.Fatal(`Mkdir "_test/dir":`, err) + t.Fatalf("Mkdir %s: %s", dir, err) } - defer RemoveAll("_test/dir") + defer RemoveAll(dir) - err = Symlink("dir", "_test/link") + link := tmpDir + "/link" + err = Symlink("dir", link) if err != nil { - t.Fatal(`Symlink "dir", "_test/link":`, err) + t.Fatalf("Symlink %s: %s", link, err) } - defer RemoveAll("_test/link") + defer RemoveAll(link) - path := "_test/link/foo" + path := link + "/foo" err = MkdirAll(path, 0755) if err != nil { t.Errorf("MkdirAll %q: %s", path, err) @@ -198,7 +202,7 @@ func TestMkdirAllAtSlash(t *testing.T) { if err != nil { pathErr, ok := err.(*PathError) // common for users not to be able to write to / - if ok && pathErr.Err == EACCES { + if ok && pathErr.Err == syscall.EACCES { return } t.Fatalf(`MkdirAll "/_go_os_test/dir": %v`, err) diff --git a/libgo/go/os/signal/signal_stub.go b/libgo/go/os/signal/signal_stub.go new file mode 100644 index 00000000000..fc227cf4c2d --- /dev/null +++ b/libgo/go/os/signal/signal_stub.go @@ -0,0 +1,11 @@ +// 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 plan9 + +package signal + +import "os" + +func enableSignal(sig os.Signal) {} diff --git a/libgo/go/os/stat_plan9.go b/libgo/go/os/stat_plan9.go index 00622581f4f..a7990a359ec 100644 --- a/libgo/go/os/stat_plan9.go +++ b/libgo/go/os/stat_plan9.go @@ -62,7 +62,7 @@ func dirstat(arg interface{}) (d *Dir, err error) { return nil, &PathError{"stat", name, err} } if n < syscall.STATFIXLEN { - return nil, &PathError{"stat", name, Eshortstat} + return nil, &PathError{"stat", name, errShortStat} } // Pull the real size out of the stat message. @@ -79,7 +79,7 @@ func dirstat(arg interface{}) (d *Dir, err error) { return } } - return nil, &PathError{"stat", name, Ebadstat} + return nil, &PathError{"stat", name, errBadStat} } // Stat returns a FileInfo structure describing the named file. diff --git a/libgo/go/path/example_test.go b/libgo/go/path/example_test.go index f0ac1301408..ca18b32305e 100644 --- a/libgo/go/path/example_test.go +++ b/libgo/go/path/example_test.go @@ -11,17 +11,11 @@ import ( "path" ) -// b func ExampleBase() { fmt.Println(path.Base("/a/b")) + // Output: b } -// Clean("a/c") = "a/c" -// Clean("a//c") = "a/c" -// Clean("a/c/.") = "a/c" -// Clean("a/c/b/..") = "a/c" -// Clean("/../a/c") = "/a/c" -// Clean("/../a/b/../././/c") = "/a/c" func ExampleClean() { paths := []string{ "a/c", @@ -35,31 +29,39 @@ func ExampleClean() { for _, p := range paths { fmt.Printf("Clean(%q) = %q\n", p, path.Clean(p)) } + + // Output: + // Clean("a/c") = "a/c" + // Clean("a//c") = "a/c" + // Clean("a/c/.") = "a/c" + // Clean("a/c/b/..") = "a/c" + // Clean("/../a/c") = "/a/c" + // Clean("/../a/b/../././/c") = "/a/c" } -// /a/b func ExampleDir() { fmt.Println(path.Dir("/a/b/c")) + // Output: /a/b } -// .css func ExampleExt() { fmt.Println(path.Ext("/a/b/c/bar.css")) + // Output: .css } -// true func ExampleIsAbs() { fmt.Println(path.IsAbs("/dev/null")) + // Output: true } -// a/b/c func ExampleJoin() { fmt.Println(path.Join("a", "b", "c")) + // Output: a/b/c } -// static/ myfile.css func ExampleSplit() { fmt.Println(path.Split("static/myfile.css")) + // Output: static/ myfile.css } */ diff --git a/libgo/go/path/filepath/match.go b/libgo/go/path/filepath/match.go index c3678f541d4..38d264fb97a 100644 --- a/libgo/go/path/filepath/match.go +++ b/libgo/go/path/filepath/match.go @@ -12,6 +12,7 @@ import ( "unicode/utf8" ) +// ErrBadPattern indicates a globbing pattern was malformed. var ErrBadPattern = errors.New("syntax error in pattern") // Match returns true if name matches the shell file name pattern. @@ -33,7 +34,8 @@ var ErrBadPattern = errors.New("syntax error in pattern") // lo '-' hi matches character c for lo <= c <= hi // // Match requires pattern to match all of name, not just a substring. -// The only possible error return occurs when the pattern is malformed. +// The only possible returned error is ErrBadPattern, when pattern +// is malformed. // func Match(pattern, name string) (matched bool, err error) { Pattern: @@ -211,7 +213,6 @@ func getEsc(chunk string) (r rune, nchunk string, err error) { // if there is no matching file. The syntax of patterns is the same // as in Match. The pattern may describe hierarchical names such as // /usr/*/bin/ed (assuming the Separator is '/'). -// The only possible error return occurs when the pattern is malformed. // func Glob(pattern string) (matches []string, err error) { if !hasMeta(pattern) { @@ -253,7 +254,6 @@ func Glob(pattern string) (matches []string, err error) { // and appends them to matches. If the directory cannot be // opened, it returns the existing matches. New matches are // added in lexicographical order. -// The only possible error return occurs when the pattern is malformed. func glob(dir, pattern string, matches []string) (m []string, e error) { m = matches fi, err := os.Stat(dir) diff --git a/libgo/go/path/filepath/path.go b/libgo/go/path/filepath/path.go index 3dc52aab467..f468d33264b 100644 --- a/libgo/go/path/filepath/path.go +++ b/libgo/go/path/filepath/path.go @@ -36,7 +36,7 @@ const ( // returns the string ".". // // See also Rob Pike, ``Lexical File Names in Plan 9 or -// Getting Dot-Dot right,'' +// Getting Dot-Dot Right,'' // http://plan9.bell-labs.com/sys/doc/lexnames.html func Clean(path string) string { vol := VolumeName(path) @@ -118,7 +118,8 @@ func Clean(path string) string { } // ToSlash returns the result of replacing each separator character -// in path with a slash ('/') character. +// in path with a slash ('/') character. Multiple separators are +// replaced by multiple slashes. func ToSlash(path string) string { if Separator == '/' { return path @@ -127,7 +128,8 @@ func ToSlash(path string) string { } // FromSlash returns the result of replacing each slash ('/') character -// in path with a separator character. +// in path with a separator character. Multiple slashes are replaced +// by multiple separators. func FromSlash(path string) string { if Separator == '/' { return path @@ -135,7 +137,8 @@ func FromSlash(path string) string { return strings.Replace(path, "/", string(Separator), -1) } -// SplitList splits a list of paths joined by the OS-specific ListSeparator. +// SplitList splits a list of paths joined by the OS-specific ListSeparator, +// usually found in PATH or GOPATH environment variables. func SplitList(path string) []string { if path == "" { return []string{} @@ -158,7 +161,8 @@ func Split(path string) (dir, file string) { } // Join joins any number of path elements into a single path, adding -// a Separator if necessary. All empty strings are ignored. +// a Separator if necessary. The result is Cleaned, in particular +// all empty strings are ignored. func Join(elem ...string) string { for i, e := range elem { if e != "" { @@ -183,7 +187,8 @@ func Ext(path string) string { // EvalSymlinks returns the path name after the evaluation of any symbolic // links. -// If path is relative it will be evaluated relative to the current directory. +// If path is relative the result will be relative to the current directory, +// unless one of the components is an absolute symbolic link. func EvalSymlinks(path string) (string, error) { if runtime.GOOS == "windows" { // Symlinks are not supported under windows. @@ -443,7 +448,7 @@ func Base(path string) string { return path } -// Dir returns the all but the last element of path, typically the path's directory. +// Dir returns all but the last element of path, typically the path's directory. // Trailing path separators are removed before processing. // If the path is empty, Dir returns ".". // If the path consists entirely of separators, Dir returns a single separator. diff --git a/libgo/go/path/filepath/path_test.go b/libgo/go/path/filepath/path_test.go index 36ab422c930..93cca1e4c2b 100644 --- a/libgo/go/path/filepath/path_test.go +++ b/libgo/go/path/filepath/path_test.go @@ -558,6 +558,7 @@ var EvalSymlinksTestDirs = []EvalSymlinksTest{ {"test/dir/link3", "../../"}, {"test/link1", "../test"}, {"test/link2", "dir"}, + {"test/linkabs", "/"}, } var EvalSymlinksTests = []EvalSymlinksTest{ @@ -570,6 +571,7 @@ var EvalSymlinksTests = []EvalSymlinksTest{ {"test/link2/..", "test"}, {"test/dir/link3", "."}, {"test/link2/link3/test", "test"}, + {"test/linkabs", "/"}, } var EvalSymlinksAbsWindowsTests = []EvalSymlinksTest{ @@ -628,6 +630,9 @@ func TestEvalSymlinks(t *testing.T) { for _, d := range tests { path := simpleJoin(tmpDir, d.path) dest := simpleJoin(tmpDir, d.dest) + if filepath.IsAbs(d.dest) { + dest = d.dest + } if p, err := filepath.EvalSymlinks(path); err != nil { t.Errorf("EvalSymlinks(%q) error: %v", d.path, err) } else if filepath.Clean(p) != filepath.Clean(dest) { diff --git a/libgo/go/path/match.go b/libgo/go/path/match.go index ba7e4de321e..8154bf60251 100644 --- a/libgo/go/path/match.go +++ b/libgo/go/path/match.go @@ -10,6 +10,7 @@ import ( "unicode/utf8" ) +// ErrBadPattern indicates a globbing pattern was malformed. var ErrBadPattern = errors.New("syntax error in pattern") // Match returns true if name matches the shell file name pattern. @@ -31,7 +32,8 @@ var ErrBadPattern = errors.New("syntax error in pattern") // lo '-' hi matches character c for lo <= c <= hi // // Match requires pattern to match all of name, not just a substring. -// The only possible error return is when pattern is malformed. +// The only possible returned error is ErrBadPattern, when pattern +// is malformed. // func Match(pattern, name string) (matched bool, err error) { Pattern: diff --git a/libgo/go/path/path.go b/libgo/go/path/path.go index 20d89c9ff0c..13abed0b09d 100644 --- a/libgo/go/path/path.go +++ b/libgo/go/path/path.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package path implements utility routines for manipulating slash-separated -// filename paths. +// paths. package path import ( @@ -25,7 +25,7 @@ import ( // returns the string ".". // // See also Rob Pike, ``Lexical File Names in Plan 9 or -// Getting Dot-Dot right,'' +// Getting Dot-Dot Right,'' // http://plan9.bell-labs.com/sys/doc/lexnames.html func Clean(path string) string { if path == "" { @@ -100,17 +100,19 @@ func Clean(path string) string { return string(buf[0:w]) } -// Split splits path immediately following the final path separator, +// Split splits path immediately following the final slash. // separating it into a directory and file name component. -// If there is no separator in path, Split returns an empty dir and +// If there is no slash path, Split returns an empty dir and // file set to path. +// The returned values have the property that path = dir+file. func Split(path string) (dir, file string) { i := strings.LastIndex(path, "/") return path[:i+1], path[i+1:] } // Join joins any number of path elements into a single path, adding a -// separating slash if necessary. All empty strings are ignored. +// separating slash if necessary. The result is Cleaned; in particular, +// all empty strings are ignored. func Join(elem ...string) string { for i, e := range elem { if e != "" { @@ -161,11 +163,12 @@ func IsAbs(path string) bool { return len(path) > 0 && path[0] == '/' } -// Dir returns the all but the last element of path, typically the path's directory. -// Trailing path separators are removed before processing. +// Dir returns all but the last element of path, typically the path's directory. +// The path is Cleaned and trailing slashes are removed before processing. // If the path is empty, Dir returns ".". -// If the path consists entirely of separators, Dir returns a single separator. -// The returned path does not end in a separator unless it is the root directory. +// If the path consists entirely of slashes followed by non-slash bytes, Dir +// returns a single slash. In any other case, the returned path does not end in a +// slash. func Dir(path string) string { dir, _ := Split(path) dir = Clean(dir) diff --git a/libgo/go/runtime/debug.go b/libgo/go/runtime/debug.go index bd6dcc971a1..b802fc63f71 100644 --- a/libgo/go/runtime/debug.go +++ b/libgo/go/runtime/debug.go @@ -26,23 +26,11 @@ func GOMAXPROCS(n int) int // NumCPU returns the number of logical CPUs on the local machine. func NumCPU() int -// Cgocalls returns the number of cgo calls made by the current process. -func Cgocalls() int64 +// NumCgoCall returns the number of cgo calls made by the current process. +func NumCgoCall() int64 -// Goroutines returns the number of goroutines that currently exist. -func Goroutines() int32 - -// Alloc allocates a block of the given size. -// FOR TESTING AND DEBUGGING ONLY. -func Alloc(uintptr) *byte - -// Free frees the block starting at the given pointer. -// FOR TESTING AND DEBUGGING ONLY. -func Free(*byte) - -// Lookup returns the base and size of the block containing the given pointer. -// FOR TESTING AND DEBUGGING ONLY. -func Lookup(*byte) (*byte, uintptr) +// NumGoroutine returns the number of goroutines that currently exist. +func NumGoroutine() int // MemProfileRate controls the fraction of memory allocations // that are recorded and reported in the memory profile. @@ -101,15 +89,14 @@ func (r *MemProfileRecord) Stack() []uintptr { // of calling MemProfile directly. func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) -// A ThreadProfileRecord describes the execution stack that -// caused a new thread to be created. -type ThreadProfileRecord struct { +// A StackRecord describes a single execution stack. +type StackRecord struct { Stack0 [32]uintptr // stack trace for this record; ends at first 0 entry } // Stack returns the stack trace associated with the record, // a prefix of r.Stack0. -func (r *ThreadProfileRecord) Stack() []uintptr { +func (r *StackRecord) Stack() []uintptr { for i, v := range r.Stack0 { if v == 0 { return r.Stack0[0:i] @@ -118,13 +105,21 @@ func (r *ThreadProfileRecord) Stack() []uintptr { return r.Stack0[0:] } -// ThreadProfile returns n, the number of records in the current thread profile. -// If len(p) >= n, ThreadProfile copies the profile into p and returns n, true. -// If len(p) < n, ThreadProfile does not change p and returns n, false. +// ThreadCreateProfile returns n, the number of records in the thread creation profile. +// If len(p) >= n, ThreadCreateProfile copies the profile into p and returns n, true. +// If len(p) < n, ThreadCreateProfile does not change p and returns n, false. // // Most clients should use the runtime/pprof package instead -// of calling ThreadProfile directly. -func ThreadProfile(p []ThreadProfileRecord) (n int, ok bool) +// of calling ThreadCreateProfile directly. +func ThreadCreateProfile(p []StackRecord) (n int, ok bool) + +// GoroutineProfile returns n, the number of records in the active goroutine stack profile. +// If len(p) >= n, GoroutineProfile copies the profile into p and returns n, true. +// If len(p) < n, GoroutineProfile does not change p and returns n, false. +// +// Most clients should use the runtime/pprof package instead +// of calling GoroutineProfile directly. +func GoroutineProfile(p []StackRecord) (n int, ok bool) // CPUProfile returns the next chunk of binary CPU profiling stack trace data, // blocking until data is available. If profiling is turned off and all the profile @@ -142,3 +137,9 @@ func CPUProfile() []byte // the testing package's -test.cpuprofile flag instead of calling // SetCPUProfileRate directly. func SetCPUProfileRate(hz int) + +// Stack formats a stack trace of the calling goroutine into buf +// and returns the number of bytes written to buf. +// If all is true, Stack formats stack traces of all other goroutines +// into buf after the trace for the current goroutine. +func Stack(buf []byte, all bool) int diff --git a/libgo/go/runtime/extern.go b/libgo/go/runtime/extern.go index eafa2f19f19..5fbfe547e46 100644 --- a/libgo/go/runtime/extern.go +++ b/libgo/go/runtime/extern.go @@ -68,17 +68,6 @@ func funcline_go(*Func, uintptr) (string, int) // mid returns the current os thread (m) id. func mid() uint32 -// Semacquire waits until *s > 0 and then atomically decrements it. -// It is intended as a simple sleep primitive for use by the synchronization -// library and should not be used directly. -func Semacquire(s *uint32) - -// Semrelease atomically increments *s and notifies a waiting goroutine -// if one is blocked in Semacquire. -// It is intended as a simple wakeup primitive for use by the synchronization -// library and should not be used directly. -func Semrelease(s *uint32) - // SetFinalizer sets the finalizer associated with x to f. // When the garbage collector finds an unreachable block // with an associated finalizer, it clears the association and runs @@ -141,10 +130,10 @@ func Version() string { return theVersion } -// GOOS is the Go tree's operating system target: +// GOOS is the running program's operating system target: // one of darwin, freebsd, linux, and so on. const GOOS string = theGoos -// GOARCH is the Go tree's architecture target: +// GOARCH is the running program's architecture target: // 386, amd64, or arm. const GOARCH string = theGoarch diff --git a/gcc/testsuite/go.test/test/malloc1.go b/libgo/go/runtime/malloc1.go index 0f7f0b267a0..da92f4c2fbf 100644 --- a/gcc/testsuite/go.test/test/malloc1.go +++ b/libgo/go/runtime/malloc1.go @@ -1,9 +1,9 @@ -// $G $D/$F.go && $L $F.$A && ./$A.out - // Copyright 2009 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 ignore + // trivial malloc test package main diff --git a/gcc/testsuite/go.test/test/mallocrand.go b/libgo/go/runtime/mallocrand.go index 69d07cec5d2..f1bcb89cfa4 100644 --- a/gcc/testsuite/go.test/test/mallocrand.go +++ b/libgo/go/runtime/mallocrand.go @@ -1,9 +1,9 @@ -// $G $D/$F.go && $L $F.$A && ./$A.out - // Copyright 2009 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 ignore + // Random malloc test. package main diff --git a/gcc/testsuite/go.test/test/mallocrep.go b/libgo/go/runtime/mallocrep.go index 4188da9b833..03ee71edb42 100644 --- a/gcc/testsuite/go.test/test/mallocrep.go +++ b/libgo/go/runtime/mallocrep.go @@ -1,11 +1,11 @@ -// $G $D/$F.go && $L $F.$A && ./$A.out - // Copyright 2009 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. // Repeated malloc test. +// +build ignore + package main import ( diff --git a/gcc/testsuite/go.test/test/mallocrep1.go b/libgo/go/runtime/mallocrep1.go index f9d7286efd9..41c104c0ba7 100644 --- a/gcc/testsuite/go.test/test/mallocrep1.go +++ b/libgo/go/runtime/mallocrep1.go @@ -1,9 +1,9 @@ -// $G $D/$F.go && $L $F.$A && ./$A.out - // Copyright 2009 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 ignore + // Repeated malloc test. package main diff --git a/libgo/go/runtime/mem.go b/libgo/go/runtime/mem.go index 1301674c027..95e8aa7a533 100644 --- a/libgo/go/runtime/mem.go +++ b/libgo/go/runtime/mem.go @@ -17,11 +17,12 @@ type MemStats struct { Frees uint64 // number of frees // Main allocation heap statistics. - HeapAlloc uint64 // bytes allocated and still in use - HeapSys uint64 // bytes obtained from system - HeapIdle uint64 // bytes in idle spans - HeapInuse uint64 // bytes in non-idle span - HeapObjects uint64 // total number of allocated objects + HeapAlloc uint64 // bytes allocated and still in use + HeapSys uint64 // bytes obtained from system + HeapIdle uint64 // bytes in idle spans + HeapInuse uint64 // bytes in non-idle span + HeapReleased uint64 // bytes released to the OS + HeapObjects uint64 // total number of allocated objects // Low-level fixed-size structure allocator statistics. // Inuse is bytes used now. @@ -35,7 +36,8 @@ type MemStats struct { BuckHashSys uint64 // profiling bucket hash table // Garbage collector statistics. - NextGC uint64 + NextGC uint64 // next run in HeapAlloc time (bytes) + LastGC uint64 // last run in absolute time (ns) PauseTotalNs uint64 PauseNs [256]uint64 // most recent GC pause times NumGC uint32 diff --git a/libgo/go/runtime/pprof/pprof.go b/libgo/go/runtime/pprof/pprof.go index 42f04f320a7..099bb6a92f9 100644 --- a/libgo/go/runtime/pprof/pprof.go +++ b/libgo/go/runtime/pprof/pprof.go @@ -10,19 +10,354 @@ package pprof import ( "bufio" + "bytes" "fmt" "io" "runtime" + "sort" + "strings" "sync" + "text/tabwriter" ) // BUG(rsc): CPU profiling is broken on OS X, due to an Apple kernel bug. // For details, see http://code.google.com/p/go/source/detail?r=35b716c94225. -// WriteHeapProfile writes a pprof-formatted heap profile to w. -// If a write to w returns an error, WriteHeapProfile returns that error. -// Otherwise, WriteHeapProfile returns nil. +// A Profile is a collection of stack traces showing the call sequences +// that led to instances of a particular event, such as allocation. +// Packages can create and maintain their own profiles; the most common +// use is for tracking resources that must be explicitly closed, such as files +// or network connections. +// +// A Profile's methods can be called from multiple goroutines simultaneously. +// +// Each Profile has a unique name. A few profiles are predefined: +// +// goroutine - stack traces of all current goroutines +// heap - a sampling of all heap allocations +// threadcreate - stack traces that led to the creation of new OS threads +// +// These predefine profiles maintain themselves and panic on an explicit +// Add or Remove method call. +// +// The CPU profile is not available as a Profile. It has a special API, +// the StartCPUProfile and StopCPUProfile functions, because it streams +// output to a writer during profiling. +// +type Profile struct { + name string + mu sync.Mutex + m map[interface{}][]uintptr + count func() int + write func(io.Writer, int) error +} + +// profiles records all registered profiles. +var profiles struct { + mu sync.Mutex + m map[string]*Profile +} + +var goroutineProfile = &Profile{ + name: "goroutine", + count: countGoroutine, + write: writeGoroutine, +} + +var threadcreateProfile = &Profile{ + name: "threadcreate", + count: countThreadCreate, + write: writeThreadCreate, +} + +var heapProfile = &Profile{ + name: "heap", + count: countHeap, + write: writeHeap, +} + +func lockProfiles() { + profiles.mu.Lock() + if profiles.m == nil { + // Initial built-in profiles. + profiles.m = map[string]*Profile{ + "goroutine": goroutineProfile, + "threadcreate": threadcreateProfile, + "heap": heapProfile, + } + } +} + +func unlockProfiles() { + profiles.mu.Unlock() +} + +// NewProfile creates a new profile with the given name. +// If a profile with that name already exists, NewProfile panics. +// The convention is to use a 'import/path.' prefix to create +// separate name spaces for each package. +func NewProfile(name string) *Profile { + lockProfiles() + defer unlockProfiles() + if name == "" { + panic("pprof: NewProfile with empty name") + } + if profiles.m[name] != nil { + panic("pprof: NewProfile name already in use: " + name) + } + p := &Profile{ + name: name, + m: map[interface{}][]uintptr{}, + } + profiles.m[name] = p + return p +} + +// Lookup returns the profile with the given name, or nil if no such profile exists. +func Lookup(name string) *Profile { + lockProfiles() + defer unlockProfiles() + return profiles.m[name] +} + +// Profiles returns a slice of all the known profiles, sorted by name. +func Profiles() []*Profile { + lockProfiles() + defer unlockProfiles() + + var all []*Profile + for _, p := range profiles.m { + all = append(all, p) + } + + sort.Sort(byName(all)) + return all +} + +type byName []*Profile + +func (x byName) Len() int { return len(x) } +func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byName) Less(i, j int) bool { return x[i].name < x[j].name } + +// Name returns this profile's name, which can be passed to Lookup to reobtain the profile. +func (p *Profile) Name() string { + return p.name +} + +// Count returns the number of execution stacks currently in the profile. +func (p *Profile) Count() int { + p.mu.Lock() + defer p.mu.Unlock() + if p.count != nil { + return p.count() + } + return len(p.m) +} + +// Add adds the current execution stack to the profile, associated with value. +// Add stores value in an internal map, so value must be suitable for use as +// a map key and will not be garbage collected until the corresponding +// call to Remove. Add panics if the profile already contains a stack for value. +// +// The skip parameter has the same meaning as runtime.Caller's skip +// and controls where the stack trace begins. Passing skip=0 begins the +// trace in the function calling Add. For example, given this +// execution stack: +// +// Add +// called from rpc.NewClient +// called from mypkg.Run +// called from main.main +// +// Passing skip=0 begins the stack trace at the call to Add inside rpc.NewClient. +// Passing skip=1 begins the stack trace at the call to NewClient inside mypkg.Run. +// +func (p *Profile) Add(value interface{}, skip int) { + if p.name == "" { + panic("pprof: use of uninitialized Profile") + } + if p.write != nil { + panic("pprof: Add called on built-in Profile " + p.name) + } + + stk := make([]uintptr, 32) + n := runtime.Callers(skip+1, stk[:]) + + p.mu.Lock() + defer p.mu.Unlock() + if p.m[value] != nil { + panic("pprof: Profile.Add of duplicate value") + } + p.m[value] = stk[:n] +} + +// Remove removes the execution stack associated with value from the profile. +// It is a no-op if the value is not in the profile. +func (p *Profile) Remove(value interface{}) { + p.mu.Lock() + defer p.mu.Unlock() + delete(p.m, value) +} + +// WriteTo writes a pprof-formatted snapshot of the profile to w. +// If a write to w returns an error, WriteTo returns that error. +// Otherwise, WriteTo returns nil. +// +// The debug parameter enables additional output. +// Passing debug=0 prints only the hexadecimal addresses that pprof needs. +// Passing debug=1 adds comments translating addresses to function names +// and line numbers, so that a programmer can read the profile without tools. +// +// The predefined profiles may assign meaning to other debug values; +// for example, when printing the "goroutine" profile, debug=2 means to +// print the goroutine stacks in the same form that a Go program uses +// when dying due to an unrecovered panic. +func (p *Profile) WriteTo(w io.Writer, debug int) error { + if p.name == "" { + panic("pprof: use of zero Profile") + } + if p.write != nil { + return p.write(w, debug) + } + + // Obtain consistent snapshot under lock; then process without lock. + var all [][]uintptr + p.mu.Lock() + for _, stk := range p.m { + all = append(all, stk) + } + p.mu.Unlock() + + // Map order is non-deterministic; make output deterministic. + sort.Sort(stackProfile(all)) + + return printCountProfile(w, debug, p.name, stackProfile(all)) +} + +type stackProfile [][]uintptr + +func (x stackProfile) Len() int { return len(x) } +func (x stackProfile) Stack(i int) []uintptr { return x[i] } +func (x stackProfile) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x stackProfile) Less(i, j int) bool { + t, u := x[i], x[j] + for k := 0; k < len(t) && k < len(u); k++ { + if t[k] != u[k] { + return t[k] < u[k] + } + } + return len(t) < len(u) +} + +// A countProfile is a set of stack traces to be printed as counts +// grouped by stack trace. There are multiple implementations: +// all that matters is that we can find out how many traces there are +// and obtain each trace in turn. +type countProfile interface { + Len() int + Stack(i int) []uintptr +} + +// printCountProfile prints a countProfile at the specified debug level. +func printCountProfile(w io.Writer, debug int, name string, p countProfile) error { + b := bufio.NewWriter(w) + var tw *tabwriter.Writer + w = b + if debug > 0 { + tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0) + w = tw + } + + fmt.Fprintf(w, "%s profile: total %d\n", name, p.Len()) + + // Build count of each stack. + var buf bytes.Buffer + key := func(stk []uintptr) string { + buf.Reset() + fmt.Fprintf(&buf, "@") + for _, pc := range stk { + fmt.Fprintf(&buf, " %#x", pc) + } + return buf.String() + } + m := map[string]int{} + n := p.Len() + for i := 0; i < n; i++ { + m[key(p.Stack(i))]++ + } + + // Print stacks, listing count on first occurrence of a unique stack. + for i := 0; i < n; i++ { + stk := p.Stack(i) + s := key(stk) + if count := m[s]; count != 0 { + fmt.Fprintf(w, "%d %s\n", count, s) + if debug > 0 { + printStackRecord(w, stk, false) + } + delete(m, s) + } + } + + if tw != nil { + tw.Flush() + } + return b.Flush() +} + +// printStackRecord prints the function + source line information +// for a single stack trace. +func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) { + show := allFrames + for _, pc := range stk { + f := runtime.FuncForPC(pc) + if f == nil { + show = true + fmt.Fprintf(w, "#\t%#x\n", pc) + } else { + file, line := f.FileLine(pc) + name := f.Name() + // Hide runtime.goexit and any runtime functions at the beginning. + // This is useful mainly for allocation traces. + if name == "runtime.goexit" || !show && strings.HasPrefix(name, "runtime.") { + continue + } + show = true + fmt.Fprintf(w, "#\t%#x\t%s+%#x\t%s:%d\n", pc, f.Name(), pc-f.Entry(), file, line) + } + } + if !show { + // We didn't print anything; do it again, + // and this time include runtime functions. + printStackRecord(w, stk, true) + return + } + fmt.Fprintf(w, "\n") +} + +// Interface to system profiles. + +type byInUseBytes []runtime.MemProfileRecord + +func (x byInUseBytes) Len() int { return len(x) } +func (x byInUseBytes) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byInUseBytes) Less(i, j int) bool { return x[i].InUseBytes() > x[j].InUseBytes() } + +// WriteHeapProfile is shorthand for Lookup("heap").WriteTo(w, 0). +// It is preserved for backwards compatibility. func WriteHeapProfile(w io.Writer) error { + return writeHeap(w, 0) +} + +// countHeap returns the number of records in the heap profile. +func countHeap() int { + n, _ := runtime.MemProfile(nil, false) + return n +} + +// writeHeapProfile writes the current runtime heap profile to w. +func writeHeap(w io.Writer, debug int) error { // Find out how many records there are (MemProfile(nil, false)), // allocate that many records, and get the data. // There's a race—more records might be added between @@ -44,6 +379,16 @@ func WriteHeapProfile(w io.Writer) error { // Profile grew; try again. } + sort.Sort(byInUseBytes(p)) + + b := bufio.NewWriter(w) + var tw *tabwriter.Writer + w = b + if debug > 0 { + tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0) + w = tw + } + var total runtime.MemProfileRecord for i := range p { r := &p[i] @@ -56,78 +401,120 @@ func WriteHeapProfile(w io.Writer) error { // Technically the rate is MemProfileRate not 2*MemProfileRate, // but early versions of the C++ heap profiler reported 2*MemProfileRate, // so that's what pprof has come to expect. - b := bufio.NewWriter(w) - fmt.Fprintf(b, "heap profile: %d: %d [%d: %d] @ heap/%d\n", + fmt.Fprintf(w, "heap profile: %d: %d [%d: %d] @ heap/%d\n", total.InUseObjects(), total.InUseBytes(), total.AllocObjects, total.AllocBytes, 2*runtime.MemProfileRate) for i := range p { r := &p[i] - fmt.Fprintf(b, "%d: %d [%d: %d] @", + fmt.Fprintf(w, "%d: %d [%d: %d] @", r.InUseObjects(), r.InUseBytes(), r.AllocObjects, r.AllocBytes) for _, pc := range r.Stack() { - fmt.Fprintf(b, " %#x", pc) + fmt.Fprintf(w, " %#x", pc) + } + fmt.Fprintf(w, "\n") + if debug > 0 { + printStackRecord(w, r.Stack(), false) } - fmt.Fprintf(b, "\n") } // Print memstats information too. - // Pprof will ignore, but useful for people. - s := new(runtime.MemStats) - runtime.ReadMemStats(s) - fmt.Fprintf(b, "\n# runtime.MemStats\n") - fmt.Fprintf(b, "# Alloc = %d\n", s.Alloc) - fmt.Fprintf(b, "# TotalAlloc = %d\n", s.TotalAlloc) - fmt.Fprintf(b, "# Sys = %d\n", s.Sys) - fmt.Fprintf(b, "# Lookups = %d\n", s.Lookups) - fmt.Fprintf(b, "# Mallocs = %d\n", s.Mallocs) - - fmt.Fprintf(b, "# HeapAlloc = %d\n", s.HeapAlloc) - fmt.Fprintf(b, "# HeapSys = %d\n", s.HeapSys) - fmt.Fprintf(b, "# HeapIdle = %d\n", s.HeapIdle) - fmt.Fprintf(b, "# HeapInuse = %d\n", s.HeapInuse) - - fmt.Fprintf(b, "# Stack = %d / %d\n", s.StackInuse, s.StackSys) - fmt.Fprintf(b, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys) - fmt.Fprintf(b, "# MCache = %d / %d\n", s.MCacheInuse, s.MCacheSys) - fmt.Fprintf(b, "# BuckHashSys = %d\n", s.BuckHashSys) - - fmt.Fprintf(b, "# NextGC = %d\n", s.NextGC) - fmt.Fprintf(b, "# PauseNs = %d\n", s.PauseNs) - fmt.Fprintf(b, "# NumGC = %d\n", s.NumGC) - fmt.Fprintf(b, "# EnableGC = %v\n", s.EnableGC) - fmt.Fprintf(b, "# DebugGC = %v\n", s.DebugGC) - - fmt.Fprintf(b, "# BySize = Size * (Active = Mallocs - Frees)\n") - fmt.Fprintf(b, "# (Excluding large blocks.)\n") - for _, t := range s.BySize { - if t.Mallocs > 0 { - fmt.Fprintf(b, "# %d * (%d = %d - %d)\n", t.Size, t.Mallocs-t.Frees, t.Mallocs, t.Frees) - } + // Pprof will ignore, but useful for people + if debug > 0 { + s := new(runtime.MemStats) + runtime.ReadMemStats(s) + fmt.Fprintf(w, "\n# runtime.MemStats\n") + fmt.Fprintf(w, "# Alloc = %d\n", s.Alloc) + fmt.Fprintf(w, "# TotalAlloc = %d\n", s.TotalAlloc) + fmt.Fprintf(w, "# Sys = %d\n", s.Sys) + fmt.Fprintf(w, "# Lookups = %d\n", s.Lookups) + fmt.Fprintf(w, "# Mallocs = %d\n", s.Mallocs) + + fmt.Fprintf(w, "# HeapAlloc = %d\n", s.HeapAlloc) + fmt.Fprintf(w, "# HeapSys = %d\n", s.HeapSys) + fmt.Fprintf(w, "# HeapIdle = %d\n", s.HeapIdle) + fmt.Fprintf(w, "# HeapInuse = %d\n", s.HeapInuse) + + fmt.Fprintf(w, "# Stack = %d / %d\n", s.StackInuse, s.StackSys) + fmt.Fprintf(w, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys) + fmt.Fprintf(w, "# MCache = %d / %d\n", s.MCacheInuse, s.MCacheSys) + fmt.Fprintf(w, "# BuckHashSys = %d\n", s.BuckHashSys) + + fmt.Fprintf(w, "# NextGC = %d\n", s.NextGC) + fmt.Fprintf(w, "# PauseNs = %d\n", s.PauseNs) + fmt.Fprintf(w, "# NumGC = %d\n", s.NumGC) + fmt.Fprintf(w, "# EnableGC = %v\n", s.EnableGC) + fmt.Fprintf(w, "# DebugGC = %v\n", s.DebugGC) + } + + if tw != nil { + tw.Flush() } return b.Flush() } -// WriteThreadProfile writes a pprof-formatted thread creation profile to w. -// If a write to w returns an error, WriteThreadProfile returns that error. -// Otherwise, WriteThreadProfile returns nil. -func WriteThreadProfile(w io.Writer) error { - // Find out how many records there are (ThreadProfile(nil)), +// countThreadCreate returns the size of the current ThreadCreateProfile. +func countThreadCreate() int { + n, _ := runtime.ThreadCreateProfile(nil) + return n +} + +// writeThreadCreate writes the current runtime ThreadCreateProfile to w. +func writeThreadCreate(w io.Writer, debug int) error { + return writeRuntimeProfile(w, debug, "threadcreate", runtime.ThreadCreateProfile) +} + +// countGoroutine returns the number of goroutines. +func countGoroutine() int { + return runtime.NumGoroutine() +} + +// writeGoroutine writes the current runtime GoroutineProfile to w. +func writeGoroutine(w io.Writer, debug int) error { + if debug >= 2 { + return writeGoroutineStacks(w) + } + return writeRuntimeProfile(w, debug, "goroutine", runtime.GoroutineProfile) +} + +func writeGoroutineStacks(w io.Writer) error { + // We don't know how big the buffer needs to be to collect + // all the goroutines. Start with 1 MB and try a few times, doubling each time. + // Give up and use a truncated trace if 64 MB is not enough. + buf := make([]byte, 1<<20) + for i := 0; ; i++ { + n := runtime.Stack(buf, true) + if n < len(buf) { + buf = buf[:n] + break + } + if len(buf) >= 64<<20 { + // Filled 64 MB - stop there. + break + } + buf = make([]byte, 2*len(buf)) + } + _, err := w.Write(buf) + return err +} + +func writeRuntimeProfile(w io.Writer, debug int, name string, fetch func([]runtime.StackRecord) (int, bool)) error { + // Find out how many records there are (fetch(nil)), // allocate that many records, and get the data. - // There's a race—more records (threads) might be added between + // There's a race—more records might be added between // the two calls—so allocate a few extra records for safety // and also try again if we're very unlucky. // The loop should only execute one iteration in the common case. - var p []runtime.ThreadProfileRecord - n, ok := runtime.ThreadProfile(nil) + var p []runtime.StackRecord + n, ok := fetch(nil) for { // Allocate room for a slightly bigger profile, // in case a few more entries have been added // since the call to ThreadProfile. - p = make([]runtime.ThreadProfileRecord, n+10) - n, ok = runtime.ThreadProfile(p) + p = make([]runtime.StackRecord, n+10) + n, ok = fetch(p) if ok { p = p[0:n] break @@ -135,19 +522,14 @@ func WriteThreadProfile(w io.Writer) error { // Profile grew; try again. } - b := bufio.NewWriter(w) - fmt.Fprintf(b, "thread creation profile: %d threads\n", n) - for i := range p { - r := &p[i] - fmt.Fprintf(b, "@") - for _, pc := range r.Stack() { - fmt.Fprintf(b, " %#x", pc) - } - fmt.Fprintf(b, "\n") - } - return b.Flush() + return printCountProfile(w, debug, name, runtimeProfile(p)) } +type runtimeProfile []runtime.StackRecord + +func (p runtimeProfile) Len() int { return len(p) } +func (p runtimeProfile) Stack(i int) []uintptr { return p[i].Stack() } + var cpu struct { sync.Mutex profiling bool diff --git a/libgo/go/sort/example_interface_test.go b/libgo/go/sort/example_interface_test.go new file mode 100644 index 00000000000..4c88821be7c --- /dev/null +++ b/libgo/go/sort/example_interface_test.go @@ -0,0 +1,77 @@ +// 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 sort_test + +import ( + "fmt" + "sort" +) + +type Grams int + +func (g Grams) String() string { return fmt.Sprintf("%dg", int(g)) } + +type Organ struct { + Name string + Weight Grams +} + +type Organs []*Organ + +func (s Organs) Len() int { return len(s) } +func (s Organs) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// ByName implements sort.Interface by providing Less and using the Len and +// Swap methods of the embedded Organs value. +type ByName struct{ Organs } + +func (s ByName) Less(i, j int) bool { return s.Organs[i].Name < s.Organs[j].Name } + +// ByWeight implements sort.Interface by providing Less and using the Len and +// Swap methods of the embedded Organs value. +type ByWeight struct{ Organs } + +func (s ByWeight) Less(i, j int) bool { return s.Organs[i].Weight < s.Organs[j].Weight } + +func ExampleInterface() { + s := []*Organ{ + {"brain", 1340}, + {"heart", 290}, + {"liver", 1494}, + {"pancreas", 131}, + {"prostate", 62}, + {"spleen", 162}, + } + + sort.Sort(ByWeight{s}) + fmt.Println("Organs by weight:") + printOrgans(s) + + sort.Sort(ByName{s}) + fmt.Println("Organs by name:") + printOrgans(s) + + // Output: + // Organs by weight: + // prostate (62g) + // pancreas (131g) + // spleen (162g) + // heart (290g) + // brain (1340g) + // liver (1494g) + // Organs by name: + // brain (1340g) + // heart (290g) + // liver (1494g) + // pancreas (131g) + // prostate (62g) + // spleen (162g) +} + +func printOrgans(s []*Organ) { + for _, o := range s { + fmt.Printf("%-8s (%v)\n", o.Name, o.Weight) + } +} diff --git a/libgo/go/sort/example_reverse_test.go b/libgo/go/sort/example_reverse_test.go new file mode 100644 index 00000000000..7c7f05bf3a2 --- /dev/null +++ b/libgo/go/sort/example_reverse_test.go @@ -0,0 +1,30 @@ +// 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 sort_test + +import ( + "fmt" + "sort" +) + +// Reverse embeds a sort.Interface value and implements a reverse sort over +// that value. +type Reverse struct { + // This embedded Interface permits Reverse to use the methods of + // another Interface implementation. + sort.Interface +} + +// Less returns the opposite of the embedded implementation's Less method. +func (r Reverse) Less(i, j int) bool { + return r.Interface.Less(j, i) +} + +func ExampleInterface_reverse() { + s := []int{5, 2, 6, 3, 1, 4} // unsorted + sort.Sort(Reverse{sort.IntSlice(s)}) + fmt.Println(s) + // Output: [6 5 4 3 2 1] +} diff --git a/libgo/go/sort/example_test.go b/libgo/go/sort/example_test.go index 2224db7e13c..f57d02546f7 100644 --- a/libgo/go/sort/example_test.go +++ b/libgo/go/sort/example_test.go @@ -9,9 +9,9 @@ import ( "sort" ) -// [1 2 3 4 5 6] func ExampleInts() { s := []int{5, 2, 6, 3, 1, 4} // unsorted sort.Ints(s) fmt.Println(s) + // Output: [1 2 3 4 5 6] } diff --git a/libgo/go/strconv/atof.go b/libgo/go/strconv/atof.go index cd3031b0e61..d99117bed1d 100644 --- a/libgo/go/strconv/atof.go +++ b/libgo/go/strconv/atof.go @@ -13,6 +13,7 @@ package strconv // 3) Multiply by 2^precision and round to get mantissa. import "math" +import "runtime" var optimize = true // can change for testing @@ -300,6 +301,11 @@ func (d *decimal) atof64() (f float64, ok bool) { if d.nd > 15 { return } + // gccgo gets this wrong on 32-bit i386 when not using -msse. + // See TestRoundTrip in atof_test.go for a test case. + if runtime.GOARCH == "386" { + return + } switch { case d.dp == d.nd: // int f := d.atof64int() diff --git a/libgo/go/strconv/itoa_test.go b/libgo/go/strconv/itoa_test.go index bac23e6ea67..63d2fa44e02 100644 --- a/libgo/go/strconv/itoa_test.go +++ b/libgo/go/strconv/itoa_test.go @@ -127,6 +127,7 @@ func TestUitoa(t *testing.T) { } func numAllocations(f func()) int { + runtime.GC() memstats := new(runtime.MemStats) runtime.ReadMemStats(memstats) n0 := memstats.Mallocs diff --git a/libgo/go/strings/example_test.go b/libgo/go/strings/example_test.go index 5ef0b93d15e..0b583411331 100644 --- a/libgo/go/strings/example_test.go +++ b/libgo/go/strings/example_test.go @@ -9,134 +9,142 @@ import ( "strings" ) -// Fields are: ["foo" "bar" "baz"] func ExampleFields() { fmt.Printf("Fields are: %q", strings.Fields(" foo bar baz ")) + // Output: Fields are: ["foo" "bar" "baz"] } -// true -// false -// true -// true func ExampleContains() { fmt.Println(strings.Contains("seafood", "foo")) fmt.Println(strings.Contains("seafood", "bar")) fmt.Println(strings.Contains("seafood", "")) fmt.Println(strings.Contains("", "")) + // Output: + // true + // false + // true + // true } -// false -// true -// false -// false func ExampleContainsAny() { fmt.Println(strings.ContainsAny("team", "i")) fmt.Println(strings.ContainsAny("failure", "u & i")) fmt.Println(strings.ContainsAny("foo", "")) fmt.Println(strings.ContainsAny("", "")) - + // Output: + // false + // true + // false + // false } -// 3 -// 5 func ExampleCount() { fmt.Println(strings.Count("cheese", "e")) fmt.Println(strings.Count("five", "")) // before & after each rune + // Output: + // 3 + // 5 } -// true func ExampleEqualFold() { fmt.Println(strings.EqualFold("Go", "go")) + // Output: true } -// 4 -// -1 func ExampleIndex() { fmt.Println(strings.Index("chicken", "ken")) fmt.Println(strings.Index("chicken", "dmr")) + // Output: + // 4 + // -1 } -// 4 -// -1 func ExampleRune() { fmt.Println(strings.IndexRune("chicken", 'k')) fmt.Println(strings.IndexRune("chicken", 'd')) + // Output: + // 4 + // -1 } -// 0 -// 3 -// -1 func ExampleLastIndex() { fmt.Println(strings.Index("go gopher", "go")) fmt.Println(strings.LastIndex("go gopher", "go")) fmt.Println(strings.LastIndex("go gopher", "rodent")) + // Output: + // 0 + // 3 + // -1 } -// foo, bar, baz func ExampleJoin() { s := []string{"foo", "bar", "baz"} fmt.Println(strings.Join(s, ", ")) + // Output: foo, bar, baz } -// banana func ExampleRepeat() { fmt.Println("ba" + strings.Repeat("na", 2)) + // Output: banana } -// oinky oinky oink -// moo moo moo func ExampleReplace() { fmt.Println(strings.Replace("oink oink oink", "k", "ky", 2)) fmt.Println(strings.Replace("oink oink oink", "oink", "moo", -1)) + // Output: + // oinky oinky oink + // moo moo moo } -// ["a" "b" "c"] -// ["" "man " "plan " "canal panama"] -// [" " "x" "y" "z" " "] -// [""] func ExampleSplit() { fmt.Printf("%q\n", strings.Split("a,b,c", ",")) fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a ")) fmt.Printf("%q\n", strings.Split(" xyz ", "")) fmt.Printf("%q\n", strings.Split("", "Bernardo O'Higgins")) + // Output: + // ["a" "b" "c"] + // ["" "man " "plan " "canal panama"] + // [" " "x" "y" "z" " "] + // [""] } -// ["a" "b,c"] -// [] (nil = true) func ExampleSplitN() { fmt.Printf("%q\n", strings.SplitN("a,b,c", ",", 2)) z := strings.SplitN("a,b,c", ",", 0) fmt.Printf("%q (nil = %v)\n", z, z == nil) + // Output: + // ["a" "b,c"] + // [] (nil = true) } -// ["a," "b," "c"] func ExampleSplitAfter() { fmt.Printf("%q\n", strings.SplitAfter("a,b,c", ",")) + // Output: ["a," "b," "c"] } -// ["a," "b,c"] func ExampleSplitAfterN() { fmt.Printf("%q\n", strings.SplitAfterN("a,b,c", ",", 2)) + // Output: ["a," "b,c"] } -// Her Royal Highness func ExampleTitle() { fmt.Println(strings.Title("her royal highness")) + // Output: Her Royal Highness } -// LOUD NOISES -// ХЛЕБ func ExampleToTitle() { fmt.Println(strings.ToTitle("loud noises")) fmt.Println(strings.ToTitle("хлеб")) + // Output: + // LOUD NOISES + // ХЛЕБ } -// [Achtung] func ExampleTrim() { - fmt.Printf("[%s]", strings.Trim(" !!! Achtung !!! ", "! ")) + fmt.Printf("[%q]", strings.Trim(" !!! Achtung !!! ", "! ")) + // Output: ["Achtung"] } -// 'Gjnf oevyyvt naq gur fyvgul tbcure... func ExampleMap() { rot13 := func(r rune) rune { switch { @@ -148,25 +156,26 @@ func ExampleMap() { return r } fmt.Println(strings.Map(rot13, "'Twas brillig and the slithy gopher...")) + // Output: 'Gjnf oevyyvt naq gur fyvgul tbcure... } -// a lone gopher func ExampleTrimSpace() { fmt.Println(strings.TrimSpace(" \t\n a lone gopher \n\t\r\n")) + // Output: a lone gopher } -// This is <b>HTML</b>! func ExampleNewReplacer() { r := strings.NewReplacer("<", "<", ">", ">") fmt.Println(r.Replace("This is <b>HTML</b>!")) + // Output: This is <b>HTML</b>! } -// GOPHER func ExampleToUpper() { fmt.Println(strings.ToUpper("Gopher")) + // Output: GOPHER } -// gopher func ExampleToLower() { fmt.Println(strings.ToLower("Gopher")) + // Output: gopher } diff --git a/libgo/go/sync/cond.go b/libgo/go/sync/cond.go index 75494b53536..1fc3deaf1e0 100644 --- a/libgo/go/sync/cond.go +++ b/libgo/go/sync/cond.go @@ -4,8 +4,6 @@ package sync -import "runtime" - // Cond implements a condition variable, a rendezvous point // for goroutines waiting for or announcing the occurrence // of an event. @@ -43,9 +41,10 @@ func NewCond(l Locker) *Cond { // Wait atomically unlocks c.L and suspends execution // of the calling goroutine. After later resuming execution, -// Wait locks c.L before returning. +// Wait locks c.L before returning. Unlike in other systems, +// Wait cannot return unless awoken by Broadcast or Signal. // -// Because L is not locked when Wait first resumes, the caller +// Because c.L is not locked when Wait first resumes, the caller // typically cannot assume that the condition is true when // Wait returns. Instead, the caller should Wait in a loop: // @@ -65,7 +64,7 @@ func (c *Cond) Wait() { c.newWaiters++ c.m.Unlock() c.L.Unlock() - runtime.Semacquire(s) + runtime_Semacquire(s) c.L.Lock() } @@ -84,7 +83,7 @@ func (c *Cond) Signal() { } if c.oldWaiters > 0 { c.oldWaiters-- - runtime.Semrelease(c.oldSema) + runtime_Semrelease(c.oldSema) } c.m.Unlock() } @@ -98,13 +97,13 @@ func (c *Cond) Broadcast() { // Wake both generations. if c.oldWaiters > 0 { for i := 0; i < c.oldWaiters; i++ { - runtime.Semrelease(c.oldSema) + runtime_Semrelease(c.oldSema) } c.oldWaiters = 0 } if c.newWaiters > 0 { for i := 0; i < c.newWaiters; i++ { - runtime.Semrelease(c.newSema) + runtime_Semrelease(c.newSema) } c.newWaiters = 0 c.newSema = nil diff --git a/libgo/go/sync/example_test.go b/libgo/go/sync/example_test.go new file mode 100644 index 00000000000..1424b1e79e6 --- /dev/null +++ b/libgo/go/sync/example_test.go @@ -0,0 +1,34 @@ +// 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 sync_test + +import ( + "net/http" + "sync" +) + +// This example fetches several URLs concurrently, +// using a WaitGroup to block until all the fetches are complete. +func ExampleWaitGroup() { + var wg sync.WaitGroup + var urls = []string{ + "http://www.golang.org/", + "http://www.google.com/", + "http://www.somestupidname.com/", + } + for _, url := range urls { + // Increment the WaitGroup counter. + wg.Add(1) + // Launch a goroutine to fetch the URL. + go func(url string) { + // Fetch the URL. + http.Get(url) + // Decrement the counter. + wg.Done() + }(url) + } + // Wait for all HTTP fetches to complete. + wg.Wait() +} diff --git a/libgo/go/sync/export_test.go b/libgo/go/sync/export_test.go new file mode 100644 index 00000000000..fa5983a2d1e --- /dev/null +++ b/libgo/go/sync/export_test.go @@ -0,0 +1,9 @@ +// 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 sync + +// Export for testing. +var Runtime_Semacquire = runtime_Semacquire +var Runtime_Semrelease = runtime_Semrelease diff --git a/libgo/go/sync/mutex.go b/libgo/go/sync/mutex.go index 4fc02743c6e..9494cc3f826 100644 --- a/libgo/go/sync/mutex.go +++ b/libgo/go/sync/mutex.go @@ -10,10 +10,7 @@ // Values containing the types defined in this package should not be copied. package sync -import ( - "runtime" - "sync/atomic" -) +import "sync/atomic" // A Mutex is a mutual exclusion lock. // Mutexes can be created as part of other structures; @@ -60,7 +57,7 @@ func (m *Mutex) Lock() { if old&mutexLocked == 0 { break } - runtime.Semacquire(&m.sema) + runtime_Semacquire(&m.sema) awoke = true } } @@ -89,7 +86,7 @@ func (m *Mutex) Unlock() { // Grab the right to wake someone. new = (old - 1<<mutexWaiterShift) | mutexWoken if atomic.CompareAndSwapInt32(&m.state, old, new) { - runtime.Semrelease(&m.sema) + runtime_Semrelease(&m.sema) return } old = m.state diff --git a/libgo/go/sync/mutex_test.go b/libgo/go/sync/mutex_test.go index a514b4ad4c0..bf78c6f609c 100644 --- a/libgo/go/sync/mutex_test.go +++ b/libgo/go/sync/mutex_test.go @@ -15,8 +15,8 @@ import ( func HammerSemaphore(s *uint32, loops int, cdone chan bool) { for i := 0; i < loops; i++ { - runtime.Semacquire(s) - runtime.Semrelease(s) + Runtime_Semacquire(s) + Runtime_Semrelease(s) } cdone <- true } diff --git a/libgo/go/sync/runtime.go b/libgo/go/sync/runtime.go new file mode 100644 index 00000000000..e99599c11ae --- /dev/null +++ b/libgo/go/sync/runtime.go @@ -0,0 +1,18 @@ +// 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 sync + +// defined in package runtime + +// Semacquire waits until *s > 0 and then atomically decrements it. +// It is intended as a simple sleep primitive for use by the synchronization +// library and should not be used directly. +func runtime_Semacquire(s *uint32) + +// Semrelease atomically increments *s and notifies a waiting goroutine +// if one is blocked in Semacquire. +// It is intended as a simple wakeup primitive for use by the synchronization +// library and should not be used directly. +func runtime_Semrelease(s *uint32) diff --git a/libgo/go/runtime/sema_test.go b/libgo/go/sync/runtime_sema_test.go index d95bb1ec58f..57a8dbee783 100644 --- a/libgo/go/runtime/sema_test.go +++ b/libgo/go/sync/runtime_sema_test.go @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package runtime_test +package sync_test import ( "runtime" + . "sync" "sync/atomic" "testing" ) @@ -25,8 +26,8 @@ func BenchmarkSemaUncontended(b *testing.B) { for atomic.AddInt32(&N, -1) >= 0 { runtime.Gosched() for g := 0; g < CallsPerSched; g++ { - runtime.Semrelease(&sem.sem) - runtime.Semacquire(&sem.sem) + Runtime_Semrelease(&sem.sem) + Runtime_Semacquire(&sem.sem) } } c <- true @@ -48,7 +49,7 @@ func benchmarkSema(b *testing.B, block, work bool) { if block { for p := 0; p < procs/2; p++ { go func() { - runtime.Semacquire(&sem) + Runtime_Semacquire(&sem) c2 <- true }() } @@ -59,18 +60,18 @@ func benchmarkSema(b *testing.B, block, work bool) { for atomic.AddInt32(&N, -1) >= 0 { runtime.Gosched() for g := 0; g < CallsPerSched; g++ { - runtime.Semrelease(&sem) + Runtime_Semrelease(&sem) if work { for i := 0; i < LocalWork; i++ { foo *= 2 foo /= 2 } } - runtime.Semacquire(&sem) + Runtime_Semacquire(&sem) } } c <- foo == 42 - runtime.Semrelease(&sem) + Runtime_Semrelease(&sem) }() } if block { diff --git a/libgo/go/sync/rwmutex.go b/libgo/go/sync/rwmutex.go index cb1a47720b2..782a9c31968 100644 --- a/libgo/go/sync/rwmutex.go +++ b/libgo/go/sync/rwmutex.go @@ -4,10 +4,7 @@ package sync -import ( - "runtime" - "sync/atomic" -) +import "sync/atomic" // An RWMutex is a reader/writer mutual exclusion lock. // The lock can be held by an arbitrary number of readers @@ -29,7 +26,7 @@ const rwmutexMaxReaders = 1 << 30 func (rw *RWMutex) RLock() { if atomic.AddInt32(&rw.readerCount, 1) < 0 { // A writer is pending, wait for it. - runtime.Semacquire(&rw.readerSem) + runtime_Semacquire(&rw.readerSem) } } @@ -42,7 +39,7 @@ func (rw *RWMutex) RUnlock() { // A writer is pending. if atomic.AddInt32(&rw.readerWait, -1) == 0 { // The last reader unblocks the writer. - runtime.Semrelease(&rw.writerSem) + runtime_Semrelease(&rw.writerSem) } } } @@ -60,7 +57,7 @@ func (rw *RWMutex) Lock() { r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders // Wait for active readers. if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 { - runtime.Semacquire(&rw.writerSem) + runtime_Semacquire(&rw.writerSem) } } @@ -75,7 +72,7 @@ func (rw *RWMutex) Unlock() { r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders) // Unblock blocked readers, if any. for i := 0; i < int(r); i++ { - runtime.Semrelease(&rw.readerSem) + runtime_Semrelease(&rw.readerSem) } // Allow other writers to proceed. rw.w.Unlock() diff --git a/libgo/go/sync/waitgroup.go b/libgo/go/sync/waitgroup.go index a4c9b7e43cd..3e7d9d3c8f4 100644 --- a/libgo/go/sync/waitgroup.go +++ b/libgo/go/sync/waitgroup.go @@ -4,10 +4,7 @@ package sync -import ( - "runtime" - "sync/atomic" -) +import "sync/atomic" // A WaitGroup waits for a collection of goroutines to finish. // The main goroutine calls Add to set the number of @@ -60,7 +57,7 @@ func (wg *WaitGroup) Add(delta int) { } wg.m.Lock() for i := int32(0); i < wg.waiters; i++ { - runtime.Semrelease(wg.sema) + runtime_Semrelease(wg.sema) } wg.waiters = 0 wg.sema = nil @@ -93,5 +90,5 @@ func (wg *WaitGroup) Wait() { } s := wg.sema wg.m.Unlock() - runtime.Semacquire(s) + runtime_Semacquire(s) } diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go index d5d60eae4cd..adc8c09f217 100644 --- a/libgo/go/testing/testing.go +++ b/libgo/go/testing/testing.go @@ -38,16 +38,25 @@ // } // } // -// The package also runs and verifies example code. Example functions -// include an introductory comment that is compared with the standard output -// of the function when the tests are run, as in this example of an example: +// The package also runs and verifies example code. Example functions may +// include a concluding comment that begins with "Output:" and is compared with +// the standard output of the function when the tests are run, as in these +// examples of an example: // -// // hello // func ExampleHello() { // fmt.Println("hello") +// // Output: hello // } // -// Example functions without comments are compiled but not executed. +// func ExampleSalutations() { +// fmt.Println("hello, and") +// fmt.Println("goodbye") +// // Output: +// // hello, and +// // goodbye +// } +// +// Example functions without output comments are compiled but not executed. // // The naming convention to declare examples for a function F, a type T and // method M on type T are: diff --git a/libgo/go/text/tabwriter/example_test.go b/libgo/go/text/tabwriter/example_test.go new file mode 100644 index 00000000000..20443cb1ffb --- /dev/null +++ b/libgo/go/text/tabwriter/example_test.go @@ -0,0 +1,38 @@ +// 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 tabwriter_test + +import ( + "fmt" + "os" + "text/tabwriter" +) + +func ExampleWriter_Init() { + w := new(tabwriter.Writer) + + // Format in tab-separated columns with a tab stop of 8. + w.Init(os.Stdout, 0, 8, 0, '\t', 0) + fmt.Fprintln(w, "a\tb\tc\td\t.") + fmt.Fprintln(w, "123\t12345\t1234567\t123456789\t.") + fmt.Fprintln(w) + w.Flush() + + // Format right-aligned in space-separated columns of minimal width 5 + // and at least one blank of padding (so wider column entries do not + // touch each other). + w.Init(os.Stdout, 5, 0, 1, ' ', tabwriter.AlignRight) + fmt.Fprintln(w, "a\tb\tc\td\t.") + fmt.Fprintln(w, "123\t12345\t1234567\t123456789\t.") + fmt.Fprintln(w) + w.Flush() + + // output: + // a b c d . + // 123 12345 1234567 123456789 . + // + // a b c d. + // 123 12345 1234567 123456789. +} diff --git a/libgo/go/text/tabwriter/tabwriter.go b/libgo/go/text/tabwriter/tabwriter.go index ea7c4008111..ce84600d604 100644 --- a/libgo/go/text/tabwriter/tabwriter.go +++ b/libgo/go/text/tabwriter/tabwriter.go @@ -169,12 +169,6 @@ const ( // to the tab width in the viewer displaying the result) // flags formatting control // -// To format in tab-separated columns with a tab stop of 8: -// b.Init(w, 8, 1, 8, '\t', 0); -// -// To format in space-separated columns with at least 4 spaces between columns: -// b.Init(w, 0, 4, 8, ' ', 0); -// func (b *Writer) Init(output io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *Writer { if minwidth < 0 || tabwidth < 0 || padding < 0 { panic("negative minwidth, tabwidth, or padding") diff --git a/libgo/go/text/tabwriter/tabwriter_test.go b/libgo/go/text/tabwriter/tabwriter_test.go index 1ffb330d432..ace53564737 100644 --- a/libgo/go/text/tabwriter/tabwriter_test.go +++ b/libgo/go/text/tabwriter/tabwriter_test.go @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package tabwriter +package tabwriter_test import ( "io" "testing" + . "text/tabwriter" ) type buffer struct { diff --git a/libgo/go/text/template/doc.go b/libgo/go/text/template/doc.go index 35c4c681183..ae91f4a5419 100644 --- a/libgo/go/text/template/doc.go +++ b/libgo/go/text/template/doc.go @@ -22,6 +22,20 @@ Actions may not span newlines, although comments can. Once constructed, a template may be executed safely in parallel. +Here is a trivial example that prints "17 items are made of wool". + + type Inventory struct { + Material string + Count uint + } + sweaters := Inventory{"wool", 17} + tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}") + if err != nil { panic(err) } + err = tmpl.Execute(os.Stdout, sweaters) + if err != nil { panic(err) } + +More intricate examples appear below. + Actions Here is the list of actions. "Arguments" and "pipelines" are evaluations of @@ -128,6 +142,11 @@ An argument is a simple value, denoted by one of the following. .Field1.Key1.Method1.Field2.Key2.Method2 Methods can also be evaluated on variables, including chaining: $x.Method1.Field + - The name of a niladic function-valued struct field of the data, + preceded by a period, such as + .Function + Function-valued fields behave like methods (of structs) but do not + pass a receiver. - The name of a niladic function, such as fun The result is the value of invoking the function, fun(). The return @@ -148,6 +167,9 @@ value (argument) or a function or method call, possibly with multiple arguments: The result is the value of calling the method with the arguments: dot.Method(Argument1, etc.) + .Function [Argument...] + A function-valued field of a struct works like a method but does + not pass the receiver. functionName [Argument...] The result is the value of calling the function associated with the name: @@ -303,7 +325,7 @@ produce the text By construction, a template may reside in only one association. If it's necessary to have a template addressable from multiple associations, the template definition must be parsed multiple times to create distinct *Template -values. +values, or must be copied with the Clone or AddParseTree method. Parse may be called multiple times to assemble the various associated templates; see the ParseFiles and ParseGlob functions and methods for simple ways to parse diff --git a/libgo/go/text/template/exec.go b/libgo/go/text/template/exec.go index 973189a8a62..af745286c0b 100644 --- a/libgo/go/text/template/exec.go +++ b/libgo/go/text/template/exec.go @@ -419,10 +419,14 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node tField, ok := receiver.Type().FieldByName(fieldName) if ok { field := receiver.FieldByIndex(tField.Index) - if hasArgs { - s.errorf("%s is not a method but has arguments", fieldName) - } if tField.PkgPath == "" { // field is exported + // If it's a function, we must call it. + if field.Type().Kind() == reflect.Func { + return s.evalCall(dot, field, fieldName, args, final) + } + if hasArgs { + s.errorf("%s is not a method or function but has arguments", fieldName) + } return field } } diff --git a/libgo/go/text/template/exec_test.go b/libgo/go/text/template/exec_test.go index 9bb55e48aac..159cf5100d9 100644 --- a/libgo/go/text/template/exec_test.go +++ b/libgo/go/text/template/exec_test.go @@ -59,6 +59,8 @@ type T struct { PI *int PSI *[]int NIL *int + // Function (not method) + Func func(...string) string // Template to test evaluation of templates. Tmpl *Template } @@ -118,6 +120,7 @@ var tVal = &T{ Err: errors.New("erroozle"), PI: newInt(23), PSI: newIntSlice(21, 22, 23), + Func: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") }, Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X } @@ -297,8 +300,13 @@ var execTests = []execTest{ "{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}", "true", tVal, true}, + // Function call + {".Func", "-{{.Func}}-", "-<>-", tVal, true}, + {".Func2", "-{{.Func `he` `llo`}}-", "-<he+llo>-", tVal, true}, + // Pipelines. {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true}, + {"pipeline func", "-{{.Func `llo` | .Func `he` }}-", "-<he+<llo>>-", tVal, true}, // If. {"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true}, diff --git a/libgo/go/text/template/multi_test.go b/libgo/go/text/template/multi_test.go index 274f5ef1477..f205e6be1b4 100644 --- a/libgo/go/text/template/multi_test.go +++ b/libgo/go/text/template/multi_test.go @@ -193,7 +193,7 @@ func TestClone(t *testing.T) { if err != nil { t.Fatal(err) } - clone := root.Clone() + clone := Must(root.Clone()) // Add variants to both. _, err = root.Parse(cloneText3) if err != nil { diff --git a/libgo/go/text/template/parse/parse.go b/libgo/go/text/template/parse/parse.go index 4da756657d5..35194f7dfdb 100644 --- a/libgo/go/text/template/parse/parse.go +++ b/libgo/go/text/template/parse/parse.go @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package parse builds parse trees for templates. The grammar is defined -// in the documents for the template package. +// Package parse builds parse trees for templates as defined by text/template +// and html/template. Clients should use those packages to construct templates +// rather than this one, which provides shared internal data structures not +// intended for general use. package parse import ( diff --git a/libgo/go/text/template/template.go b/libgo/go/text/template/template.go index 87e39d3af74..7494f9d8c45 100644 --- a/libgo/go/text/template/template.go +++ b/libgo/go/text/template/template.go @@ -69,9 +69,9 @@ func (t *Template) init() { // templates. The actual representation is not copied, but the name space of // associated templates is, so further calls to Parse in the copy will add // templates to the copy but not to the original. Clone can be used to prepare -// common templates and use them with variant definitions for other templates by -// adding the variants after the clone is made. -func (t *Template) Clone() *Template { +// common templates and use them with variant definitions for other templates +// by adding the variants after the clone is made. +func (t *Template) Clone() (*Template, error) { nt := t.copy(nil) nt.init() nt.tmpl[t.name] = nt @@ -89,7 +89,7 @@ func (t *Template) Clone() *Template { for k, v := range t.execFuncs { nt.execFuncs[k] = v } - return nt + return nt, nil } // copy returns a shallow copy of t, with common set to the argument. diff --git a/libgo/go/time/example_test.go b/libgo/go/time/example_test.go index b25e64cda3e..944cc789c31 100644 --- a/libgo/go/time/example_test.go +++ b/libgo/go/time/example_test.go @@ -51,8 +51,8 @@ func ExampleMonth() { } } -// Go launched at 2009-11-10 15:00:00 -0800 PST func ExampleDate() { t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) fmt.Printf("Go launched at %s\n", t.Local()) + // Output: Go launched at 2009-11-10 15:00:00 -0800 PST } diff --git a/libgo/go/time/sys_plan9.go b/libgo/go/time/sys_plan9.go index c7cfa792a29..8484729448e 100644 --- a/libgo/go/time/sys_plan9.go +++ b/libgo/go/time/sys_plan9.go @@ -6,7 +6,10 @@ package time -import "syscall" +import ( + "errors" + "syscall" +) // for testing: whatever interrupts a sleep func interrupt() { @@ -38,3 +41,36 @@ func readFile(name string) ([]byte, error) { } return ret, err } + +func open(name string) (uintptr, error) { + fd, err := syscall.Open(name, syscall.O_RDONLY) + if err != nil { + return 0, err + } + return uintptr(fd), nil +} + +func closefd(fd uintptr) { + syscall.Close(int(fd)) +} + +func preadn(fd uintptr, buf []byte, off int) error { + whence := 0 + if off < 0 { + whence = 2 + } + if _, err := syscall.Seek(int(fd), int64(off), whence); err != nil { + return err + } + for len(buf) > 0 { + m, err := syscall.Read(int(fd), buf) + if m <= 0 { + if err == nil { + return errors.New("short read") + } + return err + } + buf = buf[m:] + } + return nil +} diff --git a/libgo/go/time/sys_unix.go b/libgo/go/time/sys_unix.go index 56a7414e0ce..7f69b492c9f 100644 --- a/libgo/go/time/sys_unix.go +++ b/libgo/go/time/sys_unix.go @@ -6,7 +6,10 @@ package time -import "syscall" +import ( + "errors" + "syscall" +) // for testing: whatever interrupts a sleep func interrupt() { @@ -38,3 +41,36 @@ func readFile(name string) ([]byte, error) { } return ret, err } + +func open(name string) (uintptr, error) { + fd, err := syscall.Open(name, syscall.O_RDONLY, 0) + if err != nil { + return 0, err + } + return uintptr(fd), nil +} + +func closefd(fd uintptr) { + syscall.Close(int(fd)) +} + +func preadn(fd uintptr, buf []byte, off int) error { + whence := 0 + if off < 0 { + whence = 2 + } + if _, err := syscall.Seek(int(fd), int64(off), whence); err != nil { + return err + } + for len(buf) > 0 { + m, err := syscall.Read(int(fd), buf) + if m <= 0 { + if err == nil { + return errors.New("short read") + } + return err + } + buf = buf[m:] + } + return nil +} diff --git a/libgo/go/time/sys_windows.go b/libgo/go/time/sys_windows.go index 8c7242f4275..de63b4bf4bb 100644 --- a/libgo/go/time/sys_windows.go +++ b/libgo/go/time/sys_windows.go @@ -4,6 +4,70 @@ package time +import ( + "errors" + "syscall" +) + // for testing: whatever interrupts a sleep func interrupt() { } + +// readFile reads and returns the content of the named file. +// It is a trivial implementation of ioutil.ReadFile, reimplemented +// here to avoid depending on io/ioutil or os. +func readFile(name string) ([]byte, error) { + f, err := syscall.Open(name, syscall.O_RDONLY, 0) + if err != nil { + return nil, err + } + defer syscall.Close(f) + var ( + buf [4096]byte + ret []byte + n int + ) + for { + n, err = syscall.Read(f, buf[:]) + if n > 0 { + ret = append(ret, buf[:n]...) + } + if n == 0 || err != nil { + break + } + } + return ret, err +} + +func open(name string) (uintptr, error) { + fd, err := syscall.Open(name, syscall.O_RDONLY, 0) + if err != nil { + return 0, err + } + return uintptr(fd), nil +} + +func closefd(fd uintptr) { + syscall.Close(syscall.Handle(fd)) +} + +func preadn(fd uintptr, buf []byte, off int) error { + whence := 0 + if off < 0 { + whence = 2 + } + if _, err := syscall.Seek(syscall.Handle(fd), int64(off), whence); err != nil { + return err + } + for len(buf) > 0 { + m, err := syscall.Read(syscall.Handle(fd), buf) + if m <= 0 { + if err == nil { + return errors.New("short read") + } + return err + } + buf = buf[m:] + } + return nil +} diff --git a/libgo/go/time/tick_test.go b/libgo/go/time/tick_test.go index 0c1c4d67ab0..914f02c861c 100644 --- a/libgo/go/time/tick_test.go +++ b/libgo/go/time/tick_test.go @@ -12,9 +12,6 @@ import ( func TestTicker(t *testing.T) { const Count = 10 Delta := 100 * Millisecond - if testing.Short() { - Delta = 10 * Millisecond - } ticker := NewTicker(Delta) t0 := Now() for i := 0; i < Count; i++ { diff --git a/libgo/go/time/zoneinfo.go b/libgo/go/time/zoneinfo.go index aca56e746af..3c57744043e 100644 --- a/libgo/go/time/zoneinfo.go +++ b/libgo/go/time/zoneinfo.go @@ -4,7 +4,10 @@ package time -import "sync" +import ( + "sync" + "syscall" +) // A Location maps time instants to the zone in use at that time. // Typically, the Location represents the collection of time offsets @@ -168,10 +171,7 @@ func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) { // NOTE(rsc): Eventually we will need to accept the POSIX TZ environment // syntax too, but I don't feel like implementing it today. -// NOTE(rsc): Using the IANA names below means ensuring we have access -// to the database. Probably we will ship the files in $GOROOT/lib/zoneinfo/ -// and only look there if there are no system files available (such as on Windows). -// The files total 200 kB. +var zoneinfo, _ = syscall.Getenv("ZONEINFO") // LoadLocation returns the Location with the given name. // @@ -180,6 +180,13 @@ func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) { // // Otherwise, the name is taken to be a location name corresponding to a file // in the IANA Time Zone database, such as "America/New_York". +// +// The time zone database needed by LoadLocation may not be +// present on all systems, especially non-Unix systems. +// LoadLocation looks in the directory or uncompressed zip file +// named by the ZONEINFO environment variable, if any, then looks in +// known installation locations on Unix systems, +// and finally looks in $GOROOT/lib/time/zoneinfo.zip. func LoadLocation(name string) (*Location, error) { if name == "" || name == "UTC" { return UTC, nil @@ -187,5 +194,11 @@ func LoadLocation(name string) (*Location, error) { if name == "Local" { return Local, nil } + if zoneinfo != "" { + if z, err := loadZoneFile(zoneinfo, name); err == nil { + z.name = name + return z, nil + } + } return loadLocation(name) } diff --git a/libgo/go/time/zoneinfo_plan9.go b/libgo/go/time/zoneinfo_plan9.go index 9c052d42cd3..6855238dc84 100644 --- a/libgo/go/time/zoneinfo_plan9.go +++ b/libgo/go/time/zoneinfo_plan9.go @@ -8,11 +8,10 @@ package time import ( "errors" + "runtime" "syscall" ) -var badData = errors.New("malformed time zone information") - func isSpace(r rune) bool { return r == ' ' || r == '\t' || r == '\n' } @@ -51,7 +50,7 @@ func fields(s string) []string { return a } -func loadZoneData(s string) (l *Location, err error) { +func loadZoneDataPlan9(s string) (l *Location, err error) { f := fields(s) if len(f) < 4 { if len(f) == 2 && f[0] == "GMT" { @@ -112,33 +111,32 @@ func loadZoneData(s string) (l *Location, err error) { return l, nil } -func loadZoneFile(name string) (*Location, error) { +func loadZoneFilePlan9(name string) (*Location, error) { b, err := readFile(name) if err != nil { return nil, err } - return loadZoneData(string(b)) + return loadZoneDataPlan9(string(b)) } func initTestingZone() { - if z, err := loadZoneFile("/adm/timezone/US_Pacific"); err == nil { - localLoc = *z - return + z, err := loadLocation("America/Los_Angeles") + if err != nil { + panic("cannot load America/Los_Angeles for testing: " + err.Error()) } - - // Fall back to UTC. - localLoc.name = "UTC" + z.name = "Local" + localLoc = *z } func initLocal() { t, ok := syscall.Getenv("timezone") if ok { - if z, err := loadZoneData(t); err == nil { + if z, err := loadZoneDataPlan9(t); err == nil { localLoc = *z return } } else { - if z, err := loadZoneFile("/adm/timezone/local"); err == nil { + if z, err := loadZoneFilePlan9("/adm/timezone/local"); err == nil { localLoc = *z localLoc.name = "Local" return @@ -150,7 +148,8 @@ func initLocal() { } func loadLocation(name string) (*Location, error) { - if z, err := loadZoneFile("/adm/timezone/" + name); err == nil { + if z, err := loadZoneFile(runtime.GOROOT()+"/lib/time/zoneinfo.zip", name); err == nil { + z.name = name return z, nil } return nil, errors.New("unknown time zone " + name) diff --git a/libgo/go/time/zoneinfo_read.go b/libgo/go/time/zoneinfo_read.go new file mode 100644 index 00000000000..ebb4205a98f --- /dev/null +++ b/libgo/go/time/zoneinfo_read.go @@ -0,0 +1,341 @@ +// Copyright 2009 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. + +// Parse "zoneinfo" time zone file. +// This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others. +// See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo, +// and ftp://munnari.oz.au/pub/oldtz/ + +package time + +import "errors" + +const ( + headerSize = 4 + 16 + 4*7 +) + +// Simple I/O interface to binary blob of data. +type data struct { + p []byte + error bool +} + +func (d *data) read(n int) []byte { + if len(d.p) < n { + d.p = nil + d.error = true + return nil + } + p := d.p[0:n] + d.p = d.p[n:] + return p +} + +func (d *data) big4() (n uint32, ok bool) { + p := d.read(4) + if len(p) < 4 { + d.error = true + return 0, false + } + return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true +} + +func (d *data) byte() (n byte, ok bool) { + p := d.read(1) + if len(p) < 1 { + d.error = true + return 0, false + } + return p[0], true +} + +// Make a string by stopping at the first NUL +func byteString(p []byte) string { + for i := 0; i < len(p); i++ { + if p[i] == 0 { + return string(p[0:i]) + } + } + return string(p) +} + +var badData = errors.New("malformed time zone information") + +func loadZoneData(bytes []byte) (l *Location, err error) { + d := data{bytes, false} + + // 4-byte magic "TZif" + if magic := d.read(4); string(magic) != "TZif" { + return nil, badData + } + + // 1-byte version, then 15 bytes of padding + var p []byte + if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' { + return nil, badData + } + + // six big-endian 32-bit integers: + // number of UTC/local indicators + // number of standard/wall indicators + // number of leap seconds + // number of transition times + // number of local time zones + // number of characters of time zone abbrev strings + const ( + NUTCLocal = iota + NStdWall + NLeap + NTime + NZone + NChar + ) + var n [6]int + for i := 0; i < 6; i++ { + nn, ok := d.big4() + if !ok { + return nil, badData + } + n[i] = int(nn) + } + + // Transition times. + txtimes := data{d.read(n[NTime] * 4), false} + + // Time zone indices for transition times. + txzones := d.read(n[NTime]) + + // Zone info structures + zonedata := data{d.read(n[NZone] * 6), false} + + // Time zone abbreviations. + abbrev := d.read(n[NChar]) + + // Leap-second time pairs + d.read(n[NLeap] * 8) + + // Whether tx times associated with local time types + // are specified as standard time or wall time. + isstd := d.read(n[NStdWall]) + + // Whether tx times associated with local time types + // are specified as UTC or local time. + isutc := d.read(n[NUTCLocal]) + + if d.error { // ran out of data + return nil, badData + } + + // If version == 2, the entire file repeats, this time using + // 8-byte ints for txtimes and leap seconds. + // We won't need those until 2106. + + // Now we can build up a useful data structure. + // First the zone information. + // utcoff[4] isdst[1] nameindex[1] + zone := make([]zone, n[NZone]) + for i := range zone { + var ok bool + var n uint32 + if n, ok = zonedata.big4(); !ok { + return nil, badData + } + zone[i].offset = int(n) + var b byte + if b, ok = zonedata.byte(); !ok { + return nil, badData + } + zone[i].isDST = b != 0 + if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) { + return nil, badData + } + zone[i].name = byteString(abbrev[b:]) + } + + // Now the transition time info. + tx := make([]zoneTrans, n[NTime]) + for i := range tx { + var ok bool + var n uint32 + if n, ok = txtimes.big4(); !ok { + return nil, badData + } + tx[i].when = int64(int32(n)) + if int(txzones[i]) >= len(zone) { + return nil, badData + } + tx[i].index = txzones[i] + if i < len(isstd) { + tx[i].isstd = isstd[i] != 0 + } + if i < len(isutc) { + tx[i].isutc = isutc[i] != 0 + } + } + + // Commited to succeed. + l = &Location{zone: zone, tx: tx} + + // Fill in the cache with information about right now, + // since that will be the most common lookup. + sec, _ := now() + for i := range tx { + if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) { + l.cacheStart = tx[i].when + l.cacheEnd = 1<<63 - 1 + if i+1 < len(tx) { + l.cacheEnd = tx[i+1].when + } + l.cacheZone = &l.zone[tx[i].index] + } + } + + return l, nil +} + +func loadZoneFile(dir, name string) (l *Location, err error) { + if len(dir) > 4 && dir[len(dir)-4:] == ".zip" { + return loadZoneZip(dir, name) + } + if dir != "" { + name = dir + "/" + name + } + buf, err := readFile(name) + if err != nil { + return + } + return loadZoneData(buf) +} + +// There are 500+ zoneinfo files. Rather than distribute them all +// individually, we ship them in an uncompressed zip file. +// Used this way, the zip file format serves as a commonly readable +// container for the individual small files. We choose zip over tar +// because zip files have a contiguous table of contents, making +// individual file lookups faster, and because the per-file overhead +// in a zip file is considerably less than tar's 512 bytes. + +// get4 returns the little-endian 32-bit value in b. +func get4(b []byte) int { + if len(b) < 4 { + return 0 + } + return int(b[0]) | int(b[1])<<8 | int(b[2])<<16 | int(b[3])<<24 +} + +// get2 returns the little-endian 16-bit value in b. +func get2(b []byte) int { + if len(b) < 2 { + return 0 + } + return int(b[0]) | int(b[1])<<8 +} + +func loadZoneZip(zipfile, name string) (l *Location, err error) { + fd, err := open(zipfile) + if err != nil { + return nil, errors.New("open " + zipfile + ": " + err.Error()) + } + defer closefd(fd) + + const ( + zecheader = 0x06054b50 + zcheader = 0x02014b50 + ztailsize = 22 + + zheadersize = 30 + zheader = 0x04034b50 + ) + + buf := make([]byte, ztailsize) + if err := preadn(fd, buf, -ztailsize); err != nil || get4(buf) != zecheader { + return nil, errors.New("corrupt zip file " + zipfile) + } + n := get2(buf[10:]) + size := get4(buf[12:]) + off := get4(buf[16:]) + + buf = make([]byte, size) + if err := preadn(fd, buf, off); err != nil { + return nil, errors.New("corrupt zip file " + zipfile) + } + + for i := 0; i < n; i++ { + // zip entry layout: + // 0 magic[4] + // 4 madevers[1] + // 5 madeos[1] + // 6 extvers[1] + // 7 extos[1] + // 8 flags[2] + // 10 meth[2] + // 12 modtime[2] + // 14 moddate[2] + // 16 crc[4] + // 20 csize[4] + // 24 uncsize[4] + // 28 namelen[2] + // 30 xlen[2] + // 32 fclen[2] + // 34 disknum[2] + // 36 iattr[2] + // 38 eattr[4] + // 42 off[4] + // 46 name[namelen] + // 46+namelen+xlen+fclen - next header + // + if get4(buf) != zcheader { + break + } + meth := get2(buf[10:]) + size := get4(buf[24:]) + namelen := get2(buf[28:]) + xlen := get2(buf[30:]) + fclen := get2(buf[32:]) + off := get4(buf[42:]) + zname := buf[46 : 46+namelen] + buf = buf[46+namelen+xlen+fclen:] + if string(zname) != name { + continue + } + if meth != 0 { + return nil, errors.New("unsupported compression for " + name + " in " + zipfile) + } + + // zip per-file header layout: + // 0 magic[4] + // 4 extvers[1] + // 5 extos[1] + // 6 flags[2] + // 8 meth[2] + // 10 modtime[2] + // 12 moddate[2] + // 14 crc[4] + // 18 csize[4] + // 22 uncsize[4] + // 26 namelen[2] + // 28 xlen[2] + // 30 name[namelen] + // 30+namelen+xlen - file data + // + buf = make([]byte, zheadersize+namelen) + if err := preadn(fd, buf, off); err != nil || + get4(buf) != zheader || + get2(buf[8:]) != meth || + get2(buf[26:]) != namelen || + string(buf[30:30+namelen]) != name { + return nil, errors.New("corrupt zip file " + zipfile) + } + xlen = get2(buf[28:]) + + buf = make([]byte, size) + if err := preadn(fd, buf, off+30+namelen+xlen); err != nil { + return nil, errors.New("corrupt zip file " + zipfile) + } + + return loadZoneData(buf) + } + + return nil, errors.New("cannot find " + name + " in zip file " + zipfile) +} diff --git a/libgo/go/time/zoneinfo_unix.go b/libgo/go/time/zoneinfo_unix.go index 540b653c57d..1bf1f11e24f 100644 --- a/libgo/go/time/zoneinfo_unix.go +++ b/libgo/go/time/zoneinfo_unix.go @@ -13,200 +13,10 @@ package time import ( "errors" + "runtime" "syscall" ) -const ( - headerSize = 4 + 16 + 4*7 -) - -// Simple I/O interface to binary blob of data. -type data struct { - p []byte - error bool -} - -func (d *data) read(n int) []byte { - if len(d.p) < n { - d.p = nil - d.error = true - return nil - } - p := d.p[0:n] - d.p = d.p[n:] - return p -} - -func (d *data) big4() (n uint32, ok bool) { - p := d.read(4) - if len(p) < 4 { - d.error = true - return 0, false - } - return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true -} - -func (d *data) byte() (n byte, ok bool) { - p := d.read(1) - if len(p) < 1 { - d.error = true - return 0, false - } - return p[0], true -} - -// Make a string by stopping at the first NUL -func byteString(p []byte) string { - for i := 0; i < len(p); i++ { - if p[i] == 0 { - return string(p[0:i]) - } - } - return string(p) -} - -var badData = errors.New("malformed time zone information") - -func loadZoneData(bytes []byte) (l *Location, err error) { - d := data{bytes, false} - - // 4-byte magic "TZif" - if magic := d.read(4); string(magic) != "TZif" { - return nil, badData - } - - // 1-byte version, then 15 bytes of padding - var p []byte - if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' { - return nil, badData - } - - // six big-endian 32-bit integers: - // number of UTC/local indicators - // number of standard/wall indicators - // number of leap seconds - // number of transition times - // number of local time zones - // number of characters of time zone abbrev strings - const ( - NUTCLocal = iota - NStdWall - NLeap - NTime - NZone - NChar - ) - var n [6]int - for i := 0; i < 6; i++ { - nn, ok := d.big4() - if !ok { - return nil, badData - } - n[i] = int(nn) - } - - // Transition times. - txtimes := data{d.read(n[NTime] * 4), false} - - // Time zone indices for transition times. - txzones := d.read(n[NTime]) - - // Zone info structures - zonedata := data{d.read(n[NZone] * 6), false} - - // Time zone abbreviations. - abbrev := d.read(n[NChar]) - - // Leap-second time pairs - d.read(n[NLeap] * 8) - - // Whether tx times associated with local time types - // are specified as standard time or wall time. - isstd := d.read(n[NStdWall]) - - // Whether tx times associated with local time types - // are specified as UTC or local time. - isutc := d.read(n[NUTCLocal]) - - if d.error { // ran out of data - return nil, badData - } - - // If version == 2, the entire file repeats, this time using - // 8-byte ints for txtimes and leap seconds. - // We won't need those until 2106. - - // Now we can build up a useful data structure. - // First the zone information. - // utcoff[4] isdst[1] nameindex[1] - zone := make([]zone, n[NZone]) - for i := range zone { - var ok bool - var n uint32 - if n, ok = zonedata.big4(); !ok { - return nil, badData - } - zone[i].offset = int(n) - var b byte - if b, ok = zonedata.byte(); !ok { - return nil, badData - } - zone[i].isDST = b != 0 - if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) { - return nil, badData - } - zone[i].name = byteString(abbrev[b:]) - } - - // Now the transition time info. - tx := make([]zoneTrans, n[NTime]) - for i := range tx { - var ok bool - var n uint32 - if n, ok = txtimes.big4(); !ok { - return nil, badData - } - tx[i].when = int64(int32(n)) - if int(txzones[i]) >= len(zone) { - return nil, badData - } - tx[i].index = txzones[i] - if i < len(isstd) { - tx[i].isstd = isstd[i] != 0 - } - if i < len(isutc) { - tx[i].isutc = isutc[i] != 0 - } - } - - // Commited to succeed. - l = &Location{zone: zone, tx: tx} - - // Fill in the cache with information about right now, - // since that will be the most common lookup. - sec, _ := now() - for i := range tx { - if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) { - l.cacheStart = tx[i].when - l.cacheEnd = 1<<63 - 1 - if i+1 < len(tx) { - l.cacheEnd = tx[i+1].when - } - l.cacheZone = &l.zone[tx[i].index] - } - } - - return l, nil -} - -func loadZoneFile(name string) (l *Location, err error) { - buf, err := readFile(name) - if err != nil { - return - } - return loadZoneData(buf) -} - func initTestingZone() { syscall.Setenv("TZ", "America/Los_Angeles") initLocal() @@ -218,6 +28,7 @@ var zoneDirs = []string{ "/usr/share/zoneinfo/", "/usr/share/lib/zoneinfo/", "/usr/lib/locale/TZ/", + runtime.GOROOT() + "/lib/time/zoneinfo/", } func initLocal() { @@ -229,7 +40,7 @@ func initLocal() { tz, ok := syscall.Getenv("TZ") switch { case !ok: - z, err := loadZoneFile("/etc/localtime") + z, err := loadZoneFile("", "/etc/localtime") if err == nil { localLoc = *z localLoc.name = "Local" @@ -248,7 +59,7 @@ func initLocal() { func loadLocation(name string) (*Location, error) { for _, zoneDir := range zoneDirs { - if z, err := loadZoneFile(zoneDir + name); err == nil { + if z, err := loadZoneFile(zoneDir, name); err == nil { z.name = name return z, nil } diff --git a/libgo/go/time/zoneinfo_windows.go b/libgo/go/time/zoneinfo_windows.go index beef4de92b0..754e392deca 100644 --- a/libgo/go/time/zoneinfo_windows.go +++ b/libgo/go/time/zoneinfo_windows.go @@ -6,6 +6,7 @@ package time import ( "errors" + "runtime" "syscall" ) @@ -151,7 +152,10 @@ func initLocal() { initLocalFromTZI(&i) } -// TODO(rsc): Implement. func loadLocation(name string) (*Location, error) { + if z, err := loadZoneFile(runtime.GOROOT()+`\lib\time\zoneinfo.zip`, name); err == nil { + z.name = name + return z, nil + } return nil, errors.New("unknown time zone " + name) } diff --git a/libgo/runtime/malloc.goc b/libgo/runtime/malloc.goc index 8b48c866346..23641e8298f 100644 --- a/libgo/runtime/malloc.goc +++ b/libgo/runtime/malloc.goc @@ -19,6 +19,7 @@ package runtime #include "go-type.h" MHeap runtime_mheap; + extern MStats mstats; // defined in extern.go extern volatile int32 runtime_MemProfileRate @@ -429,18 +430,6 @@ func new(typ *Type) (ret *uint8) { ret = runtime_mallocgc(typ->__size, flag, 1, 1); } -func Alloc(n uintptr) (p *byte) { - p = runtime_malloc(n); -} - -func Free(p *byte) { - runtime_free(p); -} - -func Lookup(p *byte) (base *byte, size uintptr) { - runtime_mlookup(p, &base, &size, nil); -} - func GC() { runtime_gc(1); } diff --git a/libgo/runtime/malloc.h b/libgo/runtime/malloc.h index c1cf02c6deb..4cb07477f15 100644 --- a/libgo/runtime/malloc.h +++ b/libgo/runtime/malloc.h @@ -205,6 +205,7 @@ struct MStats uint64 heap_sys; // bytes obtained from system uint64 heap_idle; // bytes in idle spans uint64 heap_inuse; // bytes in non-idle spans + uint64 heap_released; // bytes released to the OS uint64 heap_objects; // total number of allocated objects // Statistics about allocation of low-level fixed-size structures. @@ -220,6 +221,7 @@ struct MStats // Statistics about garbage collector. // Protected by stopping the world during GC. uint64 next_gc; // next GC (in heap_alloc time) + uint64 last_gc; // last GC (in absolute time) uint64 pause_total_ns; uint64 pause_ns[256]; uint32 numgc; @@ -304,14 +306,16 @@ struct MSpan { MSpan *next; // in a span linked list MSpan *prev; // in a span linked list - MSpan *allnext; // in the list of all spans + MSpan *allnext; // in the list of all spans PageID start; // starting page number uintptr npages; // number of pages in span MLink *freelist; // list of free objects uint32 ref; // number of allocated objects in this span uint32 sizeclass; // size class uint32 state; // MSpanInUse etc - byte *limit; // end of data in span + int64 unusedsince; // First time spotted by GC in MSpanFree state + uintptr npreleased; // number of pages released to the OS + byte *limit; // end of data in span }; void runtime_MSpan_Init(MSpan *span, PageID start, uintptr npages); @@ -381,6 +385,7 @@ MSpan* runtime_MHeap_LookupMaybe(MHeap *h, void *v); void runtime_MGetSizeClassInfo(int32 sizeclass, uintptr *size, int32 *npages, int32 *nobj); void* runtime_MHeap_SysAlloc(MHeap *h, uintptr n); void runtime_MHeap_MapBits(MHeap *h); +void runtime_MHeap_Scavenger(void*); void* runtime_mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed); int32 runtime_mlookup(void *v, byte **base, uintptr *size, MSpan **s); @@ -406,19 +411,11 @@ enum void runtime_MProf_Malloc(void*, uintptr); void runtime_MProf_Free(void*, uintptr); +void runtime_MProf_GC(void); void runtime_MProf_Mark(void (*scan)(byte *, int64)); int32 runtime_helpgc(bool*); void runtime_gchelper(void); -// Malloc profiling settings. -// Must match definition in extern.go. -enum { - MProf_None = 0, - MProf_Sample = 1, - MProf_All = 2, -}; -extern int32 runtime_malloc_profile; - struct __go_func_type; bool runtime_getfinalizer(void *p, bool del, void (**fn)(void*), const struct __go_func_type **ft); void runtime_walkfintab(void (*fn)(void*), void (*scan)(byte *, int64)); diff --git a/libgo/runtime/mgc0.c b/libgo/runtime/mgc0.c index 73c399df239..d852946cdbb 100644 --- a/libgo/runtime/mgc0.c +++ b/libgo/runtime/mgc0.c @@ -61,6 +61,21 @@ enum { #define bitMask (bitBlockBoundary | bitAllocated | bitMarked | bitSpecial) +// Holding worldsema grants an M the right to try to stop the world. +// The procedure is: +// +// runtime_semacquire(&runtime_worldsema); +// m->gcing = 1; +// runtime_stoptheworld(); +// +// ... do stuff ... +// +// m->gcing = 0; +// runtime_semrelease(&runtime_worldsema); +// runtime_starttheworld(); +// +uint32 runtime_worldsema = 1; + // TODO: Make these per-M. static uint64 nhandoff; @@ -92,7 +107,6 @@ struct FinBlock Finalizer fin[1]; }; - static G *fing; static FinBlock *finq; // list of finalizers that are to be executed static FinBlock *finc; // cache of free blocks @@ -778,9 +792,11 @@ sweep(void) byte *p; MCache *c; byte *arena_start; + int64 now; m = runtime_m(); arena_start = runtime_mheap.arena_start; + now = runtime_nanotime(); for(;;) { s = work.spans; @@ -789,6 +805,11 @@ sweep(void) if(!runtime_casp(&work.spans, s, s->allnext)) continue; + // Stamp newly unused spans. The scavenger will use that + // info to potentially give back some pages to the OS. + if(s->state == MSpanFree && s->unusedsince == 0) + s->unusedsince = now; + if(s->state != MSpanInUse) continue; @@ -875,11 +896,6 @@ runtime_gchelper(void) runtime_notewakeup(&work.alldone); } -// Semaphore, not Lock, so that the goroutine -// reschedules when there is contention rather -// than spinning. -static uint32 gcsema = 1; - // Initialized from $GOGC. GOGC=off means no gc. // // Next gc is after we've allocated an extra amount of @@ -968,9 +984,9 @@ runtime_gc(int32 force) if(gcpercent < 0) return; - runtime_semacquire(&gcsema); + runtime_semacquire(&runtime_worldsema); if(!force && mstats.heap_alloc < mstats.next_gc) { - runtime_semrelease(&gcsema); + runtime_semrelease(&runtime_worldsema); return; } @@ -1032,6 +1048,7 @@ runtime_gc(int32 force) obj1 = mstats.nmalloc - mstats.nfree; t3 = runtime_nanotime(); + mstats.last_gc = t3; mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t3 - t0; mstats.pause_total_ns += t3 - t0; mstats.numgc++; @@ -1045,8 +1062,9 @@ runtime_gc(int32 force) (unsigned long long) mstats.nmalloc, (unsigned long long)mstats.nfree, (unsigned long long) nhandoff); } - - runtime_semrelease(&gcsema); + + runtime_MProf_GC(); + runtime_semrelease(&runtime_worldsema); // If we could have used another helper proc, start one now, // in the hope that it will be available next time. @@ -1073,18 +1091,18 @@ runtime_ReadMemStats(MStats *stats) { M *m; - // Have to acquire gcsema to stop the world, + // Have to acquire worldsema to stop the world, // because stoptheworld can only be used by // one goroutine at a time, and there might be // a pending garbage collection already calling it. - runtime_semacquire(&gcsema); + runtime_semacquire(&runtime_worldsema); m = runtime_m(); m->gcing = 1; runtime_stoptheworld(); cachestats(); *stats = mstats; m->gcing = 0; - runtime_semrelease(&gcsema); + runtime_semrelease(&runtime_worldsema); runtime_starttheworld(false); } diff --git a/libgo/runtime/mheap.c b/libgo/runtime/mheap.c index 5a5a1e71a12..79359d9dfca 100644 --- a/libgo/runtime/mheap.c +++ b/libgo/runtime/mheap.c @@ -103,6 +103,8 @@ HaveSpan: runtime_MSpanList_Remove(s); s->state = MSpanInUse; mstats.heap_idle -= s->npages<<PageShift; + mstats.heap_released -= s->npreleased<<PageShift; + s->npreleased = 0; if(s->npages > npage) { // Trim extra and put it back in the heap. @@ -280,6 +282,8 @@ MHeap_FreeLocked(MHeap *h, MSpan *s) } mstats.heap_idle += s->npages<<PageShift; s->state = MSpanFree; + s->unusedsince = 0; + s->npreleased = 0; runtime_MSpanList_Remove(s); sp = (uintptr*)(s->start<<PageShift); @@ -292,6 +296,7 @@ MHeap_FreeLocked(MHeap *h, MSpan *s) *tp |= *sp; // propagate "needs zeroing" mark s->start = t->start; s->npages += t->npages; + s->npreleased = t->npreleased; // absorb released pages p -= t->npages; h->map[p] = s; runtime_MSpanList_Remove(t); @@ -304,6 +309,7 @@ MHeap_FreeLocked(MHeap *h, MSpan *s) tp = (uintptr*)(t->start<<PageShift); *sp |= *tp; // propagate "needs zeroing" mark s->npages += t->npages; + s->npreleased += t->npreleased; h->map[p + s->npages - 1] = s; runtime_MSpanList_Remove(t); t->state = MSpanDead; @@ -317,8 +323,86 @@ MHeap_FreeLocked(MHeap *h, MSpan *s) runtime_MSpanList_Insert(&h->free[s->npages], s); else runtime_MSpanList_Insert(&h->large, s); +} - // TODO(rsc): IncrementalScavenge() to return memory to OS. +// Release (part of) unused memory to OS. +// Goroutine created in runtime_schedinit. +// Loop forever. +void +runtime_MHeap_Scavenger(void* dummy) +{ + MHeap *h; + MSpan *s, *list; + uint64 tick, now, forcegc, limit; + uint32 k, i; + uintptr released, sumreleased; + const byte *env; + bool trace; + Note note; + + USED(dummy); + + // If we go two minutes without a garbage collection, force one to run. + forcegc = 2*60*1e9; + // If a span goes unused for 5 minutes after a garbage collection, + // we hand it back to the operating system. + limit = 5*60*1e9; + // Make wake-up period small enough for the sampling to be correct. + if(forcegc < limit) + tick = forcegc/2; + else + tick = limit/2; + + trace = false; + env = runtime_getenv("GOGCTRACE"); + if(env != nil) + trace = runtime_atoi(env) > 0; + + h = &runtime_mheap; + for(k=0;; k++) { + runtime_noteclear(¬e); + runtime_entersyscall(); + runtime_notetsleep(¬e, tick); + runtime_exitsyscall(); + + runtime_lock(h); + now = runtime_nanotime(); + if(now - mstats.last_gc > forcegc) { + runtime_unlock(h); + runtime_gc(1); + runtime_lock(h); + now = runtime_nanotime(); + if (trace) + runtime_printf("scvg%d: GC forced\n", k); + } + sumreleased = 0; + for(i=0; i < nelem(h->free)+1; i++) { + if(i < nelem(h->free)) + list = &h->free[i]; + else + list = &h->large; + if(runtime_MSpanList_IsEmpty(list)) + continue; + for(s=list->next; s != list; s=s->next) { + if(s->unusedsince != 0 && (now - s->unusedsince) > limit) { + released = (s->npages - s->npreleased) << PageShift; + mstats.heap_released += released; + sumreleased += released; + s->npreleased = s->npages; + runtime_SysUnused((void*)(s->start << PageShift), s->npages << PageShift); + } + } + } + runtime_unlock(h); + + if(trace) { + if(sumreleased > 0) + runtime_printf("scvg%d: %p MB released\n", k, (void*)(sumreleased>>20)); + runtime_printf("scvg%d: inuse: %lld, idle: %lld, sys: %lld, released: %lld, consumed: %lld (MB)\n", + k, (long long)(mstats.heap_inuse>>20), (long long)(mstats.heap_idle>>20), (long long)(mstats.heap_sys>>20), + (long long)(mstats.heap_released>>20), (long long)((mstats.heap_sys - mstats.heap_released)>>20)); + } + } } // Initialize a new span with the given start and npages. @@ -333,6 +417,8 @@ runtime_MSpan_Init(MSpan *span, PageID start, uintptr npages) span->ref = 0; span->sizeclass = 0; span->state = 0; + span->unusedsince = 0; + span->npreleased = 0; } // Initialize an empty doubly-linked list. diff --git a/libgo/runtime/mprof.goc b/libgo/runtime/mprof.goc index 95e05fabeb3..e40dd61f784 100644 --- a/libgo/runtime/mprof.goc +++ b/libgo/runtime/mprof.goc @@ -26,6 +26,10 @@ struct Bucket uintptr frees; uintptr alloc_bytes; uintptr free_bytes; + uintptr recent_allocs; // since last gc + uintptr recent_frees; + uintptr recent_alloc_bytes; + uintptr recent_free_bytes; uintptr hash; uintptr nstk; uintptr stk[1]; @@ -39,7 +43,7 @@ static uintptr bucketmem; // Return the bucket for stk[0:nstk], allocating new bucket if needed. static Bucket* -stkbucket(uintptr *stk, int32 nstk) +stkbucket(uintptr *stk, int32 nstk, bool alloc) { int32 i; uintptr h; @@ -66,6 +70,9 @@ stkbucket(uintptr *stk, int32 nstk) runtime_mcmp((byte*)b->stk, (byte*)stk, nstk*sizeof stk[0]) == 0) return b; + if(!alloc) + return nil; + b = runtime_mallocgc(sizeof *b + nstk*sizeof stk[0], FlagNoProfiling, 0, 1); bucketmem += sizeof *b + nstk*sizeof stk[0]; runtime_memmove(b->stk, stk, nstk*sizeof stk[0]); @@ -78,6 +85,26 @@ stkbucket(uintptr *stk, int32 nstk) return b; } +// Record that a gc just happened: all the 'recent' statistics are now real. +void +runtime_MProf_GC(void) +{ + Bucket *b; + + runtime_lock(&proflock); + for(b=buckets; b; b=b->allnext) { + b->allocs += b->recent_allocs; + b->frees += b->recent_frees; + b->alloc_bytes += b->recent_alloc_bytes; + b->free_bytes += b->recent_free_bytes; + b->recent_allocs = 0; + b->recent_frees = 0; + b->recent_alloc_bytes = 0; + b->recent_free_bytes = 0; + } + runtime_unlock(&proflock); +} + // Map from pointer to Bucket* that allocated it. // Three levels: // Linked-list hash table for top N-20 bits. @@ -204,9 +231,9 @@ runtime_MProf_Malloc(void *p, uintptr size) nstk = 0; #endif runtime_lock(&proflock); - b = stkbucket(stk, nstk); - b->allocs++; - b->alloc_bytes += size; + b = stkbucket(stk, nstk, true); + b->recent_allocs++; + b->recent_alloc_bytes += size; setaddrbucket((uintptr)p, b); runtime_unlock(&proflock); m = runtime_m(); @@ -228,8 +255,8 @@ runtime_MProf_Free(void *p, uintptr size) runtime_lock(&proflock); b = getaddrbucket((uintptr)p); if(b != nil) { - b->frees++; - b->free_bytes += size; + b->recent_frees++; + b->recent_free_bytes += size; } runtime_unlock(&proflock); m = runtime_m(); @@ -293,13 +320,13 @@ runtime_MProf_Mark(void (*scan)(byte *, int64)) scan((byte*)&addrfree, sizeof addrfree); } -// Must match ThreadProfileRecord in debug.go. +// Must match StackRecord in debug.go. typedef struct TRecord TRecord; struct TRecord { uintptr stk[32]; }; -func ThreadProfile(p Slice) (n int32, ok bool) { +func ThreadCreateProfile(p Slice) (n int32, ok bool) { TRecord *r; M *first, *m; @@ -317,3 +344,89 @@ func ThreadProfile(p Slice) (n int32, ok bool) { } } } + +func Stack(b Slice, all bool) (n int32) { + byte *pc, *sp; + + sp = runtime_getcallersp(&b); + pc = runtime_getcallerpc(&b); + + if(all) { + runtime_semacquire(&runtime_worldsema); + runtime_m()->gcing = 1; + runtime_stoptheworld(); + } + + if(b.__count == 0) + n = 0; + else{ + G* g = runtime_g(); + g->writebuf = (byte*)b.__values; + g->writenbuf = b.__count; + USED(pc); + USED(sp); + // runtime_goroutineheader(g); + // runtime_traceback(pc, sp, 0, g); + // if(all) + // runtime_tracebackothers(g); + n = b.__count - g->writenbuf; + g->writebuf = nil; + g->writenbuf = 0; + } + + if(all) { + runtime_m()->gcing = 0; + runtime_semrelease(&runtime_worldsema); + runtime_starttheworld(false); + } +} + +static void +saveg(byte *pc, byte *sp, G *g, TRecord *r) +{ + int32 n; + + USED(pc); + USED(sp); + USED(g); + // n = runtime_gentraceback(pc, sp, 0, g, 0, r->stk, nelem(r->stk)); + n = 0; + if((size_t)n < nelem(r->stk)) + r->stk[n] = 0; +} + +func GoroutineProfile(b Slice) (n int32, ok bool) { + byte *pc, *sp; + TRecord *r; + G *gp; + + sp = runtime_getcallersp(&b); + pc = runtime_getcallerpc(&b); + + ok = false; + n = runtime_gcount(); + if(n <= b.__count) { + runtime_semacquire(&runtime_worldsema); + runtime_m()->gcing = 1; + runtime_stoptheworld(); + + n = runtime_gcount(); + if(n <= b.__count) { + G* g = runtime_g(); + ok = true; + r = (TRecord*)b.__values; + saveg(pc, sp, g, r++); + for(gp = runtime_allg; gp != nil; gp = gp->alllink) { + if(gp == g || gp->status == Gdead) + continue; + //saveg(gp->sched.pc, gp->sched.sp, gp, r++); + r++; + } + } + + runtime_m()->gcing = 0; + runtime_semrelease(&runtime_worldsema); + runtime_starttheworld(false); + } +} + diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c index a4e4588299e..d0ae09c45a0 100644 --- a/libgo/runtime/proc.c +++ b/libgo/runtime/proc.c @@ -362,6 +362,9 @@ runtime_mcall(void (*pfn)(G*)) } } +// Keep trace of scavenger's goroutine for deadlock detection. +static G *scvg; + // The bootstrap sequence is: // // call osinit @@ -413,6 +416,8 @@ runtime_schedinit(void) // Can not enable GC until all roots are registered. // mstats.enablegc = 1; m->nomemprof--; + + scvg = __go_go(runtime_MHeap_Scavenger, nil); } extern void main_init(void) __asm__ ("__go_init_main"); @@ -547,7 +552,7 @@ mcommoninit(M *m) // Add to runtime_allm so garbage collector doesn't free m // when it is just in a register or thread-local storage. m->alllink = runtime_allm; - // runtime_Cgocalls() iterates over allm w/o schedlock, + // runtime_NumCgoCall() iterates over allm w/o schedlock, // so we need to publish it safely. runtime_atomicstorep(&runtime_allm, m); } @@ -786,9 +791,12 @@ top: mput(m); } - v = runtime_atomicload(&runtime_sched.atomic); - if(runtime_sched.grunning == 0) - runtime_throw("all goroutines are asleep - deadlock!"); + // Look for deadlock situation: one single active g which happens to be scvg. + if(runtime_sched.grunning == 1 && runtime_sched.gwait == 0) { + if(scvg->status == Grunning || scvg->status == Gsyscall) + runtime_throw("all goroutines are asleep - deadlock!"); + } + m->nextg = nil; m->waitnextg = 1; runtime_noteclear(&m->havenextg); @@ -797,6 +805,7 @@ top: // Entersyscall might have decremented mcpu too, but if so // it will see the waitstop and take the slow path. // Exitsyscall never increments mcpu beyond mcpumax. + v = runtime_atomicload(&runtime_sched.atomic); if(atomic_waitstop(v) && atomic_mcpu(v) <= atomic_mcpumax(v)) { // set waitstop = 0 (known to be 1) runtime_xadd(&runtime_sched.atomic, -1<<waitstopShift); @@ -1472,11 +1481,17 @@ runtime_mid() return m->id; } -int32 runtime_Goroutines (void) - __asm__ ("libgo_runtime.runtime.Goroutines"); +int32 runtime_NumGoroutine (void) + __asm__ ("libgo_runtime.runtime.NumGoroutine"); + +int32 +runtime_NumGoroutine() +{ + return runtime_sched.gcount; +} int32 -runtime_Goroutines() +runtime_gcount(void) { return runtime_sched.gcount; } diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h index 713af174491..113bb7163c5 100644 --- a/libgo/runtime/runtime.h +++ b/libgo/runtime/runtime.h @@ -143,6 +143,8 @@ struct G M* lockedm; M* idlem; // int32 sig; + int32 writenbuf; + byte* writebuf; // uintptr sigcode0; // uintptr sigcode1; // uintptr sigpc; @@ -189,9 +191,9 @@ struct SigTab enum { SigNotify = 1<<0, // let signal.Notify have signal, even if from kernel - SigKill = 1<<1, // if signal.Notify doesn't take it, exit quietly - SigThrow = 1<<2, // if signal.Notify doesn't take it, exit loudly - SigPanic = 1<<3, // if the signal is from the kernel, panic + SigKill = 1<<1, // if signal.Notify doesn't take it, exit quietly + SigThrow = 1<<2, // if signal.Notify doesn't take it, exit loudly + SigPanic = 1<<3, // if the signal is from the kernel, panic SigDefault = 1<<4, // if the signal isn't explicitly requested, don't monitor it }; @@ -277,6 +279,7 @@ void runtime_panicstring(const char*) __attribute__ ((noreturn)); void* runtime_mal(uintptr); void runtime_schedinit(void); void runtime_initsig(void); +void runtime_sigenable(uint32 sig); String runtime_gostringnocopy(const byte*); void* runtime_mstart(void*); G* runtime_malg(int32, byte**, size_t*); @@ -296,6 +299,7 @@ int64 runtime_cputicks(void); void runtime_stoptheworld(void); void runtime_starttheworld(bool); +extern uint32 runtime_worldsema; G* __go_go(void (*pfn)(void*), void*); /* @@ -348,6 +352,7 @@ void runtime_futexwakeup(uint32*, uint32); #define runtime_munmap munmap #define runtime_madvise madvise #define runtime_memclr(buf, size) __builtin_memset((buf), 0, (size)) +#define runtime_getcallerpc(p) __builtin_return_address(0) #ifdef __rtems__ void __wrap_rtems_task_variable_add(void **); @@ -373,8 +378,6 @@ void reflect_call(const struct __go_func_type *, const void *, _Bool, _Bool, #define runtime_exit(s) exit(s) MCache* runtime_allocmcache(void); void free(void *v); -struct __go_func_type; -bool runtime_addfinalizer(void*, void(*fn)(void*), const struct __go_func_type *); #define runtime_cas(pval, old, new) __sync_bool_compare_and_swap (pval, old, new) #define runtime_casp(pval, old, new) __sync_bool_compare_and_swap (pval, old, new) #define runtime_xadd(p, v) __sync_add_and_fetch (p, v) @@ -384,6 +387,11 @@ bool runtime_addfinalizer(void*, void(*fn)(void*), const struct __go_func_type * #define runtime_atomicloadp(p) __atomic_load_n (p, __ATOMIC_SEQ_CST) #define runtime_atomicstorep(p, v) __atomic_store_n (p, v, __ATOMIC_SEQ_CST) +struct __go_func_type; +bool runtime_addfinalizer(void*, void(*fn)(void*), const struct __go_func_type *); +#define runtime_getcallersp(p) __builtin_frame_address(1) +int32 runtime_mcount(void); +int32 runtime_gcount(void); void runtime_dopanic(int32) __attribute__ ((noreturn)); void runtime_startpanic(void); void runtime_ready(G*); diff --git a/libgo/runtime/sema.goc b/libgo/runtime/sema.goc index dd58cf38fb8..ff9c4f2e19e 100644 --- a/libgo/runtime/sema.goc +++ b/libgo/runtime/sema.goc @@ -17,7 +17,7 @@ // See Mullender and Cox, ``Semaphores in Plan 9,'' // http://swtch.com/semaphore.pdf -package runtime +package sync #include "runtime.h" #include "arch.h" @@ -172,10 +172,10 @@ runtime_semrelease(uint32 volatile *addr) runtime_ready(s->g); } -func Semacquire(addr *uint32) { +func runtime_Semacquire(addr *uint32) { runtime_semacquire(addr); } -func Semrelease(addr *uint32) { +func runtime_Semrelease(addr *uint32) { runtime_semrelease(addr); } diff --git a/libgo/runtime/sigqueue.goc b/libgo/runtime/sigqueue.goc index c550a4ebe9b..be7c5920cbc 100644 --- a/libgo/runtime/sigqueue.goc +++ b/libgo/runtime/sigqueue.goc @@ -142,10 +142,12 @@ func signal_enable(s uint32) { // Special case: want everything. for(i=0; (size_t)i<nelem(sig.wanted); i++) sig.wanted[i] = ~(uint32)0; + runtime_sigenable(s); return; } if(s >= nelem(sig.wanted)*32) return; sig.wanted[s/32] |= 1U<<(s&31); + runtime_sigenable(s); } |