OSDN Git Service

Update to current Go library.
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 30 Mar 2011 15:33:16 +0000 (15:33 +0000)
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 30 Mar 2011 15:33:16 +0000 (15:33 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@171732 138bc75d-0d04-0410-961f-82ee72b054a4

114 files changed:
gcc/testsuite/go.test/test/fixedbugs/bug243.go
libgo/MERGE
libgo/Makefile.am
libgo/Makefile.in
libgo/go/archive/tar/writer_test.go
libgo/go/asn1/asn1.go
libgo/go/asn1/asn1_test.go
libgo/go/asn1/common.go
libgo/go/big/int_test.go
libgo/go/bytes/buffer_test.go
libgo/go/bytes/bytes.go
libgo/go/bytes/bytes_test.go
libgo/go/container/vector/numbers_test.go
libgo/go/crypto/cipher/ctr.go
libgo/go/crypto/ecdsa/ecdsa_test.go
libgo/go/crypto/elliptic/elliptic_test.go
libgo/go/crypto/openpgp/s2k/s2k_test.go
libgo/go/crypto/rand/rand_test.go
libgo/go/crypto/rsa/pkcs1v15_test.go
libgo/go/crypto/rsa/rsa_test.go
libgo/go/crypto/tls/common.go
libgo/go/crypto/tls/conn.go
libgo/go/crypto/tls/handshake_client.go
libgo/go/crypto/tls/handshake_client_test.go
libgo/go/crypto/tls/handshake_messages_test.go
libgo/go/crypto/tls/tls.go
libgo/go/crypto/x509/x509.go
libgo/go/debug/gosym/pclntab_test.go
libgo/go/ebnf/ebnf_test.go
libgo/go/ebnf/parser.go
libgo/go/exp/datafmt/parser.go
libgo/go/exp/draw/x11/conn.go
libgo/go/exp/eval/eval_test.go
libgo/go/exp/ogle/cmd.go
libgo/go/flag/flag.go
libgo/go/fmt/fmt_test.go
libgo/go/go/ast/ast.go
libgo/go/go/ast/filter.go
libgo/go/go/ast/resolve.go [new file with mode: 0644]
libgo/go/go/ast/scope.go
libgo/go/go/doc/comment.go
libgo/go/go/doc/doc.go
libgo/go/go/parser/interface.go
libgo/go/go/parser/parser.go
libgo/go/go/printer/nodes.go
libgo/go/go/printer/printer.go
libgo/go/go/printer/printer_test.go
libgo/go/go/printer/testdata/expressions.golden
libgo/go/go/printer/testdata/expressions.input
libgo/go/go/printer/testdata/expressions.raw
libgo/go/go/scanner/scanner.go
libgo/go/go/scanner/scanner_test.go
libgo/go/go/token/token.go
libgo/go/go/typechecker/scope.go
libgo/go/go/typechecker/typechecker.go
libgo/go/go/typechecker/typechecker_test.go
libgo/go/go/typechecker/universe.go
libgo/go/gob/type.go
libgo/go/http/fs.go
libgo/go/http/pprof/pprof.go
libgo/go/http/serve_test.go
libgo/go/http/transport.go
libgo/go/image/png/reader_test.go
libgo/go/image/png/writer_test.go
libgo/go/json/decode_test.go
libgo/go/json/scanner_test.go
libgo/go/net/cgo_stub.go [new file with mode: 0644]
libgo/go/net/dial.go
libgo/go/net/dialgoogle_test.go
libgo/go/net/dnsclient.go
libgo/go/net/dnsmsg.go
libgo/go/net/fd.go
libgo/go/net/fd_windows.go
libgo/go/net/file.go [new file with mode: 0644]
libgo/go/net/file_test.go [new file with mode: 0644]
libgo/go/net/file_windows.go [new file with mode: 0644]
libgo/go/net/hosts_test.go
libgo/go/net/ip.go
libgo/go/net/ip_test.go
libgo/go/net/iprawsock.go
libgo/go/net/ipsock.go
libgo/go/net/lookup.go [new file with mode: 0644]
libgo/go/net/net_test.go
libgo/go/net/port.go
libgo/go/net/server_test.go
libgo/go/net/sock.go
libgo/go/net/tcpsock.go
libgo/go/net/textproto/textproto.go
libgo/go/net/timeout_test.go
libgo/go/net/udpsock.go
libgo/go/netchan/import.go
libgo/go/netchan/netchan_test.go
libgo/go/os/file_unix.go
libgo/go/rpc/client.go
libgo/go/rpc/jsonrpc/client.go
libgo/go/runtime/pprof/pprof_test.go
libgo/go/smtp/smtp.go
libgo/go/sort/sort_test.go
libgo/go/strings/strings.go
libgo/go/strings/strings_test.go
libgo/go/sync/atomic/atomic_test.go
libgo/go/sync/atomic/doc.go
libgo/go/sync/rwmutex_test.go
libgo/go/syslog/syslog.go
libgo/go/syslog/syslog_unix.go
libgo/go/testing/testing.go
libgo/go/time/sleep_test.go
libgo/go/utf8/string_test.go
libgo/go/websocket/client.go
libgo/go/websocket/websocket_test.go
libgo/syscalls/socket.go
libgo/syscalls/sysfile_posix.go
libgo/testsuite/gotest
libgo/testsuite/libgo.testmain/testmain.exp

index 236c144..0c53196 100644 (file)
@@ -6,12 +6,14 @@
 
 package main
 
-import (
-       "net"
-)
+import "os"
+
+// Issue 481: closures and var declarations
+// with multiple variables assigned from one
+// function call.
 
 func main() {
-       var listen, _ = net.Listen("tcp", "127.0.0.1:0")
+       var listen, _ = Listen("tcp", "127.0.0.1:0")
 
        go func() {
                for {
@@ -20,6 +22,31 @@ func main() {
                }
        }()
 
-       var conn, _ = net.Dial("tcp", "", listen.Addr().String())
+       var conn, _ = Dial("tcp", "", listen.Addr().String())
        _ = conn
 }
+
+// Simulated net interface to exercise bug
+// without involving a real network.
+type T chan int
+
+var global T
+
+func Listen(x, y string) (T, string) {
+       global = make(chan int)
+       return global, y
+}
+
+func (t T) Addr() os.Error {
+       return os.ErrorString("stringer")
+}
+
+func (t T) Accept() (int, string) {
+       return <-t, ""
+}
+
+func Dial(x, y, z string) (int, string) {
+       global <- 1
+       return 0, ""
+}
+
index 729be06..b78ba69 100644 (file)
@@ -1,4 +1,4 @@
-31d7feb9281b
+342e3b11f21a
 
 The first line of this file holds the Mercurial revision number of the
 last merge done from the master library sources.
index 6eb752c..0545d7d 100644 (file)
@@ -657,6 +657,7 @@ endif # !LIBGO_IS_LINUX
 endif # !LIBGO_IS_RTEMS
 
 go_net_files = \
+       go/net/cgo_stub.go \
        go/net/dial.go \
        go/net/dnsclient.go \
        go/net/dnsconfig.go \
@@ -664,10 +665,12 @@ go_net_files = \
        $(go_net_newpollserver_file) \
        go/net/fd.go \
        $(go_net_fd_os_file) \
+       go/net/file.go \
        go/net/hosts.go \
        go/net/ip.go \
        go/net/iprawsock.go \
        go/net/ipsock.go \
+       go/net/lookup.go \
        go/net/net.go \
        go/net/parse.go \
        go/net/pipe.go \
@@ -1095,6 +1098,7 @@ go_go_ast_files = \
        go/go/ast/ast.go \
        go/go/ast/filter.go \
        go/go/ast/print.go \
+       go/go/ast/resolve.go \
        go/go/ast/scope.go \
        go/go/ast/walk.go
 go_go_doc_files = \
@@ -2327,8 +2331,8 @@ exp/eval/check: $(CHECK_DEPS)
        $(CHECK)
 .PHONY: exp/eval/check
 
-go/ast.lo: $(go_go_ast_files) bytes.gox fmt.gox go/token.gox io.gox os.gox \
-               reflect.gox unicode.gox utf8.gox
+go/ast.lo: $(go_go_ast_files) bytes.gox fmt.gox go/scanner.gox go/token.gox \
+               io.gox os.gox reflect.gox unicode.gox utf8.gox
        $(BUILDPACKAGE)
 go/ast/check: $(CHECK_DEPS)
        @$(MKDIR_P) go/ast
index 3af5ffc..b750066 100644 (file)
@@ -1039,6 +1039,7 @@ go_mime_files = \
 @LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go
 @LIBGO_IS_RTEMS_TRUE@go_net_newpollserver_file = go/net/newpollserver_rtems.go
 go_net_files = \
+       go/net/cgo_stub.go \
        go/net/dial.go \
        go/net/dnsclient.go \
        go/net/dnsconfig.go \
@@ -1046,10 +1047,12 @@ go_net_files = \
        $(go_net_newpollserver_file) \
        go/net/fd.go \
        $(go_net_fd_os_file) \
+       go/net/file.go \
        go/net/hosts.go \
        go/net/ip.go \
        go/net/iprawsock.go \
        go/net/ipsock.go \
+       go/net/lookup.go \
        go/net/net.go \
        go/net/parse.go \
        go/net/pipe.go \
@@ -1483,6 +1486,7 @@ go_go_ast_files = \
        go/go/ast/ast.go \
        go/go/ast/filter.go \
        go/go/ast/print.go \
+       go/go/ast/resolve.go \
        go/go/ast/scope.go \
        go/go/ast/walk.go
 
@@ -4747,8 +4751,8 @@ exp/eval/check: $(CHECK_DEPS)
        $(CHECK)
 .PHONY: exp/eval/check
 
-go/ast.lo: $(go_go_ast_files) bytes.gox fmt.gox go/token.gox io.gox os.gox \
-               reflect.gox unicode.gox utf8.gox
+go/ast.lo: $(go_go_ast_files) bytes.gox fmt.gox go/scanner.gox go/token.gox \
+               io.gox os.gox reflect.gox unicode.gox utf8.gox
        $(BUILDPACKAGE)
 go/ast/check: $(CHECK_DEPS)
        @$(MKDIR_P) go/ast
index 48b8911..838cb7e 100644 (file)
@@ -150,5 +150,8 @@ testLoop:
                        t.Errorf("test %d: Incorrect result: (-=expected, +=actual)\n%v",
                                i, bytediff(expected, actual))
                }
+               if testing.Short() { // The second test is expensive.
+                       break
+               }
        }
 }
