]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libgo: Update to Go 1.0.2 release.
authorIan Lance Taylor <ian@gcc.gnu.org>
Mon, 25 Jun 2012 16:20:03 +0000 (16:20 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Mon, 25 Jun 2012 16:20:03 +0000 (16:20 +0000)
From-SVN: r188943

59 files changed:
libgo/MERGE
libgo/go/compress/flate/deflate.go
libgo/go/compress/flate/deflate_test.go
libgo/go/crypto/aes/const.go
libgo/go/crypto/ecdsa/ecdsa.go
libgo/go/crypto/rsa/pkcs1v15.go
libgo/go/crypto/tls/handshake_messages.go
libgo/go/crypto/x509/x509.go
libgo/go/debug/gosym/pclntab_test.go
libgo/go/encoding/base64/base64.go
libgo/go/encoding/base64/base64_test.go
libgo/go/encoding/gob/doc.go
libgo/go/encoding/json/decode.go
libgo/go/encoding/json/decode_test.go
libgo/go/encoding/json/encode.go
libgo/go/flag/flag.go
libgo/go/fmt/doc.go
libgo/go/go/ast/ast.go
libgo/go/go/ast/ast_test.go [new file with mode: 0644]
libgo/go/go/build/build.go
libgo/go/go/parser/parser.go
libgo/go/go/parser/parser_test.go
libgo/go/go/printer/nodes.go
libgo/go/log/log.go
libgo/go/math/big/nat.go
libgo/go/math/big/nat_test.go
libgo/go/mime/multipart/multipart.go
libgo/go/mime/multipart/multipart_test.go
libgo/go/net/file.go
libgo/go/net/http/client.go
libgo/go/net/http/client_test.go
libgo/go/net/http/proxy_test.go
libgo/go/net/http/response.go
libgo/go/net/http/response_test.go
libgo/go/net/http/server.go
libgo/go/net/http/transfer.go
libgo/go/net/http/transport.go
libgo/go/net/mail/message.go
libgo/go/net/mail/message_test.go
libgo/go/net/url/url.go
libgo/go/net/url/url_test.go
libgo/go/os/exec/exec.go
libgo/go/path/filepath/path.go
libgo/go/path/filepath/path_plan9.go
libgo/go/path/filepath/path_test.go
libgo/go/regexp/regexp.go
libgo/go/regexp/syntax/parse.go
libgo/go/regexp/syntax/parse_test.go
libgo/go/runtime/extern.go
libgo/go/strconv/itoa.go
libgo/go/strings/example_test.go
libgo/go/text/template/exec.go
libgo/go/text/template/exec_test.go
libgo/go/time/sleep_test.go
libgo/go/unicode/tables.go
libgo/runtime/malloc.goc
libgo/runtime/mfinal.c
libgo/runtime/mgc0.c
libgo/runtime/time.goc

index 33159fdd4c89e7742ee8742b096630da2f79f077..e3e47d3bd1eb37baea9ceb0d40d5df20a5d7f4e8 100644 (file)
@@ -1,4 +1,4 @@
-2ccfd4b451d3
+5e806355a9e1
 
 The first line of this file holds the Mercurial revision number of the
 last merge done from the master library sources.
index 20408409c8ecb4ab4b416c2558b75af6c163d186..e511b50fd1b079066382eaf59b5de29c53a978e8 100644 (file)
@@ -32,6 +32,7 @@ const (
        hashSize            = 1 << hashBits
        hashMask            = (1 << hashBits) - 1
        hashShift           = (hashBits + minMatchLength - 1) / minMatchLength
+       maxHashOffset       = 1 << 24
 
        skipNever = math.MaxInt32
 )
@@ -106,6 +107,25 @@ func (d *compressor) fillDeflate(b []byte) int {
                        d.blockStart = math.MaxInt32
                }
                d.hashOffset += windowSize
+               if d.hashOffset > maxHashOffset {
+                       delta := d.hashOffset - 1
+                       d.hashOffset -= delta
+                       d.chainHead -= delta
+                       for i, v := range d.hashPrev {
+                               if v > delta {
+                                       d.hashPrev[i] -= delta
+                               } else {
+                                       d.hashPrev[i] = 0
+                               }
+                       }
+                       for i, v := range d.hashHead {
+                               if v > delta {
+                                       d.hashHead[i] -= delta
+                               } else {
+                                       d.hashHead[i] = 0
+                               }
+                       }
+               }
        }
        n := copy(d.window[d.windowEnd:], b)
        d.windowEnd += n
index 543c5950586ce3e31a49bdf6b1cc4fab8499e605..f1e6db2ace4f19bab5058306e39f040fdddab2d9 100644 (file)
@@ -94,6 +94,50 @@ func TestDeflate(t *testing.T) {
        }
 }
 
