libgo: Update to weekly.2012-03-13.
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 30 Mar 2012 21:27:11 +0000 (21:27 +0000)
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 30 Mar 2012 21:27:11 +0000 (21:27 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@186023 138bc75d-0d04-0410-961f-82ee72b054a4

137 files changed:
libgo/MERGE
libgo/Makefile.am
libgo/Makefile.in
libgo/go/archive/tar/reader.go
libgo/go/archive/tar/writer.go
libgo/go/archive/tar/writer_test.go
libgo/go/archive/zip/reader.go
libgo/go/archive/zip/reader_test.go
libgo/go/archive/zip/struct.go
libgo/go/archive/zip/testdata/crc32-not-streamed.zip [new file with mode: 0644]
libgo/go/archive/zip/testdata/go-no-datadesc-sig.zip [new file with mode: 0644]
libgo/go/archive/zip/testdata/go-with-datadesc-sig.zip [new file with mode: 0644]
libgo/go/archive/zip/writer.go
libgo/go/archive/zip/writer_test.go
libgo/go/crypto/tls/common.go
libgo/go/crypto/tls/handshake_client.go
libgo/go/crypto/tls/handshake_server_test.go
libgo/go/crypto/tls/root_test.go
libgo/go/crypto/tls/root_windows.go [deleted file]
libgo/go/crypto/tls/tls.go
libgo/go/crypto/x509/pkcs1.go
libgo/go/crypto/x509/root.go [new file with mode: 0644]
libgo/go/crypto/x509/root_darwin.go [moved from libgo/go/crypto/tls/root_darwin.go with 90% similarity]
libgo/go/crypto/x509/root_stub.go [moved from libgo/go/crypto/tls/root_stub.go with 51% similarity]
libgo/go/crypto/x509/root_unix.go [moved from libgo/go/crypto/tls/root_unix.go with 76% similarity]
libgo/go/crypto/x509/root_windows.go [new file with mode: 0644]
libgo/go/crypto/x509/verify.go
libgo/go/crypto/x509/verify_test.go
libgo/go/crypto/x509/x509.go
libgo/go/database/sql/driver/driver.go
libgo/go/database/sql/fakedb_test.go
libgo/go/database/sql/sql.go
libgo/go/database/sql/sql_test.go
libgo/go/encoding/asn1/asn1.go
libgo/go/encoding/asn1/asn1_test.go
libgo/go/encoding/asn1/common.go
libgo/go/encoding/asn1/marshal.go
libgo/go/encoding/asn1/marshal_test.go
libgo/go/encoding/binary/binary.go
libgo/go/encoding/csv/reader.go
libgo/go/encoding/gob/decode.go
libgo/go/encoding/gob/encoder_test.go
libgo/go/encoding/gob/gobencdec_test.go
libgo/go/encoding/json/encode.go
libgo/go/exp/norm/maketables.go
libgo/go/exp/wingui/gui.go [deleted file]
libgo/go/exp/wingui/winapi.go [deleted file]
libgo/go/exp/wingui/zwinapi.go [deleted file]
libgo/go/expvar/expvar.go
libgo/go/fmt/doc.go
libgo/go/fmt/export_test.go [new file with mode: 0644]
libgo/go/fmt/fmt_test.go
libgo/go/fmt/format.go
libgo/go/fmt/print.go
libgo/go/fmt/scan.go
libgo/go/go/build/build.go
libgo/go/go/build/deps_test.go [new file with mode: 0644]
libgo/go/go/parser/error_test.go [new file with mode: 0644]
libgo/go/go/parser/parser.go
libgo/go/go/parser/parser_test.go
libgo/go/go/parser/short_test.go [new file with mode: 0644]
libgo/go/go/parser/testdata/commas.src [new file with mode: 0644]
libgo/go/go/parser/testdata/issue3106.src [new file with mode: 0644]
libgo/go/go/printer/nodes.go
libgo/go/go/printer/testdata/statements.golden
libgo/go/go/printer/testdata/statements.input
libgo/go/go/scanner/scanner.go
libgo/go/html/template/doc.go
libgo/go/io/io.go
libgo/go/log/log.go
libgo/go/net/dial_test.go
libgo/go/net/dnsclient.go
libgo/go/net/dnsmsg.go
libgo/go/net/dnsmsg_test.go
libgo/go/net/fd_linux.go
libgo/go/net/file_test.go
libgo/go/net/http/client_test.go
libgo/go/net/http/request.go
libgo/go/net/http/request_test.go
libgo/go/net/http/server.go
libgo/go/net/http/transport.go
libgo/go/net/http/transport_test.go
libgo/go/net/interface.go
libgo/go/net/interface_linux.go
libgo/go/net/iprawsock_posix.go
libgo/go/net/ipsock_posix.go
libgo/go/net/mac.go
libgo/go/net/mac_test.go
libgo/go/net/mail/message.go
libgo/go/net/multicast_test.go
libgo/go/net/net.go
libgo/go/net/net_test.go
libgo/go/net/parse_test.go
libgo/go/net/rpc/client.go
libgo/go/net/server_test.go
libgo/go/net/sock.go
libgo/go/net/sockopt.go
libgo/go/net/sockopt_bsd.go
libgo/go/net/sockopt_linux.go
libgo/go/net/sockopt_windows.go
libgo/go/net/tcpsock_posix.go
libgo/go/net/timeout_test.go
libgo/go/net/udp_test.go
libgo/go/net/udpsock_posix.go
libgo/go/net/unicast_test.go
libgo/go/net/unixsock_posix.go
libgo/go/os/error_posix.go
libgo/go/os/error_test.go [new file with mode: 0644]
libgo/go/os/error_windows.go [new file with mode: 0644]
libgo/go/os/exec/exec.go
libgo/go/os/types.go
libgo/go/path/filepath/path.go
libgo/go/path/filepath/path_test.go
libgo/go/path/filepath/symlink.go [new file with mode: 0644]
libgo/go/path/filepath/symlink_windows.go [new file with mode: 0644]
libgo/go/reflect/type.go
libgo/go/reflect/value.go
libgo/go/runtime/compiler.go [new file with mode: 0644]
libgo/go/runtime/debug/stack_test.go
libgo/go/runtime/pprof/pprof_test.go
libgo/go/strconv/isprint.go [new file with mode: 0644]
libgo/go/strconv/makeisprint.go [new file with mode: 0644]
libgo/go/strconv/quote.go
libgo/go/strconv/quote_test.go
libgo/go/strings/example_test.go
libgo/go/sync/atomic/atomic_test.go
libgo/go/testing/testing.go
libgo/go/time/tick_test.go
libgo/go/time/time.go
libgo/go/unicode/utf16/export_test.go [new file with mode: 0644]
libgo/go/unicode/utf16/utf16.go
libgo/go/unicode/utf16/utf16_test.go
libgo/go/unicode/utf8/utf8.go
libgo/go/unicode/utf8/utf8_test.go
libgo/runtime/malloc.goc
libgo/runtime/proc.c
libgo/runtime/runtime.h

index 17d01ce..13b0438 100644 (file)
@@ -1,4 +1,4 @@
-f4470a54e6db
+3cdba7b0650c
 
 The first line of this file holds the Mercurial revision number of the
 last merge done from the master library sources.
index 99294f1..14f72ec 100644 (file)
@@ -813,6 +813,7 @@ go_net_rpc_files = \
        go/net/rpc/server.go
 
 go_runtime_files = \
+       go/runtime/compiler.go \
        go/runtime/debug.go \
        go/runtime/error.go \
        go/runtime/extern.go \
@@ -843,6 +844,7 @@ go_strconv_files = \
        go/strconv/decimal.go \
        go/strconv/extfloat.go \
        go/strconv/ftoa.go \
+       go/strconv/isprint.go \
        go/strconv/itoa.go \
        go/strconv/quote.go
 
@@ -1000,12 +1002,13 @@ go_crypto_tls_files = \
        go/crypto/tls/handshake_server.go \
        go/crypto/tls/key_agreement.go \
        go/crypto/tls/prf.go \
-       go/crypto/tls/root_unix.go \
        go/crypto/tls/tls.go
 go_crypto_x509_files = \
        go/crypto/x509/cert_pool.go \
        go/crypto/x509/pkcs1.go \
        go/crypto/x509/pkcs8.go \
+       go/crypto/x509/root.go \
+       go/crypto/x509/root_unix.go \
        go/crypto/x509/verify.go \
        go/crypto/x509/x509.go
 
@@ -1320,7 +1323,8 @@ go_os_user_files = \
 go_path_filepath_files = \
        go/path/filepath/match.go \
        go/path/filepath/path.go \
-       go/path/filepath/path_unix.go
+       go/path/filepath/path_unix.go \
+       go/path/filepath/symlink.go
 
 go_regexp_syntax_files = \
        go/regexp/syntax/compile.go \
index b57d929..720d57e 100644 (file)
@@ -1131,6 +1131,7 @@ go_net_rpc_files = \
        go/net/rpc/server.go
 
 go_runtime_files = \
+       go/runtime/compiler.go \
        go/runtime/debug.go \
        go/runtime/error.go \
        go/runtime/extern.go \
@@ -1150,6 +1151,7 @@ go_strconv_files = \
        go/strconv/decimal.go \
        go/strconv/extfloat.go \
        go/strconv/ftoa.go \
+       go/strconv/isprint.go \
        go/strconv/itoa.go \
        go/strconv/quote.go
 
@@ -1315,13 +1317,14 @@ go_crypto_tls_files = \
        go/crypto/tls/handshake_server.go \
        go/crypto/tls/key_agreement.go \
        go/crypto/tls/prf.go \
-       go/crypto/tls/root_unix.go \
        go/crypto/tls/tls.go
 
 go_crypto_x509_files = \
        go/crypto/x509/cert_pool.go \
        go/crypto/x509/pkcs1.go \
        go/crypto/x509/pkcs8.go \
+       go/crypto/x509/root.go \
+       go/crypto/x509/root_unix.go \
        go/crypto/x509/verify.go \
        go/crypto/x509/x509.go
 
@@ -1677,7 +1680,8 @@ go_os_user_files = \
 go_path_filepath_files = \
        go/path/filepath/match.go \
        go/path/filepath/path.go \
-       go/path/filepath/path_unix.go
+       go/path/filepath/path_unix.go \
+       go/path/filepath/symlink.go
 
 go_regexp_syntax_files = \
        go/regexp/syntax/compile.go \
index 755a730..1b40af8 100644 (file)
@@ -18,7 +18,7 @@ import (
 )
 
 var (
-       ErrHeader = errors.New("invalid tar header")
+       ErrHeader = errors.New("archive/tar: invalid tar header")
 )
 
 // A Reader provides sequential access to the contents of a tar archive.
index d35726b..b2b7a58 100644 (file)
@@ -5,18 +5,19 @@
 package tar
 
 // TODO(dsymonds):
-// - catch more errors (no first header, write after close, etc.)
+// - catch more errors (no first header, etc.)
 
 import (
        "errors"
+       "fmt"
        "io"
        "strconv"
 )
 
 var (
-       ErrWriteTooLong    = errors.New("write too long")
-       ErrFieldTooLong    = errors.New("header field too long")
-       ErrWriteAfterClose = errors.New("write after close")
+       ErrWriteTooLong    = errors.New("archive/tar: write too long")
+       ErrFieldTooLong    = errors.New("archive/tar: header field too long")
+       ErrWriteAfterClose = errors.New("archive/tar: write after close")
 )
 
 // A Writer provides sequential writing of a tar archive in POSIX.1 format.
@@ -48,6 +49,11 @@ func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
 
 // Flush finishes writing the current file (optional).
 func (tw *Writer) Flush() error {
+       if tw.nb > 0 {
+               tw.err = fmt.Errorf("archive/tar: missed writing %d bytes", tw.nb)
+               return tw.err
+       }
+
        n := tw.nb + tw.pad
        for n > 0 && tw.err == nil {
                nr := n
@@ -193,6 +199,9 @@ func (tw *Writer) Close() error {
        }
        tw.Flush()
        tw.closed = true
+       if tw.err != nil {
+               return tw.err
+       }
 
        // trailer: two zero blocks
        for i := 0; i < 2; i++ {
index 0b41372..a214e57 100644 (file)
@@ -9,6 +9,7 @@ import (
        "fmt"
        "io"
        "io/ioutil"
+       "strings"
        "testing"
        "testing/iotest"
        "time"
@@ -95,7 +96,8 @@ var writerTests = []*writerTest{
                                        Uname:    "dsymonds",
                                        Gname:    "eng",
                                },
-                               // no contents
+                               // fake contents
+                               contents: strings.Repeat("\x00", 4<<10),
                        },
                },
        },
@@ -150,7 +152,9 @@ testLoop:
 
                buf := new(bytes.Buffer)
                tw := NewWriter(iotest.TruncateWriter(buf, 4<<10)) // only catch the first 4 KB
+               big := false
                for j, entry := range test.entries {
+                       big = big || entry.header.Size > 1<<10
                        if err := tw.WriteHeader(entry.header); err != nil {
                                t.Errorf("test %d, entry %d: Failed writing header: %v", i, j, err)
                                continue testLoop
@@ -160,7 +164,8 @@ testLoop:
                                continue testLoop
                        }
                }
-               if err := tw.Close(); err != nil {
+               // Only interested in Close failures for the small tests.
+               if err := tw.Close(); err != nil && !big {
                        t.Errorf("test %d: Failed closing archive: %v", i, err)
                        continue testLoop
                }
index f3826dc..ddd5075 100644 (file)
@@ -124,10 +124,6 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
                return
        }
        size := int64(f.CompressedSize)
-       if size == 0 && f.hasDataDescriptor() {
-               // permit SectionReader to see the rest of the file
-               size = f.zipsize - (f.headerOffset + bodyOffset)
-       }
        r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size)
        switch f.Method {
        case Store: // (no compression)
@@ -136,10 +132,13 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
                rc = flate.NewReader(r)
        default:
                err = ErrAlgorithm
+               return
        }
-       if rc != nil {
-               rc = &checksumReader{rc, crc32.NewIEEE(), f, r}
+       var desr io.Reader
+       if f.hasDataDescriptor() {
+               desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen)
        }
+       rc = &checksumReader{rc, crc32.NewIEEE(), f, desr, nil}
        return
 }
 
@@ -147,23 +146,36 @@ type checksumReader struct {
        rc   io.ReadCloser
        hash hash.Hash32
        f    *File
-       zipr io.Reader // for reading the data descriptor
+       desr io.Reader // if non-nil, where to read the data descriptor
+       err  error     // sticky error
 }
 
 func (r *checksumReader) Read(b []byte) (n int, err error) {
+       if r.err != nil {
+               return 0, r.err
+       }
        n, err = r.rc.Read(b)
        r.hash.Write(b[:n])
-       if err != io.EOF {
+       if err == nil {
                return
        }
-       if r.f.hasDataDescriptor() {
-               if err = readDataDescriptor(r.zipr, r.f); err != nil {
-                       return
+       if err == io.EOF {
+               if r.desr != nil {
+                       if err1 := readDataDescriptor(r.desr, r.f); err1 != nil {
+                               err = err1
+                       } else if r.hash.Sum32() != r.f.CRC32 {
+                               err = ErrChecksum
+                       }
+               } else {
+                       // If there's not a data descriptor, we still compare
+                       // the CRC32 of what we've read against the file header
+                       // or TOC's CRC32, if it seems like it was set.
+                       if r.f.CRC32 != 0 && r.hash.Sum32() != r.f.CRC32 {
+                               err = ErrChecksum
+                       }
                }
        }
-       if r.hash.Sum32() != r.f.CRC32 {
-               err = ErrChecksum
-       }
+       r.err = err
        return
 }
 
@@ -226,10 +238,31 @@ func readDirectoryHeader(f *File, r io.Reader) error {
 
 func readDataDescriptor(r io.Reader, f *File) error {
        var buf [dataDescriptorLen]byte
-       if _, err := io.ReadFull(r, buf[:]); err != nil {
+
+       // The spec says: "Although not originally assigned a
+       // signature, the value 0x08074b50 has commonly been adopted
+       // as a signature value for the data descriptor record.
+       // Implementers should be aware that ZIP files may be
+       // encountered with or without this signature marking data
+       // descriptors and should account for either case when reading
+       // ZIP files to ensure compatibility."
+       //
+       // dataDescriptorLen includes the size of the signature but
+       // first read just those 4 bytes to see if it exists.
+       if _, err := io.ReadFull(r, buf[:4]); err != nil {
                return err
        }
-       b := readBuf(buf[:])
+       off := 0
+       maybeSig := readBuf(buf[:4])
+       if maybeSig.uint32() != dataDescriptorSignature {
+               // No data descriptor signature. Keep these four
+               // bytes.
+               off += 4
+       }
+       if _, err := io.ReadFull(r, buf[off:12]); err != nil {
+               return err
+       }
+       b := readBuf(buf[:12])
        f.CRC32 = b.uint32()
        f.CompressedSize = b.uint32()
        f.UncompressedSize = b.uint32()
index 066a615..c2db0dc 100644 (file)
@@ -10,23 +10,26 @@ import (
        "io"
        "io/ioutil"
        "os"
+       "path/filepath"
        "testing"
        "time"
 )
 
 type ZipTest struct {
        Name    string
+       Source  func() (r io.ReaderAt, size int64) // if non-nil, used instead of testdata/<Name> file
        Comment string
        File    []ZipTestFile
        Error   error // the error that Opening this file should return
 }
 
 type ZipTestFile struct {
-       Name    string
-       Content []byte // if blank, will attempt to compare against File
-       File    string // name of file to compare to (relative to testdata/)
-       Mtime   string // modified time in format "mm-dd-yy hh:mm:ss"
-       Mode    os.FileMode
+       Name       string
+       Content    []byte // if blank, will attempt to compare against File
+       ContentErr error
+       File       string // name of file to compare to (relative to testdata/)
+       Mtime      string // modified time in format "mm-dd-yy hh:mm:ss"
+       Mode       os.FileMode
 }
 
 // Caution: The Mtime values found for the test files should correspond to
@@ -107,6 +110,99 @@ var tests = []ZipTest{
                Name: "unix.zip",
                File: crossPlatform,
        },
+       {
+               // created by Go, before we wrote the "optional" data
+               // descriptor signatures (which are required by OS X)
+               Name: "go-no-datadesc-sig.zip",
+               File: []ZipTestFile{
+                       {
+                               Name:    "foo.txt",
+                               Content: []byte("foo\n"),
+                               Mtime:   "03-08-12 16:59:10",
+                               Mode:    0644,
+                       },
+                       {
+                               Name:    "bar.txt",
+                               Content: []byte("bar\n"),
+                               Mtime:   "03-08-12 16:59:12",
+                               Mode:    0644,
+                       },
+               },
+       },
+       {
+               // created by Go, after we wrote the "optional" data
+               // descriptor signatures (which are required by OS X)
+               Name: "go-with-datadesc-sig.zip",
+               File: []ZipTestFile{
+                       {
+                               Name:    "foo.txt",
+                               Content: []byte("foo\n"),
+                               Mode:    0666,
+                       },
+                       {
+                               Name:    "bar.txt",
+                               Content: []byte("bar\n"),
+                               Mode:    0666,
+                       },
+               },
+       },
+       {
+               Name:   "Bad-CRC32-in-data-descriptor",
+               Source: returnCorruptCRC32Zip,
+               File: []ZipTestFile{
+                       {
+                               Name:       "foo.txt",
+                               Content:    []byte("foo\n"),
+                               Mode:       0666,
+                               ContentErr: ErrChecksum,
+                       },
+                       {
+                               Name:    "bar.txt",
+                               Content: []byte("bar\n"),
+                               Mode:    0666,
+                       },
+               },
+       },
+       // Tests that we verify (and accept valid) crc32s on files
+       // with crc32s in their file header (not in data descriptors)
+       {
+               Name: "crc32-not-streamed.zip",
+               File: []ZipTestFile{
+                       {
+                               Name:    "foo.txt",
+                               Content: []byte("foo\n"),
+                               Mtime:   "03-08-12 16:59:10",
+                               Mode:    0644,
+                       },
+                       {
+                               Name:    "bar.txt",
+                               Content: []byte("bar\n"),
+                               Mtime:   "03-08-12 16:59:12",
+                               Mode:    0644,
+                       },
+               },
+       },
+       // Tests that we verify (and reject invalid) crc32s on files
+       // with crc32s in their file header (not in data descriptors)
+       {
+               Name:   "crc32-not-streamed.zip",
+               Source: returnCorruptNotStreamedZip,
+               File: []ZipTestFile{
+                       {
+                               Name:       "foo.txt",
+                               Content:    []byte("foo\n"),
+                               Mtime:      "03-08-12 16:59:10",
+                               Mode:       0644,
+                               ContentErr: ErrChecksum,
+                       },
+                       {
+                               Name:    "bar.txt",
+                               Content: []byte("bar\n"),
+                               Mtime:   "03-08-12 16:59:12",
+                               Mode:    0644,
+                       },
+               },
+       },
 }
 
 var crossPlatform = []ZipTestFile{
@@ -139,7 +235,18 @@ func TestReader(t *testing.T) {
 }
 
 func readTestZip(t *testing.T, zt ZipTest) {
-       z, err := OpenReader("testdata/" + zt.Name)
+       var z *Reader
+       var err error
+       if zt.Source != nil {
+               rat, size := zt.Source()
+               z, err = NewReader(rat, size)
+       } else {
+               var rc *ReadCloser
+               rc, err = OpenReader(filepath.Join("testdata", zt.Name))
+               if err == nil {
+                       z = &rc.Reader
+               }
+       }
        if err != zt.Error {
                t.Errorf("error=%v, want %v", err, zt.Error)
                return
@@ -149,11 +256,6 @@ func readTestZip(t *testing.T, zt ZipTest) {
        if err == ErrFormat {
                return
        }
-       defer func() {
-               if err := z.Close(); err != nil {
-                       t.Errorf("error %q when closing zip file", err)
-               }
-       }()
 
        // bail here if no Files expected to be tested
        // (there may actually be files in the zip, but we don't care)
@@ -170,7 +272,7 @@ func readTestZip(t *testing.T, zt ZipTest) {
 
        // test read of each file
        for i, ft := range zt.File {
-               readTestFile(t, ft, z.File[i])
+               readTestFile(t, zt, ft, z.File[i])
        }
 
        // test simultaneous reads
@@ -179,7 +281,7 @@ func readTestZip(t *testing.T, zt ZipTest) {
        for i := 0; i < 5; i++ {
                for j, ft := range zt.File {
                        go func(j int, ft ZipTestFile) {
-                               readTestFile(t, ft, z.File[j])
+                               readTestFile(t, zt, ft, z.File[j])
                                done <- true
                        }(j, ft)
                        n++
@@ -188,26 +290,11 @@ func readTestZip(t *testing.T, zt ZipTest) {
        for ; n > 0; n-- {
                <-done
        }
-
-       // test invalid checksum
-       if !z.File[0].hasDataDescriptor() { // skip test when crc32 in dd
-               z.File[0].CRC32++ // invalidate
-               r, err := z.File[0].Open()
-               if err != nil {
-                       t.Error(err)
-                       return
-               }
-               var b bytes.Buffer
-               _, err = io.Copy(&b, r)
-               if err != ErrChecksum {
-                       t.Errorf("%s: copy error=%v, want %v", z.File[0].Name, err, ErrChecksum)
-               }
-       }
 }
 
-func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
+func readTestFile(t *testing.T, zt ZipTest, ft ZipTestFile, f *File) {
        if f.Name != ft.Name {
-               t.Errorf("name=%q, want %q", f.Name, ft.Name)
+               t.Errorf("%s: name=%q, want %q", zt.Name, f.Name, ft.Name)
        }
 
        if ft.Mtime != "" {
@@ -217,11 +304,11 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
                        return
                }
                if ft := f.ModTime(); !ft.Equal(mtime) {
-                       t.Errorf("%s: mtime=%s, want %s", f.Name, ft, mtime)
+                       t.Errorf("%s: %s: mtime=%s, want %s", zt.Name, f.Name, ft, mtime)
                }
        }
 
-       testFileMode(t, f, ft.Mode)
+       testFileMode(t, zt.Name, f, ft.Mode)
 
        size0 := f.UncompressedSize
 
@@ -237,8 +324,10 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
        }
 
        _, err = io.Copy(&b, r)
+       if err != ft.ContentErr {
+               t.Errorf("%s: copying contents: %v (want %v)", zt.Name, err, ft.ContentErr)
+       }
        if err != nil {
-               t.Error(err)
                return
        }
        r.Close()
@@ -264,12 +353,12 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
        }
 }
 
-func testFileMode(t *testing.T, f *File, want os.FileMode) {
+func testFileMode(t *testing.T, zipName string, f *File, want os.FileMode) {
        mode := f.Mode()
        if want == 0 {
-               t.Errorf("%s mode: got %v, want none", f.Name, mode)
+               t.Errorf("%s: %s mode: got %v, want none", zipName, f.Name, mode)
        } else if mode != want {
-               t.Errorf("%s mode: want %v, got %v", f.Name, want, mode)
+               t.Errorf("%s: %s mode: want %v, got %v", zipName, f.Name, want, mode)
        }
 }
 
@@ -294,3 +383,35 @@ func TestInvalidFiles(t *testing.T) {
                t.Errorf("sigs: error=%v, want %v", err, ErrFormat)
        }
 }
+
+func messWith(fileName string, corrupter func(b []byte)) (r io.ReaderAt, size int64) {
+       data, err := ioutil.ReadFile(filepath.Join("testdata", fileName))
+       if err != nil {
+               panic("Error reading " + fileName + ": " + err.Error())
+       }
+       corrupter(data)
+       return bytes.NewReader(data), int64(len(data))
+}
+
+func returnCorruptCRC32Zip() (r io.ReaderAt, size int64) {
+       return messWith("go-with-datadesc-sig.zip", func(b []byte) {
+               // Corrupt one of the CRC32s in the data descriptor:
+               b[0x2d]++
+       })
+}
+
+func returnCorruptNotStreamedZip() (r io.ReaderAt, size int64) {
+       return messWith("crc32-not-streamed.zip", func(b []byte) {
+               // Corrupt foo.txt's final crc32 byte, in both
+               // the file header and TOC. (0x7e -> 0x7f)
+               b[0x11]++
+               b[0x9d]++
+
+               // TODO(bradfitz): add a new test that only corrupts
+               // one of these values, and verify that that's also an
+               // error. Currently, the reader code doesn't verify the
+               // fileheader and TOC's crc32 match if they're both
+               // non-zero and only the second line above, the TOC,
+               // is what matters.
+       })
+}
index fdbd16d..55f3dcf 100644 (file)
@@ -27,10 +27,11 @@ const (
        fileHeaderSignature      = 0x04034b50
        directoryHeaderSignature = 0x02014b50
        directoryEndSignature    = 0x06054b50
-       fileHeaderLen            = 30 // + filename + extra
-       directoryHeaderLen       = 46 // + filename + extra + comment
-       directoryEndLen          = 22 // + comment
-       dataDescriptorLen        = 12
+       dataDescriptorSignature  = 0x08074b50 // de-facto standard; required by OS X Finder
+       fileHeaderLen            = 30         // + filename + extra
+       directoryHeaderLen       = 46         // + filename + extra + comment
+       directoryEndLen          = 22         // + comment
+       dataDescriptorLen        = 16         // four uint32: descriptor signature, crc32, compressed size, size
 
        // Constants for the first byte in CreatorVersion
        creatorFAT    = 0
diff --git a/libgo/go/archive/zip/testdata/crc32-not-streamed.zip b/libgo/go/archive/zip/testdata/crc32-not-streamed.zip
new file mode 100644 (file)
index 0000000..f268d88
Binary files /dev/null and b/libgo/go/archive/zip/testdata/crc32-not-streamed.zip differ
diff --git a/libgo/go/archive/zip/testdata/go-no-datadesc-sig.zip b/libgo/go/archive/zip/testdata/go-no-datadesc-sig.zip
new file mode 100644 (file)
index 0000000..c3d593f
Binary files /dev/null and b/libgo/go/archive/zip/testdata/go-no-datadesc-sig.zip differ
diff --git a/libgo/go/archive/zip/testdata/go-with-datadesc-sig.zip b/libgo/go/archive/zip/testdata/go-with-datadesc-sig.zip
new file mode 100644 (file)
index 0000000..bcfe121
Binary files /dev/null and b/libgo/go/archive/zip/testdata/go-with-datadesc-sig.zip differ
index b2cc55b..45eb6bd 100644 (file)
@@ -224,6 +224,7 @@ func (w *fileWriter) close() error {
        // write data descriptor
        var buf [dataDescriptorLen]byte
        b := writeBuf(buf[:])
+       b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
        b.uint32(fh.CRC32)
        b.uint32(fh.CompressedSize)
        b.uint32(fh.UncompressedSize)
index 88e5211..8b1c4df 100644 (file)
@@ -108,7 +108,7 @@ func testReadFile(t *testing.T, f *File, wt *WriteTest) {
        if f.Name != wt.Name {
                t.Fatalf("File name: got %q, want %q", f.Name, wt.Name)
        }
-       testFileMode(t, f, wt.Mode)
+       testFileMode(t, wt.Name, f, wt.Mode)
        rc, err := f.Open()
        if err != nil {
                t.Fatal("opening:", err)
index 25f7a92..4ba0bf8 100644 (file)
@@ -198,14 +198,6 @@ func (c *Config) time() time.Time {
        return t()
 }
 
-func (c *Config) rootCAs() *x509.CertPool {
-       s := c.RootCAs
-       if s == nil {
-               s = defaultRoots()
-       }
-       return s
-}
-
 func (c *Config) cipherSuites() []uint16 {
        s := c.CipherSuites
        if s == nil {
@@ -311,28 +303,16 @@ func defaultConfig() *Config {
        return &emptyConfig
 }
 
-var once sync.Once
-
-func defaultRoots() *x509.CertPool {
-       once.Do(initDefaults)
-       return varDefaultRoots
-}
+var (
+       once                   sync.Once
+       varDefaultCipherSuites []uint16
+)
 
 func defaultCipherSuites() []uint16 {
-       once.Do(initDefaults)
+       once.Do(initDefaultCipherSuites)
        return varDefaultCipherSuites
 }
 
-func initDefaults() {
-       initDefaultRoots()
-       initDefaultCipherSuites()
-}
-
-var (
-       varDefaultRoots        *x509.CertPool
-       varDefaultCipherSuites []uint16
-)
-
 func initDefaultCipherSuites() {
        varDefaultCipherSuites = make([]uint16, len(cipherSuites))
        for i, suite := range cipherSuites {
index 0d7b806..266eb8f 100644 (file)
@@ -102,7 +102,7 @@ func (c *Conn) clientHandshake() error {
 
        if !c.config.InsecureSkipVerify {
                opts := x509.VerifyOptions{
-                       Roots:         c.config.rootCAs(),
+                       Roots:         c.config.RootCAs,
                        CurrentTime:   c.config.time(),
                        DNSName:       c.config.ServerName,
                        Intermediates: x509.NewCertPool(),
index bd31d31..08a0ccb 100644 (file)
@@ -143,7 +143,7 @@ func testServerScript(t *testing.T, name string, serverScript [][]byte, config *
        if peers != nil {
                gotpeers := <-pchan
                if len(peers) == len(gotpeers) {
-                       for i, _ := range peers {
+                       for i := range peers {
                                if !peers[i].Equal(gotpeers[i]) {
                                        t.Fatalf("%s: mismatch on peer cert %d", name, i)
                                }
index 95a89d8..e61c218 100644 (file)
@@ -5,25 +5,25 @@
 package tls
 
 import (
+       "crypto/x509"
+       "runtime"
        "testing"
 )
 
 var tlsServers = []string{
-       "google.com:443",
-       "github.com:443",
-       "twitter.com:443",
+       "google.com",
+       "github.com",
+       "twitter.com",
 }
 
 func TestOSCertBundles(t *testing.T) {
-       defaultRoots()
-
        if testing.Short() {
                t.Logf("skipping certificate tests in short mode")
                return
        }
 
        for _, addr := range tlsServers {
-               conn, err := Dial("tcp", addr, nil)
+               conn, err := Dial("tcp", addr+":443", &Config{ServerName: addr})
                if err != nil {
                        t.Errorf("unable to verify %v: %v", addr, err)
                        continue
@@ -34,3 +34,28 @@ func TestOSCertBundles(t *testing.T) {
                }
        }
 }
+
+func TestCertHostnameVerifyWindows(t *testing.T) {
+       if runtime.GOOS != "windows" {
+               return
+       }
+
+       if testing.Short() {
+               t.Logf("skipping certificate tests in short mode")
+               return
+       }
+
+       for _, addr := range tlsServers {
+               cfg := &Config{ServerName: "example.com"}
+               conn, err := Dial("tcp", addr+":443", cfg)
+               if err == nil {
+                       conn.Close()
+                       t.Errorf("should fail to verify for example.com: %v", addr)
+                       continue
+               }
+               _, ok := err.(x509.HostnameError)
+               if !ok {
+                       t.Errorf("error type mismatch, got: %v", err)
+               }
+       }
+}
diff --git a/libgo/go/crypto/tls/root_windows.go b/libgo/go/crypto/tls/root_windows.go
deleted file mode 100644 (file)
index 319309a..0000000
+++ /dev/null
@@ -1,47 +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 tls
-
-import (
-       "crypto/x509"
-       "syscall"
-       "unsafe"
-)
-
-func loadStore(roots *x509.CertPool, name string) {
-       store, err := syscall.CertOpenSystemStore(syscall.InvalidHandle, syscall.StringToUTF16Ptr(name))
-       if err != nil {
-               return
-       }
-       defer syscall.CertCloseStore(store, 0)
-
-       var cert *syscall.CertContext
-       for {
-               cert, err = syscall.CertEnumCertificatesInStore(store, cert)
-               if err != nil {
-                       return
-               }
-
-               buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
-               // ParseCertificate requires its own copy of certificate data to keep.
-               buf2 := make([]byte, cert.Length)
-               copy(buf2, buf)
-               if c, err := x509.ParseCertificate(buf2); err == nil {
-                       roots.AddCert(c)
-               }
-       }
-}
-
-func initDefaultRoots() {
-       roots := x509.NewCertPool()
-
-       // Roots
-       loadStore(roots, "ROOT")
-
-       // Intermediates
-       loadStore(roots, "CA")
-
-       varDefaultRoots = roots
-}
index 9184e8e..09df5ad 100644 (file)
@@ -2,8 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package tls partially implements the TLS 1.1 protocol, as specified in RFC
-// 4346.
+// Package tls partially implements TLS 1.0, as specified in RFC 2246.
 package tls
 
 import (
@@ -98,7 +97,9 @@ func Dial(network, addr string, config *Config) (*Conn, error) {
        if config == nil {
                config = defaultConfig()
        }
-       if config.ServerName != "" {
+       // If no ServerName is set, infer the ServerName
+       // from the hostname we're connecting to.
+       if config.ServerName == "" {
                // Make a copy to avoid polluting argument or default.
                c := *config
                c.ServerName = hostname
index 3aaa8c5..873d396 100644 (file)
@@ -24,7 +24,7 @@ type pkcs1PrivateKey struct {
        Dq   *big.Int `asn1:"optional"`
        Qinv *big.Int `asn1:"optional"`
 
-       AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional"`
+       AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional,omitempty"`
 }
 
 type pkcs1AdditionalRSAPrime struct {
diff --git a/libgo/go/crypto/x509/root.go b/libgo/go/crypto/x509/root.go
new file mode 100644 (file)
index 0000000..8aae14e
--- /dev/null
@@ -0,0 +1,17 @@
+// 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 x509
+
+import "sync"
+
+var (
+       once        sync.Once
+       systemRoots *CertPool
+)
+
+func systemRootsPool() *CertPool {
+       once.Do(initSystemRoots)
+       return systemRoots
+}
similarity index 90%
rename from libgo/go/crypto/tls/root_darwin.go
rename to libgo/go/crypto/x509/root_darwin.go
index 911a9a6..0f99581 100644 (file)
@@ -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 tls
+package x509
 
 /*
 #cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060
@@ -59,13 +59,14 @@ int FetchPEMRoots(CFDataRef *pemRoots) {
 }
 */
 import "C"
-import (
-       "crypto/x509"
-       "unsafe"
-)
+import "unsafe"
 
-func initDefaultRoots() {
-       roots := x509.NewCertPool()
+func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
+       return nil, nil
+}
+
+func initSystemRoots() {
+       roots := NewCertPool()
 
        var data C.CFDataRef = nil
        err := C.FetchPEMRoots(&data)
@@ -75,5 +76,5 @@ func initDefaultRoots() {
                roots.AppendCertsFromPEM(buf)
        }
 
-       varDefaultRoots = roots
+       systemRoots = roots
 }
similarity index 51%
rename from libgo/go/crypto/tls/root_stub.go
rename to libgo/go/crypto/x509/root_stub.go
index ee2c3e0..5680041 100644 (file)
@@ -4,7 +4,12 @@
 
 // +build plan9 darwin,!cgo
 
-package tls
+package x509
 
-func initDefaultRoots() {
+func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
+       return nil, nil
+}
+
+func initSystemRoots() {
+       systemRoots = NewCertPool()
 }
similarity index 76%
rename from libgo/go/crypto/tls/root_unix.go
rename to libgo/go/crypto/x509/root_unix.go
index acaf3dd..76e79f4 100644 (file)
@@ -4,12 +4,9 @@
 
 // +build freebsd linux openbsd netbsd
 
-package tls
+package x509
 
-import (
-       "crypto/x509"
-       "io/ioutil"
-)
+import "io/ioutil"
 
 // Possible certificate files; stop after finding one.
 var certFiles = []string{
@@ -20,8 +17,12 @@ var certFiles = []string{
        "/usr/local/share/certs/ca-root-nss.crt", // FreeBSD
 }
 
-func initDefaultRoots() {
-       roots := x509.NewCertPool()
+func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
+       return nil, nil
+}
+
+func initSystemRoots() {
+       roots := NewCertPool()
        for _, file := range certFiles {
                data, err := ioutil.ReadFile(file)
                if err == nil {
@@ -29,5 +30,6 @@ func initDefaultRoots() {
                        break
                }
        }
-       varDefaultRoots = roots
+
+       systemRoots = roots
 }
diff --git a/libgo/go/crypto/x509/root_windows.go b/libgo/go/crypto/x509/root_windows.go
new file mode 100644 (file)
index 0000000..7e8f2af
--- /dev/null
@@ -0,0 +1,226 @@
+// 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 x509
+
+import (
+       "errors"
+       "syscall"
+       "unsafe"
+)
+
+// Creates a new *syscall.CertContext representing the leaf certificate in an in-memory
+// certificate store containing itself and all of the intermediate certificates specified
+// in the opts.Intermediates CertPool.
+//
+// A pointer to the in-memory store is available in the returned CertContext's Store field.
+// The store is automatically freed when the CertContext is freed using
+// syscall.CertFreeCertificateContext.
+func createStoreContext(leaf *Certificate, opts *VerifyOptions) (*syscall.CertContext, error) {
+       var storeCtx *syscall.CertContext
+
+       leafCtx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &leaf.Raw[0], uint32(len(leaf.Raw)))
+       if err != nil {
+               return nil, err
+       }
+       defer syscall.CertFreeCertificateContext(leafCtx)
+
+       handle, err := syscall.CertOpenStore(syscall.CERT_STORE_PROV_MEMORY, 0, 0, syscall.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0)
+       if err != nil {
+               return nil, err
+       }
+       defer syscall.CertCloseStore(handle, 0)
+
+       err = syscall.CertAddCertificateContextToStore(handle, leafCtx, syscall.CERT_STORE_ADD_ALWAYS, &storeCtx)
+       if err != nil {
+               return nil, err
+       }
+
+       if opts.Intermediates != nil {
+               for _, intermediate := range opts.Intermediates.certs {
+                       ctx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &intermediate.Raw[0], uint32(len(intermediate.Raw)))
+                       if err != nil {
+                               return nil, err
+                       }
+
+                       err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil)
+                       syscall.CertFreeCertificateContext(ctx)
+                       if err != nil {
+                               return nil, err
+                       }
+               }
+       }
+
+       return storeCtx, nil
+}
+
+// extractSimpleChain extracts the final certificate chain from a CertSimpleChain.
+func extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error) {
+       if simpleChain == nil || count == 0 {
+               return nil, errors.New("x509: invalid simple chain")
+       }
+
+       simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(simpleChain))[:]
+       lastChain := simpleChains[count-1]
+       elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:]
+       for i := 0; i < int(lastChain.NumElements); i++ {
+               // Copy the buf, since ParseCertificate does not create its own copy.
+               cert := elements[i].CertContext
+               encodedCert := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
+               buf := make([]byte, cert.Length)
+               copy(buf, encodedCert[:])
+               parsedCert, err := ParseCertificate(buf)
+               if err != nil {
+                       return nil, err
+               }
+               chain = append(chain, parsedCert)
+       }
+
+       return chain, nil
+}
+
+// checkChainTrustStatus checks the trust status of the certificate chain, translating
+// any errors it finds into Go errors in the process.
+func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) error {
+       if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR {
+               status := chainCtx.TrustStatus.ErrorStatus
+               switch status {
+               case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
+                       return CertificateInvalidError{c, Expired}
+               default:
+                       return UnknownAuthorityError{c}
+               }
+       }
+       return nil
+}
+
+// checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
+// use as a certificate chain for a SSL/TLS server.
+func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
+       sslPara := &syscall.SSLExtraCertChainPolicyPara{
+               AuthType:   syscall.AUTHTYPE_SERVER,
+               ServerName: syscall.StringToUTF16Ptr(opts.DNSName),
+       }
+       sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
+
+       para := &syscall.CertChainPolicyPara{
+               ExtraPolicyPara: uintptr(unsafe.Pointer(sslPara)),
+       }
+       para.Size = uint32(unsafe.Sizeof(*para))
+
+       status := syscall.CertChainPolicyStatus{}
+       err := syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
+       if err != nil {
+               return err
+       }
+
+       // TODO(mkrautz): use the lChainIndex and lElementIndex fields
+       // of the CertChainPolicyStatus to provide proper context, instead
+       // using c.
+       if status.Error != 0 {
+               switch status.Error {
+               case syscall.CERT_E_EXPIRED:
+                       return CertificateInvalidError{c, Expired}
+               case syscall.CERT_E_CN_NO_MATCH:
+                       return HostnameError{c, opts.DNSName}
+               case syscall.CERT_E_UNTRUSTEDROOT:
+                       return UnknownAuthorityError{c}
+               default:
+                       return UnknownAuthorityError{c}
+               }
+       }
+
+       return nil
+}
+
+// systemVerify is like Verify, except that it uses CryptoAPI calls
+// to build certificate chains and verify them.
+func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
+       hasDNSName := opts != nil && len(opts.DNSName) > 0
+
+       storeCtx, err := createStoreContext(c, opts)
+       if err != nil {
+               return nil, err
+       }
+       defer syscall.CertFreeCertificateContext(storeCtx)
+
+       para := new(syscall.CertChainPara)
+       para.Size = uint32(unsafe.Sizeof(*para))
+
+       // If there's a DNSName set in opts, assume we're verifying
+       // a certificate from a TLS server.
+       if hasDNSName {
+               oids := []*byte{
+                       &syscall.OID_PKIX_KP_SERVER_AUTH[0],
+                       // Both IE and Chrome allow certificates with
+                       // Server Gated Crypto as well. Some certificates
+                       // in the wild require them.
+                       &syscall.OID_SERVER_GATED_CRYPTO[0],
+                       &syscall.OID_SGC_NETSCAPE[0],
+               }
+               para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
+               para.RequestedUsage.Usage.Length = uint32(len(oids))
+               para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
+       } else {
+               para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
+               para.RequestedUsage.Usage.Length = 0
+               para.RequestedUsage.Usage.UsageIdentifiers = nil
+       }
+
+       var verifyTime *syscall.Filetime
+       if opts != nil && !opts.CurrentTime.IsZero() {
+               ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano())
+               verifyTime = &ft
+       }
+
+       // CertGetCertificateChain will traverse Windows's root stores
+       // in an attempt to build a verified certificate chain.  Once
+       // it has found a verified chain, it stops. MSDN docs on
+       // CERT_CHAIN_CONTEXT:
+       //
+       //   When a CERT_CHAIN_CONTEXT is built, the first simple chain
+       //   begins with an end certificate and ends with a self-signed
+       //   certificate. If that self-signed certificate is not a root
+       //   or otherwise trusted certificate, an attempt is made to
+       //   build a new chain. CTLs are used to create the new chain
+       //   beginning with the self-signed certificate from the original
+       //   chain as the end certificate of the new chain. This process
+       //   continues building additional simple chains until the first
+       //   self-signed certificate is a trusted certificate or until
+       //   an additional simple chain cannot be built.
+       //
+       // The result is that we'll only get a single trusted chain to
+       // return to our caller.
+       var chainCtx *syscall.CertChainContext
+       err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, 0, 0, &chainCtx)
+       if err != nil {
+               return nil, err
+       }
+       defer syscall.CertFreeCertificateChain(chainCtx)
+
+       err = checkChainTrustStatus(c, chainCtx)
+       if err != nil {
+               return nil, err
+       }
+
+       if hasDNSName {
+               err = checkChainSSLServerPolicy(c, chainCtx, opts)
+               if err != nil {
+                       return nil, err
+               }
+       }
+
+       chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount))
+       if err != nil {
+               return nil, err
+       }
+
+       chains = append(chains, chain)
+
+       return chains, nil
+}
+
+func initSystemRoots() {
+       systemRoots = NewCertPool()
+}
index 3859dd8..307c5ef 100644 (file)
@@ -5,6 +5,7 @@
 package x509
 
 import (
+       "runtime"
        "strings"
        "time"
        "unicode/utf8"
@@ -23,6 +24,9 @@ const (
        // certificate has a name constraint which doesn't include the name
        // being checked.
        CANotAuthorizedForThisName
+       // TooManyIntermediates results when a path length constraint is
+       // violated.
+       TooManyIntermediates
 )
 
 // CertificateInvalidError results when an odd error occurs. Users of this
@@ -40,6 +44,8 @@ func (e CertificateInvalidError) Error() string {
                return "x509: certificate has expired or is not yet valid"
        case CANotAuthorizedForThisName:
                return "x509: a root or intermediate certificate is not authorized to sign in this domain"
+       case TooManyIntermediates:
+               return "x509: too many intermediates for path length constraint"
        }
        return "x509: unknown error"
 }
@@ -76,7 +82,7 @@ func (e UnknownAuthorityError) Error() string {
 type VerifyOptions struct {
        DNSName       string
        Intermediates *CertPool
-       Roots         *CertPool
+       Roots         *CertPool // if nil, the system roots are used
        CurrentTime   time.Time // if zero, the current time is used
 }
 
@@ -87,7 +93,7 @@ const (
 )
 
 // isValid performs validity checks on the c.
-func (c *Certificate) isValid(certType int, opts *VerifyOptions) error {
+func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error {
        now := opts.CurrentTime
        if now.IsZero() {
                now = time.Now()
@@ -130,26 +136,44 @@ func (c *Certificate) isValid(certType int, opts *VerifyOptions) error {
                return CertificateInvalidError{c, NotAuthorizedToSign}
        }
 
+       if c.BasicConstraintsValid && c.MaxPathLen >= 0 {
+               numIntermediates := len(currentChain) - 1
+               if numIntermediates > c.MaxPathLen {
+                       return CertificateInvalidError{c, TooManyIntermediates}
+               }
+       }
+
        return nil
 }
 
 // Verify attempts to verify c by building one or more chains from c to a
-// certificate in opts.roots, using certificates in opts.Intermediates if
+// certificate in opts.Roots, using certificates in opts.Intermediates if
 // needed. If successful, it returns one or more chains where the first
 // element of the chain is c and the last element is from opts.Roots.
 //
 // WARNING: this doesn't do any revocation checking.
 func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
-       err = c.isValid(leafCertificate, &opts)
+       // Use Windows's own verification and chain building.
+       if opts.Roots == nil && runtime.GOOS == "windows" {
+               return c.systemVerify(&opts)
+       }
+
+       if opts.Roots == nil {
+               opts.Roots = systemRootsPool()
+       }
+
+       err = c.isValid(leafCertificate, nil, &opts)
        if err != nil {
                return
        }
+
        if len(opts.DNSName) > 0 {
                err = c.VerifyHostname(opts.DNSName)
                if err != nil {
                        return
                }
        }
+
        return c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts)
 }
 
@@ -163,7 +187,7 @@ func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate
 func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) {
        for _, rootNum := range opts.Roots.findVerifiedParents(c) {
                root := opts.Roots.certs[rootNum]
-               err = root.isValid(rootCertificate, opts)
+               err = root.isValid(rootCertificate, currentChain, opts)
                if err != nil {
                        continue
                }
@@ -178,7 +202,7 @@ nextIntermediate:
                                continue nextIntermediate
                        }
                }
-               err = intermediate.isValid(intermediateCertificate, opts)
+               err = intermediate.isValid(intermediateCertificate, currentChain, opts)
                if err != nil {
                        continue
                }
index 2cdd66a..7b171b2 100644 (file)
@@ -8,6 +8,7 @@ import (
        "crypto/x509/pkix"
        "encoding/pem"
        "errors"
+       "runtime"
        "strings"
        "testing"
        "time"
@@ -19,7 +20,7 @@ type verifyTest struct {
        roots         []string
        currentTime   int64
        dnsName       string
-       nilRoots      bool
+       systemSkip    bool
 
        errorCallback  func(*testing.T, int, error) bool
        expectedChains [][]string
@@ -60,14 +61,6 @@ var verifyTests = []verifyTest{
        {
                leaf:          googleLeaf,
                intermediates: []string{thawteIntermediate},
-               nilRoots:      true, // verifies that we don't crash
-               currentTime:   1302726541,
-               dnsName:       "www.google.com",
-               errorCallback: expectAuthorityUnknown,
-       },
-       {
-               leaf:          googleLeaf,
-               intermediates: []string{thawteIntermediate},
                roots:         []string{verisignRoot},
                currentTime:   1,
                dnsName:       "www.example.com",
@@ -80,6 +73,9 @@ var verifyTests = []verifyTest{
                currentTime: 1302726541,
                dnsName:     "www.google.com",
 
+               // Skip when using systemVerify, since Windows
+               // *will* find the missing intermediate cert.
+               systemSkip:    true,
                errorCallback: expectAuthorityUnknown,
        },
        {
@@ -109,6 +105,9 @@ var verifyTests = []verifyTest{
                roots:         []string{startComRoot},
                currentTime:   1302726541,
 
+               // Skip when using systemVerify, since Windows
+               // can only return a single chain to us (for now).
+               systemSkip: true,
                expectedChains: [][]string{
                        {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
                        {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"},
@@ -148,23 +147,26 @@ func certificateFromPEM(pemBytes string) (*Certificate, error) {
        return ParseCertificate(block.Bytes)
 }
 
-func TestVerify(t *testing.T) {
+func testVerify(t *testing.T, useSystemRoots bool) {
        for i, test := range verifyTests {
+               if useSystemRoots && test.systemSkip {
+                       continue
+               }
+
                opts := VerifyOptions{
-                       Roots:         NewCertPool(),
                        Intermediates: NewCertPool(),
                        DNSName:       test.dnsName,
                        CurrentTime:   time.Unix(test.currentTime, 0),
                }
-               if test.nilRoots {
-                       opts.Roots = nil
-               }
 
-               for j, root := range test.roots {
-                       ok := opts.Roots.AppendCertsFromPEM([]byte(root))
-                       if !ok {
-                               t.Errorf("#%d: failed to parse root #%d", i, j)
-                               return
+               if !useSystemRoots {
+                       opts.Roots = NewCertPool()
+                       for j, root := range test.roots {
+                               ok := opts.Roots.AppendCertsFromPEM([]byte(root))
+                               if !ok {
+                                       t.Errorf("#%d: failed to parse root #%d", i, j)
+                                       return
+                               }
                        }
                }
 
@@ -225,6 +227,19 @@ func TestVerify(t *testing.T) {
        }
 }
 
+func TestGoVerify(t *testing.T) {
+       testVerify(t, false)
+}
+
+func TestSystemVerify(t *testing.T) {
+       if runtime.GOOS != "windows" {
+               t.Logf("skipping verify test using system APIs on %q", runtime.GOOS)
+               return
+       }
+
+       testVerify(t, true)
+}
+
 func chainToDebugString(chain []*Certificate) string {
        var chainStr string
        for _, cert := range chain {
index f5da86b..8dae7e7 100644 (file)
@@ -429,7 +429,7 @@ func (h UnhandledCriticalExtension) Error() string {
 
 type basicConstraints struct {
        IsCA       bool `asn1:"optional"`
-       MaxPathLen int  `asn1:"optional"`
+       MaxPathLen int  `asn1:"optional,default:-1"`
 }
 
 // RFC 5280 4.2.1.4
index 7f986b8..2f5280d 100644 (file)
@@ -43,6 +43,17 @@ type Driver interface {
 // documented.
 var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented")
 
+// ErrBadConn should be returned by a driver to signal to the sql
+// package that a driver.Conn is in a bad state (such as the server
+// having earlier closed the connection) and the sql package should
+// retry on a new connection.
+//
+// To prevent duplicate operations, ErrBadConn should NOT be returned
+// if there's a possibility that the database server might have
+// performed the operation. Even if the server sends back an error,
+// you shouldn't return ErrBadConn.
+var ErrBadConn = errors.New("driver: bad connection")
+
 // Execer is an optional interface that may be implemented by a Conn.
 //
 // If a Conn does not implement Execer, the db package's DB.Exec will
index fc63f03..184e775 100644 (file)
@@ -82,6 +82,7 @@ type fakeConn struct {
        mu          sync.Mutex
        stmtsMade   int
        stmtsClosed int
+       numPrepare  int
 }
 
 func (c *fakeConn) incrStat(v *int) {
@@ -208,10 +209,13 @@ func (c *fakeConn) Begin() (driver.Tx, error) {
 
 func (c *fakeConn) Close() error {
        if c.currTx != nil {
-               return errors.New("can't close; in a Transaction")
+               return errors.New("can't close fakeConn; in a Transaction")
        }
        if c.db == nil {
-               return errors.New("can't close; already closed")
+               return errors.New("can't close fakeConn; already closed")
+       }
+       if c.stmtsMade > c.stmtsClosed {
+               return errors.New("can't close; dangling statement(s)")
        }
        c.db = nil
        return nil
@@ -249,6 +253,7 @@ func errf(msg string, args ...interface{}) error {
 //  just a limitation for fakedb)
 func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
        if len(parts) != 3 {
+               stmt.Close()
                return nil, errf("invalid SELECT syntax with %d parts; want 3", len(parts))
        }
        stmt.table = parts[0]
@@ -259,14 +264,17 @@ func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, e
                }
                nameVal := strings.Split(colspec, "=")
                if len(nameVal) != 2 {
+                       stmt.Close()
                        return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
                }
                column, value := nameVal[0], nameVal[1]
                _, ok := c.db.columnType(stmt.table, column)
                if !ok {
+                       stmt.Close()
                        return nil, errf("SELECT on table %q references non-existent column %q", stmt.table, column)
                }
                if value != "?" {
+                       stmt.Close()
                        return nil, errf("SELECT on table %q has pre-bound value for where column %q; need a question mark",
                                stmt.table, column)
                }
@@ -279,12 +287,14 @@ func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, e
 // parts are table|col=type,col2=type2
 func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
        if len(parts) != 2 {
+               stmt.Close()
                return nil, errf("invalid CREATE syntax with %d parts; want 2", len(parts))
        }
        stmt.table = parts[0]
        for n, colspec := range strings.Split(parts[1], ",") {
                nameType := strings.Split(colspec, "=")
                if len(nameType) != 2 {
+                       stmt.Close()
                        return nil, errf("CREATE table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
                }
                stmt.colName = append(stmt.colName, nameType[0])
@@ -296,17 +306,20 @@ func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (driver.Stmt, e
 // parts are table|col=?,col2=val
 func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
        if len(parts) != 2 {
+               stmt.Close()
                return nil, errf("invalid INSERT syntax with %d parts; want 2", len(parts))
        }
        stmt.table = parts[0]
        for n, colspec := range strings.Split(parts[1], ",") {
                nameVal := strings.Split(colspec, "=")
                if len(nameVal) != 2 {
+                       stmt.Close()
                        return nil, errf("INSERT table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
                }
                column, value := nameVal[0], nameVal[1]
                ctype, ok := c.db.columnType(stmt.table, column)
                if !ok {
+                       stmt.Close()
                        return nil, errf("INSERT table %q references non-existent column %q", stmt.table, column)
                }
                stmt.colName = append(stmt.colName, column)
@@ -322,10 +335,12 @@ func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, e
                        case "int32":
                                i, err := strconv.Atoi(value)
                                if err != nil {
+                                       stmt.Close()
                                        return nil, errf("invalid conversion to int32 from %q", value)
                                }
                                subsetVal = int64(i) // int64 is a subset type, but not int32
                        default:
+                               stmt.Close()
                                return nil, errf("unsupported conversion for pre-bound parameter %q to type %q", value, ctype)
                        }
                        stmt.colValue = append(stmt.colValue, subsetVal)
@@ -339,6 +354,7 @@ func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, e
 }
 
 func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
+       c.numPrepare++
        if c.db == nil {
                panic("nil c.db; conn = " + fmt.Sprintf("%#v", c))
        }
@@ -360,6 +376,7 @@ func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
        case "INSERT":
                return c.prepareInsert(stmt, parts)
        default:
+               stmt.Close()
                return nil, errf("unsupported command type %q", cmd)
        }
        return stmt, nil
index 62b551d..51a357b 100644 (file)
@@ -175,6 +175,16 @@ var ErrNoRows = errors.New("sql: no rows in result set")
 
 // DB is a database handle. It's safe for concurrent use by multiple
 // goroutines.
+//
+// If the underlying database driver has the concept of a connection
+// and per-connection session state, the sql package manages creating
+// and freeing connections automatically, including maintaining a free
+// pool of idle connections. If observing session state is required,
+// either do not share a *DB between multiple concurrent goroutines or
+// create and observe all state only within a transaction. Once
+// DB.Open is called, the returned Tx is bound to a single isolated
+// connection. Once Tx.Commit or Tx.Rollback is called, that
+// connection is returned to DB's idle connection pool.
 type DB struct {
        driver driver.Driver
        dsn    string
@@ -241,34 +251,56 @@ func (db *DB) conn() (driver.Conn, error) {
 func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) {
        db.mu.Lock()
        defer db.mu.Unlock()
-       for n, conn := range db.freeConn {
-               if conn == wanted {
-                       db.freeConn[n] = db.freeConn[len(db.freeConn)-1]
-                       db.freeConn = db.freeConn[:len(db.freeConn)-1]
-                       return wanted, true
+       for i, conn := range db.freeConn {
+               if conn != wanted {
+                       continue
                }
+               db.freeConn[i] = db.freeConn[len(db.freeConn)-1]
+               db.freeConn = db.freeConn[:len(db.freeConn)-1]
+               return wanted, true
        }
        return nil, false
 }
 
-func (db *DB) putConn(c driver.Conn) {
+// putConnHook is a hook for testing.
+var putConnHook func(*DB, driver.Conn)
+
+// putConn adds a connection to the db's free pool.
+// err is optionally the last error that occured on this connection.
+func (db *DB) putConn(c driver.Conn, err error) {
+       if err == driver.ErrBadConn {
+               // Don't reuse bad connections.
+               return
+       }
        db.mu.Lock()
-       defer db.mu.Unlock()
+       if putConnHook != nil {
+               putConnHook(db, c)
+       }
        if n := len(db.freeConn); !db.closed && n < db.maxIdleConns() {
                db.freeConn = append(db.freeConn, c)
+               db.mu.Unlock()
                return
        }
-       db.closeConn(c) // TODO(bradfitz): release lock before calling this?
-}
-
-func (db *DB) closeConn(c driver.Conn) {
-       // TODO: check to see if we need this Conn for any prepared statements
-       // that are active.
+       // TODO: check to see if we need this Conn for any prepared
+       // statements which are still active?
+       db.mu.Unlock()
        c.Close()
 }
 
 // Prepare creates a prepared statement for later execution.
 func (db *DB) Prepare(query string) (*Stmt, error) {
+       var stmt *Stmt
+       var err error
+       for i := 0; i < 10; i++ {
+               stmt, err = db.prepare(query)
+               if err != driver.ErrBadConn {
+                       break
+               }
+       }
+       return stmt, err
+}
+
+func (db *DB) prepare(query string) (stmt *Stmt, err error) {
        // TODO: check if db.driver supports an optional
        // driver.Preparer interface and call that instead, if so,
        // otherwise we make a prepared statement that's bound
@@ -279,12 +311,12 @@ func (db *DB) Prepare(query string) (*Stmt, error) {
        if err != nil {
                return nil, err
        }
-       defer db.putConn(ci)
+       defer db.putConn(ci, err)
        si, err := ci.Prepare(query)
        if err != nil {
                return nil, err
        }
-       stmt := &Stmt{
+       stmt = &Stmt{
                db:    db,
                query: query,
                css:   []connStmt{{ci, si}},
@@ -295,15 +327,22 @@ func (db *DB) Prepare(query string) (*Stmt, error) {
 // Exec executes a query without returning any rows.
 func (db *DB) Exec(query string, args ...interface{}) (Result, error) {
        sargs, err := subsetTypeArgs(args)
-       if err != nil {
-               return nil, err
+       var res Result
+       for i := 0; i < 10; i++ {
+               res, err = db.exec(query, sargs)
+               if err != driver.ErrBadConn {
+                       break
+               }
        }
+       return res, err
+}
 
+func (db *DB) exec(query string, sargs []driver.Value) (res Result, err error) {
        ci, err := db.conn()
        if err != nil {
                return nil, err
        }
-       defer db.putConn(ci)
+       defer db.putConn(ci, err)
 
        if execer, ok := ci.(driver.Execer); ok {
                resi, err := execer.Exec(query, sargs)
@@ -354,13 +393,25 @@ func (db *DB) QueryRow(query string, args ...interface{}) *Row {
 // Begin starts a transaction. The isolation level is dependent on
 // the driver.
 func (db *DB) Begin() (*Tx, error) {
+       var tx *Tx
+       var err error
+       for i := 0; i < 10; i++ {
+               tx, err = db.begin()
+               if err != driver.ErrBadConn {
+                       break
+               }
+       }
+       return tx, err
+}
+
+func (db *DB) begin() (tx *Tx, err error) {
        ci, err := db.conn()
        if err != nil {
                return nil, err
        }
        txi, err := ci.Begin()
        if err != nil {
-               db.putConn(ci)
+               db.putConn(ci, err)
                return nil, fmt.Errorf("sql: failed to Begin transaction: %v", err)
        }
        return &Tx{
@@ -406,7 +457,7 @@ func (tx *Tx) close() {
                panic("double close") // internal error
        }
        tx.done = true
-       tx.db.putConn(tx.ci)
+       tx.db.putConn(tx.ci, nil)
        tx.ci = nil
        tx.txi = nil
 }
@@ -561,9 +612,11 @@ func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) {
                return nil, err
        }
        rows, err := stmt.Query(args...)
-       if err == nil {
-               rows.closeStmt = stmt
+       if err != nil {
+               stmt.Close()
+               return nil, err
        }
+       rows.closeStmt = stmt
        return rows, err
 }
 
@@ -609,7 +662,7 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
        if err != nil {
                return nil, err
        }
-       defer releaseConn()
+       defer releaseConn(nil)
 
        // -1 means the driver doesn't know how to count the number of
        // placeholders, so we won't sanity check input here and instead let the
@@ -672,7 +725,7 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
 // connStmt returns a free driver connection on which to execute the
 // statement, a function to call to release the connection, and a
 // statement bound to that connection.
-func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(), si driver.Stmt, err error) {
+func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(error), si driver.Stmt, err error) {
        if err = s.stickyErr; err != nil {
                return
        }
@@ -691,7 +744,7 @@ func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(), si driver.Stmt, e
                if err != nil {
                        return
                }
-               releaseConn = func() { s.tx.releaseConn() }
+               releaseConn = func(error) { s.tx.releaseConn() }
                return ci, releaseConn, s.txsi, nil
        }
 
@@ -700,7 +753,7 @@ func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(), si driver.Stmt, e
        for _, v := range s.css {
                // TODO(bradfitz): lazily clean up entries in this
                // list with dead conns while enumerating
-               if _, match = s.db.connIfFree(cs.ci); match {
+               if _, match = s.db.connIfFree(v.ci); match {
                        cs = v
                        break
                }
@@ -710,22 +763,28 @@ func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(), si driver.Stmt, e
        // Make a new conn if all are busy.
        // TODO(bradfitz): or wait for one? make configurable later?
        if !match {
-               ci, err := s.db.conn()
-               if err != nil {
-                       return nil, nil, nil, err
-               }
-               si, err := ci.Prepare(s.query)
-               if err != nil {
-                       return nil, nil, nil, err
+               for i := 0; ; i++ {
+                       ci, err := s.db.conn()
+                       if err != nil {
+                               return nil, nil, nil, err
+                       }
+                       si, err := ci.Prepare(s.query)
+                       if err == driver.ErrBadConn && i < 10 {
+                               continue
+                       }
+                       if err != nil {
+                               return nil, nil, nil, err
+                       }
+                       s.mu.Lock()
+                       cs = connStmt{ci, si}
+                       s.css = append(s.css, cs)
+                       s.mu.Unlock()
+                       break
                }
-               s.mu.Lock()
-               cs = connStmt{ci, si}
-               s.css = append(s.css, cs)
-               s.mu.Unlock()
        }
 
        conn := cs.ci
-       releaseConn = func() { s.db.putConn(conn) }
+       releaseConn = func(err error) { s.db.putConn(conn, err) }
        return conn, releaseConn, cs.si, nil
 }
 
@@ -749,7 +808,7 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
        }
        rowsi, err := si.Query(sargs)
        if err != nil {
-               s.db.putConn(ci)
+               releaseConn(err)
                return nil, err
        }
        // Note: ownership of ci passes to the *Rows, to be freed
@@ -800,7 +859,7 @@ func (s *Stmt) Close() error {
                for _, v := range s.css {
                        if ci, match := s.db.connIfFree(v.ci); match {
                                v.si.Close()
-                               s.db.putConn(ci)
+                               s.db.putConn(ci, nil)
                        } else {
                                // TODO(bradfitz): care that we can't close
                                // this statement because the statement's
@@ -827,7 +886,7 @@ func (s *Stmt) Close() error {
 type Rows struct {
        db          *DB
        ci          driver.Conn // owned; must call putconn when closed to release
-       releaseConn func()
+       releaseConn func(error)
        rowsi       driver.Rows
 
        closed    bool
@@ -939,7 +998,7 @@ func (rs *Rows) Close() error {
        }
        rs.closed = true
        err := rs.rowsi.Close()
-       rs.releaseConn()
+       rs.releaseConn(err)
        if rs.closeStmt != nil {
                rs.closeStmt.Close()
        }
@@ -963,7 +1022,7 @@ func (r *Row) Scan(dest ...interface{}) error {
        }
 
        // TODO(bradfitz): for now we need to defensively clone all
-       // []byte that the driver returned (not permitting 
+       // []byte that the driver returned (not permitting
        // *RawBytes in Rows.Scan), since we're about to close
        // the Rows in our defer, when we return from this function.
        // the contract with the driver.Next(...) interface is that it
index c985a10..b296705 100644 (file)
@@ -5,13 +5,35 @@
 package sql
 
 import (
+       "database/sql/driver"
        "fmt"
        "reflect"
+       "runtime"
        "strings"
        "testing"
        "time"
 )
 
+func init() {
+       type dbConn struct {
+               db *DB
+               c  driver.Conn
+       }
+       freedFrom := make(map[dbConn]string)
+       putConnHook = func(db *DB, c driver.Conn) {
+               for _, oc := range db.freeConn {
+                       if oc == c {
+                               // print before panic, as panic may get lost due to conflicting panic
+                               // (all goroutines asleep) elsewhere, since we might not unlock
+                               // the mutex in freeConn here.
+                               println("double free of conn. conflicts are:\nA) " + freedFrom[dbConn{db, c}] + "\n\nand\nB) " + stack())
+                               panic("double free of conn.")
+                       }
+               }
+               freedFrom[dbConn{db, c}] = stack()
+       }
+}
+
 const fakeDBName = "foo"
 
 var chrisBirthday = time.Unix(123456789, 0)
@@ -47,9 +69,19 @@ func closeDB(t *testing.T, db *DB) {
        }
 }
 
+// numPrepares assumes that db has exactly 1 idle conn and returns
+// its count of calls to Prepare
+func numPrepares(t *testing.T, db *DB) int {
+       if n := len(db.freeConn); n != 1 {
+               t.Fatalf("free conns = %d; want 1", n)
+       }
+       return db.freeConn[0].(*fakeConn).numPrepare
+}
+
 func TestQuery(t *testing.T) {
        db := newTestDB(t, "people")
        defer closeDB(t, db)
+       prepares0 := numPrepares(t, db)
        rows, err := db.Query("SELECT|people|age,name|")
        if err != nil {
                t.Fatalf("Query: %v", err)
@@ -83,7 +115,10 @@ func TestQuery(t *testing.T) {
        // And verify that the final rows.Next() call, which hit EOF,
        // also closed the rows connection.
        if n := len(db.freeConn); n != 1 {
-               t.Errorf("free conns after query hitting EOF = %d; want 1", n)
+               t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
+       }
+       if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
+               t.Errorf("executed %d Prepare statements; want 1", prepares)
        }
 }
 
@@ -216,6 +251,7 @@ func TestStatementQueryRow(t *testing.T) {
        if err != nil {
                t.Fatalf("Prepare: %v", err)
        }
+       defer stmt.Close()
        var age int
        for n, tt := range []struct {
                name string
@@ -256,6 +292,7 @@ func TestExec(t *testing.T) {
        if err != nil {
                t.Errorf("Stmt, err = %v, %v", stmt, err)
        }
+       defer stmt.Close()
 
        type execTest struct {
                args    []interface{}
@@ -297,11 +334,14 @@ func TestTxStmt(t *testing.T) {
        if err != nil {
                t.Fatalf("Stmt, err = %v, %v", stmt, err)
        }
+       defer stmt.Close()
        tx, err := db.Begin()
        if err != nil {
                t.Fatalf("Begin = %v", err)
        }
-       _, err = tx.Stmt(stmt).Exec("Bobby", 7)
+       txs := tx.Stmt(stmt)
+       defer txs.Close()
+       _, err = txs.Exec("Bobby", 7)
        if err != nil {
                t.Fatalf("Exec = %v", err)
        }
@@ -330,6 +370,7 @@ func TestTxQuery(t *testing.T) {
        if err != nil {
                t.Fatal(err)
        }
+       defer r.Close()
 
        if !r.Next() {
                if r.Err() != nil {
@@ -345,6 +386,22 @@ func TestTxQuery(t *testing.T) {
        }
 }
 
+func TestTxQueryInvalid(t *testing.T) {
+       db := newTestDB(t, "")
+       defer closeDB(t, db)
+
+       tx, err := db.Begin()
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer tx.Rollback()
+
+       _, err = tx.Query("SELECT|t1|name|")
+       if err == nil {
+               t.Fatal("Error expected")
+       }
+}
+
 // Tests fix for issue 2542, that we release a lock when querying on
 // a closed connection.
 func TestIssue2542Deadlock(t *testing.T) {
@@ -450,48 +507,48 @@ type nullTestSpec struct {
 
 func TestNullStringParam(t *testing.T) {
        spec := nullTestSpec{"nullstring", "string", [6]nullTestRow{
-               nullTestRow{NullString{"aqua", true}, "", NullString{"aqua", true}},
-               nullTestRow{NullString{"brown", false}, "", NullString{"", false}},
-               nullTestRow{"chartreuse", "", NullString{"chartreuse", true}},
-               nullTestRow{NullString{"darkred", true}, "", NullString{"darkred", true}},
-               nullTestRow{NullString{"eel", false}, "", NullString{"", false}},
-               nullTestRow{"foo", NullString{"black", false}, nil},
+               {NullString{"aqua", true}, "", NullString{"aqua", true}},
+               {NullString{"brown", false}, "", NullString{"", false}},
+               {"chartreuse", "", NullString{"chartreuse", true}},
+               {NullString{"darkred", true}, "", NullString{"darkred", true}},
+               {NullString{"eel", false}, "", NullString{"", false}},
+               {"foo", NullString{"black", false}, nil},
        }}
        nullTestRun(t, spec)
 }
 
 func TestNullInt64Param(t *testing.T) {
        spec := nullTestSpec{"nullint64", "int64", [6]nullTestRow{
-               nullTestRow{NullInt64{31, true}, 1, NullInt64{31, true}},
-               nullTestRow{NullInt64{-22, false}, 1, NullInt64{0, false}},
-               nullTestRow{22, 1, NullInt64{22, true}},
-               nullTestRow{NullInt64{33, true}, 1, NullInt64{33, true}},
-               nullTestRow{NullInt64{222, false}, 1, NullInt64{0, false}},
-               nullTestRow{0, NullInt64{31, false}, nil},
+               {NullInt64{31, true}, 1, NullInt64{31, true}},
+               {NullInt64{-22, false}, 1, NullInt64{0, false}},
+               {22, 1, NullInt64{22, true}},
+               {NullInt64{33, true}, 1, NullInt64{33, true}},
+               {NullInt64{222, false}, 1, NullInt64{0, false}},
+               {0, NullInt64{31, false}, nil},
        }}
        nullTestRun(t, spec)
 }
 
 func TestNullFloat64Param(t *testing.T) {
        spec := nullTestSpec{"nullfloat64", "float64", [6]nullTestRow{
-               nullTestRow{NullFloat64{31.2, true}, 1, NullFloat64{31.2, true}},
-               nullTestRow{NullFloat64{13.1, false}, 1, NullFloat64{0, false}},
-               nullTestRow{-22.9, 1, NullFloat64{-22.9, true}},
-               nullTestRow{NullFloat64{33.81, true}, 1, NullFloat64{33.81, true}},
-               nullTestRow{NullFloat64{222, false}, 1, NullFloat64{0, false}},
-               nullTestRow{10, NullFloat64{31.2, false}, nil},
+               {NullFloat64{31.2, true}, 1, NullFloat64{31.2, true}},
+               {NullFloat64{13.1, false}, 1, NullFloat64{0, false}},
+               {-22.9, 1, NullFloat64{-22.9, true}},
+               {NullFloat64{33.81, true}, 1, NullFloat64{33.81, true}},
+               {NullFloat64{222, false}, 1, NullFloat64{0, false}},
+               {10, NullFloat64{31.2, false}, nil},
        }}
        nullTestRun(t, spec)
 }
 
 func TestNullBoolParam(t *testing.T) {
        spec := nullTestSpec{"nullbool", "bool", [6]nullTestRow{
-               nullTestRow{NullBool{false, true}, true, NullBool{false, true}},
-               nullTestRow{NullBool{true, false}, false, NullBool{false, false}},
-               nullTestRow{true, true, NullBool{true, true}},
-               nullTestRow{NullBool{true, true}, false, NullBool{true, true}},
-               nullTestRow{NullBool{true, false}, true, NullBool{false, false}},
-               nullTestRow{true, NullBool{true, false}, nil},
+               {NullBool{false, true}, true, NullBool{false, true}},
+               {NullBool{true, false}, false, NullBool{false, false}},
+               {true, true, NullBool{true, true}},
+               {NullBool{true, true}, false, NullBool{true, true}},
+               {NullBool{true, false}, true, NullBool{false, false}},
+               {true, NullBool{true, false}, nil},
        }}
        nullTestRun(t, spec)
 }
@@ -510,6 +567,7 @@ func nullTestRun(t *testing.T, spec nullTestSpec) {
        if err != nil {
                t.Fatalf("prepare: %v", err)
        }
+       defer stmt.Close()
        if _, err := stmt.Exec(3, "chris", spec.rows[2].nullParam, spec.rows[2].notNullParam); err != nil {
                t.Errorf("exec insert chris: %v", err)
        }
@@ -549,3 +607,8 @@ func nullTestRun(t *testing.T, spec nullTestSpec) {
                }
        }
 }
+
+func stack() string {
+       buf := make([]byte, 1024)
+       return string(buf[:runtime.Stack(buf, false)])
+}
index 4d1ae38..3bf81a6 100644 (file)
@@ -250,10 +250,14 @@ func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error)
 func parseUTCTime(bytes []byte) (ret time.Time, err error) {
        s := string(bytes)
        ret, err = time.Parse("0601021504Z0700", s)
-       if err == nil {
-               return
+       if err != nil {
+               ret, err = time.Parse("060102150405Z0700", s)
        }
-       ret, err = time.Parse("060102150405Z0700", s)
+       if err == nil && ret.Year() >= 2050 {
+               // UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
+               ret = ret.AddDate(-100, 0, 0)
+       }
+
        return
 }
 
index 92c9eb6..93803f4 100644 (file)
@@ -321,7 +321,7 @@ var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParame
        {"default:42", fieldParameters{defaultValue: newInt64(42)}},
        {"tag:17", fieldParameters{tag: newInt(17)}},
        {"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}},
-       {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, false, newInt64(42), newInt(17), 0, false}},
+       {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, false, newInt64(42), newInt(17), 0, false, false}},
        {"set", fieldParameters{set: true}},
 }
 
index f7cb3ac..03856bc 100644 (file)
@@ -75,6 +75,7 @@ type fieldParameters struct {
        tag          *int   // the EXPLICIT or IMPLICIT tag (maybe nil).
        stringType   int    // the string tag to use when marshaling.
        set          bool   // true iff this should be encoded as a SET
+       omitEmpty    bool   // true iff this should be omitted if empty when marshaling.
 
        // Invariants:
        //   if explicit is set, tag is non-nil.
@@ -116,6 +117,8 @@ func parseFieldParameters(str string) (ret fieldParameters) {
                        if ret.tag == nil {
                                ret.tag = new(int)
                        }
+               case part == "omitempty":
+                       ret.omitEmpty = true
                }
        }
        return
index 774bee7..163bca5 100644 (file)
@@ -463,6 +463,10 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
                return marshalField(out, v.Elem(), params)
        }
 
+       if v.Kind() == reflect.Slice && v.Len() == 0 && params.omitEmpty {
+               return
+       }
+
        if params.optional && reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) {
                return
        }
index a7447f9..f43bcae 100644 (file)
@@ -54,6 +54,10 @@ type optionalRawValueTest struct {
        A RawValue `asn1:"optional"`
 }
 
+type omitEmptyTest struct {
+       A []string `asn1:"omitempty"`
+}
+
 type testSET []int
 
 var PST = time.FixedZone("PST", -8*60*60)
@@ -116,6 +120,8 @@ var marshalTests = []marshalTest{
        {rawContentsStruct{[]byte{0x30, 3, 1, 2, 3}, 64}, "3003010203"},
        {RawValue{Tag: 1, Class: 2, IsCompound: false, Bytes: []byte{1, 2, 3}}, "8103010203"},
        {testSET([]int{10}), "310302010a"},
+       {omitEmptyTest{[]string{}}, "3000"},
+       {omitEmptyTest{[]string{"1"}}, "30053003130131"},
 }
 
 func TestMarshal(t *testing.T) {
index 02f090d..712e490 100644 (file)
@@ -2,12 +2,17 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package binary implements translation between
-// unsigned integer values and byte sequences
-// and the reading and writing of fixed-size values.
+// Package binary implements translation between numbers and byte sequences
+// and encoding and decoding of varints.
+//
+// Numbers are translated by reading and writing fixed-size values.
 // A fixed-size value is either a fixed-size arithmetic
 // type (int8, uint8, int16, float32, complex64, ...)
 // or an array or struct containing only fixed-size values.
+//
+// Varints are a method of encoding integers using one or more bytes;
+// numbers with smaller absolute value take a smaller number of bytes.
+// For a specification, see http://code.google.com/apis/protocolbuffers/docs/encoding.html.
 package binary
 
 import (
index 9aa398e..db4d988 100644 (file)
@@ -92,7 +92,8 @@ var (
 // If FieldsPerRecord is positive, Read requires each record to
 // have the given number of fields.  If FieldsPerRecord is 0, Read sets it to
 // the number of fields in the first record, so that future records must
-// have the same field count.
+// have the same field count.  If FieldsPerRecord is negative, no check is
+// made and records may have a variable number of fields.
 //
 // If LazyQuotes is true, a quote may appear in an unquoted field and a
 // non-doubled quote may appear in a quoted field.
index 0708a83..e32a178 100644 (file)
@@ -707,6 +707,9 @@ func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, p ui
        if name == "" {
                // Copy the representation of the nil interface value to the target.
                // This is horribly unsafe and special.
+               if indir > 0 {
+                       p = allocate(ityp, p, 1) // All but the last level has been allocated by dec.Indirect
+               }
                *(*[2]uintptr)(unsafe.Pointer(p)) = ivalue.InterfaceData()
                return
        }
index 050786d..c4947cb 100644 (file)
@@ -694,8 +694,8 @@ type Bug3 struct {
 
 func TestGobPtrSlices(t *testing.T) {
        in := []*Bug3{
-               &Bug3{1, nil},
-               &Bug3{2, nil},
+               {1, nil},
+               {2, nil},
        }
        b := new(bytes.Buffer)
        err := NewEncoder(b).Encode(&in)
index 83644c0..45240d7 100644 (file)
@@ -573,3 +573,22 @@ func TestGobEncodeIsZero(t *testing.T) {
                t.Fatalf("%v != %v", x, y)
        }
 }
+
+func TestGobEncodePtrError(t *testing.T) {
+       var err error
+       b := new(bytes.Buffer)
+       enc := NewEncoder(b)
+       err = enc.Encode(&err)
+       if err != nil {
+               t.Fatal("encode:", err)
+       }
+       dec := NewDecoder(b)
+       err2 := fmt.Errorf("foo")
+       err = dec.Decode(&err2)
+       if err != nil {
+               t.Fatal("decode:", err)
+       }
+       if err2 != nil {
+               t.Fatalf("expected nil, got %v", err2)
+       }
+}
index 5425a3a..edbafcf 100644 (file)
@@ -43,7 +43,8 @@ import (
 // to keep some browsers from misinterpreting JSON output as HTML.
 //
 // Array and slice values encode as JSON arrays, except that
-// []byte encodes as a base64-encoded string.
+// []byte encodes as a base64-encoded string, and a nil slice
+// encodes as the null JSON object.
 //
 // Struct values encode as JSON objects. Each exported struct field
 // becomes a member of the object unless
index bb21bb5..1deedc9 100644 (file)
@@ -577,7 +577,7 @@ type decompSet [4]map[string]bool
 
 func makeDecompSet() decompSet {
        m := decompSet{}
-       for i, _ := range m {
+       for i := range m {
                m[i] = make(map[string]bool)
        }
        return m
@@ -646,7 +646,7 @@ func printCharInfoTables() int {
        fmt.Println("const (")
        for i, m := range decompSet {
                sa := []string{}
-               for s, _ := range m {
+               for s := range m {
                        sa = append(sa, s)
                }
                sort.Strings(sa)
diff --git a/libgo/go/exp/wingui/gui.go b/libgo/go/exp/wingui/gui.go
deleted file mode 100644 (file)
index 3b79873..0000000
+++ /dev/null
@@ -1,155 +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.
-
-// +build windows
-
-package main
-
-import (
-       "fmt"
-       "os"
-       "syscall"
-       "unsafe"
-)
-
-// some help functions
-
-func abortf(format string, a ...interface{}) {
-       fmt.Fprintf(os.Stdout, format, a...)
-       os.Exit(1)
-}
-
-func abortErrNo(funcname string, err error) {
-       errno, _ := err.(syscall.Errno)
-       abortf("%s failed: %d %s\n", funcname, uint32(errno), err)
-}
-
-// global vars
-
-var (
-       mh syscall.Handle
-       bh syscall.Handle
-)
-
-// WinProc called by windows to notify us of all windows events we might be interested in.
-func WndProc(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) (rc uintptr) {
-       switch msg {
-       case WM_CREATE:
-               var e error
-               // CreateWindowEx
-               bh, e = CreateWindowEx(
-                       0,
-                       syscall.StringToUTF16Ptr("button"),
-                       syscall.StringToUTF16Ptr("Quit"),
-                       WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,
-                       75, 70, 140, 25,
-                       hwnd, 1, mh, 0)
-               if e != nil {
-                       abortErrNo("CreateWindowEx", e)
-               }
-               fmt.Printf("button handle is %x\n", bh)
-               rc = DefWindowProc(hwnd, msg, wparam, lparam)
-       case WM_COMMAND:
-               switch syscall.Handle(lparam) {
-               case bh:
-                       e := PostMessage(hwnd, WM_CLOSE, 0, 0)
-                       if e != nil {
-                               abortErrNo("PostMessage", e)
-                       }
-               default:
-                       rc = DefWindowProc(hwnd, msg, wparam, lparam)
-               }
-       case WM_CLOSE:
-               DestroyWindow(hwnd)
-       case WM_DESTROY:
-               PostQuitMessage(0)
-       default:
-               rc = DefWindowProc(hwnd, msg, wparam, lparam)
-       }
-       //fmt.Printf("WndProc(0x%08x, %d, 0x%08x, 0x%08x) (%d)\n", hwnd, msg, wparam, lparam, rc)
-       return
-}
-
-func rungui() int {
-       var e error
-
-       // GetModuleHandle
-       mh, e = GetModuleHandle(nil)
-       if e != nil {
-               abortErrNo("GetModuleHandle", e)
-       }
-
-       // Get icon we're going to use.
-       myicon, e := LoadIcon(0, IDI_APPLICATION)
-       if e != nil {
-               abortErrNo("LoadIcon", e)
-       }
-
-       // Get cursor we're going to use.
-       mycursor, e := LoadCursor(0, IDC_ARROW)
-       if e != nil {
-               abortErrNo("LoadCursor", e)
-       }
-
-       // Create callback
-       wproc := syscall.NewCallback(WndProc)
-
-       // RegisterClassEx
-       wcname := syscall.StringToUTF16Ptr("myWindowClass")
-       var wc Wndclassex
-       wc.Size = uint32(unsafe.Sizeof(wc))
-       wc.WndProc = wproc
-       wc.Instance = mh
-       wc.Icon = myicon
-       wc.Cursor = mycursor
-       wc.Background = COLOR_BTNFACE + 1
-       wc.MenuName = nil
-       wc.ClassName = wcname
-       wc.IconSm = myicon
-       if _, e := RegisterClassEx(&wc); e != nil {
-               abortErrNo("RegisterClassEx", e)
-       }
-
-       // CreateWindowEx
-       wh, e := CreateWindowEx(
-               WS_EX_CLIENTEDGE,
-               wcname,
-               syscall.StringToUTF16Ptr("My window"),
-               WS_OVERLAPPEDWINDOW,
-               CW_USEDEFAULT, CW_USEDEFAULT, 300, 200,
-               0, 0, mh, 0)
-       if e != nil {
-               abortErrNo("CreateWindowEx", e)
-       }
-       fmt.Printf("main window handle is %x\n", wh)
-
-       // ShowWindow
-       ShowWindow(wh, SW_SHOWDEFAULT)
-
-       // UpdateWindow
-       if e := UpdateWindow(wh); e != nil {
-               abortErrNo("UpdateWindow", e)
-       }
-
-       // Process all windows messages until WM_QUIT.
-       var m Msg
-       for {
-               r, e := GetMessage(&m, 0, 0, 0)
-               if e != nil {
-                       abortErrNo("GetMessage", e)
-               }
-               if r == 0 {
-                       // WM_QUIT received -> get out
-                       break
-               }
-               TranslateMessage(&m)
-               DispatchMessage(&m)
-       }
-       return int(m.Wparam)
-}
-
-func main() {
-       rc := rungui()
-       os.Exit(rc)
-}
diff --git a/libgo/go/exp/wingui/winapi.go b/libgo/go/exp/wingui/winapi.go
deleted file mode 100644 (file)
index f876088..0000000
+++ /dev/null
@@ -1,134 +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.
-
-// +build windows
-
-package main
-
-import (
-       "syscall"
-       "unsafe"
-)
-
-type Wndclassex struct {
-       Size       uint32
-       Style      uint32
-       WndProc    uintptr
-       ClsExtra   int32
-       WndExtra   int32
-       Instance   syscall.Handle
-       Icon       syscall.Handle
-       Cursor     syscall.Handle
-       Background syscall.Handle
-       MenuName   *uint16
-       ClassName  *uint16
-       IconSm     syscall.Handle
-}
-
-type Point struct {
-       X uintptr
-       Y uintptr
-}
-
-type Msg struct {
-       Hwnd    syscall.Handle
-       Message uint32
-       Wparam  uintptr
-       Lparam  uintptr
-       Time    uint32
-       Pt      Point
-}
-
-const (
-       // Window styles
-       WS_OVERLAPPED   = 0
-       WS_POPUP        = 0x80000000
-       WS_CHILD        = 0x40000000
-       WS_MINIMIZE     = 0x20000000
-       WS_VISIBLE      = 0x10000000
-       WS_DISABLED     = 0x8000000
-       WS_CLIPSIBLINGS = 0x4000000
-       WS_CLIPCHILDREN = 0x2000000
-       WS_MAXIMIZE     = 0x1000000
-       WS_CAPTION      = WS_BORDER | WS_DLGFRAME
-       WS_BORDER       = 0x800000
-       WS_DLGFRAME     = 0x400000
-       WS_VSCROLL      = 0x200000
-       WS_HSCROLL      = 0x100000
-       WS_SYSMENU      = 0x80000
-       WS_THICKFRAME   = 0x40000
-       WS_GROUP        = 0x20000
-       WS_TABSTOP      = 0x10000
-       WS_MINIMIZEBOX  = 0x20000
-       WS_MAXIMIZEBOX  = 0x10000
-       WS_TILED        = WS_OVERLAPPED
-       WS_ICONIC       = WS_MINIMIZE
-       WS_SIZEBOX      = WS_THICKFRAME
-       // Common Window Styles
-       WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
-       WS_TILEDWINDOW      = WS_OVERLAPPEDWINDOW
-       WS_POPUPWINDOW      = WS_POPUP | WS_BORDER | WS_SYSMENU
-       WS_CHILDWINDOW      = WS_CHILD
-
-       WS_EX_CLIENTEDGE = 0x200
-
-       // Some windows messages
-       WM_CREATE  = 1
-       WM_DESTROY = 2
-       WM_CLOSE   = 16
-       WM_COMMAND = 273
-
-       // Some button control styles
-       BS_DEFPUSHBUTTON = 1
-
-       // Some color constants
-       COLOR_WINDOW  = 5
-       COLOR_BTNFACE = 15
-
-       // Default window position
-       CW_USEDEFAULT = 0x80000000 - 0x100000000
-
-       // Show window default style
-       SW_SHOWDEFAULT = 10
-)
-
-var (
-       // Some globally known cursors
-       IDC_ARROW = MakeIntResource(32512)
-       IDC_IBEAM = MakeIntResource(32513)
-       IDC_WAIT  = MakeIntResource(32514)
-       IDC_CROSS = MakeIntResource(32515)
-
-       // Some globally known icons
-       IDI_APPLICATION = MakeIntResource(32512)
-       IDI_HAND        = MakeIntResource(32513)
-       IDI_QUESTION    = MakeIntResource(32514)
-       IDI_EXCLAMATION = MakeIntResource(32515)
-       IDI_ASTERISK    = MakeIntResource(32516)
-       IDI_WINLOGO     = MakeIntResource(32517)
-       IDI_WARNING     = IDI_EXCLAMATION
-       IDI_ERROR       = IDI_HAND
-       IDI_INFORMATION = IDI_ASTERISK
-)
-
-//sys  GetModuleHandle(modname *uint16) (handle syscall.Handle, err error) = GetModuleHandleW
-//sys  RegisterClassEx(wndclass *Wndclassex) (atom uint16, err error) = user32.RegisterClassExW
-//sys  CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent syscall.Handle, menu syscall.Handle, instance syscall.Handle, param uintptr) (hwnd syscall.Handle, err error) = user32.CreateWindowExW
-//sys  DefWindowProc(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (lresult uintptr) = user32.DefWindowProcW
-//sys  DestroyWindow(hwnd syscall.Handle) (err error) = user32.DestroyWindow
-//sys  PostQuitMessage(exitcode int32) = user32.PostQuitMessage
-//sys  ShowWindow(hwnd syscall.Handle, cmdshow int32) (wasvisible bool) = user32.ShowWindow
-//sys  UpdateWindow(hwnd syscall.Handle) (err error) = user32.UpdateWindow
-//sys  GetMessage(msg *Msg, hwnd syscall.Handle, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, err error) [failretval==-1] = user32.GetMessageW
-//sys  TranslateMessage(msg *Msg) (done bool) = user32.TranslateMessage
-//sys  DispatchMessage(msg *Msg) (ret int32) = user32.DispatchMessageW
-//sys  LoadIcon(instance syscall.Handle, iconname *uint16) (icon syscall.Handle, err error) = user32.LoadIconW
-//sys  LoadCursor(instance syscall.Handle, cursorname *uint16) (cursor syscall.Handle, err error) = user32.LoadCursorW
-//sys  SetCursor(cursor syscall.Handle) (precursor syscall.Handle, err error) = user32.SetCursor
-//sys  SendMessage(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (lresult uintptr) = user32.SendMessageW
-//sys  PostMessage(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (err error) = user32.PostMessageW
-
-func MakeIntResource(id uint16) *uint16 {
-       return (*uint16)(unsafe.Pointer(uintptr(id)))
-}
diff --git a/libgo/go/exp/wingui/zwinapi.go b/libgo/go/exp/wingui/zwinapi.go
deleted file mode 100644 (file)
index 5666c6d..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-// +build windows
-// mksyscall_windows.pl winapi.go
-// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
-
-package main
-
-import "unsafe"
-import "syscall"
-
-var (
-       modkernel32 = syscall.NewLazyDLL("kernel32.dll")
-       moduser32   = syscall.NewLazyDLL("user32.dll")
-
-       procGetModuleHandleW = modkernel32.NewProc("GetModuleHandleW")
-       procRegisterClassExW = moduser32.NewProc("RegisterClassExW")
-       procCreateWindowExW  = moduser32.NewProc("CreateWindowExW")
-       procDefWindowProcW   = moduser32.NewProc("DefWindowProcW")
-       procDestroyWindow    = moduser32.NewProc("DestroyWindow")
-       procPostQuitMessage  = moduser32.NewProc("PostQuitMessage")
-       procShowWindow       = moduser32.NewProc("ShowWindow")
-       procUpdateWindow     = moduser32.NewProc("UpdateWindow")
-       procGetMessageW      = moduser32.NewProc("GetMessageW")
-       procTranslateMessage = moduser32.NewProc("TranslateMessage")
-       procDispatchMessageW = moduser32.NewProc("DispatchMessageW")
-       procLoadIconW        = moduser32.NewProc("LoadIconW")
-       procLoadCursorW      = moduser32.NewProc("LoadCursorW")
-       procSetCursor        = moduser32.NewProc("SetCursor")
-       procSendMessageW     = moduser32.NewProc("SendMessageW")
-       procPostMessageW     = moduser32.NewProc("PostMessageW")
-)
-
-func GetModuleHandle(modname *uint16) (handle syscall.Handle, err error) {
-       r0, _, e1 := syscall.Syscall(procGetModuleHandleW.Addr(), 1, uintptr(unsafe.Pointer(modname)), 0, 0)
-       handle = syscall.Handle(r0)
-       if handle == 0 {
-               if e1 != 0 {
-                       err = error(e1)
-               } else {
-                       err = syscall.EINVAL
-               }
-       }
-       return
-}
-
-func RegisterClassEx(wndclass *Wndclassex) (atom uint16, err error) {
-       r0, _, e1 := syscall.Syscall(procRegisterClassExW.Addr(), 1, uintptr(unsafe.Pointer(wndclass)), 0, 0)
-       atom = uint16(r0)
-       if atom == 0 {
-               if e1 != 0 {
-                       err = error(e1)
-               } else {
-                       err = syscall.EINVAL
-               }
-       }
-       return
-}
-
-func CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent syscall.Handle, menu syscall.Handle, instance syscall.Handle, param uintptr) (hwnd syscall.Handle, err error) {
-       r0, _, e1 := syscall.Syscall12(procCreateWindowExW.Addr(), 12, uintptr(exstyle), uintptr(unsafe.Pointer(classname)), uintptr(unsafe.Pointer(windowname)), uintptr(style), uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(wndparent), uintptr(menu), uintptr(instance), uintptr(param))
-       hwnd = syscall.Handle(r0)
-       if hwnd == 0 {
-               if e1 != 0 {
-                       err = error(e1)
-               } else {
-                       err = syscall.EINVAL
-               }
-       }
-       return
-}
-
-func DefWindowProc(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (lresult uintptr) {
-       r0, _, _ := syscall.Syscall6(procDefWindowProcW.Addr(), 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0)
-       lresult = uintptr(r0)
-       return
-}
-
-func DestroyWindow(hwnd syscall.Handle) (err error) {
-       r1, _, e1 := syscall.Syscall(procDestroyWindow.Addr(), 1, uintptr(hwnd), 0, 0)
-       if int(r1) == 0 {
-               if e1 != 0 {
-                       err = error(e1)
-               } else {
-                       err = syscall.EINVAL
-               }
-       }
-       return
-}
-
-func PostQuitMessage(exitcode int32) {
-       syscall.Syscall(procPostQuitMessage.Addr(), 1, uintptr(exitcode), 0, 0)
-       return
-}
-
-func ShowWindow(hwnd syscall.Handle, cmdshow int32) (wasvisible bool) {
-       r0, _, _ := syscall.Syscall(procShowWindow.Addr(), 2, uintptr(hwnd), uintptr(cmdshow), 0)
-       wasvisible = bool(r0 != 0)
-       return
-}
-
-func UpdateWindow(hwnd syscall.Handle) (err error) {
-       r1, _, e1 := syscall.Syscall(procUpdateWindow.Addr(), 1, uintptr(hwnd), 0, 0)
-       if int(r1) == 0 {
-               if e1 != 0 {
-                       err = error(e1)
-               } else {
-                       err = syscall.EINVAL
-               }
-       }
-       return
-}
-
-func GetMessage(msg *Msg, hwnd syscall.Handle, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, err error) {
-       r0, _, e1 := syscall.Syscall6(procGetMessageW.Addr(), 4, uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax), 0, 0)
-       ret = int32(r0)
-       if ret == -1 {
-               if e1 != 0 {
-                       err = error(e1)
-               } else {
-                       err = syscall.EINVAL
-               }
-       }
-       return
-}
-
-func TranslateMessage(msg *Msg) (done bool) {
-       r0, _, _ := syscall.Syscall(procTranslateMessage.Addr(), 1, uintptr(unsafe.Pointer(msg)), 0, 0)
-       done = bool(r0 != 0)
-       return
-}
-
-func DispatchMessage(msg *Msg) (ret int32) {
-       r0, _, _ := syscall.Syscall(procDispatchMessageW.Addr(), 1, uintptr(unsafe.Pointer(msg)), 0, 0)
-       ret = int32(r0)
-       return
-}
-
-func LoadIcon(instance syscall.Handle, iconname *uint16) (icon syscall.Handle, err error) {
-       r0, _, e1 := syscall.Syscall(procLoadIconW.Addr(), 2, uintptr(instance), uintptr(unsafe.Pointer(iconname)), 0)
-       icon = syscall.Handle(r0)
-       if icon == 0 {
-               if e1 != 0 {
-                       err = error(e1)
-               } else {
-                       err = syscall.EINVAL
-               }
-       }
-       return
-}
-
-func LoadCursor(instance syscall.Handle, cursorname *uint16) (cursor syscall.Handle, err error) {
-       r0, _, e1 := syscall.Syscall(procLoadCursorW.Addr(), 2, uintptr(instance), uintptr(unsafe.Pointer(cursorname)), 0)
-       cursor = syscall.Handle(r0)
-       if cursor == 0 {
-               if e1 != 0 {
-                       err = error(e1)
-               } else {
-                       err = syscall.EINVAL
-               }
-       }
-       return
-}
-
-func SetCursor(cursor syscall.Handle) (precursor syscall.Handle, err error) {
-       r0, _, e1 := syscall.Syscall(procSetCursor.Addr(), 1, uintptr(cursor), 0, 0)
-       precursor = syscall.Handle(r0)
-       if precursor == 0 {
-               if e1 != 0 {
-                       err = error(e1)
-               } else {
-                       err = syscall.EINVAL
-               }
-       }
-       return
-}
-
-func SendMessage(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (lresult uintptr) {
-       r0, _, _ := syscall.Syscall6(procSendMessageW.Addr(), 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0)
-       lresult = uintptr(r0)
-       return
-}
-
-func PostMessage(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (err error) {
-       r1, _, e1 := syscall.Syscall6(procPostMessageW.Addr(), 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0)
-       if int(r1) == 0 {
-               if e1 != 0 {
-                       err = error(e1)
-               } else {
-                       err = syscall.EINVAL
-               }
-       }
-       return
-}
index 1919296..b065995 100644 (file)
@@ -41,10 +41,14 @@ type Var interface {
 // Int is a 64-bit integer variable that satisfies the Var interface.
 type Int struct {
        i  int64
-       mu sync.Mutex
+       mu sync.RWMutex
 }
 
-func (v *Int) String() string { return strconv.FormatInt(v.i, 10) }
+func (v *Int) String() string {
+       v.mu.RLock()
+       defer v.mu.RUnlock()
+       return strconv.FormatInt(v.i, 10)
+}
 
 func (v *Int) Add(delta int64) {
        v.mu.Lock()
@@ -61,10 +65,14 @@ func (v *Int) Set(value int64) {
 // Float is a 64-bit float variable that satisfies the Var interface.
 type Float struct {
        f  float64
-       mu sync.Mutex
+       mu sync.RWMutex
 }
 
-func (v *Float) String() string { return strconv.FormatFloat(v.f, 'g', -1, 64) }
+func (v *Float) String() string {
+       v.mu.RLock()
+       defer v.mu.RUnlock()
+       return strconv.FormatFloat(v.f, 'g', -1, 64)
+}
 
 // Add adds delta to v.
 func (v *Float) Add(delta float64) {
@@ -95,17 +103,17 @@ type KeyValue struct {
 func (v *Map) String() string {
        v.mu.RLock()
        defer v.mu.RUnlock()
-       b := new(bytes.Buffer)
-       fmt.Fprintf(b, "{")
+       var b bytes.Buffer
+       fmt.Fprintf(&b, "{")
        first := true
        for key, val := range v.m {
                if !first {
-                       fmt.Fprintf(b, ", ")
+                       fmt.Fprintf(&b, ", ")
                }
-               fmt.Fprintf(b, "\"%s\": %v", key, val)
+               fmt.Fprintf(&b, "\"%s\": %v", key, val)
                first = false
        }
-       fmt.Fprintf(b, "}")
+       fmt.Fprintf(&b, "}")
        return b.String()
 }
 
@@ -180,12 +188,21 @@ func (v *Map) Do(f func(KeyValue)) {
 
 // String is a string variable, and satisfies the Var interface.
 type String struct {
-       s string
+       s  string
+       mu sync.RWMutex
 }
 
-func (v *String) String() string { return strconv.Quote(v.s) }
+func (v *String) String() string {
+       v.mu.RLock()
+       defer v.mu.RUnlock()
+       return strconv.Quote(v.s)
+}
 
-func (v *String) Set(value string) { v.s = value }
+func (v *String) Set(value string) {
+       v.mu.Lock()
+       defer v.mu.Unlock()
+       v.s = value
+}
 
 // Func implements Var by calling the function
 // and formatting the returned value using JSON.
index 7d4178d..9660370 100644 (file)
@@ -7,7 +7,8 @@
        to C's printf and scanf.  The format 'verbs' are derived from C's but
        are simpler.
 
-       Printing:
+
+       Printing
 
        The verbs:
 
        by a single character (the verb) and end with a parenthesized
        description.
 
-       Scanning:
+
+       Scanning
 
        An analogous set of functions scans formatted text to yield
        values.  Scan, Scanf and Scanln read from os.Stdin; Fscan,
diff --git a/libgo/go/fmt/export_test.go b/libgo/go/fmt/export_test.go
new file mode 100644 (file)
index 0000000..89d57ee
--- /dev/null
@@ -0,0 +1,7 @@
+// 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 fmt
+
+var IsSpace = isSpace
index f34df59..e0c587a 100644 (file)
@@ -13,6 +13,7 @@ import (
        "strings"
        "testing"
        "time"
+       "unicode"
 )
 
 type (
@@ -830,3 +831,13 @@ func TestBadVerbRecursion(t *testing.T) {
                t.Error("fail with value")
        }
 }
+
+func TestIsSpace(t *testing.T) {
+       // This tests the internal isSpace function.
+       // IsSpace = isSpace is defined in export_test.go.
+       for i := rune(0); i <= unicode.MaxRune; i++ {
+               if IsSpace(i) != unicode.IsSpace(i) {
+                       t.Errorf("isSpace(%U) = %v, want %v", i, IsSpace(i), unicode.IsSpace(i))
+               }
+       }
+}
index 78d9e99..2186f33 100644 (file)
@@ -5,9 +5,7 @@
 package fmt
 
 import (
-       "bytes"
        "strconv"
-       "unicode"
        "unicode/utf8"
 )
 
@@ -36,10 +34,10 @@ func init() {
 }
 
 // A fmt is the raw formatter used by Printf etc.
-// It prints into a bytes.Buffer that must be set up externally.
+// It prints into a buffer that must be set up separately.
 type fmt struct {
        intbuf [nByte]byte
-       buf    *bytes.Buffer
+       buf    *buffer
        // width, precision
        wid  int
        prec int
@@ -69,7 +67,7 @@ func (f *fmt) clearflags() {
        f.zero = false
 }
 
-func (f *fmt) init(buf *bytes.Buffer) {
+func (f *fmt) init(buf *buffer) {
        f.buf = buf
        f.clearflags()
 }
@@ -247,7 +245,7 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) {
        }
 
        // If we want a quoted char for %#U, move the data up to make room.
-       if f.unicode && f.uniQuote && a >= 0 && a <= unicode.MaxRune && unicode.IsPrint(rune(a)) {
+       if f.unicode && f.uniQuote && a >= 0 && a <= utf8.MaxRune && strconv.IsPrint(rune(a)) {
                runeWidth := utf8.RuneLen(rune(a))
                width := 1 + 1 + runeWidth + 1 // space, quote, rune, quote
                copy(buf[i-width:], buf[i:])   // guaranteed to have enough room.
@@ -290,16 +288,15 @@ func (f *fmt) fmt_s(s string) {
 // fmt_sx formats a string as a hexadecimal encoding of its bytes.
 func (f *fmt) fmt_sx(s, digits string) {
        // TODO: Avoid buffer by pre-padding.
-       var b bytes.Buffer
+       var b []byte
        for i := 0; i < len(s); i++ {
                if i > 0 && f.space {
-                       b.WriteByte(' ')
+                       b = append(b, ' ')
                }
                v := s[i]
-               b.WriteByte(digits[v>>4])
-               b.WriteByte(digits[v&0xF])
+               b = append(b, digits[v>>4], digits[v&0xF])
        }
-       f.pad(b.Bytes())
+       f.pad(b)
 }
 
 // fmt_q formats a string as a double-quoted, escaped Go string constant.
index c3ba2f3..1343824 100644 (file)
@@ -5,13 +5,11 @@
 package fmt
 
 import (
-       "bytes"
        "errors"
        "io"
        "os"
        "reflect"
        "sync"
-       "unicode"
        "unicode/utf8"
 )
 
@@ -71,11 +69,45 @@ type GoStringer interface {
        GoString() string
 }
 
+// Use simple []byte instead of bytes.Buffer to avoid large dependency.
+type buffer []byte
+
+func (b *buffer) Write(p []byte) (n int, err error) {
+       *b = append(*b, p...)
+       return len(p), nil
+}
+
+func (b *buffer) WriteString(s string) (n int, err error) {
+       *b = append(*b, s...)
+       return len(s), nil
+}
+
+func (b *buffer) WriteByte(c byte) error {
+       *b = append(*b, c)
+       return nil
+}
+
+func (bp *buffer) WriteRune(r rune) error {
+       if r < utf8.RuneSelf {
+               *bp = append(*bp, byte(r))
+               return nil
+       }
+
+       b := *bp
+       n := len(b)
+       for n+utf8.UTFMax > cap(b) {
+               b = append(b, 0)
+       }
+       w := utf8.EncodeRune(b[n:n+utf8.UTFMax], r)
+       *bp = b[:n+w]
+       return nil
+}
+
 type pp struct {
        n         int
        panicking bool
        erroring  bool // printing an error condition
-       buf       bytes.Buffer
+       buf       buffer
        // field holds the current item, as an interface{}.
        field interface{}
        // value holds the current item, as a reflect.Value, and will be
@@ -133,10 +165,10 @@ func newPrinter() *pp {
 // Save used pp structs in ppFree; avoids an allocation per invocation.
 func (p *pp) free() {
        // Don't hold on to pp structs with large buffers.
-       if cap(p.buf.Bytes()) > 1024 {
+       if cap(p.buf) > 1024 {
                return
        }
-       p.buf.Reset()
+       p.buf = p.buf[:0]
        p.field = nil
        p.value = reflect.Value{}
        ppFree.put(p)
@@ -179,7 +211,7 @@ func (p *pp) Write(b []byte) (ret int, err error) {
 func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
        p := newPrinter()
        p.doPrintf(format, a)
-       n64, err := p.buf.WriteTo(w)
+       n64, err := w.Write(p.buf)
        p.free()
        return int(n64), err
 }
@@ -194,7 +226,7 @@ func Printf(format string, a ...interface{}) (n int, err error) {
 func Sprintf(format string, a ...interface{}) string {
        p := newPrinter()
        p.doPrintf(format, a)
-       s := p.buf.String()
+       s := string(p.buf)
        p.free()
        return s
 }
@@ -213,7 +245,7 @@ func Errorf(format string, a ...interface{}) error {
 func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
        p := newPrinter()
        p.doPrint(a, false, false)
-       n64, err := p.buf.WriteTo(w)
+       n64, err := w.Write(p.buf)
        p.free()
        return int(n64), err
 }
@@ -230,7 +262,7 @@ func Print(a ...interface{}) (n int, err error) {
 func Sprint(a ...interface{}) string {
        p := newPrinter()
        p.doPrint(a, false, false)
-       s := p.buf.String()
+       s := string(p.buf)
        p.free()
        return s
 }
@@ -245,7 +277,7 @@ func Sprint(a ...interface{}) string {
 func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
        p := newPrinter()
        p.doPrint(a, true, true)
-       n64, err := p.buf.WriteTo(w)
+       n64, err := w.Write(p.buf)
        p.free()
        return int(n64), err
 }
@@ -262,7 +294,7 @@ func Println(a ...interface{}) (n int, err error) {
 func Sprintln(a ...interface{}) string {
        p := newPrinter()
        p.doPrint(a, true, true)
-       s := p.buf.String()
+       s := string(p.buf)
        p.free()
        return s
 }
@@ -352,7 +384,7 @@ func (p *pp) fmtInt64(v int64, verb rune) {
        case 'o':
                p.fmt.integer(v, 8, signed, ldigits)
        case 'q':
-               if 0 <= v && v <= unicode.MaxRune {
+               if 0 <= v && v <= utf8.MaxRune {
                        p.fmt.fmt_qc(v)
                } else {
                        p.badVerb(verb)
@@ -416,7 +448,7 @@ func (p *pp) fmtUint64(v uint64, verb rune, goSyntax bool) {
        case 'o':
                p.fmt.integer(int64(v), 8, unsigned, ldigits)
        case 'q':
-               if 0 <= v && v <= unicode.MaxRune {
+               if 0 <= v && v <= utf8.MaxRune {
                        p.fmt.fmt_qc(int64(v))
                } else {
                        p.badVerb(verb)
index fa9a558..0b3e040 100644 (file)
@@ -5,15 +5,12 @@
 package fmt
 
 import (
-       "bytes"
        "errors"
        "io"
        "math"
        "os"
        "reflect"
        "strconv"
-       "strings"
-       "unicode"
        "unicode/utf8"
 )
 
@@ -87,25 +84,36 @@ func Scanf(format string, a ...interface{}) (n int, err error) {
        return Fscanf(os.Stdin, format, a...)
 }
 
+type stringReader string
+
+func (r *stringReader) Read(b []byte) (n int, err error) {
+       n = copy(b, *r)
+       *r = (*r)[n:]
+       if n == 0 {
+               err = io.EOF
+       }
+       return
+}
+
 // Sscan scans the argument string, storing successive space-separated
 // values into successive arguments.  Newlines count as space.  It
 // returns the number of items successfully scanned.  If that is less
 // than the number of arguments, err will report why.
 func Sscan(str string, a ...interface{}) (n int, err error) {
-       return Fscan(strings.NewReader(str), a...)
+       return Fscan((*stringReader)(&str), a...)
 }
 
 // Sscanln is similar to Sscan, but stops scanning at a newline and
 // after the final item there must be a newline or EOF.
 func Sscanln(str string, a ...interface{}) (n int, err error) {
-       return Fscanln(strings.NewReader(str), a...)
+       return Fscanln((*stringReader)(&str), a...)
 }
 
 // Sscanf scans the argument string, storing successive space-separated
 // values into successive arguments as determined by the format.  It
 // returns the number of items successfully parsed.
 func Sscanf(str string, format string, a ...interface{}) (n int, err error) {
-       return Fscanf(strings.NewReader(str), format, a...)
+       return Fscanf((*stringReader)(&str), format, a...)
 }
 
 // Fscan scans text read from r, storing successive space-separated
@@ -149,7 +157,7 @@ const eof = -1
 // ss is the internal implementation of ScanState.
 type ss struct {
        rr       io.RuneReader // where to read input
-       buf      bytes.Buffer  // token accumulator
+       buf      buffer        // token accumulator
        peekRune rune          // one-rune lookahead
        prevRune rune          // last rune returned by ReadRune
        count    int           // runes consumed so far.
@@ -262,14 +270,46 @@ func (s *ss) Token(skipSpace bool, f func(rune) bool) (tok []byte, err error) {
        if f == nil {
                f = notSpace
        }
-       s.buf.Reset()
+       s.buf = s.buf[:0]
        tok = s.token(skipSpace, f)
        return
 }
 
+// space is a copy of the unicode.White_Space ranges,
+// to avoid depending on package unicode.
+var space = [][2]uint16{
+       {0x0009, 0x000d},
+       {0x0020, 0x0020},
+       {0x0085, 0x0085},
+       {0x00a0, 0x00a0},
+       {0x1680, 0x1680},
+       {0x180e, 0x180e},
+       {0x2000, 0x200a},
+       {0x2028, 0x2029},
+       {0x202f, 0x202f},
+       {0x205f, 0x205f},
+       {0x3000, 0x3000},
+}
+
+func isSpace(r rune) bool {
+       if r >= 1<<16 {
+               return false
+       }
+       rx := uint16(r)
+       for _, rng := range space {
+               if rx < rng[0] {
+                       return false
+               }
+               if rx <= rng[1] {
+                       return true
+               }
+       }
+       return false
+}
+
 // notSpace is the default scanning function used in Token.
 func notSpace(r rune) bool {
-       return !unicode.IsSpace(r)
+       return !isSpace(r)
 }
 
 // skipSpace provides Scan() methods the ability to skip space and newline characters 
@@ -378,10 +418,10 @@ func (s *ss) free(old ssave) {
                return
        }
        // Don't hold on to ss structs with large buffers.
-       if cap(s.buf.Bytes()) > 1024 {
+       if cap(s.buf) > 1024 {
                return
        }
-       s.buf.Reset()
+       s.buf = s.buf[:0]
        s.rr = nil
        ssFree.put(s)
 }
@@ -403,7 +443,7 @@ func (s *ss) skipSpace(stopAtNewline bool) {
                        s.errorString("unexpected newline")
                        return
                }
-               if !unicode.IsSpace(r) {
+               if !isSpace(r) {
                        s.UnreadRune()
                        break
                }
@@ -429,7 +469,7 @@ func (s *ss) token(skipSpace bool, f func(rune) bool) []byte {
                }
                s.buf.WriteRune(r)
        }
-       return s.buf.Bytes()
+       return s.buf
 }
 
 // typeError indicates that the type of the operand did not match the format
@@ -440,6 +480,15 @@ func (s *ss) typeError(field interface{}, expected string) {
 var complexError = errors.New("syntax error scanning complex number")
 var boolError = errors.New("syntax error scanning boolean")
 
+func indexRune(s string, r rune) int {
+       for i, c := range s {
+               if c == r {
+                       return i
+               }
+       }
+       return -1
+}
+
 // consume reads the next rune in the input and reports whether it is in the ok string.
 // If accept is true, it puts the character into the input token.
 func (s *ss) consume(ok string, accept bool) bool {
@@ -447,7 +496,7 @@ func (s *ss) consume(ok string, accept bool) bool {
        if r == eof {
                return false
        }
-       if strings.IndexRune(ok, r) >= 0 {
+       if indexRune(ok, r) >= 0 {
                if accept {
                        s.buf.WriteRune(r)
                }
@@ -465,7 +514,7 @@ func (s *ss) peek(ok string) bool {
        if r != eof {
                s.UnreadRune()
        }
-       return strings.IndexRune(ok, r) >= 0
+       return indexRune(ok, r) >= 0
 }
 
 func (s *ss) notEOF() {
@@ -560,7 +609,7 @@ func (s *ss) scanNumber(digits string, haveDigits bool) string {
        }
        for s.accept(digits) {
        }
-       return s.buf.String()
+       return string(s.buf)
 }
 
 // scanRune returns the next rune value in the input.
@@ -660,16 +709,16 @@ func (s *ss) scanUint(verb rune, bitSize int) uint64 {
 // if the width is specified. It's not rigorous about syntax because it doesn't check that
 // we have at least some digits, but Atof will do that.
 func (s *ss) floatToken() string {
-       s.buf.Reset()
+       s.buf = s.buf[:0]
        // NaN?
        if s.accept("nN") && s.accept("aA") && s.accept("nN") {
-               return s.buf.String()
+               return string(s.buf)
        }
        // leading sign?
        s.accept(sign)
        // Inf?
        if s.accept("iI") && s.accept("nN") && s.accept("fF") {
-               return s.buf.String()
+               return string(s.buf)
        }
        // digits?
        for s.accept(decimalDigits) {
@@ -688,7 +737,7 @@ func (s *ss) floatToken() string {
                for s.accept(decimalDigits) {
                }
        }
-       return s.buf.String()
+       return string(s.buf)
 }
 
 // complexTokens returns the real and imaginary parts of the complex number starting here.
@@ -698,13 +747,13 @@ func (s *ss) complexTokens() (real, imag string) {
        // TODO: accept N and Ni independently?
        parens := s.accept("(")
        real = s.floatToken()
-       s.buf.Reset()
+       s.buf = s.buf[:0]
        // Must now have a sign.
        if !s.accept("+-") {
                s.error(complexError)
        }
        // Sign is now in buffer
-       imagSign := s.buf.String()
+       imagSign := string(s.buf)
        imag = s.floatToken()
        if !s.accept("i") {
                s.error(complexError)
@@ -717,7 +766,7 @@ func (s *ss) complexTokens() (real, imag string) {
 
 // convertFloat converts the string to a float64value.
 func (s *ss) convertFloat(str string, n int) float64 {
-       if p := strings.Index(str, "p"); p >= 0 {
+       if p := indexRune(str, 'p'); p >= 0 {
                // Atof doesn't handle power-of-2 exponents,
                // but they're easy to evaluate.
                f, err := strconv.ParseFloat(str[:p], n)
@@ -794,7 +843,7 @@ func (s *ss) quotedString() string {
                        }
                        s.buf.WriteRune(r)
                }
-               return s.buf.String()
+               return string(s.buf)
        case '"':
                // Double-quoted: Include the quotes and let strconv.Unquote do the backslash escapes.
                s.buf.WriteRune(quote)
@@ -811,7 +860,7 @@ func (s *ss) quotedString() string {
                                break
                        }
                }
-               result, err := strconv.Unquote(s.buf.String())
+               result, err := strconv.Unquote(string(s.buf))
                if err != nil {
                        s.error(err)
                }
@@ -844,7 +893,7 @@ func (s *ss) hexByte() (b byte, ok bool) {
        if rune1 == eof {
                return
        }
-       if unicode.IsSpace(rune1) {
+       if isSpace(rune1) {
                s.UnreadRune()
                return
        }
@@ -862,11 +911,11 @@ func (s *ss) hexString() string {
                }
                s.buf.WriteByte(b)
        }
-       if s.buf.Len() == 0 {
+       if len(s.buf) == 0 {
                s.errorString("Scan: no hex data for %x string")
                return ""
        }
-       return s.buf.String()
+       return string(s.buf)
 }
 
 const floatVerbs = "beEfFgGv"
@@ -875,7 +924,7 @@ const hugeWid = 1 << 30
 
 // scanOne scans a single value, deriving the scanner from the type of the argument.
 func (s *ss) scanOne(verb rune, field interface{}) {
-       s.buf.Reset()
+       s.buf = s.buf[:0]
        var err error
        // If the parameter has its own Scan method, use that.
        if v, ok := field.(Scanner); ok {
@@ -1004,7 +1053,7 @@ func (s *ss) doScan(a []interface{}) (numProcessed int, err error) {
                        if r == '\n' || r == eof {
                                break
                        }
-                       if !unicode.IsSpace(r) {
+                       if !isSpace(r) {
                                s.errorString("Scan: expected newline")
                                break
                        }
@@ -1032,7 +1081,7 @@ func (s *ss) advance(format string) (i int) {
                        i += w // skip the first %
                }
                sawSpace := false
-               for unicode.IsSpace(fmtc) && i < len(format) {
+               for isSpace(fmtc) && i < len(format) {
                        sawSpace = true
                        i += w
                        fmtc, w = utf8.DecodeRuneInString(format[i:])
@@ -1044,7 +1093,7 @@ func (s *ss) advance(format string) (i int) {
                        if inputc == eof {
                                return
                        }
-                       if !unicode.IsSpace(inputc) {
+                       if !isSpace(inputc) {
                                // Space in format but not in input: error
                                s.errorString("expected space in input to match format")
                        }
index eece761..dc9dcd1 100644 (file)
@@ -34,7 +34,7 @@ type Context struct {
        CgoEnabled  bool     // whether cgo can be used
        BuildTags   []string // additional tags to recognize in +build lines
        UseAllFiles bool     // use files regardless of +build lines, file names
-       Gccgo       bool     // assume use of gccgo when computing object paths
+       Compiler    string   // compiler to assume when computing target paths
 
        // By default, Import uses the operating system's file system calls
        // to read directories and files.  To read from other sources,
@@ -210,6 +210,7 @@ func (ctxt *Context) SrcDirs() []string {
 // if set, or else the compiled code's GOARCH, GOOS, and GOROOT.
 var Default Context = defaultContext()
 
+// This list is also known to ../../../cmd/dist/build.c.
 var cgoEnabled = map[string]bool{
        "darwin/386":    true,
        "darwin/amd64":  true,
@@ -228,6 +229,7 @@ func defaultContext() Context {
        c.GOOS = envOr("GOOS", runtime.GOOS)
        c.GOROOT = runtime.GOROOT()
        c.GOPATH = envOr("GOPATH", "")
+       c.Compiler = runtime.Compiler
 
        switch os.Getenv("CGO_ENABLED") {
        case "1":
@@ -277,11 +279,12 @@ type Package struct {
        PkgObj     string // installed .a file
 
        // Source files
-       GoFiles  []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
-       CgoFiles []string // .go source files that import "C"
-       CFiles   []string // .c source files
-       HFiles   []string // .h source files
-       SFiles   []string // .s source files
+       GoFiles   []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+       CgoFiles  []string // .go source files that import "C"
+       CFiles    []string // .c source files
+       HFiles    []string // .h source files
+       SFiles    []string // .s source files
+       SysoFiles []string // .syso system object files to add to archive
 
        // Cgo directives
        CgoPkgConfig []string // Cgo pkg-config directives
@@ -314,6 +317,16 @@ func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) {
        return ctxt.Import(".", dir, mode)
 }
 
+// NoGoError is the error used by Import to describe a directory
+// containing no Go source files.
+type NoGoError struct {
+       Dir string
+}
+
+func (e *NoGoError) Error() string {
+       return "no Go source files in " + e.Dir
+}
+
 // Import returns details about the Go package named by the import path,
 // interpreting local import paths relative to the src directory.  If the path
 // is a local import path naming a package that can be imported using a
@@ -336,11 +349,16 @@ func (ctxt *Context) Import(path string, src string, mode ImportMode) (*Package,
        }
 
        var pkga string
-       if ctxt.Gccgo {
+       var pkgerr error
+       switch ctxt.Compiler {
+       case "gccgo":
                dir, elem := pathpkg.Split(p.ImportPath)
                pkga = "pkg/gccgo/" + dir + "lib" + elem + ".a"
-       } else {
+       case "gc":
                pkga = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + "/" + p.ImportPath + ".a"
+       default:
+               // Save error for end of function.
+               pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler)
        }
 
        binaryOnly := false
@@ -396,7 +414,7 @@ func (ctxt *Context) Import(path string, src string, mode ImportMode) (*Package,
                if ctxt.GOROOT != "" {
                        dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", path)
                        isDir := ctxt.isDir(dir)
-                       binaryOnly = !isDir && mode&AllowBinary != 0 && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
+                       binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
                        if isDir || binaryOnly {
                                p.Dir = dir
                                p.Goroot = true
@@ -407,7 +425,7 @@ func (ctxt *Context) Import(path string, src string, mode ImportMode) (*Package,
                for _, root := range ctxt.gopath() {
                        dir := ctxt.joinPath(root, "src", path)
                        isDir := ctxt.isDir(dir)
-                       binaryOnly = !isDir && mode&AllowBinary != 0 && ctxt.isFile(ctxt.joinPath(root, pkga))
+                       binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(root, pkga))
                        if isDir || binaryOnly {
                                p.Dir = dir
                                p.Root = root
@@ -426,14 +444,16 @@ Found:
                }
                p.PkgRoot = ctxt.joinPath(p.Root, "pkg")
                p.BinDir = ctxt.joinPath(p.Root, "bin")
-               p.PkgObj = ctxt.joinPath(p.Root, pkga)
+               if pkga != "" {
+                       p.PkgObj = ctxt.joinPath(p.Root, pkga)
+               }
        }
 
        if mode&FindOnly != 0 {
-               return p, nil
+               return p, pkgerr
        }
        if binaryOnly && (mode&AllowBinary) != 0 {
-               return p, nil
+               return p, pkgerr
        }
 
        dirs, err := ctxt.readDir(p.Dir)
@@ -467,7 +487,13 @@ Found:
                ext := name[i:]
                switch ext {
                case ".go", ".c", ".s", ".h", ".S":
-                       // tentatively okay
+                       // tentatively okay - read to make sure
+               case ".syso":
+                       // binary objects to add to package archive
+                       // Likely of the form foo_windows.syso, but
+                       // the name was vetted above with goodOSArchFile.
+                       p.SysoFiles = append(p.SysoFiles, name)
+                       continue
                default:
                        // skip
                        continue
@@ -586,7 +612,7 @@ Found:
                }
        }
        if p.Name == "" {
-               return p, fmt.Errorf("no Go source files in %s", p.Dir)
+               return p, &NoGoError{p.Dir}
        }
 
        p.Imports, p.ImportPos = cleanImports(imported)
@@ -601,7 +627,7 @@ Found:
                sort.Strings(p.SFiles)
        }
 
-       return p, nil
+       return p, pkgerr
 }
 
 func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) {
diff --git a/libgo/go/go/build/deps_test.go b/libgo/go/go/build/deps_test.go
new file mode 100644 (file)
index 0000000..4e9f32a
--- /dev/null
@@ -0,0 +1,424 @@
+// 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 file exercises the import parser but also checks that
+// some low-level packages do not have new dependencies added.
+
+package build_test
+
+import (
+       "go/build"
+       "sort"
+       "testing"
+)
+
+// pkgDeps defines the expected dependencies between packages in
+// the Go source tree.  It is a statement of policy.
+// Changes should not be made to this map without prior discussion.
+//
+// The map contains two kinds of entries:
+// 1) Lower-case keys are standard import paths and list the
+// allowed imports in that package.
+// 2) Upper-case keys define aliases for package sets, which can then
+// be used as dependencies by other rules.
+//
+// DO NOT CHANGE THIS DATA TO FIX BUILDS.
+// 
+var pkgDeps = map[string][]string{
+       // L0 is the lowest level, core, nearly unavoidable packages.
+       "errors":      {},
+       "io":          {"errors", "sync"},
+       "runtime":     {"unsafe"},
+       "sync":        {"sync/atomic"},
+       "sync/atomic": {"unsafe"},
+       "unsafe":      {},
+
+       "L0": {
+               "errors",
+               "io",
+               "runtime",
+               "sync",
+               "sync/atomic",
+               "unsafe",
+       },
+
+       // L1 adds simple functions and strings processing,
+       // but not Unicode tables.
+       "math":          {"unsafe"},
+       "math/cmplx":    {"math"},
+       "math/rand":     {"L0", "math"},
+       "sort":          {"math"},
+       "strconv":       {"L0", "unicode/utf8", "math"},
+       "unicode/utf16": {},
+       "unicode/utf8":  {},
+
+       "L1": {
+               "L0",
+               "math",
+               "math/cmplx",
+               "math/rand",
+               "sort",
+               "strconv",
+               "unicode/utf16",
+               "unicode/utf8",
+       },
+
+       // L2 adds Unicode and strings processing.
+       "bufio":   {"L0", "unicode/utf8", "bytes"},
+       "bytes":   {"L0", "unicode", "unicode/utf8"},
+       "path":    {"L0", "unicode/utf8", "strings"},
+       "strings": {"L0", "unicode", "unicode/utf8"},
+       "unicode": {},
+
+       "L2": {
+               "L1",
+               "bufio",
+               "bytes",
+               "path",
+               "strings",
+               "unicode",
+       },
+
+       // L3 adds reflection and some basic utility packages
+       // and interface definitions, but nothing that makes
+       // system calls.
+       "crypto":          {"L2", "hash"}, // interfaces
+       "crypto/cipher":   {"L2"},         // interfaces
+       "encoding/base32": {"L2"},
+       "encoding/base64": {"L2"},
+       "encoding/binary": {"L2", "reflect"},
+       "hash":            {"L2"}, // interfaces
+       "hash/adler32":    {"L2", "hash"},
+       "hash/crc32":      {"L2", "hash"},
+       "hash/crc64":      {"L2", "hash"},
+       "hash/fnv":        {"L2", "hash"},
+       "image":           {"L2", "image/color"}, // interfaces
+       "image/color":     {"L2"},                // interfaces
+       "reflect":         {"L2"},
+
+       "L3": {
+               "L2",
+               "crypto",
+               "crypto/cipher",
+               "encoding/base32",
+               "encoding/base64",
+               "encoding/binary",
+               "hash",
+               "hash/adler32",
+               "hash/crc32",
+               "hash/crc64",
+               "hash/fnv",
+               "image",
+               "image/color",
+               "reflect",
+       },
+
+       // End of linear dependency definitions.
+
+       // Operating system access.
+       "syscall":       {"L0", "unicode/utf16"},
+       "time":          {"L0", "syscall"},
+       "os":            {"L1", "os", "syscall", "time"},
+       "path/filepath": {"L2", "os", "syscall"},
+       "io/ioutil":     {"L2", "os", "path/filepath", "time"},
+       "os/exec":       {"L2", "os", "syscall"},
+       "os/signal":     {"L2", "os", "syscall"},
+
+       // OS enables basic operating system functionality,
+       // but not direct use of package syscall, nor os/signal.
+       "OS": {
+               "io/ioutil",
+               "os",
+               "os/exec",
+               "path/filepath",
+               "time",
+       },
+
+       // Formatted I/O: few dependencies (L1) but we must add reflect.
+       "fmt": {"L1", "os", "reflect"},
+       "log": {"L1", "os", "fmt", "time"},
+
+       // Packages used by testing must be low-level (L2+fmt).
+       "regexp":         {"L2", "regexp/syntax"},
+       "regexp/syntax":  {"L2"},
+       "runtime/debug":  {"L2", "fmt", "io/ioutil", "os"},
+       "runtime/pprof":  {"L2", "fmt", "text/tabwriter"},
+       "text/tabwriter": {"L2"},
+
+       "testing":        {"L2", "flag", "fmt", "os", "runtime/pprof", "time"},
+       "testing/iotest": {"L2", "log"},
+       "testing/quick":  {"L2", "flag", "fmt", "reflect"},
+
+       // L4 is defined as L3+fmt+log+time, because in general once
+       // you're using L3 packages, use of fmt, log, or time is not a big deal.
+       "L4": {
+               "L3",
+               "fmt",
+               "log",
+               "time",
+       },
+
+       // Go parser.
+       "go/ast":     {"L4", "OS", "go/scanner", "go/token"},
+       "go/doc":     {"L4", "go/ast", "go/token", "regexp", "text/template"},
+       "go/parser":  {"L4", "OS", "go/ast", "go/scanner", "go/token"},
+       "go/printer": {"L4", "OS", "go/ast", "go/scanner", "go/token", "text/tabwriter"},
+       "go/scanner": {"L4", "OS", "go/token"},
+       "go/token":   {"L4"},
+
+       "GOPARSER": {
+               "go/ast",
+               "go/doc",
+               "go/parser",
+               "go/printer",
+               "go/scanner",
+               "go/token",
+       },
+
+       // One of a kind.
+       "archive/tar":         {"L4", "OS"},
+       "archive/zip":         {"L4", "OS", "compress/flate"},
+       "compress/bzip2":      {"L4"},
+       "compress/flate":      {"L4"},
+       "compress/gzip":       {"L4", "compress/flate"},
+       "compress/lzw":        {"L4"},
+       "compress/zlib":       {"L4", "compress/flate"},
+       "database/sql":        {"L4", "database/sql/driver"},
+       "database/sql/driver": {"L4", "time"},
+       "debug/dwarf":         {"L4"},
+       "debug/elf":           {"L4", "OS", "debug/dwarf"},
+       "debug/gosym":         {"L4"},
+       "debug/macho":         {"L4", "OS", "debug/dwarf"},
+       "debug/pe":            {"L4", "OS", "debug/dwarf"},
+       "encoding/ascii85":    {"L4"},
+       "encoding/asn1":       {"L4", "math/big"},
+       "encoding/csv":        {"L4"},
+       "encoding/gob":        {"L4", "OS"},
+       "encoding/hex":        {"L4"},
+       "encoding/json":       {"L4"},
+       "encoding/pem":        {"L4"},
+       "encoding/xml":        {"L4"},
+       "flag":                {"L4", "OS"},
+       "go/build":            {"L4", "OS", "GOPARSER"},
+       "html":                {"L4"},
+       "image/draw":          {"L4"},
+       "image/gif":           {"L4", "compress/lzw"},
+       "image/jpeg":          {"L4"},
+       "image/png":           {"L4", "compress/zlib"},
+       "index/suffixarray":   {"L4", "regexp"},
+       "math/big":            {"L4"},
+       "mime":                {"L4", "OS", "syscall"},
+       "net/url":             {"L4"},
+       "text/scanner":        {"L4", "OS"},
+       "text/template/parse": {"L4"},
+
+       "html/template": {
+               "L4", "OS", "encoding/json", "html", "text/template",
+               "text/template/parse",
+       },
+       "text/template": {
+               "L4", "OS", "net/url", "text/template/parse",
+       },
+
+       // Cgo.
+       "runtime/cgo": {"L0", "C"},
+       "CGO":         {"C", "runtime/cgo"},
+
+       // Fake entry to satisfy the pseudo-import "C"
+       // that shows up in programs that use cgo.
+       "C": {},
+
+       "os/user": {"L4", "CGO", "syscall"},
+
+       // Basic networking.
+       // Because net must be used by any package that wants to
+       // do networking portably, it must have a small dependency set: just L1+basic os.
+       "net": {"L1", "CGO", "os", "syscall", "time"},
+
+       // NET enables use of basic network-related packages.
+       "NET": {
+               "net",
+               "mime",
+               "net/textproto",
+               "net/url",
+       },
+
+       // Uses of networking.
+       "log/syslog":    {"L4", "OS", "net"},
+       "net/mail":      {"L4", "NET", "OS"},
+       "net/textproto": {"L4", "OS", "net"},
+
+       // Core crypto.
+       "crypto/aes":    {"L3"},
+       "crypto/des":    {"L3"},
+       "crypto/hmac":   {"L3"},
+       "crypto/md5":    {"L3"},
+       "crypto/rc4":    {"L3"},
+       "crypto/sha1":   {"L3"},
+       "crypto/sha256": {"L3"},
+       "crypto/sha512": {"L3"},
+       "crypto/subtle": {"L3"},
+
+       "CRYPTO": {
+               "crypto/aes",
+               "crypto/des",
+               "crypto/hmac",
+               "crypto/md5",
+               "crypto/rc4",
+               "crypto/sha1",
+               "crypto/sha256",
+               "crypto/sha512",
+               "crypto/subtle",
+       },
+
+       // Random byte, number generation.
+       // This would be part of core crypto except that it imports
+       // math/big, which imports fmt.
+       "crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall"},
+
+       // Mathematical crypto: dependencies on fmt (L4) and math/big.
+       // We could avoid some of the fmt, but math/big imports fmt anyway.
+       "crypto/dsa":      {"L4", "CRYPTO", "math/big"},
+       "crypto/ecdsa":    {"L4", "CRYPTO", "crypto/elliptic", "math/big"},
+       "crypto/elliptic": {"L4", "CRYPTO", "math/big"},
+       "crypto/rsa":      {"L4", "CRYPTO", "crypto/rand", "math/big"},
+
+       "CRYPTO-MATH": {
+               "CRYPTO",
+               "crypto/dsa",
+               "crypto/ecdsa",
+               "crypto/elliptic",
+               "crypto/rand",
+               "crypto/rsa",
+               "encoding/asn1",
+               "math/big",
+       },
+
+       // SSL/TLS.
+       "crypto/tls": {
+               "L4", "CRYPTO-MATH", "CGO", "OS",
+               "crypto/x509", "encoding/pem", "net", "syscall",
+       },
+       "crypto/x509":      {"L4", "CRYPTO-MATH", "OS", "CGO", "crypto/x509/pkix", "encoding/pem", "syscall"},
+       "crypto/x509/pkix": {"L4", "CRYPTO-MATH"},
+
+       // Simple net+crypto-aware packages.
+       "mime/multipart": {"L4", "OS", "mime", "crypto/rand", "net/textproto"},
+       "net/smtp":       {"L4", "CRYPTO", "NET", "crypto/tls"},
+
+       // HTTP, kingpin of dependencies.
+       "net/http": {
+               "L4", "NET", "OS",
+               "compress/gzip", "crypto/tls", "mime/multipart", "runtime/debug",
+       },
+
+       // HTTP-using packages.
+       "expvar":            {"L4", "OS", "encoding/json", "net/http"},
+       "net/http/cgi":      {"L4", "NET", "OS", "crypto/tls", "net/http", "regexp"},
+       "net/http/fcgi":     {"L4", "NET", "OS", "net/http", "net/http/cgi"},
+       "net/http/httptest": {"L4", "NET", "OS", "crypto/tls", "flag", "net/http"},
+       "net/http/httputil": {"L4", "NET", "OS", "net/http"},
+       "net/http/pprof":    {"L4", "OS", "html/template", "net/http", "runtime/pprof"},
+       "net/rpc":           {"L4", "NET", "encoding/gob", "net/http", "text/template"},
+       "net/rpc/jsonrpc":   {"L4", "NET", "encoding/json", "net/rpc"},
+}
+
+// isMacro reports whether p is a package dependency macro
+// (uppercase name).
+func isMacro(p string) bool {
+       return 'A' <= p[0] && p[0] <= 'Z'
+}
+
+func allowed(pkg string) map[string]bool {
+       m := map[string]bool{}
+       var allow func(string)
+       allow = func(p string) {
+               if m[p] {
+                       return
+               }
+               m[p] = true // set even for macros, to avoid loop on cycle
+
+               // Upper-case names are macro-expanded.
+               if isMacro(p) {
+                       for _, pp := range pkgDeps[p] {
+                               allow(pp)
+                       }
+               }
+       }
+       for _, pp := range pkgDeps[pkg] {
+               allow(pp)
+       }
+       return m
+}
+
+var bools = []bool{false, true}
+var geese = []string{"darwin", "freebsd", "linux", "netbsd", "openbsd", "plan9", "windows"}
+var goarches = []string{"386", "amd64", "arm"}
+
+type osPkg struct {
+       goos, pkg string
+}
+
+// allowedErrors are the operating systems and packages known to contain errors
+// (currently just "no Go source files")
+var allowedErrors = map[osPkg]bool{
+       osPkg{"windows", "log/syslog"}: true,
+       osPkg{"plan9", "log/syslog"}:   true,
+}
+
+func TestDependencies(t *testing.T) {
+       var all []string
+
+       for k := range pkgDeps {
+               all = append(all, k)
+       }
+       sort.Strings(all)
+
+       ctxt := build.Default
+       test := func(mustImport bool) {
+               for _, pkg := range all {
+                       if isMacro(pkg) {
+                               continue
+                       }
+                       p, err := ctxt.Import(pkg, "", 0)
+                       if err != nil {
+                               if allowedErrors[osPkg{ctxt.GOOS, pkg}] {
+                                       continue
+                               }
+                               // Some of the combinations we try might not
+                               // be reasonable (like arm,plan9,cgo), so ignore
+                               // errors for the auto-generated combinations.
+                               if !mustImport {
+                                       continue
+                               }
+                               t.Errorf("%s/%s/cgo=%v %v", ctxt.GOOS, ctxt.GOARCH, ctxt.CgoEnabled, err)
+                               continue
+                       }
+                       ok := allowed(pkg)
+                       var bad []string
+                       for _, imp := range p.Imports {
+                               if !ok[imp] {
+                                       bad = append(bad, imp)
+                               }
+                       }
+                       if bad != nil {
+                               t.Errorf("%s/%s/cgo=%v unexpected dependency: %s imports %v", ctxt.GOOS, ctxt.GOARCH, ctxt.CgoEnabled, pkg, bad)
+                       }
+               }
+       }
+       test(true)
+
+       if testing.Short() {
+               t.Logf("skipping other systems")
+               return
+       }
+
+       for _, ctxt.GOOS = range geese {
+               for _, ctxt.GOARCH = range goarches {
+                       for _, ctxt.CgoEnabled = range bools {
+                               test(false)
+                       }
+               }
+       }
+}
diff --git a/libgo/go/go/parser/error_test.go b/libgo/go/go/parser/error_test.go
new file mode 100644 (file)
index 0000000..377c8b8
--- /dev/null
@@ -0,0 +1,166 @@
+// 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 file implements a parser test harness. The files in the testdata
+// directory are parsed and the errors reported are compared against the
+// error messages expected in the test files. The test files must end in
+// .src rather than .go so that they are not disturbed by gofmt runs.
+//
+// Expected errors are indicated in the test files by putting a comment
+// of the form /* ERROR "rx" */ immediately following an offending token.
+// The harness will verify that an error matching the regular expression
+// rx is reported at that source position.
+//
+// For instance, the following test file indicates that a "not declared"
+// error should be reported for the undeclared variable x:
+//
+//     package p
+//     func f() {
+//             _ = x /* ERROR "not declared" */ + 1
+//     }
+
+package parser
+
+import (
+       "go/scanner"
+       "go/token"
+       "io/ioutil"
+       "path/filepath"
+       "regexp"
+       "strings"
+       "testing"
+)
+
+const testdata = "testdata"
+
+// getFile assumes that each filename occurs at most once
+func getFile(filename string) (file *token.File) {
+       fset.Iterate(func(f *token.File) bool {
+               if f.Name() == filename {
+                       if file != nil {
+                               panic(filename + " used multiple times")
+                       }
+                       file = f
+               }
+               return true
+       })
+       return file
+}
+
+func getPos(filename string, offset int) token.Pos {
+       if f := getFile(filename); f != nil {
+               return f.Pos(offset)
+       }
+       return token.NoPos
+}
+
+// ERROR comments must be of the form /* ERROR "rx" */ and rx is
+// a regular expression that matches the expected error message.
+//
+var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`)
+
+// expectedErrors collects the regular expressions of ERROR comments found
+// in files and returns them as a map of error positions to error messages.
+//
+func expectedErrors(t *testing.T, filename string, src []byte) map[token.Pos]string {
+       errors := make(map[token.Pos]string)
+
+       var s scanner.Scanner
+       // file was parsed already - do not add it again to the file
+       // set otherwise the position information returned here will
+       // not match the position information collected by the parser
+       s.Init(getFile(filename), src, nil, scanner.ScanComments)
+       var prev token.Pos // position of last non-comment, non-semicolon token
+
+       for {
+               pos, tok, lit := s.Scan()
+               switch tok {
+               case token.EOF:
+                       return errors
+               case token.COMMENT:
+                       s := errRx.FindStringSubmatch(lit)
+                       if len(s) == 2 {
+                               errors[prev] = string(s[1])
+                       }
+               default:
+                       prev = pos
+               }
+       }
+
+       panic("unreachable")
+}
+
+// compareErrors compares the map of expected error messages with the list
+// of found errors and reports discrepancies.
+//
+func compareErrors(t *testing.T, expected map[token.Pos]string, found scanner.ErrorList) {
+       for _, error := range found {
+               // error.Pos is a token.Position, but we want
+               // a token.Pos so we can do a map lookup
+               pos := getPos(error.Pos.Filename, error.Pos.Offset)
+               if msg, found := expected[pos]; found {
+                       // we expect a message at pos; check if it matches
+                       rx, err := regexp.Compile(msg)
+                       if err != nil {
+                               t.Errorf("%s: %v", error.Pos, err)
+                               continue
+                       }
+                       if match := rx.MatchString(error.Msg); !match {
+                               t.Errorf("%s: %q does not match %q", error.Pos, error.Msg, msg)
+                               continue
+                       }
+                       // we have a match - eliminate this error
+                       delete(expected, pos)
+               } else {
+                       // To keep in mind when analyzing failed test output:
+                       // If the same error position occurs multiple times in errors,
+                       // this message will be triggered (because the first error at
+                       // the position removes this position from the expected errors).
+                       t.Errorf("%s: unexpected error: %s", error.Pos, error.Msg)
+               }
+       }
+
+       // there should be no expected errors left
+       if len(expected) > 0 {
+               t.Errorf("%d errors not reported:", len(expected))
+               for pos, msg := range expected {
+                       t.Errorf("%s: %s\n", fset.Position(pos), msg)
+               }
+       }
+}
+
+func checkErrors(t *testing.T, filename string, input interface{}) {
+       src, err := readSource(filename, input)
+       if err != nil {
+               t.Error(err)
+               return
+       }
+
+       _, err = ParseFile(fset, filename, src, DeclarationErrors)
+       found, ok := err.(scanner.ErrorList)
+       if err != nil && !ok {
+               t.Error(err)
+               return
+       }
+
+       // we are expecting the following errors
+       // (collect these after parsing a file so that it is found in the file set)
+       expected := expectedErrors(t, filename, src)
+
+       // verify errors returned by the parser
+       compareErrors(t, expected, found)
+}
+
+func TestErrors(t *testing.T) {
+       list, err := ioutil.ReadDir(testdata)
+       if err != nil {
+               t.Fatal(err)
+       }
+       for _, fi := range list {
+               name := fi.Name()
+               if !fi.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".src") {
+                       checkErrors(t, filepath.Join(testdata, name), nil)
+               }
+       }
+}
index a122baf..e362e13 100644 (file)
@@ -40,6 +40,13 @@ type parser struct {
        tok token.Token // one token look-ahead
        lit string      // token literal
 
+       // Error recovery
+       // (used to limit the number of calls to syncXXX functions
+       // w/o making scanning progress - avoids potential endless
+       // loops across multiple parser functions during error recovery)
+       syncPos token.Pos // last synchronization position
+       syncCnt int       // number of calls to syncXXX without progress
+
        // Non-syntactic parser control
        exprLev int // < 0: in control clause, >= 0: in expression
 
@@ -362,18 +369,36 @@ func (p *parser) expect(tok token.Token) token.Pos {
 // expectClosing is like expect but provides a better error message
 // for the common case of a missing comma before a newline.
 //
-func (p *parser) expectClosing(tok token.Token, construct string) token.Pos {
+func (p *parser) expectClosing(tok token.Token, context string) token.Pos {
        if p.tok != tok && p.tok == token.SEMICOLON && p.lit == "\n" {
-               p.error(p.pos, "missing ',' before newline in "+construct)
+               p.error(p.pos, "missing ',' before newline in "+context)
                p.next()
        }
        return p.expect(tok)
 }
 
 func (p *parser) expectSemi() {
+       // semicolon is optional before a closing ')' or '}'
        if p.tok != token.RPAREN && p.tok != token.RBRACE {
-               p.expect(token.SEMICOLON)
+               if p.tok == token.SEMICOLON {
+                       p.next()
+               } else {
+                       p.errorExpected(p.pos, "';'")
+                       syncStmt(p)
+               }
+       }
+}
+
+func (p *parser) atComma(context string) bool {
+       if p.tok == token.COMMA {
+               return true
+       }
+       if p.tok == token.SEMICOLON && p.lit == "\n" {
+               p.error(p.pos, "missing ',' before newline in "+context)
+               return true // "insert" the comma and continue
+
        }
+       return false
 }
 
 func assert(cond bool, msg string) {
@@ -382,6 +407,68 @@ func assert(cond bool, msg string) {
        }
 }
 
+// syncStmt advances to the next statement.
+// Used for synchronization after an error.
+//
+func syncStmt(p *parser) {
+       for {
+               switch p.tok {
+               case token.BREAK, token.CONST, token.CONTINUE, token.DEFER,
+                       token.FALLTHROUGH, token.FOR, token.GO, token.GOTO,
+                       token.IF, token.RETURN, token.SELECT, token.SWITCH,
+                       token.TYPE, token.VAR:
+                       // Return only if parser made some progress since last
+                       // sync or if it has not reached 10 sync calls without
+                       // progress. Otherwise consume at least one token to
+                       // avoid an endless parser loop (it is possible that
+                       // both parseOperand and parseStmt call syncStmt and
+                       // correctly do not advance, thus the need for the
+                       // invocation limit p.syncCnt).
+                       if p.pos == p.syncPos && p.syncCnt < 10 {
+                               p.syncCnt++
+                               return
+                       }
+                       if p.pos > p.syncPos {
+                               p.syncPos = p.pos
+                               p.syncCnt = 0
+                               return
+                       }
+                       // Reaching here indicates a parser bug, likely an
+                       // incorrect token list in this function, but it only
+                       // leads to skipping of possibly correct code if a
+                       // previous error is present, and thus is preferred
+                       // over a non-terminating parse.
+               case token.EOF:
+                       return
+               }
+               p.next()
+       }
+}
+
+// syncDecl advances to the next declaration.
+// Used for synchronization after an error.
+//
+func syncDecl(p *parser) {
+       for {
+               switch p.tok {
+               case token.CONST, token.TYPE, token.VAR:
+                       // see comments in syncStmt
+                       if p.pos == p.syncPos && p.syncCnt < 10 {
+                               p.syncCnt++
+                               return
+                       }
+                       if p.pos > p.syncPos {
+                               p.syncPos = p.pos
+                               p.syncCnt = 0
+                               return
+                       }
+               case token.EOF:
+                       return
+               }
+               p.next()
+       }
+}
+
 // ----------------------------------------------------------------------------
 // Identifiers
 
@@ -522,9 +609,11 @@ func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident {
        for i, x := range list {
                ident, isIdent := x.(*ast.Ident)
                if !isIdent {
-                       pos := x.Pos()
-                       p.errorExpected(pos, "identifier")
-                       ident = &ast.Ident{NamePos: pos, Name: "_"}
+                       if _, isBad := x.(*ast.BadExpr); !isBad {
+                               // only report error if it's a new one
+                               p.errorExpected(x.Pos(), "identifier")
+                       }
+                       ident = &ast.Ident{NamePos: x.Pos(), Name: "_"}
                }
                idents[i] = ident
        }
@@ -688,7 +777,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
                        // Go spec: The scope of an identifier denoting a function
                        // parameter or result variable is the function body.
                        p.declare(field, nil, scope, ast.Var, idents...)
-                       if p.tok != token.COMMA {
+                       if !p.atComma("parameter list") {
                                break
                        }
                        p.next()
@@ -991,19 +1080,19 @@ func (p *parser) parseOperand(lhs bool) ast.Expr {
 
        case token.FUNC:
                return p.parseFuncTypeOrLit()
+       }
 
-       default:
-               if typ := p.tryIdentOrType(true); typ != nil {
-                       // could be type for composite literal or conversion
-                       _, isIdent := typ.(*ast.Ident)
-                       assert(!isIdent, "type cannot be identifier")
-                       return typ
-               }
+       if typ := p.tryIdentOrType(true); typ != nil {
+               // could be type for composite literal or conversion
+               _, isIdent := typ.(*ast.Ident)
+               assert(!isIdent, "type cannot be identifier")
+               return typ
        }
 
+       // we have an error
        pos := p.pos
        p.errorExpected(pos, "operand")
-       p.next() // make progress
+       syncStmt(p)
        return &ast.BadExpr{From: pos, To: p.pos}
 }
 
@@ -1078,7 +1167,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
                        ellipsis = p.pos
                        p.next()
                }
-               if p.tok != token.COMMA {
+               if !p.atComma("argument list") {
                        break
                }
                p.next()
@@ -1118,7 +1207,7 @@ func (p *parser) parseElementList() (list []ast.Expr) {
 
        for p.tok != token.RBRACE && p.tok != token.EOF {
                list = append(list, p.parseElement(true))
-               if p.tok != token.COMMA {
+               if !p.atComma("composite literal") {
                        break
                }
                p.next()
@@ -1262,8 +1351,8 @@ L:
                                x = p.parseTypeAssertion(p.checkExpr(x))
                        default:
                                pos := p.pos
-                               p.next() // make progress
                                p.errorExpected(pos, "selector or type assertion")
+                               p.next() // make progress
                                x = &ast.BadExpr{From: pos, To: p.pos}
                        }
                case token.LBRACK:
@@ -1471,7 +1560,10 @@ func (p *parser) parseCallExpr() *ast.CallExpr {
        if call, isCall := x.(*ast.CallExpr); isCall {
                return call
        }
-       p.errorExpected(x.Pos(), "function/method call")
+       if _, isBad := x.(*ast.BadExpr); !isBad {
+               // only report error if it's a new one
+               p.errorExpected(x.Pos(), "function/method call")
+       }
        return nil
 }
 
@@ -1862,7 +1954,7 @@ func (p *parser) parseStmt() (s ast.Stmt) {
 
        switch p.tok {
        case token.CONST, token.TYPE, token.VAR:
-               s = &ast.DeclStmt{Decl: p.parseDecl()}
+               s = &ast.DeclStmt{Decl: p.parseDecl(syncStmt)}
        case
                // tokens that may start an expression
                token.IDENT, token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operands
@@ -1904,7 +1996,7 @@ func (p *parser) parseStmt() (s ast.Stmt) {
                // no statement found
                pos := p.pos
                p.errorExpected(pos, "statement")
-               p.next() // make progress
+               syncStmt(p)
                s = &ast.BadStmt{From: pos, To: p.pos}
        }
 
@@ -2095,8 +2187,13 @@ func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
        recv := par.List[0]
        base := deref(recv.Type)
        if _, isIdent := base.(*ast.Ident); !isIdent {
-               p.errorExpected(base.Pos(), "(unqualified) identifier")
-               par.List = []*ast.Field{{Type: &ast.BadExpr{From: recv.Pos(), To: recv.End()}}}
+               if _, isBad := base.(*ast.BadExpr); !isBad {
+                       // only report error if it's a new one
+                       p.errorExpected(base.Pos(), "(unqualified) identifier")
+               }
+               par.List = []*ast.Field{
+                       {Type: &ast.BadExpr{From: recv.Pos(), To: recv.End()}},
+               }
        }
 
        return par
@@ -2152,7 +2249,7 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
        return decl
 }
 
-func (p *parser) parseDecl() ast.Decl {
+func (p *parser) parseDecl(sync func(*parser)) ast.Decl {
        if p.trace {
                defer un(trace(p, "Declaration"))
        }
@@ -2174,9 +2271,8 @@ func (p *parser) parseDecl() ast.Decl {
        default:
                pos := p.pos
                p.errorExpected(pos, "declaration")
-               p.next() // make progress
-               decl := &ast.BadDecl{From: pos, To: p.pos}
-               return decl
+               sync(p)
+               return &ast.BadDecl{From: pos, To: p.pos}
        }
 
        return p.parseGenDecl(p.tok, f)
@@ -2215,7 +2311,7 @@ func (p *parser) parseFile() *ast.File {
                if p.mode&ImportsOnly == 0 {
                        // rest of package body
                        for p.tok != token.EOF {
-                               decls = append(decls, p.parseDecl())
+                               decls = append(decls, p.parseDecl(syncDecl))
                        }
                }
        }
index 93ca3d6..5e45acd 100644 (file)
@@ -14,87 +14,14 @@ import (
 
 var fset = token.NewFileSet()
 
-var illegalInputs = []interface{}{
-       nil,
-       3.14,
-       []byte(nil),
-       "foo!",
-       `package p; func f() { if /* should have condition */ {} };`,
-       `package p; func f() { if ; /* should have condition */ {} };`,
-       `package p; func f() { if f(); /* should have condition */ {} };`,
-       `package p; const c; /* should have constant value */`,
-       `package p; func f() { if _ = range x; true {} };`,
-       `package p; func f() { switch _ = range x; true {} };`,
-       `package p; func f() { for _ = range x ; ; {} };`,
-       `package p; func f() { for ; ; _ = range x {} };`,
-       `package p; func f() { for ; _ = range x ; {} };`,
-       `package p; func f() { switch t = t.(type) {} };`,
-       `package p; func f() { switch t, t = t.(type) {} };`,
-       `package p; func f() { switch t = t.(type), t {} };`,
-       `package p; var a = [1]int; /* illegal expression */`,
-       `package p; var a = [...]int; /* illegal expression */`,
-       `package p; var a = struct{} /* illegal expression */`,
-       `package p; var a = func(); /* illegal expression */`,
-       `package p; var a = interface{} /* illegal expression */`,
-       `package p; var a = []int /* illegal expression */`,
-       `package p; var a = map[int]int /* illegal expression */`,
-       `package p; var a = chan int; /* illegal expression */`,
-       `package p; var a = []int{[]int}; /* illegal expression */`,
-       `package p; var a = ([]int); /* illegal expression */`,
-       `package p; var a = a[[]int:[]int]; /* illegal expression */`,
-       `package p; var a = <- chan int; /* illegal expression */`,
-       `package p; func f() { select { case _ <- chan int: } };`,
-}
-
-func TestParseIllegalInputs(t *testing.T) {
-       for _, src := range illegalInputs {
-               _, err := ParseFile(fset, "", src, 0)
-               if err == nil {
-                       t.Errorf("ParseFile(%v) should have failed", src)
-               }
-       }
-}
-
-var validPrograms = []string{
-       "package p\n",
-       `package p;`,
-       `package p; import "fmt"; func f() { fmt.Println("Hello, World!") };`,
-       `package p; func f() { if f(T{}) {} };`,
-       `package p; func f() { _ = (<-chan int)(x) };`,
-       `package p; func f() { _ = (<-chan <-chan int)(x) };`,
-       `package p; func f(func() func() func());`,
-       `package p; func f(...T);`,
-       `package p; func f(float, ...int);`,
-       `package p; func f(x int, a ...int) { f(0, a...); f(1, a...,) };`,
-       `package p; func f(int,) {};`,
-       `package p; func f(...int,) {};`,
-       `package p; func f(x ...int,) {};`,
-       `package p; type T []int; var a []bool; func f() { if a[T{42}[0]] {} };`,
-       `package p; type T []int; func g(int) bool { return true }; func f() { if g(T{42}[0]) {} };`,
-       `package p; type T []int; func f() { for _ = range []int{T{42}[0]} {} };`,
-       `package p; var a = T{{1, 2}, {3, 4}}`,
-       `package p; func f() { select { case <- c: case c <- d: case c <- <- d: case <-c <- d: } };`,
-       `package p; func f() { select { case x := (<-c): } };`,
-       `package p; func f() { if ; true {} };`,
-       `package p; func f() { switch ; {} };`,
-       `package p; func f() { for _ = range "foo" + "bar" {} };`,
-}
-
-func TestParseValidPrograms(t *testing.T) {
-       for _, src := range validPrograms {
-               _, err := ParseFile(fset, "", src, SpuriousErrors)
-               if err != nil {
-                       t.Errorf("ParseFile(%q): %v", src, err)
-               }
-       }
-}
-
 var validFiles = []string{
        "parser.go",
        "parser_test.go",
+       "error_test.go",
+       "short_test.go",
 }
 
-func TestParse3(t *testing.T) {
+func TestParse(t *testing.T) {
        for _, filename := range validFiles {
                _, err := ParseFile(fset, filename, nil, DeclarationErrors)
                if err != nil {
@@ -116,7 +43,7 @@ func nameFilter(filename string) bool {
 
 func dirFilter(f os.FileInfo) bool { return nameFilter(f.Name()) }
 
-func TestParse4(t *testing.T) {
+func TestParseDir(t *testing.T) {
        path := "."
        pkgs, err := ParseDir(fset, path, dirFilter, 0)
        if err != nil {
@@ -158,7 +85,7 @@ func TestParseExpr(t *testing.T) {
        }
 
        // it must not crash
-       for _, src := range validPrograms {
+       for _, src := range valids {
                ParseExpr(src)
        }
 }
diff --git a/libgo/go/go/parser/short_test.go b/libgo/go/go/parser/short_test.go
new file mode 100644 (file)
index 0000000..238492b
--- /dev/null
@@ -0,0 +1,75 @@
+// 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.
+
+// This file contains test cases for short valid and invalid programs.
+
+package parser
+
+import "testing"
+
+var valids = []string{
+       "package p\n",
+       `package p;`,
+       `package p; import "fmt"; func f() { fmt.Println("Hello, World!") };`,
+       `package p; func f() { if f(T{}) {} };`,
+       `package p; func f() { _ = (<-chan int)(x) };`,
+       `package p; func f() { _ = (<-chan <-chan int)(x) };`,
+       `package p; func f(func() func() func());`,
+       `package p; func f(...T);`,
+       `package p; func f(float, ...int);`,
+       `package p; func f(x int, a ...int) { f(0, a...); f(1, a...,) };`,
+       `package p; func f(int,) {};`,
+       `package p; func f(...int,) {};`,
+       `package p; func f(x ...int,) {};`,
+       `package p; type T []int; var a []bool; func f() { if a[T{42}[0]] {} };`,
+       `package p; type T []int; func g(int) bool { return true }; func f() { if g(T{42}[0]) {} };`,
+       `package p; type T []int; func f() { for _ = range []int{T{42}[0]} {} };`,
+       `package p; var a = T{{1, 2}, {3, 4}}`,
+       `package p; func f() { select { case <- c: case c <- d: case c <- <- d: case <-c <- d: } };`,
+       `package p; func f() { select { case x := (<-c): } };`,
+       `package p; func f() { if ; true {} };`,
+       `package p; func f() { switch ; {} };`,
+       `package p; func f() { for _ = range "foo" + "bar" {} };`,
+}
+
+func TestValid(t *testing.T) {
+       for _, src := range valids {
+               checkErrors(t, src, src)
+       }
+}
+
+var invalids = []string{
+       `foo /* ERROR "expected 'package'" */ !`,
+       `package p; func f() { if { /* ERROR "expected operand" */ } };`,
+       `package p; func f() { if ; { /* ERROR "expected operand" */ } };`,
+       `package p; func f() { if f(); { /* ERROR "expected operand" */ } };`,
+       `package p; const c; /* ERROR "expected '='" */`,
+       `package p; func f() { if _ /* ERROR "expected condition" */ = range x; true {} };`,
+       `package p; func f() { switch _ /* ERROR "expected condition" */ = range x; true {} };`,
+       `package p; func f() { for _ = range x ; /* ERROR "expected '{'" */ ; {} };`,
+       `package p; func f() { for ; ; _ = range /* ERROR "expected operand" */ x {} };`,
+       `package p; func f() { for ; _ /* ERROR "expected condition" */ = range x ; {} };`,
+       `package p; func f() { switch t /* ERROR "expected condition" */ = t.(type) {} };`,
+       `package p; func f() { switch t /* ERROR "expected condition" */ , t = t.(type) {} };`,
+       `package p; func f() { switch t /* ERROR "expected condition" */ = t.(type), t {} };`,
+       `package p; var a = [ /* ERROR "expected expression" */ 1]int;`,
+       `package p; var a = [ /* ERROR "expected expression" */ ...]int;`,
+       `package p; var a = struct /* ERROR "expected expression" */ {}`,
+       `package p; var a = func /* ERROR "expected expression" */ ();`,
+       `package p; var a = interface /* ERROR "expected expression" */ {}`,
+       `package p; var a = [ /* ERROR "expected expression" */ ]int`,
+       `package p; var a = map /* ERROR "expected expression" */ [int]int`,
+       `package p; var a = chan /* ERROR "expected expression" */ int;`,
+       `package p; var a = []int{[ /* ERROR "expected expression" */ ]int};`,
+       `package p; var a = ( /* ERROR "expected expression" */ []int);`,
+       `package p; var a = a[[ /* ERROR "expected expression" */ ]int:[]int];`,
+       `package p; var a = <-  /* ERROR "expected expression" */ chan int;`,
+       `package p; func f() { select { case _ <- chan  /* ERROR "expected expression" */ int: } };`,
+}
+
+func TestInvalid(t *testing.T) {
+       for _, src := range invalids {
+               checkErrors(t, src, src)
+       }
+}
diff --git a/libgo/go/go/parser/testdata/commas.src b/libgo/go/go/parser/testdata/commas.src
new file mode 100644 (file)
index 0000000..af6e706
--- /dev/null
@@ -0,0 +1,19 @@
+// 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.
+
+// Test case for error messages/parser synchronization
+// after missing commas.
+
+package p
+
+var _ = []int{
+       0 /* ERROR "missing ','" */
+}
+
+var _ = []int{
+       0,
+       1,
+       2,
+       3 /* ERROR "missing ','" */
+}
diff --git a/libgo/go/go/parser/testdata/issue3106.src b/libgo/go/go/parser/testdata/issue3106.src
new file mode 100644 (file)
index 0000000..82796c8
--- /dev/null
@@ -0,0 +1,46 @@
+// 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.
+
+// Test case for issue 3106: Better synchronization of
+// parser after certain syntax errors.
+
+package main
+
+func f() {
+       var m Mutex
+       c := MakeCond(&m)
+       percent := 0
+       const step = 10
+       for i := 0; i < 5; i++ {
+               go func() {
+                       for {
+                               // Emulates some useful work.
+                               time.Sleep(1e8)
+                               m.Lock()
+                               defer
+                               if /* ERROR "expected operand, found 'if'" */ percent == 100 {
+                                       m.Unlock()
+                                       break
+                               }
+                               percent++
+                               if percent % step == 0 {
+                                       //c.Signal()
+                               }
+                               m.Unlock()
+                       }
+               }()
+       }
+       for {
+               m.Lock()
+               if percent == 0 || percent % step != 0 {
+                       c.Wait()
+               }
+               fmt.Print(",")
+               if percent == 100 {
+                       m.Unlock()
+                       break
+               }
+               m.Unlock()
+       }
+}
index 05b4ef5..6be3c09 100644 (file)
@@ -15,7 +15,7 @@ import (
        "unicode/utf8"
 )
 
-// Other formatting issues:
+// Formatting issues:
 // - better comment formatting for /*-style comments at the end of a line (e.g. a declaration)
 //   when the comment spans multiple lines; if such a comment is just two lines, formatting is
 //   not idempotent
@@ -964,6 +964,41 @@ func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, po
        }
 }
 
+// indentList reports whether an expression list would look better if it
+// were indented wholesale (starting with the very first element, rather
+// than starting at the first line break).
+//
+func (p *printer) indentList(list []ast.Expr) bool {
+       // Heuristic: indentList returns true if there are more than one multi-
+       // line element in the list, or if there is any element that is not
+       // starting on the same line as the previous one ends.
+       if len(list) >= 2 {
+               var b = p.lineFor(list[0].Pos())
+               var e = p.lineFor(list[len(list)-1].End())
+               if 0 < b && b < e {
+                       // list spans multiple lines
+                       n := 0 // multi-line element count
+                       line := b
+                       for _, x := range list {
+                               xb := p.lineFor(x.Pos())
+                               xe := p.lineFor(x.End())
+                               if line < xb {
+                                       // x is not starting on the same
+                                       // line as the previous one ended
+                                       return true
+                               }
+                               if xb < xe {
+                                       // x is a multi-line element
+                                       n++
+                               }
+                               line = xe
+                       }
+                       return n > 1
+               }
+       }
+       return false
+}
+
 func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {
        p.print(stmt.Pos())
 
@@ -1030,7 +1065,18 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {
                p.print(token.RETURN)
                if s.Results != nil {
                        p.print(blank)
-                       p.exprList(s.Pos(), s.Results, 1, 0, token.NoPos)
+                       // Use indentList heuristic to make corner cases look
+                       // better (issue 1207). A more systematic approach would
+                       // always indent, but this would cause significant
+                       // reformatting of the code base and not necessarily
+                       // lead to more nicely formatted code in general.
+                       if p.indentList(s.Results) {
+                               p.print(indent)
+                               p.exprList(s.Pos(), s.Results, 1, noIndent, token.NoPos)
+                               p.print(unindent)
+                       } else {
+                               p.exprList(s.Pos(), s.Results, 1, 0, token.NoPos)
+                       }
                }
 
        case *ast.BranchStmt:
@@ -1200,9 +1246,9 @@ func keepTypeColumn(specs []ast.Spec) []bool {
        return m
 }
 
-func (p *printer) valueSpec(s *ast.ValueSpec, keepType, doIndent bool) {
+func (p *printer) valueSpec(s *ast.ValueSpec, keepType bool) {
        p.setComment(s.Doc)
-       p.identList(s.Names, doIndent) // always present
+       p.identList(s.Names, false) // always present
        extraTabs := 3
        if s.Type != nil || keepType {
                p.print(vtab)
@@ -1290,7 +1336,7 @@ func (p *printer) genDecl(d *ast.GenDecl) {
                                        if i > 0 {
                                                p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection)
                                        }
-                                       p.valueSpec(s.(*ast.ValueSpec), keepType[i], false)
+                                       p.valueSpec(s.(*ast.ValueSpec), keepType[i])
                                        newSection = p.isMultiLine(s)
                                }
                        } else {
index ffca21e..4d70617 100644 (file)
@@ -55,12 +55,24 @@ func _f() {
        return T{
                1,
                2,
-       },
+       }, nil
+       return T{
+                       1,
+                       2,
+               },
+               T{
+                       x:      3,
+                       y:      4,
+               }, nil
+       return T{
+                       1,
+                       2,
+               },
                nil
        return T{
-               1,
-               2,
-       },
+                       1,
+                       2,
+               },
                T{
                        x:      3,
                        y:      4,
@@ -70,10 +82,10 @@ func _f() {
                z
        return func() {}
        return func() {
-               _ = 0
-       }, T{
-               1, 2,
-       }
+                       _ = 0
+               }, T{
+                       1, 2,
+               }
        return func() {
                _ = 0
        }
@@ -84,6 +96,37 @@ func _f() {
        }
 }
 
+// Formatting of multi-line returns: test cases from issue 1207.
+func F() (*T, os.Error) {
+       return &T{
+                       X:      1,
+                       Y:      2,
+               },
+               nil
+}
+
+func G() (*T, *T, os.Error) {
+       return &T{
+                       X:      1,
+                       Y:      2,
+               },
+               &T{
+                       X:      3,
+                       Y:      4,
+               },
+               nil
+}
+
+func _() interface{} {
+       return &fileStat{
+               name:           basename(file.name),
+               size:           mkSize(d.FileSizeHigh, d.FileSizeLow),
+               modTime:        mkModTime(d.LastWriteTime),
+               mode:           mkMode(d.FileAttributes),
+               sys:            mkSysFromFI(&d),
+       }, nil
+}
+
 // Formatting of if-statement headers.
 func _() {
        if true {
index 99945e9..bd03bc9 100644 (file)
@@ -55,6 +55,18 @@ func _f() {
        return T{
                        1,
                        2,
+               }, nil
+       return T{
+                       1,
+                       2,
+               },
+               T{
+                       x: 3,
+                       y: 4,
+               }, nil
+       return T{
+                       1,
+                       2,
                },
                nil
        return T{
@@ -84,6 +96,37 @@ func _f() {
        }
 }
 
+// Formatting of multi-line returns: test cases from issue 1207.
+func F() (*T, os.Error) {
+       return &T{
+               X: 1,
+               Y: 2,
+       },
+               nil
+}
+
+func G() (*T, *T, os.Error) {
+       return &T{
+               X: 1,
+               Y: 2,
+       },
+               &T{
+                       X: 3,
+                       Y: 4,
+               },
+               nil
+}
+
+func _() interface{} {
+       return &fileStat{
+                       name:    basename(file.name),
+                       size:    mkSize(d.FileSizeHigh, d.FileSizeLow),
+                       modTime: mkModTime(d.LastWriteTime),
+                       mode:    mkMode(d.FileAttributes),
+                       sys:     mkSysFromFI(&d),
+               }, nil
+}
+
 // Formatting of if-statement headers.
 func _() {
        if true {}
index 2395363..da50874 100644 (file)
@@ -109,7 +109,7 @@ const (
 func (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode) {
        // Explicitly initialize all fields since a scanner may be reused.
        if file.Size() != len(src) {
-               panic("file size does not match src len")
+               panic(fmt.Sprintf("file size (%d) does not match src len (%d)", file.Size(), len(src)))
        }
        s.file = file
        s.dir, _ = filepath.Split(file.Name())
index 3699ea1..f470fac 100644 (file)
@@ -29,7 +29,7 @@ can be safely embedded in an HTML document. The escaping is contextual, so
 actions can appear within JavaScript, CSS, and URI contexts.
 
 The security model used by this package assumes that template authors are
-trusted, while text/template Execute's data parameter is not. More details are
+trusted, while Execute's data parameter is not. More details are
 provided below.
 
 Example
index 7074834..54bf159 100644 (file)
@@ -173,6 +173,13 @@ type ReaderAt interface {
 // at offset off.  It returns the number of bytes written from p (0 <= n <= len(p))
 // and any error encountered that caused the write to stop early.
 // WriteAt must return a non-nil error if it returns n < len(p).
+//
+// If WriteAt is writing to a destination with a seek offset,
+// WriteAt should not affect nor be affected by the underlying
+// seek offset.
+//
+// Clients of WriteAt can execute parallel WriteAt calls on the same
+// destination if the ranges do not overlap.
 type WriterAt interface {
        WriteAt(p []byte, off int64) (n int, err error)
 }
index 02a407e..1d7f209 100644 (file)
@@ -13,8 +13,6 @@
 package log
 
 import (
-       "bytes"
-       _ "debug/elf"
        "fmt"
        "io"
        "os"
@@ -29,7 +27,7 @@ const (
        // order they appear (the order listed here) or the format they present (as
        // described in the comments).  A colon appears after these items:
        //      2009/0123 01:23:23.123123 /a/b/c/d.go:23: message
-       Ldate         = 1 << iota     // the date: 2009/0123
+       Ldate         = 1 << iota     // the date: 2009/01/23
        Ltime                         // the time: 01:23:23
        Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.
        Llongfile                     // full file name and line number: /a/b/c/d.go:23
@@ -42,11 +40,11 @@ const (
 // the Writer's Write method.  A Logger can be used simultaneously from
 // multiple goroutines; it guarantees to serialize access to the Writer.
 type Logger struct {
-       mu     sync.Mutex   // ensures atomic writes; protects the following fields
-       prefix string       // prefix to write at beginning of each line
-       flag   int          // properties
-       out    io.Writer    // destination for output
-       buf    bytes.Buffer // for accumulating text to write
+       mu     sync.Mutex // ensures atomic writes; protects the following fields
+       prefix string     // prefix to write at beginning of each line
+       flag   int        // properties
+       out    io.Writer  // destination for output
+       buf    []byte     // for accumulating text to write
 }
 
 // New creates a new Logger.   The out variable sets the
@@ -61,10 +59,10 @@ var std = New(os.Stderr, "", LstdFlags)
 
 // Cheap integer to fixed-width decimal ASCII.  Give a negative width to avoid zero-padding.
 // Knows the buffer has capacity.
-func itoa(buf *bytes.Buffer, i int, wid int) {
+func itoa(buf *[]byte, i int, wid int) {
        var u uint = uint(i)
        if u == 0 && wid <= 1 {
-               buf.WriteByte('0')
+               *buf = append(*buf, '0')
                return
        }
 
@@ -76,38 +74,33 @@ func itoa(buf *bytes.Buffer, i int, wid int) {
                wid--
                b[bp] = byte(u%10) + '0'
        }
-
-       // avoid slicing b to avoid an allocation.
-       for bp < len(b) {
-               buf.WriteByte(b[bp])
-               bp++
-       }
+       *buf = append(*buf, b[bp:]...)
 }
 
-func (l *Logger) formatHeader(buf *bytes.Buffer, t time.Time, file string, line int) {
-       buf.WriteString(l.prefix)
+func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {
+       *buf = append(*buf, l.prefix...)
        if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
                if l.flag&Ldate != 0 {
                        year, month, day := t.Date()
                        itoa(buf, year, 4)
-                       buf.WriteByte('/')
+                       *buf = append(*buf, '/')
                        itoa(buf, int(month), 2)
-                       buf.WriteByte('/')
+                       *buf = append(*buf, '/')
                        itoa(buf, day, 2)
-                       buf.WriteByte(' ')
+                       *buf = append(*buf, ' ')
                }
                if l.flag&(Ltime|Lmicroseconds) != 0 {
                        hour, min, sec := t.Clock()
                        itoa(buf, hour, 2)
-                       buf.WriteByte(':')
+                       *buf = append(*buf, ':')
                        itoa(buf, min, 2)
-                       buf.WriteByte(':')
+                       *buf = append(*buf, ':')
                        itoa(buf, sec, 2)
                        if l.flag&Lmicroseconds != 0 {
-                               buf.WriteByte('.')
+                               *buf = append(*buf, '.')
                                itoa(buf, t.Nanosecond()/1e3, 6)
                        }
-                       buf.WriteByte(' ')
+                       *buf = append(*buf, ' ')
                }
        }
        if l.flag&(Lshortfile|Llongfile) != 0 {
@@ -121,10 +114,10 @@ func (l *Logger) formatHeader(buf *bytes.Buffer, t time.Time, file string, line
                        }
                        file = short
                }
-               buf.WriteString(file)
-               buf.WriteByte(':')
+               *buf = append(*buf, file...)
+               *buf = append(*buf, ':')
                itoa(buf, line, -1)
-               buf.WriteString(": ")
+               *buf = append(*buf, ": "...)
        }
 }
 
@@ -151,13 +144,13 @@ func (l *Logger) Output(calldepth int, s string) error {
                }
                l.mu.Lock()
        }
-       l.buf.Reset()
+       l.buf = l.buf[:0]
        l.formatHeader(&l.buf, now, file, line)
-       l.buf.WriteString(s)
+       l.buf = append(l.buf, s...)
        if len(s) > 0 && s[len(s)-1] != '\n' {
-               l.buf.WriteByte('\n')
+               l.buf = append(l.buf, '\n')
        }
-       _, err := l.out.Write(l.buf.Bytes())
+       _, err := l.out.Write(l.buf)
        return err
 }
 
index 5f5aea1..7212087 100644 (file)
@@ -6,6 +6,7 @@ package net
 
 import (
        "flag"
+       "fmt"
        "regexp"
        "runtime"
        "testing"
@@ -32,7 +33,7 @@ func TestDialTimeout(t *testing.T) {
        numConns := listenerBacklog + 10
 
        // TODO(bradfitz): It's hard to test this in a portable
-       // way. This is unforunate, but works for now.
+       // way. This is unfortunate, but works for now.
        switch runtime.GOOS {
        case "linux":
                // The kernel will start accepting TCP connections before userspace
@@ -44,13 +45,25 @@ func TestDialTimeout(t *testing.T) {
                                errc <- err
                        }()
                }
-       case "darwin":
+       case "darwin", "windows":
                // At least OS X 10.7 seems to accept any number of
                // connections, ignoring listen's backlog, so resort
                // to connecting to a hopefully-dead 127/8 address.
                // Same for windows.
+               //
+               // Use an IANA reserved port (49151) instead of 80, because
+               // on our 386 builder, this Dial succeeds, connecting
+               // to an IIS web server somewhere.  The data center
+               // or VM or firewall must be stealing the TCP connection.
+               // 
+               // IANA Service Name and Transport Protocol Port Number Registry
+               // <http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml>
                go func() {
-                       _, err := DialTimeout("tcp", "127.0.71.111:80", 200*time.Millisecond)
+                       c, err := DialTimeout("tcp", "127.0.71.111:49151", 200*time.Millisecond)
+                       if err == nil {
+                               err = fmt.Errorf("unexpected: connected to %s!", c.RemoteAddr())
+                               c.Close()
+                       }
                        errc <- err
                }()
        default:
index f4ed8b8..e69cb31 100644 (file)
@@ -5,8 +5,6 @@
 package net
 
 import (
-       "bytes"
-       "fmt"
        "math/rand"
        "sort"
 )
@@ -45,20 +43,22 @@ func reverseaddr(addr string) (arpa string, err error) {
                return "", &DNSError{Err: "unrecognized address", Name: addr}
        }
        if ip.To4() != nil {
-               return fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa.", ip[15], ip[14], ip[13], ip[12]), nil
+               return itoa(int(ip[15])) + "." + itoa(int(ip[14])) + "." + itoa(int(ip[13])) + "." +
+                       itoa(int(ip[12])) + ".in-addr.arpa.", nil
        }
        // Must be IPv6
-       var buf bytes.Buffer
+       buf := make([]byte, 0, len(ip)*4+len("ip6.arpa."))
        // Add it, in reverse, to the buffer
        for i := len(ip) - 1; i >= 0; i-- {
-               s := fmt.Sprintf("%02x", ip[i])
-               buf.WriteByte(s[1])
-               buf.WriteByte('.')
-               buf.WriteByte(s[0])
-               buf.WriteByte('.')
+               v := ip[i]
+               buf = append(buf, hexDigit[v&0xF])
+               buf = append(buf, '.')
+               buf = append(buf, hexDigit[v>>4])
+               buf = append(buf, '.')
        }
        // Append "ip6.arpa." and return (buf already has the final .)
-       return buf.String() + "ip6.arpa.", nil
+       buf = append(buf, "ip6.arpa."...)
+       return string(buf), nil
 }
 
 // Find answer for name in dns message.
index 97c5062..b6ebe11 100644 (file)
@@ -7,11 +7,10 @@
 // This is intended to support name resolution during Dial.
 // It doesn't have to be blazing fast.
 //
-// Rather than write the usual handful of routines to pack and
-// unpack every message that can appear on the wire, we use
-// reflection to write a generic pack/unpack for structs and then
-// use it.  Thus, if in the future we need to define new message
-// structs, no new pack/unpack/printing code needs to be written.
+// Each message structure has a Walk method that is used by
+// a generic pack/unpack routine. Thus, if in the future we need
+// to define new message structs, no new pack/unpack/printing code
+// needs to be written.
 //
 // The first half of this file defines the DNS message formats.
 // The second half implements the conversion to and from wire format.
 
 package net
 
-import (
-       "fmt"
-       "os"
-       "reflect"
-)
-
 // Packet formats
 
 // Wire constants.
@@ -75,6 +68,20 @@ const (
        dnsRcodeRefused        = 5
 )
 
+// A dnsStruct describes how to iterate over its fields to emulate
+// reflective marshalling.
+type dnsStruct interface {
+       // Walk iterates over fields of a structure and calls f
+       // with a reference to that field, the name of the field
+       // and a tag ("", "domain", "ipv4", "ipv6") specifying
+       // particular encodings. Possible concrete types
+       // for v are *uint16, *uint32, *string, or []byte, and
+       // *int, *bool in the case of dnsMsgHdr.
+       // Whenever f returns false, Walk must stop and return
+       // false, and otherwise return true.
+       Walk(f func(v interface{}, name, tag string) (ok bool)) (ok bool)
+}
+
 // The wire format for the DNS packet header.
 type dnsHeader struct {
        Id                                 uint16
@@ -82,6 +89,15 @@ type dnsHeader struct {
        Qdcount, Ancount, Nscount, Arcount uint16
 }
 
+func (h *dnsHeader) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return f(&h.Id, "Id", "") &&
+               f(&h.Bits, "Bits", "") &&
+               f(&h.Qdcount, "Qdcount", "") &&
+               f(&h.Ancount, "Ancount", "") &&
+               f(&h.Nscount, "Nscount", "") &&
+               f(&h.Arcount, "Arcount", "")
+}
+
 const (
        // dnsHeader.Bits
        _QR = 1 << 15 // query/response (response=1)
@@ -98,6 +114,12 @@ type dnsQuestion struct {
        Qclass uint16
 }
 
+func (q *dnsQuestion) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return f(&q.Name, "Name", "domain") &&
+               f(&q.Qtype, "Qtype", "") &&
+               f(&q.Qclass, "Qclass", "")
+}
+
 // DNS responses (resource records).
 // There are many types of messages,
 // but they all share the same header.
@@ -113,7 +135,16 @@ func (h *dnsRR_Header) Header() *dnsRR_Header {
        return h
 }
 
+func (h *dnsRR_Header) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return f(&h.Name, "Name", "domain") &&
+               f(&h.Rrtype, "Rrtype", "") &&
+               f(&h.Class, "Class", "") &&
+               f(&h.Ttl, "Ttl", "") &&
+               f(&h.Rdlength, "Rdlength", "")
+}
+
 type dnsRR interface {
+       dnsStruct
        Header() *dnsRR_Header
 }
 
@@ -128,6 +159,10 @@ func (rr *dnsRR_CNAME) Header() *dnsRR_Header {
        return &rr.Hdr
 }
 
+func (rr *dnsRR_CNAME) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return rr.Hdr.Walk(f) && f(&rr.Cname, "Cname", "domain")
+}
+
 type dnsRR_HINFO struct {
        Hdr dnsRR_Header
        Cpu string
@@ -138,6 +173,10 @@ func (rr *dnsRR_HINFO) Header() *dnsRR_Header {
        return &rr.Hdr
 }
 
+func (rr *dnsRR_HINFO) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return rr.Hdr.Walk(f) && f(&rr.Cpu, "Cpu", "") && f(&rr.Os, "Os", "")
+}
+
 type dnsRR_MB struct {
        Hdr dnsRR_Header
        Mb  string `net:"domain-name"`
@@ -147,6 +186,10 @@ func (rr *dnsRR_MB) Header() *dnsRR_Header {
        return &rr.Hdr
 }
 
+func (rr *dnsRR_MB) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return rr.Hdr.Walk(f) && f(&rr.Mb, "Mb", "domain")
+}
+
 type dnsRR_MG struct {
        Hdr dnsRR_Header
        Mg  string `net:"domain-name"`
@@ -156,6 +199,10 @@ func (rr *dnsRR_MG) Header() *dnsRR_Header {
        return &rr.Hdr
 }
 
+func (rr *dnsRR_MG) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return rr.Hdr.Walk(f) && f(&rr.Mg, "Mg", "domain")
+}
+
 type dnsRR_MINFO struct {
        Hdr   dnsRR_Header
        Rmail string `net:"domain-name"`
@@ -166,6 +213,10 @@ func (rr *dnsRR_MINFO) Header() *dnsRR_Header {
        return &rr.Hdr
 }
 
+func (rr *dnsRR_MINFO) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return rr.Hdr.Walk(f) && f(&rr.Rmail, "Rmail", "domain") && f(&rr.Email, "Email", "domain")
+}
+
 type dnsRR_MR struct {
        Hdr dnsRR_Header
        Mr  string `net:"domain-name"`
@@ -175,6 +226,10 @@ func (rr *dnsRR_MR) Header() *dnsRR_Header {
        return &rr.Hdr
 }
 
+func (rr *dnsRR_MR) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return rr.Hdr.Walk(f) && f(&rr.Mr, "Mr", "domain")
+}
+
 type dnsRR_MX struct {
        Hdr  dnsRR_Header
        Pref uint16
@@ -185,6 +240,10 @@ func (rr *dnsRR_MX) Header() *dnsRR_Header {
        return &rr.Hdr
 }
 
+func (rr *dnsRR_MX) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return rr.Hdr.Walk(f) && f(&rr.Pref, "Pref", "") && f(&rr.Mx, "Mx", "domain")
+}
+
 type dnsRR_NS struct {
        Hdr dnsRR_Header
        Ns  string `net:"domain-name"`
@@ -194,6 +253,10 @@ func (rr *dnsRR_NS) Header() *dnsRR_Header {
        return &rr.Hdr
 }
 
+func (rr *dnsRR_NS) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return rr.Hdr.Walk(f) && f(&rr.Ns, "Ns", "domain")
+}
+
 type dnsRR_PTR struct {
        Hdr dnsRR_Header
        Ptr string `net:"domain-name"`
@@ -203,6 +266,10 @@ func (rr *dnsRR_PTR) Header() *dnsRR_Header {
        return &rr.Hdr
 }
 
+func (rr *dnsRR_PTR) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return rr.Hdr.Walk(f) && f(&rr.Ptr, "Ptr", "domain")
+}
+
 type dnsRR_SOA struct {
        Hdr     dnsRR_Header
        Ns      string `net:"domain-name"`
@@ -218,6 +285,17 @@ func (rr *dnsRR_SOA) Header() *dnsRR_Header {
        return &rr.Hdr
 }
 
+func (rr *dnsRR_SOA) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return rr.Hdr.Walk(f) &&
+               f(&rr.Ns, "Ns", "domain") &&
+               f(&rr.Mbox, "Mbox", "domain") &&
+               f(&rr.Serial, "Serial", "") &&
+               f(&rr.Refresh, "Refresh", "") &&
+               f(&rr.Retry, "Retry", "") &&
+               f(&rr.Expire, "Expire", "") &&
+               f(&rr.Minttl, "Minttl", "")
+}
+
 type dnsRR_TXT struct {
        Hdr dnsRR_Header
        Txt string // not domain name
@@ -227,6 +305,10 @@ func (rr *dnsRR_TXT) Header() *dnsRR_Header {
        return &rr.Hdr
 }
 
+func (rr *dnsRR_TXT) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return rr.Hdr.Walk(f) && f(&rr.Txt, "Txt", "")
+}
+
 type dnsRR_SRV struct {
        Hdr      dnsRR_Header
        Priority uint16
@@ -239,6 +321,14 @@ func (rr *dnsRR_SRV) Header() *dnsRR_Header {
        return &rr.Hdr
 }
 
+func (rr *dnsRR_SRV) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return rr.Hdr.Walk(f) &&
+               f(&rr.Priority, "Priority", "") &&
+               f(&rr.Weight, "Weight", "") &&
+               f(&rr.Port, "Port", "") &&
+               f(&rr.Target, "Target", "domain")
+}
+
 type dnsRR_A struct {
        Hdr dnsRR_Header
        A   uint32 `net:"ipv4"`
@@ -248,6 +338,10 @@ func (rr *dnsRR_A) Header() *dnsRR_Header {
        return &rr.Hdr
 }
 
+func (rr *dnsRR_A) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return rr.Hdr.Walk(f) && f(&rr.A, "A", "ipv4")
+}
+
 type dnsRR_AAAA struct {
        Hdr  dnsRR_Header
        AAAA [16]byte `net:"ipv6"`
@@ -257,6 +351,10 @@ func (rr *dnsRR_AAAA) Header() *dnsRR_Header {
        return &rr.Hdr
 }
 
+func (rr *dnsRR_AAAA) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return rr.Hdr.Walk(f) && f(rr.AAAA[:], "AAAA", "ipv6")
+}
+
 // Packing and unpacking.
 //
 // All the packers and unpackers take a (msg []byte, off int)
@@ -386,134 +484,107 @@ Loop:
        return s, off1, true
 }
 
-// TODO(rsc): Move into generic library?
-// Pack a reflect.StructValue into msg.  Struct members can only be uint16, uint32, string,
-// [n]byte, and other (often anonymous) structs.
-func packStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) {
-       for i := 0; i < val.NumField(); i++ {
-               f := val.Type().Field(i)
-               switch fv := val.Field(i); fv.Kind() {
+// packStruct packs a structure into msg at specified offset off, and
+// returns off1 such that msg[off:off1] is the encoded data.
+func packStruct(any dnsStruct, msg []byte, off int) (off1 int, ok bool) {
+       ok = any.Walk(func(field interface{}, name, tag string) bool {
+               switch fv := field.(type) {
                default:
-                       fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type)
-                       return len(msg), false
-               case reflect.Struct:
-                       off, ok = packStructValue(fv, msg, off)
-               case reflect.Uint16:
+                       println("net: dns: unknown packing type")
+                       return false
+               case *uint16:
+                       i := *fv
                        if off+2 > len(msg) {
-                               return len(msg), false
+                               return false
                        }
-                       i := fv.Uint()
                        msg[off] = byte(i >> 8)
                        msg[off+1] = byte(i)
                        off += 2
-               case reflect.Uint32:
-                       if off+4 > len(msg) {
-                               return len(msg), false
-                       }
-                       i := fv.Uint()
+               case *uint32:
+                       i := *fv
                        msg[off] = byte(i >> 24)
                        msg[off+1] = byte(i >> 16)
                        msg[off+2] = byte(i >> 8)
                        msg[off+3] = byte(i)
                        off += 4
-               case reflect.Array:
-                       if fv.Type().Elem().Kind() != reflect.Uint8 {
-                               fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type)
-                               return len(msg), false
-                       }
-                       n := fv.Len()
+               case []byte:
+                       n := len(fv)
                        if off+n > len(msg) {
-                               return len(msg), false
+                               return false
                        }
-                       reflect.Copy(reflect.ValueOf(msg[off:off+n]), fv)
+                       copy(msg[off:off+n], fv)
                        off += n
-               case reflect.String:
-                       // There are multiple string encodings.
-                       // The tag distinguishes ordinary strings from domain names.
-                       s := fv.String()
-                       switch f.Tag {
+               case *string:
+                       s := *fv
+                       switch tag {
                        default:
-                               fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag)
-                               return len(msg), false
-                       case `net:"domain-name"`:
+                               println("net: dns: unknown string tag", tag)
+                               return false
+                       case "domain":
                                off, ok = packDomainName(s, msg, off)
                                if !ok {
-                                       return len(msg), false
+                                       return false
                                }
                        case "":
                                // Counted string: 1 byte length.
                                if len(s) > 255 || off+1+len(s) > len(msg) {
-                                       return len(msg), false
+                                       return false
                                }
                                msg[off] = byte(len(s))
                                off++
                                off += copy(msg[off:], s)
                        }
                }
+               return true
+       })
+       if !ok {
+               return len(msg), false
        }
        return off, true
 }
 
-func structValue(any interface{}) reflect.Value {
-       return reflect.ValueOf(any).Elem()
-}
-
-func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
-       off, ok = packStructValue(structValue(any), msg, off)
-       return off, ok
-}
-
-// TODO(rsc): Move into generic library?
-// Unpack a reflect.StructValue from msg.
-// Same restrictions as packStructValue.
-func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) {
-       for i := 0; i < val.NumField(); i++ {
-               f := val.Type().Field(i)
-               switch fv := val.Field(i); fv.Kind() {
+// unpackStruct decodes msg[off:] into the given structure, and
+// returns off1 such that msg[off:off1] is the encoded data.
+func unpackStruct(any dnsStruct, msg []byte, off int) (off1 int, ok bool) {
+       ok = any.Walk(func(field interface{}, name, tag string) bool {
+               switch fv := field.(type) {
                default:
-                       fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type)
-                       return len(msg), false
-               case reflect.Struct:
-                       off, ok = unpackStructValue(fv, msg, off)
-               case reflect.Uint16:
+                       println("net: dns: unknown packing type")
+                       return false
+               case *uint16:
                        if off+2 > len(msg) {
-                               return len(msg), false
+                               return false
                        }
-                       i := uint16(msg[off])<<8 | uint16(msg[off+1])
-                       fv.SetUint(uint64(i))
+                       *fv = uint16(msg[off])<<8 | uint16(msg[off+1])
                        off += 2
-               case reflect.Uint32:
+               case *uint32:
                        if off+4 > len(msg) {
-                               return len(msg), false
+                               return false
                        }
-                       i := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3])
-                       fv.SetUint(uint64(i))
+                       *fv = uint32(msg[off])<<24 | uint32(msg[off+1])<<16 |
+                               uint32(msg[off+2])<<8 | uint32(msg[off+3])
                        off += 4
-               case reflect.Array:
-                       if fv.Type().Elem().Kind() != reflect.Uint8 {
-                               fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type)
-                               return len(msg), false
-                       }
-                       n := fv.Len()
+               case []byte:
+                       n := len(fv)
                        if off+n > len(msg) {
-                               return len(msg), false
+                               return false
                        }
-                       reflect.Copy(fv, reflect.ValueOf(msg[off:off+n]))
+                       copy(fv, msg[off:off+n])
                        off += n
-               case reflect.String:
+               case *string:
                        var s string
-                       switch f.Tag {
+                       switch tag {
                        default:
-                               fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag)
-                               return len(msg), false
-                       case `net:"domain-name"`:
+                               println("net: dns: unknown string tag", tag)
+                               return false
+                       case "domain":
                                s, off, ok = unpackDomainName(msg, off)
                                if !ok {
-                                       return len(msg), false
+                                       return false
                                }
                        case "":
                                if off >= len(msg) || off+1+int(msg[off]) > len(msg) {
-                                       return len(msg), false
+                                       return false
                                }
                                n := int(msg[off])
                                off++
@@ -524,51 +595,77 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok boo
                                off += n
                                s = string(b)
                        }
-                       fv.SetString(s)
+                       *fv = s
                }
+               return true
+       })
+       if !ok {
+               return len(msg), false
        }
        return off, true
 }
 
-func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
-       off, ok = unpackStructValue(structValue(any), msg, off)
-       return off, ok
-}
-
-// Generic struct printer.
-// Doesn't care about the string tag `net:"domain-name"`,
-// but does look for an `net:"ipv4"` tag on uint32 variables
-// and the `net:"ipv6"` tag on array variables,
-// printing them as IP addresses.
-func printStructValue(val reflect.Value) string {
+// Generic struct printer. Prints fields with tag "ipv4" or "ipv6"
+// as IP addresses.
+func printStruct(any dnsStruct) string {
        s := "{"
-       for i := 0; i < val.NumField(); i++ {
-               if i > 0 {
+       i := 0
+       any.Walk(func(val interface{}, name, tag string) bool {
+               i++
+               if i > 1 {
                        s += ", "
                }
-               f := val.Type().Field(i)
-               if !f.Anonymous {
-                       s += f.Name + "="
-               }
-               fval := val.Field(i)
-               if fv := fval; fv.Kind() == reflect.Struct {
-                       s += printStructValue(fv)
-               } else if fv := fval; (fv.Kind() == reflect.Uint || fv.Kind() == reflect.Uint8 || fv.Kind() == reflect.Uint16 || fv.Kind() == reflect.Uint32 || fv.Kind() == reflect.Uint64 || fv.Kind() == reflect.Uintptr) && f.Tag == `net:"ipv4"` {
-                       i := fv.Uint()
+               s += name + "="
+               switch tag {
+               case "ipv4":
+                       i := val.(uint32)
                        s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String()
-               } else if fv := fval; fv.Kind() == reflect.Array && f.Tag == `net:"ipv6"` {
-                       i := fv.Interface().([]byte)
+               case "ipv6":
+                       i := val.([]byte)
                        s += IP(i).String()
-               } else {
-                       s += fmt.Sprint(fval.Interface())
+               default:
+                       var i int64
+                       switch v := val.(type) {
+                       default:
+                               // can't really happen.
+                               s += "<unknown type>"
+                               return true
+                       case *string:
+                               s += *v
+                               return true
+                       case []byte:
+                               s += string(v)
+                               return true
+                       case *bool:
+                               if *v {
+                                       s += "true"
+                               } else {
+                                       s += "false"
+                               }
+                               return true
+                       case *int:
+                               i = int64(*v)
+                       case *uint:
+                               i = int64(*v)
+                       case *uint8:
+                               i = int64(*v)
+                       case *uint16:
+                               i = int64(*v)
+                       case *uint32:
+                               i = int64(*v)
+                       case *uint64:
+                               i = int64(*v)
+                       case *uintptr:
+                               i = int64(*v)
+                       }
+                       s += itoa(int(i))
                }
-       }
+               return true
+       })
        s += "}"
        return s
 }
 
-func printStruct(any interface{}) string { return printStructValue(structValue(any)) }
-
 // Resource record packer.
 func packRR(rr dnsRR, msg []byte, off int) (off2 int, ok bool) {
        var off1 int
@@ -627,6 +724,17 @@ type dnsMsgHdr struct {
        rcode               int
 }
 
+func (h *dnsMsgHdr) Walk(f func(v interface{}, name, tag string) bool) bool {
+       return f(&h.id, "id", "") &&
+               f(&h.response, "response", "") &&
+               f(&h.opcode, "opcode", "") &&
+               f(&h.authoritative, "authoritative", "") &&
+               f(&h.truncated, "truncated", "") &&
+               f(&h.recursion_desired, "recursion_desired", "") &&
+               f(&h.recursion_available, "recursion_available", "") &&
+               f(&h.rcode, "rcode", "")
+}
+
 type dnsMsg struct {
        dnsMsgHdr
        question []dnsQuestion
index 06152a0..c39dbdb 100644 (file)
@@ -6,6 +6,7 @@ package net
 
 import (
        "encoding/hex"
+       "reflect"
        "testing"
 )
 
@@ -19,6 +20,7 @@ func TestDNSParseSRVReply(t *testing.T) {
        if !ok {
                t.Fatalf("unpacking packet failed")
        }
+       msg.String() // exercise this code path
        if g, e := len(msg.answer), 5; g != e {
                t.Errorf("len(msg.answer) = %d; want %d", g, e)
        }
@@ -38,6 +40,16 @@ func TestDNSParseSRVReply(t *testing.T) {
                t.Errorf("len(addrs) = %d; want %d", g, e)
                t.Logf("addrs = %#v", addrs)
        }
+       // repack and unpack.
+       data2, ok := msg.Pack()
+       msg2 := new(dnsMsg)
+       msg2.Unpack(data2)
+       switch {
+       case !ok:
+               t.Errorf("failed to repack message")
+       case !reflect.DeepEqual(msg, msg2):
+               t.Errorf("repacked message differs from original")
+       }
 }
 
 func TestDNSParseCorruptSRVReply(t *testing.T) {
@@ -50,6 +62,7 @@ func TestDNSParseCorruptSRVReply(t *testing.T) {
        if !ok {
                t.Fatalf("unpacking packet failed")
        }
+       msg.String() // exercise this code path
        if g, e := len(msg.answer), 5; g != e {
                t.Errorf("len(msg.answer) = %d; want %d", g, e)
        }
index a1d62ac..085e423 100644 (file)
@@ -84,7 +84,8 @@ func (p *pollster) StopWaiting(fd int, bits uint) {
 
        events, already := p.events[fd]
        if !already {
-               print("Epoll unexpected fd=", fd, "\n")
+               // The fd returned by the kernel may have been
+               // cancelled already; return silently.
                return
        }
 
index 868388e..95c0b66 100644 (file)
@@ -27,7 +27,8 @@ type connFile interface {
 }
 
 func testFileListener(t *testing.T, net, laddr string) {
-       if net == "tcp" {
+       switch net {
+       case "tcp", "tcp4", "tcp6":
                laddr += ":0" // any available port
        }
        l, err := Listen(net, laddr)
@@ -55,20 +56,52 @@ func testFileListener(t *testing.T, net, laddr string) {
        }
 }
 
+var fileListenerTests = []struct {
+       net   string
+       laddr string
+       ipv6  bool // test with underlying AF_INET6 socket
+       linux bool // test with abstract unix domain socket, a Linux-ism
+}{
+       {net: "tcp", laddr: ""},
+       {net: "tcp", laddr: "0.0.0.0"},
+       {net: "tcp", laddr: "[::ffff:0.0.0.0]"},
+       {net: "tcp", laddr: "[::]", ipv6: true},
+
+       {net: "tcp", laddr: "127.0.0.1"},
+       {net: "tcp", laddr: "[::ffff:127.0.0.1]"},
+       {net: "tcp", laddr: "[::1]", ipv6: true},
+
+       {net: "tcp4", laddr: ""},
+       {net: "tcp4", laddr: "0.0.0.0"},
+       {net: "tcp4", laddr: "[::ffff:0.0.0.0]"},
+
+       {net: "tcp4", laddr: "127.0.0.1"},
+       {net: "tcp4", laddr: "[::ffff:127.0.0.1]"},
+
+       {net: "tcp6", laddr: "", ipv6: true},
+       {net: "tcp6", laddr: "[::]", ipv6: true},
+
+       {net: "tcp6", laddr: "[::1]", ipv6: true},
+
+       {net: "unix", laddr: "@gotest/net", linux: true},
+       {net: "unixpacket", laddr: "@gotest/net", linux: true},
+}
+
 func TestFileListener(t *testing.T) {
-       if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
+       switch runtime.GOOS {
+       case "plan9", "windows":
+               t.Logf("skipping test on %q", runtime.GOOS)
                return
        }
-       testFileListener(t, "tcp", "127.0.0.1")
-       testFileListener(t, "tcp", "127.0.0.1")
-       if supportsIPv6 && supportsIPv4map {
-               testFileListener(t, "tcp", "[::ffff:127.0.0.1]")
-               testFileListener(t, "tcp", "127.0.0.1")
-               testFileListener(t, "tcp", "[::ffff:127.0.0.1]")
-       }
-       if runtime.GOOS == "linux" {
-               testFileListener(t, "unix", "@gotest/net")
-               testFileListener(t, "unixpacket", "@gotest/net")
+
+       for _, tt := range fileListenerTests {
+               if skipServerTest(tt.net, "unix", tt.laddr, tt.ipv6, false, tt.linux) {
+                       continue
+               }
+               if skipServerTest(tt.net, "unixpacket", tt.laddr, tt.ipv6, false, tt.linux) {
+                       continue
+               }
+               testFileListener(t, tt.net, tt.laddr)
        }
 }
 
@@ -98,9 +131,13 @@ func testFilePacketConn(t *testing.T, pcf packetConnFile, listen bool) {
 }
 
 func testFilePacketConnListen(t *testing.T, net, laddr string) {
+       switch net {
+       case "udp", "udp4", "udp6":
+               laddr += ":0" // any available port
+       }
        l, err := ListenPacket(net, laddr)
        if err != nil {
-               t.Fatalf("Listen failed: %v", err)
+               t.Fatalf("ListenPacket failed: %v", err)
        }
        testFilePacketConn(t, l.(packetConnFile), true)
        if err := l.Close(); err != nil {
@@ -109,6 +146,10 @@ func testFilePacketConnListen(t *testing.T, net, laddr string) {
 }
 
 func testFilePacketConnDial(t *testing.T, net, raddr string) {
+       switch net {
+       case "udp", "udp4", "udp6":
+               raddr += ":12345"
+       }
        c, err := Dial(net, raddr)
        if err != nil {
                t.Fatalf("Dial failed: %v", err)
@@ -119,19 +160,42 @@ func testFilePacketConnDial(t *testing.T, net, raddr string) {
        }
 }
 
+var filePacketConnTests = []struct {
+       net   string
+       addr  string
+       ipv6  bool // test with underlying AF_INET6 socket
+       linux bool // test with abstract unix domain socket, a Linux-ism
+}{
+       {net: "udp", addr: "127.0.0.1"},
+       {net: "udp", addr: "[::ffff:127.0.0.1]"},
+       {net: "udp", addr: "[::1]", ipv6: true},
+
+       {net: "udp4", addr: "127.0.0.1"},
+       {net: "udp4", addr: "[::ffff:127.0.0.1]"},
+
+       {net: "udp6", addr: "[::1]", ipv6: true},
+
+       {net: "unixgram", addr: "@gotest3/net", linux: true},
+}
+
 func TestFilePacketConn(t *testing.T) {
-       if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
+       switch runtime.GOOS {
+       case "plan9", "windows":
+               t.Logf("skipping test on %q", runtime.GOOS)
                return
        }
-       testFilePacketConnListen(t, "udp", "127.0.0.1:0")
-       testFilePacketConnDial(t, "udp", "127.0.0.1:12345")
-       if supportsIPv6 {
-               testFilePacketConnListen(t, "udp", "[::1]:0")
-       }
-       if supportsIPv6 && supportsIPv4map {
-               testFilePacketConnDial(t, "udp", "[::ffff:127.0.0.1]:12345")
-       }
-       if runtime.GOOS == "linux" {
-               testFilePacketConnListen(t, "unixgram", "@gotest1/net")
+
+       for _, tt := range filePacketConnTests {
+               if skipServerTest(tt.net, "unixgram", tt.addr, tt.ipv6, false, tt.linux) {
+                       continue
+               }
+               testFilePacketConnListen(t, tt.net, tt.addr)
+               switch tt.addr {
+               case "", "0.0.0.0", "[::ffff:0.0.0.0]", "[::]":
+               default:
+                       if tt.net != "unixgram" {
+                               testFilePacketConnDial(t, tt.net, tt.addr)
+                       }
+               }
        }
 }
index aa0bf4b..e00b62e 100644 (file)
@@ -238,9 +238,9 @@ func TestRedirects(t *testing.T) {
 }
 
 var expectedCookies = []*Cookie{
-       &Cookie{Name: "ChocolateChip", Value: "tasty"},
-       &Cookie{Name: "First", Value: "Hit"},
-       &Cookie{Name: "Second", Value: "Hit"},
+       {Name: "ChocolateChip", Value: "tasty"},
+       {Name: "First", Value: "Hit"},
+       {Name: "Second", Value: "Hit"},
 }
 
 var echoCookiesRedirectHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
index 5277657..f5bc6eb 100644 (file)
@@ -455,11 +455,13 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) {
        // First line: GET /index.html HTTP/1.0
        var s string
        if s, err = tp.ReadLine(); err != nil {
+               return nil, err
+       }
+       defer func() {
                if err == io.EOF {
                        err = io.ErrUnexpectedEOF
                }
-               return nil, err
-       }
+       }()
 
        var f []string
        if f = strings.SplitN(s, " ", 3); len(f) < 3 {
index 7a3556d..6e00b9b 100644 (file)
@@ -5,6 +5,7 @@
 package http_test
 
 import (
+       "bufio"
        "bytes"
        "fmt"
        "io"
@@ -177,6 +178,24 @@ func TestRequestMultipartCallOrder(t *testing.T) {
        }
 }
 
+var readRequestErrorTests = []struct {
+       in  string
+       err error
+}{
+       {"GET / HTTP/1.1\r\nheader:foo\r\n\r\n", nil},
+       {"GET / HTTP/1.1\r\nheader:foo\r\n", io.ErrUnexpectedEOF},
+       {"", io.EOF},
+}
+
+func TestReadRequestErrors(t *testing.T) {
+       for i, tt := range readRequestErrorTests {
+               _, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.in)))
+               if err != tt.err {
+                       t.Errorf("%d. got error = %v; want %v", i, err, tt.err)
+               }
+       }
+}
+
 func testMissingFile(t *testing.T, req *Request) {
        f, fh, err := req.FormFile("missing")
        if f != nil {
index fa0df54..228ac40 100644 (file)
@@ -601,7 +601,7 @@ func (c *conn) serve() {
                                // while they're still writing their
                                // request.  Undefined behavior.
                                msg = "413 Request Entity Too Large"
-                       } else if err == io.ErrUnexpectedEOF {
+                       } else if err == io.EOF {
                                break // Don't reply
                        } else if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
                                break // Don't reply
index 09579f8..0249759 100644 (file)
@@ -196,7 +196,7 @@ func (t *Transport) CloseIdleConnections() {
                        pconn.close()
                }
        }
-       t.idleConn = nil
+       t.idleConn = make(map[string][]*persistConn)
 }
 
 //
index cbb3884..a9e401d 100644 (file)
@@ -698,6 +698,32 @@ func TestTransportPersistConnLeak(t *testing.T) {
        }
 }
 
+// This used to crash; http://golang.org/issue/3266
+func TestTransportIdleConnCrash(t *testing.T) {
+       tr := &Transport{}
+       c := &Client{Transport: tr}
+
+       unblockCh := make(chan bool, 1)
+       ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               <-unblockCh
+               tr.CloseIdleConnections()
+       }))
+       defer ts.Close()
+
+       didreq := make(chan bool)
+       go func() {
+               res, err := c.Get(ts.URL)
+               if err != nil {
+                       t.Error(err)
+               } else {
+                       res.Body.Close() // returns idle conn
+               }
+               didreq <- true
+       }()
+       unblockCh <- true
+       <-didreq
+}
+
 type fooProto struct{}
 
 func (fooProto) RoundTrip(req *Request) (*Response, error) {
index f25d046..ee23570 100644 (file)
@@ -78,7 +78,7 @@ func (ifi *Interface) MulticastAddrs() ([]Addr, error) {
        return interfaceMulticastAddrTable(ifi.Index)
 }
 
-// Interfaces returns a list of the systems's network interfaces.
+// Interfaces returns a list of the system's network interfaces.
 func Interfaces() ([]Interface, error) {
        return interfaceTable(0)
 }
index 15c2f37..8c9c304 100644 (file)
@@ -7,7 +7,6 @@
 package net
 
 import (
-       "fmt"
        "os"
        "syscall"
        "unsafe"
@@ -194,7 +193,9 @@ func parseProcNetIGMP(path string, ifi *Interface) []Addr {
                        name = f[1]
                case len(f[0]) == 8:
                        if ifi == nil || name == ifi.Name {
-                               fmt.Sscanf(f[0], "%08x", &b)
+                               for i := 0; i+1 < len(f[0]); i += 2 {
+                                       b[i/2], _ = xtoi2(f[0][i:i+2], 0)
+                               }
                                ifma := IPAddr{IP: IPv4(b[3], b[2], b[1], b[0])}
                                ifmat = append(ifmat, ifma.toAddr())
                        }
@@ -218,10 +219,11 @@ func parseProcNetIGMP6(path string, ifi *Interface) []Addr {
                        continue
                }
                if ifi == nil || f[1] == ifi.Name {
-                       fmt.Sscanf(f[2], "%32x", &b)
+                       for i := 0; i+1 < len(f[2]); i += 2 {
+                               b[i/2], _ = xtoi2(f[2][i:i+2], 0)
+                       }
                        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]}}
                        ifmat = append(ifmat, ifma.toAddr())
-
                }
        }
        return ifmat
index 9caa869..6bbe67c 100644 (file)
@@ -34,6 +34,13 @@ func (a *IPAddr) family() int {
        return syscall.AF_INET6
 }
 
+func (a *IPAddr) isWildcard() bool {
+       if a == nil || a.IP == nil {
+               return true
+       }
+       return a.IP.IsUnspecified()
+}
+
 func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
        return ipToSockaddr(family, a.IP, 0)
 }
index 4841057..ed31319 100644 (file)
@@ -38,6 +38,7 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
                        continue
                }
                defer closesocket(s)
+               syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
                sa, err := probes[i].la.toAddr().sockaddr(syscall.AF_INET6)
                if err != nil {
                        continue
@@ -55,58 +56,75 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
 // favoriteAddrFamily returns the appropriate address family to
 // the given net, laddr, raddr and mode.  At first it figures
 // address family out from the net.  If mode indicates "listen"
-// and laddr.(type).IP is nil, it assumes that the user wants to
-// make a passive connection with wildcard address family, both
-// INET and INET6, and wildcard address.  Otherwise guess: if the
-// addresses are IPv4 then returns INET, or else returns INET6.
-func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) int {
+// and laddr is a wildcard, it assumes that the user wants to
+// make a passive connection with a wildcard address family, both
+// AF_INET and AF_INET6, and a wildcard address like following:
+//
+//     1. A wild-wild listen, "tcp" + ""
+//     If the platform supports both IPv6 and IPv6 IPv4-mapping
+//     capabilities, we assume that the user want to listen on
+//     both IPv4 and IPv6 wildcard address over an AF_INET6
+//     socket with IPV6_V6ONLY=0.  Otherwise we prefer an IPv4
+//     wildcard address listen over an AF_INET socket.
+//
+//     2. A wild-ipv4wild listen, "tcp" + "0.0.0.0"
+//     Same as 1.
+//
+//     3. A wild-ipv6wild listen, "tcp" + "[::]"
+//     Almost same as 1 but we prefer an IPv6 wildcard address
+//     listen over an AF_INET6 socket with IPV6_V6ONLY=0 when
+//     the platform supports IPv6 capability but not IPv6 IPv4-
+//     mapping capability.
+//
+//     4. A ipv4-ipv4wild listen, "tcp4" + "" or "0.0.0.0"
+//     We use an IPv4 (AF_INET) wildcard address listen.
+//
+//     5. A ipv6-ipv6wild listen, "tcp6" + "" or "[::]"
+//     We use an IPv6 (AF_INET6, IPV6_V6ONLY=1) wildcard address
+//     listen.
+//
+// Otherwise guess: if the addresses are IPv4 then returns AF_INET,
+// or else returns AF_INET6.  It also returns a boolean value what
+// designates IPV6_V6ONLY option.
+//
+// Note that OpenBSD allows neither "net.inet6.ip6.v6only=1" change
+// nor IPPROTO_IPV6 level IPV6_V6ONLY socket option setting.
+func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family int, ipv6only bool) {
        switch net[len(net)-1] {
        case '4':
-               return syscall.AF_INET
+               return syscall.AF_INET, false
        case '6':
-               return syscall.AF_INET6
+               return syscall.AF_INET6, true
        }
 
-       if mode == "listen" {
-               // Note that OpenBSD allows neither "net.inet6.ip6.v6only"
-               // change nor IPPROTO_IPV6 level IPV6_V6ONLY socket option
-               // setting.
-               switch a := laddr.(type) {
-               case *TCPAddr:
-                       if a.IP == nil && supportsIPv6 && supportsIPv4map {
-                               return syscall.AF_INET6
-                       }
-               case *UDPAddr:
-                       if a.IP == nil && supportsIPv6 && supportsIPv4map {
-                               return syscall.AF_INET6
-                       }
-               case *IPAddr:
-                       if a.IP == nil && supportsIPv6 && supportsIPv4map {
-                               return syscall.AF_INET6
-                       }
+       if mode == "listen" && laddr.isWildcard() {
+               if supportsIPv4map {
+                       return syscall.AF_INET6, false
                }
+               return laddr.family(), false
        }
 
        if (laddr == nil || laddr.family() == syscall.AF_INET) &&
                (raddr == nil || raddr.family() == syscall.AF_INET) {
-               return syscall.AF_INET
+               return syscall.AF_INET, false
        }
-       return syscall.AF_INET6
+       return syscall.AF_INET6, false
 }
 
-// Internet sockets (TCP, UDP)
+// Internet sockets (TCP, UDP, IP)
 
-// A sockaddr represents a TCP or UDP network address that can
+// A sockaddr represents a TCP, UDP or IP network address that can
 // be converted into a syscall.Sockaddr.
 type sockaddr interface {
        Addr
-       sockaddr(family int) (syscall.Sockaddr, error)
        family() int
+       isWildcard() bool
+       sockaddr(family int) (syscall.Sockaddr, error)
 }
 
 func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
        var la, ra syscall.Sockaddr
-       family := favoriteAddrFamily(net, laddr, raddr, mode)
+       family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
        if laddr != nil {
                if la, err = laddr.sockaddr(family); err != nil {
                        goto Error
@@ -117,7 +135,7 @@ func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode s
                        goto Error
                }
        }
-       fd, err = socket(net, family, sotype, proto, la, ra, toAddr)
+       fd, err = socket(net, family, sotype, proto, ipv6only, la, ra, toAddr)
        if err != nil {
                goto Error
        }
@@ -152,7 +170,7 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) {
                }
                // IPv4 callers use 0.0.0.0 to mean "announce on any available address".
                // In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0",
-               // which it refuses to do.  Rewrite to the IPv6 all zeros.
+               // which it refuses to do.  Rewrite to the IPv6 unspecified address.
                if ip.Equal(IPv4zero) {
                        ip = IPv6zero
                }
index e0637d0..d616b1f 100644 (file)
@@ -6,24 +6,26 @@
 
 package net
 
-import (
-       "bytes"
-       "errors"
-       "fmt"
-)
+import "errors"
+
+const hexDigit = "0123456789abcdef"
 
 // A HardwareAddr represents a physical hardware address.
 type HardwareAddr []byte
 
 func (a HardwareAddr) String() string {
-       var buf bytes.Buffer
+       if len(a) == 0 {
+               return ""
+       }
+       buf := make([]byte, 0, len(a)*3-1)
        for i, b := range a {
                if i > 0 {
-                       buf.WriteByte(':')
+                       buf = append(buf, ':')
                }
-               fmt.Fprintf(&buf, "%02x", b)
+               buf = append(buf, hexDigit[b>>4])
+               buf = append(buf, hexDigit[b&0xF])
        }
-       return buf.String()
+       return string(buf)
 }
 
 // ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, or EUI-64 using one of the
index 3837e74..8f9dc66 100644 (file)
@@ -43,12 +43,24 @@ func match(err error, s string) bool {
        return err != nil && strings.Contains(err.Error(), s)
 }
 
-func TestParseMAC(t *testing.T) {
-       for _, tt := range mactests {
+func TestMACParseString(t *testing.T) {
+       for i, tt := range mactests {
                out, err := ParseMAC(tt.in)
                if !reflect.DeepEqual(out, tt.out) || !match(err, tt.err) {
                        t.Errorf("ParseMAC(%q) = %v, %v, want %v, %v", tt.in, out, err, tt.out,
                                tt.err)
                }
+               if tt.err == "" {
+                       // Verify that serialization works too, and that it round-trips.
+                       s := out.String()
+                       out2, err := ParseMAC(s)
+                       if err != nil {
+                               t.Errorf("%d. ParseMAC(%q) = %v", i, s, err)
+                               continue
+                       }
+                       if !reflect.DeepEqual(out2, out) {
+                               t.Errorf("%d. ParseMAC(%q) = %v, want %v", i, s, out2, out)
+                       }
+               }
        }
 }
index bf22c71..0917bbe 100644 (file)
@@ -394,8 +394,7 @@ func (p *addrParser) consumeAtom(dot bool) (atom string, err error) {
        i := 1
        for ; i < p.len() && isAtext((*p)[i], dot); i++ {
        }
-       // TODO(dsymonds): Remove the []byte() conversion here when 6g doesn't need it.
-       atom, *p = string([]byte((*p)[:i])), (*p)[i:]
+       atom, *p = string((*p)[:i]), (*p)[i:]
        return atom, nil
 }
 
index 1d760c2..67261b1 100644 (file)
@@ -47,9 +47,11 @@ var multicastListenerTests = []struct {
 func TestMulticastListener(t *testing.T) {
        switch runtime.GOOS {
        case "netbsd", "openbsd", "plan9", "windows":
+               t.Logf("skipping test on %q", runtime.GOOS)
                return
        case "linux":
                if runtime.GOARCH == "arm" || runtime.GOARCH == "alpha" {
+                       t.Logf("skipping test on %q/%q", runtime.GOOS, runtime.GOARCH)
                        return
                }
        }
@@ -86,7 +88,13 @@ func TestMulticastListener(t *testing.T) {
 func TestSimpleMulticastListener(t *testing.T) {
        switch runtime.GOOS {
        case "plan9":
+               t.Logf("skipping test on %q", runtime.GOOS)
                return
+       case "windows":
+               if testing.Short() || !*testExternal {
+                       t.Logf("skipping test on windows to avoid firewall")
+                       return
+               }
        }
 
        for _, tt := range multicastListenerTests {
index bf242ff..9ebcdbe 100644 (file)
@@ -54,6 +54,8 @@ type Addr interface {
 }
 
 // Conn is a generic stream-oriented network connection.
+//
+// Multiple goroutines may invoke methods on a Conn simultaneously.
 type Conn interface {
        // Read reads data from the connection.
        // Read can be made to time out and return a Error with Timeout() == true
@@ -66,6 +68,7 @@ type Conn interface {
        Write(b []byte) (n int, err error)
 
        // Close closes the connection.
+       // Any blocked Read or Write operations will be unblocked and return errors.
        Close() error
 
        // LocalAddr returns the local network address.
@@ -89,11 +92,11 @@ type Conn interface {
        // A zero value for t means I/O operations will not time out.
        SetDeadline(t time.Time) error
 
-       // SetReadDeadline sets the deadline for Read calls.
+       // SetReadDeadline sets the deadline for future Read calls.
        // A zero value for t means Read will not time out.
        SetReadDeadline(t time.Time) error
 
-       // SetWriteDeadline sets the deadline for Write calls.
+       // SetWriteDeadline sets the deadline for future 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.
@@ -108,6 +111,8 @@ type Error interface {
 }
 
 // PacketConn is a generic packet-oriented network connection.
+//
+// Multiple goroutines may invoke methods on a PacketConn simultaneously.
 type PacketConn interface {
        // ReadFrom reads a packet from the connection,
        // copying the payload into b.  It returns the number of
@@ -126,6 +131,7 @@ type PacketConn interface {
        WriteTo(b []byte, addr Addr) (n int, err error)
 
        // Close closes the connection.
+       // Any blocked ReadFrom or WriteTo operations will be unblocked and return errors.
        Close() error
 
        // LocalAddr returns the local network address.
@@ -135,13 +141,13 @@ type PacketConn interface {
        // with the connection.
        SetDeadline(t time.Time) error
 
-       // SetReadDeadline sets the deadline for all Read calls to return.
+       // SetReadDeadline sets the deadline for future Read calls.
        // If the deadline is reached, Read will fail with a timeout
        // (see type Error) instead of blocking.
        // 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.
+       // SetWriteDeadline sets the deadline for future Write calls.
        // 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.
@@ -151,11 +157,14 @@ type PacketConn interface {
 }
 
 // A Listener is a generic network listener for stream-oriented protocols.
+//
+// Multiple goroutines may invoke methods on a Listener simultaneously.
 type Listener interface {
        // Accept waits for and returns the next connection to the listener.
        Accept() (c Conn, err error)
 
        // Close closes the listener.
+       // Any blocked Accept operations will be unblocked and return errors.
        Close() error
 
        // Addr returns the listener's network address.
index c1a90de..fd145e1 100644 (file)
@@ -13,6 +13,7 @@ import (
 
 func TestShutdown(t *testing.T) {
        if runtime.GOOS == "plan9" {
+               t.Logf("skipping test on %q", runtime.GOOS)
                return
        }
        l, err := Listen("tcp", "127.0.0.1:0")
index dfbaba4..30fda45 100644 (file)
@@ -13,7 +13,9 @@ import (
 
 func TestReadLine(t *testing.T) {
        // /etc/services file does not exist on windows and Plan 9.
-       if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
+       switch runtime.GOOS {
+       case "plan9", "windows":
+               t.Logf("skipping test on %q", runtime.GOOS)
                return
        }
        filename := "/etc/services" // a nice big file
index f7abf21..db2da8e 100644 (file)
@@ -36,7 +36,8 @@ type Call struct {
 
 // Client represents an RPC Client.
 // There may be multiple outstanding Calls associated
-// with a single Client.
+// with a single Client, and a Client may be used by
+// multiple goroutines simultaneously.
 type Client struct {
        mutex    sync.Mutex // protects pending, seq, request
        sending  sync.Mutex
index b986216..158b947 100644 (file)
@@ -9,234 +9,461 @@ import (
        "io"
        "os"
        "runtime"
-       "strings"
        "testing"
        "time"
 )
 
-// Do not test empty datagrams by default.
-// It causes unexplained timeouts on some systems,
-// including Snow Leopard.  I think that the kernel
-// doesn't quite expect them.
-var testUDP = flag.Bool("udp", false, "whether to test UDP datagrams")
+func skipServerTest(net, unixsotype, addr string, ipv6, ipv4map, linuxonly bool) bool {
+       switch runtime.GOOS {
+       case "linux":
+       case "plan9", "windows":
+               // "unix" sockets are not supported on Windows and Plan 9.
+               if net == unixsotype {
+                       return true
+               }
+       default:
+               if net == unixsotype && linuxonly {
+                       return true
+               }
+       }
+       switch addr {
+       case "", "0.0.0.0", "[::ffff:0.0.0.0]", "[::]":
+               if testing.Short() || !*testExternal {
+                       return true
+               }
+       }
+       if ipv6 && !supportsIPv6 {
+               return true
+       }
+       if ipv4map && !supportsIPv4map {
+               return true
+       }
+       return false
+}
 
-func runEcho(fd io.ReadWriter, done chan<- int) {
-       var buf [1024]byte
+var streamConnServerTests = []struct {
+       snet    string // server side
+       saddr   string
+       cnet    string // client side
+       caddr   string
+       ipv6    bool // test with underlying AF_INET6 socket
+       ipv4map bool // test with IPv6 IPv4-mapping functionality
+       empty   bool // test with empty data
+       linux   bool // test with abstract unix domain socket, a Linux-ism
+}{
+       {snet: "tcp", saddr: "", cnet: "tcp", caddr: "127.0.0.1"},
+       {snet: "tcp", saddr: "0.0.0.0", cnet: "tcp", caddr: "127.0.0.1"},
+       {snet: "tcp", saddr: "[::ffff:0.0.0.0]", cnet: "tcp", caddr: "127.0.0.1"},
+       {snet: "tcp", saddr: "[::]", cnet: "tcp", caddr: "[::1]", ipv6: true},
 
-       for {
-               n, err := fd.Read(buf[0:])
-               if err != nil || n == 0 || string(buf[:n]) == "END" {
-                       break
+       {snet: "tcp", saddr: "", cnet: "tcp", caddr: "[::1]", ipv4map: true},
+       {snet: "tcp", saddr: "0.0.0.0", cnet: "tcp", caddr: "[::1]", ipv4map: true},
+       {snet: "tcp", saddr: "[::ffff:0.0.0.0]", cnet: "tcp", caddr: "[::1]", ipv4map: true},
+       {snet: "tcp", saddr: "[::]", cnet: "tcp", caddr: "127.0.0.1", ipv4map: true},
+
+       {snet: "tcp", saddr: "", cnet: "tcp4", caddr: "127.0.0.1"},
+       {snet: "tcp", saddr: "0.0.0.0", cnet: "tcp4", caddr: "127.0.0.1"},
+       {snet: "tcp", saddr: "[::ffff:0.0.0.0]", cnet: "tcp4", caddr: "127.0.0.1"},
+       {snet: "tcp", saddr: "[::]", cnet: "tcp6", caddr: "[::1]", ipv6: true},
+
+       {snet: "tcp", saddr: "", cnet: "tcp6", caddr: "[::1]", ipv4map: true},
+       {snet: "tcp", saddr: "0.0.0.0", cnet: "tcp6", caddr: "[::1]", ipv4map: true},
+       {snet: "tcp", saddr: "[::ffff:0.0.0.0]", cnet: "tcp6", caddr: "[::1]", ipv4map: true},
+       {snet: "tcp", saddr: "[::]", cnet: "tcp4", caddr: "127.0.0.1", ipv4map: true},
+
+       {snet: "tcp", saddr: "127.0.0.1", cnet: "tcp", caddr: "127.0.0.1"},
+       {snet: "tcp", saddr: "[::ffff:127.0.0.1]", cnet: "tcp", caddr: "127.0.0.1"},
+       {snet: "tcp", saddr: "[::1]", cnet: "tcp", caddr: "[::1]", ipv6: true},
+
+       {snet: "tcp4", saddr: "", cnet: "tcp4", caddr: "127.0.0.1"},
+       {snet: "tcp4", saddr: "0.0.0.0", cnet: "tcp4", caddr: "127.0.0.1"},
+       {snet: "tcp4", saddr: "[::ffff:0.0.0.0]", cnet: "tcp4", caddr: "127.0.0.1"},
+
+       {snet: "tcp4", saddr: "127.0.0.1", cnet: "tcp4", caddr: "127.0.0.1"},
+
+       {snet: "tcp6", saddr: "", cnet: "tcp6", caddr: "[::1]", ipv6: true},
+       {snet: "tcp6", saddr: "[::]", cnet: "tcp6", caddr: "[::1]", ipv6: true},
+
+       {snet: "tcp6", saddr: "[::1]", cnet: "tcp6", caddr: "[::1]", ipv6: true},
+
+       {snet: "unix", saddr: "/tmp/gotest1.net", cnet: "unix", caddr: "/tmp/gotest1.net.local"},
+       {snet: "unix", saddr: "@gotest2/net", cnet: "unix", caddr: "@gotest2/net.local", linux: true},
+}
+
+func TestStreamConnServer(t *testing.T) {
+       for _, tt := range streamConnServerTests {
+               if skipServerTest(tt.snet, "unix", tt.saddr, tt.ipv6, tt.ipv4map, tt.linux) {
+                       continue
+               }
+
+               listening := make(chan string)
+               done := make(chan int)
+               switch tt.snet {
+               case "tcp", "tcp4", "tcp6":
+                       tt.saddr += ":0"
+               case "unix":
+                       os.Remove(tt.saddr)
+                       os.Remove(tt.caddr)
+               }
+
+               go runStreamConnServer(t, tt.snet, tt.saddr, listening, done)
+               taddr := <-listening // wait for server to start
+
+               switch tt.cnet {
+               case "tcp", "tcp4", "tcp6":
+                       _, port, err := SplitHostPort(taddr)
+                       if err != nil {
+                               t.Errorf("SplitHostPort(%q) failed: %v", taddr, err)
+                               return
+                       }
+                       taddr = tt.caddr + ":" + port
+               }
+
+               runStreamConnClient(t, tt.cnet, taddr, tt.empty)
+               <-done // make sure server stopped
+
+               switch tt.snet {
+               case "unix":
+                       os.Remove(tt.saddr)
+                       os.Remove(tt.caddr)
+               }
+       }
+}
+
+var seqpacketConnServerTests = []struct {
+       net   string
+       saddr string // server address
+       caddr string // client address
+       empty bool   // test with empty data
+}{
+       {net: "unixpacket", saddr: "/tmp/gotest3.net", caddr: "/tmp/gotest3.net.local"},
+       {net: "unixpacket", saddr: "@gotest4/net", caddr: "@gotest4/net.local"},
+}
+
+func TestSeqpacketConnServer(t *testing.T) {
+       if runtime.GOOS != "linux" {
+               t.Logf("skipping test on %q", runtime.GOOS)
+               return
+       }
+
+       for _, tt := range seqpacketConnServerTests {
+               listening := make(chan string)
+               done := make(chan int)
+               switch tt.net {
+               case "unixpacket":
+                       os.Remove(tt.saddr)
+                       os.Remove(tt.caddr)
+               }
+
+               go runStreamConnServer(t, tt.net, tt.saddr, listening, done)
+               taddr := <-listening // wait for server to start
+
+               runStreamConnClient(t, tt.net, taddr, tt.empty)
+               <-done // make sure server stopped
+
+               switch tt.net {
+               case "unixpacket":
+                       os.Remove(tt.saddr)
+                       os.Remove(tt.caddr)
                }
-               fd.Write(buf[0:n])
        }
-       done <- 1
 }
 
-func runServe(t *testing.T, network, addr string, listening chan<- string, done chan<- int) {
-       l, err := Listen(network, addr)
+func runStreamConnServer(t *testing.T, net, laddr string, listening chan<- string, done chan<- int) {
+       l, err := Listen(net, laddr)
        if err != nil {
-               t.Fatalf("net.Listen(%q, %q) = _, %v", network, addr, err)
+               t.Errorf("Listen(%q, %q) failed: %v", net, laddr, err)
+               listening <- "<nil>"
+               done <- 1
+               return
        }
+       defer l.Close()
        listening <- l.Addr().String()
 
+       echo := func(rw io.ReadWriter, done chan<- int) {
+               buf := make([]byte, 1024)
+               for {
+                       n, err := rw.Read(buf[0:])
+                       if err != nil || n == 0 || string(buf[:n]) == "END" {
+                               break
+                       }
+                       rw.Write(buf[0:n])
+               }
+               done <- 1
+       }
+
+run:
        for {
-               fd, err := l.Accept()
+               c, err := l.Accept()
                if err != nil {
-                       break
+                       continue run
                }
                echodone := make(chan int)
-               go runEcho(fd, echodone)
-               <-echodone // make sure Echo stops
-               l.Close()
+               go echo(c, echodone)
+               <-echodone // make sure echo stopped
+               c.Close()
+               break run
        }
        done <- 1
 }
 
-func connect(t *testing.T, network, addr string, isEmpty bool) {
-       var fd Conn
-       var err error
-       if network == "unixgram" {
-               fd, err = DialUnix(network, &UnixAddr{addr + ".local", network}, &UnixAddr{addr, network})
-       } else {
-               fd, err = Dial(network, addr)
-       }
+func runStreamConnClient(t *testing.T, net, taddr string, isEmpty bool) {
+       c, err := Dial(net, taddr)
        if err != nil {
-               t.Fatalf("net.Dial(%q, %q) = _, %v", network, addr, err)
+               t.Errorf("Dial(%q, %q) failed: %v", net, taddr, err)
+               return
        }
-       fd.SetReadDeadline(time.Now().Add(1 * time.Second))
+       defer c.Close()
+       c.SetReadDeadline(time.Now().Add(1 * time.Second))
 
-       var b []byte
+       var wb []byte
        if !isEmpty {
-               b = []byte("hello, world\n")
+               wb = []byte("StreamConnClient by Dial\n")
        }
-       var b1 [100]byte
-
-       n, err1 := fd.Write(b)
-       if n != len(b) {
-               t.Fatalf("fd.Write(%q) = %d, %v", b, n, err1)
+       if n, err := c.Write(wb); err != nil || n != len(wb) {
+               t.Errorf("Write failed: %v, %v; want %v, <nil>", n, err, len(wb))
+               return
        }
 
-       n, err1 = fd.Read(b1[0:])
-       if n != len(b) || err1 != nil {
-               t.Fatalf("fd.Read() = %d, %v (want %d, nil)", n, err1, len(b))
+       rb := make([]byte, 1024)
+       if n, err := c.Read(rb[0:]); err != nil || n != len(wb) {
+               t.Errorf("Read failed: %v, %v; want %v, <nil>", n, err, len(wb))
+               return
        }
 
        // Send explicit ending for unixpacket.
        // Older Linux kernels do not stop reads on close.
-       if network == "unixpacket" {
-               fd.Write([]byte("END"))
+       switch net {
+       case "unixpacket":
+               c.Write([]byte("END"))
        }
-
-       fd.Close()
 }
 
-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() || !*testExternal {
-                       t.Logf("skip wildcard listen during short test")
-                       return
-               }
-       }
-       listening := make(chan string)
-       done := make(chan int)
-       if network == "tcp" || network == "tcp4" || network == "tcp6" {
-               listenaddr += ":0" // any available port
-       }
-       go runServe(t, network, listenaddr, listening, done)
-       addr := <-listening // wait for server to start
-       if network == "tcp" || network == "tcp4" || network == "tcp6" {
-               dialaddr += addr[strings.LastIndex(addr, ":"):]
-       }
-       connect(t, network, dialaddr, false)
-       <-done // make sure server stopped
-}
+// Do not test empty datagrams by default.
+// It causes unexplained timeouts on some systems,
+// including Snow Leopard.  I think that the kernel
+// doesn't quite expect them.
+var testDatagram = flag.Bool("datagram", false, "whether to test udp and unixgram")
 
-func TestTCPServer(t *testing.T) {
-       doTest(t, "tcp", "", "127.0.0.1")
-       doTest(t, "tcp", "0.0.0.0", "127.0.0.1")
-       doTest(t, "tcp", "127.0.0.1", "127.0.0.1")
-       doTest(t, "tcp4", "", "127.0.0.1")
-       doTest(t, "tcp4", "0.0.0.0", "127.0.0.1")
-       doTest(t, "tcp4", "127.0.0.1", "127.0.0.1")
-       if supportsIPv6 {
-               doTest(t, "tcp", "[::]", "[::1]")
-               doTest(t, "tcp", "[::1]", "[::1]")
-               doTest(t, "tcp6", "", "[::1]")
-               doTest(t, "tcp6", "[::]", "[::1]")
-               doTest(t, "tcp6", "[::1]", "[::1]")
-       }
-       if supportsIPv6 && supportsIPv4map {
-               doTest(t, "tcp", "[::ffff:0.0.0.0]", "127.0.0.1")
-               doTest(t, "tcp", "[::]", "127.0.0.1")
-               doTest(t, "tcp4", "[::ffff:0.0.0.0]", "127.0.0.1")
-               doTest(t, "tcp6", "", "127.0.0.1")
-               doTest(t, "tcp6", "[::ffff:0.0.0.0]", "127.0.0.1")
-               doTest(t, "tcp6", "[::]", "127.0.0.1")
-               doTest(t, "tcp", "127.0.0.1", "[::ffff:127.0.0.1]")
-               doTest(t, "tcp", "[::ffff:127.0.0.1]", "127.0.0.1")
-               doTest(t, "tcp4", "127.0.0.1", "[::ffff:127.0.0.1]")
-               doTest(t, "tcp4", "[::ffff:127.0.0.1]", "127.0.0.1")
-               doTest(t, "tcp6", "127.0.0.1", "[::ffff:127.0.0.1]")
-               doTest(t, "tcp6", "[::ffff:127.0.0.1]", "127.0.0.1")
-       }
+var datagramPacketConnServerTests = []struct {
+       snet    string // server side
+       saddr   string
+       cnet    string // client side
+       caddr   string
+       ipv6    bool // test with underlying AF_INET6 socket
+       ipv4map bool // test with IPv6 IPv4-mapping functionality
+       dial    bool // test with Dial or DialUnix
+       empty   bool // test with empty data
+       linux   bool // test with abstract unix domain socket, a Linux-ism
+}{
+       {snet: "udp", saddr: "", cnet: "udp", caddr: "127.0.0.1"},
+       {snet: "udp", saddr: "0.0.0.0", cnet: "udp", caddr: "127.0.0.1"},
+       {snet: "udp", saddr: "[::ffff:0.0.0.0]", cnet: "udp", caddr: "127.0.0.1"},
+       {snet: "udp", saddr: "[::]", cnet: "udp", caddr: "[::1]", ipv6: true},
+
+       {snet: "udp", saddr: "", cnet: "udp", caddr: "[::1]", ipv4map: true},
+       {snet: "udp", saddr: "0.0.0.0", cnet: "udp", caddr: "[::1]", ipv4map: true},
+       {snet: "udp", saddr: "[::ffff:0.0.0.0]", cnet: "udp", caddr: "[::1]", ipv4map: true},
+       {snet: "udp", saddr: "[::]", cnet: "udp", caddr: "127.0.0.1", ipv4map: true},
+
+       {snet: "udp", saddr: "", cnet: "udp4", caddr: "127.0.0.1"},
+       {snet: "udp", saddr: "0.0.0.0", cnet: "udp4", caddr: "127.0.0.1"},
+       {snet: "udp", saddr: "[::ffff:0.0.0.0]", cnet: "udp4", caddr: "127.0.0.1"},
+       {snet: "udp", saddr: "[::]", cnet: "udp6", caddr: "[::1]", ipv6: true},
+
+       {snet: "udp", saddr: "", cnet: "udp6", caddr: "[::1]", ipv4map: true},
+       {snet: "udp", saddr: "0.0.0.0", cnet: "udp6", caddr: "[::1]", ipv4map: true},
+       {snet: "udp", saddr: "[::ffff:0.0.0.0]", cnet: "udp6", caddr: "[::1]", ipv4map: true},
+       {snet: "udp", saddr: "[::]", cnet: "udp4", caddr: "127.0.0.1", ipv4map: true},
+
+       {snet: "udp", saddr: "127.0.0.1", cnet: "udp", caddr: "127.0.0.1"},
+       {snet: "udp", saddr: "[::ffff:127.0.0.1]", cnet: "udp", caddr: "127.0.0.1"},
+       {snet: "udp", saddr: "[::1]", cnet: "udp", caddr: "[::1]", ipv6: true},
+
+       {snet: "udp4", saddr: "", cnet: "udp4", caddr: "127.0.0.1"},
+       {snet: "udp4", saddr: "0.0.0.0", cnet: "udp4", caddr: "127.0.0.1"},
+       {snet: "udp4", saddr: "[::ffff:0.0.0.0]", cnet: "udp4", caddr: "127.0.0.1"},
+
+       {snet: "udp4", saddr: "127.0.0.1", cnet: "udp4", caddr: "127.0.0.1"},
+
+       {snet: "udp6", saddr: "", cnet: "udp6", caddr: "[::1]", ipv6: true},
+       {snet: "udp6", saddr: "[::]", cnet: "udp6", caddr: "[::1]", ipv6: true},
+
+       {snet: "udp6", saddr: "[::1]", cnet: "udp6", caddr: "[::1]", ipv6: true},
+
+       {snet: "udp", saddr: "127.0.0.1", cnet: "udp", caddr: "127.0.0.1", dial: true},
+       {snet: "udp", saddr: "127.0.0.1", cnet: "udp", caddr: "127.0.0.1", empty: true},
+       {snet: "udp", saddr: "127.0.0.1", cnet: "udp", caddr: "127.0.0.1", dial: true, empty: true},
+
+       {snet: "udp", saddr: "[::1]", cnet: "udp", caddr: "[::1]", ipv6: true, dial: true},
+       {snet: "udp", saddr: "[::1]", cnet: "udp", caddr: "[::1]", ipv6: true, empty: true},
+       {snet: "udp", saddr: "[::1]", cnet: "udp", caddr: "[::1]", ipv6: true, dial: true, empty: true},
+
+       {snet: "unixgram", saddr: "/tmp/gotest5.net", cnet: "unixgram", caddr: "/tmp/gotest5.net.local"},
+       {snet: "unixgram", saddr: "/tmp/gotest5.net", cnet: "unixgram", caddr: "/tmp/gotest5.net.local", dial: true},
+       {snet: "unixgram", saddr: "/tmp/gotest5.net", cnet: "unixgram", caddr: "/tmp/gotest5.net.local", empty: true},
+       {snet: "unixgram", saddr: "/tmp/gotest5.net", cnet: "unixgram", caddr: "/tmp/gotest5.net.local", dial: true, empty: true},
+
+       {snet: "unixgram", saddr: "@gotest6/net", cnet: "unixgram", caddr: "@gotest6/net.local", linux: true},
 }
 
-func TestUnixServer(t *testing.T) {
-       // "unix" sockets are not supported on windows and Plan 9.
-       if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
+func TestDatagramPacketConnServer(t *testing.T) {
+       if !*testDatagram {
                return
        }
-       os.Remove("/tmp/gotest.net")
-       doTest(t, "unix", "/tmp/gotest.net", "/tmp/gotest.net")
-       os.Remove("/tmp/gotest.net")
-       if runtime.GOOS == "linux" {
-               doTest(t, "unixpacket", "/tmp/gotest.net", "/tmp/gotest.net")
-               os.Remove("/tmp/gotest.net")
-               // Test abstract unix domain socket, a Linux-ism
-               doTest(t, "unix", "@gotest/net", "@gotest/net")
-               doTest(t, "unixpacket", "@gotest/net", "@gotest/net")
+
+       for _, tt := range datagramPacketConnServerTests {
+               if skipServerTest(tt.snet, "unixgram", tt.saddr, tt.ipv6, tt.ipv4map, tt.linux) {
+                       continue
+               }
+
+               listening := make(chan string)
+               done := make(chan int)
+               switch tt.snet {
+               case "udp", "udp4", "udp6":
+                       tt.saddr += ":0"
+               case "unixgram":
+                       os.Remove(tt.saddr)
+                       os.Remove(tt.caddr)
+               }
+
+               go runDatagramPacketConnServer(t, tt.snet, tt.saddr, listening, done)
+               taddr := <-listening // wait for server to start
+
+               switch tt.cnet {
+               case "udp", "udp4", "udp6":
+                       _, port, err := SplitHostPort(taddr)
+                       if err != nil {
+                               t.Errorf("SplitHostPort(%q) failed: %v", taddr, err)
+                               return
+                       }
+                       taddr = tt.caddr + ":" + port
+                       tt.caddr += ":0"
+               }
+               if tt.dial {
+                       runDatagramConnClient(t, tt.cnet, tt.caddr, taddr, tt.empty)
+               } else {
+                       runDatagramPacketConnClient(t, tt.cnet, tt.caddr, taddr, tt.empty)
+               }
+               <-done // tell server to stop
+               <-done // make sure server stopped
+
+               switch tt.snet {
+               case "unixgram":
+                       os.Remove(tt.saddr)
+                       os.Remove(tt.caddr)
+               }
        }
 }
 
-func runPacket(t *testing.T, network, addr string, listening chan<- string, done chan<- int) {
-       c, err := ListenPacket(network, addr)
+func runDatagramPacketConnServer(t *testing.T, net, laddr string, listening chan<- string, done chan<- int) {
+       c, err := ListenPacket(net, laddr)
        if err != nil {
-               t.Fatalf("net.ListenPacket(%q, %q) = _, %v", network, addr, err)
+               t.Errorf("ListenPacket(%q, %q) failed: %v", net, laddr, err)
+               listening <- "<nil>"
+               done <- 1
+               return
        }
+       defer c.Close()
        listening <- c.LocalAddr().String()
-       var buf [1000]byte
-Run:
+
+       buf := make([]byte, 1024)
+run:
        for {
                c.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
-               n, addr, err := c.ReadFrom(buf[0:])
-               if e, ok := err.(Error); ok && e.Timeout() {
+               n, ra, err := c.ReadFrom(buf[0:])
+               if nerr, ok := err.(Error); ok && nerr.Timeout() {
                        select {
                        case done <- 1:
-                               break Run
+                               break run
                        default:
-                               continue Run
+                               continue run
                        }
                }
                if err != nil {
-                       break
+                       break run
                }
-               if _, err = c.WriteTo(buf[0:n], addr); err != nil {
-                       t.Fatalf("WriteTo %v: %v", addr, err)
+               if _, err = c.WriteTo(buf[0:n], ra); err != nil {
+                       t.Errorf("WriteTo(%v) failed: %v", ra, err)
+                       break run
                }
        }
-       c.Close()
        done <- 1
 }
 
-func doTestPacket(t *testing.T, network, listenaddr, dialaddr string, isEmpty bool) {
-       t.Logf("TestPacket %q %q %q", network, listenaddr, dialaddr)
-       listening := make(chan string)
-       done := make(chan int)
-       if network == "udp" {
-               listenaddr += ":0" // any available port
-       }
-       go runPacket(t, network, listenaddr, listening, done)
-       addr := <-listening // wait for server to start
-       if network == "udp" {
-               dialaddr += addr[strings.LastIndex(addr, ":"):]
-       }
-       connect(t, network, dialaddr, isEmpty)
-       <-done // tell server to stop
-       <-done // wait for stop
-}
+func runDatagramConnClient(t *testing.T, net, laddr, taddr string, isEmpty bool) {
+       var c Conn
+       var err error
+       switch net {
+       case "udp", "udp4", "udp6":
+               c, err = Dial(net, taddr)
+               if err != nil {
+                       t.Errorf("Dial(%q, %q) failed: %v", net, taddr, err)
+                       return
+               }
+       case "unixgram":
+               c, err = DialUnix(net, &UnixAddr{laddr, net}, &UnixAddr{taddr, net})
+               if err != nil {
+                       t.Errorf("DialUnix(%q, {%q, %q}) failed: %v", net, laddr, taddr, err)
+                       return
+               }
+       }
+       defer c.Close()
+       c.SetReadDeadline(time.Now().Add(1 * time.Second))
 
-func TestUDPServer(t *testing.T) {
-       if !*testUDP {
+       var wb []byte
+       if !isEmpty {
+               wb = []byte("DatagramConnClient by Dial\n")
+       }
+       if n, err := c.Write(wb[0:]); err != nil || n != len(wb) {
+               t.Errorf("Write failed: %v, %v; want %v, <nil>", n, err, len(wb))
                return
        }
-       for _, isEmpty := range []bool{false, true} {
-               doTestPacket(t, "udp", "0.0.0.0", "127.0.0.1", isEmpty)
-               doTestPacket(t, "udp", "", "127.0.0.1", isEmpty)
-               if supportsIPv6 && supportsIPv4map {
-                       doTestPacket(t, "udp", "[::]", "[::ffff:127.0.0.1]", isEmpty)
-                       doTestPacket(t, "udp", "[::]", "127.0.0.1", isEmpty)
-                       doTestPacket(t, "udp", "0.0.0.0", "[::ffff:127.0.0.1]", isEmpty)
-               }
+
+       rb := make([]byte, 1024)
+       if n, err := c.Read(rb[0:]); err != nil || n != len(wb) {
+               t.Errorf("Read failed: %v, %v; want %v, <nil>", n, err, len(wb))
+               return
        }
 }
 
-func TestUnixDatagramServer(t *testing.T) {
-       // "unix" sockets are not supported on windows and Plan 9.
-       if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
+func runDatagramPacketConnClient(t *testing.T, net, laddr, taddr string, isEmpty bool) {
+       var ra Addr
+       var err error
+       switch net {
+       case "udp", "udp4", "udp6":
+               ra, err = ResolveUDPAddr(net, taddr)
+               if err != nil {
+                       t.Errorf("ResolveUDPAddr(%q, %q) failed: %v", net, taddr, err)
+                       return
+               }
+       case "unixgram":
+               ra, err = ResolveUnixAddr(net, taddr)
+               if err != nil {
+                       t.Errorf("ResolveUxixAddr(%q, %q) failed: %v", net, taddr, err)
+                       return
+               }
+       }
+       c, err := ListenPacket(net, laddr)
+       if err != nil {
+               t.Errorf("ListenPacket(%q, %q) faild: %v", net, laddr, err)
                return
        }
-       for _, isEmpty := range []bool{false} {
-               os.Remove("/tmp/gotest1.net")
-               os.Remove("/tmp/gotest1.net.local")
-               doTestPacket(t, "unixgram", "/tmp/gotest1.net", "/tmp/gotest1.net", isEmpty)
-               os.Remove("/tmp/gotest1.net")
-               os.Remove("/tmp/gotest1.net.local")
-               if runtime.GOOS == "linux" {
-                       // Test abstract unix domain socket, a Linux-ism
-                       doTestPacket(t, "unixgram", "@gotest1/net", "@gotest1/net", isEmpty)
-               }
+       defer c.Close()
+       c.SetReadDeadline(time.Now().Add(1 * time.Second))
+
+       var wb []byte
+       if !isEmpty {
+               wb = []byte("DatagramPacketConnClient by ListenPacket\n")
+       }
+       if n, err := c.WriteTo(wb[0:], ra); err != nil || n != len(wb) {
+               t.Errorf("WriteTo(%v) failed: %v, %v; want %v, <nil>", ra, n, err, len(wb))
+               return
+       }
+
+       rb := make([]byte, 1024)
+       if n, _, err := c.ReadFrom(rb[0:]); err != nil || n != len(wb) {
+               t.Errorf("ReadFrom failed: %v, %v; want %v, <nil>", n, err, len(wb))
+               return
        }
 }
index dc139f0..3ae1605 100644 (file)
@@ -16,7 +16,7 @@ import (
 var listenerBacklog = maxListenerBacklog()
 
 // Generic socket creation.
-func socket(net string, f, t, p int, la, ra syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
+func socket(net string, f, t, p int, ipv6only bool, la, ra syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
        // See ../syscall/exec.go for description of ForkLock.
        syscall.ForkLock.RLock()
        s, err := syscall.Socket(f, t, p)
@@ -27,7 +27,7 @@ func socket(net string, f, t, p int, la, ra syscall.Sockaddr, toAddr func(syscal
        syscall.CloseOnExec(s)
        syscall.ForkLock.RUnlock()
 
-       err = setDefaultSockopts(s, f, t)
+       err = setDefaultSockopts(s, f, t, ipv6only)
        if err != nil {
                closesocket(s)
                return nil, err
index 0a051d7..0cd1926 100644 (file)
@@ -9,7 +9,6 @@
 package net
 
 import (
-       "bytes"
        "os"
        "syscall"
        "time"
@@ -98,7 +97,7 @@ func setIPv4MreqToInterface(mreq *syscall.IPMreq, ifi *Interface) error {
                }
        }
 done:
-       if bytes.Equal(mreq.Multiaddr[:], IPv4zero.To4()) {
+       if bytesEqual(mreq.Multiaddr[:], IPv4zero.To4()) {
                return errNoSuchMulticastInterface
        }
        return nil
index 79e0e57..fff65f3 100644 (file)
@@ -13,12 +13,17 @@ import (
        "syscall"
 )
 
-func setDefaultSockopts(s, f, t int) error {
+func setDefaultSockopts(s, f, t int, ipv6only bool) error {
        switch f {
        case syscall.AF_INET6:
-               // Allow both IP versions even if the OS default is otherwise.
-               // Note that some operating systems never admit this option.
-               syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
+               if ipv6only {
+                       syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 1)
+               } else {
+                       // Allow both IP versions even if the OS default
+                       // is otherwise.  Note that some operating systems
+                       // never admit this option.
+                       syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
+               }
        }
        // Allow broadcast.
        err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
index 7509c29..0f47538 100644 (file)
@@ -11,12 +11,17 @@ import (
        "syscall"
 )
 
-func setDefaultSockopts(s, f, t int) error {
+func setDefaultSockopts(s, f, t int, ipv6only bool) error {
        switch f {
        case syscall.AF_INET6:
-               // Allow both IP versions even if the OS default is otherwise.
-               // Note that some operating systems never admit this option.
-               syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
+               if ipv6only {
+                       syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 1)
+               } else {
+                       // Allow both IP versions even if the OS default
+                       // is otherwise.  Note that some operating systems
+                       // never admit this option.
+                       syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
+               }
        }
        // Allow broadcast.
        err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
index b18af67..509b596 100644 (file)
@@ -11,12 +11,17 @@ import (
        "syscall"
 )
 
-func setDefaultSockopts(s syscall.Handle, f, t int) error {
+func setDefaultSockopts(s syscall.Handle, f, t int, ipv6only bool) error {
        switch f {
        case syscall.AF_INET6:
-               // Allow both IP versions even if the OS default is otherwise.
-               // Note that some operating systems never admit this option.
-               syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
+               if ipv6only {
+                       syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 1)
+               } else {
+                       // Allow both IP versions even if the OS default
+                       // is otherwise.  Note that some operating systems
+                       // never admit this option.
+                       syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
+               }
        }
        // Allow broadcast.
        syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
index e05bc10..15f8efd 100644 (file)
@@ -9,7 +9,6 @@
 package net
 
 import (
-       "fmt"
        "io"
        "os"
        "syscall"
@@ -30,7 +29,7 @@ func sockaddrToTCP(sa syscall.Sockaddr) Addr {
        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))
+                       panic("unexpected type in sockaddrToTCP")
                }
        }
        return nil
@@ -46,6 +45,13 @@ func (a *TCPAddr) family() int {
        return syscall.AF_INET6
 }
 
+func (a *TCPAddr) isWildcard() bool {
+       if a == nil || a.IP == nil {
+               return true
+       }
+       return a.IP.IsUnspecified()
+}
+
 func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
        return ipToSockaddr(family, a.IP, a.Port)
 }
index ef350f0..672fb72 100644 (file)
@@ -11,13 +11,13 @@ import (
        "time"
 )
 
-func testTimeout(t *testing.T, network, addr string, readFrom bool) {
-       fd, err := Dial(network, addr)
+func testTimeout(t *testing.T, net, addr string, readFrom bool) {
+       c, err := Dial(net, addr)
        if err != nil {
-               t.Errorf("dial %s %s failed: %v", network, addr, err)
+               t.Errorf("Dial(%q, %q) failed: %v", net, addr, err)
                return
        }
-       defer fd.Close()
+       defer c.Close()
        what := "Read"
        if readFrom {
                what = "ReadFrom"
@@ -26,22 +26,22 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) {
        errc := make(chan error, 1)
        go func() {
                t0 := time.Now()
-               fd.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+               c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
                var b [100]byte
                var n int
-               var err1 error
+               var err error
                if readFrom {
-                       n, _, err1 = fd.(PacketConn).ReadFrom(b[0:])
+                       n, _, err = c.(PacketConn).ReadFrom(b[0:])
                } else {
-                       n, err1 = fd.Read(b[0:])
+                       n, err = c.Read(b[0:])
                }
                t1 := time.Now()
-               if n != 0 || err1 == nil || !err1.(Error).Timeout() {
-                       errc <- fmt.Errorf("fd.%s on %s %s did not return 0, timeout: %v, %v", what, network, addr, n, err1)
+               if n != 0 || err == nil || !err.(Error).Timeout() {
+                       errc <- fmt.Errorf("%s(%q, %q) did not return 0, timeout: %v, %v", what, net, addr, n, err)
                        return
                }
                if dt := t1.Sub(t0); dt < 50*time.Millisecond || !testing.Short() && dt > 250*time.Millisecond {
-                       errc <- fmt.Errorf("fd.%s on %s %s took %s, expected 0.1s", what, network, addr, dt)
+                       errc <- fmt.Errorf("%s(%q, %q) took %s, expected 0.1s", what, net, addr, dt)
                        return
                }
                errc <- nil
@@ -52,26 +52,39 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) {
                        t.Error(err)
                }
        case <-time.After(1 * time.Second):
-               t.Errorf("%s on %s %s took over 1 second, expected 0.1s", what, network, addr)
+               t.Errorf("%s(%q, %q) took over 1 second, expected 0.1s", what, net, addr)
        }
 }
 
 func TestTimeoutUDP(t *testing.T) {
-       if runtime.GOOS == "plan9" {
+       switch runtime.GOOS {
+       case "plan9":
+               t.Logf("skipping test on %q", runtime.GOOS)
                return
        }
-       testTimeout(t, "udp", "127.0.0.1:53", false)
-       testTimeout(t, "udp", "127.0.0.1:53", true)
+
+       // set up a listener that won't talk back
+       listening := make(chan string)
+       done := make(chan int)
+       go runDatagramPacketConnServer(t, "udp", "127.0.0.1:0", listening, done)
+       addr := <-listening
+
+       testTimeout(t, "udp", addr, false)
+       testTimeout(t, "udp", addr, true)
+       <-done
 }
 
 func TestTimeoutTCP(t *testing.T) {
-       if runtime.GOOS == "plan9" {
+       switch runtime.GOOS {
+       case "plan9":
+               t.Logf("skipping test on %q", runtime.GOOS)
                return
        }
+
        // set up a listener that won't talk back
        listening := make(chan string)
        done := make(chan int)
-       go runServe(t, "tcp", "127.0.0.1:0", listening, done)
+       go runStreamConnServer(t, "tcp", "127.0.0.1:0", listening, done)
        addr := <-listening
 
        testTimeout(t, "tcp", addr, false)
@@ -79,7 +92,9 @@ func TestTimeoutTCP(t *testing.T) {
 }
 
 func TestDeadlineReset(t *testing.T) {
-       if runtime.GOOS == "plan9" {
+       switch runtime.GOOS {
+       case "plan9":
+               t.Logf("skipping test on %q", runtime.GOOS)
                return
        }
        ln, err := Listen("tcp", "127.0.0.1:0")
index ea5fad4..f80d3b5 100644 (file)
@@ -10,7 +10,9 @@ import (
 )
 
 func TestWriteToUDP(t *testing.T) {
-       if runtime.GOOS == "plan9" {
+       switch runtime.GOOS {
+       case "plan9":
+               t.Logf("skipping test on %q", runtime.GOOS)
                return
        }
 
index 1f99dc5..9e820e1 100644 (file)
@@ -37,6 +37,13 @@ func (a *UDPAddr) family() int {
        return syscall.AF_INET6
 }
 
+func (a *UDPAddr) isWildcard() bool {
+       if a == nil || a.IP == nil {
+               return true
+       }
+       return a.IP.IsUnspecified()
+}
+
 func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
        return ipToSockaddr(family, a.IP, a.Port)
 }
index 297276d..a23bc5a 100644 (file)
@@ -7,81 +7,481 @@ package net
 import (
        "io"
        "runtime"
+       "syscall"
        "testing"
 )
 
-var unicastTests = []struct {
-       net    string
-       laddr  string
-       ipv6   bool
-       packet bool
+var listenerTests = []struct {
+       net      string
+       laddr    string
+       ipv6     bool // test with underlying AF_INET6 socket
+       wildcard bool // test with wildcard address
 }{
-       {net: "tcp4", laddr: "127.0.0.1:0"},
-       {net: "tcp4", laddr: "previous"},
-       {net: "tcp6", laddr: "[::1]:0", ipv6: true},
-       {net: "tcp6", laddr: "previous", ipv6: true},
-       {net: "udp4", laddr: "127.0.0.1:0", packet: true},
-       {net: "udp6", laddr: "[::1]:0", ipv6: true, packet: true},
+       {net: "tcp", laddr: "", wildcard: true},
+       {net: "tcp", laddr: "0.0.0.0", wildcard: true},
+       {net: "tcp", laddr: "[::ffff:0.0.0.0]", wildcard: true},
+       {net: "tcp", laddr: "[::]", ipv6: true, wildcard: true},
+
+       {net: "tcp", laddr: "127.0.0.1"},
+       {net: "tcp", laddr: "[::ffff:127.0.0.1]"},
+       {net: "tcp", laddr: "[::1]", ipv6: true},
+
+       {net: "tcp4", laddr: "", wildcard: true},
+       {net: "tcp4", laddr: "0.0.0.0", wildcard: true},
+       {net: "tcp4", laddr: "[::ffff:0.0.0.0]", wildcard: true},
+
+       {net: "tcp4", laddr: "127.0.0.1"},
+       {net: "tcp4", laddr: "[::ffff:127.0.0.1]"},
+
+       {net: "tcp6", laddr: "", ipv6: true, wildcard: true},
+       {net: "tcp6", laddr: "[::]", ipv6: true, wildcard: true},
+
+       {net: "tcp6", laddr: "[::1]", ipv6: true},
 }
 
-func TestUnicastTCPAndUDP(t *testing.T) {
-       if runtime.GOOS == "plan9" || runtime.GOOS == "windows" {
+// TestTCPListener tests both single and double listen to a test
+// listener with same address family, same listening address and
+// same port.
+func TestTCPListener(t *testing.T) {
+       switch runtime.GOOS {
+       case "plan9", "windows":
+               t.Logf("skipping test on %q", runtime.GOOS)
                return
        }
 
-       prevladdr := ""
-       for _, tt := range unicastTests {
+       for _, tt := range listenerTests {
+               if tt.wildcard && (testing.Short() || !*testExternal) {
+                       continue
+               }
                if tt.ipv6 && !supportsIPv6 {
                        continue
                }
-               var (
-                       fd     *netFD
-                       closer io.Closer
-               )
-               if !tt.packet {
-                       if tt.laddr == "previous" {
-                               tt.laddr = prevladdr
+               l1, port := usableListenPort(t, tt.net, tt.laddr)
+               checkFirstListener(t, tt.net, tt.laddr+":"+port, l1)
+               l2, err := Listen(tt.net, tt.laddr+":"+port)
+               checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2)
+               fd := l1.(*TCPListener).fd
+               switch fd.family {
+               case syscall.AF_INET:
+                       testIPv4UnicastSocketOptions(t, fd)
+               case syscall.AF_INET6:
+                       testIPv6UnicastSocketOptions(t, fd)
+               }
+               l1.(io.Closer).Close()
+       }
+}
+
+// TestUDPListener tests both single and double listen to a test
+// listener with same address family, same listening address and
+// same port.
+func TestUDPListener(t *testing.T) {
+       switch runtime.GOOS {
+       case "plan9", "windows":
+               t.Logf("skipping test on %q", runtime.GOOS)
+               return
+       }
+
+       toudpnet := func(net string) string {
+               switch net {
+               case "tcp":
+                       return "udp"
+               case "tcp4":
+                       return "udp4"
+               case "tcp6":
+                       return "udp6"
+               }
+               return "<nil>"
+       }
+
+       for _, tt := range listenerTests {
+               if tt.wildcard && (testing.Short() || !*testExternal) {
+                       continue
+               }
+               if tt.ipv6 && !supportsIPv6 {
+                       continue
+               }
+               tt.net = toudpnet(tt.net)
+               l1, port := usableListenPacketPort(t, tt.net, tt.laddr)
+               checkFirstListener(t, tt.net, tt.laddr+":"+port, l1)
+               l2, err := ListenPacket(tt.net, tt.laddr+":"+port)
+               checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2)
+               fd := l1.(*UDPConn).fd
+               switch fd.family {
+               case syscall.AF_INET:
+                       testIPv4UnicastSocketOptions(t, fd)
+               case syscall.AF_INET6:
+                       testIPv6UnicastSocketOptions(t, fd)
+               }
+               l1.(io.Closer).Close()
+       }
+}
+
+func TestSimpleTCPListener(t *testing.T) {
+       switch runtime.GOOS {
+       case "plan9":
+               t.Logf("skipping test on %q", runtime.GOOS)
+               return
+       }
+
+       for _, tt := range listenerTests {
+               if tt.wildcard && (testing.Short() || !*testExternal) {
+                       continue
+               }
+               if tt.ipv6 {
+                       continue
+               }
+               l1, port := usableListenPort(t, tt.net, tt.laddr)
+               checkFirstListener(t, tt.net, tt.laddr+":"+port, l1)
+               l2, err := Listen(tt.net, tt.laddr+":"+port)
+               checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2)
+               l1.(io.Closer).Close()
+       }
+}
+
+func TestSimpleUDPListener(t *testing.T) {
+       switch runtime.GOOS {
+       case "plan9":
+               t.Logf("skipping test on %q", runtime.GOOS)
+               return
+       }
+
+       toudpnet := func(net string) string {
+               switch net {
+               case "tcp":
+                       return "udp"
+               case "tcp4":
+                       return "udp4"
+               case "tcp6":
+                       return "udp6"
+               }
+               return "<nil>"
+       }
+
+       for _, tt := range listenerTests {
+               if tt.wildcard && (testing.Short() || !*testExternal) {
+                       continue
+               }
+               if tt.ipv6 {
+                       continue
+               }
+               tt.net = toudpnet(tt.net)
+               l1, port := usableListenPacketPort(t, tt.net, tt.laddr)
+               checkFirstListener(t, tt.net, tt.laddr+":"+port, l1)
+               l2, err := ListenPacket(tt.net, tt.laddr+":"+port)
+               checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2)
+               l1.(io.Closer).Close()
+       }
+}
+
+var dualStackListenerTests = []struct {
+       net1     string // first listener
+       laddr1   string
+       net2     string // second listener
+       laddr2   string
+       wildcard bool  // test with wildcard address
+       xerr     error // expected error value, nil or other
+}{
+       // Test cases and expected results for the attemping 2nd listen on the same port
+       // 1st listen                2nd listen                 darwin  freebsd  linux  openbsd
+       // ------------------------------------------------------------------------------------
+       // "tcp"  ""                 "tcp"  ""                    -        -       -       - 
+       // "tcp"  ""                 "tcp"  "0.0.0.0"             -        -       -       - 
+       // "tcp"  "0.0.0.0"          "tcp"  ""                    -        -       -       - 
+       // ------------------------------------------------------------------------------------
+       // "tcp"  ""                 "tcp"  "[::]"                -        -       -       ok
+       // "tcp"  "[::]"             "tcp"  ""                    -        -       -       ok
+       // "tcp"  "0.0.0.0"          "tcp"  "[::]"                -        -       -       ok
+       // "tcp"  "[::]"             "tcp"  "0.0.0.0"             -        -       -       ok
+       // "tcp"  "[::ffff:0.0.0.0]" "tcp"  "[::]"                -        -       -       ok
+       // "tcp"  "[::]"             "tcp"  "[::ffff:0.0.0.0]"    -        -       -       ok
+       // ------------------------------------------------------------------------------------
+       // "tcp4" ""                 "tcp6" ""                    ok       ok      ok      ok
+       // "tcp6" ""                 "tcp4" ""                    ok       ok      ok      ok
+       // "tcp4" "0.0.0.0"          "tcp6" "[::]"                ok       ok      ok      ok
+       // "tcp6" "[::]"             "tcp4" "0.0.0.0"             ok       ok      ok      ok
+       // ------------------------------------------------------------------------------------
+       // "tcp"  "127.0.0.1"        "tcp"  "[::1]"               ok       ok      ok      ok
+       // "tcp"  "[::1]"            "tcp"  "127.0.0.1"           ok       ok      ok      ok
+       // "tcp4" "127.0.0.1"        "tcp6" "[::1]"               ok       ok      ok      ok
+       // "tcp6" "[::1]"            "tcp4" "127.0.0.1"           ok       ok      ok      ok
+       //
+       // Platform default configurations:
+       // darwin, kernel version 11.3.0
+       //      net.inet6.ip6.v6only=0 (overridable by sysctl or IPV6_V6ONLY option)
+       // freebsd, kernel version 8.2
+       //      net.inet6.ip6.v6only=1 (overridable by sysctl or IPV6_V6ONLY option)
+       // linux, kernel version 3.0.0
+       //      net.ipv6.bindv6only=0 (overridable by sysctl or IPV6_V6ONLY option)
+       // openbsd, kernel version 5.0
+       //      net.inet6.ip6.v6only=1 (overriding is prohibited)
+
+       {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE},
+       {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "0.0.0.0", wildcard: true, xerr: syscall.EADDRINUSE},
+       {net1: "tcp", laddr1: "0.0.0.0", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE},
+
+       {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE},
+       {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE},
+       {net1: "tcp", laddr1: "0.0.0.0", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE},
+       {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "0.0.0.0", wildcard: true, xerr: syscall.EADDRINUSE},
+       {net1: "tcp", laddr1: "[::ffff:0.0.0.0]", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE},
+       {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "[::ffff:0.0.0.0]", wildcard: true, xerr: syscall.EADDRINUSE},
+
+       {net1: "tcp4", laddr1: "", net2: "tcp6", laddr2: "", wildcard: true},
+       {net1: "tcp6", laddr1: "", net2: "tcp4", laddr2: "", wildcard: true},
+       {net1: "tcp4", laddr1: "0.0.0.0", net2: "tcp6", laddr2: "[::]", wildcard: true},
+       {net1: "tcp6", laddr1: "[::]", net2: "tcp4", laddr2: "0.0.0.0", wildcard: true},
+
+       {net1: "tcp", laddr1: "127.0.0.1", net2: "tcp", laddr2: "[::1]"},
+       {net1: "tcp", laddr1: "[::1]", net2: "tcp", laddr2: "127.0.0.1"},
+       {net1: "tcp4", laddr1: "127.0.0.1", net2: "tcp6", laddr2: "[::1]"},
+       {net1: "tcp6", laddr1: "[::1]", net2: "tcp4", laddr2: "127.0.0.1"},
+}
+
+// TestDualStackTCPListener tests both single and double listen
+// to a test listener with various address families, differnet
+// listening address and same port.
+func TestDualStackTCPListener(t *testing.T) {
+       switch runtime.GOOS {
+       case "plan9":
+               t.Logf("skipping test on %q", runtime.GOOS)
+               return
+       }
+       if !supportsIPv6 {
+               return
+       }
+
+       for _, tt := range dualStackListenerTests {
+               if tt.wildcard && (testing.Short() || !*testExternal) {
+                       continue
+               }
+               switch runtime.GOOS {
+               case "openbsd":
+                       if tt.wildcard && differentWildcardAddr(tt.laddr1, tt.laddr2) {
+                               tt.xerr = nil
+                       }
+               }
+               l1, port := usableListenPort(t, tt.net1, tt.laddr1)
+               laddr := tt.laddr1 + ":" + port
+               checkFirstListener(t, tt.net1, laddr, l1)
+               laddr = tt.laddr2 + ":" + port
+               l2, err := Listen(tt.net2, laddr)
+               checkDualStackSecondListener(t, tt.net2, laddr, tt.xerr, err, l2)
+               l1.Close()
+       }
+}
+
+// TestDualStackUDPListener tests both single and double listen
+// to a test listener with various address families, differnet
+// listening address and same port.
+func TestDualStackUDPListener(t *testing.T) {
+       switch runtime.GOOS {
+       case "plan9":
+               t.Logf("skipping test on %q", runtime.GOOS)
+               return
+       }
+       if !supportsIPv6 {
+               return
+       }
+
+       toudpnet := func(net string) string {
+               switch net {
+               case "tcp":
+                       return "udp"
+               case "tcp4":
+                       return "udp4"
+               case "tcp6":
+                       return "udp6"
+               }
+               return "<nil>"
+       }
+
+       for _, tt := range dualStackListenerTests {
+               if tt.wildcard && (testing.Short() || !*testExternal) {
+                       continue
+               }
+               tt.net1 = toudpnet(tt.net1)
+               tt.net2 = toudpnet(tt.net2)
+               switch runtime.GOOS {
+               case "openbsd":
+                       if tt.wildcard && differentWildcardAddr(tt.laddr1, tt.laddr2) {
+                               tt.xerr = nil
                        }
-                       l, err := Listen(tt.net, tt.laddr)
-                       if err != nil {
-                               t.Fatalf("Listen failed: %v", err)
+               }
+               l1, port := usableListenPacketPort(t, tt.net1, tt.laddr1)
+               laddr := tt.laddr1 + ":" + port
+               checkFirstListener(t, tt.net1, laddr, l1)
+               laddr = tt.laddr2 + ":" + port
+               l2, err := ListenPacket(tt.net2, laddr)
+               checkDualStackSecondListener(t, tt.net2, laddr, tt.xerr, err, l2)
+               l1.Close()
+       }
+}
+
+func usableListenPort(t *testing.T, net, laddr string) (l Listener, port string) {
+       var nladdr string
+       var err error
+       switch net {
+       default:
+               panic("usableListenPort net=" + net)
+       case "tcp", "tcp4", "tcp6":
+               l, err = Listen(net, laddr+":0")
+               if err != nil {
+                       t.Fatalf("Probe Listen(%q, %q) failed: %v", net, laddr, err)
+               }
+               nladdr = l.(*TCPListener).Addr().String()
+       }
+       _, port, err = SplitHostPort(nladdr)
+       if err != nil {
+               t.Fatalf("SplitHostPort failed: %v", err)
+       }
+       return l, port
+}
+
+func usableListenPacketPort(t *testing.T, net, laddr string) (l PacketConn, port string) {
+       var nladdr string
+       var err error
+       switch net {
+       default:
+               panic("usableListenPacketPort net=" + net)
+       case "udp", "udp4", "udp6":
+               l, err = ListenPacket(net, laddr+":0")
+               if err != nil {
+                       t.Fatalf("Probe ListenPacket(%q, %q) failed: %v", net, laddr, err)
+               }
+               nladdr = l.(*UDPConn).LocalAddr().String()
+       }
+       _, port, err = SplitHostPort(nladdr)
+       if err != nil {
+               t.Fatalf("SplitHostPort failed: %v", err)
+       }
+       return l, port
+}
+
+func differentWildcardAddr(i, j string) bool {
+       if (i == "" || i == "0.0.0.0" || i == "::ffff:0.0.0.0") && (j == "" || j == "0.0.0.0" || j == "::ffff:0.0.0.0") {
+               return false
+       }
+       if i == "[::]" && j == "[::]" {
+               return false
+       }
+       return true
+}
+
+func checkFirstListener(t *testing.T, net, laddr string, l interface{}) {
+       switch net {
+       case "tcp":
+               fd := l.(*TCPListener).fd
+               checkDualStackAddrFamily(t, net, laddr, fd)
+       case "tcp4":
+               fd := l.(*TCPListener).fd
+               if fd.family != syscall.AF_INET {
+                       t.Fatalf("First Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET)
+               }
+       case "tcp6":
+               fd := l.(*TCPListener).fd
+               if fd.family != syscall.AF_INET6 {
+                       t.Fatalf("First Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6)
+               }
+       case "udp":
+               fd := l.(*UDPConn).fd
+               checkDualStackAddrFamily(t, net, laddr, fd)
+       case "udp4":
+               fd := l.(*UDPConn).fd
+               if fd.family != syscall.AF_INET {
+                       t.Fatalf("First ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET)
+               }
+       case "udp6":
+               fd := l.(*UDPConn).fd
+               if fd.family != syscall.AF_INET6 {
+                       t.Fatalf("First ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6)
+               }
+       default:
+               t.Fatalf("Unexpected network: %q", net)
+       }
+}
+
+func checkSecondListener(t *testing.T, net, laddr string, err error, l interface{}) {
+       switch net {
+       case "tcp", "tcp4", "tcp6":
+               if err == nil {
+                       l.(*TCPListener).Close()
+                       t.Fatalf("Second Listen(%q, %q) should fail", net, laddr)
+               }
+       case "udp", "udp4", "udp6":
+               if err == nil {
+                       l.(*UDPConn).Close()
+                       t.Fatalf("Second ListenPacket(%q, %q) should fail", net, laddr)
+               }
+       default:
+               t.Fatalf("Unexpected network: %q", net)
+       }
+}
+
+func checkDualStackSecondListener(t *testing.T, net, laddr string, xerr, err error, l interface{}) {
+       switch net {
+       case "tcp", "tcp4", "tcp6":
+               if xerr == nil && err != nil || xerr != nil && err == nil {
+                       t.Fatalf("Second Listen(%q, %q) returns %v, expected %v", net, laddr, err, xerr)
+               }
+               l.(*TCPListener).Close()
+       case "udp", "udp4", "udp6":
+               if xerr == nil && err != nil || xerr != nil && err == nil {
+                       t.Fatalf("Second ListenPacket(%q, %q) returns %v, expected %v", net, laddr, err, xerr)
+               }
+               l.(*UDPConn).Close()
+       default:
+               t.Fatalf("Unexpected network: %q", net)
+       }
+}
+
+func checkDualStackAddrFamily(t *testing.T, net, laddr string, fd *netFD) {
+       switch a := fd.laddr.(type) {
+       case *TCPAddr:
+               // If a node under test supports both IPv6 capability
+               // and IPv6 IPv4-mapping capability, we can assume
+               // that the node listens on a wildcard address with an
+               // AF_INET6 socket.
+               if supportsIPv4map && fd.laddr.(*TCPAddr).isWildcard() {
+                       if fd.family != syscall.AF_INET6 {
+                               t.Fatalf("Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6)
                        }
-                       prevladdr = l.Addr().String()
-                       closer = l
-                       fd = l.(*TCPListener).fd
                } else {
-                       c, err := ListenPacket(tt.net, tt.laddr)
-                       if err != nil {
-                               t.Fatalf("ListenPacket failed: %v", err)
+                       if fd.family != a.family() {
+                               t.Fatalf("Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, a.family())
                        }
-                       closer = c
-                       fd = c.(*UDPConn).fd
                }
-               if !tt.ipv6 {
-                       testIPv4UnicastSocketOptions(t, fd)
+       case *UDPAddr:
+               // If a node under test supports both IPv6 capability
+               // and IPv6 IPv4-mapping capability, we can assume
+               // that the node listens on a wildcard address with an
+               // AF_INET6 socket.
+               if supportsIPv4map && fd.laddr.(*UDPAddr).isWildcard() {
+                       if fd.family != syscall.AF_INET6 {
+                               t.Fatalf("ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6)
+                       }
                } else {
-                       testIPv6UnicastSocketOptions(t, fd)
+                       if fd.family != a.family() {
+                               t.Fatalf("ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, a.family())
+                       }
                }
-               closer.Close()
+       default:
+               t.Fatalf("Unexpected protocol address type: %T", a)
        }
 }
 
 func testIPv4UnicastSocketOptions(t *testing.T, fd *netFD) {
-       tos, err := ipv4TOS(fd)
+       _, err := ipv4TOS(fd)
        if err != nil {
                t.Fatalf("ipv4TOS failed: %v", err)
        }
-       t.Logf("IPv4 TOS: %v", tos)
        err = setIPv4TOS(fd, 1)
        if err != nil {
                t.Fatalf("setIPv4TOS failed: %v", err)
        }
-
-       ttl, err := ipv4TTL(fd)
+       _, err = ipv4TTL(fd)
        if err != nil {
                t.Fatalf("ipv4TTL failed: %v", err)
        }
-       t.Logf("IPv4 TTL: %v", ttl)
        err = setIPv4TTL(fd, 1)
        if err != nil {
                t.Fatalf("setIPv4TTL failed: %v", err)
@@ -89,23 +489,50 @@ func testIPv4UnicastSocketOptions(t *testing.T, fd *netFD) {
 }
 
 func testIPv6UnicastSocketOptions(t *testing.T, fd *netFD) {
-       tos, err := ipv6TrafficClass(fd)
+       _, err := ipv6TrafficClass(fd)
        if err != nil {
                t.Fatalf("ipv6TrafficClass failed: %v", err)
        }
-       t.Logf("IPv6 TrafficClass: %v", tos)
        err = setIPv6TrafficClass(fd, 1)
        if err != nil {
                t.Fatalf("setIPv6TrafficClass failed: %v", err)
        }
-
-       hoplim, err := ipv6HopLimit(fd)
+       _, err = ipv6HopLimit(fd)
        if err != nil {
                t.Fatalf("ipv6HopLimit failed: %v", err)
        }
-       t.Logf("IPv6 HopLimit: %v", hoplim)
        err = setIPv6HopLimit(fd, 1)
        if err != nil {
                t.Fatalf("setIPv6HopLimit failed: %v", err)
        }
 }
+
+var prohibitionaryDialArgTests = []struct {
+       net  string
+       addr string
+}{
+       {"tcp6", "127.0.0.1"},
+       {"tcp6", "[::ffff:127.0.0.1]"},
+}
+
+func TestProhibitionaryDialArgs(t *testing.T) {
+       switch runtime.GOOS {
+       case "plan9":
+               t.Logf("skipping test on %q", runtime.GOOS)
+               return
+       }
+       // This test requires both IPv6 and IPv6 IPv4-mapping functionality.
+       if !supportsIPv4map || testing.Short() || !*testExternal {
+               return
+       }
+
+       l, port := usableListenPort(t, "tcp", "[::]")
+       defer l.Close()
+
+       for _, tt := range prohibitionaryDialArgTests {
+               _, err := Dial(tt.net, tt.addr+":"+port)
+               if err == nil {
+                       t.Fatalf("Dial(%q, %q) should fail", tt.net, tt.addr)
+               }
+       }
+}
index 3a94cf5..37a2b1e 100644 (file)
@@ -59,7 +59,7 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err
                f = sockaddrToUnixpacket
        }
 
-       fd, err = socket(net, syscall.AF_UNIX, sotype, 0, la, ra, f)
+       fd, err = socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, f)
        if err != nil {
                goto Error
        }
@@ -208,8 +208,8 @@ func (c *UnixConn) SetWriteBuffer(bytes int) error {
 }
 
 // ReadFromUnix reads a packet from c, copying the payload into b.
-// It returns the number of bytes copied into b and the return address
-// that was on the packet.
+// It returns the number of bytes copied into b and the source address
+// of the packet.
 //
 // ReadFromUnix can be made to time out and return
 // an error with Timeout() == true after a fixed time limit;
@@ -264,6 +264,11 @@ func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) {
        return c.WriteToUnix(b, a)
 }
 
+// ReadMsgUnix reads a packet from c, copying the payload into b
+// and the associated out-of-band data into oob.
+// It returns the number of bytes copied into b, the number of
+// bytes copied into oob, the flags that were set on the packet,
+// and the source address of the packet.
 func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
        if !c.ok() {
                return 0, 0, 0, nil, syscall.EINVAL
@@ -276,6 +281,9 @@ func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAdd
        return
 }
 
+// WriteMsgUnix writes a packet to addr via c, copying the payload from b
+// and the associated out-of-band data from oob.  It returns the number
+// of payload and out-of-band bytes written.
 func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
        if !c.ok() {
                return 0, 0, syscall.EINVAL
index 74b75d1..d08ad5d 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin freebsd linux netbsd openbsd windows
+// +build darwin freebsd linux netbsd openbsd
 
 package os
 
diff --git a/libgo/go/os/error_test.go b/libgo/go/os/error_test.go
new file mode 100644 (file)
index 0000000..8218f86
--- /dev/null
@@ -0,0 +1,31 @@
+// 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 os_test
+
+import (
+       "io/ioutil"
+       "os"
+       "testing"
+)
+
+func TestErrIsExist(t *testing.T) {
+       f, err := ioutil.TempFile("", "_Go_ErrIsExist")
+       if err != nil {
+               t.Fatalf("open ErrIsExist tempfile: %s", err)
+               return
+       }
+       defer os.Remove(f.Name())
+       defer f.Close()
+       f2, err := os.OpenFile(f.Name(), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
+       if err == nil {
+               f2.Close()
+               t.Fatal("Open should have failed")
+               return
+       }
+       if !os.IsExist(err) {
+               t.Fatalf("os.IsExist does not work as expected for %#v", err)
+               return
+       }
+}
diff --git a/libgo/go/os/error_windows.go b/libgo/go/os/error_windows.go
new file mode 100644 (file)
index 0000000..84bf5ea
--- /dev/null
@@ -0,0 +1,35 @@
+// 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 os
+
+import "syscall"
+
+// 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 == syscall.ERROR_ALREADY_EXISTS ||
+               err == syscall.ERROR_FILE_EXISTS || 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
+}
index ebe92a9..bbd0490 100644 (file)
@@ -59,7 +59,7 @@ type Cmd struct {
        // If either is nil, Run connects the corresponding file descriptor
        // to the null device (os.DevNull).
        //
-       // If Stdout and Stderr are are the same writer, at most one
+       // If Stdout and Stderr are the same writer, at most one
        // goroutine at a time will call Write.
        Stdout io.Writer
        Stderr io.Writer
index c7c5199..01dddf5 100644 (file)
@@ -15,7 +15,7 @@ func Getpagesize() int { return syscall.Getpagesize() }
 // A FileInfo describes a file and is returned by Stat and Lstat
 type FileInfo interface {
        Name() string       // base name of the file
-       Size() int64        // length in bytes
+       Size() int64        // length in bytes for regular files; system-dependent for others
        Mode() FileMode     // file mode bits
        ModTime() time.Time // modification time
        IsDir() bool        // abbreviation for Mode().IsDir()
index cfe4698..1e74872 100644 (file)
@@ -7,10 +7,8 @@
 package filepath
 
 import (
-       "bytes"
        "errors"
        "os"
-       "runtime"
        "sort"
        "strings"
 )
@@ -191,64 +189,7 @@ func Ext(path string) string {
 // 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.
-               _, err := os.Lstat(path)
-               if err != nil {
-                       return "", err
-               }
-               return Clean(path), nil
-       }
-       const maxIter = 255
-       originalPath := path
-       // consume path by taking each frontmost path element,
-       // expanding it if it's a symlink, and appending it to b
-       var b bytes.Buffer
-       for n := 0; path != ""; n++ {
-               if n > maxIter {
-                       return "", errors.New("EvalSymlinks: too many links in " + originalPath)
-               }
-
-               // find next path component, p
-               i := strings.IndexRune(path, Separator)
-               var p string
-               if i == -1 {
-                       p, path = path, ""
-               } else {
-                       p, path = path[:i], path[i+1:]
-               }
-
-               if p == "" {
-                       if b.Len() == 0 {
-                               // must be absolute path
-                               b.WriteRune(Separator)
-                       }
-                       continue
-               }
-
-               fi, err := os.Lstat(b.String() + p)
-               if err != nil {
-                       return "", err
-               }
-               if fi.Mode()&os.ModeSymlink == 0 {
-                       b.WriteString(p)
-                       if path != "" {
-                               b.WriteRune(Separator)
-                       }
-                       continue
-               }
-
-               // it's a symlink, put it at the front of path
-               dest, err := os.Readlink(b.String() + p)
-               if err != nil {
-                       return "", err
-               }
-               if IsAbs(dest) {
-                       b.Reset()
-               }
-               path = dest + string(Separator) + path
-       }
-       return Clean(b.String()), nil
+       return evalSymlinks(path)
 }
 
 // Abs returns an absolute representation of path.
index 93cca1e..87cb5e5 100644 (file)
@@ -10,6 +10,7 @@ import (
        "path/filepath"
        "reflect"
        "runtime"
+       "strings"
        "testing"
 )
 
@@ -439,7 +440,7 @@ func TestBase(t *testing.T) {
        tests := basetests
        if runtime.GOOS == "windows" {
                // make unix tests work on windows
-               for i, _ := range tests {
+               for i := range tests {
                        tests[i].result = filepath.Clean(tests[i].result)
                }
                // add windows specific tests
@@ -482,7 +483,7 @@ func TestDir(t *testing.T) {
        tests := dirtests
        if runtime.GOOS == "windows" {
                // make unix tests work on windows
-               for i, _ := range tests {
+               for i := range tests {
                        tests[i].result = filepath.Clean(tests[i].result)
                }
                // add windows specific tests
@@ -620,6 +621,12 @@ func TestEvalSymlinks(t *testing.T) {
                        if d.path == d.dest {
                                // will test only real files and directories
                                tests = append(tests, d)
+                               // test "canonical" names
+                               d2 := EvalSymlinksTest{
+                                       path: strings.ToUpper(d.path),
+                                       dest: d.dest,
+                               }
+                               tests = append(tests, d2)
                        }
                }
        } else {
@@ -641,35 +648,61 @@ func TestEvalSymlinks(t *testing.T) {
        }
 }
 
-/* These tests do not work in the gccgo test environment.
+// Test directories relative to temporary directory.
+// The tests are run in absTestDirs[0].
+var absTestDirs = []string{
+       "a",
+       "a/b",
+       "a/b/c",
+}
 
-// Test paths relative to $GOROOT/src
-var abstests = []string{
-       "../AUTHORS",
-       "pkg/../../AUTHORS",
-       "Make.inc",
-       "pkg/math",
+// Test paths relative to temporary directory. $ expands to the directory.
+// The tests are run in absTestDirs[0].
+// We create absTestDirs first.
+var absTests = []string{
        ".",
-       "$GOROOT/src/Make.inc",
-       "$GOROOT/src/../src/Make.inc",
-       "$GOROOT/misc/cgo",
-       "$GOROOT",
+       "b",
+       "../a",
+       "../a/b",
+       "../a/b/./c/../../.././a",
+       "$",
+       "$/.",
+       "$/a/../a/b",
+       "$/a/b/c/../../.././a",
 }
 
 func TestAbs(t *testing.T) {
-       t.Logf("test needs to be rewritten; disabled")
-       return
-
        oldwd, err := os.Getwd()
        if err != nil {
-               t.Fatal("Getwd failed: " + err.Error())
+               t.Fatal("Getwd failed: ", err)
        }
        defer os.Chdir(oldwd)
-       goroot := os.Getenv("GOROOT")
-       cwd := filepath.Join(goroot, "src")
-       os.Chdir(cwd)
-       for _, path := range abstests {
-               path = strings.Replace(path, "$GOROOT", goroot, -1)
+
+       root, err := ioutil.TempDir("", "TestAbs")
+       if err != nil {
+               t.Fatal("TempDir failed: ", err)
+       }
+       defer os.RemoveAll(root)
+
+       err = os.Chdir(root)
+       if err != nil {
+               t.Fatal("chdir failed: ", err)
+       }
+
+       for _, dir := range absTestDirs {
+               err = os.Mkdir(dir, 0777)
+               if err != nil {
+                       t.Fatal("Mkdir failed: ", err)
+               }
+       }
+
+       err = os.Chdir(absTestDirs[0])
+       if err != nil {
+               t.Fatal("chdir failed: ", err)
+       }
+
+       for _, path := range absTests {
+               path = strings.Replace(path, "$", root, -1)
                info, err := os.Stat(path)
                if err != nil {
                        t.Errorf("%s: %s", path, err)
@@ -694,8 +727,6 @@ func TestAbs(t *testing.T) {
        }
 }
 
-*/
-
 type RelTests struct {
        root, path, want string
 }
diff --git a/libgo/go/path/filepath/symlink.go b/libgo/go/path/filepath/symlink.go
new file mode 100644 (file)
index 0000000..307dd0f
--- /dev/null
@@ -0,0 +1,67 @@
+// 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 !windows
+
+package filepath
+
+import (
+       "bytes"
+       "errors"
+       "os"
+       "strings"
+)
+
+func evalSymlinks(path string) (string, error) {
+       const maxIter = 255
+       originalPath := path
+       // consume path by taking each frontmost path element,
+       // expanding it if it's a symlink, and appending it to b
+       var b bytes.Buffer
+       for n := 0; path != ""; n++ {
+               if n > maxIter {
+                       return "", errors.New("EvalSymlinks: too many links in " + originalPath)
+               }
+
+               // find next path component, p
+               i := strings.IndexRune(path, Separator)
+               var p string
+               if i == -1 {
+                       p, path = path, ""
+               } else {
+                       p, path = path[:i], path[i+1:]
+               }
+
+               if p == "" {
+                       if b.Len() == 0 {
+                               // must be absolute path
+                               b.WriteRune(Separator)
+                       }
+                       continue
+               }
+
+               fi, err := os.Lstat(b.String() + p)
+               if err != nil {
+                       return "", err
+               }
+               if fi.Mode()&os.ModeSymlink == 0 {
+                       b.WriteString(p)
+                       if path != "" {
+                               b.WriteRune(Separator)
+                       }
+                       continue
+               }
+
+               // it's a symlink, put it at the front of path
+               dest, err := os.Readlink(b.String() + p)
+               if err != nil {
+                       return "", err
+               }
+               if IsAbs(dest) {
+                       b.Reset()
+               }
+               path = dest + string(Separator) + path
+       }
+       return Clean(b.String()), nil
+}
diff --git a/libgo/go/path/filepath/symlink_windows.go b/libgo/go/path/filepath/symlink_windows.go
new file mode 100644 (file)
index 0000000..afa88bf
--- /dev/null
@@ -0,0 +1,27 @@
+// 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 filepath
+
+import (
+       "syscall"
+)
+
+func evalSymlinks(path string) (string, error) {
+       p := syscall.StringToUTF16(path)
+       b := p // GetLongPathName says we can reuse buffer
+       n, err := syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
+       if err != nil {
+               return "", err
+       }
+       if n > uint32(len(b)) {
+               b = make([]uint16, n)
+               n, err = syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
+               if err != nil {
+                       return "", err
+               }
+       }
+       b = b[:n]
+       return Clean(syscall.UTF16ToString(b)), nil
+}
index b1dd0a1..92e84f4 100644 (file)
@@ -234,6 +234,7 @@ type commonType struct {
        kind       uint8   // enumeration for C
        align      int8    // alignment of variable with this type
        fieldAlign uint8   // alignment of struct field with this type
+       _          uint8   // unused/padding
        size       uintptr // size in bytes
        hash       uint32  // hash of type; avoids computation in hash tables
 
index b490e99..f3a0a7c 100644 (file)
@@ -54,6 +54,10 @@ func memmove(adst, asrc unsafe.Pointer, n uintptr) {
 // its String method returns "<invalid Value>", and all other methods panic.
 // Most functions and methods never return an invalid value.
 // If one does, its documentation states the conditions explicitly.
+//
+// A Value can be used concurrently by multiple goroutines provided that
+// the underlying Go value can be used concurrently for the equivalent
+// direct operations.
 type Value struct {
        // typ holds the type of the value represented by a Value.
        typ *commonType
diff --git a/libgo/go/runtime/compiler.go b/libgo/go/runtime/compiler.go
new file mode 100644 (file)
index 0000000..0ed3b18
--- /dev/null
@@ -0,0 +1,13 @@
+// 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 runtime
+
+// Compiler is the name of the compiler toolchain that built the
+// running binary.  Known toolchains are:
+//
+//     gc      The 5g/6g/8g compiler suite at code.google.com/p/go.
+//     gccgo   The gccgo front end, part of the GCC compiler suite.
+//
+const Compiler = "gccgo"
index 94293bb..f33f507 100644 (file)
@@ -39,13 +39,20 @@ func TestStack(t *testing.T) {
        if len(lines) <= 6 {
                t.Fatal("too few lines")
        }
-       check(t, lines[0], "src/pkg/runtime/debug/stack_test.go")
-       check(t, lines[1], "\t(*T).ptrmethod: return Stack()")
-       check(t, lines[2], "src/pkg/runtime/debug/stack_test.go")
-       check(t, lines[3], "\tT.method: return t.ptrmethod()")
-       check(t, lines[4], "src/pkg/runtime/debug/stack_test.go")
-       check(t, lines[5], "\tTestStack: b := T(0).method()")
-       check(t, lines[6], "src/pkg/testing/testing.go")
+       n := 0
+       frame := func(line, code string) {
+               check(t, lines[n], line)
+               n++
+               // The source might not be available while running the test.
+               if strings.HasPrefix(lines[n], "\t") {
+                       check(t, lines[n], code)
+                       n++
+               }
+       }
+       frame("src/pkg/runtime/debug/stack_test.go", "\t(*T).ptrmethod: return Stack()")
+       frame("src/pkg/runtime/debug/stack_test.go", "\tT.method: return t.ptrmethod()")
+       frame("src/pkg/runtime/debug/stack_test.go", "\tTestStack: b := T(0).method()")
+       frame("src/pkg/testing/testing.go", "")
 }
 
 func check(t *testing.T, line, has string) {
index 2dc7aef..82bb2a2 100644 (file)
@@ -24,8 +24,9 @@ func TestCPUProfile(t *testing.T) {
                }
                vers := string(out)
                t.Logf("uname -a: %v", vers)
-               if strings.Contains(vers, "Darwin Kernel Version 10.8.0") && strings.Contains(vers, "root:xnu-1504.15.3~1/RELEASE_X86_64") {
-                       t.Logf("skipping test on known-broken kernel (64-bit Snow Leopard)")
+               // Lion uses "Darwin Kernel Version 11".
+               if strings.Contains(vers, "Darwin Kernel Version 10") && strings.Contains(vers, "RELEASE_X86_64") {
+                       t.Logf("skipping test on known-broken kernel (64-bit Leopard / Snow Leopard)")
                        return
                }
        case "plan9":
diff --git a/libgo/go/strconv/isprint.go b/libgo/go/strconv/isprint.go
new file mode 100644 (file)
index 0000000..a03a07b
--- /dev/null
@@ -0,0 +1,521 @@
+// DO NOT EDIT.  GENERATED BY
+//     go run makeisprint.go >x && mv x isprint.go
+
+package strconv
+
+// (474+134+42)*2 + (180)*4 = 2020 bytes
+
+var isPrint16 = []uint16{
+       0x0020, 0x007e,
+       0x00a1, 0x0377,
+       0x037a, 0x037e,
+       0x0384, 0x0527,
+       0x0531, 0x0556,
+       0x0559, 0x058a,
+       0x0591, 0x05c7,
+       0x05d0, 0x05ea,
+       0x05f0, 0x05f4,
+       0x0606, 0x061b,
+       0x061e, 0x070d,
+       0x0710, 0x074a,
+       0x074d, 0x07b1,
+       0x07c0, 0x07fa,
+       0x0800, 0x082d,
+       0x0830, 0x085b,
+       0x085e, 0x085e,
+       0x0900, 0x098c,
+       0x098f, 0x0990,
+       0x0993, 0x09b2,
+       0x09b6, 0x09b9,
+       0x09bc, 0x09c4,
+       0x09c7, 0x09c8,
+       0x09cb, 0x09ce,
+       0x09d7, 0x09d7,
+       0x09dc, 0x09e3,
+       0x09e6, 0x09fb,
+       0x0a01, 0x0a0a,
+       0x0a0f, 0x0a10,
+       0x0a13, 0x0a39,
+       0x0a3c, 0x0a42,
+       0x0a47, 0x0a48,
+       0x0a4b, 0x0a4d,
+       0x0a51, 0x0a51,
+       0x0a59, 0x0a5e,
+       0x0a66, 0x0a75,
+       0x0a81, 0x0ab9,
+       0x0abc, 0x0acd,
+       0x0ad0, 0x0ad0,
+       0x0ae0, 0x0ae3,
+       0x0ae6, 0x0af1,
+       0x0b01, 0x0b0c,
+       0x0b0f, 0x0b10,
+       0x0b13, 0x0b39,
+       0x0b3c, 0x0b44,
+       0x0b47, 0x0b48,
+       0x0b4b, 0x0b4d,
+       0x0b56, 0x0b57,
+       0x0b5c, 0x0b63,
+       0x0b66, 0x0b77,
+       0x0b82, 0x0b8a,
+       0x0b8e, 0x0b95,
+       0x0b99, 0x0b9f,
+       0x0ba3, 0x0ba4,
+       0x0ba8, 0x0baa,
+       0x0bae, 0x0bb9,
+       0x0bbe, 0x0bc2,
+       0x0bc6, 0x0bcd,
+       0x0bd0, 0x0bd0,
+       0x0bd7, 0x0bd7,
+       0x0be6, 0x0bfa,
+       0x0c01, 0x0c39,
+       0x0c3d, 0x0c4d,
+       0x0c55, 0x0c59,
+       0x0c60, 0x0c63,
+       0x0c66, 0x0c6f,
+       0x0c78, 0x0c7f,
+       0x0c82, 0x0cb9,
+       0x0cbc, 0x0ccd,
+       0x0cd5, 0x0cd6,
+       0x0cde, 0x0ce3,
+       0x0ce6, 0x0cf2,
+       0x0d02, 0x0d3a,
+       0x0d3d, 0x0d4e,
+       0x0d57, 0x0d57,
+       0x0d60, 0x0d63,
+       0x0d66, 0x0d75,
+       0x0d79, 0x0d7f,
+       0x0d82, 0x0d96,
+       0x0d9a, 0x0dbd,
+       0x0dc0, 0x0dc6,
+       0x0dca, 0x0dca,
+       0x0dcf, 0x0ddf,
+       0x0df2, 0x0df4,
+       0x0e01, 0x0e3a,
+       0x0e3f, 0x0e5b,
+       0x0e81, 0x0e84,
+       0x0e87, 0x0e8a,
+       0x0e8d, 0x0e8d,
+       0x0e94, 0x0ea7,
+       0x0eaa, 0x0ebd,
+       0x0ec0, 0x0ecd,
+       0x0ed0, 0x0ed9,
+       0x0edc, 0x0edd,
+       0x0f00, 0x0f6c,
+       0x0f71, 0x0fda,
+       0x1000, 0x10c5,
+       0x10d0, 0x10fc,
+       0x1100, 0x124d,
+       0x1250, 0x125d,
+       0x1260, 0x128d,
+       0x1290, 0x12b5,
+       0x12b8, 0x12c5,
+       0x12c8, 0x1315,
+       0x1318, 0x135a,
+       0x135d, 0x137c,
+       0x1380, 0x1399,
+       0x13a0, 0x13f4,
+       0x1400, 0x169c,
+       0x16a0, 0x16f0,
+       0x1700, 0x1714,
+       0x1720, 0x1736,
+       0x1740, 0x1753,
+       0x1760, 0x1773,
+       0x1780, 0x17b3,
+       0x17b6, 0x17dd,
+       0x17e0, 0x17e9,
+       0x17f0, 0x17f9,
+       0x1800, 0x180d,
+       0x1810, 0x1819,
+       0x1820, 0x1877,
+       0x1880, 0x18aa,
+       0x18b0, 0x18f5,
+       0x1900, 0x191c,
+       0x1920, 0x192b,
+       0x1930, 0x193b,
+       0x1940, 0x1940,
+       0x1944, 0x196d,
+       0x1970, 0x1974,
+       0x1980, 0x19ab,
+       0x19b0, 0x19c9,
+       0x19d0, 0x19da,
+       0x19de, 0x1a1b,
+       0x1a1e, 0x1a7c,
+       0x1a7f, 0x1a89,
+       0x1a90, 0x1a99,
+       0x1aa0, 0x1aad,
+       0x1b00, 0x1b4b,
+       0x1b50, 0x1b7c,
+       0x1b80, 0x1baa,
+       0x1bae, 0x1bb9,
+       0x1bc0, 0x1bf3,
+       0x1bfc, 0x1c37,
+       0x1c3b, 0x1c49,
+       0x1c4d, 0x1c7f,
+       0x1cd0, 0x1cf2,
+       0x1d00, 0x1de6,
+       0x1dfc, 0x1f15,
+       0x1f18, 0x1f1d,
+       0x1f20, 0x1f45,
+       0x1f48, 0x1f4d,
+       0x1f50, 0x1f7d,
+       0x1f80, 0x1fd3,
+       0x1fd6, 0x1fef,
+       0x1ff2, 0x1ffe,
+       0x2010, 0x2027,
+       0x2030, 0x205e,
+       0x2070, 0x2071,
+       0x2074, 0x209c,
+       0x20a0, 0x20b9,
+       0x20d0, 0x20f0,
+       0x2100, 0x2189,
+       0x2190, 0x23f3,
+       0x2400, 0x2426,
+       0x2440, 0x244a,
+       0x2460, 0x2b4c,
+       0x2b50, 0x2b59,
+       0x2c00, 0x2cf1,
+       0x2cf9, 0x2d25,
+       0x2d30, 0x2d65,
+       0x2d6f, 0x2d70,
+       0x2d7f, 0x2d96,
+       0x2da0, 0x2e31,
+       0x2e80, 0x2ef3,
+       0x2f00, 0x2fd5,
+       0x2ff0, 0x2ffb,
+       0x3001, 0x3096,
+       0x3099, 0x30ff,
+       0x3105, 0x312d,
+       0x3131, 0x31ba,
+       0x31c0, 0x31e3,
+       0x31f0, 0x4db5,
+       0x4dc0, 0x9fcb,
+       0xa000, 0xa48c,
+       0xa490, 0xa4c6,
+       0xa4d0, 0xa62b,
+       0xa640, 0xa673,
+       0xa67c, 0xa697,
+       0xa6a0, 0xa6f7,
+       0xa700, 0xa791,
+       0xa7a0, 0xa7a9,
+       0xa7fa, 0xa82b,
+       0xa830, 0xa839,
+       0xa840, 0xa877,
+       0xa880, 0xa8c4,
+       0xa8ce, 0xa8d9,
+       0xa8e0, 0xa8fb,
+       0xa900, 0xa953,
+       0xa95f, 0xa97c,
+       0xa980, 0xa9d9,
+       0xa9de, 0xa9df,
+       0xaa00, 0xaa36,
+       0xaa40, 0xaa4d,
+       0xaa50, 0xaa59,
+       0xaa5c, 0xaa7b,
+       0xaa80, 0xaac2,
+       0xaadb, 0xaadf,
+       0xab01, 0xab06,
+       0xab09, 0xab0e,
+       0xab11, 0xab16,
+       0xab20, 0xab2e,
+       0xabc0, 0xabed,
+       0xabf0, 0xabf9,
+       0xac00, 0xd7a3,
+       0xd7b0, 0xd7c6,
+       0xd7cb, 0xd7fb,
+       0xf900, 0xfa2d,
+       0xfa30, 0xfa6d,
+       0xfa70, 0xfad9,
+       0xfb00, 0xfb06,
+       0xfb13, 0xfb17,
+       0xfb1d, 0xfbc1,
+       0xfbd3, 0xfd3f,
+       0xfd50, 0xfd8f,
+       0xfd92, 0xfdc7,
+       0xfdf0, 0xfdfd,
+       0xfe00, 0xfe19,
+       0xfe20, 0xfe26,
+       0xfe30, 0xfe6b,
+       0xfe70, 0xfefc,
+       0xff01, 0xffbe,
+       0xffc2, 0xffc7,
+       0xffca, 0xffcf,
+       0xffd2, 0xffd7,
+       0xffda, 0xffdc,
+       0xffe0, 0xffee,
+       0xfffc, 0xfffd,
+}
+
+var isNotPrint16 = []uint16{
+       0x00ad,
+       0x038b,
+       0x038d,
+       0x03a2,
+       0x0560,
+       0x0588,
+       0x06dd,
+       0x083f,
+       0x0978,
+       0x0980,
+       0x0984,
+       0x09a9,
+       0x09b1,
+       0x09de,
+       0x0a04,
+       0x0a29,
+       0x0a31,
+       0x0a34,
+       0x0a37,
+       0x0a3d,
+       0x0a5d,
+       0x0a84,
+       0x0a8e,
+       0x0a92,
+       0x0aa9,
+       0x0ab1,
+       0x0ab4,
+       0x0ac6,
+       0x0aca,
+       0x0af0,
+       0x0b04,
+       0x0b29,
+       0x0b31,
+       0x0b34,
+       0x0b5e,
+       0x0b84,
+       0x0b91,
+       0x0b9b,
+       0x0b9d,
+       0x0bc9,
+       0x0c04,
+       0x0c0d,
+       0x0c11,
+       0x0c29,
+       0x0c34,
+       0x0c45,
+       0x0c49,
+       0x0c57,
+       0x0c84,
+       0x0c8d,
+       0x0c91,
+       0x0ca9,
+       0x0cb4,
+       0x0cc5,
+       0x0cc9,
+       0x0cdf,
+       0x0cf0,
+       0x0d04,
+       0x0d0d,
+       0x0d11,
+       0x0d45,
+       0x0d49,
+       0x0d84,
+       0x0db2,
+       0x0dbc,
+       0x0dd5,
+       0x0dd7,
+       0x0e83,
+       0x0e89,
+       0x0e98,
+       0x0ea0,
+       0x0ea4,
+       0x0ea6,
+       0x0eac,
+       0x0eba,
+       0x0ec5,
+       0x0ec7,
+       0x0f48,
+       0x0f98,
+       0x0fbd,
+       0x0fcd,
+       0x1249,
+       0x1257,
+       0x1259,
+       0x1289,
+       0x12b1,
+       0x12bf,
+       0x12c1,
+       0x12d7,
+       0x1311,
+       0x1680,
+       0x170d,
+       0x176d,
+       0x1771,
+       0x1a5f,
+       0x1f58,
+       0x1f5a,
+       0x1f5c,
+       0x1f5e,
+       0x1fb5,
+       0x1fc5,
+       0x1fdc,
+       0x1ff5,
+       0x208f,
+       0x2700,
+       0x27cb,
+       0x27cd,
+       0x2c2f,
+       0x2c5f,
+       0x2da7,
+       0x2daf,
+       0x2db7,
+       0x2dbf,
+       0x2dc7,
+       0x2dcf,
+       0x2dd7,
+       0x2ddf,
+       0x2e9a,
+       0x3040,
+       0x318f,
+       0x321f,
+       0x32ff,
+       0xa78f,
+       0xa9ce,
+       0xab27,
+       0xfb37,
+       0xfb3d,
+       0xfb3f,
+       0xfb42,
+       0xfb45,
+       0xfe53,
+       0xfe67,
+       0xfe75,
+       0xffe7,
+}
+
+var isPrint32 = []uint32{
+       0x010000, 0x01004d,
+       0x010050, 0x01005d,
+       0x010080, 0x0100fa,
+       0x010100, 0x010102,
+       0x010107, 0x010133,
+       0x010137, 0x01018a,
+       0x010190, 0x01019b,
+       0x0101d0, 0x0101fd,
+       0x010280, 0x01029c,
+       0x0102a0, 0x0102d0,
+       0x010300, 0x010323,
+       0x010330, 0x01034a,
+       0x010380, 0x0103c3,
+       0x0103c8, 0x0103d5,
+       0x010400, 0x01049d,
+       0x0104a0, 0x0104a9,
+       0x010800, 0x010805,
+       0x010808, 0x010838,
+       0x01083c, 0x01083c,
+       0x01083f, 0x01085f,
+       0x010900, 0x01091b,
+       0x01091f, 0x010939,
+       0x01093f, 0x01093f,
+       0x010a00, 0x010a06,
+       0x010a0c, 0x010a33,
+       0x010a38, 0x010a3a,
+       0x010a3f, 0x010a47,
+       0x010a50, 0x010a58,
+       0x010a60, 0x010a7f,
+       0x010b00, 0x010b35,
+       0x010b39, 0x010b55,
+       0x010b58, 0x010b72,
+       0x010b78, 0x010b7f,
+       0x010c00, 0x010c48,
+       0x010e60, 0x010e7e,
+       0x011000, 0x01104d,
+       0x011052, 0x01106f,
+       0x011080, 0x0110c1,
+       0x012000, 0x01236e,
+       0x012400, 0x012462,
+       0x012470, 0x012473,
+       0x013000, 0x01342e,
+       0x016800, 0x016a38,
+       0x01b000, 0x01b001,
+       0x01d000, 0x01d0f5,
+       0x01d100, 0x01d126,
+       0x01d129, 0x01d172,
+       0x01d17b, 0x01d1dd,
+       0x01d200, 0x01d245,
+       0x01d300, 0x01d356,
+       0x01d360, 0x01d371,
+       0x01d400, 0x01d49f,
+       0x01d4a2, 0x01d4a2,
+       0x01d4a5, 0x01d4a6,
+       0x01d4a9, 0x01d50a,
+       0x01d50d, 0x01d546,
+       0x01d54a, 0x01d6a5,
+       0x01d6a8, 0x01d7cb,
+       0x01d7ce, 0x01d7ff,
+       0x01f000, 0x01f02b,
+       0x01f030, 0x01f093,
+       0x01f0a0, 0x01f0ae,
+       0x01f0b1, 0x01f0be,
+       0x01f0c1, 0x01f0df,
+       0x01f100, 0x01f10a,
+       0x01f110, 0x01f169,
+       0x01f170, 0x01f19a,
+       0x01f1e6, 0x01f202,
+       0x01f210, 0x01f23a,
+       0x01f240, 0x01f248,
+       0x01f250, 0x01f251,
+       0x01f300, 0x01f320,
+       0x01f330, 0x01f37c,
+       0x01f380, 0x01f393,
+       0x01f3a0, 0x01f3ca,
+       0x01f3e0, 0x01f3f0,
+       0x01f400, 0x01f4fc,
+       0x01f500, 0x01f53d,
+       0x01f550, 0x01f567,
+       0x01f5fb, 0x01f625,
+       0x01f628, 0x01f62d,
+       0x01f630, 0x01f640,
+       0x01f645, 0x01f64f,
+       0x01f680, 0x01f6c5,
+       0x01f700, 0x01f773,
+       0x020000, 0x02a6d6,
+       0x02a700, 0x02b734,
+       0x02b740, 0x02b81d,
+       0x02f800, 0x02fa1d,
+       0x0e0100, 0x0e01ef,
+}
+
+var isNotPrint32 = []uint16{ // add 0x10000 to each entry
+       0x000c,
+       0x0027,
+       0x003b,
+       0x003e,
+       0x031f,
+       0x039e,
+       0x0809,
+       0x0836,
+       0x0856,
+       0x0a04,
+       0x0a14,
+       0x0a18,
+       0x10bd,
+       0xd455,
+       0xd49d,
+       0xd4ad,
+       0xd4ba,
+       0xd4bc,
+       0xd4c4,
+       0xd506,
+       0xd515,
+       0xd51d,
+       0xd53a,
+       0xd53f,
+       0xd545,
+       0xd551,
+       0xf0d0,
+       0xf12f,
+       0xf336,
+       0xf3c5,
+       0xf43f,
+       0xf441,
+       0xf4f8,
+       0xf600,
+       0xf611,
+       0xf615,
+       0xf617,
+       0xf619,
+       0xf61b,
+       0xf61f,
+       0xf62c,
+       0xf634,
+}
diff --git a/libgo/go/strconv/makeisprint.go b/libgo/go/strconv/makeisprint.go
new file mode 100644 (file)
index 0000000..8a6699b
--- /dev/null
@@ -0,0 +1,162 @@
+// 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 ignore
+
+// makeisprint generates the tables for strconv's compact isPrint.
+package main
+
+import (
+       "fmt"
+       "os"
+       "unicode"
+)
+
+var (
+       range16  []uint16
+       except16 []uint16
+       range32  []uint32
+       except32 []uint32
+)
+
+// bsearch16 returns the smallest i such that a[i] >= x.
+// If there is no such i, bsearch16 returns len(a).
+func bsearch16(a []uint16, x uint16) int {
+       i, j := 0, len(a)
+       for i < j {
+               h := i + (j-i)/2
+               if a[h] < x {
+                       i = h + 1
+               } else {
+                       j = h
+               }
+       }
+       return i
+}
+
+// bsearch32 returns the smallest i such that a[i] >= x.
+// If there is no such i, bsearch32 returns len(a).
+func bsearch32(a []uint32, x uint32) int {
+       i, j := 0, len(a)
+       for i < j {
+               h := i + (j-i)/2
+               if a[h] < x {
+                       i = h + 1
+               } else {
+                       j = h
+               }
+       }
+       return i
+}
+
+func isPrint(r rune) bool {
+       // Same algorithm, either on uint16 or uint32 value.
+       // First, find first i such that rang[i] >= x.
+       // This is the index of either the start or end of a pair that might span x.
+       // The start is even (rang[i&^1]) and the end is odd (rang[i|1]).
+       // If we find x in a range, make sure x is not in exception list.
+
+       if 0 <= r && r < 1<<16 {
+               rr, rang, except := uint16(r), range16, except16
+               i := bsearch16(rang, rr)
+               if i >= len(rang) || rr < rang[i&^1] || rang[i|1] < rr {
+                       return false
+               }
+               j := bsearch16(except, rr)
+               return j >= len(except) || except[j] != rr
+       }
+
+       rr, rang, except := uint32(r), range32, except32
+       i := bsearch32(rang, rr)
+       if i >= len(rang) || rr < rang[i&^1] || rang[i|1] < rr {
+               return false
+       }
+       j := bsearch32(except, rr)
+       return j >= len(except) || except[j] != rr
+}
+
+func scan(min, max rune) (rang, except []uint32) {
+       lo := rune(-1)
+       for i := min; ; i++ {
+               if (i > max || !unicode.IsPrint(i)) && lo >= 0 {
+                       // End range, but avoid flip flop.
+                       if i+1 <= max && unicode.IsPrint(i+1) {
+                               except = append(except, uint32(i))
+                               continue
+                       }
+                       rang = append(rang, uint32(lo), uint32(i-1))
+                       lo = -1
+               }
+               if i > max {
+                       break
+               }
+               if lo < 0 && unicode.IsPrint(i) {
+                       lo = i
+               }
+       }
+       return
+}
+
+func to16(x []uint32) []uint16 {
+       var y []uint16
+       for _, v := range x {
+               if uint32(uint16(v)) != v {
+                       panic("bad 32->16 conversion")
+               }
+               y = append(y, uint16(v))
+       }
+       return y
+}
+
+func main() {
+       rang, except := scan(0, 0xFFFF)
+       range16 = to16(rang)
+       except16 = to16(except)
+       range32, except32 = scan(0x10000, unicode.MaxRune)
+
+       for i := rune(0); i <= unicode.MaxRune; i++ {
+               if isPrint(i) != unicode.IsPrint(i) {
+                       fmt.Fprintf(os.Stderr, "%U: isPrint=%v, want %v\n", i, isPrint(i), unicode.IsPrint(i))
+                       return
+               }
+       }
+
+       fmt.Printf("// DO NOT EDIT.  GENERATED BY\n")
+       fmt.Printf("//     go run makeisprint.go >x && mv x isprint.go\n\n")
+       fmt.Printf("package strconv\n\n")
+
+       fmt.Printf("// (%d+%d+%d)*2 + (%d)*4 = %d bytes\n\n",
+               len(range16), len(except16), len(except32),
+               len(range32),
+               (len(range16)+len(except16)+len(except32))*2+
+                       (len(range32))*4)
+
+       fmt.Printf("var isPrint16 = []uint16{\n")
+       for i := 0; i < len(range16); i += 2 {
+               fmt.Printf("\t%#04x, %#04x,\n", range16[i], range16[i+1])
+       }
+       fmt.Printf("}\n\n")
+
+       fmt.Printf("var isNotPrint16 = []uint16{\n")
+       for _, r := range except16 {
+               fmt.Printf("\t%#04x,\n", r)
+       }
+       fmt.Printf("}\n\n")
+
+       fmt.Printf("var isPrint32 = []uint32{\n")
+       for i := 0; i < len(range32); i += 2 {
+               fmt.Printf("\t%#06x, %#06x,\n", range32[i], range32[i+1])
+       }
+       fmt.Printf("}\n\n")
+
+       fmt.Printf("var isNotPrint32 = []uint16{ // add 0x10000 to each entry\n")
+       for _, r := range except32 {
+               if r >= 0x20000 {
+                       fmt.Fprintf(os.Stderr, "%U too big for isNotPrint32\n", r)
+                       return
+               }
+               fmt.Printf("\t%#04x,\n", r-0x10000)
+       }
+       fmt.Printf("}\n")
+}
index 61dbcae..8a73f9d 100644 (file)
@@ -5,17 +5,15 @@
 package strconv
 
 import (
-       "bytes"
-       "strings"
-       "unicode"
        "unicode/utf8"
 )
 
 const lowerhex = "0123456789abcdef"
 
 func quoteWith(s string, quote byte, ASCIIonly bool) string {
-       var buf bytes.Buffer
-       buf.WriteByte(quote)
+       var runeTmp [utf8.UTFMax]byte
+       buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations.
+       buf = append(buf, quote)
        for width := 0; len(s) > 0; s = s[width:] {
                r := rune(s[0])
                width = 1
@@ -23,71 +21,72 @@ func quoteWith(s string, quote byte, ASCIIonly bool) string {
                        r, width = utf8.DecodeRuneInString(s)
                }
                if width == 1 && r == utf8.RuneError {
-                       buf.WriteString(`\x`)
-                       buf.WriteByte(lowerhex[s[0]>>4])
-                       buf.WriteByte(lowerhex[s[0]&0xF])
+                       buf = append(buf, `\x`...)
+                       buf = append(buf, lowerhex[s[0]>>4])
+                       buf = append(buf, lowerhex[s[0]&0xF])
                        continue
                }
                if r == rune(quote) || r == '\\' { // always backslashed
-                       buf.WriteByte('\\')
-                       buf.WriteByte(byte(r))
+                       buf = append(buf, '\\')
+                       buf = append(buf, byte(r))
                        continue
                }
                if ASCIIonly {
-                       if r <= unicode.MaxASCII && unicode.IsPrint(r) {
-                               buf.WriteRune(r)
+                       if r < utf8.RuneSelf && IsPrint(r) {
+                               buf = append(buf, byte(r))
                                continue
                        }
-               } else if unicode.IsPrint(r) {
-                       buf.WriteRune(r)
+               } else if IsPrint(r) {
+                       n := utf8.EncodeRune(runeTmp[:], r)
+                       buf = append(buf, runeTmp[:n]...)
                        continue
                }
                switch r {
                case '\a':
-                       buf.WriteString(`\a`)
+                       buf = append(buf, `\a`...)
                case '\b':
-                       buf.WriteString(`\b`)
+                       buf = append(buf, `\b`...)
                case '\f':
-                       buf.WriteString(`\f`)
+                       buf = append(buf, `\f`...)
                case '\n':
-                       buf.WriteString(`\n`)
+                       buf = append(buf, `\n`...)
                case '\r':
-                       buf.WriteString(`\r`)
+                       buf = append(buf, `\r`...)
                case '\t':
-                       buf.WriteString(`\t`)
+                       buf = append(buf, `\t`...)
                case '\v':
-                       buf.WriteString(`\v`)
+                       buf = append(buf, `\v`...)
                default:
                        switch {
                        case r < ' ':
-                               buf.WriteString(`\x`)
-                               buf.WriteByte(lowerhex[s[0]>>4])
-                               buf.WriteByte(lowerhex[s[0]&0xF])
-                       case r > unicode.MaxRune:
+                               buf = append(buf, `\x`...)
+                               buf = append(buf, lowerhex[s[0]>>4])
+                               buf = append(buf, lowerhex[s[0]&0xF])
+                       case r > utf8.MaxRune:
                                r = 0xFFFD
                                fallthrough
                        case r < 0x10000:
-                               buf.WriteString(`\u`)
+                               buf = append(buf, `\u`...)
                                for s := 12; s >= 0; s -= 4 {
-                                       buf.WriteByte(lowerhex[r>>uint(s)&0xF])
+                                       buf = append(buf, lowerhex[r>>uint(s)&0xF])
                                }
                        default:
-                               buf.WriteString(`\U`)
+                               buf = append(buf, `\U`...)
                                for s := 28; s >= 0; s -= 4 {
-                                       buf.WriteByte(lowerhex[r>>uint(s)&0xF])
+                                       buf = append(buf, lowerhex[r>>uint(s)&0xF])
                                }
                        }
                }
        }
-       buf.WriteByte(quote)
-       return buf.String()
+       buf = append(buf, quote)
+       return string(buf)
 
 }
 
 // Quote returns a double-quoted Go string literal representing s.  The
 // returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for
 // control characters and non-printable characters as defined by
-// unicode.IsPrint.
+// IsPrint.
 func Quote(s string) string {
        return quoteWith(s, '"', false)
 }
@@ -100,8 +99,7 @@ func AppendQuote(dst []byte, s string) []byte {
 
 // QuoteToASCII returns a double-quoted Go string literal representing s.
 // The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for
-// non-ASCII characters and non-printable characters as defined by
-// unicode.IsPrint.
+// non-ASCII characters and non-printable characters as defined by IsPrint.
 func QuoteToASCII(s string) string {
        return quoteWith(s, '"', true)
 }
@@ -114,8 +112,7 @@ func AppendQuoteToASCII(dst []byte, s string) []byte {
 
 // QuoteRune returns a single-quoted Go character literal representing the
 // rune.  The returned string uses Go escape sequences (\t, \n, \xFF, \u0100)
-// for control characters and non-printable characters as defined by
-// unicode.IsPrint.
+// for control characters and non-printable characters as defined by IsPrint.
 func QuoteRune(r rune) string {
        // TODO: avoid the allocation here.
        return quoteWith(string(r), '\'', false)
@@ -130,7 +127,7 @@ func AppendQuoteRune(dst []byte, r rune) []byte {
 // QuoteRuneToASCII returns a single-quoted Go character literal representing
 // the rune.  The returned string uses Go escape sequences (\t, \n, \xFF,
 // \u0100) for non-ASCII characters and non-printable characters as defined
-// by unicode.IsPrint.
+// by IsPrint.
 func QuoteRuneToASCII(r rune) string {
        // TODO: avoid the allocation here.
        return quoteWith(string(r), '\'', true)
@@ -245,7 +242,7 @@ func UnquoteChar(s string, quote byte) (value rune, multibyte bool, tail string,
                        value = v
                        break
                }
-               if v > unicode.MaxRune {
+               if v > utf8.MaxRune {
                        err = ErrSyntax
                        return
                }
@@ -304,7 +301,7 @@ func Unquote(s string) (t string, err error) {
        s = s[1 : n-1]
 
        if quote == '`' {
-               if strings.Contains(s, "`") {
+               if contains(s, '`') {
                        return "", ErrSyntax
                }
                return s, nil
@@ -312,12 +309,12 @@ func Unquote(s string) (t string, err error) {
        if quote != '"' && quote != '\'' {
                return "", ErrSyntax
        }
-       if strings.Index(s, "\n") >= 0 {
+       if contains(s, '\n') {
                return "", ErrSyntax
        }
 
        // Is it trivial?  Avoid allocation.
-       if strings.Index(s, `\`) < 0 && strings.IndexRune(s, rune(quote)) < 0 {
+       if !contains(s, '\\') && !contains(s, quote) {
                switch quote {
                case '"':
                        return s, nil
@@ -329,7 +326,8 @@ func Unquote(s string) (t string, err error) {
                }
        }
 
-       var buf bytes.Buffer
+       var runeTmp [utf8.UTFMax]byte
+       buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations.
        for len(s) > 0 {
                c, multibyte, ss, err := UnquoteChar(s, quote)
                if err != nil {
@@ -337,14 +335,107 @@ func Unquote(s string) (t string, err error) {
                }
                s = ss
                if c < utf8.RuneSelf || !multibyte {
-                       buf.WriteByte(byte(c))
+                       buf = append(buf, byte(c))
                } else {
-                       buf.WriteString(string(c))
+                       n := utf8.EncodeRune(runeTmp[:], c)
+                       buf = append(buf, runeTmp[:n]...)
                }
                if quote == '\'' && len(s) != 0 {
                        // single-quoted must be single character
                        return "", ErrSyntax
                }
        }
-       return buf.String(), nil
+       return string(buf), nil
+}
+
+// contains reports whether the string contains the byte c.
+func contains(s string, c byte) bool {
+       for i := 0; i < len(s); i++ {
+               if s[i] == c {
+                       return true
+               }
+       }
+       return false
+}
+
+// bsearch16 returns the smallest i such that a[i] >= x.
+// If there is no such i, bsearch16 returns len(a).
+func bsearch16(a []uint16, x uint16) int {
+       i, j := 0, len(a)
+       for i < j {
+               h := i + (j-i)/2
+               if a[h] < x {
+                       i = h + 1
+               } else {
+                       j = h
+               }
+       }
+       return i
+}
+
+// bsearch32 returns the smallest i such that a[i] >= x.
+// If there is no such i, bsearch32 returns len(a).
+func bsearch32(a []uint32, x uint32) int {
+       i, j := 0, len(a)
+       for i < j {
+               h := i + (j-i)/2
+               if a[h] < x {
+                       i = h + 1
+               } else {
+                       j = h
+               }
+       }
+       return i
+}
+
+// TODO: IsPrint is a local implementation of unicode.IsPrint, verified by the tests
+// to give the same answer. It allows this package not to depend on unicode,
+// and therefore not pull in all the Unicode tables. If the linker were better
+// at tossing unused tables, we could get rid of this implementation.
+// That would be nice.
+
+// IsPrint reports whether the rune is defined as printable by Go, with
+// the same definition as unicode.IsPrint: letters, numbers, punctuation,
+// symbols and ASCII space.
+func IsPrint(r rune) bool {
+       // Fast check for Latin-1
+       if r <= 0xFF {
+               if 0x20 <= r && r <= 0x7E {
+                       // All the ASCII is printable from space through DEL-1.
+                       return true
+               }
+               if 0xA1 <= r && r <= 0xFF {
+                       // Similarly for Â¡ through Ã¿...
+                       return r != 0xAD // ...except for the bizarre soft hyphen.
+               }
+               return false
+       }
+
+       // Same algorithm, either on uint16 or uint32 value.
+       // First, find first i such that isPrint[i] >= x.
+       // This is the index of either the start or end of a pair that might span x.
+       // The start is even (isPrint[i&^1]) and the end is odd (isPrint[i|1]).
+       // If we find x in a range, make sure x is not in isNotPrint list.
+
+       if 0 <= r && r < 1<<16 {
+               rr, isPrint, isNotPrint := uint16(r), isPrint16, isNotPrint16
+               i := bsearch16(isPrint, rr)
+               if i >= len(isPrint) || rr < isPrint[i&^1] || isPrint[i|1] < rr {
+                       return false
+               }
+               j := bsearch16(isNotPrint, rr)
+               return j >= len(isNotPrint) || isNotPrint[j] != rr
+       }
+
+       rr, isPrint, isNotPrint := uint32(r), isPrint32, isNotPrint32
+       i := bsearch32(isPrint, rr)
+       if i >= len(isPrint) || rr < isPrint[i&^1] || isPrint[i|1] < rr {
+               return false
+       }
+       if r >= 0x20000 {
+               return true
+       }
+       r -= 0x10000
+       j := bsearch16(isNotPrint, uint16(r))
+       return j >= len(isNotPrint) || isNotPrint[j] != uint16(r)
 }
index 3f544c4..61d9bf9 100644 (file)
@@ -7,8 +7,23 @@ package strconv_test
 import (
        . "strconv"
        "testing"
+       "unicode"
 )
 
+// Verify that our isPrint agrees with unicode.IsPrint
+func TestIsPrint(t *testing.T) {
+       n := 0
+       for r := rune(0); r <= unicode.MaxRune; r++ {
+               if IsPrint(r) != unicode.IsPrint(r) {
+                       t.Errorf("IsPrint(%U)=%t incorrect", r, IsPrint(r))
+                       n++
+                       if n > 10 {
+                               return
+                       }
+               }
+       }
+}
+
 type quoteTest struct {
        in    string
        out   string
index daeb85e..1141710 100644 (file)
@@ -60,7 +60,7 @@ func ExampleIndex() {
        // -1
 }
 
-func ExampleRune() {
+func ExampleIndexRune() {
        fmt.Println(strings.IndexRune("chicken", 'k'))
        fmt.Println(strings.IndexRune("chicken", 'd'))
        // Output:
index a06c85c..f60d997 100644 (file)
@@ -1012,6 +1012,10 @@ func TestHammerStoreLoad(t *testing.T) {
 }
 
 func TestStoreLoadSeqCst32(t *testing.T) {
+       if runtime.NumCPU() == 1 {
+               t.Logf("Skipping test on %v processor machine", runtime.NumCPU())
+               return
+       }
        defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
        N := int32(1e3)
        if testing.Short() {
@@ -1049,6 +1053,10 @@ func TestStoreLoadSeqCst32(t *testing.T) {
 }
 
 func TestStoreLoadSeqCst64(t *testing.T) {
+       if runtime.NumCPU() == 1 {
+               t.Logf("Skipping test on %v processor machine", runtime.NumCPU())
+               return
+       }
        if test64err != nil {
                t.Logf("Skipping 64-bit tests: %v", test64err)
                return
@@ -1090,6 +1098,10 @@ func TestStoreLoadSeqCst64(t *testing.T) {
 }
 
 func TestStoreLoadRelAcq32(t *testing.T) {
+       if runtime.NumCPU() == 1 {
+               t.Logf("Skipping test on %v processor machine", runtime.NumCPU())
+               return
+       }
        defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
        N := int32(1e3)
        if testing.Short() {
@@ -1132,6 +1144,10 @@ func TestStoreLoadRelAcq32(t *testing.T) {
 }
 
 func TestStoreLoadRelAcq64(t *testing.T) {
+       if runtime.NumCPU() == 1 {
+               t.Logf("Skipping test on %v processor machine", runtime.NumCPU())
+               return
+       }
        if test64err != nil {
                t.Logf("Skipping 64-bit tests: %v", test64err)
                return
index 7072262..1cb8a07 100644 (file)
@@ -108,6 +108,8 @@ var (
        cpuListStr     = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test")
        parallel       = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "maximum test parallelism")
 
+       haveExamples bool // are there examples?
+
        cpuList []int
 )
 
@@ -280,6 +282,7 @@ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest,
 
        before()
        startAlarm()
+       haveExamples = len(examples) > 0
        testOk := RunTests(matchString, tests)
        exampleOk := RunExamples(matchString, examples)
        if !testOk || !exampleOk {
@@ -304,7 +307,7 @@ func (t *T) report() {
 
 func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool) {
        ok = true
-       if len(tests) == 0 {
+       if len(tests) == 0 && !haveExamples {
                fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
                return
        }
index 914f02c..d8a086c 100644 (file)
@@ -22,7 +22,7 @@ func TestTicker(t *testing.T) {
        dt := t1.Sub(t0)
        target := Delta * Count
        slop := target * 2 / 10
-       if dt < target-slop || dt > target+slop {
+       if dt < target-slop || (!testing.Short() && dt > target+slop) {
                t.Fatalf("%d %s ticks took %s, expected [%s,%s]", Count, Delta, dt, target-slop, target+slop)
        }
        // Now test that the ticker stopped
index f7ded24..473bc2a 100644 (file)
@@ -13,7 +13,8 @@ import "errors"
 //
 // Programs using times should typically store and pass them as values,
 // not pointers.  That is, time variables and struct fields should be of
-// type time.Time, not *time.Time.
+// type time.Time, not *time.Time.  A Time value can be used by
+// multiple goroutines simultaneously.
 //
 // Time instants can be compared using the Before, After, and Equal methods.
 // The Sub method subtracts two instants, producing a Duration.
@@ -755,13 +756,13 @@ func (t Time) Zone() (name string, offset int) {
        return
 }
 
-// Unix returns the Unix time, the number of seconds elapsed
+// Unix returns t as a Unix time, the number of seconds elapsed
 // since January 1, 1970 UTC.
 func (t Time) Unix() int64 {
        return t.sec + internalToUnix
 }
 
-// UnixNano returns the Unix time, the number of nanoseconds elapsed
+// UnixNano returns t as a Unix time, the number of nanoseconds elapsed
 // since January 1, 1970 UTC.
 func (t Time) UnixNano() int64 {
        return (t.sec+internalToUnix)*1e9 + int64(t.nsec)
diff --git a/libgo/go/unicode/utf16/export_test.go b/libgo/go/unicode/utf16/export_test.go
new file mode 100644 (file)
index 0000000..306247e
--- /dev/null
@@ -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.
+
+package utf16
+
+// Extra names for constants so we can validate them during testing.
+const (
+       MaxRune         = maxRune
+       ReplacementChar = replacementChar
+)
index 2b2eb28..903e401 100644 (file)
@@ -5,7 +5,14 @@
 // Package utf16 implements encoding and decoding of UTF-16 sequences.
 package utf16
 
-import "unicode"
+// The conditions replacementChar==unicode.ReplacementChar and
+// maxRune==unicode.MaxRune are verified in the tests.
+// Defining them locally avoids this package depending on package unicode.
+
+const (
+       replacementChar = '\uFFFD'     // Unicode replacement character
+       maxRune         = '\U0010FFFF' // Maximum valid Unicode code point.
+)
 
 const (
        // 0xd800-0xdc00 encodes the high 10 bits of a pair.
@@ -31,15 +38,15 @@ func DecodeRune(r1, r2 rune) rune {
        if surr1 <= r1 && r1 < surr2 && surr2 <= r2 && r2 < surr3 {
                return (rune(r1)-surr1)<<10 | (rune(r2) - surr2) + 0x10000
        }
-       return unicode.ReplacementChar
+       return replacementChar
 }
 
 // EncodeRune returns the UTF-16 surrogate pair r1, r2 for the given rune.
 // If the rune is not a valid Unicode code point or does not need encoding,
 // EncodeRune returns U+FFFD, U+FFFD.
 func EncodeRune(r rune) (r1, r2 rune) {
-       if r < surrSelf || r > unicode.MaxRune || IsSurrogate(r) {
-               return unicode.ReplacementChar, unicode.ReplacementChar
+       if r < surrSelf || r > maxRune || IsSurrogate(r) {
+               return replacementChar, replacementChar
        }
        r -= surrSelf
        return surr1 + (r>>10)&0x3ff, surr2 + r&0x3ff
@@ -58,8 +65,8 @@ func Encode(s []rune) []uint16 {
        n = 0
        for _, v := range s {
                switch {
-               case v < 0, surr1 <= v && v < surr3, v > unicode.MaxRune:
-                       v = unicode.ReplacementChar
+               case v < 0, surr1 <= v && v < surr3, v > maxRune:
+                       v = replacementChar
                        fallthrough
                case v < surrSelf:
                        a[n] = uint16(v)
@@ -89,7 +96,7 @@ func Decode(s []uint16) []rune {
                        n++
                case surr1 <= r && r < surr3:
                        // invalid surrogate sequence
-                       a[n] = unicode.ReplacementChar
+                       a[n] = replacementChar
                        n++
                default:
                        // normal rune
index d453b2f..ee16a30 100644 (file)
@@ -11,6 +11,16 @@ import (
        . "unicode/utf16"
 )
 
+// Validate the constants redefined from unicode.
+func TestConstants(t *testing.T) {
+       if MaxRune != unicode.MaxRune {
+               t.Errorf("utf16.maxRune is wrong: %x should be %x", MaxRune, unicode.MaxRune)
+       }
+       if ReplacementChar != unicode.ReplacementChar {
+               t.Errorf("utf16.replacementChar is wrong: %x should be %x", ReplacementChar, unicode.ReplacementChar)
+       }
+}
+
 type encodeTest struct {
        in  []rune
        out []uint16
index 631533a..57ea19e 100644 (file)
@@ -6,13 +6,16 @@
 // UTF-8. It includes functions to translate between runes and UTF-8 byte sequences.
 package utf8
 
-import "unicode" // only needed for a couple of constants
+// The conditions RuneError==unicode.ReplacementChar and
+// MaxRune==unicode.MaxRune are verified in the tests.
+// Defining them locally avoids this package depending on package unicode.
 
 // Numbers fundamental to the encoding.
 const (
-       RuneError = unicode.ReplacementChar // the "error" Rune or "replacement character".
-       RuneSelf  = 0x80                    // characters below Runeself are represented as themselves in a single byte.
-       UTFMax    = 4                       // maximum number of bytes of a UTF-8 encoded Unicode character.
+       RuneError = '\uFFFD'     // the "error" Rune or "Unicode replacement character"
+       RuneSelf  = 0x80         // characters below Runeself are represented as themselves in a single byte.
+       MaxRune   = '\U0010FFFF' // Maximum valid Unicode code point.
+       UTFMax    = 4            // maximum number of bytes of a UTF-8 encoded Unicode character.
 )
 
 const (
@@ -309,7 +312,7 @@ func EncodeRune(p []byte, r rune) int {
                return 2
        }
 
-       if uint32(r) > unicode.MaxRune {
+       if uint32(r) > MaxRune {
                r = RuneError
        }
 
index 6351426..4f73c8f 100644 (file)
@@ -7,9 +7,30 @@ package utf8_test
 import (
        "bytes"
        "testing"
+       "unicode"
        . "unicode/utf8"
 )
 
+// Validate the constants redefined from unicode.
+func init() {
+       if MaxRune != unicode.MaxRune {
+               panic("utf8.MaxRune is wrong")
+       }
+       if RuneError != unicode.ReplacementChar {
+               panic("utf8.RuneError is wrong")
+       }
+}
+
+// Validate the constants redefined from unicode.
+func TestConstants(t *testing.T) {
+       if MaxRune != unicode.MaxRune {
+               t.Errorf("utf8.MaxRune is wrong: %x should be %x", MaxRune, unicode.MaxRune)
+       }
+       if RuneError != unicode.ReplacementChar {
+               t.Errorf("utf8.RuneError is wrong: %x should be %x", RuneError, unicode.ReplacementChar)
+       }
+}
+
 type Utf8Map struct {
        r   rune
        str string
index 3fde250..97cfabe 100644 (file)
@@ -390,6 +390,23 @@ runtime_MHeap_SysAlloc(MHeap *h, uintptr n)
 {
        byte *p;
 
+
+       if(n > (uintptr)(h->arena_end - h->arena_used)) {
+               // We are in 32-bit mode, maybe we didn't use all possible address space yet.
+               // Reserve some more space.
+               byte *new_end;
+               uintptr needed;
+
+               needed = (uintptr)h->arena_used + n - (uintptr)h->arena_end;
+               // Round wanted arena size to a multiple of 256MB.
+               needed = (needed + (256<<20) - 1) & ~((256<<20)-1);
+               new_end = h->arena_end + needed;
+               if(new_end <= h->arena_start + MaxArena32) {
+                       p = runtime_SysReserve(h->arena_end, new_end - h->arena_end);
+                       if(p == h->arena_end)
+                               h->arena_end = new_end;
+               }
+       }
        if(n <= (uintptr)(h->arena_end - h->arena_used)) {
                // Keep taking from our reservation.
                p = h->arena_used;
@@ -411,7 +428,8 @@ runtime_MHeap_SysAlloc(MHeap *h, uintptr n)
                return nil;
 
        if(p < h->arena_start || (uintptr)(p+n - h->arena_start) >= MaxArena32) {
-               runtime_printf("runtime: memory allocated by OS not in usable range\n");
+               runtime_printf("runtime: memory allocated by OS (%p) not in usable range [%p,%p)\n",
+                       p, h->arena_start, h->arena_start+MaxArena32);
                runtime_SysFree(p, n);
                return nil;
        }
index 31e8287..049f77e 100644 (file)
@@ -406,7 +406,9 @@ runtime_schedinit(void)
                        n = maxgomaxprocs;
                runtime_gomaxprocs = n;
        }
-       setmcpumax(runtime_gomaxprocs);
+       // wait for the main goroutine to start before taking
+       // GOMAXPROCS into account.
+       setmcpumax(1);
        runtime_singleproc = runtime_gomaxprocs == 1;
 
        canaddmcpu();   // mcpu++ to account for bootstrap m
@@ -432,6 +434,8 @@ runtime_main(void)
        // by calling runtime.LockOSThread during initialization
        // to preserve the lock.
        runtime_LockOSThread();
+       // From now on, newgoroutines may use non-main threads.
+       setmcpumax(runtime_gomaxprocs);
        runtime_sched.init = true;
        scvg = __go_go(runtime_MHeap_Scavenger, nil);
        main_init();
index 2deb46e..a81c210 100644 (file)
@@ -416,7 +416,6 @@ void        runtime_usleep(uint32);
 /*
  * runtime c-called (but written in Go)
  */
-void   runtime_newError(String, Eface*);
 void   runtime_printany(Eface)
      __asm__("libgo_runtime.runtime.Printany");
 void   runtime_newTypeAssertionError(const String*, const String*, const String*, const String*, Eface*)
@@ -429,7 +428,6 @@ void        runtime_newErrorString(String, Eface*)
  */
 void   runtime_semacquire(uint32 volatile *);
 void   runtime_semrelease(uint32 volatile *);
-String runtime_signame(int32 sig);
 int32  runtime_gomaxprocsfunc(int32 n);
 void   runtime_procyield(uint32);
 void   runtime_osyield(void);