index d06b1d4..c531451 100644 (file)
@@ -389,6 +389,11 @@ func parseSequenceOf(bytes []byte, sliceType *reflect.SliceType, elemType reflec
                if err != nil {
                        return
                }
+               // We pretend that GENERAL STRINGs are PRINTABLE STRINGs so
+               // that a sequence of them can be parsed into a []string.
+               if t.tag == tagGeneralString {
+                       t.tag = tagPrintableString
+               }
                if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag {
                        err = StructuralError{"sequence tag mismatch"}
                        return
@@ -516,7 +521,11 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                return
        }
        if params.explicit {
-               if t.class == classContextSpecific && t.tag == *params.tag && (t.length == 0 || t.isCompound) {
+               expectedClass := classContextSpecific
+               if params.application {
+                       expectedClass = classApplication
+               }
+               if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) {
                        if t.length > 0 {
                                t, offset, err = parseTagAndLength(bytes, offset)
                                if err != nil {
@@ -551,6 +560,10 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
        if universalTag == tagPrintableString && t.tag == tagIA5String {
                universalTag = tagIA5String
        }
+       // Likewise for GeneralString
+       if universalTag == tagPrintableString && t.tag == tagGeneralString {
+               universalTag = tagGeneralString
+       }
 
        // Special case for time: UTCTime and GeneralizedTime both map to the
        // Go type time.Time.
@@ -566,6 +579,11 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                expectedTag = *params.tag
        }
 
+       if !params.explicit && params.application && params.tag != nil {
+               expectedClass = classApplication
+               expectedTag = *params.tag
+       }
+
        // We have unwrapped any explicit tagging at this point.
        if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType {
                // Tags don't match. Again, it could be an optional element.
@@ -701,6 +719,12 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                        v, err = parseIA5String(innerBytes)
                case tagT61String:
                        v, err = parseT61String(innerBytes)
+               case tagGeneralString:
+                       // GeneralString is specified in ISO-2022/ECMA-35,
+                       // A brief review suggests that it includes structures
+                       // that allow the encoding to change midstring and
+                       // such. We give up and pass it as an 8-bit string.
+                       v, err = parseT61String(innerBytes)
                default:
                        err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)}
                }
@@ -776,8 +800,14 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
 // Other ASN.1 types are not supported; if it encounters them,
 // Unmarshal returns a parse error.
 func Unmarshal(b []byte, val interface{}) (rest []byte, err os.Error) {
+       return UnmarshalWithParams(b, val, "")
+}
+
+// UnmarshalWithParams allows field parameters to be specified for the
+// top-level element. The form of the params is the same as the field tags.
+func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err os.Error) {
        v := reflect.NewValue(val).(*reflect.PtrValue).Elem()
-       offset, err := parseField(v, b, 0, fieldParameters{})
+       offset, err := parseField(v, b, 0, parseFieldParameters(params))
        if err != nil {
                return nil, err
        }
index 34b5f1e..b776765 100644 (file)
@@ -249,11 +249,12 @@ var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParame
        {"printable", fieldParameters{stringType: tagPrintableString}},
        {"optional", fieldParameters{optional: true}},
        {"explicit", fieldParameters{explicit: true, tag: new(int)}},
+       {"application", fieldParameters{application: true, tag: new(int)}},
        {"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}},
        {"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, newInt64(42), newInt(17), 0, false}},
+       {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, false, newInt64(42), newInt(17), 0, false}},
        {"set", fieldParameters{set: true}},
 }
 
index 4a5eca1..f2254a4 100644 (file)
@@ -32,6 +32,7 @@ const (
        tagIA5String       = 22
        tagUTCTime         = 23
        tagGeneralizedTime = 24
+       tagGeneralString   = 27
 )
 
 const (
@@ -67,7 +68,8 @@ type tagAndLength struct {
 // fieldParameters is the parsed representation of tag string from a structure field.
 type fieldParameters struct {
        optional     bool   // true iff the field is OPTIONAL
-       explicit     bool   // true iff and EXPLICIT tag is in use.
+       explicit     bool   // true iff an EXPLICIT tag is in use.
+       application  bool   // true iff an APPLICATION tag is in use.
        defaultValue *int64 // a default value for INTEGER typed fields (maybe nil).
        tag          *int   // the EXPLICIT or IMPLICIT tag (maybe nil).
        stringType   int    // the string tag to use when marshaling.
@@ -89,7 +91,6 @@ func parseFieldParameters(str string) (ret fieldParameters) {
                        ret.explicit = true
                        if ret.tag == nil {
                                ret.tag = new(int)
-                               *ret.tag = 0
                        }
                case part == "ia5":
                        ret.stringType = tagIA5String
@@ -109,6 +110,11 @@ func parseFieldParameters(str string) (ret fieldParameters) {
                        }
                case part == "set":
                        ret.set = true
+               case part == "application":
+                       ret.application = true
+                       if ret.tag == nil {
+                               ret.tag = new(int)
+                       }
                }
        }
        return
index c0cc9ac..9c19dd5 100644 (file)
@@ -716,18 +716,25 @@ var composites = []string{
 
 
 func TestProbablyPrime(t *testing.T) {
+       nreps := 20
+       if testing.Short() {
+               nreps = 1
+       }
        for i, s := range primes {
                p, _ := new(Int).SetString(s, 10)
-               if !ProbablyPrime(p, 20) {
+               if !ProbablyPrime(p, nreps) {
                        t.Errorf("#%d prime found to be non-prime (%s)", i, s)
                }
        }
 
        for i, s := range composites {
                c, _ := new(Int).SetString(s, 10)
-               if ProbablyPrime(c, 20) {
+               if ProbablyPrime(c, nreps) {
                        t.Errorf("#%d composite found to be prime (%s)", i, s)
                }
+               if testing.Short() {
+                       break
+               }
        }
 }
 
index 56a2d92..14f9501 100644 (file)
@@ -178,7 +178,11 @@ func TestBasicOperations(t *testing.T) {
 
 func TestLargeStringWrites(t *testing.T) {
        var buf Buffer
-       for i := 3; i < 30; i += 3 {
+       limit := 30
+       if testing.Short() {
+               limit = 9
+       }
+       for i := 3; i < limit; i += 3 {
                s := fillString(t, "TestLargeWrites (1)", &buf, "", 5, data)
                empty(t, "TestLargeStringWrites (2)", &buf, s, make([]byte, len(data)/i))
        }
@@ -188,7 +192,11 @@ func TestLargeStringWrites(t *testing.T) {
 
 func TestLargeByteWrites(t *testing.T) {
        var buf Buffer
-       for i := 3; i < 30; i += 3 {
+       limit := 30
+       if testing.Short() {
+               limit = 9
+       }
+       for i := 3; i < limit; i += 3 {
                s := fillBytes(t, "TestLargeWrites (1)", &buf, "", 5, bytes)
                empty(t, "TestLargeByteWrites (2)", &buf, s, make([]byte, len(data)/i))
        }
index bfe2ef3..c12a135 100644 (file)
@@ -293,20 +293,10 @@ func Join(a [][]byte, sep []byte) []byte {
        }
 
        b := make([]byte, n)
-       bp := 0
-       for i := 0; i < len(a); i++ {
-               s := a[i]
-               for j := 0; j < len(s); j++ {
-                       b[bp] = s[j]
-                       bp++
-               }
-               if i+1 < len(a) {
-                       s = sep
-                       for j := 0; j < len(s); j++ {
-                               b[bp] = s[j]
-                               bp++
-                       }
-               }
+       bp := copy(b, a[0])
+       for _, s := range a[1:] {
+               bp += copy(b[bp:], sep)
+               bp += copy(b[bp:], s)
        }
        return b
 }
index 063686e..4ce291a 100644 (file)
@@ -201,7 +201,10 @@ func TestIndexByte(t *testing.T) {
 
 // test a larger buffer with different sizes and alignments
 func TestIndexByteBig(t *testing.T) {
-       const n = 1024
+       var n = 1024
+       if testing.Short() {
+               n = 128
+       }
        b := make([]byte, n)
        for i := 0; i < n; i++ {
                // different start alignments
index d540ace..b83b0bf 100644 (file)
@@ -33,6 +33,9 @@ func s(n uint64) string {
 
 
 func TestVectorNums(t *testing.T) {
+       if testing.Short() {
+               return
+       }
        var v Vector
        c := int(0)
        runtime.GC()
@@ -51,6 +54,9 @@ func TestVectorNums(t *testing.T) {
 
 
 func TestIntVectorNums(t *testing.T) {
+       if testing.Short() {
+               return
+       }
        var v IntVector
        c := int(0)
        runtime.GC()
@@ -69,6 +75,9 @@ func TestIntVectorNums(t *testing.T) {
 
 
 func TestStringVectorNums(t *testing.T) {
+       if testing.Short() {
+               return
+       }
        var v StringVector
        c := ""
        runtime.GC()
index 04436ec..147b74f 100644 (file)
@@ -22,6 +22,10 @@ type ctr struct {
 // NewCTR returns a Stream which encrypts/decrypts using the given Block in
 // counter mode. The length of iv must be the same as the Block's block size.
 func NewCTR(block Block, iv []byte) Stream {
+       if len(iv) != block.BlockSize() {
+               panic("cipher.NewCTR: iv length must equal block size")
+       }
+
        return &ctr{
                b:       block,
                ctr:     dup(iv),
index cc22b7a..d6b4039 100644 (file)
@@ -20,12 +20,15 @@ func testKeyGeneration(t *testing.T, c *elliptic.Curve, tag string) {
                return
        }
        if !c.IsOnCurve(priv.PublicKey.X, priv.PublicKey.Y) {
-               t.Errorf("%s: public key invalid", tag, err)
+               t.Errorf("%s: public key invalid: %s", tag, err)
        }
 }
 
 func TestKeyGeneration(t *testing.T) {
        testKeyGeneration(t, elliptic.P224(), "p224")
+       if testing.Short() {
+               return
+       }
        testKeyGeneration(t, elliptic.P256(), "p256")
        testKeyGeneration(t, elliptic.P384(), "p384")
        testKeyGeneration(t, elliptic.P521(), "p521")
@@ -53,6 +56,9 @@ func testSignAndVerify(t *testing.T, c *elliptic.Curve, tag string) {
 
 func TestSignAndVerify(t *testing.T) {
        testSignAndVerify(t, elliptic.P224(), "p224")
+       if testing.Short() {
+               return
+       }
        testSignAndVerify(t, elliptic.P256(), "p256")
        testSignAndVerify(t, elliptic.P384(), "p384")
        testSignAndVerify(t, elliptic.P521(), "p521")
@@ -214,5 +220,8 @@ func TestVectors(t *testing.T) {
                if Verify(&pub, hashed, r, s) != test.ok {
                        t.Errorf("%d: bad result", i)
                }
+               if testing.Short() {
+                       break
+               }
        }
 }
index 6ae6fb9..02083a9 100644 (file)
@@ -297,6 +297,9 @@ func TestBaseMult(t *testing.T) {
                if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y {
                        t.Errorf("%d: bad output for k=%s: got (%x, %s), want (%s, %s)", i, e.k, x, y, e.x, e.y)
                }
+               if testing.Short() && i > 5 {
+                       break
+               }
        }
 }
 
index 814b786..75bc47e 100644 (file)
@@ -90,5 +90,8 @@ func TestParse(t *testing.T) {
                if !bytes.Equal(out, expected) {
                        t.Errorf("%d: output got: %x want: %x", i, out, expected)
                }
+               if testing.Short() {
+                       break
+               }
        }
 }
index f64ead4..bfae7ce 100644 (file)
@@ -11,7 +11,11 @@ import (
 )
 
 func TestRead(t *testing.T) {
-       b := make([]byte, 4e6)
+       var n int = 4e6
+       if testing.Short() {
+               n = 1e5
+       }
+       b := make([]byte, n)
        n, err := Read(b)
        if n != len(b) || err != nil {
                t.Fatalf("Read(buf) = %d, %s", n, err)
index 7b2ce08..30a4824 100644 (file)
@@ -97,7 +97,11 @@ func TestEncryptPKCS1v15(t *testing.T) {
                return true
        }
 
-       quick.Check(tryEncryptDecrypt, nil)
+       config := new(quick.Config)
+       if testing.Short() {
+               config.MaxCount = 10
+       }
+       quick.Check(tryEncryptDecrypt, config)
 }
 
 // These test vectors were generated with `openssl rsautl -pkcs -encrypt`
index 22d4576..bf7c051 100644 (file)
@@ -15,7 +15,11 @@ import (
 func TestKeyGeneration(t *testing.T) {
        random := rand.Reader
 
-       priv, err := GenerateKey(random, 1024)
+       size := 1024
+       if testing.Short() {
+               size = 128
+       }
+       priv, err := GenerateKey(random, size)
        if err != nil {
                t.Errorf("failed to generate key")
        }
@@ -99,6 +103,9 @@ func TestDecryptOAEP(t *testing.T) {
                                t.Errorf("#%d,%d (blind) bad result: %#v (want %#v)", i, j, out, message.in)
                        }
                }
+               if testing.Short() {
+                       break
+               }
        }
 }
 
index 81b5a07..c779234 100644 (file)
@@ -93,9 +93,10 @@ const (
 
 // ConnectionState records basic TLS details about the connection.
 type ConnectionState struct {
-       HandshakeComplete  bool
-       CipherSuite        uint16
-       NegotiatedProtocol string
+       HandshakeComplete          bool
+       CipherSuite                uint16
+       NegotiatedProtocol         string
+       NegotiatedProtocolIsMutual bool
 
        // the certificate chain that was presented by the other side
        PeerCertificates []*x509.Certificate
@@ -124,7 +125,6 @@ type Config struct {
        RootCAs *CASet
 
        // NextProtos is a list of supported, application level protocols.
-       // Currently only server-side handling is supported.
        NextProtos []string
 
        // ServerName is included in the client's handshake to support virtual
index 1e6fe60..b94e235 100644 (file)
@@ -35,7 +35,8 @@ type Conn struct {
        ocspResponse      []byte // stapled OCSP response
        peerCertificates  []*x509.Certificate
 
-       clientProtocol string
+       clientProtocol         string
+       clientProtocolFallback bool
 
        // first permanent error
        errMutex sync.Mutex
@@ -761,6 +762,7 @@ func (c *Conn) ConnectionState() ConnectionState {
        state.HandshakeComplete = c.handshakeComplete
        if c.handshakeComplete {
                state.NegotiatedProtocol = c.clientProtocol
+               state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback
                state.CipherSuite = c.cipherSuite
                state.PeerCertificates = c.peerCertificates
        }
index a325a9b..540b25c 100644 (file)
@@ -29,6 +29,7 @@ func (c *Conn) clientHandshake() os.Error {
                serverName:         c.config.ServerName,
                supportedCurves:    []uint16{curveP256, curveP384, curveP521},
                supportedPoints:    []uint8{pointFormatUncompressed},
+               nextProtoNeg:       len(c.config.NextProtos) > 0,
        }
 
        t := uint32(c.config.time())
@@ -66,6 +67,11 @@ func (c *Conn) clientHandshake() os.Error {
                return c.sendAlert(alertUnexpectedMessage)
        }
 
+       if !hello.nextProtoNeg && serverHello.nextProtoNeg {
+               c.sendAlert(alertHandshakeFailure)
+               return os.ErrorString("server advertised unrequested NPN")
+       }
+
        suite, suiteId := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite)
        if suite == nil {
                return c.sendAlert(alertHandshakeFailure)
@@ -267,6 +273,17 @@ func (c *Conn) clientHandshake() os.Error {
        c.out.prepareCipherSpec(clientCipher, clientHash)
        c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
 
+       if serverHello.nextProtoNeg {
+               nextProto := new(nextProtoMsg)
+               proto, fallback := mutualProtocol(c.config.NextProtos, serverHello.nextProtos)
+               nextProto.proto = proto
+               c.clientProtocol = proto
+               c.clientProtocolFallback = fallback
+
+               finishedHash.Write(nextProto.marshal())
+               c.writeRecord(recordTypeHandshake, nextProto.marshal())
+       }
+
        finished := new(finishedMsg)
        finished.verifyData = finishedHash.clientSum(masterSecret)
        finishedHash.Write(finished.marshal())
@@ -299,3 +316,19 @@ func (c *Conn) clientHandshake() os.Error {
        c.cipherSuite = suiteId
        return nil
 }
+
+// mutualProtocol finds the mutual Next Protocol Negotiation protocol given the
+// set of client and server supported protocols. The set of client supported
+// protocols must not be empty. It returns the resulting protocol and flag
+// indicating if the fallback case was reached.
+func mutualProtocol(clientProtos, serverProtos []string) (string, bool) {
+       for _, s := range serverProtos {
+               for _, c := range clientProtos {
+                       if s == c {
+                               return s, false
+                       }
+               }
+       }
+
+       return clientProtos[0], true
+}
index fd1f145..3f91c7a 100644 (file)
@@ -50,7 +50,7 @@ func TestRunClient(t *testing.T) {
 
        testConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}
 
-       conn, err := Dial("tcp", "", "127.0.0.1:10443", testConfig)
+       conn, err := Dial("tcp", "127.0.0.1:10443", testConfig)
        if err != nil {
                t.Fatal(err)
        }
index 21577dd..0b93b89 100644 (file)
@@ -34,7 +34,11 @@ func TestMarshalUnmarshal(t *testing.T) {
        for i, iface := range tests {
                ty := reflect.NewValue(iface).Type()
 
-               for j := 0; j < 100; j++ {
+               n := 100
+               if testing.Short() {
+                       n = 5
+               }
+               for j := 0; j < n; j++ {
                        v, ok := quick.Value(ty, rand)
                        if !ok {
                                t.Errorf("#%d: failed to create value", i)
index e8290d7..f66449c 100644 (file)
@@ -87,8 +87,9 @@ func Listen(network, laddr string, config *Config) (*Listener, os.Error) {
 // Dial interprets a nil configuration as equivalent to
 // the zero configuration; see the documentation of Config
 // for the defaults.
-func Dial(network, laddr, raddr string, config *Config) (*Conn, os.Error) {
-       c, err := net.Dial(network, laddr, raddr)
+func Dial(network, addr string, config *Config) (*Conn, os.Error) {
+       raddr := addr
+       c, err := net.Dial(network, raddr)
        if err != nil {
                return nil, err
        }
index 3af8ba8..853bcde 100644 (file)
@@ -304,6 +304,42 @@ const (
        KeyUsageDecipherOnly
 )
 
+// RFC 5280, 4.2.1.12  Extended Key Usage
+//
+// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 }
+//
+// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
+//
+// id-kp-serverAuth             OBJECT IDENTIFIER ::= { id-kp 1 }
+// id-kp-clientAuth             OBJECT IDENTIFIER ::= { id-kp 2 }
+// id-kp-codeSigning            OBJECT IDENTIFIER ::= { id-kp 3 }
+// id-kp-emailProtection        OBJECT IDENTIFIER ::= { id-kp 4 }
+// id-kp-timeStamping           OBJECT IDENTIFIER ::= { id-kp 8 }
+// id-kp-OCSPSigning            OBJECT IDENTIFIER ::= { id-kp 9 }
+var (
+       oidExtKeyUsageAny             = asn1.ObjectIdentifier{2, 5, 29, 37, 0}
+       oidExtKeyUsageServerAuth      = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1}
+       oidExtKeyUsageClientAuth      = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2}
+       oidExtKeyUsageCodeSigning     = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3}
+       oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4}
+       oidExtKeyUsageTimeStamping    = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8}
+       oidExtKeyUsageOCSPSigning     = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9}
+)
+
+// ExtKeyUsage represents an extended set of actions that are valid for a given key.
+// Each of the ExtKeyUsage* constants define a unique action.
+type ExtKeyUsage int
+
+const (
+       ExtKeyUsageAny ExtKeyUsage = iota
+       ExtKeyUsageServerAuth
+       ExtKeyUsageClientAuth
+       ExtKeyUsageCodeSigning
+       ExtKeyUsageEmailProtection
+       ExtKeyUsageTimeStamping
+       ExtKeyUsageOCSPSigning
+)
+
 // A Certificate represents an X.509 certificate.
 type Certificate struct {
        Raw                []byte // Raw ASN.1 DER contents.
@@ -320,6 +356,9 @@ type Certificate struct {
        NotBefore, NotAfter *time.Time // Validity bounds.
        KeyUsage            KeyUsage
 
+       ExtKeyUsage        []ExtKeyUsage           // Sequence of extended key usages.
+       UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package.
+
        BasicConstraintsValid bool // if true then the next two fields are valid.
        IsCA                  bool
        MaxPathLen            int
@@ -666,6 +705,44 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) {
                                out.AuthorityKeyId = a.Id
                                continue
 
+                       case 37:
+                               // RFC 5280, 4.2.1.12.  Extended Key Usage
+
+                               // id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
+                               //
+                               // ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+                               //
+                               // KeyPurposeId ::= OBJECT IDENTIFIER
+
+                               var keyUsage []asn1.ObjectIdentifier
+                               _, err = asn1.Unmarshal(e.Value, &keyUsage)
+                               if err != nil {
+                                       return nil, err
+                               }
+
+                               for _, u := range keyUsage {
+                                       switch {
+                                       case u.Equal(oidExtKeyUsageAny):
+                                               out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageAny)
+                                       case u.Equal(oidExtKeyUsageServerAuth):
+                                               out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageServerAuth)
+                                       case u.Equal(oidExtKeyUsageClientAuth):
+                                               out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageClientAuth)
+                                       case u.Equal(oidExtKeyUsageCodeSigning):
+                                               out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageCodeSigning)
+                                       case u.Equal(oidExtKeyUsageEmailProtection):
+                                               out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageEmailProtection)
+                                       case u.Equal(oidExtKeyUsageTimeStamping):
+                                               out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageTimeStamping)
+                                       case u.Equal(oidExtKeyUsageOCSPSigning):
+                                               out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageOCSPSigning)
+                                       default:
+                                               out.UnknownExtKeyUsage = append(out.UnknownExtKeyUsage, u)
+                                       }
+                               }
+
+                               continue
+
                        case 14:
                                // RFC 5280, 4.2.1.2
                                var keyid []byte
index 9087021..c83e64e 100644 (file)
@@ -143,9 +143,6 @@ func TestLineAline(t *testing.T) {
        }
 }
 
-// gotest: if [ "$(uname)-$(uname -m)" = Linux-x86_64 -a "$GOARCH" = amd64 ]; then
-// gotest:    mkdir -p _test && $AS pclinetest.s && $LD -E main -o _test/pclinetest pclinetest.$O
-// gotest: fi
 func TestPCLine(t *testing.T) {
        if !dotest() {
                return
index 69ad5fe..e77cf64 100644 (file)
@@ -15,31 +15,26 @@ var fset = token.NewFileSet()
 
 
 var grammars = []string{
-`Program = .
-`,
-
-`Program = foo .
-foo = "foo" .
-`,
-
-`Program = "a" | "b" "c" .
-`,
-
-`Program = "a" ... "z" .
-`,
-
-`Program = Song .
- Song = { Note } .
- Note = Do | (Re | Mi | Fa | So | La) | Ti .
- Do = "c" .
- Re = "d" .
- Mi = "e" .
- Fa = "f" .
- So = "g" .
- La = "a" .
- Ti = ti .
- ti = "b" .
-`,
+       `Program = .`,
+
+       `Program = foo .
+        foo = "foo" .`,
+
+       `Program = "a" | "b" "c" .`,
+
+       `Program = "a" ... "z" .`,
+
+       `Program = Song .
+        Song = { Note } .
+        Note = Do | (Re | Mi | Fa | So | La) | Ti .
+        Do = "c" .
+        Re = "d" .
+        Mi = "e" .
+        Fa = "f" .
+        So = "g" .
+        La = "a" .
+        Ti = ti .
+        ti = "b" .`,
 }
 
 
index c385301..818168e 100644 (file)
@@ -18,7 +18,7 @@ type parser struct {
        scanner scanner.Scanner
        pos     token.Pos   // token position
        tok     token.Token // one token look-ahead
-       lit     []byte      // token literal
+       lit     string      // token literal
 }
 
 
@@ -44,7 +44,7 @@ func (p *parser) errorExpected(pos token.Pos, msg string) {
                // make the error message more specific
                msg += ", found '" + p.tok.String() + "'"
                if p.tok.IsLiteral() {
-                       msg += " " + string(p.lit)
+                       msg += " " + p.lit
                }
        }
        p.error(pos, msg)
@@ -63,7 +63,7 @@ func (p *parser) expect(tok token.Token) token.Pos {
 
 func (p *parser) parseIdentifier() *Name {
        pos := p.pos
-       name := string(p.lit)
+       name := p.lit
        p.expect(token.IDENT)
        return &Name{pos, name}
 }
@@ -73,7 +73,7 @@ func (p *parser) parseToken() *Token {
        pos := p.pos
        value := ""
        if p.tok == token.STRING {
-               value, _ = strconv.Unquote(string(p.lit))
+               value, _ = strconv.Unquote(p.lit)
                // Unquote may fail with an error, but only if the scanner found
                // an illegal string in the first place. In this case the error
                // has already been reported.
index c6d1402..7dedb53 100644 (file)
@@ -22,7 +22,7 @@ type parser struct {
        file    *token.File
        pos     token.Pos   // token position
        tok     token.Token // one token look-ahead
-       lit     []byte      // token literal
+       lit     string      // token literal
 
        packs map[string]string // PackageName -> ImportPath
        rules map[string]expr   // RuleName -> Expression
@@ -62,7 +62,7 @@ func (p *parser) errorExpected(pos token.Pos, msg string) {
                // make the error message more specific
                msg += ", found '" + p.tok.String() + "'"
                if p.tok.IsLiteral() {
-                       msg += " " + string(p.lit)
+                       msg += " " + p.lit
                }
        }
        p.error(pos, msg)
@@ -80,7 +80,7 @@ func (p *parser) expect(tok token.Token) token.Pos {
 
 
 func (p *parser) parseIdentifier() string {
-       name := string(p.lit)
+       name := p.lit
        p.expect(token.IDENT)
        return name
 }
@@ -130,7 +130,7 @@ func (p *parser) parseRuleName() (string, bool) {
 func (p *parser) parseString() string {
        s := ""
        if p.tok == token.STRING {
-               s, _ = strconv.Unquote(string(p.lit))
+               s, _ = strconv.Unquote(p.lit)
                // Unquote may fail with an error, but only if the scanner found
                // an illegal string in the first place. In this case the error
                // has already been reported.
@@ -181,7 +181,7 @@ func (p *parser) parseField() expr {
        var fname string
        switch p.tok {
        case token.ILLEGAL:
-               if string(p.lit) != "@" {
+               if p.lit != "@" {
                        return nil
                }
                fname = "@"
index e28fb21..53294af 100644 (file)
@@ -286,11 +286,11 @@ func connect(display string) (conn net.Conn, displayStr string, err os.Error) {
        }
        // Make the connection.
        if socket != "" {
-               conn, err = net.Dial("unix", "", socket+":"+displayStr)
+               conn, err = net.Dial("unix", socket+":"+displayStr)
        } else if host != "" {
-               conn, err = net.Dial(protocol, "", host+":"+strconv.Itoa(6000+displayInt))
+               conn, err = net.Dial(protocol, host+":"+strconv.Itoa(6000+displayInt))
        } else {
-               conn, err = net.Dial("unix", "", "/tmp/.X11-unix/X"+displayStr)
+               conn, err = net.Dial("unix", "/tmp/.X11-unix/X"+displayStr)
        }
        if err != nil {
                return nil, "", os.NewError("cannot connect to " + display + ": " + err.String())
index ff28cf1..541d3fe 100644 (file)
@@ -39,9 +39,13 @@ type job struct {
 }
 
 func runTests(t *testing.T, baseName string, tests []test) {
-       for i, test := range tests {
+       delta := 1
+       if testing.Short() {
+               delta = 16
+       }
+       for i := 0; i < len(tests); i += delta {
                name := fmt.Sprintf("%s[%d]", baseName, i)
-               test.run(t, name)
+               tests[i].run(t, name)
        }
 }
 
index 9920ff6..ba056e8 100644 (file)
@@ -205,7 +205,7 @@ func parseLoad(args []byte) (ident string, path string, err os.Error) {
        sc, ev := newScanner(args)
 
        var toks [4]token.Token
-       var lits [4][]byte
+       var lits [4]string
        for i := range toks {
                _, toks[i], lits[i] = sc.Scan()
        }
index 14f4d52..19a3104 100644 (file)
@@ -56,7 +56,7 @@
 
                flag.Bool(...)  // global options
                flag.Parse()  // parse leading command
-               subcmd := flag.Arg[0]
+               subcmd := flag.Arg(0)
                switch subcmd {
                        // add per-subcommand options
                }
index c8aa609..4d308ac 100644 (file)
@@ -442,6 +442,9 @@ func BenchmarkSprintfPrefixedInt(b *testing.B) {
 }
 
 func TestCountMallocs(t *testing.T) {
+       if testing.Short() {
+               return
+       }
        mallocs := 0 - runtime.MemStats.Mallocs
        for i := 0; i < 100; i++ {
                Sprintf("")
index 4a4c12b..ed3e2cd 100644 (file)
@@ -66,7 +66,7 @@ type Decl interface {
 // A Comment node represents a single //-style or /*-style comment.
 type Comment struct {
        Slash token.Pos // position of "/" starting the comment
-       Text  []byte    // comment text (excluding '\n' for //-style comments)
+       Text  string    // comment text (excluding '\n' for //-style comments)
 }
 
 
@@ -199,7 +199,7 @@ type (
        BasicLit struct {
                ValuePos token.Pos   // literal position
                Kind     token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING
-               Value    []byte      // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
+               Value    string      // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
        }
 
        // A FuncLit node represents a function literal.
@@ -781,7 +781,7 @@ type (
        ImportSpec struct {
                Doc     *CommentGroup // associated documentation; or nil
                Name    *Ident        // local package name (including "."); or nil
-               Path    *BasicLit     // package path
+               Path    *BasicLit     // import path
                Comment *CommentGroup // line comments; or nil
        }
 
@@ -925,8 +925,9 @@ type File struct {
        Package    token.Pos       // position of "package" keyword
        Name       *Ident          // package name
        Decls      []Decl          // top-level declarations; or nil
-       Scope      *Scope          // package scope
-       Unresolved []*Ident        // unresolved global identifiers
+       Scope      *Scope          // package scope (this file only)
+       Imports    []*ImportSpec   // imports in this file
+       Unresolved []*Ident        // unresolved identifiers in this file
        Comments   []*CommentGroup // list of all comments in the source file
 }
 
@@ -944,9 +945,10 @@ func (f *File) End() token.Pos {
 // collectively building a Go package.
 //
 type Package struct {
-       Name  string           // package name
-       Scope *Scope           // package scope
-       Files map[string]*File // Go source files by filename
+       Name    string            // package name
+       Scope   *Scope            // package scope
+       Imports map[string]*Scope // map of import path -> package scope across all files
+       Files   map[string]*File  // Go source files by filename
 }
 
 
index 4da487c..090d08d 100644 (file)
@@ -304,7 +304,7 @@ const (
 // separator is an empty //-style comment that is interspersed between
 // different comment groups when they are concatenated into a single group
 //
-var separator = &Comment{noPos, []byte("//")}
+var separator = &Comment{noPos, "//"}
 
 
 // MergePackageFiles creates a file AST by merging the ASTs of the
@@ -426,5 +426,6 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
        }
 
        // TODO(gri) need to compute pkgScope and unresolved identifiers!
-       return &File{doc, pos, NewIdent(pkg.Name), decls, nil, nil, comments}
+       // TODO(gri) need to compute imports!
+       return &File{doc, pos, NewIdent(pkg.Name), decls, nil, nil, nil, comments}
 }
diff --git a/libgo/go/go/ast/resolve.go b/libgo/go/go/ast/resolve.go
new file mode 100644 (file)
index 0000000..fddc3ba
--- /dev/null
@@ -0,0 +1,188 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements NewPackage.
+
+package ast
+
+import (
+       "fmt"
+       "go/scanner"
+       "go/token"
+       "os"
+)
+
+
+type pkgBuilder struct {
+       scanner.ErrorVector
+       fset *token.FileSet
+}
+
+
+func (p *pkgBuilder) error(pos token.Pos, msg string) {
+       p.Error(p.fset.Position(pos), msg)
+}
+
+
+func (p *pkgBuilder) errorf(pos token.Pos, format string, args ...interface{}) {
+       p.error(pos, fmt.Sprintf(format, args...))
+}
+
+
+func (p *pkgBuilder) declare(scope, altScope *Scope, obj *Object) {
+       alt := scope.Insert(obj)
+       if alt == nil && altScope != nil {
+               // see if there is a conflicting declaration in altScope
+               alt = altScope.Lookup(obj.Name)
+       }
+       if alt != nil {
+               prevDecl := ""
+               if pos := alt.Pos(); pos.IsValid() {
+                       prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.fset.Position(pos))
+               }
+               p.error(obj.Pos(), fmt.Sprintf("%s redeclared in this block%s", obj.Name, prevDecl))
+       }
+}
+
+
+func resolve(scope *Scope, ident *Ident) bool {
+       for ; scope != nil; scope = scope.Outer {
+               if obj := scope.Lookup(ident.Name); obj != nil {
+                       ident.Obj = obj
+                       return true
+               }
+       }
+       return false
+}
+
+
+// NewPackage uses an Importer to resolve imports. Given an importPath,
+// an importer returns the imported package's name, its scope of exported
+// objects, and an error, if any.
+//
+type Importer func(path string) (name string, scope *Scope, err os.Error)
+
+
+// NewPackage creates a new Package node from a set of File nodes. It resolves
+// unresolved identifiers across files and updates each file's Unresolved list
+// accordingly. If a non-nil importer and universe scope are provided, they are
+// used to resolve identifiers not declared in any of the package files. Any
+// remaining unresolved identifiers are reported as undeclared. If the files
+// belong to different packages, one package name is selected and files with
+// different package name are reported and then ignored.
+// The result is a package node and a scanner.ErrorList if there were errors.
+//
+func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer, universe *Scope) (*Package, os.Error) {
+       var p pkgBuilder
+       p.fset = fset
+
+       // complete package scope
+       pkgName := ""
+       pkgScope := NewScope(universe)
+       for _, file := range files {
+               // package names must match
+               switch name := file.Name.Name; {
+               case pkgName == "":
+                       pkgName = name
+               case name != pkgName:
+                       p.errorf(file.Package, "package %s; expected %s", name, pkgName)
+                       continue // ignore this file
+               }
+
+               // collect top-level file objects in package scope
+               for _, obj := range file.Scope.Objects {
+                       p.declare(pkgScope, nil, obj)
+               }
+       }
+
+       // imports maps import paths to package names and scopes
+       // TODO(gri): Eventually we like to get to the import scope from
+       //            a package object. Then we can have a map path -> Obj.
+       type importedPkg struct {
+               name  string
+               scope *Scope
+       }
+       imports := make(map[string]*importedPkg)
+
+       // complete file scopes with imports and resolve identifiers
+       for _, file := range files {
+               // ignore file if it belongs to a different package
+               // (error has already been reported)
+               if file.Name.Name != pkgName {
+                       continue
+               }
+
+               // build file scope by processing all imports
+               importErrors := false
+               fileScope := NewScope(pkgScope)
+               for _, spec := range file.Imports {
+                       // add import to global map of imports
+                       path := string(spec.Path.Value)
+                       path = path[1 : len(path)-1] // strip ""'s
+                       pkg := imports[path]
+                       if pkg == nil {
+                               if importer == nil {
+                                       importErrors = true
+                                       continue
+                               }
+                               name, scope, err := importer(path)
+                               if err != nil {
+                                       p.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err)
+                                       importErrors = true
+                                       continue
+                               }
+                               pkg = &importedPkg{name, scope}
+                               imports[path] = pkg
+                               // TODO(gri) If a local package name != "." is provided,
+                               // global identifier resolution could proceed even if the
+                               // import failed. Consider adjusting the logic here a bit.
+                       }
+                       // local name overrides imported package name
+                       name := pkg.name
+                       if spec.Name != nil {
+                               name = spec.Name.Name
+                       }
+                       // add import to file scope
+                       if name == "." {
+                               // merge imported scope with file scope
+                               for _, obj := range pkg.scope.Objects {
+                                       p.declare(fileScope, pkgScope, obj)
+                               }
+                       } else {
+                               // declare imported package object in file scope
+                               obj := NewObj(Pkg, name)
+                               obj.Decl = spec
+                               p.declare(fileScope, pkgScope, obj)
+                       }
+               }
+
+               // resolve identifiers
+               if importErrors {
+                       // don't use the universe scope without correct imports
+                       // (objects in the universe may be shadowed by imports;
+                       // with missing imports identifiers might get resolved
+                       // wrongly)
+                       pkgScope.Outer = nil
+               }
+               i := 0
+               for _, ident := range file.Unresolved {
+                       if !resolve(fileScope, ident) {
+                               p.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
+                               file.Unresolved[i] = ident
+                               i++
+                       }
+
+               }
+               file.Unresolved = file.Unresolved[0:i]
+               pkgScope.Outer = universe // reset universe scope
+       }
+
+       // collect all import paths and respective package scopes
+       importedScopes := make(map[string]*Scope)
+       for path, pkg := range imports {
+               importedScopes[path] = pkg.scope
+       }
+
+       return &Package{pkgName, pkgScope, importedScopes, files}, p.GetError(scanner.Sorted)
+}
index 91866dc..830d88a 100644 (file)
@@ -39,16 +39,14 @@ func (s *Scope) Lookup(name string) *Object {
 }
 
 
-// Insert attempts to insert a named object into the scope s.
-// If the scope does not contain an object with that name yet,
-// Insert inserts the object and returns it. Otherwise, Insert
-// leaves the scope unchanged and returns the object found in
-// the scope instead.
+// Insert attempts to insert a named object obj into the scope s.
+// If the scope already contains an object alt with the same name,
+// Insert leaves the scope unchanged and returns alt. Otherwise
+// it inserts obj and returns nil."
 //
 func (s *Scope) Insert(obj *Object) (alt *Object) {
        if alt = s.Objects[obj.Name]; alt == nil {
                s.Objects[obj.Name] = obj
-               alt = obj
        }
        return
 }
@@ -101,6 +99,11 @@ func (obj *Object) Pos() token.Pos {
                                return n.Pos()
                        }
                }
+       case *ImportSpec:
+               if d.Name != nil && d.Name.Name == name {
+                       return d.Name.Pos()
+               }
+               return d.Path.Pos()
        case *ValueSpec:
                for _, n := range d.Names {
                        if n.Name == name {
index 9ff0bd5..f1ebfa9 100644 (file)
@@ -286,7 +286,7 @@ func unindent(block [][]byte) {
 // nor to have trailing spaces at the end of lines.
 // The comment markers have already been removed.
 //
-// Turn each run of multiple \n into </p><p>
+// Turn each run of multiple \n into </p><p>.
 // Turn each run of indented lines into a <pre> block without indent.
 //
 // URLs in the comment text are converted into links; if the URL also appears
index e46857c..e7a8d3f 100644 (file)
@@ -66,7 +66,7 @@ func (doc *docReader) addDoc(comments *ast.CommentGroup) {
        n2 := len(comments.List)
        list := make([]*ast.Comment, n1+1+n2) // + 1 for separator line
        copy(list, doc.doc.List)
-       list[n1] = &ast.Comment{token.NoPos, []byte("//")} // separator line
+       list[n1] = &ast.Comment{token.NoPos, "//"} // separator line
        copy(list[n1+1:], comments.List)
        doc.doc = &ast.CommentGroup{list}
 }
@@ -105,7 +105,7 @@ func baseTypeName(typ ast.Expr) string {
                // if the type is not exported, the effect to
                // a client is as if there were no type name
                if t.IsExported() {
-                       return string(t.Name)
+                       return t.Name
                }
        case *ast.StarExpr:
                return baseTypeName(t.X)
@@ -300,9 +300,9 @@ func (doc *docReader) addFile(src *ast.File) {
        // collect BUG(...) comments
        for _, c := range src.Comments {
                text := c.List[0].Text
-               if m := bug_markers.FindIndex(text); m != nil {
+               if m := bug_markers.FindStringIndex(text); m != nil {
                        // found a BUG comment; maybe empty
-                       if btxt := text[m[1]:]; bug_content.Match(btxt) {
+                       if btxt := text[m[1]:]; bug_content.MatchString(btxt) {
                                // non-empty BUG comment; collect comment without BUG prefix
                                list := copyCommentList(c.List)
                                list[0].Text = text[m[1]:]
index 6f35b49..fc4ae09 100644 (file)
@@ -69,7 +69,7 @@ func ParseExpr(fset *token.FileSet, filename string, src interface{}) (ast.Expr,
 
        var p parser
        p.init(fset, filename, data, 0)
-       x := p.parseExpr()
+       x := p.parseRhs()
        if p.tok == token.SEMICOLON {
                p.next() // consume automatically inserted semicolon, if any
        }
@@ -159,7 +159,8 @@ func ParseFiles(fset *token.FileSet, filenames []string, mode uint) (pkgs map[st
                        name := src.Name.Name
                        pkg, found := pkgs[name]
                        if !found {
-                               pkg = &ast.Package{name, nil, make(map[string]*ast.File)}
+                               // TODO(gri) Use NewPackage here; reconsider ParseFiles API.
+                               pkg = &ast.Package{name, nil, nil, make(map[string]*ast.File)}
                                pkgs[name] = pkg
                        }
                        pkg.Files[filename] = src
index b0e8c8a..ad7e4cd 100644 (file)
@@ -47,17 +47,18 @@ type parser struct {
        lineComment *ast.CommentGroup // last line comment
 
        // Next token
-       pos  token.Pos   // token position
-       tok  token.Token // one token look-ahead
-       lit_ []byte      // token literal (slice into original source, don't hold on to it)
+       pos token.Pos   // token position
+       tok token.Token // one token look-ahead
+       lit string      // token literal
 
        // Non-syntactic parser control
        exprLev int // < 0: in control clause, >= 0: in expression
 
        // Ordinary identifer scopes
-       pkgScope   *ast.Scope   // pkgScope.Outer == nil
-       topScope   *ast.Scope   // top-most scope; may be pkgScope
-       unresolved []*ast.Ident // unresolved global identifiers
+       pkgScope   *ast.Scope        // pkgScope.Outer == nil
+       topScope   *ast.Scope        // top-most scope; may be pkgScope
+       unresolved []*ast.Ident      // unresolved identifiers
+       imports    []*ast.ImportSpec // list of imports
 
        // Label scope
        // (maintained by open/close LabelScope)
@@ -95,15 +96,6 @@ func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode uin
 }
 
 
-func (p *parser) lit() []byte {
-       // make a copy of p.lit_ so that we don't hold on to
-       // a copy of the entire source indirectly in the AST
-       t := make([]byte, len(p.lit_))
-       copy(t, p.lit_)
-       return t
-}
-
-
 // ----------------------------------------------------------------------------
 // Scoping support
 
@@ -141,13 +133,13 @@ func (p *parser) closeLabelScope() {
 
 func (p *parser) declare(decl interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
        for _, ident := range idents {
+               assert(ident.Obj == nil, "identifier already declared or resolved")
                if ident.Name != "_" {
                        obj := ast.NewObj(kind, ident.Name)
                        // remember the corresponding declaration for redeclaration
                        // errors and global variable resolution/typechecking phase
                        obj.Decl = decl
-                       alt := scope.Insert(obj)
-                       if alt != obj && p.mode&DeclarationErrors != 0 {
+                       if alt := scope.Insert(obj); alt != nil && p.mode&DeclarationErrors != 0 {
                                prevDecl := ""
                                if pos := alt.Pos(); pos.IsValid() {
                                        prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.file.Position(pos))
@@ -166,14 +158,16 @@ func (p *parser) shortVarDecl(idents []*ast.Ident) {
        // the same type, and at least one of the non-blank variables is new.
        n := 0 // number of new variables
        for _, ident := range idents {
+               assert(ident.Obj == nil, "identifier already declared or resolved")
                if ident.Name != "_" {
                        obj := ast.NewObj(ast.Var, ident.Name)
                        // short var declarations cannot have redeclaration errors
                        // and are not global => no need to remember the respective
                        // declaration
                        alt := p.topScope.Insert(obj)
-                       if alt == obj {
+                       if alt == nil {
                                n++ // new declaration
+                               alt = obj
                        }
                        ident.Obj = alt
                }
@@ -184,7 +178,19 @@ func (p *parser) shortVarDecl(idents []*ast.Ident) {
 }
 
 
-func (p *parser) resolve(ident *ast.Ident) {
+// The unresolved object is a sentinel to mark identifiers that have been added
+// to the list of unresolved identifiers. The sentinel is only used for verifying
+// internal consistency.
+var unresolved = new(ast.Object)
+
+
+func (p *parser) resolve(x ast.Expr) {
+       // nothing to do if x is not an identifier or the blank identifier
+       ident, _ := x.(*ast.Ident)
+       if ident == nil {
+               return
+       }
+       assert(ident.Obj == nil, "identifier already declared or resolved")
        if ident.Name == "_" {
                return
        }
@@ -195,10 +201,12 @@ func (p *parser) resolve(ident *ast.Ident) {
                        return
                }
        }
-       // collect unresolved global identifiers; ignore the others
-       if p.topScope == p.pkgScope {
-               p.unresolved = append(p.unresolved, ident)
-       }
+       // all local scopes are known, so any unresolved identifier
+       // must be found either in the file scope, package scope
+       // (perhaps in another file), or universe scope --- collect
+       // them so that they can be resolved later
+       ident.Obj = unresolved
+       p.unresolved = append(p.unresolved, ident)
 }
 
 
@@ -244,7 +252,7 @@ func (p *parser) next0() {
                s := p.tok.String()
                switch {
                case p.tok.IsLiteral():
-                       p.printTrace(s, string(p.lit_))
+                       p.printTrace(s, p.lit)
                case p.tok.IsOperator(), p.tok.IsKeyword():
                        p.printTrace("\"" + s + "\"")
                default:
@@ -252,7 +260,7 @@ func (p *parser) next0() {
                }
        }
 
-       p.pos, p.tok, p.lit_ = p.scanner.Scan()
+       p.pos, p.tok, p.lit = p.scanner.Scan()
 }
 
 // Consume a comment and return it and the line on which it ends.
@@ -260,15 +268,16 @@ func (p *parser) consumeComment() (comment *ast.Comment, endline int) {
        // /*-style comments may end on a different line than where they start.
        // Scan the comment for '\n' chars and adjust endline accordingly.
        endline = p.file.Line(p.pos)
-       if p.lit_[1] == '*' {
-               for _, b := range p.lit_ {
-                       if b == '\n' {
+       if p.lit[1] == '*' {
+               // don't use range here - no need to decode Unicode code points
+               for i := 0; i < len(p.lit); i++ {
+                       if p.lit[i] == '\n' {
                                endline++
                        }
                }
        }
 
-       comment = &ast.Comment{p.pos, p.lit()}
+       comment = &ast.Comment{p.pos, p.lit}
        p.next0()
 
        return
@@ -358,12 +367,12 @@ func (p *parser) errorExpected(pos token.Pos, msg string) {
        if pos == p.pos {
                // the error happened at the current position;
                // make the error message more specific
-               if p.tok == token.SEMICOLON && p.lit_[0] == '\n' {
+               if p.tok == token.SEMICOLON && p.lit[0] == '\n' {
                        msg += ", found newline"
                } else {
                        msg += ", found '" + p.tok.String() + "'"
                        if p.tok.IsLiteral() {
-                               msg += " " + string(p.lit_)
+                               msg += " " + p.lit
                        }
                }
        }
@@ -388,6 +397,13 @@ func (p *parser) expectSemi() {
 }
 
 
+func assert(cond bool, msg string) {
+       if !cond {
+               panic("go/parser internal error: " + msg)
+       }
+}
+
+
 // ----------------------------------------------------------------------------
 // Identifiers
 
@@ -395,7 +411,7 @@ func (p *parser) parseIdent() *ast.Ident {
        pos := p.pos
        name := "_"
        if p.tok == token.IDENT {
-               name = string(p.lit_)
+               name = p.lit
                p.next()
        } else {
                p.expect(token.IDENT) // use expect() error handling
@@ -422,21 +438,51 @@ func (p *parser) parseIdentList() (list []*ast.Ident) {
 // ----------------------------------------------------------------------------
 // Common productions
 
-func (p *parser) parseExprList() (list []ast.Expr) {
+// If lhs is set, result list elements which are identifiers are not resolved.
+func (p *parser) parseExprList(lhs bool) (list []ast.Expr) {
        if p.trace {
                defer un(trace(p, "ExpressionList"))
        }
 
-       list = append(list, p.parseExpr())
+       list = append(list, p.parseExpr(lhs))
        for p.tok == token.COMMA {
                p.next()
-               list = append(list, p.parseExpr())
+               list = append(list, p.parseExpr(lhs))
        }
 
        return
 }
 
 
+func (p *parser) parseLhsList() []ast.Expr {
+       list := p.parseExprList(true)
+       switch p.tok {
+       case token.DEFINE:
+               // lhs of a short variable declaration
+               p.shortVarDecl(p.makeIdentList(list))
+       case token.COLON:
+               // lhs of a label declaration or a communication clause of a select
+               // statement (parseLhsList is not called when parsing the case clause
+               // of a switch statement):
+               // - labels are declared by the caller of parseLhsList
+               // - for communication clauses, if there is a stand-alone identifier
+               //   followed by a colon, we have a syntax error; there is no need
+               //   to resolve the identifier in that case
+       default:
+               // identifiers must be declared elsewhere
+               for _, x := range list {
+                       p.resolve(x)
+               }
+       }
+       return list
+}
+
+
+func (p *parser) parseRhsList() []ast.Expr {
+       return p.parseExprList(false)
+}
+
+
 // ----------------------------------------------------------------------------
 // Types
 
@@ -458,31 +504,24 @@ func (p *parser) parseType() ast.Expr {
 }
 
 
-func (p *parser) parseQualifiedIdent() ast.Expr {
+// If the result is an identifier, it is not resolved.
+func (p *parser) parseTypeName() ast.Expr {
        if p.trace {
-               defer un(trace(p, "QualifiedIdent"))
+               defer un(trace(p, "TypeName"))
        }
 
        ident := p.parseIdent()
-       p.resolve(ident)
-       var x ast.Expr = ident
+       // don't resolve ident yet - it may be a parameter or field name
+
        if p.tok == token.PERIOD {
-               // first identifier is a package identifier
+               // ident is a package name
                p.next()
+               p.resolve(ident)
                sel := p.parseIdent()
-               x = &ast.SelectorExpr{x, sel}
+               return &ast.SelectorExpr{ident, sel}
        }
 
-       return x
-}
-
-
-func (p *parser) parseTypeName() ast.Expr {
-       if p.trace {
-               defer un(trace(p, "TypeName"))
-       }
-
-       return p.parseQualifiedIdent()
+       return ident
 }
 
 
@@ -497,7 +536,7 @@ func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr {
                len = &ast.Ellipsis{p.pos, nil}
                p.next()
        } else if p.tok != token.RBRACK {
-               len = p.parseExpr()
+               len = p.parseRhs()
        }
        p.expect(token.RBRACK)
        elt := p.parseType()
@@ -521,7 +560,7 @@ func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident {
 }
 
 
-func (p *parser) parseFieldDecl() *ast.Field {
+func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
        if p.trace {
                defer un(trace(p, "FieldDecl"))
        }
@@ -534,7 +573,7 @@ func (p *parser) parseFieldDecl() *ast.Field {
        // optional tag
        var tag *ast.BasicLit
        if p.tok == token.STRING {
-               tag = &ast.BasicLit{p.pos, p.tok, p.lit()}
+               tag = &ast.BasicLit{p.pos, p.tok, p.lit}
                p.next()
        }
 
@@ -546,6 +585,7 @@ func (p *parser) parseFieldDecl() *ast.Field {
        } else {
                // ["*"] TypeName (AnonymousField)
                typ = list[0] // we always have at least one element
+               p.resolve(typ)
                if n := len(list); n > 1 || !isTypeName(deref(typ)) {
                        pos := typ.Pos()
                        p.errorExpected(pos, "anonymous field")
@@ -555,7 +595,10 @@ func (p *parser) parseFieldDecl() *ast.Field {
 
        p.expectSemi() // call before accessing p.linecomment
 
-       return &ast.Field{doc, idents, typ, tag, p.lineComment}
+       field := &ast.Field{doc, idents, typ, tag, p.lineComment}
+       p.declare(field, scope, ast.Var, idents...)
+
+       return field
 }
 
 
@@ -566,15 +609,17 @@ func (p *parser) parseStructType() *ast.StructType {
 
        pos := p.expect(token.STRUCT)
        lbrace := p.expect(token.LBRACE)
+       scope := ast.NewScope(nil) // struct scope
        var list []*ast.Field
        for p.tok == token.IDENT || p.tok == token.MUL || p.tok == token.LPAREN {
                // a field declaration cannot start with a '(' but we accept
                // it here for more robust parsing and better error messages
                // (parseFieldDecl will check and complain if necessary)
-               list = append(list, p.parseFieldDecl())
+               list = append(list, p.parseFieldDecl(scope))
        }
        rbrace := p.expect(token.RBRACE)
 
+       // TODO(gri): store struct scope in AST
        return &ast.StructType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
 }
 
@@ -595,7 +640,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
        if isParam && p.tok == token.ELLIPSIS {
                pos := p.pos
                p.next()
-               typ := p.tryType() // don't use parseType so we can provide better error message
+               typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message
                if typ == nil {
                        p.error(pos, "'...' parameter is missing type")
                        typ = &ast.BadExpr{pos, p.pos}
@@ -605,7 +650,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
                }
                return &ast.Ellipsis{pos, typ}
        }
-       return p.tryType()
+       return p.tryIdentOrType(false)
 }
 
 
@@ -641,6 +686,9 @@ func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
 
        // if we had a list of identifiers, it must be followed by a type
        typ = p.tryVarType(isParam)
+       if typ != nil {
+               p.resolve(typ)
+       }
 
        return
 }
@@ -682,6 +730,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
                // Type { "," Type } (anonymous parameters)
                params = make([]*ast.Field, len(list))
                for i, x := range list {
+                       p.resolve(x)
                        params[i] = &ast.Field{Type: x}
                }
        }
@@ -751,7 +800,7 @@ func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) {
 }
 
 
-func (p *parser) parseMethodSpec() *ast.Field {
+func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {
        if p.trace {
                defer un(trace(p, "MethodSpec"))
        }
@@ -759,7 +808,7 @@ func (p *parser) parseMethodSpec() *ast.Field {
        doc := p.leadComment
        var idents []*ast.Ident
        var typ ast.Expr
-       x := p.parseQualifiedIdent()
+       x := p.parseTypeName()
        if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN {
                // method
                idents = []*ast.Ident{ident}
@@ -772,7 +821,10 @@ func (p *parser) parseMethodSpec() *ast.Field {
        }
        p.expectSemi() // call before accessing p.linecomment
 
-       return &ast.Field{doc, idents, typ, nil, p.lineComment}
+       spec := &ast.Field{doc, idents, typ, nil, p.lineComment}
+       p.declare(spec, scope, ast.Fun, idents...)
+
+       return spec
 }
 
 
@@ -783,12 +835,14 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
 
        pos := p.expect(token.INTERFACE)
        lbrace := p.expect(token.LBRACE)
+       scope := ast.NewScope(nil) // interface scope
        var list []*ast.Field
        for p.tok == token.IDENT {
-               list = append(list, p.parseMethodSpec())
+               list = append(list, p.parseMethodSpec(scope))
        }
        rbrace := p.expect(token.RBRACE)
 
+       // TODO(gri): store interface scope in AST
        return &ast.InterfaceType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
 }
 
@@ -832,7 +886,8 @@ func (p *parser) parseChanType() *ast.ChanType {
 }
 
 
-func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
+// If the result is an identifier, it is not resolved.
+func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr {
        switch p.tok {
        case token.IDENT:
                return p.parseTypeName()
@@ -864,7 +919,13 @@ func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
 }
 
 
-func (p *parser) tryType() ast.Expr { return p.tryRawType(false) }
+func (p *parser) tryType() ast.Expr {
+       typ := p.tryIdentOrType(false)
+       if typ != nil {
+               p.resolve(typ)
+       }
+       return typ
+}
 
 
 // ----------------------------------------------------------------------------
@@ -939,20 +1000,23 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr {
 
 // parseOperand may return an expression or a raw type (incl. array
 // types of the form [...]T. Callers must verify the result.
+// If lhs is set and the result is an identifier, it is not resolved.
 //
-func (p *parser) parseOperand() ast.Expr {
+func (p *parser) parseOperand(lhs bool) ast.Expr {
        if p.trace {
                defer un(trace(p, "Operand"))
        }
 
        switch p.tok {
        case token.IDENT:
-               ident := p.parseIdent()
-               p.resolve(ident)
-               return ident
+               x := p.parseIdent()
+               if !lhs {
+                       p.resolve(x)
+               }
+               return x
 
        case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING:
-               x := &ast.BasicLit{p.pos, p.tok, p.lit()}
+               x := &ast.BasicLit{p.pos, p.tok, p.lit}
                p.next()
                return x
 
@@ -960,7 +1024,7 @@ func (p *parser) parseOperand() ast.Expr {
                lparen := p.pos
                p.next()
                p.exprLev++
-               x := p.parseExpr()
+               x := p.parseRhs()
                p.exprLev--
                rparen := p.expect(token.RPAREN)
                return &ast.ParenExpr{lparen, x, rparen}
@@ -969,9 +1033,11 @@ func (p *parser) parseOperand() ast.Expr {
                return p.parseFuncTypeOrLit()
 
        default:
-               t := p.tryRawType(true) // could be type for composite literal or conversion
-               if t != nil {
-                       return t
+               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
                }
        }
 
@@ -982,19 +1048,22 @@ func (p *parser) parseOperand() ast.Expr {
 }
 
 
-func (p *parser) parseSelectorOrTypeAssertion(x ast.Expr) ast.Expr {
+func (p *parser) parseSelector(x ast.Expr) ast.Expr {
        if p.trace {
-               defer un(trace(p, "SelectorOrTypeAssertion"))
+               defer un(trace(p, "Selector"))
        }
 
-       p.expect(token.PERIOD)
-       if p.tok == token.IDENT {
-               // selector
-               sel := p.parseIdent()
-               return &ast.SelectorExpr{x, sel}
+       sel := p.parseIdent()
+
+       return &ast.SelectorExpr{x, sel}
+}
+
+
+func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr {
+       if p.trace {
+               defer un(trace(p, "TypeAssertion"))
        }
 
-       // type assertion
        p.expect(token.LPAREN)
        var typ ast.Expr
        if p.tok == token.TYPE {
@@ -1019,13 +1088,13 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
        var low, high ast.Expr
        isSlice := false
        if p.tok != token.COLON {
-               low = p.parseExpr()
+               low = p.parseRhs()
        }
        if p.tok == token.COLON {
                isSlice = true
                p.next()
                if p.tok != token.RBRACK {
-                       high = p.parseExpr()
+                       high = p.parseRhs()
                }
        }
        p.exprLev--
@@ -1048,7 +1117,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
        var list []ast.Expr
        var ellipsis token.Pos
        for p.tok != token.RPAREN && p.tok != token.EOF && !ellipsis.IsValid() {
-               list = append(list, p.parseExpr())
+               list = append(list, p.parseRhs())
                if p.tok == token.ELLIPSIS {
                        ellipsis = p.pos
                        p.next()
@@ -1074,12 +1143,16 @@ func (p *parser) parseElement(keyOk bool) ast.Expr {
                return p.parseLiteralValue(nil)
        }
 
-       x := p.parseExpr()
-       if keyOk && p.tok == token.COLON {
-               colon := p.pos
-               p.next()
-               x = &ast.KeyValueExpr{x, colon, p.parseElement(false)}
+       x := p.parseExpr(keyOk) // don't resolve if map key
+       if keyOk {
+               if p.tok == token.COLON {
+                       colon := p.pos
+                       p.next()
+                       return &ast.KeyValueExpr{x, colon, p.parseElement(false)}
+               }
+               p.resolve(x) // not a map key
        }
+
        return x
 }
 
@@ -1231,23 +1304,47 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
 }
 
 
-func (p *parser) parsePrimaryExpr() ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parsePrimaryExpr(lhs bool) ast.Expr {
        if p.trace {
                defer un(trace(p, "PrimaryExpr"))
        }
 
-       x := p.parseOperand()
+       x := p.parseOperand(lhs)
 L:
        for {
                switch p.tok {
                case token.PERIOD:
-                       x = p.parseSelectorOrTypeAssertion(p.checkExpr(x))
+                       p.next()
+                       if lhs {
+                               p.resolve(x)
+                       }
+                       switch p.tok {
+                       case token.IDENT:
+                               x = p.parseSelector(p.checkExpr(x))
+                       case token.LPAREN:
+                               x = p.parseTypeAssertion(p.checkExpr(x))
+                       default:
+                               pos := p.pos
+                               p.next() // make progress
+                               p.errorExpected(pos, "selector or type assertion")
+                               x = &ast.BadExpr{pos, p.pos}
+                       }
                case token.LBRACK:
+                       if lhs {
+                               p.resolve(x)
+                       }
                        x = p.parseIndexOrSlice(p.checkExpr(x))
                case token.LPAREN:
+                       if lhs {
+                               p.resolve(x)
+                       }
                        x = p.parseCallOrConversion(p.checkExprOrType(x))
                case token.LBRACE:
                        if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
+                               if lhs {
+                                       p.resolve(x)
+                               }
                                x = p.parseLiteralValue(x)
                        } else {
                                break L
@@ -1255,13 +1352,15 @@ L:
                default:
                        break L
                }
+               lhs = false // no need to try to resolve again
        }
 
        return x
 }
 
 
-func (p *parser) parseUnaryExpr() ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parseUnaryExpr(lhs bool) ast.Expr {
        if p.trace {
                defer un(trace(p, "UnaryExpr"))
        }
@@ -1270,7 +1369,7 @@ func (p *parser) parseUnaryExpr() ast.Expr {
        case token.ADD, token.SUB, token.NOT, token.XOR, token.AND, token.RANGE:
                pos, op := p.pos, p.tok
                p.next()
-               x := p.parseUnaryExpr()
+               x := p.parseUnaryExpr(false)
                return &ast.UnaryExpr{pos, op, p.checkExpr(x)}
 
        case token.ARROW:
@@ -1283,32 +1382,37 @@ func (p *parser) parseUnaryExpr() ast.Expr {
                        return &ast.ChanType{pos, ast.RECV, value}
                }
 
-               x := p.parseUnaryExpr()
+               x := p.parseUnaryExpr(false)
                return &ast.UnaryExpr{pos, token.ARROW, p.checkExpr(x)}
 
        case token.MUL:
                // pointer type or unary "*" expression
                pos := p.pos
                p.next()
-               x := p.parseUnaryExpr()
+               x := p.parseUnaryExpr(false)
                return &ast.StarExpr{pos, p.checkExprOrType(x)}
        }
 
-       return p.parsePrimaryExpr()
+       return p.parsePrimaryExpr(lhs)
 }
 
 
-func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr {
        if p.trace {
                defer un(trace(p, "BinaryExpr"))
        }
 
-       x := p.parseUnaryExpr()
+       x := p.parseUnaryExpr(lhs)
        for prec := p.tok.Precedence(); prec >= prec1; prec-- {
                for p.tok.Precedence() == prec {
                        pos, op := p.pos, p.tok
                        p.next()
-                       y := p.parseBinaryExpr(prec + 1)
+                       if lhs {
+                               p.resolve(x)
+                               lhs = false
+                       }
+                       y := p.parseBinaryExpr(false, prec+1)
                        x = &ast.BinaryExpr{p.checkExpr(x), pos, op, p.checkExpr(y)}
                }
        }
@@ -1317,14 +1421,20 @@ func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
 }
 
 
+// If lhs is set and the result is an identifier, it is not resolved.
 // TODO(gri): parseExpr may return a type or even a raw type ([..]int) -
 //            should reject when a type/raw type is obviously not allowed
-func (p *parser) parseExpr() ast.Expr {
+func (p *parser) parseExpr(lhs bool) ast.Expr {
        if p.trace {
                defer un(trace(p, "Expression"))
        }
 
-       return p.parseBinaryExpr(token.LowestPrec + 1)
+       return p.parseBinaryExpr(lhs, token.LowestPrec+1)
+}
+
+
+func (p *parser) parseRhs() ast.Expr {
+       return p.parseExpr(false)
 }
 
 
@@ -1336,7 +1446,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
                defer un(trace(p, "SimpleStmt"))
        }
 
-       x := p.parseExprList()
+       x := p.parseLhsList()
 
        switch p.tok {
        case
@@ -1347,10 +1457,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
                // assignment statement
                pos, tok := p.pos, p.tok
                p.next()
-               y := p.parseExprList()
-               if tok == token.DEFINE {
-                       p.shortVarDecl(p.makeIdentList(x))
-               }
+               y := p.parseRhsList()
                return &ast.AssignStmt{x, pos, tok, y}
        }
 
@@ -1379,7 +1486,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
                // send statement
                arrow := p.pos
                p.next() // consume "<-"
-               y := p.parseExpr()
+               y := p.parseRhs()
                return &ast.SendStmt{x[0], arrow, y}
 
        case token.INC, token.DEC:
@@ -1395,7 +1502,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
 
 
 func (p *parser) parseCallExpr() *ast.CallExpr {
-       x := p.parseExpr()
+       x := p.parseRhs()
        if call, isCall := x.(*ast.CallExpr); isCall {
                return call
        }
@@ -1445,7 +1552,7 @@ func (p *parser) parseReturnStmt() *ast.ReturnStmt {
        p.expect(token.RETURN)
        var x []ast.Expr
        if p.tok != token.SEMICOLON && p.tok != token.RBRACE {
-               x = p.parseExprList()
+               x = p.parseRhsList()
        }
        p.expectSemi()
 
@@ -1500,12 +1607,12 @@ func (p *parser) parseIfStmt() *ast.IfStmt {
                p.exprLev = -1
                if p.tok == token.SEMICOLON {
                        p.next()
-                       x = p.parseExpr()
+                       x = p.parseRhs()
                } else {
                        s = p.parseSimpleStmt(false)
                        if p.tok == token.SEMICOLON {
                                p.next()
-                               x = p.parseExpr()
+                               x = p.parseRhs()
                        } else {
                                x = p.makeExpr(s)
                                s = nil
@@ -1552,7 +1659,7 @@ func (p *parser) parseCaseClause(exprSwitch bool) *ast.CaseClause {
        if p.tok == token.CASE {
                p.next()
                if exprSwitch {
-                       list = p.parseExprList()
+                       list = p.parseRhsList()
                } else {
                        list = p.parseTypeList()
                }
@@ -1639,7 +1746,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
        var comm ast.Stmt
        if p.tok == token.CASE {
                p.next()
-               lhs := p.parseExprList()
+               lhs := p.parseLhsList()
                if p.tok == token.ARROW {
                        // SendStmt
                        if len(lhs) > 1 {
@@ -1648,7 +1755,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
                        }
                        arrow := p.pos
                        p.next()
-                       rhs := p.parseExpr()
+                       rhs := p.parseRhs()
                        comm = &ast.SendStmt{lhs[0], arrow, rhs}
                } else {
                        // RecvStmt
@@ -1663,10 +1770,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
                                        lhs = lhs[0:2]
                                }
                                p.next()
-                               rhs = p.parseExpr()
-                               if tok == token.DEFINE {
-                                       p.shortVarDecl(p.makeIdentList(lhs))
-                               }
+                               rhs = p.parseRhs()
                        } else {
                                // rhs must be single receive operation
                                if len(lhs) > 1 {
@@ -1866,14 +1970,18 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
 
        var path *ast.BasicLit
        if p.tok == token.STRING {
-               path = &ast.BasicLit{p.pos, p.tok, p.lit()}
+               path = &ast.BasicLit{p.pos, p.tok, p.lit}
                p.next()
        } else {
                p.expect(token.STRING) // use expect() error handling
        }
        p.expectSemi() // call before accessing p.linecomment
 
-       return &ast.ImportSpec{doc, ident, path, p.lineComment}
+       // collect imports
+       spec := &ast.ImportSpec{doc, ident, path, p.lineComment}
+       p.imports = append(p.imports, spec)
+
+       return spec
 }
 
 
@@ -1887,7 +1995,7 @@ func parseConstSpec(p *parser, doc *ast.CommentGroup, iota int) ast.Spec {
        var values []ast.Expr
        if typ != nil || p.tok == token.ASSIGN || iota == 0 {
                p.expect(token.ASSIGN)
-               values = p.parseExprList()
+               values = p.parseRhsList()
        }
        p.expectSemi() // call before accessing p.linecomment
 
@@ -1932,7 +2040,7 @@ func parseVarSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
        var values []ast.Expr
        if typ == nil || p.tok == token.ASSIGN {
                p.expect(token.ASSIGN)
-               values = p.parseExprList()
+               values = p.parseRhsList()
        }
        p.expectSemi() // call before accessing p.linecomment
 
@@ -2120,20 +2228,20 @@ func (p *parser) parseFile() *ast.File {
                }
        }
 
-       if p.topScope != p.pkgScope {
-               panic("internal error: imbalanced scopes")
-       }
+       assert(p.topScope == p.pkgScope, "imbalanced scopes")
 
        // resolve global identifiers within the same file
        i := 0
        for _, ident := range p.unresolved {
                // i <= index for current ident
-               ident.Obj = p.pkgScope.Lookup(ident.Name)
+               assert(ident.Obj == unresolved, "object already resolved")
+               ident.Obj = p.pkgScope.Lookup(ident.Name) // also removes unresolved sentinel
                if ident.Obj == nil {
                        p.unresolved[i] = ident
                        i++
                }
        }
 
-       return &ast.File{doc, pos, ident, decls, p.pkgScope, p.unresolved[0:i], p.comments}
+       // TODO(gri): store p.imports in AST
+       return &ast.File{doc, pos, ident, decls, p.pkgScope, p.imports, p.unresolved[0:i], p.comments}
 }
index 2238b6b..0b3b662 100644 (file)
@@ -160,19 +160,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
        // the first linebreak is always a formfeed since this section must not
        // depend on any previous formatting
        prevBreak := -1 // index of last expression that was followed by a linebreak
-       linebreakMin := 1
-       if mode&periodSep != 0 {
-               // Make fragments like
-               //
-               // a.Bar(1,
-               //   2).Foo
-               //
-               // format correctly (a linebreak shouldn't be added before Foo) when
-               // doing period-separated expr lists by setting minimum linebreak to 0
-               // lines for them.
-               linebreakMin = 0
-       }
-       if prev.IsValid() && prev.Line < line && p.linebreak(line, linebreakMin, ws, true) {
+       if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) {
                ws = ignore
                *multiLine = true
                prevBreak = 0
@@ -237,7 +225,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
                                // lines are broken using newlines so comments remain aligned
                                // unless forceFF is set or there are multiple expressions on
                                // the same line in which case formfeed is used
-                               if p.linebreak(line, linebreakMin, ws, useFF || prevBreak+1 < i) {
+                               if p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) {
                                        ws = ignore
                                        *multiLine = true
                                        prevBreak = i
@@ -363,7 +351,7 @@ func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
 
 
 func (p *printer) setLineComment(text string) {
-       p.setComment(&ast.CommentGroup{[]*ast.Comment{&ast.Comment{token.NoPos, []byte(text)}}})
+       p.setComment(&ast.CommentGroup{[]*ast.Comment{&ast.Comment{token.NoPos, text}}})
 }
 
 
@@ -527,7 +515,7 @@ func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
                }
 
        case *ast.StarExpr:
-               if e.Op.String() == "/" {
+               if e.Op == token.QUO { // `*/`
                        maxProblem = 5
                }
 
index a43e4a1..697a83f 100644 (file)
@@ -34,12 +34,6 @@ const (
 )
 
 
-const (
-       esc2 = '\xfe'                        // an escape byte that cannot occur in regular UTF-8
-       _    = 1 / (esc2 - tabwriter.Escape) // cause compiler error if esc2 == tabwriter.Escape
-)
-
-
 var (
        esc       = []byte{tabwriter.Escape}
        htab      = []byte{'\t'}
@@ -81,8 +75,9 @@ type printer struct {
        mode    pmode       // current printer mode
        lastTok token.Token // the last token printed (token.ILLEGAL if it's whitespace)
 
-       // Buffered whitespace
-       buffer []whiteSpace
+       // Reused buffers
+       wsbuf  []whiteSpace // delayed white space
+       litbuf bytes.Buffer // for creation of escaped literals and comments
 
        // The (possibly estimated) position in the generated output;
        // in AST space (i.e., pos is set whenever a token position is
@@ -109,7 +104,7 @@ func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet, nodeS
        p.Config = *cfg
        p.fset = fset
        p.errors = make(chan os.Error)
-       p.buffer = make([]whiteSpace, 0, 16) // whitespace sequences are short
+       p.wsbuf = make([]whiteSpace, 0, 16) // whitespace sequences are short
        p.nodeSizes = nodeSizes
 }
 
@@ -123,6 +118,20 @@ func (p *printer) internalError(msg ...interface{}) {
 }
 
 
+// escape escapes string s by bracketing it with tabwriter.Escape.
+// Escaped strings pass through tabwriter unchanged. (Note that
+// valid Go programs cannot contain tabwriter.Escape bytes since
+// they do not appear in legal UTF-8 sequences).
+//
+func (p *printer) escape(s string) string {
+       p.litbuf.Reset()
+       p.litbuf.WriteByte(tabwriter.Escape)
+       p.litbuf.WriteString(s)
+       p.litbuf.WriteByte(tabwriter.Escape)
+       return p.litbuf.String()
+}
+
+
 // nlines returns the adjusted number of linebreaks given the desired number
 // of breaks n such that min <= result <= max where max depends on the current
 // nesting level.
@@ -230,7 +239,7 @@ func (p *printer) writeNewlines(n int, useFF bool) {
 // source text. writeItem updates p.last to the position immediately following
 // the data.
 //
-func (p *printer) writeItem(pos token.Position, data []byte) {
+func (p *printer) writeItem(pos token.Position, data string) {
        if pos.IsValid() {
                // continue with previous position if we don't have a valid pos
                if p.last.IsValid() && p.last.Filename != pos.Filename {
@@ -239,7 +248,7 @@ func (p *printer) writeItem(pos token.Position, data []byte) {
                        // e.g., the result of ast.MergePackageFiles)
                        p.indent = 0
                        p.mode = 0
-                       p.buffer = p.buffer[0:0]
+                       p.wsbuf = p.wsbuf[0:0]
                }
                p.pos = pos
        }
@@ -248,7 +257,7 @@ func (p *printer) writeItem(pos token.Position, data []byte) {
                _, filename := filepath.Split(pos.Filename)
                p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, pos.Column)))
        }
-       p.write(data)
+       p.write([]byte(data))
        p.last = p.pos
 }
 
@@ -280,11 +289,11 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
                if prev == nil {
                        // first comment of a comment group
                        j := 0
-                       for i, ch := range p.buffer {
+                       for i, ch := range p.wsbuf {
                                switch ch {
                                case blank:
                                        // ignore any blanks before a comment
-                                       p.buffer[i] = ignore
+                                       p.wsbuf[i] = ignore
                                        continue
                                case vtab:
                                        // respect existing tabs - important
@@ -318,11 +327,11 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
                if prev == nil {
                        // first comment of a comment group
                        j := 0
-                       for i, ch := range p.buffer {
+                       for i, ch := range p.wsbuf {
                                switch ch {
                                case blank, vtab:
                                        // ignore any horizontal whitespace before line breaks
-                                       p.buffer[i] = ignore
+                                       p.wsbuf[i] = ignore
                                        continue
                                case indent:
                                        // apply pending indentation
@@ -339,7 +348,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
                                        }
                                case newline, formfeed:
                                        // TODO(gri): may want to keep formfeed info in some cases
-                                       p.buffer[i] = ignore
+                                       p.wsbuf[i] = ignore
                                }
                                j = i
                                break
@@ -360,12 +369,8 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
 }
 
 
-func (p *printer) writeCommentLine(comment *ast.Comment, pos token.Position, line []byte) {
-       // line must pass through unchanged, bracket it with tabwriter.Escape
-       line = bytes.Join([][]byte{esc, line, esc}, nil)
-       p.writeItem(pos, line)
-}
-
+// TODO(gri): It should be possible to convert the code below from using
+//            []byte to string and in the process eliminate some conversions.
 
 // Split comment text into lines
 func split(text []byte) [][]byte {
@@ -546,13 +551,13 @@ func (p *printer) writeComment(comment *ast.Comment) {
 
        // shortcut common case of //-style comments
        if text[1] == '/' {
-               p.writeCommentLine(comment, p.fset.Position(comment.Pos()), text)
+               p.writeItem(p.fset.Position(comment.Pos()), p.escape(text))
                return
        }
 
        // for /*-style comments, print line by line and let the
        // write function take care of the proper indentation
-       lines := split(text)
+       lines := split([]byte(text))
        stripCommonPrefix(lines)
 
        // write comment lines, separated by formfeed,
@@ -565,7 +570,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
                        pos = p.pos
                }
                if len(line) > 0 {
-                       p.writeCommentLine(comment, pos, line)
+                       p.writeItem(pos, p.escape(string(line)))
                }
        }
 }
@@ -578,11 +583,11 @@ func (p *printer) writeComment(comment *ast.Comment) {
 // formfeed was dropped from the whitespace buffer.
 //
 func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
-       for i, ch := range p.buffer {
+       for i, ch := range p.wsbuf {
                switch ch {
                case blank, vtab:
                        // ignore trailing whitespace
-                       p.buffer[i] = ignore
+                       p.wsbuf[i] = ignore
                case indent, unindent:
                        // don't loose indentation information
                case newline, formfeed:
@@ -594,11 +599,11 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
                                if ch == formfeed {
                                        droppedFF = true
                                }
-                               p.buffer[i] = ignore
+                               p.wsbuf[i] = ignore
                        }
                }
        }
-       p.writeWhitespace(len(p.buffer))
+       p.writeWhitespace(len(p.wsbuf))
 
        // make sure we have a line break
        if needsLinebreak {
@@ -652,7 +657,7 @@ func (p *printer) writeWhitespace(n int) {
        // write entries
        var data [1]byte
        for i := 0; i < n; i++ {
-               switch ch := p.buffer[i]; ch {
+               switch ch := p.wsbuf[i]; ch {
                case ignore:
                        // ignore!
                case indent:
@@ -670,13 +675,13 @@ func (p *printer) writeWhitespace(n int) {
                        // the line break and the label, the unindent is not
                        // part of the comment whitespace prefix and the comment
                        // will be positioned correctly indented.
-                       if i+1 < n && p.buffer[i+1] == unindent {
+                       if i+1 < n && p.wsbuf[i+1] == unindent {
                                // Use a formfeed to terminate the current section.
                                // Otherwise, a long label name on the next line leading
                                // to a wide column may increase the indentation column
                                // of lines before the label; effectively leading to wrong
                                // indentation.
-                               p.buffer[i], p.buffer[i+1] = unindent, formfeed
+                               p.wsbuf[i], p.wsbuf[i+1] = unindent, formfeed
                                i-- // do it again
                                continue
                        }
@@ -689,11 +694,11 @@ func (p *printer) writeWhitespace(n int) {
 
        // shift remaining entries down
        i := 0
-       for ; n < len(p.buffer); n++ {
-               p.buffer[i] = p.buffer[n]
+       for ; n < len(p.wsbuf); n++ {
+               p.wsbuf[i] = p.wsbuf[n]
                i++
        }
-       p.buffer = p.buffer[0:i]
+       p.wsbuf = p.wsbuf[0:i]
 }
 
 
@@ -734,7 +739,7 @@ func mayCombine(prev token.Token, next byte) (b bool) {
 func (p *printer) print(args ...interface{}) {
        for _, f := range args {
                next := p.pos // estimated position of next item
-               var data []byte
+               var data string
                var tok token.Token
 
                switch x := f.(type) {
@@ -748,42 +753,22 @@ func (p *printer) print(args ...interface{}) {
                                // LabeledStmt)
                                break
                        }
-                       i := len(p.buffer)
-                       if i == cap(p.buffer) {
+                       i := len(p.wsbuf)
+                       if i == cap(p.wsbuf) {
                                // Whitespace sequences are very short so this should
                                // never happen. Handle gracefully (but possibly with
                                // bad comment placement) if it does happen.
                                p.writeWhitespace(i)
                                i = 0
                        }
-                       p.buffer = p.buffer[0 : i+1]
-                       p.buffer[i] = x
+                       p.wsbuf = p.wsbuf[0 : i+1]
+                       p.wsbuf[i] = x
                case *ast.Ident:
-                       data = []byte(x.Name)
+                       data = x.Name
                        tok = token.IDENT
                case *ast.BasicLit:
-                       // escape all literals so they pass through unchanged
-                       // (note that valid Go programs cannot contain
-                       // tabwriter.Escape bytes since they do not appear in
-                       // legal UTF-8 sequences)
-                       data = make([]byte, 0, len(x.Value)+2)
-                       data = append(data, tabwriter.Escape)
-                       data = append(data, x.Value...)
-                       data = append(data, tabwriter.Escape)
+                       data = p.escape(x.Value)
                        tok = x.Kind
-                       // If we have a raw string that spans multiple lines and
-                       // the opening quote (`) is on a line preceded only by
-                       // indentation, we don't want to write that indentation
-                       // because the following lines of the raw string are not
-                       // indented. It's easiest to correct the output at the end
-                       // via the trimmer (because of the complex handling of
-                       // white space).
-                       // Mark multi-line raw strings by replacing the opening
-                       // quote with esc2 and have the trimmer take care of fixing
-                       // it up. (Do this _after_ making a copy of data!)
-                       if data[1] == '`' && bytes.IndexByte(data, '\n') > 0 {
-                               data[1] = esc2
-                       }
                case token.Token:
                        s := x.String()
                        if mayCombine(p.lastTok, s[0]) {
@@ -793,13 +778,13 @@ func (p *printer) print(args ...interface{}) {
                                // (except for token.INT followed by a '.' this
                                // should never happen because it is taken care
                                // of via binary expression formatting)
-                               if len(p.buffer) != 0 {
+                               if len(p.wsbuf) != 0 {
                                        p.internalError("whitespace buffer not empty")
                                }
-                               p.buffer = p.buffer[0:1]
-                               p.buffer[0] = ' '
+                               p.wsbuf = p.wsbuf[0:1]
+                               p.wsbuf[0] = ' '
                        }
-                       data = []byte(s)
+                       data = s
                        tok = x
                case token.Pos:
                        if x.IsValid() {
@@ -813,7 +798,7 @@ func (p *printer) print(args ...interface{}) {
                p.lastTok = tok
                p.pos = next
 
-               if data != nil {
+               if data != "" {
                        droppedFF := p.flush(next, tok)
 
                        // intersperse extra newlines if present in the source
@@ -848,7 +833,7 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) {
                droppedFF = p.intersperseComments(next, tok)
        } else {
                // otherwise, write any leftover whitespace
-               p.writeWhitespace(len(p.buffer))
+               p.writeWhitespace(len(p.wsbuf))
        }
        return
 }
@@ -864,10 +849,9 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) {
 // through unchanged.
 //
 type trimmer struct {
-       output  io.Writer
-       state   int
-       space   bytes.Buffer
-       hasText bool
+       output io.Writer
+       state  int
+       space  bytes.Buffer
 }
 
 
@@ -875,15 +859,11 @@ type trimmer struct {
 // It can be in one of the following states:
 const (
        inSpace  = iota // inside space
-       atEscape        // inside space and the last char was an opening tabwriter.Escape
        inEscape        // inside text bracketed by tabwriter.Escapes
        inText          // inside text
 )
 
 
-var backquote = []byte{'`'}
-
-
 // Design note: It is tempting to eliminate extra blanks occurring in
 //              whitespace in this function as it could simplify some
 //              of the blanks logic in the node printing functions.
@@ -892,9 +872,8 @@ var backquote = []byte{'`'}
 
 func (p *trimmer) Write(data []byte) (n int, err os.Error) {
        // invariants:
-       // p.state == inSpace, atEscape:
+       // p.state == inSpace:
        //      p.space is unwritten
-       //      p.hasText indicates if there is any text on this line
        // p.state == inEscape, inText:
        //      data[m:n] is unwritten
        m := 0
@@ -911,32 +890,20 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) {
                        case '\n', '\f':
                                p.space.Reset()                        // discard trailing space
                                _, err = p.output.Write(newlines[0:1]) // write newline
-                               p.hasText = false
                        case tabwriter.Escape:
-                               p.state = atEscape
+                               _, err = p.output.Write(p.space.Bytes())
+                               p.state = inEscape
+                               m = n + 1 // +1: skip tabwriter.Escape
                        default:
                                _, err = p.output.Write(p.space.Bytes())
                                p.state = inText
                                m = n
                        }
-               case atEscape:
-                       // discard indentation if we have a multi-line raw string
-                       // (see printer.print for details)
-                       if b != esc2 || p.hasText {
-                               _, err = p.output.Write(p.space.Bytes())
-                       }
-                       p.state = inEscape
-                       m = n
-                       if b == esc2 {
-                               _, err = p.output.Write(backquote) // convert back
-                               m++
-                       }
                case inEscape:
                        if b == tabwriter.Escape {
                                _, err = p.output.Write(data[m:n])
                                p.state = inSpace
                                p.space.Reset()
-                               p.hasText = true
                        }
                case inText:
                        switch b {
@@ -945,19 +912,18 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) {
                                p.state = inSpace
                                p.space.Reset()
                                p.space.WriteByte(b) // WriteByte returns no errors
-                               p.hasText = true
                        case '\n', '\f':
                                _, err = p.output.Write(data[m:n])
                                p.state = inSpace
                                p.space.Reset()
                                _, err = p.output.Write(newlines[0:1]) // write newline
-                               p.hasText = false
                        case tabwriter.Escape:
                                _, err = p.output.Write(data[m:n])
-                               p.state = atEscape
-                               p.space.Reset()
-                               p.hasText = true
+                               p.state = inEscape
+                               m = n + 1 // +1: skip tabwriter.Escape
                        }
+               default:
+                       panic("unreachable")
                }
                if err != nil {
                        return
@@ -970,7 +936,6 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) {
                _, err = p.output.Write(data[m:n])
                p.state = inSpace
                p.space.Reset()
-               p.hasText = true
        }
 
        return
index 3ff087e..090f92a 100644 (file)
@@ -114,7 +114,7 @@ func check(t *testing.T, source, golden string, mode checkMode) {
        // start a timer to produce a time-out signal
        tc := make(chan int)
        go func() {
-               time.Sleep(20e9) // plenty of a safety margin, even for very slow machines
+               time.Sleep(10e9) // plenty of a safety margin, even for very slow machines
                tc <- 0
        }()
 
@@ -156,12 +156,15 @@ var data = []entry{
 
 
 func TestFiles(t *testing.T) {
-       for _, e := range data {
+       for i, e := range data {
                source := filepath.Join(dataDir, e.source)
                golden := filepath.Join(dataDir, e.golden)
                check(t, source, golden, e.mode)
                // TODO(gri) check that golden is idempotent
-               //check(t, golden, golden, e.mode);
+               //check(t, golden, golden, e.mode)
+               if testing.Short() && i >= 3 {
+                       break
+               }
        }
 }
 
index 314d321..c1a7e97 100644 (file)
@@ -253,8 +253,8 @@ bar`
        var _ = ``
        var _ = `foo`
        var _ =
-       // the next line should not be indented
-`foo
+       // the next line should remain indented
+       `foo
 bar`
 
        var _ = // comment
@@ -262,8 +262,8 @@ bar`
        var _ = // comment
        `foo`
        var _ = // comment
-       // the next line should not be indented
-`foo
+       // the next line should remain indented
+       `foo
 bar`
 
        var _ = /* comment */ ``
@@ -276,12 +276,12 @@ bar`
        var _ = /* comment */
        `foo`
        var _ = /* comment */
-       // the next line should not be indented
-`foo
+       // the next line should remain indented
+       `foo
 bar`
 
        var board = []int(
-`...........
+               `...........
 ...........
 ....●●●....
 ....●●●....
@@ -296,8 +296,8 @@ bar`
 
        var state = S{
                "foo",
-               // the next line should not be indented
-`...........
+               // the next line should remain indented
+               `...........
 ...........
 ....●●●....
 ....●●●....
@@ -619,3 +619,13 @@ func _() {
                b.(T).
                c
 }
+
+
+// Don't introduce extra newlines in strangely formatted expression lists.
+func f() {
+       // os.Open parameters should remain on two lines
+       if writer, err = os.Open(outfile, s.O_WRONLY|os.O_CREATE|
+               os.O_TRUNC,0666); err != nil {
+               log.Fatal(err)
+       }
+}
index cac22af..b873811 100644 (file)
@@ -256,7 +256,7 @@ var _ =
 var _ =
        `foo`
 var _ =
-       // the next line should not be indented
+       // the next line should remain indented
        `foo
 bar`
 
@@ -266,7 +266,7 @@ bar`
        var _ = // comment
                `foo`
        var _ = // comment
-               // the next line should not be indented
+               // the next line should remain indented
                `foo
 bar`
 
@@ -282,7 +282,7 @@ bar`
        var _ = /* comment */
                `foo`
        var _ = /* comment */
-               // the next line should not be indented
+               // the next line should remain indented
                `foo
 bar`
 
@@ -304,7 +304,7 @@ var board = []int(
 
        var state = S{
                "foo",
-               // the next line should not be indented
+               // the next line should remain indented
                `...........
 ...........
 ....●●●....
@@ -625,3 +625,13 @@ baz()
        (T).
        c
 }
+
+
+// Don't introduce extra newlines in strangely formatted expression lists.
+func f() {
+       // os.Open parameters should remain on two lines
+       if writer, err = os.Open(outfile, s.O_WRONLY|os.O_CREATE|
+               os.O_TRUNC, 0666); err != nil {
+           log.Fatal(err)
+       }
+}
index f22ceeb..735cd94 100644 (file)
@@ -239,7 +239,8 @@ func _() {
        _ = `foo
                bar`
        _ = `three spaces before the end of the line starting here:   
-they must not be removed`\f}
+they must not be removed`
+}
 
 
 func _() {
@@ -252,8 +253,8 @@ bar`
        var _ = ``
        var _ = `foo`
        var _ =
-       // the next line should not be indented
-`foo
+       // the next line should remain indented
+       `foo
 bar`
 
        var _ = // comment
@@ -261,8 +262,8 @@ bar`
        var _ = // comment
        `foo`
        var _ = // comment
-       // the next line should not be indented
-`foo
+       // the next line should remain indented
+       `foo
 bar`
 
        var _ = /* comment */ ``
@@ -275,12 +276,12 @@ bar`
        var _ = /* comment */
        `foo`
        var _ = /* comment */
-       // the next line should not be indented
-`foo
+       // the next line should remain indented
+       `foo
 bar`
 
        var board = []int(
-`...........
+               `...........
 ...........
 ....●●●....
 ....●●●....
@@ -295,8 +296,8 @@ bar`
 
        var state = S{
                "foo",
-               // the next line should not be indented
-`...........
+               // the next line should remain indented
+               `...........
 ...........
 ....●●●....
 ....●●●....
@@ -618,3 +619,13 @@ func _() {
                b.(T).
                c
 }
+
+
+// Don't introduce extra newlines in strangely formatted expression lists.
+func f() {
+       // os.Open parameters should remain on two lines
+       if writer, err = os.Open(outfile, s.O_WRONLY|os.O_CREATE|
+               os.O_TRUNC,0666); err != nil {
+               log.Fatal(err)
+       }
+}
index 59fed9d..2f949ad 100644 (file)
@@ -538,14 +538,12 @@ func (S *Scanner) switch4(tok0, tok1 token.Token, ch2 int, tok2, tok3 token.Toke
 }
 
 
-var newline = []byte{'\n'}
-
-// Scan scans the next token and returns the token position pos,
-// the token tok, and the literal text lit corresponding to the
+// Scan scans the next token and returns the token position,
+// the token, and the literal string corresponding to the
 // token. The source end is indicated by token.EOF.
 //
 // If the returned token is token.SEMICOLON, the corresponding
-// literal value is ";" if the semicolon was present in the source,
+// literal string is ";" if the semicolon was present in the source,
 // and "\n" if the semicolon was inserted because of a newline or
 // at EOF.
 //
@@ -560,7 +558,7 @@ var newline = []byte{'\n'}
 // set with Init. Token positions are relative to that file
 // and thus relative to the file set.
 //
-func (S *Scanner) Scan() (token.Pos, token.Token, []byte) {
+func (S *Scanner) Scan() (token.Pos, token.Token, string) {
 scanAgain:
        S.skipWhitespace()
 
@@ -586,7 +584,7 @@ scanAgain:
                case -1:
                        if S.insertSemi {
                                S.insertSemi = false // EOF consumed
-                               return S.file.Pos(offs), token.SEMICOLON, newline
+                               return S.file.Pos(offs), token.SEMICOLON, "\n"
                        }
                        tok = token.EOF
                case '\n':
@@ -594,7 +592,7 @@ scanAgain:
                        // set in the first place and exited early
                        // from S.skipWhitespace()
                        S.insertSemi = false // newline consumed
-                       return S.file.Pos(offs), token.SEMICOLON, newline
+                       return S.file.Pos(offs), token.SEMICOLON, "\n"
                case '"':
                        insertSemi = true
                        tok = token.STRING
@@ -662,7 +660,7 @@ scanAgain:
                                        S.offset = offs
                                        S.rdOffset = offs + 1
                                        S.insertSemi = false // newline consumed
-                                       return S.file.Pos(offs), token.SEMICOLON, newline
+                                       return S.file.Pos(offs), token.SEMICOLON, "\n"
                                }
                                S.scanComment()
                                if S.mode&ScanComments == 0 {
@@ -711,5 +709,9 @@ scanAgain:
        if S.mode&InsertSemis != 0 {
                S.insertSemi = insertSemi
        }
-       return S.file.Pos(offs), tok, S.src[offs:S.offset]
+
+       // TODO(gri): The scanner API should change such that the literal string
+       //            is only valid if an actual literal was scanned. This will
+       //            permit a more efficient implementation.
+       return S.file.Pos(offs), tok, string(S.src[offs:S.offset])
 }
index 93f3458..8afb00e 100644 (file)
@@ -234,12 +234,11 @@ func TestScan(t *testing.T) {
        index := 0
        epos := token.Position{"", 0, 1, 1} // expected position
        for {
-               pos, tok, litb := s.Scan()
+               pos, tok, lit := s.Scan()
                e := elt{token.EOF, "", special}
                if index < len(tokens) {
                        e = tokens[index]
                }
-               lit := string(litb)
                if tok == token.EOF {
                        lit = "<EOF>"
                        epos.Line = src_linecount
@@ -257,7 +256,7 @@ func TestScan(t *testing.T) {
                }
                epos.Offset += len(lit) + len(whitespace)
                epos.Line += newlineCount(lit) + whitespace_linecount
-               if tok == token.COMMENT && litb[1] == '/' {
+               if tok == token.COMMENT && lit[1] == '/' {
                        // correct for unaccounted '/n' in //-style comment
                        epos.Offset++
                        epos.Line++
@@ -292,7 +291,7 @@ func checkSemi(t *testing.T, line string, mode uint) {
                        semiPos.Column++
                        pos, tok, lit = S.Scan()
                        if tok == token.SEMICOLON {
-                               if string(lit) != semiLit {
+                               if lit != semiLit {
                                        t.Errorf(`bad literal for %q: got %q, expected %q`, line, lit, semiLit)
                                }
                                checkPos(t, line, pos, semiPos)
@@ -493,7 +492,7 @@ func TestLineComments(t *testing.T) {
        for _, s := range segments {
                p, _, lit := S.Scan()
                pos := file.Position(p)
-               checkPos(t, string(lit), p, token.Position{s.filename, pos.Offset, s.line, pos.Column})
+               checkPos(t, lit, p, token.Position{s.filename, pos.Offset, s.line, pos.Column})
        }
 
        if S.ErrorCount != 0 {
@@ -547,10 +546,10 @@ func TestIllegalChars(t *testing.T) {
        for offs, ch := range src {
                pos, tok, lit := s.Scan()
                if poffs := file.Offset(pos); poffs != offs {
-                       t.Errorf("bad position for %s: got %d, expected %d", string(lit), poffs, offs)
+                       t.Errorf("bad position for %s: got %d, expected %d", lit, poffs, offs)
                }
-               if tok == token.ILLEGAL && string(lit) != string(ch) {
-                       t.Errorf("bad token: got %s, expected %s", string(lit), string(ch))
+               if tok == token.ILLEGAL && lit != string(ch) {
+                       t.Errorf("bad token: got %s, expected %s", lit, string(ch))
                }
        }
 
index 2a2d3ec..a5f21df 100644 (file)
@@ -126,10 +126,7 @@ const (
 )
 
 
-// At the moment we have no array literal syntax that lets us describe
-// the index for each element - use a map for now to make sure they are
-// in sync.
-var tokens = map[Token]string{
+var tokens = [...]string{
        ILLEGAL: "ILLEGAL",
 
        EOF:     "EOF",
@@ -237,10 +234,14 @@ var tokens = map[Token]string{
 // constant name (e.g. for the token IDENT, the string is "IDENT").
 //
 func (tok Token) String() string {
-       if str, exists := tokens[tok]; exists {
-               return str
+       s := ""
+       if 0 <= tok && tok < Token(len(tokens)) {
+               s = tokens[tok]
        }
-       return "token(" + strconv.Itoa(int(tok)) + ")"
+       if s == "" {
+               s = "token(" + strconv.Itoa(int(tok)) + ")"
+       }
+       return s
 }
 
 
index bd24f4c..a4bee6e 100644 (file)
@@ -33,7 +33,7 @@ func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.ObjKind, name *ast
        //obj.N = n
        name.Obj = obj
        if name.Name != "_" {
-               if alt := scope.Insert(obj); alt != obj {
+               if alt := scope.Insert(obj); alt != nil {
                        tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, tc.fset.Position(alt.Pos()).String())
                }
        }
index 4fc5647..b5e695d 100644 (file)
@@ -53,7 +53,7 @@ func CheckPackage(fset *token.FileSet, pkg *ast.Package, importer Importer) os.E
 //
 func CheckFile(fset *token.FileSet, file *ast.File, importer Importer) os.Error {
        // create a single-file dummy package
-       pkg := &ast.Package{file.Name.Name, nil, map[string]*ast.File{fset.Position(file.Name.NamePos).Filename: file}}
+       pkg := &ast.Package{file.Name.Name, nil, nil, map[string]*ast.File{fset.Position(file.Name.NamePos).Filename: file}}
        return CheckPackage(fset, pkg, importer)
 }
 
@@ -327,8 +327,8 @@ func (tc *typechecker) checkBlock(body []ast.Stmt, ftype *Type) {
        if ftype != nil {
                for _, par := range ftype.Params.Objects {
                        if par.Name != "_" {
-                               obj := tc.topScope.Insert(par)
-                               assert(obj == par) // ftype has no double declarations
+                               alt := tc.topScope.Insert(par)
+                               assert(alt == nil) // ftype has no double declarations
                        }
                }
        }
index 3988ff1..d16e069 100644 (file)
@@ -78,7 +78,7 @@ func expectedErrors(t *testing.T, pkg *ast.Package) (list scanner.ErrorList) {
                        case token.EOF:
                                break loop
                        case token.COMMENT:
-                               s := errRx.FindSubmatch(lit)
+                               s := errRx.FindStringSubmatch(lit)
                                if len(s) == 2 {
                                        list = append(list, &scanner.Error{fset.Position(prev), string(s[1])})
                                }
index cf44349..abc8bbb 100644 (file)
@@ -14,7 +14,7 @@ var Universe *ast.Scope
 
 func def(obj *ast.Object) {
        alt := Universe.Insert(obj)
-       if alt != obj {
+       if alt != nil {
                panic("object declared twice")
        }
 }
index fc620f5..305d419 100644 (file)
@@ -407,7 +407,7 @@ func (s *structType) string() string { return s.safeString(make(map[typeId]bool)
 func newStructType(name string) *structType {
        s := &structType{CommonType{Name: name}, nil}
        // For historical reasons we set the id here rather than init.
-       // Se the comment in newTypeObject for details.
+       // See the comment in newTypeObject for details.
        setTypeId(s)
        return s
 }
@@ -545,7 +545,7 @@ func getBaseType(name string, rt reflect.Type) (gobType, os.Error) {
 // getType returns the Gob type describing the given reflect.Type.
 // Should be called only when handling GobEncoders/Decoders,
 // which may be pointers.  All other types are handled through the
-//  base type, never a pointer.
+// base type, never a pointer.
 // typeLock must be held.
 func getType(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error) {
        typ, present := types[rt]
@@ -561,7 +561,7 @@ func getType(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error)
 
 func checkId(want, got typeId) {
        if want != got {
-               fmt.Fprintf(os.Stderr, "checkId: %d should be %d\n", int(want), int(got))
+               fmt.Fprintf(os.Stderr, "checkId: %d should be %d\n", int(got), int(want))
                panic("bootstrap type wrong id: " + got.name() + " " + got.string() + " not " + want.string())
        }
 }
index 4ad680c..8b5c477 100644 (file)
@@ -154,7 +154,10 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) {
        // handle Content-Range header.
        // TODO(adg): handle multiple ranges
        ranges, err := parseRange(r.Header.Get("Range"), size)
-       if err != nil || len(ranges) > 1 {
+       if err == nil && len(ranges) > 1 {
+               err = os.ErrorString("multiple ranges not supported")
+       }
+       if err != nil {
                Error(w, err.String(), StatusRequestedRangeNotSatisfiable)
                return
        }
index 0bac266..bc79e21 100644 (file)
 //
 //     pprof http://localhost:6060/debug/pprof/heap
 //
+// Or to look at a 30-second CPU profile:
+//
+//     pprof http://localhost:6060/debug/pprof/profile
+//
 package pprof
 
 import (
@@ -29,10 +33,12 @@ import (
        "runtime/pprof"
        "strconv"
        "strings"
+       "time"
 )
 
 func init() {
        http.Handle("/debug/pprof/cmdline", http.HandlerFunc(Cmdline))
+       http.Handle("/debug/pprof/profile", http.HandlerFunc(Profile))
        http.Handle("/debug/pprof/heap", http.HandlerFunc(Heap))
        http.Handle("/debug/pprof/symbol", http.HandlerFunc(Symbol))
 }
@@ -41,22 +47,46 @@ func init() {
 // command line, with arguments separated by NUL bytes.
 // The package initialization registers it as /debug/pprof/cmdline.
 func Cmdline(w http.ResponseWriter, r *http.Request) {
-       w.Header().Set("content-type", "text/plain; charset=utf-8")
+       w.Header().Set("Content-Type", "text/plain; charset=utf-8")
        fmt.Fprintf(w, strings.Join(os.Args, "\x00"))
 }
 
 // Heap responds with the pprof-formatted heap profile.
 // The package initialization registers it as /debug/pprof/heap.
 func Heap(w http.ResponseWriter, r *http.Request) {
-       w.Header().Set("content-type", "text/plain; charset=utf-8")
+       w.Header().Set("Content-Type", "text/plain; charset=utf-8")
        pprof.WriteHeapProfile(w)
 }
 
+// Profile responds with the pprof-formatted cpu profile.
+// The package initialization registers it as /debug/pprof/profile.
+func Profile(w http.ResponseWriter, r *http.Request) {
+       sec, _ := strconv.Atoi64(r.FormValue("seconds"))
+       if sec == 0 {
+               sec = 30
+       }
+
+       // Set Content Type assuming StartCPUProfile will work,
+       // because if it does it starts writing.
+       w.Header().Set("Content-Type", "application/octet-stream")
+       if err := pprof.StartCPUProfile(w); err != nil {
+               // StartCPUProfile failed, so no writes yet.
+               // Can change header back to text content
+               // and send error code.
+               w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+               w.WriteHeader(http.StatusInternalServerError)
+               fmt.Fprintf(w, "Could not enable CPU profiling: %s\n", err)
+               return
+       }
+       time.Sleep(sec * 1e9)
+       pprof.StopCPUProfile()
+}
+
 // Symbol looks up the program counters listed in the request,
 // responding with a table mapping program counters to function names.
 // The package initialization registers it as /debug/pprof/symbol.
 func Symbol(w http.ResponseWriter, r *http.Request) {
-       w.Header().Set("content-type", "text/plain; charset=utf-8")
+       w.Header().Set("Content-Type", "text/plain; charset=utf-8")
 
        // We don't know how many symbols we have, but we
        // do have symbol information.  Pprof only cares whether
index 683de85..b0e26e5 100644 (file)
@@ -175,7 +175,7 @@ func TestHostHandlers(t *testing.T) {
        ts := httptest.NewServer(nil)
        defer ts.Close()
 
-       conn, err := net.Dial("tcp", "", ts.Listener.Addr().String())
+       conn, err := net.Dial("tcp", ts.Listener.Addr().String())
        if err != nil {
                t.Fatal(err)
        }
@@ -265,7 +265,7 @@ func TestServerTimeouts(t *testing.T) {
 
        // Slow client that should timeout.
        t1 := time.Nanoseconds()
-       conn, err := net.Dial("tcp", "", fmt.Sprintf("localhost:%d", addr.Port))
+       conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", addr.Port))
        if err != nil {
                t.Fatalf("Dial: %v", err)
        }
@@ -348,7 +348,7 @@ func TestIdentityResponse(t *testing.T) {
        }
        // Verify that the connection is closed when the declared Content-Length
        // is larger than what the handler wrote.
-       conn, err := net.Dial("tcp", "", ts.Listener.Addr().String())
+       conn, err := net.Dial("tcp", ts.Listener.Addr().String())
        if err != nil {
                t.Fatalf("error dialing: %v", err)
        }
@@ -377,7 +377,7 @@ func TestServeHTTP10Close(t *testing.T) {
        }))
        defer s.Close()
 
-       conn, err := net.Dial("tcp", "", s.Listener.Addr().String())
+       conn, err := net.Dial("tcp", s.Listener.Addr().String())
        if err != nil {
                t.Fatal("dial error:", err)
        }
index 8a73ead..ed7843b 100644 (file)
@@ -195,7 +195,7 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, os.Error) {
                return pc, nil
        }
 
-       conn, err := net.Dial("tcp", "", cm.addr())
+       conn, err := net.Dial("tcp", cm.addr())
        if err != nil {
                return nil, err
        }
index 8314a83..0b2058d 100644 (file)
@@ -34,6 +34,12 @@ var filenames = []string{
        "basn6a16",
 }
 
+var filenamesShort = []string{
+       "basn0g01",
+       "basn0g04-31",
+       "basn6a16",
+}
+
 func readPng(filename string) (image.Image, os.Error) {
        f, err := os.Open(filename, os.O_RDONLY, 0444)
        if err != nil {
@@ -157,7 +163,11 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
 }
 
 func TestReader(t *testing.T) {
-       for _, fn := range filenames {
+       names := filenames
+       if testing.Short() {
+               names = filenamesShort
+       }
+       for _, fn := range names {
                // Read the .png file.
                img, err := readPng("testdata/pngsuite/" + fn + ".png")
                if err != nil {
index f218a55..4d9929f 100644 (file)
@@ -32,7 +32,11 @@ func diff(m0, m1 image.Image) os.Error {
 
 func TestWriter(t *testing.T) {
        // The filenames variable is declared in reader_test.go.
-       for _, fn := range filenames {
+       names := filenames
+       if testing.Short() {
+               names = filenamesShort
+       }
+       for _, fn := range names {
                qfn := "testdata/pngsuite/" + fn + ".png"
                // Read the image.
                m0, err := readPng(qfn)
index ad60263..aad8b63 100644 (file)
@@ -157,6 +157,7 @@ func TestUnmarshal(t *testing.T) {
 }
 
 func TestUnmarshalMarshal(t *testing.T) {
+       initBig()
        var v interface{}
        if err := Unmarshal(jsonBig, &v); err != nil {
                t.Fatalf("Unmarshal: %v", err)
index 2dc8ff8..0d4de32 100644 (file)
@@ -85,6 +85,7 @@ func TestIndent(t *testing.T) {
 // Tests of a large random structure.
 
 func TestCompactBig(t *testing.T) {
+       initBig()
        var buf bytes.Buffer
        if err := Compact(&buf, jsonBig); err != nil {
                t.Fatalf("Compact: %v", err)
@@ -98,6 +99,7 @@ func TestCompactBig(t *testing.T) {
 }
 
 func TestIndentBig(t *testing.T) {
+       initBig()
        var buf bytes.Buffer
        if err := Indent(&buf, jsonBig, "", "\t"); err != nil {
                t.Fatalf("Indent1: %v", err)
@@ -135,6 +137,7 @@ func TestIndentBig(t *testing.T) {
 }
 
 func TestNextValueBig(t *testing.T) {
+       initBig()
        var scan scanner
        item, rest, err := nextValue(jsonBig, &scan)
        if err != nil {
@@ -160,6 +163,7 @@ func TestNextValueBig(t *testing.T) {
 }
 
 func BenchmarkSkipValue(b *testing.B) {
+       initBig()
        var scan scanner
        for i := 0; i < b.N; i++ {
                nextValue(jsonBig, &scan)
@@ -191,12 +195,23 @@ func trim(b []byte) []byte {
 
 var jsonBig []byte
 
-func init() {
-       b, err := Marshal(genValue(10000))
-       if err != nil {
-               panic(err)
+const (
+       big   = 10000
+       small = 100
+)
+
+func initBig() {
+       n := big
+       if testing.Short() {
+               n = small
+       }
+       if len(jsonBig) != n {
+               b, err := Marshal(genValue(n))
+               if err != nil {
+                       panic(err)
+               }
+               jsonBig = b
        }
-       jsonBig = b
 }
 
 func genValue(n int) interface{} {
diff --git a/libgo/go/net/cgo_stub.go b/libgo/go/net/cgo_stub.go
new file mode 100644 (file)
index 0000000..e28f662
--- /dev/null
@@ -0,0 +1,21 @@
+// 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.
+
+// Stub cgo routines for systems that do not use cgo to do network lookups.
+
+package net
+
+import "os"
+
+func cgoLookupHost(name string) (addrs []string, err os.Error, completed bool) {
+       return nil, nil, false
+}
+
+func cgoLookupPort(network, service string) (port int, err os.Error, completed bool) {
+       return 0, nil, false
+}
+
+func cgoLookupIP(name string) (addrs []IP, err os.Error, completed bool) {
+       return nil, nil, false
+}
index 1cf8e79..66cb09b 100644 (file)
@@ -6,9 +6,7 @@ package net
 
 import "os"
 
-// Dial connects to the remote address raddr on the network net.
-// If the string laddr is not empty, it is used as the local address
-// for the connection.
+// Dial connects to the address addr on the network net.
 //
 // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
 // "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
@@ -16,79 +14,56 @@ import "os"
 //
 // For IP networks, addresses have the form host:port.  If host is
 // a literal IPv6 address, it must be enclosed in square brackets.
+// The functions JoinHostPort and SplitHostPort manipulate 
+// addresses in this form.
 //
 // Examples:
-//     Dial("tcp", "", "12.34.56.78:80")
-//     Dial("tcp", "", "google.com:80")
-//     Dial("tcp", "", "[de:ad:be:ef::ca:fe]:80")
-//     Dial("tcp", "127.0.0.1:123", "127.0.0.1:88")
+//     Dial("tcp", "12.34.56.78:80")
+//     Dial("tcp", "google.com:80")
+//     Dial("tcp", "[de:ad:be:ef::ca:fe]:80")
 //
-func Dial(net, laddr, raddr string) (c Conn, err os.Error) {
+func Dial(net, addr string) (c Conn, err os.Error) {
+       raddr := addr
+       if raddr == "" {
+               return nil, &OpError{"dial", net, nil, errMissingAddress}
+       }
        switch net {
        case "tcp", "tcp4", "tcp6":
-               var la, ra *TCPAddr
-               if laddr != "" {
-                       if la, err = ResolveTCPAddr(laddr); err != nil {
-                               goto Error
-                       }
-               }
-               if raddr != "" {
-                       if ra, err = ResolveTCPAddr(raddr); err != nil {
-                               goto Error
-                       }
+               var ra *TCPAddr
+               if ra, err = ResolveTCPAddr(raddr); err != nil {
+                       goto Error
                }
-               c, err := DialTCP(net, la, ra)
+               c, err := DialTCP(net, nil, ra)
                if err != nil {
                        return nil, err
                }
                return c, nil
        case "udp", "udp4", "udp6":
-               var la, ra *UDPAddr
-               if laddr != "" {
-                       if la, err = ResolveUDPAddr(laddr); err != nil {
-                               goto Error
-                       }
-               }
-               if raddr != "" {
-                       if ra, err = ResolveUDPAddr(raddr); err != nil {
-                               goto Error
-                       }
+               var ra *UDPAddr
+               if ra, err = ResolveUDPAddr(raddr); err != nil {
+                       goto Error
                }
-               c, err := DialUDP(net, la, ra)
+               c, err := DialUDP(net, nil, ra)
                if err != nil {
                        return nil, err
                }
                return c, nil
        case "unix", "unixgram", "unixpacket":
-               var la, ra *UnixAddr
-               if raddr != "" {
-                       if ra, err = ResolveUnixAddr(net, raddr); err != nil {
-                               goto Error
-                       }
-               }
-               if laddr != "" {
-                       if la, err = ResolveUnixAddr(net, laddr); err != nil {
-                               goto Error
-                       }
+               var ra *UnixAddr
+               if ra, err = ResolveUnixAddr(net, raddr); err != nil {
+                       goto Error
                }
-               c, err = DialUnix(net, la, ra)
+               c, err = DialUnix(net, nil, ra)
                if err != nil {
                        return nil, err
                }
                return c, nil
        case "ip", "ip4", "ip6":
-               var la, ra *IPAddr
-               if laddr != "" {
-                       if la, err = ResolveIPAddr(laddr); err != nil {
-                               goto Error
-                       }
-               }
-               if raddr != "" {
-                       if ra, err = ResolveIPAddr(raddr); err != nil {
-                               goto Error
-                       }
+               var ra *IPAddr
+               if ra, err = ResolveIPAddr(raddr); err != nil {
+                       goto Error
                }
-               c, err := DialIP(net, la, ra)
+               c, err := DialIP(net, nil, ra)
                if err != nil {
                        return nil, err
                }
index a432800..9a9c02e 100644 (file)
@@ -32,7 +32,7 @@ func fetchGoogle(t *testing.T, fd Conn, network, addr string) {
 }
 
 func doDial(t *testing.T, network, addr string) {
-       fd, err := Dial(network, "", addr)
+       fd, err := Dial(network, addr)
        if err != nil {
                t.Errorf("Dial(%q, %q, %q) = _, %v", network, "", addr, err)
                return
@@ -55,6 +55,13 @@ var googleaddrs = []string{
        "[2001:4860:0:2001::68]:80", // ipv6.google.com; removed if ipv6 flag not set
 }
 
+func TestLookupCNAME(t *testing.T) {
+       cname, err := LookupCNAME("www.google.com")
+       if cname != "www.l.google.com." || err != nil {
+               t.Errorf(`LookupCNAME("www.google.com.") = %q, %v, want "www.l.google.com.", nil`, cname, err)
+       }
+}
+
 func TestDialGoogle(t *testing.T) {
        // If no ipv6 tunnel, don't try the last address.
        if !*ipv6 {
@@ -64,14 +71,14 @@ func TestDialGoogle(t *testing.T) {
        // Insert an actual IP address for google.com
        // into the table.
 
-       _, addrs, err := LookupHost("www.google.com")
+       addrs, err := LookupIP("www.google.com")
        if err != nil {
                t.Fatalf("lookup www.google.com: %v", err)
        }
        if len(addrs) == 0 {
                t.Fatalf("no addresses for www.google.com")
        }
-       ip := ParseIP(addrs[0]).To4()
+       ip := addrs[0].To4()
 
        for i, s := range googleaddrs {
                if strings.Contains(s, "%") {
index 3252dd4..32cea61 100644 (file)
@@ -159,7 +159,7 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs
                // all the cfg.servers[i] are IP addresses, which
                // Dial will use without a DNS lookup.
                server := cfg.servers[i] + ":53"
-               c, cerr := Dial("udp", "", server)
+               c, cerr := Dial("udp", server)
                if cerr != nil {
                        err = cerr
                        continue
@@ -178,12 +178,23 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs
        return
 }
 
-func convertRR_A(records []dnsRR) []string {
-       addrs := make([]string, len(records))
+func convertRR_A(records []dnsRR) []IP {
+       addrs := make([]IP, len(records))
        for i := 0; i < len(records); i++ {
                rr := records[i]
                a := rr.(*dnsRR_A).A
-               addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)).String()
+               addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
+       }
+       return addrs
+}
+
+func convertRR_AAAA(records []dnsRR) []IP {
+       addrs := make([]IP, len(records))
+       for i := 0; i < len(records); i++ {
+               rr := records[i]
+               a := make(IP, 16)
+               copy(a, rr.(*dnsRR_AAAA).AAAA[:])
+               addrs[i] = a
        }
        return addrs
 }
@@ -294,10 +305,8 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Erro
        return
 }
 
-// LookupHost looks for name using the local hosts file and DNS resolver.
-// It returns the canonical name for the host and an array of that
-// host's addresses.
-func LookupHost(name string) (cname string, addrs []string, err os.Error) {
+// goLookupHost is the native Go implementation of LookupHost.
+func goLookupHost(name string) (addrs []string, err os.Error) {
        onceLoadConfig.Do(loadConfig)
        if dnserr != nil || cfg == nil {
                err = dnserr
@@ -306,18 +315,69 @@ func LookupHost(name string) (cname string, addrs []string, err os.Error) {
        // Use entries from /etc/hosts if they match.
        addrs = lookupStaticHost(name)
        if len(addrs) > 0 {
-               cname = name
+               return
+       }
+       ips, err := goLookupIP(name)
+       if err != nil {
+               return
+       }
+       addrs = make([]string, 0, len(ips))
+       for _, ip := range ips {
+               addrs = append(addrs, ip.String())
+       }
+       return
+}
+
+// goLookupIP is the native Go implementation of LookupIP.
+func goLookupIP(name string) (addrs []IP, err os.Error) {
+       onceLoadConfig.Do(loadConfig)
+       if dnserr != nil || cfg == nil {
+               err = dnserr
                return
        }
        var records []dnsRR
+       var cname string
        cname, records, err = lookup(name, dnsTypeA)
        if err != nil {
                return
        }
        addrs = convertRR_A(records)
+       if cname != "" {
+               name = cname
+       }
+       _, records, err = lookup(name, dnsTypeAAAA)
+       if err != nil && len(addrs) > 0 {
+               // Ignore error because A lookup succeeded.
+               err = nil
+       }
+       if err != nil {
+               return
+       }
+       addrs = append(addrs, convertRR_AAAA(records)...)
+       return
+}
+
+// LookupCNAME returns the canonical DNS host for the given name.
+// Callers that do not care about the canonical name can call
+// LookupHost or LookupIP directly; both take care of resolving
+// the canonical name as part of the lookup.
+func LookupCNAME(name string) (cname string, err os.Error) {
+       onceLoadConfig.Do(loadConfig)
+       if dnserr != nil || cfg == nil {
+               err = dnserr
+               return
+       }
+       _, rr, err := lookup(name, dnsTypeCNAME)
+       if err != nil {
+               return
+       }
+       if len(rr) >= 0 {
+               cname = rr[0].(*dnsRR_CNAME).Cname
+       }
        return
 }
 
+// An SRV represents a single DNS SRV record.
 type SRV struct {
        Target   string
        Port     uint16
@@ -344,11 +404,13 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.
        return
 }
 
+// An MX represents a single DNS MX record.
 type MX struct {
        Host string
        Pref uint16
 }
 
+// LookupMX returns the DNS MX records associated with name.
 func LookupMX(name string) (entries []*MX, err os.Error) {
        var records []dnsRR
        _, records, err = lookup(name, dnsTypeMX)
index dc195ca..5209c1a 100644 (file)
@@ -50,6 +50,7 @@ const (
        dnsTypeMINFO = 14
        dnsTypeMX    = 15
        dnsTypeTXT   = 16
+       dnsTypeAAAA  = 28
        dnsTypeSRV   = 33
 
        // valid dnsQuestion.qtype only
@@ -244,8 +245,18 @@ type dnsRR_A struct {
        A   uint32 "ipv4"
 }
 
-func (rr *dnsRR_A) Header() *dnsRR_Header { return &rr.Hdr }
+func (rr *dnsRR_A) Header() *dnsRR_Header {
+       return &rr.Hdr
+}
+
+type dnsRR_AAAA struct {
+       Hdr  dnsRR_Header
+       AAAA [16]byte "ipv6"
+}
 
+func (rr *dnsRR_AAAA) Header() *dnsRR_Header {
+       return &rr.Hdr
+}
 
 // Packing and unpacking.
 //
@@ -270,6 +281,7 @@ var rr_mk = map[int]func() dnsRR{
        dnsTypeTXT:   func() dnsRR { return new(dnsRR_TXT) },
        dnsTypeSRV:   func() dnsRR { return new(dnsRR_SRV) },
        dnsTypeA:     func() dnsRR { return new(dnsRR_A) },
+       dnsTypeAAAA:  func() dnsRR { return new(dnsRR_AAAA) },
 }
 
 // Pack a domain name s into msg[off:].
@@ -377,7 +389,7 @@ Loop:
 
 // TODO(rsc): Move into generic library?
 // Pack a reflect.StructValue into msg.  Struct members can only be uint16, uint32, string,
-// and other (often anonymous) structs.
+// [n]byte, and other (often anonymous) structs.
 func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) {
        for i := 0; i < val.NumField(); i++ {
                f := val.Type().(*reflect.StructType).Field(i)
@@ -410,6 +422,16 @@ func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, o
                                msg[off+3] = byte(i)
                                off += 4
                        }
+               case *reflect.ArrayValue:
+                       if fv.Type().(*reflect.ArrayType).Elem().Kind() != reflect.Uint8 {
+                               goto BadType
+                       }
+                       n := fv.Len()
+                       if off+n > len(msg) {
+                               return len(msg), false
+                       }
+                       reflect.Copy(reflect.NewValue(msg[off:off+n]).(*reflect.SliceValue), fv)
+                       off += n
                case *reflect.StringValue:
                        // There are multiple string encodings.
                        // The tag distinguishes ordinary strings from domain names.
@@ -478,6 +500,16 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int,
                                fv.Set(uint64(i))
                                off += 4
                        }
+               case *reflect.ArrayValue:
+                       if fv.Type().(*reflect.ArrayType).Elem().Kind() != reflect.Uint8 {
+                               goto BadType
+                       }
+                       n := fv.Len()
+                       if off+n > len(msg) {
+                               return len(msg), false
+                       }
+                       reflect.Copy(fv, reflect.NewValue(msg[off:off+n]).(*reflect.SliceValue))
+                       off += n
                case *reflect.StringValue:
                        var s string
                        switch f.Tag {
@@ -515,7 +547,8 @@ func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
 
 // Generic struct printer.
 // Doesn't care about the string tag "domain-name",
-// but does look for an "ipv4" tag on uint32 variables,
+// but does look for an "ipv4" tag on uint32 variables
+// and the "ipv6" tag on array variables,
 // printing them as IP addresses.
 func printStructValue(val *reflect.StructValue) string {
        s := "{"
@@ -533,6 +566,9 @@ func printStructValue(val *reflect.StructValue) string {
                } else if fv, ok := fval.(*reflect.UintValue); ok && f.Tag == "ipv4" {
                        i := fv.Get()
                        s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String()
+               } else if fv, ok := fval.(*reflect.ArrayValue); ok && f.Tag == "ipv6" {
+                       i := fv.Interface().([]byte)
+                       s += IP(i).String()
                } else {
                        s += fmt.Sprint(fval.Interface())
                }
index c8440ae..7e4b83a 100644 (file)
@@ -274,19 +274,25 @@ func startServer() {
        pollserver = p
 }
 
-func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err os.Error) {
+func newFD(fd, family, proto int, net string) (f *netFD, err os.Error) {
        onceStartServer.Do(startServer)
        if e := syscall.SetNonblock(fd, true); e != 0 {
-               return nil, &OpError{"setnonblock", net, laddr, os.Errno(e)}
+               return nil, os.Errno(e)
        }
        f = &netFD{
                sysfd:  fd,
                family: family,
                proto:  proto,
                net:    net,
-               laddr:  laddr,
-               raddr:  raddr,
        }
+       f.cr = make(chan bool, 1)
+       f.cw = make(chan bool, 1)
+       return f, nil
+}
+
+func (fd *netFD) setAddr(laddr, raddr Addr) {
+       fd.laddr = laddr
+       fd.raddr = raddr
        var ls, rs string
        if laddr != nil {
                ls = laddr.String()
@@ -294,10 +300,23 @@ func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err
        if raddr != nil {
                rs = raddr.String()
        }
-       f.sysfile = os.NewFile(fd, net+":"+ls+"->"+rs)
-       f.cr = make(chan bool, 1)
-       f.cw = make(chan bool, 1)
-       return f, nil
+       fd.sysfile = os.NewFile(fd.sysfd, fd.net+":"+ls+"->"+rs)
+}
+
+func (fd *netFD) connect(ra syscall.Sockaddr) (err os.Error) {
+       e := syscall.Connect(fd.sysfd, ra)
+       if e == syscall.EINPROGRESS {
+               var errno int
+               pollserver.WaitWrite(fd)
+               e, errno = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
+               if errno != 0 {
+                       return os.NewSyscallError("getsockopt", errno)
+               }
+       }
+       if e != 0 {
+               return os.Errno(e)
+       }
+       return nil
 }
 
 // Add a reference to this fd.
@@ -593,10 +612,11 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.
        syscall.CloseOnExec(s)
        syscall.ForkLock.RUnlock()
 
-       if nfd, err = newFD(s, fd.family, fd.proto, fd.net, fd.laddr, toAddr(sa)); err != nil {
+       if nfd, err = newFD(s, fd.family, fd.proto, fd.net); err != nil {
                syscall.Close(s)
                return nil, err
        }
+       nfd.setAddr(fd.laddr, toAddr(sa))
        return nfd, nil
 }
 
index 63a8fbc..c2f736c 100644 (file)
@@ -225,29 +225,40 @@ type netFD struct {
        wio             sync.Mutex
 }
 
-func allocFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD) {
+func allocFD(fd, family, proto int, net string) (f *netFD) {
        f = &netFD{
                sysfd:  fd,
                family: family,
                proto:  proto,
                net:    net,
-               laddr:  laddr,
-               raddr:  raddr,
        }
        runtime.SetFinalizer(f, (*netFD).Close)
        return f
 }
 
-func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err os.Error) {
+func newFD(fd, family, proto int, net string) (f *netFD, err os.Error) {
        if initErr != nil {
                return nil, initErr
        }
        onceStartServer.Do(startServer)
        // Associate our socket with resultsrv.iocp.
        if _, e := syscall.CreateIoCompletionPort(int32(fd), resultsrv.iocp, 0, 0); e != 0 {
-               return nil, &OpError{"CreateIoCompletionPort", net, laddr, os.Errno(e)}
+               return nil, os.Errno(e)
+       }
+       return allocFD(fd, family, proto, net), nil
+}
+
+func (fd *netFD) setAddr(laddr, raddr Addr) {
+       fd.laddr = laddr
+       fd.raddr = raddr
+}
+
+func (fd *netFD) connect(ra syscall.Sockaddr) (err os.Error) {
+       e := syscall.Connect(fd.sysfd, ra)
+       if e != 0 {
+               return os.Errno(e)
        }
-       return allocFD(fd, family, proto, net, laddr, raddr), nil
+       return nil
 }
 
 // Add a reference to this fd.
@@ -497,7 +508,9 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.
        lsa, _ := lrsa.Sockaddr()
        rsa, _ := rrsa.Sockaddr()
 
-       return allocFD(s, fd.family, fd.proto, fd.net, toAddr(lsa), toAddr(rsa)), nil
+       nfd = allocFD(s, fd.family, fd.proto, fd.net)
+       nfd.setAddr(toAddr(lsa), toAddr(rsa))
+       return nfd, nil
 }
 
 // Not implemeted functions.
diff --git a/libgo/go/net/file.go b/libgo/go/net/file.go
new file mode 100644 (file)
index 0000000..0e411a1
--- /dev/null
@@ -0,0 +1,119 @@
+// 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 net
+
+import (
+       "os"
+       "syscall"
+)
+
+func newFileFD(f *os.File) (nfd *netFD, err os.Error) {
+       fd, errno := syscall.Dup(f.Fd())
+       if errno != 0 {
+               return nil, os.NewSyscallError("dup", errno)
+       }
+
+       proto, errno := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE)
+       if errno != 0 {
+               return nil, os.NewSyscallError("getsockopt", errno)
+       }
+
+       toAddr := sockaddrToTCP
+       sa, _ := syscall.Getsockname(fd)
+       switch sa.(type) {
+       default:
+               closesocket(fd)
+               return nil, os.EINVAL
+       case *syscall.SockaddrInet4:
+               if proto == syscall.SOCK_DGRAM {
+                       toAddr = sockaddrToUDP
+               } else if proto == syscall.SOCK_RAW {
+                       toAddr = sockaddrToIP
+               }
+       case *syscall.SockaddrInet6:
+               if proto == syscall.SOCK_DGRAM {
+                       toAddr = sockaddrToUDP
+               } else if proto == syscall.SOCK_RAW {
+                       toAddr = sockaddrToIP
+               }
+       case *syscall.SockaddrUnix:
+               toAddr = sockaddrToUnix
+               if proto == syscall.SOCK_DGRAM {
+                       toAddr = sockaddrToUnixgram
+               } else if proto == syscall.SOCK_SEQPACKET {
+                       toAddr = sockaddrToUnixpacket
+               }
+       }
+       laddr := toAddr(sa)
+       sa, _ = syscall.Getpeername(fd)
+       raddr := toAddr(sa)
+
+       if nfd, err = newFD(fd, 0, proto, laddr.Network()); err != nil {
+               return nil, err
+       }
+       nfd.setAddr(laddr, raddr)
+       return nfd, nil
+}
+
+// FileConn returns a copy of the network connection corresponding to
+// the open file f.  It is the caller's responsibility to close f when
+// finished.  Closing c does not affect f, and closing f does not
+// affect c.
+func FileConn(f *os.File) (c Conn, err os.Error) {
+       fd, err := newFileFD(f)
+       if err != nil {
+               return nil, err
+       }
+       switch fd.laddr.(type) {
+       case *TCPAddr:
+               return newTCPConn(fd), nil
+       case *UDPAddr:
+               return newUDPConn(fd), nil
+       case *UnixAddr:
+               return newUnixConn(fd), nil
+       case *IPAddr:
+               return newIPConn(fd), nil
+       }
+       fd.Close()
+       return nil, os.EINVAL
+}
+
+// FileListener returns a copy of the network listener corresponding
+// to the open file f.  It is the caller's responsibility to close l
+// when finished.  Closing c does not affect l, and closing l does not
+// affect c.
+func FileListener(f *os.File) (l Listener, err os.Error) {
+       fd, err := newFileFD(f)
+       if err != nil {
+               return nil, err
+       }
+       switch laddr := fd.laddr.(type) {
+       case *TCPAddr:
+               return &TCPListener{fd}, nil
+       case *UnixAddr:
+               return &UnixListener{fd, laddr.Name}, nil
+       }
+       fd.Close()
+       return nil, os.EINVAL
+}
+
+// FilePacketConn returns a copy of the packet network connection
+// corresponding to the open file f.  It is the caller's
+// responsibility to close f when finished.  Closing c does not affect
+// f, and closing f does not affect c.
+func FilePacketConn(f *os.File) (c PacketConn, err os.Error) {
+       fd, err := newFileFD(f)
+       if err != nil {
+               return nil, err
+       }
+       switch fd.laddr.(type) {
+       case *UDPAddr:
+               return newUDPConn(fd), nil
+       case *UnixAddr:
+               return newUnixConn(fd), nil
+       }
+       fd.Close()
+       return nil, os.EINVAL
+}
diff --git a/libgo/go/net/file_test.go b/libgo/go/net/file_test.go
new file mode 100644 (file)
index 0000000..1ec05fd
--- /dev/null
@@ -0,0 +1,131 @@
+// 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 net
+
+import (
+       "os"
+       "reflect"
+       "runtime"
+       "syscall"
+       "testing"
+)
+
+type listenerFile interface {
+       Listener
+       File() (f *os.File, err os.Error)
+}
+
+type packetConnFile interface {
+       PacketConn
+       File() (f *os.File, err os.Error)
+}
+
+type connFile interface {
+       Conn
+       File() (f *os.File, err os.Error)
+}
+
+func testFileListener(t *testing.T, net, laddr string) {
+       if net == "tcp" {
+               laddr += ":0" // any available port
+       }
+       l, err := Listen(net, laddr)
+       if err != nil {
+               t.Fatalf("Listen failed: %v", err)
+       }
+       defer l.Close()
+       lf := l.(listenerFile)
+       f, err := lf.File()
+       if err != nil {
+               t.Fatalf("File failed: %v", err)
+       }
+       c, err := FileListener(f)
+       if err != nil {
+               t.Fatalf("FileListener failed: %v", err)
+       }
+       if !reflect.DeepEqual(l.Addr(), c.Addr()) {
+               t.Fatalf("Addrs not equal: %#v != %#v", l.Addr(), c.Addr())
+       }
+       if err := c.Close(); err != nil {
+               t.Fatalf("Close failed: %v", err)
+       }
+       if err := f.Close(); err != nil {
+               t.Fatalf("Close failed: %v", err)
+       }
+}
+
+func TestFileListener(t *testing.T) {
+       if runtime.GOOS == "windows" {
+               return
+       }
+       testFileListener(t, "tcp", "127.0.0.1")
+       testFileListener(t, "tcp", "127.0.0.1")
+       if kernelSupportsIPv6() {
+               testFileListener(t, "tcp", "[::ffff:127.0.0.1]")
+               testFileListener(t, "tcp", "127.0.0.1")
+               testFileListener(t, "tcp", "[::ffff:127.0.0.1]")
+       }
+       if syscall.OS == "linux" {
+               testFileListener(t, "unix", "@gotest/net")
+               testFileListener(t, "unixpacket", "@gotest/net")
+       }
+}
+
+func testFilePacketConn(t *testing.T, pcf packetConnFile) {
+       f, err := pcf.File()
+       if err != nil {
+               t.Fatalf("File failed: %v", err)
+       }
+       c, err := FilePacketConn(f)
+       if err != nil {
+               t.Fatalf("FilePacketConn failed: %v", err)
+       }
+       if !reflect.DeepEqual(pcf.LocalAddr(), c.LocalAddr()) {
+               t.Fatalf("LocalAddrs not equal: %#v != %#v", pcf.LocalAddr(), c.LocalAddr())
+       }
+       if err := c.Close(); err != nil {
+               t.Fatalf("Close failed: %v", err)
+       }
+       if err := f.Close(); err != nil {
+               t.Fatalf("Close failed: %v", err)
+       }
+}
+
+func testFilePacketConnListen(t *testing.T, net, laddr string) {
+       l, err := ListenPacket(net, laddr)
+       if err != nil {
+               t.Fatalf("Listen failed: %v", err)
+       }
+       testFilePacketConn(t, l.(packetConnFile))
+       if err := l.Close(); err != nil {
+               t.Fatalf("Close failed: %v", err)
+       }
+}
+
+func testFilePacketConnDial(t *testing.T, net, raddr string) {
+       c, err := Dial(net, raddr)
+       if err != nil {
+               t.Fatalf("Dial failed: %v", err)
+       }
+       testFilePacketConn(t, c.(packetConnFile))
+       if err := c.Close(); err != nil {
+               t.Fatalf("Close failed: %v", err)
+       }
+}
+
+func TestFilePacketConn(t *testing.T) {
+       if runtime.GOOS == "windows" {
+               return
+       }
+       testFilePacketConnListen(t, "udp", "127.0.0.1:0")
+       testFilePacketConnDial(t, "udp", "127.0.0.1:12345")
+       if kernelSupportsIPv6() {
+               testFilePacketConnListen(t, "udp", "[::1]:0")
+               testFilePacketConnDial(t, "udp", "[::ffff:127.0.0.1]:12345")
+       }
+       if syscall.OS == "linux" {
+               testFilePacketConnListen(t, "unixgram", "@gotest1/net")
+       }
+}
diff --git a/libgo/go/net/file_windows.go b/libgo/go/net/file_windows.go
new file mode 100644 (file)
index 0000000..94aa583
--- /dev/null
@@ -0,0 +1,25 @@
+// 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 net
+
+import (
+       "os"
+       "syscall"
+)
+
+func FileConn(f *os.File) (c Conn, err os.Error) {
+       // TODO: Implement this
+       return nil, os.NewSyscallError("FileConn", syscall.EWINDOWS)
+}
+
+func FileListener(f *os.File) (l Listener, err os.Error) {
+       // TODO: Implement this
+       return nil, os.NewSyscallError("FileListener", syscall.EWINDOWS)
+}
+
+func FilePacketConn(f *os.File) (c PacketConn, err os.Error) {
+       // TODO: Implement this
+       return nil, os.NewSyscallError("FilePacketConn", syscall.EWINDOWS)
+}
index 84cd92e..470e35f 100644 (file)
@@ -13,7 +13,6 @@ type hostTest struct {
        ips  []IP
 }
 
-
 var hosttests = []hostTest{
        {"odin", []IP{
                IPv4(127, 0, 0, 2),
index 1904af0..12bb6f3 100644 (file)
@@ -474,13 +474,13 @@ func parseIPv6(s string) IP {
        return p
 }
 
-// A SyntaxError represents a malformed text string and the type of string that was expected.
-type SyntaxError struct {
+// A ParseError represents a malformed text string and the type of string that was expected.
+type ParseError struct {
        Type string
        Text string
 }
 
-func (e *SyntaxError) String() string {
+func (e *ParseError) String() string {
        return "invalid " + e.Type + ": " + e.Text
 }
 
@@ -507,33 +507,46 @@ func ParseIP(s string) IP {
 }
 
 // ParseCIDR parses s as a CIDR notation IP address and mask,
-// like "192.168.100.1/24" or "2001:DB8::/48".
+// like "192.168.100.1/24", "2001:DB8::/48", as defined in
+// RFC 4632 and RFC 4291.
 func ParseCIDR(s string) (ip IP, mask IPMask, err os.Error) {
        i := byteIndex(s, '/')
        if i < 0 {
-               return nil, nil, &SyntaxError{"CIDR address", s}
+               return nil, nil, &ParseError{"CIDR address", s}
        }
        ipstr, maskstr := s[:i], s[i+1:]
-       ip = ParseIP(ipstr)
+       iplen := 4
+       ip = parseIPv4(ipstr)
+       if ip == nil {
+               iplen = 16
+               ip = parseIPv6(ipstr)
+       }
        nn, i, ok := dtoi(maskstr, 0)
-       if ip == nil || !ok || i != len(maskstr) || nn < 0 || nn > 8*len(ip) {
-               return nil, nil, &SyntaxError{"CIDR address", s}
+       if ip == nil || !ok || i != len(maskstr) || nn < 0 || nn > 8*iplen {
+               return nil, nil, &ParseError{"CIDR address", s}
        }
        n := uint(nn)
-       if len(ip) == 4 {
+       if iplen == 4 {
                v4mask := ^uint32(0xffffffff >> n)
-               mask = IPMask(IPv4(byte(v4mask>>24), byte(v4mask>>16), byte(v4mask>>8), byte(v4mask)))
-               return ip, mask, nil
-       }
-       mask = make(IPMask, 16)
-       for i := 0; i < 16; i++ {
-               if n >= 8 {
-                       mask[i] = 0xff
-                       n -= 8
-                       continue
+               mask = IPv4Mask(byte(v4mask>>24), byte(v4mask>>16), byte(v4mask>>8), byte(v4mask))
+       } else {
+               mask = make(IPMask, 16)
+               for i := 0; i < 16; i++ {
+                       if n >= 8 {
+                               mask[i] = 0xff
+                               n -= 8
+                               continue
+                       }
+                       mask[i] = ^byte(0xff >> n)
+                       n = 0
+
+               }
+       }
+       // address must not have any bits not in mask
+       for i := range ip {
+               if ip[i]&^mask[i] != 0 {
+                       return nil, nil, &ParseError{"CIDR address", s}
                }
-               mask[i] = ^byte(0xff >> n)
-               n = 0
        }
        return ip, mask, nil
 }
index e29c302..f1a4716 100644 (file)
@@ -5,30 +5,26 @@
 package net
 
 import (
+       "bytes"
+       "reflect"
        "testing"
+       "os"
 )
 
-func isEqual(a, b IP) bool {
+func isEqual(a, b []byte) bool {
        if a == nil && b == nil {
                return true
        }
-       if a == nil || b == nil || len(a) != len(b) {
+       if a == nil || b == nil {
                return false
        }
-       for i := 0; i < len(a); i++ {
-               if a[i] != b[i] {
-                       return false
-               }
-       }
-       return true
+       return bytes.Equal(a, b)
 }
 
-type parseIPTest struct {
+var parseiptests = []struct {
        in  string
        out IP
-}
-
-var parseiptests = []parseIPTest{
+}{
        {"127.0.1.2", IPv4(127, 0, 1, 2)},
        {"127.0.0.1", IPv4(127, 0, 0, 1)},
        {"127.0.0.256", nil},
@@ -43,20 +39,17 @@ var parseiptests = []parseIPTest{
 }
 
 func TestParseIP(t *testing.T) {
-       for i := 0; i < len(parseiptests); i++ {
-               tt := parseiptests[i]
+       for _, tt := range parseiptests {
                if out := ParseIP(tt.in); !isEqual(out, tt.out) {
                        t.Errorf("ParseIP(%#q) = %v, want %v", tt.in, out, tt.out)
                }
        }
 }
 
-type ipStringTest struct {
+var ipstringtests = []struct {
        in  IP
        out string
-}
-
-var ipstringtests = []ipStringTest{
+}{
        // cf. RFC 5952 (A Recommendation for IPv6 Address Text Representation)
        {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0,
                0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1},
@@ -85,10 +78,67 @@ var ipstringtests = []ipStringTest{
 }
 
 func TestIPString(t *testing.T) {
-       for i := 0; i < len(ipstringtests); i++ {
-               tt := ipstringtests[i]
+       for _, tt := range ipstringtests {
                if out := tt.in.String(); out != tt.out {
                        t.Errorf("IP.String(%v) = %#q, want %#q", tt.in, out, tt.out)
                }
        }
 }
+
+var parsecidrtests = []struct {
+       in   string
+       ip   IP
+       mask IPMask
+       err  os.Error
+}{
+       {"135.104.0.0/32", IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 255), nil},
+       {"0.0.0.0/24", IPv4(0, 0, 0, 0), IPv4Mask(255, 255, 255, 0), nil},
+       {"135.104.0.0/24", IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0), nil},
+       {"135.104.0.1/32", IPv4(135, 104, 0, 1), IPv4Mask(255, 255, 255, 255), nil},
+       {"135.104.0.1/24", nil, nil, &ParseError{"CIDR address", "135.104.0.1/24"}},
+       {"::1/128", ParseIP("::1"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")), nil},
+       {"abcd:2345::/127", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe")), nil},
+       {"abcd:2345::/65", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::")), nil},
+       {"abcd:2345::/64", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff::")), nil},
+       {"abcd:2345::/63", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:fffe::")), nil},
+       {"abcd:2345::/33", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:8000::")), nil},
+       {"abcd:2345::/32", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff::")), nil},
+       {"abcd:2344::/31", ParseIP("abcd:2344::"), IPMask(ParseIP("ffff:fffe::")), nil},
+       {"abcd:2300::/24", ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::")), nil},
+       {"abcd:2345::/24", nil, nil, &ParseError{"CIDR address", "abcd:2345::/24"}},
+       {"2001:DB8::/48", ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::")), nil},
+}
+
+func TestParseCIDR(t *testing.T) {
+       for _, tt := range parsecidrtests {
+               if ip, mask, err := ParseCIDR(tt.in); !isEqual(ip, tt.ip) || !isEqual(mask, tt.mask) || !reflect.DeepEqual(err, tt.err) {
+                       t.Errorf("ParseCIDR(%q) = %v, %v, %v; want %v, %v, %v", tt.in, ip, mask, err, tt.ip, tt.mask, tt.err)
+               }
+       }
+}
+
+var splitjointests = []struct {
+       Host string
+       Port string
+       Join string
+}{
+       {"www.google.com", "80", "www.google.com:80"},
+       {"127.0.0.1", "1234", "127.0.0.1:1234"},
+       {"::1", "80", "[::1]:80"},
+}
+
+func TestSplitHostPort(t *testing.T) {
+       for _, tt := range splitjointests {
+               if host, port, err := SplitHostPort(tt.Join); host != tt.Host || port != tt.Port || err != nil {
+                       t.Errorf("SplitHostPort(%q) = %q, %q, %v; want %q, %q, nil", tt.Join, host, port, err, tt.Host, tt.Port)
+               }
+       }
+}
+
+func TestJoinHostPort(t *testing.T) {
+       for _, tt := range splitjointests {
+               if join := JoinHostPort(tt.Host, tt.Port); join != tt.Join {
+                       t.Errorf("JoinHostPort(%q, %q) = %q; want %q", tt.Host, tt.Port, join, tt.Join)
+               }
+       }
+}
index 81a918c..6043330 100644 (file)
@@ -240,7 +240,7 @@ func hostToIP(host string) (ip IP, err os.Error) {
        addr = ParseIP(host)
        if addr == nil {
                // Not an IP address.  Try as a DNS name.
-               _, addrs, err1 := LookupHost(host)
+               addrs, err1 := LookupHost(host)
                if err1 != nil {
                        err = err1
                        goto Error
index ae4204b..80bc3ee 100644 (file)
@@ -170,9 +170,10 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
        return nil, InvalidAddrError("unexpected socket family")
 }
 
-// Split "host:port" into "host" and "port".
-// Host cannot contain colons unless it is bracketed.
-func splitHostPort(hostport string) (host, port string, err os.Error) {
+// SplitHostPort splits a network address of the form
+// "host:port" or "[host]:port" into host and port.
+// The latter form must be used when host contains a colon.
+func SplitHostPort(hostport string) (host, port string, err os.Error) {
        // The port starts after the last colon.
        i := last(hostport, ':')
        if i < 0 {
@@ -195,9 +196,9 @@ func splitHostPort(hostport string) (host, port string, err os.Error) {
        return
 }
 
-// Join "host" and "port" into "host:port".
-// If host contains colons, will join into "[host]:port".
-func joinHostPort(host, port string) string {
+// JoinHostPort combines host and port into a network address
+// of the form "host:port" or, if host contains a colon, "[host]:port".
+func JoinHostPort(host, port string) string {
        // If host has colons, have to bracket it.
        if byteIndex(host, ':') >= 0 {
                return "[" + host + "]:" + port
@@ -207,7 +208,7 @@ func joinHostPort(host, port string) string {
 
 // Convert "host:port" into IP address and port.
 func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
-       host, port, err := splitHostPort(hostport)
+       host, port, err := SplitHostPort(hostport)
        if err != nil {
                goto Error
        }
@@ -218,7 +219,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
                addr = ParseIP(host)
                if addr == nil {
                        // Not an IP address.  Try as a DNS name.
-                       _, addrs, err1 := LookupHost(host)
+                       addrs, err1 := LookupHost(host)
                        if err1 != nil {
                                err = err1
                                goto Error
diff --git a/libgo/go/net/lookup.go b/libgo/go/net/lookup.go
new file mode 100644 (file)
index 0000000..7b2185e
--- /dev/null
@@ -0,0 +1,38 @@
+// 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 net
+
+import (
+       "os"
+)
+
+// LookupHost looks up the given host using the local resolver.
+// It returns an array of that host's addresses.
+func LookupHost(host string) (addrs []string, err os.Error) {
+       addrs, err, ok := cgoLookupHost(host)
+       if !ok {
+               addrs, err = goLookupHost(host)
+       }
+       return
+}
+
+// LookupIP looks up host using the local resolver.
+// It returns an array of that host's IPv4 and IPv6 addresses.
+func LookupIP(host string) (addrs []IP, err os.Error) {
+       addrs, err, ok := cgoLookupIP(host)
+       if !ok {
+               addrs, err = goLookupIP(host)
+       }
+       return
+}
+
+// LookupPort looks up the port for the given network and service.
+func LookupPort(network, service string) (port int, err os.Error) {
+       port, err, ok := cgoLookupPort(network, service)
+       if !ok {
+               port, err = goLookupPort(network, service)
+       }
+       return
+}
index 275b31c..da79283 100644 (file)
@@ -15,50 +15,49 @@ var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check f
 
 type DialErrorTest struct {
        Net     string
-       Laddr   string
        Raddr   string
        Pattern string
 }
 
 var dialErrorTests = []DialErrorTest{
        {
-               "datakit", "", "mh/astro/r70",
+               "datakit", "mh/astro/r70",
                "dial datakit mh/astro/r70: unknown network datakit",
        },
        {
-               "tcp", "", "127.0.0.1:☺",
+               "tcp", "127.0.0.1:☺",
                "dial tcp 127.0.0.1:☺: unknown port tcp/☺",
        },
        {
-               "tcp", "", "no-such-name.google.com.:80",
+               "tcp", "no-such-name.google.com.:80",
                "dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.( on .*)?: no (.*)",
        },
        {
-               "tcp", "", "no-such-name.no-such-top-level-domain.:80",
+               "tcp", "no-such-name.no-such-top-level-domain.:80",
                "dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.( on .*)?: no (.*)",
        },
        {
-               "tcp", "", "no-such-name:80",
+               "tcp", "no-such-name:80",
                `dial tcp no-such-name:80: lookup no-such-name\.(.*\.)?( on .*)?: no (.*)`,
        },
        {
-               "tcp", "", "mh/astro/r70:http",
+               "tcp", "mh/astro/r70:http",
                "dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name",
        },
        {
-               "unix", "", "/etc/file-not-found",
+               "unix", "/etc/file-not-found",
                "dial unix /etc/file-not-found: [nN]o such file or directory",
        },
        {
-               "unix", "", "/etc/",
-               "dial unix /etc/: ([pP]ermission denied|[sS]ocket operation on non-socket|[cC]onnection refused)",
+               "unix", "/etc/",
+               "dial unix /etc/: ([pP]ermission denied|socket operation on non-socket|connection refused)",
        },
        {
-               "unixpacket", "", "/etc/file-not-found",
+               "unixpacket", "/etc/file-not-found",
                "dial unixpacket /etc/file-not-found: no such file or directory",
        },
        {
-               "unixpacket", "", "/etc/",
+               "unixpacket", "/etc/",
                "dial unixpacket /etc/: (permission denied|socket operation on non-socket|connection refused)",
        },
 }
@@ -69,7 +68,7 @@ func TestDialError(t *testing.T) {
                return
        }
        for i, tt := range dialErrorTests {
-               c, e := Dial(tt.Net, tt.Laddr, tt.Raddr)
+               c, e := Dial(tt.Net, tt.Raddr)
                if c != nil {
                        c.Close()
                }
index 7d25058..8f8327a 100644 (file)
@@ -50,8 +50,8 @@ func readServices() {
        file.close()
 }
 
-// LookupPort looks up the port for the given network and service.
-func LookupPort(network, service string) (port int, err os.Error) {
+// goLookupPort is the native Go implementation of LookupPort.
+func goLookupPort(network, service string) (port int, err os.Error) {
        onceReadServices.Do(readServices)
 
        switch network {
index 3dda500..37695a0 100644 (file)
@@ -54,13 +54,15 @@ func runServe(t *testing.T, network, addr string, listening chan<- string, done
 }
 
 func connect(t *testing.T, network, addr string, isEmpty bool) {
-       var laddr string
+       var fd Conn
+       var err os.Error
        if network == "unixgram" {
-               laddr = addr + ".local"
+               fd, err = DialUnix(network, &UnixAddr{addr + ".local", network}, &UnixAddr{addr, network})
+       } else {
+               fd, err = Dial(network, addr)
        }
-       fd, err := Dial(network, laddr, addr)
        if err != nil {
-               t.Fatalf("net.Dial(%q, %q, %q) = _, %v", network, laddr, addr, err)
+               t.Fatalf("net.Dial(%q, %q) = _, %v", network, addr, err)
        }
        fd.SetReadTimeout(1e9) // 1s
 
index 5a88ddc..933700a 100644 (file)
@@ -52,14 +52,16 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal
                }
        }
 
+       if fd, err = newFD(s, f, p, net); err != nil {
+               closesocket(s)
+               return nil, err
+       }
+
        if ra != nil {
-               e = syscall.Connect(s, ra)
-               for e == syscall.EINTR {
-                       e = syscall.Connect(s, ra)
-               }
-               if e != 0 {
+               if err = fd.connect(ra); err != nil {
+                       fd.sysfd = -1
                        closesocket(s)
-                       return nil, os.Errno(e)
+                       return nil, err
                }
        }
 
@@ -68,12 +70,7 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal
        sa, _ = syscall.Getpeername(s)
        raddr := toAddr(sa)
 
-       fd, err = newFD(s, f, p, net, laddr, raddr)
-       if err != nil {
-               closesocket(s)
-               return nil, err
-       }
-
+       fd.setAddr(laddr, raddr)
        return fd, nil
 }
 
@@ -170,9 +167,9 @@ func (e *UnknownSocketError) String() string {
 func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) {
        switch a := sa.(type) {
        case *syscall.SockaddrInet4:
-               return joinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
+               return JoinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
        case *syscall.SockaddrInet6:
-               return joinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
+               return JoinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
        case *syscall.SockaddrUnix:
                return a.Name, nil
        }
index a4bca11..b484be2 100644 (file)
@@ -34,7 +34,7 @@ func (a *TCPAddr) String() string {
        if a == nil {
                return "<nil>"
        }
-       return joinHostPort(a.IP.String(), itoa(a.Port))
+       return JoinHostPort(a.IP.String(), itoa(a.Port))
 }
 
 func (a *TCPAddr) family() int {
@@ -213,8 +213,9 @@ func (c *TCPConn) SetNoDelay(noDelay bool) os.Error {
 // Closing c does not affect f, and closing f does not affect c.
 func (c *TCPConn) File() (f *os.File, err os.Error) { return c.fd.dup() }
 
-// DialTCP is like Dial but can only connect to TCP networks
-// and returns a TCPConn structure.
+// DialTCP connects to the remote address raddr on the network net,
+// which must be "tcp", "tcp4", or "tcp6".  If laddr is not nil, it is used
+// as the local address for the connection.
 func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error) {
        if raddr == nil {
                return nil, &OpError{"dial", "tcp", nil, errMissingAddress}
index f62009c..fbfad9d 100644 (file)
@@ -78,7 +78,7 @@ func (c *Conn) Close() os.Error {
 // Dial connects to the given address on the given network using net.Dial
 // and then returns a new Conn for the connection.
 func Dial(network, addr string) (*Conn, os.Error) {
-       c, err := net.Dial(network, "", addr)
+       c, err := net.Dial(network, addr)
        if err != nil {
                return nil, err
        }
index 09a257d..0dbab58 100644 (file)
@@ -11,7 +11,7 @@ import (
 )
 
 func testTimeout(t *testing.T, network, addr string, readFrom bool) {
-       fd, err := Dial(network, "", addr)
+       fd, err := Dial(network, addr)
        if err != nil {
                t.Errorf("dial %s %s failed: %v", network, addr, err)
                return
index f927449..44d618d 100644 (file)
@@ -34,7 +34,7 @@ func (a *UDPAddr) String() string {
        if a == nil {
                return "<nil>"
        }
-       return joinHostPort(a.IP.String(), itoa(a.Port))
+       return JoinHostPort(a.IP.String(), itoa(a.Port))
 }
 
 func (a *UDPAddr) family() int {
index 5db679a..8ba5df9 100644 (file)
@@ -48,7 +48,7 @@ func NewImporter(conn io.ReadWriter) *Importer {
 
 // Import imports a set of channels from the given network and address.
 func Import(network, remoteaddr string) (*Importer, os.Error) {
-       conn, err := net.Dial(network, "", remoteaddr)
+       conn, err := net.Dial(network, remoteaddr)
        if err != nil {
                return nil, err
        }
index 1b5c560..fd4d8f7 100644 (file)
@@ -399,7 +399,7 @@ func TestImportFlowControl(t *testing.T) {
 
 func testFlow(sendDone chan bool, ch <-chan int, N int, t *testing.T) {
        go func() {
-               time.Sleep(1e9)
+               time.Sleep(0.5e9)
                sendDone <- false
        }()
 
index 57d4a47..01268e5 100644 (file)
@@ -108,3 +108,21 @@ func Truncate(name string, size int64) Error {
        }
        return nil
 }
+
+// basename removes trailing slashes and the leading directory name from path name
+func basename(name string) string {
+       i := len(name) - 1
+       // Remove trailing slashes
+       for ; i > 0 && name[i] == '/'; i-- {
+               name = name[:i]
+       }
+       // Remove leading directory name
+       for i--; i >= 0; i-- {
+               if name[i] == '/' {
+                       name = name[i+1:]
+                       break
+               }
+       }
+
+       return name
+}
index 9237252..8af4afc 100644 (file)
@@ -208,7 +208,7 @@ func DialHTTP(network, address string) (*Client, os.Error) {
 // at the specified network address and path.
 func DialHTTPPath(network, address, path string) (*Client, os.Error) {
        var err os.Error
-       conn, err := net.Dial(network, "", address)
+       conn, err := net.Dial(network, address)
        if err != nil {
                return nil, err
        }
@@ -229,7 +229,7 @@ func DialHTTPPath(network, address, path string) (*Client, os.Error) {
 
 // Dial connects to an RPC server at the specified network address.
 func Dial(network, address string) (*Client, os.Error) {
-       conn, err := net.Dial(network, "", address)
+       conn, err := net.Dial(network, address)
        if err != nil {
                return nil, err
        }
index 5b806bd..57e977d 100644 (file)
@@ -116,7 +116,7 @@ func NewClient(conn io.ReadWriteCloser) *rpc.Client {
 
 // Dial connects to a JSON-RPC server at the specified network address.
 func Dial(network, address string) (*rpc.Client, os.Error) {
-       conn, err := net.Dial(network, "", address)
+       conn, err := net.Dial(network, address)
        if err != nil {
                return nil, err
        }
index 603465e..a060917 100644 (file)
@@ -15,7 +15,15 @@ import (
 )
 
 func TestCPUProfile(t *testing.T) {
-       if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
+       switch runtime.GOOS {
+       case "darwin":
+               // see Apple Bug Report #9177434 (copied into change description)
+               return
+       case "plan9":
+               // unimplemented
+               return
+       case "windows":
+               // unimplemented
                return
        }
 
index 2f6d2f3..3f89af1 100644 (file)
@@ -39,7 +39,7 @@ type Client struct {
 
 // Dial returns a new Client connected to an SMTP server at addr.
 func Dial(addr string) (*Client, os.Error) {
-       conn, err := net.Dial("tcp", "", addr)
+       conn, err := net.Dial("tcp", addr)
        if err != nil {
                return nil, err
        }
index 1bea8f0..3d7337f 100644 (file)
@@ -74,7 +74,11 @@ func TestSortStrings(t *testing.T) {
 }
 
 func TestSortLarge_Random(t *testing.T) {
-       data := make([]int, 1000000)
+       n := 1000000
+       if testing.Short() {
+               n /= 100
+       }
+       data := make([]int, n)
        for i := 0; i < len(data); i++ {
                data[i] = rand.Intn(100)
        }
@@ -174,6 +178,9 @@ func lg(n int) int {
 
 func TestBentleyMcIlroy(t *testing.T) {
        sizes := []int{100, 1023, 1024, 1025}
+       if testing.Short() {
+               sizes = []int{100, 127, 128, 129}
+       }
        dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"}
        modes := []string{"copy", "reverse", "reverse1", "reverse2", "sort", "dither"}
        var tmp1, tmp2 [1025]int
index 5f009e5..93c7c46 100644 (file)
@@ -275,20 +275,10 @@ func Join(a []string, sep string) string {
        }
 
        b := make([]byte, n)
-       bp := 0
-       for i := 0; i < len(a); i++ {
-               s := a[i]
-               for j := 0; j < len(s); j++ {
-                       b[bp] = s[j]
-                       bp++
-               }
-               if i+1 < len(a) {
-                       s = sep
-                       for j := 0; j < len(s); j++ {
-                               b[bp] = s[j]
-                               bp++
-                       }
-               }
+       bp := copy(b, a[0])
+       for _, s := range a[1:] {
+               bp += copy(b[bp:], sep)
+               bp += copy(b[bp:], s)
        }
        return string(b)
 }
@@ -312,9 +302,19 @@ func Map(mapping func(rune int) int, s string) string {
        // fine.  It could also shrink but that falls out naturally.
        maxbytes := len(s) // length of b
        nbytes := 0        // number of bytes encoded in b
-       b := make([]byte, maxbytes)
-       for _, c := range s {
+       // The output buffer b is initialized on demand, the first
+       // time a character differs.
+       var b []byte
+
+       for i, c := range s {
                rune := mapping(c)
+               if b == nil {
+                       if rune == c {
+                               continue
+                       }
+                       b = make([]byte, maxbytes)
+                       nbytes = copy(b, s[:i])
+               }
                if rune >= 0 {
                        wid := 1
                        if rune >= utf8.RuneSelf {
@@ -330,6 +330,9 @@ func Map(mapping func(rune int) int, s string) string {
                        nbytes += utf8.EncodeRune(b[nbytes:maxbytes], rune)
                }
        }
+       if b == nil {
+               return s
+       }
        return string(b[0:nbytes])
 }
 
index 41e3987..c45b148 100644 (file)
@@ -6,10 +6,12 @@ package strings_test
 
 import (
        "os"
+       "reflect"
        "strconv"
        . "strings"
        "testing"
        "unicode"
+       "unsafe"
        "utf8"
 )
 
@@ -429,12 +431,32 @@ func TestMap(t *testing.T) {
        if m != expect {
                t.Errorf("drop: expected %q got %q", expect, m)
        }
+
+       // 6. Identity
+       identity := func(rune int) int {
+               return rune
+       }
+       orig := "Input string that we expect not to be copied."
+       m = Map(identity, orig)
+       if (*reflect.StringHeader)(unsafe.Pointer(&orig)).Data !=
+               (*reflect.StringHeader)(unsafe.Pointer(&m)).Data {
+               t.Error("unexpected copy during identity map")
+       }
 }
 
 func TestToUpper(t *testing.T) { runStringTests(t, ToUpper, "ToUpper", upperTests) }
 
 func TestToLower(t *testing.T) { runStringTests(t, ToLower, "ToLower", lowerTests) }
 
+func BenchmarkMapNoChanges(b *testing.B) {
+       identity := func(rune int) int {
+               return rune
+       }
+       for i := 0; i < b.N; i++ {
+               Map(identity, "Some string that won't be modified.")
+       }
+}
+
 func TestSpecialCase(t *testing.T) {
        lower := "abcçdefgğhıijklmnoöprsştuüvyz"
        upper := "ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZ"
@@ -617,7 +639,11 @@ func equal(m string, s1, s2 string, t *testing.T) bool {
 
 func TestCaseConsistency(t *testing.T) {
        // Make a string of all the runes.
-       a := make([]int, unicode.MaxRune+1)
+       numRunes := unicode.MaxRune + 1
+       if testing.Short() {
+               numRunes = 1000
+       }
+       a := make([]int, numRunes)
        for i := range a {
                a[i] = i
        }
@@ -627,10 +653,10 @@ func TestCaseConsistency(t *testing.T) {
        lower := ToLower(s)
 
        // Consistency checks
-       if n := utf8.RuneCountInString(upper); n != unicode.MaxRune+1 {
+       if n := utf8.RuneCountInString(upper); n != numRunes {
                t.Error("rune count wrong in upper:", n)
        }
-       if n := utf8.RuneCountInString(lower); n != unicode.MaxRune+1 {
+       if n := utf8.RuneCountInString(lower); n != numRunes {
                t.Error("rune count wrong in lower:", n)
        }
        if !equal("ToUpper(upper)", ToUpper(upper), upper, t) {
index 7b204b1..119ad00 100644 (file)
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package atomic
+package atomic_test
 
 import (
        "runtime"
+       . "sync/atomic"
        "testing"
        "unsafe"
 )
@@ -27,6 +28,16 @@ const (
        magic64 = 0xdeddeadbeefbeef
 )
 
+// Do the 64-bit functions panic?  If so, don't bother testing.
+var test64err = func() (err interface{}) {
+       defer func() {
+               err = recover()
+       }()
+       var x int64
+       AddInt64(&x, 1)
+       return nil
+}()
+
 func TestAddInt32(t *testing.T) {
        var x struct {
                before int32
@@ -70,6 +81,10 @@ func TestAddUint32(t *testing.T) {
 }
 
 func TestAddInt64(t *testing.T) {
+       if test64err != nil {
+               t.Logf("Skipping 64-bit tests: %v", test64err)
+               return
+       }
        var x struct {
                before int64
                i      int64
@@ -91,6 +106,10 @@ func TestAddInt64(t *testing.T) {
 }
 
 func TestAddUint64(t *testing.T) {
+       if test64err != nil {
+               t.Logf("Skipping 64-bit tests: %v", test64err)
+               return
+       }
        var x struct {
                before uint64
                i      uint64
@@ -193,6 +212,10 @@ func TestCompareAndSwapUint32(t *testing.T) {
 }
 
 func TestCompareAndSwapInt64(t *testing.T) {
+       if test64err != nil {
+               t.Logf("Skipping 64-bit tests: %v", test64err)
+               return
+       }
        var x struct {
                before int64
                i      int64
@@ -222,6 +245,10 @@ func TestCompareAndSwapInt64(t *testing.T) {
 }
 
 func TestCompareAndSwapUint64(t *testing.T) {
+       if test64err != nil {
+               t.Logf("Skipping 64-bit tests: %v", test64err)
+               return
+       }
        var x struct {
                before uint64
                i      uint64
@@ -370,10 +397,11 @@ func hammerCompareAndSwapUintptr32(uval *uint32, count int) {
 }
 
 func TestHammer32(t *testing.T) {
-       const (
-               n = 100000
-               p = 4
-       )
+       const p = 4
+       n := 100000
+       if testing.Short() {
+               n = 1000
+       }
        defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(p))
 
        for _, tt := range hammer32 {
@@ -391,7 +419,7 @@ func TestHammer32(t *testing.T) {
                for i := 0; i < p; i++ {
                        <-c
                }
-               if val != n*p {
+               if val != uint32(n)*p {
                        t.Errorf("%s: val=%d want %d", tt.name, val, n*p)
                }
        }
@@ -478,10 +506,15 @@ func hammerCompareAndSwapUintptr64(uval *uint64, count int) {
 }
 
 func TestHammer64(t *testing.T) {
-       const (
-               n = 100000
-               p = 4
-       )
+       if test64err != nil {
+               t.Logf("Skipping 64-bit tests: %v", test64err)
+               return
+       }
+       const p = 4
+       n := 100000
+       if testing.Short() {
+               n = 1000
+       }
        defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(p))
 
        for _, tt := range hammer64 {
@@ -499,7 +532,7 @@ func TestHammer64(t *testing.T) {
                for i := 0; i < p; i++ {
                        <-c
                }
-               if val != n*p {
+               if val != uint64(n)*p {
                        t.Errorf("%s: val=%d want %d", tt.name, val, n*p)
                }
        }
index 1335def..ec5a0d3 100644 (file)
@@ -55,3 +55,8 @@ func AddUint64(val *uint64, delta uint64) (new uint64)
 
 // AddUintptr atomically adds delta to *val and returns the new value.
 func AddUintptr(val *uintptr, delta uintptr) (new uintptr)
+
+// Helper for ARM.  Linker will discard on other systems
+func panic64() {
+       panic("sync/atomic: broken 64-bit atomic operations (buggy QEMU)")
+}
index 4050792..9fb89f8 100644 (file)
@@ -102,16 +102,20 @@ func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) {
 }
 
 func TestRWMutex(t *testing.T) {
-       HammerRWMutex(1, 1, 1000)
-       HammerRWMutex(1, 3, 1000)
-       HammerRWMutex(1, 10, 1000)
-       HammerRWMutex(4, 1, 1000)
-       HammerRWMutex(4, 3, 1000)
-       HammerRWMutex(4, 10, 1000)
-       HammerRWMutex(10, 1, 1000)
-       HammerRWMutex(10, 3, 1000)
-       HammerRWMutex(10, 10, 1000)
-       HammerRWMutex(10, 5, 10000)
+       n := 1000
+       if testing.Short() {
+               n = 5
+       }
+       HammerRWMutex(1, 1, n)
+       HammerRWMutex(1, 3, n)
+       HammerRWMutex(1, 10, n)
+       HammerRWMutex(4, 1, n)
+       HammerRWMutex(4, 3, n)
+       HammerRWMutex(4, 10, n)
+       HammerRWMutex(10, 1, n)
+       HammerRWMutex(10, 3, n)
+       HammerRWMutex(10, 10, n)
+       HammerRWMutex(10, 5, n)
 }
 
 func TestRLocker(t *testing.T) {
index 711d5dd..4ada113 100644 (file)
@@ -67,7 +67,7 @@ func Dial(network, raddr string, priority Priority, prefix string) (w *Writer, e
                conn, err = unixSyslog()
        } else {
                var c net.Conn
-               c, err = net.Dial(network, "", raddr)
+               c, err = net.Dial(network, raddr)
                conn = netConn{c}
        }
        return &Writer{priority, prefix, conn}, err
index b4daf88..fa15e88 100644 (file)
@@ -19,7 +19,7 @@ func unixSyslog() (conn serverConn, err os.Error) {
        for _, network := range logTypes {
                for _, path := range logPaths {
                        raddr = path
-                       conn, err := net.Dial(network, "", raddr)
+                       conn, err := net.Dial(network, raddr)
                        if err != nil {
                                continue
                        } else {
index ab8cf99..d189390 100644 (file)
@@ -48,6 +48,13 @@ import (
 )
 
 var (
+       // The short flag requests that tests run more quickly, but its functionality
+       // is provided by test writers themselves.  The testing package is just its
+       // home.  The all.bash installation script sets it to make installation more
+       // efficient, but by default the flag is off so a plain "gotest" will do a
+       // full test of the package.
+       short = flag.Bool("test.short", false, "run smaller test suite to save time")
+
        // Report as tests are run; default is silent for success.
        chatty         = flag.Bool("test.v", false, "verbose: print additional output")
        match          = flag.String("test.run", "", "regular expression to select tests to run")
@@ -56,6 +63,11 @@ var (
        cpuProfile     = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution")
 )
 
+// Short reports whether the -test.short flag is set.
+func Short() bool {
+       return *short
+}
+
 
 // Insert final newline if needed and tabs after internal newlines.
 func tabify(s string) string {
@@ -174,7 +186,7 @@ func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern
                go tRunner(t, &tests[i])
                <-t.ch
                ns += time.Nanoseconds()
-               tstr := fmt.Sprintf("(%.1f seconds)", float64(ns)/1e9)
+               tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9)
                if t.failed {
                        println("--- FAIL:", tests[i].Name, tstr)
                        print(t.errors)
index 5fe4d7f..eb6bb25 100644 (file)
@@ -5,6 +5,7 @@
 package time_test
 
 import (
+       "fmt"
        "os"
        "syscall"
        "testing"
@@ -132,8 +133,22 @@ func TestAfterStop(t *testing.T) {
        }
 }
 
+func TestAfterQueuing(t *testing.T) {
+       // This test flakes out on some systems,
+       // so we'll try it a few times before declaring it a failure.
+       const attempts = 3
+       err := os.NewError("!=nil")
+       for i := 0; i < attempts && err != nil; i++ {
+               if err = testAfterQueuing(t); err != nil {
+                       t.Logf("attempt %v failed: %v", i, err)
+               }
+       }
+       if err != nil {
+               t.Fatal(err)
+       }
+}
+
 // For gccgo omit 0 for now because it can take too long to start the
-// thread.
 var slots = []int{5, 3, 6, 6, 6, 1, 1, 2, 7, 9, 4, 8, /*0*/}
 
 type afterResult struct {
@@ -145,7 +160,7 @@ func await(slot int, result chan<- afterResult, ac <-chan int64) {
        result <- afterResult{slot, <-ac}
 }
 
-func TestAfterQueuing(t *testing.T) {
+func testAfterQueuing(t *testing.T) os.Error {
        const (
                Delta = 100 * 1e6
        )
@@ -162,13 +177,14 @@ func TestAfterQueuing(t *testing.T) {
        for _, slot := range slots {
                r := <-result
                if r.slot != slot {
-                       t.Fatalf("after queue got slot %d, expected %d", r.slot, slot)
+                       return fmt.Errorf("after queue got slot %d, expected %d", r.slot, slot)
                }
                ns := r.t - t0
                target := int64(slot * Delta)
                slop := int64(Delta) / 4
                if ns < target-slop || ns > target+slop {
-              &n