+// A sparseReader returns a stream consisting of 0s followed by 1<<16 1s.
+// This tests missing hash references in a very large input.
+type sparseReader struct {
+       l   int64
+       cur int64
+}
+
+func (r *sparseReader) Read(b []byte) (n int, err error) {
+       if r.cur >= r.l {
+               return 0, io.EOF
+       }
+       n = len(b)
+       cur := r.cur + int64(n)
+       if cur > r.l {
+               n -= int(cur - r.l)
+               cur = r.l
+       }
+       for i := range b[0:n] {
+               if r.cur+int64(i) >= r.l-1<<16 {
+                       b[i] = 1
+               } else {
+                       b[i] = 0
+               }
+       }
+       r.cur = cur
+       return
+}
+
+func TestVeryLongSparseChunk(t *testing.T) {
+       if testing.Short() {
+               t.Logf("skipping sparse chunk during short test")
+               return
+       }
+       w, err := NewWriter(ioutil.Discard, 1)
+       if err != nil {
+               t.Errorf("NewWriter: %v", err)
+               return
+       }
+       if _, err = io.Copy(w, &sparseReader{l: 23E8}); err != nil {
+               t.Errorf("Compress failed: %v", err)
+               return
+       }
+}
+
 type syncBuffer struct {
        buf    bytes.Buffer
        mu     sync.RWMutex
index f0b4eabf6ea0f4e4e8b69230f1307d7ef2468336..aee73a7c52c75eb2f6d2c1e451c3905949debfd1 100644 (file)
@@ -11,11 +11,11 @@ package aes
 // http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf
 
 // AES is based on the mathematical behavior of binary polynomials
-// (polynomials over GF(2)) modulo the irreducible polynomial xâ\81¸ + xâ\81´ + x² + x + 1.
+// (polynomials over GF(2)) modulo the irreducible polynomial xâ\81¸ + xâ\81´ + x³ + x + 1.
 // Addition of these binary polynomials corresponds to binary xor.
 // Reducing mod poly corresponds to binary xor with poly every
 // time a 0x100 bit appears.
-const poly = 1<<8 | 1<<4 | 1<<3 | 1<<1 | 1<<0 // xâ\81¸ + xâ\81´ + x² + x + 1
+const poly = 1<<8 | 1<<4 | 1<<3 | 1<<1 | 1<<0 // xâ\81¸ + xâ\81´ + x³ + x + 1
 
 // Powers of x mod poly in GF(2).
 var powx = [16]byte{
index b28239b7862d3714eea3a353b2777eef8cddb6f7..8508e3b4f8da113771a725f9ed8249144a5a333d 100644 (file)
@@ -66,7 +66,9 @@ func GenerateKey(c elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error)
 // hashToInt converts a hash value to an integer. There is some disagreement
 // about how this is done. [NSA] suggests that this is done in the obvious
 // manner, but [SECG] truncates the hash to the bit-length of the curve order
-// first. We follow [SECG] because that's what OpenSSL does.
+// first. We follow [SECG] because that's what OpenSSL does. Additionally,
+// OpenSSL right shifts excess bits from the number if the hash is too large
+// and we mirror that too.
 func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
        orderBits := c.Params().N.BitLen()
        orderBytes := (orderBits + 7) / 8
@@ -75,7 +77,7 @@ func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
        }
 
        ret := new(big.Int).SetBytes(hash)
-       excess := orderBytes*8 - orderBits
+       excess := len(hash)*8 - orderBits
        if excess > 0 {
                ret.Rsh(ret, uint(excess))
        }
index 254f4a3da04203ff0a7b5a2b6be4ee667f9d1654..a32236e4729e58a42e21d265333210ddfa94dd2b 100644 (file)
@@ -151,6 +151,7 @@ func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) {
 var hashPrefixes = map[crypto.Hash][]byte{
        crypto.MD5:       {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
        crypto.SHA1:      {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
+       crypto.SHA224:    {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c},
        crypto.SHA256:    {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
        crypto.SHA384:    {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
        crypto.SHA512:    {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
index e1517cc794ff21c8eb474ab094a7e9d8e1f5b718..54c7a3e6316bcec5a2720d9a1e0189577e689485 100644 (file)
@@ -563,7 +563,7 @@ func (m *certificateMsg) unmarshal(data []byte) bool {
                if len(d) < 4 {
                        return false
                }
-               certLen := uint32(d[0])<<24 | uint32(d[1])<<8 | uint32(d[2])
+               certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
                if uint32(len(d)) < 3+certLen {
                        return false
                }
@@ -575,7 +575,7 @@ func (m *certificateMsg) unmarshal(data []byte) bool {
        m.certificates = make([][]byte, numCerts)
        d = data[7:]
        for i := 0; i < numCerts; i++ {
-               certLen := uint32(d[0])<<24 | uint32(d[1])<<8 | uint32(d[2])
+               certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
                m.certificates[i] = d[3 : 3+certLen]
                d = d[3+certLen:]
        }
index 8dae7e7fcf948c302c0ac97dd1fa415e5fb6334a..c4d85e67f0c28667229388c19a061fe0540367b8 100644 (file)
@@ -388,10 +388,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
                return ErrUnsupportedAlgorithm
        }
 
-       h := hashType.New()
-       if h == nil {
+       if !hashType.Available() {
                return ErrUnsupportedAlgorithm
        }
+       h := hashType.New()
 
        h.Write(signed)
        digest := h.Sum(nil)
index b2400bb3ba7b99799998c3e321615b8670da3103..ade704335d14b843ec67b3d2b151f1bbb687f968 100644 (file)
@@ -7,14 +7,19 @@ package gosym
 import (
        "debug/elf"
        "fmt"
+       "io/ioutil"
        "os"
        "os/exec"
+       "path/filepath"
        "runtime"
        "strings"
        "testing"
 )
 
-var pclinetestBinary string
+var (
+       pclineTempDir    string
+       pclinetestBinary string
+)
 
 func dotest() bool {
        // For now, only works on ELF platforms.
@@ -24,10 +29,18 @@ func dotest() bool {
        if pclinetestBinary != "" {
                return true
        }
+       var err error
+       pclineTempDir, err = ioutil.TempDir("", "pclinetest")
+       if err != nil {
+               panic(err)
+       }
+       if strings.Contains(pclineTempDir, " ") {
+               panic("unexpected space in tempdir")
+       }
        // This command builds pclinetest from pclinetest.asm;
        // the resulting binary looks like it was built from pclinetest.s,
        // but we have renamed it to keep it away from the go tool.
-       pclinetestBinary = os.TempDir() + "/pclinetest"
+       pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
        command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -E main -o %s %s.6",
                pclinetestBinary, pclinetestBinary, pclinetestBinary)
        cmd := exec.Command("sh", "-c", command)
@@ -170,6 +183,7 @@ func TestPCLine(t *testing.T) {
        if !dotest() {
                return
        }
+       defer os.RemoveAll(pclineTempDir)
 
        f, tab := crack(pclinetestBinary, t)
        text := f.Section(".text")
index f8a51a4e7568fc8c5087718bb0057c1b5db306dc..0b842f066105d14ed3a724af215e970bb540ea84 100644 (file)
@@ -318,7 +318,7 @@ func (d *decoder) Read(p []byte) (n int, err error) {
        }
        nn, d.err = io.ReadAtLeast(d.r, d.buf[d.nbuf:nn], 4-d.nbuf)
        d.nbuf += nn
-       if d.nbuf < 4 {
+       if d.err != nil || d.nbuf < 4 {
                return 0, d.err
        }
 
index 9c35372598cb292220b4bdb5781cff92e636a806..f9b863c364c90a75033f388b648b809f5c40ff32 100644 (file)
@@ -6,9 +6,11 @@ package base64
 
 import (
        "bytes"
+       "errors"
        "io"
        "io/ioutil"
        "testing"
+       "time"
 )
 
 type testpair struct {
@@ -226,3 +228,50 @@ func TestNewLineCharacters(t *testing.T) {
                }
        }
 }
+
+type nextRead struct {
+       n   int   // bytes to return
+       err error // error to return
+}
+
+// faultInjectReader returns data from source, rate-limited
+// and with the errors as written to nextc.
+type faultInjectReader struct {
+       source string
+       nextc  <-chan nextRead
+}
+
+func (r *faultInjectReader) Read(p []byte) (int, error) {
+       nr := <-r.nextc
+       if len(p) > nr.n {
+               p = p[:nr.n]
+       }
+       n := copy(p, r.source)
+       r.source = r.source[n:]
+       return n, nr.err
+}
+
+// tests that we don't ignore errors from our underlying reader
+func TestDecoderIssue3577(t *testing.T) {
+       next := make(chan nextRead, 10)
+       wantErr := errors.New("my error")
+       next <- nextRead{5, nil}
+       next <- nextRead{10, wantErr}
+       d := NewDecoder(StdEncoding, &faultInjectReader{
+               source: "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==", // twas brillig...
+               nextc:  next,
+       })
+       errc := make(chan error)
+       go func() {
+               _, err := ioutil.ReadAll(d)
+               errc <- err
+       }()
+       select {
+       case err := <-errc:
+               if err != wantErr {
+                       t.Errorf("got error %v; want %v", err, wantErr)
+               }
+       case <-time.After(5 * time.Second):
+               t.Errorf("timeout; Decoder blocked without returning an error")
+       }
+}
index 96885f8ded4a2b8c0df4adb5147130a9f146ba51..821d9a3fe8ef1a9890836942758c0c5b675fb3cb 100644 (file)
@@ -116,7 +116,7 @@ uninterpreted bytes of the value.
 All other slices and arrays are sent as an unsigned count followed by that many
 elements using the standard gob encoding for their type, recursively.
 
-Maps are sent as an unsigned count followed by that man key, element
+Maps are sent as an unsigned count followed by that many key, element
 pairs. Empty but non-nil maps are sent, so if the sender has allocated
 a map, the receiver will allocate a map even no elements are
 transmitted.
index 110c6fd62386e5af5ad5871c9b31067c4175c569..d61f8870646cb32c58e430af9eed3e96aa965553 100644 (file)
@@ -273,9 +273,14 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler,
                        _, isUnmarshaler = v.Interface().(Unmarshaler)
                }
 
+               // Load value from interface, but only if the result will be
+               // usefully addressable.
                if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() {
-                       v = iv.Elem()
-                       continue
+                       e := iv.Elem()
+                       if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
+                               v = e
+                               continue
+                       }
                }
 
                pv := v
@@ -588,6 +593,11 @@ func (d *decodeState) literal(v reflect.Value) {
 // produce more helpful error messages.
 func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) {
        // Check for unmarshaler.
+       if len(item) == 0 {
+               //Empty string given
+               d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
+               return
+       }
        wantptr := item[0] == 'n' // null
        unmarshaler, pv := d.indirect(v, wantptr)
        if unmarshaler != nil {
index d758758d97816352b6c50bc2451cb2cea3d6d510..6fac22c4a35747fc1ee2ffb5aec4853a726cbcad 100644 (file)
@@ -638,3 +638,68 @@ func TestAnonymous(t *testing.T) {
                t.Fatal("Unmarshal: did set T.Y")
        }
 }
+
+// Test that the empty string doesn't panic decoding when ,string is specified
+// Issue 3450
+func TestEmptyString(t *testing.T) {
+       type T2 struct {
+               Number1 int `json:",string"`
+               Number2 int `json:",string"`
+       }
+       data := `{"Number1":"1", "Number2":""}`
+       dec := NewDecoder(strings.NewReader(data))
+       var t2 T2
+       err := dec.Decode(&t2)
+       if err == nil {
+               t.Fatal("Decode: did not return error")
+       }
+       if t2.Number1 != 1 {
+               t.Fatal("Decode: did not set Number1")
+       }
+}
+
+func intp(x int) *int {
+       p := new(int)
+       *p = x
+       return p
+}
+
+func intpp(x *int) **int {
+       pp := new(*int)
+       *pp = x
+       return pp
+}
+
+var interfaceSetTests = []struct {
+       pre  interface{}
+       json string
+       post interface{}
+}{
+       {"foo", `"bar"`, "bar"},
+       {"foo", `2`, 2.0},
+       {"foo", `true`, true},
+       {"foo", `null`, nil},
+
+       {nil, `null`, nil},
+       {new(int), `null`, nil},
+       {(*int)(nil), `null`, nil},
+       {new(*int), `null`, new(*int)},
+       {(**int)(nil), `null`, nil},
+       {intp(1), `null`, nil},
+       {intpp(nil), `null`, intpp(nil)},
+       {intpp(intp(1)), `null`, intpp(nil)},
+}
+
+func TestInterfaceSet(t *testing.T) {
+       for _, tt := range interfaceSetTests {
+               b := struct{ X interface{} }{tt.pre}
+               blob := `{"X":` + tt.json + `}`
+               if err := Unmarshal([]byte(blob), &b); err != nil {
+                       t.Errorf("Unmarshal %#q: %v", blob, err)
+                       continue
+               }
+               if !reflect.DeepEqual(b.X, tt.post) {
+                       t.Errorf("Unmarshal %#q into %#v: X=%#v, want %#v", blob, tt.pre, b.X, tt.post)
+               }
+       }
+}
index 842672c3974509e2c70e0624ac3a268c0d2b6584..b6e1cb16e5a471b198d6c620badfbbaf464dd23a 100644 (file)
@@ -96,7 +96,7 @@ import (
 //
 // Channel, complex, and function values cannot be encoded in JSON.
 // Attempting to encode such a value causes Marshal to return
-// an InvalidTypeError.
+// an UnsupportedTypeError.
 //
 // JSON cannot represent cyclic data structures and Marshal does not
 // handle them.  Passing cyclic structures to Marshal will result in
@@ -157,6 +157,8 @@ type Marshaler interface {
        MarshalJSON() ([]byte, error)
 }
 
+// An UnsupportedTypeError is returned by Marshal when attempting
+// to encode an unsupported value type.
 type UnsupportedTypeError struct {
        Type reflect.Type
 }
index f0842a18a295d8690cf6588cc6743c429422eb49..5444ad141c5905aedc97c824e2ff6d37ad7e1280 100644 (file)
@@ -620,8 +620,9 @@ func (f *FlagSet) Var(value Value, name string, usage string) {
        flag := &Flag{name, usage, value, value.String()}
        _, alreadythere := f.formal[name]
        if alreadythere {
-               fmt.Fprintf(f.out(), "%s flag redefined: %s\n", f.name, name)
-               panic("flag redefinition") // Happens only if flags are declared with identical names
+               msg := fmt.Sprintf("%s flag redefined: %s", f.name, name)
+               fmt.Fprintln(f.out(), msg)
+               panic(msg) // Happens only if flags are declared with identical names
        }
        if f.formal == nil {
                f.formal = make(map[string]*Flag)
index 9660370c2928665a3e6ca7f8302843dbf24312e9..a9b9c9d0c27ac2128bafedba1fb64f54a5f4d1ee 100644 (file)
        Fscanf and Fscanln read from a specified io.Reader; Sscan,
        Sscanf and Sscanln read from an argument string.  Scanln,
        Fscanln and Sscanln stop scanning at a newline and require that
-       the items be followed by one; Sscanf, Fscanf and Sscanf require
+       the items be followed by one; Scanf, Fscanf and Sscanf require
        newlines in the input to match newlines in the format; the other
        routines treat newlines as spaces.
 
index 7123fe58f50b40aefa16b4571bb1d809b1d158b1..d2e75dc1c0312bf70fd79c08cc2a96db1f56474f 100644 (file)
@@ -87,8 +87,12 @@ func stripTrailingWhitespace(s string) string {
        return s[0:i]
 }
 
-// Text returns the text of the comment,
-// with the comment markers - //, /*, and */ - removed.
+// Text returns the text of the comment.
+// Comment markers (//, /*, and */), the first space of a line comment, and
+// leading and trailing empty lines are removed. Multiple empty lines are
+// reduced to one, and trailing space on lines is trimmed. Unless the result
+// is empty, it is newline-terminated.
+//
 func (g *CommentGroup) Text() string {
        if g == nil {
                return ""
@@ -104,11 +108,9 @@ func (g *CommentGroup) Text() string {
                // The parser has given us exactly the comment text.
                switch c[1] {
                case '/':
-                       //-style comment
+                       //-style comment (no newline at the end)
                        c = c[2:]
-                       // Remove leading space after //, if there is one.
-                       // TODO(gri) This appears to be necessary in isolated
-                       //           cases (bignum.RatFromString) - why?
+                       // strip first space - required for Example tests
                        if len(c) > 0 && c[0] == ' ' {
                                c = c[1:]
                        }
diff --git a/libgo/go/go/ast/ast_test.go b/libgo/go/go/ast/ast_test.go
new file mode 100644 (file)
index 0000000..1a6a283
--- /dev/null
@@ -0,0 +1,50 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ast
+
+import (
+       "testing"
+)
+
+var comments = []struct {
+       list []string
+       text string
+}{
+       {[]string{"//"}, ""},
+       {[]string{"//   "}, ""},
+       {[]string{"//", "//", "//   "}, ""},
+       {[]string{"// foo   "}, "foo\n"},
+       {[]string{"//", "//", "// foo"}, "foo\n"},
+       {[]string{"// foo  bar  "}, "foo  bar\n"},
+       {[]string{"// foo", "// bar"}, "foo\nbar\n"},
+       {[]string{"// foo", "//", "//", "//", "// bar"}, "foo\n\nbar\n"},
+       {[]string{"// foo", "/* bar */"}, "foo\n bar\n"},
+       {[]string{"//", "//", "//", "// foo", "//", "//", "//"}, "foo\n"},
+
+       {[]string{"/**/"}, ""},
+       {[]string{"/*   */"}, ""},
+       {[]string{"/**/", "/**/", "/*   */"}, ""},
+       {[]string{"/* Foo   */"}, " Foo\n"},
+       {[]string{"/* Foo  Bar  */"}, " Foo  Bar\n"},
+       {[]string{"/* Foo*/", "/* Bar*/"}, " Foo\n Bar\n"},
+       {[]string{"/* Foo*/", "/**/", "/**/", "/**/", "// Bar"}, " Foo\n\nBar\n"},
+       {[]string{"/* Foo*/", "/*\n*/", "//", "/*\n*/", "// Bar"}, " Foo\n\nBar\n"},
+       {[]string{"/* Foo*/", "// Bar"}, " Foo\nBar\n"},
+       {[]string{"/* Foo\n Bar*/"}, " Foo\n Bar\n"},
+}
+
+func TestCommentText(t *testing.T) {
+       for i, c := range comments {
+               list := make([]*Comment, len(c.list))
+               for i, s := range c.list {
+                       list[i] = &Comment{Text: s}
+               }
+
+               text := (&CommentGroup{list}).Text()
+               if text != c.text {
+                       t.Errorf("case %d: got %q; expected %q", i, text, c.text)
+               }
+       }
+}
index d749aef15130d776a84e9ff5fd6bc57cc2575001..7a81d50303dd71ba8ec6eb2f02ac0f38feeba48b 100644 (file)
@@ -68,7 +68,7 @@ type Context struct {
 
        // ReadDir returns a slice of os.FileInfo, sorted by Name,
        // describing the content of the named directory.
-       // If ReadDir is nil, Import uses io.ReadDir.
+       // If ReadDir is nil, Import uses ioutil.ReadDir.
        ReadDir func(dir string) (fi []os.FileInfo, err error)
 
        // OpenFile opens a file (not a directory) for reading.
@@ -339,7 +339,7 @@ func (e *NoGoError) Error() string {
 //     - files starting with _ or . (likely editor temporary files)
 //     - files with build constraints not satisfied by the context
 //
-// If an error occurs, Import returns a non-nil error also returns a non-nil
+// If an error occurs, Import returns a non-nil error and a non-nil
 // *Package containing partial information.
 //
 func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) {
index e362e13a7b632f28e3b095db8e2856a56467a2f3..20e505d97a89aa8babd2ee99b72ae2f6e9592bdf 100644 (file)
@@ -267,13 +267,13 @@ func (p *parser) consumeComment() (comment *ast.Comment, endline int) {
 
 // Consume a group of adjacent comments, add it to the parser's
 // comments list, and return it together with the line at which
-// the last comment in the group ends. An empty line or non-comment
-// token terminates a comment group.
+// the last comment in the group ends. A non-comment token or n
+// empty lines terminate a comment group.
 //
-func (p *parser) consumeCommentGroup() (comments *ast.CommentGroup, endline int) {
+func (p *parser) consumeCommentGroup(n int) (comments *ast.CommentGroup, endline int) {
        var list []*ast.Comment
        endline = p.file.Line(p.pos)
-       for p.tok == token.COMMENT && endline+1 >= p.file.Line(p.pos) {
+       for p.tok == token.COMMENT && p.file.Line(p.pos) <= endline+n {
                var comment *ast.Comment
                comment, endline = p.consumeComment()
                list = append(list, comment)
@@ -314,7 +314,7 @@ func (p *parser) next() {
                if p.file.Line(p.pos) == line {
                        // The comment is on same line as the previous token; it
                        // cannot be a lead comment but may be a line comment.
-                       comment, endline = p.consumeCommentGroup()
+                       comment, endline = p.consumeCommentGroup(0)
                        if p.file.Line(p.pos) != endline {
                                // The next token is on a different line, thus
                                // the last comment group is a line comment.
@@ -325,7 +325,7 @@ func (p *parser) next() {
                // consume successor comments, if any
                endline = -1
                for p.tok == token.COMMENT {
-                       comment, endline = p.consumeCommentGroup()
+                       comment, endline = p.consumeCommentGroup(1)
                }
 
                if endline+1 == p.file.Line(p.pos) {
@@ -627,10 +627,10 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
 
        doc := p.leadComment
 
-       // fields
+       // FieldDecl
        list, typ := p.parseVarList(false)
 
-       // optional tag
+       // Tag
        var tag *ast.BasicLit
        if p.tok == token.STRING {
                tag = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
@@ -645,7 +645,6 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *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")
@@ -657,6 +656,7 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
 
        field := &ast.Field{Doc: doc, Names: idents, Type: typ, Tag: tag, Comment: p.lineComment}
        p.declare(field, nil, scope, ast.Var, idents...)
+       p.resolve(typ)
 
        return field
 }
@@ -699,12 +699,15 @@ func (p *parser) parsePointerType() *ast.StarExpr {
        return &ast.StarExpr{Star: star, X: base}
 }
 
+// If the result is an identifier, it is not resolved.
 func (p *parser) tryVarType(isParam bool) ast.Expr {
        if isParam && p.tok == token.ELLIPSIS {
                pos := p.pos
                p.next()
                typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message
-               if typ == nil {
+               if typ != nil {
+                       p.resolve(typ)
+               } else {
                        p.error(pos, "'...' parameter is missing type")
                        typ = &ast.BadExpr{From: pos, To: p.pos}
                }
@@ -713,6 +716,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
        return p.tryIdentOrType(false)
 }
 
+// If the result is an identifier, it is not resolved.
 func (p *parser) parseVarType(isParam bool) ast.Expr {
        typ := p.tryVarType(isParam)
        if typ == nil {
@@ -724,6 +728,7 @@ func (p *parser) parseVarType(isParam bool) ast.Expr {
        return typ
 }
 
+// If any of the results are identifiers, they are not resolved.
 func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
        if p.trace {
                defer un(trace(p, "VarList"))
@@ -744,9 +749,7 @@ 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
-       if typ = p.tryVarType(isParam); typ != nil {
-               p.resolve(typ)
-       }
+       typ = p.tryVarType(isParam)
 
        return
 }
@@ -756,7 +759,10 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
                defer un(trace(p, "ParameterList"))
        }
 
+       // ParameterDecl
        list, typ := p.parseVarList(ellipsisOk)
+
+       // analyze case
        if typ != nil {
                // IdentifierList Type
                idents := p.makeIdentList(list)
@@ -765,10 +771,10 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
                // Go spec: The scope of an identifier denoting a function
                // parameter or result variable is the function body.
                p.declare(field, nil, scope, ast.Var, idents...)
+               p.resolve(typ)
                if p.tok == token.COMMA {
                        p.next()
                }
-
                for p.tok != token.RPAREN && p.tok != token.EOF {
                        idents := p.parseIdentList()
                        typ := p.parseVarType(ellipsisOk)
@@ -777,18 +783,18 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
                        // Go spec: The scope of an identifier denoting a function
                        // parameter or result variable is the function body.
                        p.declare(field, nil, scope, ast.Var, idents...)
+                       p.resolve(typ)
                        if !p.atComma("parameter list") {
                                break
                        }
                        p.next()
                }
-
        } else {
                // Type { "," Type } (anonymous parameters)
                params = make([]*ast.Field, len(list))
-               for i, x := range list {
-                       p.resolve(x)
-                       params[i] = &ast.Field{Type: x}
+               for i, typ := range list {
+                       p.resolve(typ)
+                       params[i] = &ast.Field{Type: typ}
                }
        }
 
index 5e45acd007afe101db4955bf670ae5559c629b71..1b7a41b1bf15007e1ae92551f1f1703429a6287f 100644 (file)
@@ -5,10 +5,12 @@
 package parser
 
 import (
+       "bytes"
        "fmt"
        "go/ast"
        "go/token"
        "os"
+       "strings"
        "testing"
 )
 
@@ -25,7 +27,7 @@ func TestParse(t *testing.T) {
        for _, filename := range validFiles {
                _, err := ParseFile(fset, filename, nil, DeclarationErrors)
                if err != nil {
-                       t.Errorf("ParseFile(%s): %v", filename, err)
+                       t.Fatalf("ParseFile(%s): %v", filename, err)
                }
        }
 }
@@ -70,7 +72,7 @@ func TestParseExpr(t *testing.T) {
        src := "a + b"
        x, err := ParseExpr(src)
        if err != nil {
-               t.Errorf("ParseExpr(%s): %v", src, err)
+               t.Fatalf("ParseExpr(%s): %v", src, err)
        }
        // sanity check
        if _, ok := x.(*ast.BinaryExpr); !ok {
@@ -81,7 +83,7 @@ func TestParseExpr(t *testing.T) {
        src = "a + *"
        _, err = ParseExpr(src)
        if err == nil {
-               t.Errorf("ParseExpr(%s): %v", src, err)
+               t.Fatalf("ParseExpr(%s): %v", src, err)
        }
 
        // it must not crash
@@ -93,7 +95,7 @@ func TestParseExpr(t *testing.T) {
 func TestColonEqualsScope(t *testing.T) {
        f, err := ParseFile(fset, "", `package p; func f() { x, y, z := x, y, z }`, 0)
        if err != nil {
-               t.Errorf("parse: %s", err)
+               t.Fatal(err)
        }
 
        // RHS refers to undefined globals; LHS does not.
@@ -115,7 +117,7 @@ func TestColonEqualsScope(t *testing.T) {
 func TestVarScope(t *testing.T) {
        f, err := ParseFile(fset, "", `package p; func f() { var x, y, z = x, y, z }`, 0)
        if err != nil {
-               t.Errorf("parse: %s", err)
+               t.Fatal(err)
        }
 
        // RHS refers to undefined globals; LHS does not.
@@ -133,6 +135,67 @@ func TestVarScope(t *testing.T) {
        }
 }
 
+func TestUnresolved(t *testing.T) {
+       f, err := ParseFile(fset, "", `
+package p
+//
+func f1a(int)
+func f2a(byte, int, float)
+func f3a(a, b int, c float)
+func f4a(...complex)
+func f5a(a s1a, b ...complex)
+//
+func f1b(*int)
+func f2b([]byte, (int), *float)
+func f3b(a, b *int, c []float)
+func f4b(...*complex)
+func f5b(a s1a, b ...[]complex)
+//
+type s1a struct { int }
+type s2a struct { byte; int; s1a }
+type s3a struct { a, b int; c float }
+//
+type s1b struct { *int }
+type s2b struct { byte; int; *float }
+type s3b struct { a, b *s3b; c []float }
+`, 0)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       want := "int " + // f1a
+               "byte int float " + // f2a
+               "int float " + // f3a
+               "complex " + // f4a
+               "complex " + // f5a
+               //
+               "int " + // f1b
+               "byte int float " + // f2b
+               "int float " + // f3b
+               "complex " + // f4b
+               "complex " + // f5b
+               //
+               "int " + // s1a
+               "byte int " + // s2a
+               "int float " + // s3a
+               //
+               "int " + // s1a
+               "byte int float " + // s2a
+               "float " // s3a
+
+       // collect unresolved identifiers
+       var buf bytes.Buffer
+       for _, u := range f.Unresolved {
+               buf.WriteString(u.Name)
+               buf.WriteByte(' ')
+       }
+       got := buf.String()
+
+       if got != want {
+               t.Errorf("\ngot:  %s\nwant: %s", got, want)
+       }
+}
+
 var imports = map[string]bool{
        `"a"`:        true,
        "`a`":        true,
@@ -177,3 +240,125 @@ func TestImports(t *testing.T) {
                }
        }
 }
+
+func TestCommentGroups(t *testing.T) {
+       f, err := ParseFile(fset, "", `
+package p /* 1a */ /* 1b */      /* 1c */ // 1d
+/* 2a
+*/
+// 2b
+const pi = 3.1415
+/* 3a */ // 3b
+/* 3c */ const e = 2.7182
+
+// Example from issue 3139
+func ExampleCount() {
+       fmt.Println(strings.Count("cheese", "e"))
+       fmt.Println(strings.Count("five", "")) // before & after each rune
+       // Output:
+       // 3
+       // 5
+}
+`, ParseComments)
+       if err != nil {
+               t.Fatal(err)
+       }
+       expected := [][]string{
+               {"/* 1a */", "/* 1b */", "/* 1c */", "// 1d"},
+               {"/* 2a\n*/", "// 2b"},
+               {"/* 3a */", "// 3b", "/* 3c */"},
+               {"// Example from issue 3139"},
+               {"// before & after each rune"},
+               {"// Output:", "// 3", "// 5"},
+       }
+       if len(f.Comments) != len(expected) {
+               t.Fatalf("got %d comment groups; expected %d", len(f.Comments), len(expected))
+       }
+       for i, exp := range expected {
+               got := f.Comments[i].List
+               if len(got) != len(exp) {
+                       t.Errorf("got %d comments in group %d; expected %d", len(got), i, len(exp))
+                       continue
+               }
+               for j, exp := range exp {
+                       got := got[j].Text
+                       if got != exp {
+                               t.Errorf("got %q in group %d; expected %q", got, i, exp)
+                       }
+               }
+       }
+}
+
+func getField(file *ast.File, fieldname string) *ast.Field {
+       parts := strings.Split(fieldname, ".")
+       for _, d := range file.Decls {
+               if d, ok := d.(*ast.GenDecl); ok && d.Tok == token.TYPE {
+                       for _, s := range d.Specs {
+                               if s, ok := s.(*ast.TypeSpec); ok && s.Name.Name == parts[0] {
+                                       if s, ok := s.Type.(*ast.StructType); ok {
+                                               for _, f := range s.Fields.List {
+                                                       for _, name := range f.Names {
+                                                               if name.Name == parts[1] {
+                                                                       return f
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       return nil
+}
+
+// Don't use ast.CommentGroup.Text() - we want to see exact comment text.
+func commentText(c *ast.CommentGroup) string {
+       var buf bytes.Buffer
+       if c != nil {
+               for _, c := range c.List {
+                       buf.WriteString(c.Text)
+               }
+       }
+       return buf.String()
+}
+
+func checkFieldComments(t *testing.T, file *ast.File, fieldname, lead, line string) {
+       f := getField(file, fieldname)
+       if f == nil {
+               t.Fatalf("field not found: %s", fieldname)
+       }
+       if got := commentText(f.Doc); got != lead {
+               t.Errorf("got lead comment %q; expected %q", got, lead)
+       }
+       if got := commentText(f.Comment); got != line {
+               t.Errorf("got line comment %q; expected %q", got, line)
+       }
+}
+
+func TestLeadAndLineComments(t *testing.T) {
+       f, err := ParseFile(fset, "", `
+package p
+type T struct {
+       /* F1 lead comment */
+       //
+       F1 int  /* F1 */ // line comment
+       // F2 lead
+       // comment
+       F2 int  // F2 line comment
+       // f3 lead comment
+       f3 int  // f3 line comment
+}
+`, ParseComments)
+       if err != nil {
+               t.Fatal(err)
+       }
+       checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
+       checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
+       checkFieldComments(t, f, "T.f3", "// f3 lead comment", "// f3 line comment")
+       ast.FileExports(f)
+       checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
+       checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
+       if getField(f, "T.f3") != nil {
+               t.Error("not expected to find T.f3")
+       }
+}
index 727d2a37147286845bb7e37748ceb4fd3a627e49..f13f9a5a8436ca0e03052cd5d5da913f4035b329 100644 (file)
@@ -60,8 +60,8 @@ func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (prin
 
 // setComment sets g as the next comment if g != nil and if node comments
 // are enabled - this mode is used when printing source code fragments such
-// as exports only. It assumes that there are no other pending comments to
-// intersperse.
+// as exports only. It assumes that there is no pending comment in p.comments
+// and at most one pending comment in the p.comment cache.
 func (p *printer) setComment(g *ast.CommentGroup) {
        if g == nil || !p.useNodeComments {
                return
@@ -74,10 +74,19 @@ func (p *printer) setComment(g *ast.CommentGroup) {
                // should never happen - handle gracefully and flush
                // all comments up to g, ignore anything after that
                p.flush(p.posFor(g.List[0].Pos()), token.ILLEGAL)
+               p.comments = p.comments[0:1]
+               // in debug mode, report error
+               p.internalError("setComment found pending comments")
        }
        p.comments[0] = g
        p.cindex = 0
-       p.nextComment() // get comment ready for use
+       // don't overwrite any pending comment in the p.comment cache
+       // (there may be a pending comment when a line comment is
+       // immediately followed by a lead comment with no other
+       // tokens inbetween)
+       if p.commentOffset == infinity {
+               p.nextComment() // get comment ready for use
+       }
 }
 
 type exprListMode uint
index 1d7f209d1daf676bce28a54aed4ba4bb3327b43a..d37e4375e4c8753d2a26a60c2246f5c78d5f8530 100644 (file)
@@ -26,7 +26,7 @@ const (
        // Bits or'ed together to control what's printed. There is no control over the
        // order they appear (the order listed here) or the format they present (as
        // described in the comments).  A colon appears after these items:
-       //      2009/0123 01:23:23.123123 /a/b/c/d.go:23: message
+       //      2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
        Ldate         = 1 << iota     // the date: 2009/01/23
        Ltime                         // the time: 01:23:23
        Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.
index 0bc6572b962a9bbd14436d293d74852a182afae8..eaa6ff0666c7a90d48468ac435d6ada257a85d8b 100644 (file)
@@ -271,10 +271,10 @@ func karatsuba(z, x, y nat) {
        //   xd = x1 - x0
        //   yd = y0 - y1
        //
-       //   z1 =      xd*yd                    + z1 + z0
-       //      = (x1-x0)*(y0 - y1)             + z1 + z0
-       //      = x1*y0 - x1*y1 - x0*y0 + x0*y1 + z1 + z0
-       //      = x1*y0 -    z1 -    z0 + x0*y1 + z1 + z0
+       //   z1 =      xd*yd                    + z2 + z0
+       //      = (x1-x0)*(y0 - y1)             + z2 + z0
+       //      = x1*y0 - x1*y1 - x0*y0 + x0*y1 + z2 + z0
+       //      = x1*y0 -    z2 -    z0 + x0*y1 + z2 + z0
        //      = x1*y0                 + x0*y1
 
        // split x, y into "digits"
@@ -318,7 +318,7 @@ func karatsuba(z, x, y nat) {
        // save original z2:z0
        // (ok to use upper half of z since we're done recursing)
        r := z[n*4:]
-       copy(r, z)
+       copy(r, z[:n*2])
 
        // add up all partial products
        //
index 7f3f76dc36f6ce97bbe98fde928a9edf17d59188..becde5d1719483437b6afcfaf9de1e20e8f07a97 100644 (file)
@@ -661,3 +661,21 @@ func TestExpNN(t *testing.T) {
                }
        }
 }
+
+func ExpHelper(b *testing.B, x, y Word) {
+       var z nat
+       for i := 0; i < b.N; i++ {
+               z.expWW(x, y)
+       }
+}
+
+func BenchmarkExp3Power0x10(b *testing.B)     { ExpHelper(b, 3, 0x10) }
+func BenchmarkExp3Power0x40(b *testing.B)     { ExpHelper(b, 3, 0x40) }
+func BenchmarkExp3Power0x100(b *testing.B)    { ExpHelper(b, 3, 0x100) }
+func BenchmarkExp3Power0x400(b *testing.B)    { ExpHelper(b, 3, 0x400) }
+func BenchmarkExp3Power0x1000(b *testing.B)   { ExpHelper(b, 3, 0x1000) }
+func BenchmarkExp3Power0x4000(b *testing.B)   { ExpHelper(b, 3, 0x4000) }
+func BenchmarkExp3Power0x10000(b *testing.B)  { ExpHelper(b, 3, 0x10000) }
+func BenchmarkExp3Power0x40000(b *testing.B)  { ExpHelper(b, 3, 0x40000) }
+func BenchmarkExp3Power0x100000(b *testing.B) { ExpHelper(b, 3, 0x100000) }
+func BenchmarkExp3Power0x400000(b *testing.B) { ExpHelper(b, 3, 0x400000) }
index 6ace4be564c94a7782e5ab48557cc3b56ac1e975..e9e337b9222bd6124b6f3538bb6d2a8a3a1fc111 100644 (file)
@@ -22,11 +22,6 @@ import (
        "net/textproto"
 )
 
-// TODO(bradfitz): inline these once the compiler can inline them in
-// read-only situation (such as bytes.HasSuffix)
-var lf = []byte("\n")
-var crlf = []byte("\r\n")
-
 var emptyParams = make(map[string]string)
 
 // A Part represents a single part in a multipart body.
@@ -36,8 +31,9 @@ type Part struct {
        // i.e. "foo-bar" changes case to "Foo-Bar"
        Header textproto.MIMEHeader
 
-       buffer *bytes.Buffer
-       mr     *Reader
+       buffer    *bytes.Buffer
+       mr        *Reader
+       bytesRead int
 
        disposition       string
        dispositionParams map[string]string
@@ -113,14 +109,26 @@ func (bp *Part) populateHeaders() error {
 // Read reads the body of a part, after its headers and before the
 // next part (if any) begins.
 func (p *Part) Read(d []byte) (n int, err error) {
+       defer func() {
+               p.bytesRead += n
+       }()
        if p.buffer.Len() >= len(d) {
                // Internal buffer of unconsumed data is large enough for
                // the read request.  No need to parse more at the moment.
                return p.buffer.Read(d)
        }
        peek, err := p.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessor
-       unexpectedEof := err == io.EOF
-       if err != nil && !unexpectedEof {
+
+       // Look for an immediate empty part without a leading \r\n
+       // before the boundary separator.  Some MIME code makes empty
+       // parts like this. Most browsers, however, write the \r\n
+       // before the subsequent boundary even for empty parts and
+       // won't hit this path.
+       if p.bytesRead == 0 && p.mr.peekBufferIsEmptyPart(peek) {
+               return 0, io.EOF
+       }
+       unexpectedEOF := err == io.EOF
+       if err != nil && !unexpectedEOF {
                return 0, fmt.Errorf("multipart: Part Read: %v", err)
        }
        if peek == nil {
@@ -138,7 +146,7 @@ func (p *Part) Read(d []byte) (n int, err error) {
                foundBoundary = true
        } else if safeCount := len(peek) - len(p.mr.nlDashBoundary); safeCount > 0 {
                nCopy = safeCount
-       } else if unexpectedEof {
+       } else if unexpectedEOF {
                // If we've run out of peek buffer and the boundary
                // wasn't found (and can't possibly fit), we must have
                // hit the end of the file unexpectedly.
@@ -172,7 +180,10 @@ type Reader struct {
        currentPart *Part
        partsRead   int
 
-       nl, nlDashBoundary, dashBoundaryDash, dashBoundary []byte
+       nl               []byte // "\r\n" or "\n" (set after seeing first boundary line)
+       nlDashBoundary   []byte // nl + "--boundary"
+       dashBoundaryDash []byte // "--boundary--"
+       dashBoundary     []byte // "--boundary"
 }
 
 // NextPart returns the next part in the multipart or an error.
@@ -185,7 +196,7 @@ func (r *Reader) NextPart() (*Part, error) {
        expectNewPart := false
        for {
                line, err := r.bufReader.ReadSlice('\n')
-               if err == io.EOF && bytes.Equal(line, r.dashBoundaryDash) {
+               if err == io.EOF && r.isFinalBoundary(line) {
                        // If the buffer ends in "--boundary--" without the
                        // trailing "\r\n", ReadSlice will return an error
                        // (since it's missing the '\n'), but this is a valid
@@ -207,7 +218,7 @@ func (r *Reader) NextPart() (*Part, error) {
                        return bp, nil
                }
 
-               if hasPrefixThenNewline(line, r.dashBoundaryDash) {
+               if r.isFinalBoundary(line) {
                        // Expected EOF
                        return nil, io.EOF
                }
@@ -235,7 +246,19 @@ func (r *Reader) NextPart() (*Part, error) {
        panic("unreachable")
 }
 
-func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {
+// isFinalBoundary returns whether line is the final boundary line
+// indiciating that all parts are over.
+// It matches `^--boundary--[ \t]*(\r\n)?$`
+func (mr *Reader) isFinalBoundary(line []byte) bool {
+       if !bytes.HasPrefix(line, mr.dashBoundaryDash) {
+               return false
+       }
+       rest := line[len(mr.dashBoundaryDash):]
+       rest = skipLWSPChar(rest)
+       return len(rest) == 0 || bytes.Equal(rest, mr.nl)
+}
+
+func (mr *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) {
        // http://tools.ietf.org/html/rfc2046#section-5.1
        //   The boundary delimiter line is then defined as a line
        //   consisting entirely of two hyphen characters ("-",
@@ -245,32 +268,52 @@ func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {
        if !bytes.HasPrefix(line, mr.dashBoundary) {
                return false
        }
-       if bytes.HasSuffix(line, mr.nl) {
-               return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-len(mr.nl)])
+       rest := line[len(mr.dashBoundary):]
+       rest = skipLWSPChar(rest)
+
+       // On the first part, see our lines are ending in \n instead of \r\n
+       // and switch into that mode if so.  This is a violation of the spec,
+       // but occurs in practice.
+       if mr.partsRead == 0 && len(rest) == 1 && rest[0] == '\n' {
+               mr.nl = mr.nl[1:]
+               mr.nlDashBoundary = mr.nlDashBoundary[1:]
        }
-       // Violate the spec and also support newlines without the
-       // carriage return...
-       if mr.partsRead == 0 && bytes.HasSuffix(line, lf) {
-               if onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1]) {
-                       mr.nl = mr.nl[1:]
-                       mr.nlDashBoundary = mr.nlDashBoundary[1:]
-                       return true
-               }
-       }
-       return false
+       return bytes.Equal(rest, mr.nl)
 }
 
-func onlyHorizontalWhitespace(s []byte) bool {
-       for _, b := range s {
-               if b != ' ' && b != '\t' {
-                       return false
-               }
+// peekBufferIsEmptyPart returns whether the provided peek-ahead
+// buffer represents an empty part.  This is only called if we've not
+// already read any bytes in this part and checks for the case of MIME
+// software not writing the \r\n on empty parts. Some does, some
+// doesn't.
+//
+// This checks that what follows the "--boundary" is actually the end
+// ("--boundary--" with optional whitespace) or optional whitespace
+// and then a newline, so we don't catch "--boundaryFAKE", in which
+// case the whole line is part of the data.
+func (mr *Reader) peekBufferIsEmptyPart(peek []byte) bool {
+       // End of parts case.
+       // Test whether peek matches `^--boundary--[ \t]*(?:\r\n|$)`
+       if bytes.HasPrefix(peek, mr.dashBoundaryDash) {
+               rest := peek[len(mr.dashBoundaryDash):]
+               rest = skipLWSPChar(rest)
+               return bytes.HasPrefix(rest, mr.nl) || len(rest) == 0
        }
-       return true
+       if !bytes.HasPrefix(peek, mr.dashBoundary) {
+               return false
+       }
+       // Test whether rest matches `^[ \t]*\r\n`)
+       rest := peek[len(mr.dashBoundary):]
+       rest = skipLWSPChar(rest)
+       return bytes.HasPrefix(rest, mr.nl)
 }
 
-func hasPrefixThenNewline(s, prefix []byte) bool {
-       return bytes.HasPrefix(s, prefix) &&
-               (len(s) == len(prefix)+1 && s[len(s)-1] == '\n' ||
-                       len(s) == len(prefix)+2 && bytes.HasSuffix(s, crlf))
+// skipLWSPChar returns b with leading spaces and tabs removed.
+// RFC 822 defines:
+//    LWSP-char = SPACE / HTAB
+func skipLWSPChar(b []byte) []byte {
+       for len(b) > 0 && (b[0] == ' ' || b[0] == '\t') {
+               b = b[1:]
+       }
+       return b
 }
index ca7108d7ad0a4bd666f9d9364cba70830008f1c2..cd65e177e852e3a80539ec6c688ef0b14338ebbd 100644 (file)
@@ -10,20 +10,13 @@ import (
        "fmt"
        "io"
        "io/ioutil"
+       "net/textproto"
        "os"
+       "reflect"
        "strings"
        "testing"
 )
 
-func TestHorizontalWhitespace(t *testing.T) {
-       if !onlyHorizontalWhitespace([]byte(" \t")) {
-               t.Error("expected pass")
-       }
-       if onlyHorizontalWhitespace([]byte("foo bar")) {
-               t.Error("expected failure")
-       }
-}
-
 func TestBoundaryLine(t *testing.T) {
        mr := NewReader(strings.NewReader(""), "myBoundary")
        if !mr.isBoundaryDelimiterLine([]byte("--myBoundary\r\n")) {
@@ -319,29 +312,6 @@ Oh no, premature EOF!
        }
 }
 
-func TestZeroLengthBody(t *testing.T) {
-       testBody := strings.Replace(`
-This is a multi-part message.  This line is ignored.
---MyBoundary
-foo: bar
-
-
---MyBoundary--
-`, "\n", "\r\n", -1)
-       r := NewReader(strings.NewReader(testBody), "MyBoundary")
-       part, err := r.NextPart()
-       if err != nil {
-               t.Fatalf("didn't get a part")
-       }
-       n, err := io.Copy(ioutil.Discard, part)
-       if err != nil {
-               t.Errorf("error reading part: %v", err)
-       }
-       if n != 0 {
-               t.Errorf("read %d bytes; expected 0", n)
-       }
-}
-
 type slowReader struct {
        r io.Reader
 }
@@ -427,3 +397,214 @@ func TestNested(t *testing.T) {
                t.Fatalf("final outer NextPart = %v; want io.EOF", err)
        }
 }
+
+type headerBody struct {
+       header textproto.MIMEHeader
+       body   string
+}
+
+func formData(key, value string) headerBody {
+       return headerBody{
+               textproto.MIMEHeader{
+                       "Content-Type":        {"text/plain; charset=ISO-8859-1"},
+                       "Content-Disposition": {"form-data; name=" + key},
+               },
+               value,
+       }
+}
+
+type parseTest struct {
+       name    string
+       in, sep string
+       want    []headerBody
+}
+
+var parseTests = []parseTest{
+       // Actual body from App Engine on a blob upload. The final part (the
+       // Content-Type: message/external-body) is what App Engine replaces
+       // the uploaded file with.  The other form fields (prefixed with
+       // "other" in their form-data name) are unchanged.  A bug was
+       // reported with blob uploads failing when the other fields were
+       // empty. This was the MIME POST body that previously failed.
+       {
+               name: "App Engine post",
+               sep:  "00151757727e9583fd04bfbca4c6",
+               in:   "--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherEmpty1\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherFoo1\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherFoo2\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherEmpty2\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatFoo\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatFoo\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatEmpty\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatEmpty\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=submit\r\n\r\nSubmit\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: message/external-body; charset=ISO-8859-1; blob-key=AHAZQqG84qllx7HUqO_oou5EvdYQNS3Mbbkb0RjjBoM_Kc1UqEN2ygDxWiyCPulIhpHRPx-VbpB6RX4MrsqhWAi_ZxJ48O9P2cTIACbvATHvg7IgbvZytyGMpL7xO1tlIvgwcM47JNfv_tGhy1XwyEUO8oldjPqg5Q\r\nContent-Disposition: form-data; name=file; filename=\"fall.png\"\r\n\r\nContent-Type: image/png\r\nContent-Length: 232303\r\nX-AppEngine-Upload-Creation: 2012-05-10 23:14:02.715173\r\nContent-MD5: MzRjODU1ZDZhZGU1NmRlOWEwZmMwMDdlODBmZTA0NzA=\r\nContent-Disposition: form-data; name=file; filename=\"fall.png\"\r\n\r\n\r\n--00151757727e9583fd04bfbca4c6--",
+               want: []headerBody{
+                       formData("otherEmpty1", ""),
+                       formData("otherFoo1", "foo"),
+                       formData("otherFoo2", "foo"),
+                       formData("otherEmpty2", ""),
+                       formData("otherRepeatFoo", "foo"),
+                       formData("otherRepeatFoo", "foo"),
+                       formData("otherRepeatEmpty", ""),
+                       formData("otherRepeatEmpty", ""),
+                       formData("submit", "Submit"),
+                       {textproto.MIMEHeader{
+                               "Content-Type":        {"message/external-body; charset=ISO-8859-1; blob-key=AHAZQqG84qllx7HUqO_oou5EvdYQNS3Mbbkb0RjjBoM_Kc1UqEN2ygDxWiyCPulIhpHRPx-VbpB6RX4MrsqhWAi_ZxJ48O9P2cTIACbvATHvg7IgbvZytyGMpL7xO1tlIvgwcM47JNfv_tGhy1XwyEUO8oldjPqg5Q"},
+                               "Content-Disposition": {"form-data; name=file; filename=\"fall.png\""},
+                       }, "Content-Type: image/png\r\nContent-Length: 232303\r\nX-AppEngine-Upload-Creation: 2012-05-10 23:14:02.715173\r\nContent-MD5: MzRjODU1ZDZhZGU1NmRlOWEwZmMwMDdlODBmZTA0NzA=\r\nContent-Disposition: form-data; name=file; filename=\"fall.png\"\r\n\r\n"},
+               },
+       },
+
+       // Single empty part, ended with --boundary immediately after headers.
+       {
+               name: "single empty part, --boundary",
+               sep:  "abc",
+               in:   "--abc\r\nFoo: bar\r\n\r\n--abc--",
+               want: []headerBody{
+                       {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
+               },
+       },
+
+       // Single empty part, ended with \r\n--boundary immediately after headers.
+       {
+               name: "single empty part, \r\n--boundary",
+               sep:  "abc",
+               in:   "--abc\r\nFoo: bar\r\n\r\n\r\n--abc--",
+               want: []headerBody{
+                       {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
+               },
+       },
+
+       // Final part empty.
+       {
+               name: "final part empty",
+               sep:  "abc",
+               in:   "--abc\r\nFoo: bar\r\n\r\n--abc\r\nFoo2: bar2\r\n\r\n--abc--",
+               want: []headerBody{
+                       {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
+                       {textproto.MIMEHeader{"Foo2": {"bar2"}}, ""},
+               },
+       },
+
+       // Final part empty with newlines after final separator.
+       {
+               name: "final part empty then crlf",
+               sep:  "abc",
+               in:   "--abc\r\nFoo: bar\r\n\r\n--abc--\r\n",
+               want: []headerBody{
+                       {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
+               },
+       },
+
+       // Final part empty with lwsp-chars after final separator.
+       {
+               name: "final part empty then lwsp",
+               sep:  "abc",
+               in:   "--abc\r\nFoo: bar\r\n\r\n--abc-- \t",
+               want: []headerBody{
+                       {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
+               },
+       },
+
+       // No parts (empty form as submitted by Chrome)
+       {
+               name: "no parts",
+               sep:  "----WebKitFormBoundaryQfEAfzFOiSemeHfA",
+               in:   "------WebKitFormBoundaryQfEAfzFOiSemeHfA--\r\n",
+               want: []headerBody{},
+       },
+
+       // Part containing data starting with the boundary, but with additional suffix.
+       {
+               name: "fake separator as data",
+               sep:  "sep",
+               in:   "--sep\r\nFoo: bar\r\n\r\n--sepFAKE\r\n--sep--",
+               want: []headerBody{
+                       {textproto.MIMEHeader{"Foo": {"bar"}}, "--sepFAKE"},
+               },
+       },
+
+       // Part containing a boundary with whitespace following it.
+       {
+               name: "boundary with whitespace",
+               sep:  "sep",
+               in:   "--sep \r\nFoo: bar\r\n\r\ntext\r\n--sep--",
+               want: []headerBody{
+                       {textproto.MIMEHeader{"Foo": {"bar"}}, "text"},
+               },
+       },
+
+       // With ignored leading line.
+       {
+               name: "leading line",
+               sep:  "MyBoundary",
+               in: strings.Replace(`This is a multi-part message.  This line is ignored.
+--MyBoundary
+foo: bar
+
+
+--MyBoundary--`, "\n", "\r\n", -1),
+               want: []headerBody{
+                       {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
+               },
+       },
+
+       roundTripParseTest(),
+}
+
+func TestParse(t *testing.T) {
+Cases:
+       for _, tt := range parseTests {
+               r := NewReader(strings.NewReader(tt.in), tt.sep)
+               got := []headerBody{}
+               for {
+                       p, err := r.NextPart()
+                       if err == io.EOF {
+                               break
+                       }
+                       if err != nil {
+                               t.Errorf("in test %q, NextPart: %v", tt.name, err)
+                               continue Cases
+                       }
+                       pbody, err := ioutil.ReadAll(p)
+                       if err != nil {
+                               t.Errorf("in test %q, error reading part: %v", tt.name, err)
+                               continue Cases
+                       }
+                       got = append(got, headerBody{p.Header, string(pbody)})
+               }
+               if !reflect.DeepEqual(tt.want, got) {
+                       t.Errorf("test %q:\n got: %v\nwant: %v", tt.name, got, tt.want)
+                       if len(tt.want) != len(got) {
+                               t.Errorf("test %q: got %d parts, want %d", tt.name, len(got), len(tt.want))
+                       } else if len(got) > 1 {
+                               for pi, wantPart := range tt.want {
+                                       if !reflect.DeepEqual(wantPart, got[pi]) {
+                                               t.Errorf("test %q, part %d:\n got: %v\nwant: %v", tt.name, pi, got[pi], wantPart)
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+func roundTripParseTest() parseTest {
+       t := parseTest{
+               name: "round trip",
+               want: []headerBody{
+                       formData("empty", ""),
+                       formData("lf", "\n"),
+                       formData("cr", "\r"),
+                       formData("crlf", "\r\n"),
+                       formData("foo", "bar"),
+               },
+       }
+       var buf bytes.Buffer
+       w := NewWriter(&buf)
+       for _, p := range t.want {
+               pw, err := w.CreatePart(p.header)
+               if err != nil {
+                       panic(err)
+               }
+               _, err = pw.Write([]byte(p.body))
+               if err != nil {
+                       panic(err)
+               }
+       }
+       w.Close()
+       t.in = buf.String()
+       t.sep = w.Boundary()
+       return t
+}
index c95d16d64e796325db1cad4fa62daf623f95e6b5..fc6c6fad8e1572417a28879ca6f2239f6ef73888 100644 (file)
@@ -89,8 +89,8 @@ func FileConn(f *os.File) (c Conn, err error) {
 
 // FileListener returns a copy of the network listener corresponding
 // to the open file f.  It is the caller's responsibility to close l
-// when finished.  Closing c does not affect l, and closing l does not
-// affect c.
+// when finished.  Closing l does not affect f, and closing f does not
+// affect l.
 func FileListener(f *os.File) (l Listener, err error) {
        fd, err := newFileFD(f)
        if err != nil {
index 5d450258bd37798b035e15c891c02429375dbb29..54564e0989ecd624fe157e9bf6e6c89c51a8f449 100644 (file)
@@ -278,6 +278,11 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response,
                return nil, err
        }
        req.Header.Set("Content-Type", bodyType)
+       if c.Jar != nil {
+               for _, cookie := range c.Jar.Cookies(req.URL) {
+                       req.AddCookie(cookie)
+               }
+       }
        r, err = send(req, c.Transport)
        if err == nil && c.Jar != nil {
                c.Jar.SetCookies(req.URL, r.Cookies())
index e00b62e590a2869b13c400b0b6cce3377dcad309..9b4261b9f61e497b7ac41a827aba861d0fcabea3 100644 (file)
@@ -256,6 +256,31 @@ var echoCookiesRedirectHandler = HandlerFunc(func(w ResponseWriter, r *Request)
        }
 })
 
+func TestClientSendsCookieFromJar(t *testing.T) {
+       tr := &recordingTransport{}
+       client := &Client{Transport: tr}
+       client.Jar = &TestJar{perURL: make(map[string][]*Cookie)}
+       us := "http://dummy.faketld/"
+       u, _ := url.Parse(us)
+       client.Jar.SetCookies(u, expectedCookies)
+
+       client.Get(us) // Note: doesn't hit network
+       matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
+
+       client.Head(us) // Note: doesn't hit network
+       matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
+
+       client.Post(us, "text/plain", strings.NewReader("body")) // Note: doesn't hit network
+       matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
+
+       client.PostForm(us, url.Values{}) // Note: doesn't hit network
+       matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
+
+       req, _ := NewRequest("GET", us, nil)
+       client.Do(req) // Note: doesn't hit network
+       matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
+}
+
 // Just enough correctness for our redirect tests. Uses the URL.Host as the
 // scope of all cookies.
 type TestJar struct {
index 9b320b3aa5b9904f26cc5aba7983f60ba5ddd2b7..5ecffaface93de6a145a1f92d1101579ebcaf39b 100644 (file)
@@ -5,6 +5,7 @@
 package http
 
 import (
+       "net/url"
        "os"
        "testing"
 )
@@ -46,3 +47,32 @@ func TestUseProxy(t *testing.T) {
                }
        }
 }
+
+var cacheKeysTests = []struct {
+       proxy  string
+       scheme string
+       addr   string
+       key    string
+}{
+       {"", "http", "foo.com", "|http|foo.com"},
+       {"", "https", "foo.com", "|https|foo.com"},
+       {"http://foo.com", "http", "foo.com", "http://foo.com|http|"},
+       {"http://foo.com", "https", "foo.com", "http://foo.com|https|foo.com"},
+}
+
+func TestCacheKeys(t *testing.T) {
+       for _, tt := range cacheKeysTests {
+               var proxy *url.URL
+               if tt.proxy != "" {
+                       u, err := url.Parse(tt.proxy)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       proxy = u
+               }
+               cm := connectMethod{proxy, tt.scheme, tt.addr}
+               if cm.String() != tt.key {
+                       t.Fatalf("{%q, %q, %q} cache key %q; want %q", tt.proxy, tt.scheme, tt.addr, cm.String(), tt.key)
+               }
+       }
+}
index b79022097867992c5b6c011dec4ae9b9ea6df76d..945ecd8a4b04dbb755ac3f9909041521488d28f0 100644 (file)
@@ -202,9 +202,12 @@ func (r *Response) Write(w io.Writer) error {
                        text = "status code " + strconv.Itoa(r.StatusCode)
                }
        }
-       io.WriteString(w, "HTTP/"+strconv.Itoa(r.ProtoMajor)+".")
-       io.WriteString(w, strconv.Itoa(r.ProtoMinor)+" ")
-       io.WriteString(w, strconv.Itoa(r.StatusCode)+" "+text+"\r\n")
+       protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)
+       statusCode := strconv.Itoa(r.StatusCode) + " "
+       if strings.HasPrefix(text, statusCode) {
+               text = text[len(statusCode):]
+       }
+       io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n")
 
        // Process Body,ContentLength,Close,Trailer
        tw, err := newTransferWriter(r)
index 165ec3624a4585303f97ac6959c4f4f1bca7a934..6eed4887ddceb8620cc434fd9be18f34413d480e 100644 (file)
@@ -14,6 +14,7 @@ import (
        "io/ioutil"
        "net/url"
        "reflect"
+       "strings"
        "testing"
 )
 
@@ -444,3 +445,17 @@ func TestLocationResponse(t *testing.T) {
                }
        }
 }
+
+func TestResponseStatusStutter(t *testing.T) {
+       r := &Response{
+               Status:     "123 some status",
+               StatusCode: 123,
+               ProtoMajor: 1,
+               ProtoMinor: 3,
+       }
+       var buf bytes.Buffer
+       r.Write(&buf)
+       if strings.Contains(buf.String(), "123 123") {
+               t.Errorf("stutter in status: %s", buf.String())
+       }
+}
index 924ffd348156e030a98e227ec4bb5691761af46e..0572b4ae3477466a16a2742128af7fc9f4c6abe4 100644 (file)
@@ -31,7 +31,7 @@ import (
 // Errors introduced by the HTTP server.
 var (
        ErrWriteAfterFlush = errors.New("Conn.Write called after Flush")
-       ErrBodyNotAllowed  = errors.New("http: response status code does not allow body")
+       ErrBodyNotAllowed  = errors.New("http: request method or response status code does not allow body")
        ErrHijacked        = errors.New("Conn has been hijacked")
        ErrContentLength   = errors.New("Conn.Write wrote more than the declared Content-Length")
 )
index 3c8fe7f5b510110e49d6e07ebccbdbffb9ddbb44..9e9d84172d00effda81bb0ee7e45739d5a161f8e 100644 (file)
@@ -71,7 +71,9 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) {
                        }
                }
        case *Response:
-               t.Method = rr.Request.Method
+               if rr.Request != nil {
+                       t.Method = rr.Request.Method
+               }
                t.Body = rr.Body
                t.BodyCloser = rr.Body
                t.ContentLength = rr.ContentLength
@@ -79,7 +81,7 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) {
                t.TransferEncoding = rr.TransferEncoding
                t.Trailer = rr.Trailer
                atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
-               t.ResponseToHEAD = noBodyExpected(rr.Request.Method)
+               t.ResponseToHEAD = noBodyExpected(t.Method)
        }
 
        // Sanitize Body,ContentLength,TransferEncoding
index 024975946e52b2073800191a5bc41f917ce5743c..6efe191eb0b0dbf18161f258d4e00a0601f7a9af 100644 (file)
@@ -450,10 +450,14 @@ type connectMethod struct {
 
 func (ck *connectMethod) String() string {
        proxyStr := ""
+       targetAddr := ck.targetAddr
        if ck.proxyURL != nil {
                proxyStr = ck.proxyURL.String()
+               if ck.targetScheme == "http" {
+                       targetAddr = ""
+               }
        }
-       return strings.Join([]string{proxyStr, ck.targetScheme, ck.targetAddr}, "|")
+       return strings.Join([]string{proxyStr, ck.targetScheme, targetAddr}, "|")
 }
 
 // addr returns the first hop "host:port" to which we need to TCP connect.
index 0917bbedf1b3eb4e6dda4c6f0220dc609524d267..b610ccf3f048e16910c93ead22bfe566f4190e92 100644 (file)
@@ -69,11 +69,12 @@ var dateLayouts []string
 func init() {
        // Generate layouts based on RFC 5322, section 3.3.
 
-       dows := [...]string{"", "Mon, "}     // day-of-week
-       days := [...]string{"2", "02"}       // day = 1*2DIGIT
-       years := [...]string{"2006", "06"}   // year = 4*DIGIT / 2*DIGIT
-       seconds := [...]string{":05", ""}    // second
-       zones := [...]string{"-0700", "MST"} // zone = (("+" / "-") 4DIGIT) / "GMT" / ...
+       dows := [...]string{"", "Mon, "}   // day-of-week
+       days := [...]string{"2", "02"}     // day = 1*2DIGIT
+       years := [...]string{"2006", "06"} // year = 4*DIGIT / 2*DIGIT
+       seconds := [...]string{":05", ""}  // second
+       // "-0700 (MST)" is not in RFC 5322, but is common.
+       zones := [...]string{"-0700", "MST", "-0700 (MST)"} // zone = (("+" / "-") 4DIGIT) / "GMT" / ...
 
        for _, dow := range dows {
                for _, day := range days {
index 671ff2efacbee5674b496ef510883ba27ceff649..fd17eb414a7370636c4d6ce769da6fd324b56659 100644 (file)
@@ -95,6 +95,11 @@ func TestDateParsing(t *testing.T) {
                        "21 Nov 97 09:55:06 GMT",
                        time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("GMT", 0)),
                },
+               // Commonly found format not specified by RFC 5322.
+               {
+                       "Fri, 21 Nov 1997 09:55:06 -0600 (MDT)",
+                       time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
+               },
        }
        for _, test := range tests {
                hdr := Header{
index b6e79adc29af421e8e20b71ca1405e559570af4f..17bf0d3a342f394b3db0b9921251829aa9c1423c 100644 (file)
@@ -401,11 +401,12 @@ Error:
 }
 
 func parseAuthority(authority string) (user *Userinfo, host string, err error) {
-       if strings.Index(authority, "@") < 0 {
+       i := strings.LastIndex(authority, "@")
+       if i < 0 {
                host = authority
                return
        }
-       userinfo, host := split(authority, '@', true)
+       userinfo, host := authority[:i], authority[i+1:]
        if strings.Index(userinfo, ":") < 0 {
                if userinfo, err = unescape(userinfo, encodeUserPassword); err != nil {
                        return
index d8b253142f0c6df1a4199d1ca93244e5321ba6a8..75e8abe4eb3ff74a099d77f960e1a9b2b6a32fc8 100644 (file)
@@ -188,6 +188,37 @@ var urltests = []URLTest{
                },
                "http://user:password@google.com",
        },
+       // unescaped @ in username should not confuse host
+       {
+               "http://j@ne:password@google.com",
+               &URL{
+                       Scheme: "http",
+                       User:   UserPassword("j@ne", "password"),
+                       Host:   "google.com",
+               },
+               "http://j%40ne:password@google.com",
+       },
+       // unescaped @ in password should not confuse host
+       {
+               "http://jane:p@ssword@google.com",
+               &URL{
+                       Scheme: "http",
+                       User:   UserPassword("jane", "p@ssword"),
+                       Host:   "google.com",
+               },
+               "http://jane:p%40ssword@google.com",
+       },
+       {
+               "http://j@ne:password@google.com/p@th?q=@go",
+               &URL{
+                       Scheme:   "http",
+                       User:     UserPassword("j@ne", "password"),
+                       Host:     "google.com",
+                       Path:     "/p@th",
+                       RawQuery: "q=@go",
+               },
+               "http://j%40ne:password@google.com/p@th?q=@go",
+       },
        {
                "http://www.google.com/?q=go+language#foo",
                &URL{
index bbd04902b742d289a806f86aff7dc54ddbe8a34d..9a8e1817014beb8e621017e0ae8ea6c538a47068 100644 (file)
@@ -204,6 +204,12 @@ func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) {
        return pw, nil
 }
 
+func (c *Cmd) closeDescriptors(closers []io.Closer) {
+       for _, fd := range closers {
+               fd.Close()
+       }
+}
+
 // Run starts the specified command and waits for it to complete.
 //
 // The returned error is nil if the command runs, has no problems
@@ -233,6 +239,8 @@ func (c *Cmd) Start() error {
        for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {
                fd, err := setupFd(c)
                if err != nil {
+                       c.closeDescriptors(c.closeAfterStart)
+                       c.closeDescriptors(c.closeAfterWait)
                        return err
                }
                c.childFiles = append(c.childFiles, fd)
@@ -247,12 +255,12 @@ func (c *Cmd) Start() error {
                Sys:   c.SysProcAttr,
        })
        if err != nil {
+               c.closeDescriptors(c.closeAfterStart)
+               c.closeDescriptors(c.closeAfterWait)
                return err
        }
 
-       for _, fd := range c.closeAfterStart {
-               fd.Close()
-       }
+       c.closeDescriptors(c.closeAfterStart)
 
        c.errch = make(chan error, len(c.goroutine))
        for _, fn := range c.goroutine {
@@ -301,9 +309,7 @@ func (c *Cmd) Wait() error {
                }
        }
 
-       for _, fd := range c.closeAfterWait {
-               fd.Close()
-       }
+       c.closeDescriptors(c.closeAfterWait)
 
        if err != nil {
                return err
index a4e429baec18a51afaca0c8831b004637ccb540a..815021bd040149f669d2f1109abd547a3d49af96 100644 (file)
@@ -320,8 +320,11 @@ func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
        }
 
        for _, fileInfo := range list {
-               if err = walk(Join(path, fileInfo.Name()), fileInfo, walkFn); err != nil {
-                       return err
+               err = walk(Join(path, fileInfo.Name()), fileInfo, walkFn)
+               if err != nil {
+                       if !fileInfo.IsDir() || err != SkipDir {
+                               return err
+                       }
                }
        }
        return nil
index cf028a75c525e2495cb081590863dbc9c47d3444..59a5812dd0bad1c7acec33e2e0304b28e491a353 100644 (file)
@@ -12,7 +12,7 @@ func IsAbs(path string) bool {
 }
 
 // VolumeName returns the leading volume name on Windows.
-// It returns "" elsewhere
+// It returns "" elsewhere.
 func VolumeName(path string) string {
        return ""
 }
index b8766588cf5937d091b6facc8ebd656442e8a156..097b0d9dc82bdc3f7172b4a2994a710b2ff198e3 100644 (file)
@@ -869,3 +869,34 @@ func TestDriveLetterInEvalSymlinks(t *testing.T) {
                t.Errorf("Results of EvalSymlinks do not match: %q and %q", flp, fup)
        }
 }
+
+/* This test does not work gccgo, since the sources are arranged
+   differently.
+
+func TestBug3486(t *testing.T) { // http://code.google.com/p/go/issues/detail?id=3486
+       root, err := filepath.EvalSymlinks(os.Getenv("GOROOT"))
+       if err != nil {
+               t.Fatal(err)
+       }
+       lib := filepath.Join(root, "lib")
+       src := filepath.Join(root, "src")
+       seenSrc := false
+       filepath.Walk(root, func(pth string, info os.FileInfo, err error) error {
+               if err != nil {
+                       t.Fatal(err)
+               }
+
+               switch pth {
+               case lib:
+                       return filepath.SkipDir
+               case src:
+                       seenSrc = true
+               }
+               return nil
+       })
+       if !seenSrc {
+               t.Fatalf("%q not seen", src)
+       }
+}
+
+*/
index 54c53776cf7adbb9ab8e97cac2bf35fad55621fb..87e6b1c61e41c314b49d5c0ee6c3229ec55200f0 100644 (file)
@@ -512,7 +512,7 @@ func (re *Regexp) replaceAll(bsrc []byte, src string, nmatch int, repl func(dst
 }
 
 // ReplaceAll returns a copy of src, replacing matches of the Regexp
-// with the replacement string repl.  Inside repl, $ signs are interpreted as
+// with the replacement text repl.  Inside repl, $ signs are interpreted as
 // in Expand, so for instance $1 represents the text of the first submatch.
 func (re *Regexp) ReplaceAll(src, repl []byte) []byte {
        n := 2
@@ -726,7 +726,7 @@ func (re *Regexp) FindSubmatch(b []byte) [][]byte {
 // the submatch with the corresponding index; other names refer to
 // capturing parentheses named with the (?P<name>...) syntax.  A
 // reference to an out of range or unmatched index or a name that is not
-// present in the regular expression is replaced with an empty string.
+// present in the regular expression is replaced with an empty slice.
 // 
 // In the $name form, name is taken to be as long as possible: $1x is
 // equivalent to ${1x}, not ${1}x, and, $10 is equivalent to ${10}, not ${1}0.
index 71b07b993070aebe96ac3c6baf9b0337219a8e18..4c61cb3a064597bb4bb5512628f959d3b240dd0b 100644 (file)
@@ -48,6 +48,9 @@ const (
        ErrTrailingBackslash     ErrorCode = "trailing backslash at end of expression"
 )
 
+// TODO: Export for Go 1.1.
+const errUnexpectedParen ErrorCode = "unexpected )"
+
 func (e ErrorCode) String() string {
        return string(e)
 }
@@ -1168,13 +1171,13 @@ func (p *parser) parseRightParen() error {
 
        n := len(p.stack)
        if n < 2 {
-               return &Error{ErrInternalError, ""}
+               return &Error{errUnexpectedParen, p.wholeRegexp}
        }
        re1 := p.stack[n-1]
        re2 := p.stack[n-2]
        p.stack = p.stack[:n-2]
        if re2.Op != opLeftParen {
-               return &Error{ErrMissingParen, p.wholeRegexp}
+               return &Error{errUnexpectedParen, p.wholeRegexp}
        }
        // Restore flags at time of paren.
        p.flags = re2.Flags
index 88f65ecfc9bddc5e58d1ca0e85a80ead6e1e56d6..e247cf203ac72d03bd857baaec02a41879f9fa52 100644 (file)
@@ -442,10 +442,18 @@ var invalidRegexps = []string{
        `(`,
        `)`,
        `(a`,
+       `a)`,
+       `(a))`,
        `(a|b|`,
+       `a|b|)`,
+       `(a|b|))`,
        `(a|b`,
+       `a|b)`,
+       `(a|b))`,
        `[a-z`,
        `([a-z)`,
+       `[a-z)`,
+       `([a-z]))`,
        `x{1001}`,
        `x{9876543210}`,
        `x{2,1}`,
index 659c8f448952e1cbfd2c8460891f205b14576bc0..09d1391d25490f1816709886745e37355e0fea89 100644 (file)
@@ -20,7 +20,7 @@ func Goexit()
 
 // Caller reports file and line number information about function invocations on
 // the calling goroutine's stack.  The argument skip is the number of stack frames
-// to ascend, with 1 identifying the caller of Caller.  (For historical reasons the
+// to ascend, with 0 identifying the caller of Caller.  (For historical reasons the
 // meaning of skip differs between Caller and Callers.) The return values report the
 // program counter, file name, and line number within the file of the corresponding
 // call.  The boolean ok is false if it was not possible to recover the information.
@@ -28,7 +28,8 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool)
 
 // Callers fills the slice pc with the program counters of function invocations
 // on the calling goroutine's stack.  The argument skip is the number of stack frames
-// to skip before recording in pc, with 0 starting at the caller of Callers.
+// to skip before recording in pc, with 0 identifying the frame for Callers itself and
+// 1 identifying the caller of Callers.
 // It returns the number of entries written to pc.
 func Callers(skip int, pc []uintptr) int
 
index ca40dd7ef626d86f925f769248b3bd61492b221b..67f17d866474b462f2b42140c2649ba4f2586b95 100644 (file)
@@ -4,13 +4,17 @@
 
 package strconv
 
-// FormatUint returns the string representation of i in the given base.
+// FormatUint returns the string representation of i in the given base,
+// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
+// for digit values >= 10.
 func FormatUint(i uint64, base int) string {
        _, s := formatBits(nil, i, base, false, false)
        return s
 }
 
-// FormatInt returns the string representation of i in the given base.
+// FormatInt returns the string representation of i in the given base,
+// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
+// for digit values >= 10.
 func FormatInt(i int64, base int) string {
        _, s := formatBits(nil, uint64(i), base, i < 0, false)
        return s
index 11417107282601c959b01aac2175f316a6f1262e..733caf5f2db27bc5d5cbda3d1bb821d9301038f9 100644 (file)
@@ -41,7 +41,6 @@ func ExampleContainsAny() {
 func ExampleCount() {
        fmt.Println(strings.Count("cheese", "e"))
        fmt.Println(strings.Count("five", "")) // before & after each rune
-
        // Output:
        // 3
        // 5
index feb434a3be97f115a5047ae33156dda6dbbe2cc3..aba21ce28f55c0d0ccf9f390bdcf66f52ef60f4b 100644 (file)
@@ -518,6 +518,13 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu
                }
        }
        if !value.Type().AssignableTo(typ) {
+               if value.Kind() == reflect.Interface && !value.IsNil() {
+                       value = value.Elem()
+                       if value.Type().AssignableTo(typ) {
+                               return value
+                       }
+                       // fallthrough
+               }
                // Does one dereference or indirection work? We could do more, as we
                // do with method receivers, but that gets messy and method receivers
                // are much more constrained, so it makes more sense there than here.
index 37d25f470cd5890c043a834995eca7f4f5ab5f0b..f4ae50f0ee9dcaa7183ab3f81926a864d01a4a35 100644 (file)
@@ -311,6 +311,7 @@ var execTests = []execTest{
        {".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=<he+llo>", tVal, true},
        {"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true},
        {"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true},
+       {"Interface Call", `{{stringer .S}}`, "foozle", map[string]interface{}{"S": bytes.NewBufferString("foozle")}, true},
 
        // Erroneous function calls (check args).
        {".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false},
@@ -507,6 +508,10 @@ func vfunc(V, *V) string {
        return "vfunc"
 }
 
+func stringer(s fmt.Stringer) string {
+       return s.String()
+}
+
 func testExecute(execTests []execTest, template *Template, t *testing.T) {
        b := new(bytes.Buffer)
        funcs := FuncMap{
@@ -516,6 +521,7 @@ func testExecute(execTests []execTest, template *Template, t *testing.T) {
                "typeOf":   typeOf,
                "vfunc":    vfunc,
                "zeroArgs": zeroArgs,
+               "stringer": stringer,
        }
        for _, test := range execTests {
                var tmpl *Template
index 440d3b42f12f3b931eb0362a4601d785e8b5b7e4..caa9702c9fc74b2a48c39efce289581d73935d66 100644 (file)
@@ -224,3 +224,25 @@ func TestTimerStopStress(t *testing.T) {
        }
        Sleep(3 * Second)
 }
+
+func TestSleepZeroDeadlock(t *testing.T) {
+       // Sleep(0) used to hang, the sequence of events was as follows.
+       // Sleep(0) sets G's status to Gwaiting, but then immediately returns leaving the status.
+       // Then the goroutine calls e.g. new and falls down into the scheduler due to pending GC.
+       // After the GC nobody wakes up the goroutine from Gwaiting status.
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
+       c := make(chan bool)
+       go func() {
+               for i := 0; i < 100; i++ {
+                       runtime.GC()
+               }
+               c <- true
+       }()
+       for i := 0; i < 100; i++ {
+               Sleep(0)
+               tmp := make(chan bool, 1)
+               tmp <- true
+               <-tmp
+       }
+       <-c
+}
index 5009e6b98c8165f815a1afeb8ea89396dc749adf..ebd169b0991dc4a7c0dd646e061ebf53bc1d854b 100644 (file)
@@ -2701,7 +2701,7 @@ var _Zs = &RangeTable{
        },
 }
 
-// The following variables are of type *RangeTable:
+// These variables have type *RangeTable.
 var (
        Cc     = _Cc // Cc is the set of Unicode characters in category Cc.
        Cf     = _Cf // Cf is the set of Unicode characters in category Cf.
@@ -4054,7 +4054,7 @@ var _Yi = &RangeTable{
        },
 }
 
-// The following variables are of type *RangeTable:
+// These variables have type *RangeTable.
 var (
        Arabic                 = _Arabic                 // Arabic is the set of Unicode characters in script Arabic.
        Armenian               = _Armenian               // Armenian is the set of Unicode characters in script Armenian.
@@ -5116,7 +5116,7 @@ var _White_Space = &RangeTable{
        },
 }
 
-// The following variables are of type *RangeTable:
+// These variables have type *RangeTable.
 var (
        ASCII_Hex_Digit                    = _ASCII_Hex_Digit                    // ASCII_Hex_Digit is the set of Unicode characters with property ASCII_Hex_Digit.
        Bidi_Control                       = _Bidi_Control                       // Bidi_Control is the set of Unicode characters with property Bidi_Control.
index 1a40637c3dde13b4cfa2ebd32bc310585903105f..eb1634690762d0524b5ffe4a8e003adbba3a7ab3 100644 (file)
@@ -168,6 +168,7 @@ __go_free(void *v)
                c->local_by_size[sizeclass].nfree++;
                runtime_MCache_Free(c, v, sizeclass, size);
        }
+       c->local_nfree++;
        c->local_alloc -= size;
        if(prof)
                runtime_MProf_Free(v, size);
index a89003716794d477eb9a53d1cea19a3692eafeae..e52ae3bce539a453b5a79d81e7d0122919acc4dd 100644 (file)
@@ -150,8 +150,7 @@ runtime_addfinalizer(void *p, void (*f)(void*), const struct __go_func_type *ft)
        tab = TAB(p);
        runtime_lock(tab);
        if(f == nil) {
-               if(lookfintab(tab, p, true, nil))
-                       runtime_setblockspecial(p, false);
+               lookfintab(tab, p, true, nil);
                runtime_unlock(tab);
                return true;
        }
index bd5eedc5d8be896c7db2d63f85c5d94829dde07b..d35cc0ffbdd64f987c727fdd9ce633f844341884 100644 (file)
@@ -1132,7 +1132,6 @@ runfinq(void* dummy __attribute__ ((unused)))
 
                                f = &fb->fin[i];
                                params[0] = &f->arg;
-                               runtime_setblockspecial(f->arg, false);
                                reflect_call(f->ft, (void*)f->fn, 0, 0, params, nil);
                                f->fn = nil;
                                f->arg = nil;
index cae15e5946a1ae4de33768a6fd481c0d08462a9b..b3f0fb0278fd3dcd35a290c955477fe780bf3d08 100644 (file)
@@ -61,15 +61,21 @@ ready(int64 now, Eface e)
 void
 runtime_tsleep(int64 ns)
 {
+       G* g;
        Timer t;
 
-       if(ns <= 0)
+       g = runtime_g();
+
+       if(ns <= 0) {
+               g->status = Grunning;
+               g->waitreason = nil;
                return;
+       }
 
        t.when = runtime_nanotime() + ns;
        t.period = 0;
        t.f = ready;
-       t.arg.__object = runtime_g();
+       t.arg.__object = g;
        addtimer(&t);
        runtime_gosched();
 }