libgo: Update to Go 1.0.2 release.

From-SVN: r188943
This commit is contained in:
Ian Lance Taylor 2012-06-25 16:20:03 +00:00
parent 72de8622ae
commit 08a680a887
59 changed files with 1088 additions and 169 deletions

View File

@ -1,4 +1,4 @@
2ccfd4b451d3 5e806355a9e1
The first line of this file holds the Mercurial revision number of the The first line of this file holds the Mercurial revision number of the
last merge done from the master library sources. last merge done from the master library sources.

View File

@ -32,6 +32,7 @@ const (
hashSize = 1 << hashBits hashSize = 1 << hashBits
hashMask = (1 << hashBits) - 1 hashMask = (1 << hashBits) - 1
hashShift = (hashBits + minMatchLength - 1) / minMatchLength hashShift = (hashBits + minMatchLength - 1) / minMatchLength
maxHashOffset = 1 << 24
skipNever = math.MaxInt32 skipNever = math.MaxInt32
) )
@ -106,6 +107,25 @@ func (d *compressor) fillDeflate(b []byte) int {
d.blockStart = math.MaxInt32 d.blockStart = math.MaxInt32
} }
d.hashOffset += windowSize 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) n := copy(d.window[d.windowEnd:], b)
d.windowEnd += n d.windowEnd += n

View 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 { type syncBuffer struct {
buf bytes.Buffer buf bytes.Buffer
mu sync.RWMutex mu sync.RWMutex

View File

@ -11,11 +11,11 @@ package aes
// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf // http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf
// AES is based on the mathematical behavior of binary polynomials // AES is based on the mathematical behavior of binary polynomials
// (polynomials over GF(2)) modulo the irreducible polynomial x⁸ + x⁴ + x² + x + 1. // (polynomials over GF(2)) modulo the irreducible polynomial x⁸ + x⁴ + x³ + x + 1.
// Addition of these binary polynomials corresponds to binary xor. // Addition of these binary polynomials corresponds to binary xor.
// Reducing mod poly corresponds to binary xor with poly every // Reducing mod poly corresponds to binary xor with poly every
// time a 0x100 bit appears. // time a 0x100 bit appears.
const poly = 1<<8 | 1<<4 | 1<<3 | 1<<1 | 1<<0 // x⁸ + x⁴ + x² + x + 1 const poly = 1<<8 | 1<<4 | 1<<3 | 1<<1 | 1<<0 // x⁸ + x⁴ + x³ + x + 1
// Powers of x mod poly in GF(2). // Powers of x mod poly in GF(2).
var powx = [16]byte{ var powx = [16]byte{

View 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 // 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 // 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 // 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 { func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
orderBits := c.Params().N.BitLen() orderBits := c.Params().N.BitLen()
orderBytes := (orderBits + 7) / 8 orderBytes := (orderBits + 7) / 8
@ -75,7 +77,7 @@ func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
} }
ret := new(big.Int).SetBytes(hash) ret := new(big.Int).SetBytes(hash)
excess := orderBytes*8 - orderBits excess := len(hash)*8 - orderBits
if excess > 0 { if excess > 0 {
ret.Rsh(ret, uint(excess)) ret.Rsh(ret, uint(excess))
} }

View File

@ -151,6 +151,7 @@ func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) {
var hashPrefixes = map[crypto.Hash][]byte{ 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.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.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.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.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}, crypto.SHA512: {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},

View File

@ -563,7 +563,7 @@ func (m *certificateMsg) unmarshal(data []byte) bool {
if len(d) < 4 { if len(d) < 4 {
return false 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 { if uint32(len(d)) < 3+certLen {
return false return false
} }
@ -575,7 +575,7 @@ func (m *certificateMsg) unmarshal(data []byte) bool {
m.certificates = make([][]byte, numCerts) m.certificates = make([][]byte, numCerts)
d = data[7:] d = data[7:]
for i := 0; i < numCerts; i++ { 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] m.certificates[i] = d[3 : 3+certLen]
d = d[3+certLen:] d = d[3+certLen:]
} }

View File

@ -388,10 +388,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
return ErrUnsupportedAlgorithm return ErrUnsupportedAlgorithm
} }
h := hashType.New() if !hashType.Available() {
if h == nil {
return ErrUnsupportedAlgorithm return ErrUnsupportedAlgorithm
} }
h := hashType.New()
h.Write(signed) h.Write(signed)
digest := h.Sum(nil) digest := h.Sum(nil)

View File

@ -7,14 +7,19 @@ package gosym
import ( import (
"debug/elf" "debug/elf"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"runtime" "runtime"
"strings" "strings"
"testing" "testing"
) )
var pclinetestBinary string var (
pclineTempDir string
pclinetestBinary string
)
func dotest() bool { func dotest() bool {
// For now, only works on ELF platforms. // For now, only works on ELF platforms.
@ -24,10 +29,18 @@ func dotest() bool {
if pclinetestBinary != "" { if pclinetestBinary != "" {
return true 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; // This command builds pclinetest from pclinetest.asm;
// the resulting binary looks like it was built from pclinetest.s, // the resulting binary looks like it was built from pclinetest.s,
// but we have renamed it to keep it away from the go tool. // 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", command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -E main -o %s %s.6",
pclinetestBinary, pclinetestBinary, pclinetestBinary) pclinetestBinary, pclinetestBinary, pclinetestBinary)
cmd := exec.Command("sh", "-c", command) cmd := exec.Command("sh", "-c", command)
@ -170,6 +183,7 @@ func TestPCLine(t *testing.T) {
if !dotest() { if !dotest() {
return return
} }
defer os.RemoveAll(pclineTempDir)
f, tab := crack(pclinetestBinary, t) f, tab := crack(pclinetestBinary, t)
text := f.Section(".text") text := f.Section(".text")

View 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) nn, d.err = io.ReadAtLeast(d.r, d.buf[d.nbuf:nn], 4-d.nbuf)
d.nbuf += nn d.nbuf += nn
if d.nbuf < 4 { if d.err != nil || d.nbuf < 4 {
return 0, d.err return 0, d.err
} }

View File

@ -6,9 +6,11 @@ package base64
import ( import (
"bytes" "bytes"
"errors"
"io" "io"
"io/ioutil" "io/ioutil"
"testing" "testing"
"time"
) )
type testpair struct { 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")
}
}

View 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 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. 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 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 a map, the receiver will allocate a map even no elements are
transmitted. transmitted.

View File

@ -273,9 +273,14 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler,
_, isUnmarshaler = v.Interface().(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() { if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() {
v = iv.Elem() e := iv.Elem()
continue if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
v = e
continue
}
} }
pv := v pv := v
@ -588,6 +593,11 @@ func (d *decodeState) literal(v reflect.Value) {
// produce more helpful error messages. // produce more helpful error messages.
func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) { func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) {
// Check for unmarshaler. // 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 wantptr := item[0] == 'n' // null
unmarshaler, pv := d.indirect(v, wantptr) unmarshaler, pv := d.indirect(v, wantptr)
if unmarshaler != nil { if unmarshaler != nil {

View File

@ -638,3 +638,68 @@ func TestAnonymous(t *testing.T) {
t.Fatal("Unmarshal: did set T.Y") 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)
}
}
}

View File

@ -96,7 +96,7 @@ import (
// //
// Channel, complex, and function values cannot be encoded in JSON. // Channel, complex, and function values cannot be encoded in JSON.
// Attempting to encode such a value causes Marshal to return // Attempting to encode such a value causes Marshal to return
// an InvalidTypeError. // an UnsupportedTypeError.
// //
// JSON cannot represent cyclic data structures and Marshal does not // JSON cannot represent cyclic data structures and Marshal does not
// handle them. Passing cyclic structures to Marshal will result in // handle them. Passing cyclic structures to Marshal will result in
@ -157,6 +157,8 @@ type Marshaler interface {
MarshalJSON() ([]byte, error) MarshalJSON() ([]byte, error)
} }
// An UnsupportedTypeError is returned by Marshal when attempting
// to encode an unsupported value type.
type UnsupportedTypeError struct { type UnsupportedTypeError struct {
Type reflect.Type Type reflect.Type
} }

View File

@ -620,8 +620,9 @@ func (f *FlagSet) Var(value Value, name string, usage string) {
flag := &Flag{name, usage, value, value.String()} flag := &Flag{name, usage, value, value.String()}
_, alreadythere := f.formal[name] _, alreadythere := f.formal[name]
if alreadythere { if alreadythere {
fmt.Fprintf(f.out(), "%s flag redefined: %s\n", f.name, name) msg := fmt.Sprintf("%s flag redefined: %s", f.name, name)
panic("flag redefinition") // Happens only if flags are declared with identical names fmt.Fprintln(f.out(), msg)
panic(msg) // Happens only if flags are declared with identical names
} }
if f.formal == nil { if f.formal == nil {
f.formal = make(map[string]*Flag) f.formal = make(map[string]*Flag)

View File

@ -136,7 +136,7 @@
Fscanf and Fscanln read from a specified io.Reader; Sscan, Fscanf and Fscanln read from a specified io.Reader; Sscan,
Sscanf and Sscanln read from an argument string. Scanln, Sscanf and Sscanln read from an argument string. Scanln,
Fscanln and Sscanln stop scanning at a newline and require that 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 newlines in the input to match newlines in the format; the other
routines treat newlines as spaces. routines treat newlines as spaces.

View File

@ -87,8 +87,12 @@ func stripTrailingWhitespace(s string) string {
return s[0:i] return s[0:i]
} }
// Text returns the text of the comment, // Text returns the text of the comment.
// with the comment markers - //, /*, and */ - removed. // 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 { func (g *CommentGroup) Text() string {
if g == nil { if g == nil {
return "" return ""
@ -104,11 +108,9 @@ func (g *CommentGroup) Text() string {
// The parser has given us exactly the comment text. // The parser has given us exactly the comment text.
switch c[1] { switch c[1] {
case '/': case '/':
//-style comment //-style comment (no newline at the end)
c = c[2:] c = c[2:]
// Remove leading space after //, if there is one. // strip first space - required for Example tests
// TODO(gri) This appears to be necessary in isolated
// cases (bignum.RatFromString) - why?
if len(c) > 0 && c[0] == ' ' { if len(c) > 0 && c[0] == ' ' {
c = c[1:] c = c[1:]
} }

View File

@ -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)
}
}
}

View File

@ -68,7 +68,7 @@ type Context struct {
// ReadDir returns a slice of os.FileInfo, sorted by Name, // ReadDir returns a slice of os.FileInfo, sorted by Name,
// describing the content of the named directory. // 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) ReadDir func(dir string) (fi []os.FileInfo, err error)
// OpenFile opens a file (not a directory) for reading. // 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 starting with _ or . (likely editor temporary files)
// - files with build constraints not satisfied by the context // - 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. // *Package containing partial information.
// //
func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) { func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) {

View 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 // Consume a group of adjacent comments, add it to the parser's
// comments list, and return it together with the line at which // comments list, and return it together with the line at which
// the last comment in the group ends. An empty line or non-comment // the last comment in the group ends. A non-comment token or n
// token terminates a comment group. // 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 var list []*ast.Comment
endline = p.file.Line(p.pos) 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 var comment *ast.Comment
comment, endline = p.consumeComment() comment, endline = p.consumeComment()
list = append(list, comment) list = append(list, comment)
@ -314,7 +314,7 @@ func (p *parser) next() {
if p.file.Line(p.pos) == line { if p.file.Line(p.pos) == line {
// The comment is on same line as the previous token; it // The comment is on same line as the previous token; it
// cannot be a lead comment but may be a line comment. // 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 { if p.file.Line(p.pos) != endline {
// The next token is on a different line, thus // The next token is on a different line, thus
// the last comment group is a line comment. // the last comment group is a line comment.
@ -325,7 +325,7 @@ func (p *parser) next() {
// consume successor comments, if any // consume successor comments, if any
endline = -1 endline = -1
for p.tok == token.COMMENT { for p.tok == token.COMMENT {
comment, endline = p.consumeCommentGroup() comment, endline = p.consumeCommentGroup(1)
} }
if endline+1 == p.file.Line(p.pos) { if endline+1 == p.file.Line(p.pos) {
@ -627,10 +627,10 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
doc := p.leadComment doc := p.leadComment
// fields // FieldDecl
list, typ := p.parseVarList(false) list, typ := p.parseVarList(false)
// optional tag // Tag
var tag *ast.BasicLit var tag *ast.BasicLit
if p.tok == token.STRING { if p.tok == token.STRING {
tag = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit} 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 { } else {
// ["*"] TypeName (AnonymousField) // ["*"] TypeName (AnonymousField)
typ = list[0] // we always have at least one element typ = list[0] // we always have at least one element
p.resolve(typ)
if n := len(list); n > 1 || !isTypeName(deref(typ)) { if n := len(list); n > 1 || !isTypeName(deref(typ)) {
pos := typ.Pos() pos := typ.Pos()
p.errorExpected(pos, "anonymous field") 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} field := &ast.Field{Doc: doc, Names: idents, Type: typ, Tag: tag, Comment: p.lineComment}
p.declare(field, nil, scope, ast.Var, idents...) p.declare(field, nil, scope, ast.Var, idents...)
p.resolve(typ)
return field return field
} }
@ -699,12 +699,15 @@ func (p *parser) parsePointerType() *ast.StarExpr {
return &ast.StarExpr{Star: star, X: base} 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 { func (p *parser) tryVarType(isParam bool) ast.Expr {
if isParam && p.tok == token.ELLIPSIS { if isParam && p.tok == token.ELLIPSIS {
pos := p.pos pos := p.pos
p.next() p.next()
typ := p.tryIdentOrType(isParam) // 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 { if typ != nil {
p.resolve(typ)
} else {
p.error(pos, "'...' parameter is missing type") p.error(pos, "'...' parameter is missing type")
typ = &ast.BadExpr{From: pos, To: p.pos} typ = &ast.BadExpr{From: pos, To: p.pos}
} }
@ -713,6 +716,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
return p.tryIdentOrType(false) return p.tryIdentOrType(false)
} }
// If the result is an identifier, it is not resolved.
func (p *parser) parseVarType(isParam bool) ast.Expr { func (p *parser) parseVarType(isParam bool) ast.Expr {
typ := p.tryVarType(isParam) typ := p.tryVarType(isParam)
if typ == nil { if typ == nil {
@ -724,6 +728,7 @@ func (p *parser) parseVarType(isParam bool) ast.Expr {
return typ 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) { func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
if p.trace { if p.trace {
defer un(trace(p, "VarList")) 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 we had a list of identifiers, it must be followed by a type
if typ = p.tryVarType(isParam); typ != nil { typ = p.tryVarType(isParam)
p.resolve(typ)
}
return return
} }
@ -756,7 +759,10 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
defer un(trace(p, "ParameterList")) defer un(trace(p, "ParameterList"))
} }
// ParameterDecl
list, typ := p.parseVarList(ellipsisOk) list, typ := p.parseVarList(ellipsisOk)
// analyze case
if typ != nil { if typ != nil {
// IdentifierList Type // IdentifierList Type
idents := p.makeIdentList(list) 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 // Go spec: The scope of an identifier denoting a function
// parameter or result variable is the function body. // parameter or result variable is the function body.
p.declare(field, nil, scope, ast.Var, idents...) p.declare(field, nil, scope, ast.Var, idents...)
p.resolve(typ)
if p.tok == token.COMMA { if p.tok == token.COMMA {
p.next() p.next()
} }
for p.tok != token.RPAREN && p.tok != token.EOF { for p.tok != token.RPAREN && p.tok != token.EOF {
idents := p.parseIdentList() idents := p.parseIdentList()
typ := p.parseVarType(ellipsisOk) 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 // Go spec: The scope of an identifier denoting a function
// parameter or result variable is the function body. // parameter or result variable is the function body.
p.declare(field, nil, scope, ast.Var, idents...) p.declare(field, nil, scope, ast.Var, idents...)
p.resolve(typ)
if !p.atComma("parameter list") { if !p.atComma("parameter list") {
break break
} }
p.next() p.next()
} }
} else { } else {
// Type { "," Type } (anonymous parameters) // Type { "," Type } (anonymous parameters)
params = make([]*ast.Field, len(list)) params = make([]*ast.Field, len(list))
for i, x := range list { for i, typ := range list {
p.resolve(x) p.resolve(typ)
params[i] = &ast.Field{Type: x} params[i] = &ast.Field{Type: typ}
} }
} }

View File

@ -5,10 +5,12 @@
package parser package parser
import ( import (
"bytes"
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"os" "os"
"strings"
"testing" "testing"
) )
@ -25,7 +27,7 @@ func TestParse(t *testing.T) {
for _, filename := range validFiles { for _, filename := range validFiles {
_, err := ParseFile(fset, filename, nil, DeclarationErrors) _, err := ParseFile(fset, filename, nil, DeclarationErrors)
if err != nil { 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" src := "a + b"
x, err := ParseExpr(src) x, err := ParseExpr(src)
if err != nil { if err != nil {
t.Errorf("ParseExpr(%s): %v", src, err) t.Fatalf("ParseExpr(%s): %v", src, err)
} }
// sanity check // sanity check
if _, ok := x.(*ast.BinaryExpr); !ok { if _, ok := x.(*ast.BinaryExpr); !ok {
@ -81,7 +83,7 @@ func TestParseExpr(t *testing.T) {
src = "a + *" src = "a + *"
_, err = ParseExpr(src) _, err = ParseExpr(src)
if err == nil { if err == nil {
t.Errorf("ParseExpr(%s): %v", src, err) t.Fatalf("ParseExpr(%s): %v", src, err)
} }
// it must not crash // it must not crash
@ -93,7 +95,7 @@ func TestParseExpr(t *testing.T) {
func TestColonEqualsScope(t *testing.T) { func TestColonEqualsScope(t *testing.T) {
f, err := ParseFile(fset, "", `package p; func f() { x, y, z := x, y, z }`, 0) f, err := ParseFile(fset, "", `package p; func f() { x, y, z := x, y, z }`, 0)
if err != nil { if err != nil {
t.Errorf("parse: %s", err) t.Fatal(err)
} }
// RHS refers to undefined globals; LHS does not. // RHS refers to undefined globals; LHS does not.
@ -115,7 +117,7 @@ func TestColonEqualsScope(t *testing.T) {
func TestVarScope(t *testing.T) { func TestVarScope(t *testing.T) {
f, err := ParseFile(fset, "", `package p; func f() { var x, y, z = x, y, z }`, 0) f, err := ParseFile(fset, "", `package p; func f() { var x, y, z = x, y, z }`, 0)
if err != nil { if err != nil {
t.Errorf("parse: %s", err) t.Fatal(err)
} }
// RHS refers to undefined globals; LHS does not. // 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{ var imports = map[string]bool{
`"a"`: true, `"a"`: true,
"`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")
}
}

View 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 // 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 // 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 // as exports only. It assumes that there is no pending comment in p.comments
// intersperse. // and at most one pending comment in the p.comment cache.
func (p *printer) setComment(g *ast.CommentGroup) { func (p *printer) setComment(g *ast.CommentGroup) {
if g == nil || !p.useNodeComments { if g == nil || !p.useNodeComments {
return return
@ -74,10 +74,19 @@ func (p *printer) setComment(g *ast.CommentGroup) {
// should never happen - handle gracefully and flush // should never happen - handle gracefully and flush
// all comments up to g, ignore anything after that // all comments up to g, ignore anything after that
p.flush(p.posFor(g.List[0].Pos()), token.ILLEGAL) 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.comments[0] = g
p.cindex = 0 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 type exprListMode uint

View File

@ -26,7 +26,7 @@ const (
// Bits or'ed together to control what's printed. There is no control over the // 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 // order they appear (the order listed here) or the format they present (as
// described in the comments). A colon appears after these items: // 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 Ldate = 1 << iota // the date: 2009/01/23
Ltime // the time: 01:23:23 Ltime // the time: 01:23:23
Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime. Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.

View File

@ -271,10 +271,10 @@ func karatsuba(z, x, y nat) {
// xd = x1 - x0 // xd = x1 - x0
// yd = y0 - y1 // yd = y0 - y1
// //
// z1 = xd*yd + z1 + z0 // z1 = xd*yd + z2 + z0
// = (x1-x0)*(y0 - y1) + z1 + z0 // = (x1-x0)*(y0 - y1) + z2 + z0
// = x1*y0 - x1*y1 - x0*y0 + x0*y1 + z1 + z0 // = x1*y0 - x1*y1 - x0*y0 + x0*y1 + z2 + z0
// = x1*y0 - z1 - z0 + x0*y1 + z1 + z0 // = x1*y0 - z2 - z0 + x0*y1 + z2 + z0
// = x1*y0 + x0*y1 // = x1*y0 + x0*y1
// split x, y into "digits" // split x, y into "digits"
@ -318,7 +318,7 @@ func karatsuba(z, x, y nat) {
// save original z2:z0 // save original z2:z0
// (ok to use upper half of z since we're done recursing) // (ok to use upper half of z since we're done recursing)
r := z[n*4:] r := z[n*4:]
copy(r, z) copy(r, z[:n*2])
// add up all partial products // add up all partial products
// //

View 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) }

View File

@ -22,11 +22,6 @@ import (
"net/textproto" "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) var emptyParams = make(map[string]string)
// A Part represents a single part in a multipart body. // 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" // i.e. "foo-bar" changes case to "Foo-Bar"
Header textproto.MIMEHeader Header textproto.MIMEHeader
buffer *bytes.Buffer buffer *bytes.Buffer
mr *Reader mr *Reader
bytesRead int
disposition string disposition string
dispositionParams map[string]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 // Read reads the body of a part, after its headers and before the
// next part (if any) begins. // next part (if any) begins.
func (p *Part) Read(d []byte) (n int, err error) { func (p *Part) Read(d []byte) (n int, err error) {
defer func() {
p.bytesRead += n
}()
if p.buffer.Len() >= len(d) { if p.buffer.Len() >= len(d) {
// Internal buffer of unconsumed data is large enough for // Internal buffer of unconsumed data is large enough for
// the read request. No need to parse more at the moment. // the read request. No need to parse more at the moment.
return p.buffer.Read(d) return p.buffer.Read(d)
} }
peek, err := p.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessor 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) return 0, fmt.Errorf("multipart: Part Read: %v", err)
} }
if peek == nil { if peek == nil {
@ -138,7 +146,7 @@ func (p *Part) Read(d []byte) (n int, err error) {
foundBoundary = true foundBoundary = true
} else if safeCount := len(peek) - len(p.mr.nlDashBoundary); safeCount > 0 { } else if safeCount := len(peek) - len(p.mr.nlDashBoundary); safeCount > 0 {
nCopy = safeCount nCopy = safeCount
} else if unexpectedEof { } else if unexpectedEOF {
// If we've run out of peek buffer and the boundary // If we've run out of peek buffer and the boundary
// wasn't found (and can't possibly fit), we must have // wasn't found (and can't possibly fit), we must have
// hit the end of the file unexpectedly. // hit the end of the file unexpectedly.
@ -172,7 +180,10 @@ type Reader struct {
currentPart *Part currentPart *Part
partsRead int 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. // NextPart returns the next part in the multipart or an error.
@ -185,7 +196,7 @@ func (r *Reader) NextPart() (*Part, error) {
expectNewPart := false expectNewPart := false
for { for {
line, err := r.bufReader.ReadSlice('\n') 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 // If the buffer ends in "--boundary--" without the
// trailing "\r\n", ReadSlice will return an error // trailing "\r\n", ReadSlice will return an error
// (since it's missing the '\n'), but this is a valid // (since it's missing the '\n'), but this is a valid
@ -207,7 +218,7 @@ func (r *Reader) NextPart() (*Part, error) {
return bp, nil return bp, nil
} }
if hasPrefixThenNewline(line, r.dashBoundaryDash) { if r.isFinalBoundary(line) {
// Expected EOF // Expected EOF
return nil, io.EOF return nil, io.EOF
} }
@ -235,7 +246,19 @@ func (r *Reader) NextPart() (*Part, error) {
panic("unreachable") 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 // http://tools.ietf.org/html/rfc2046#section-5.1
// The boundary delimiter line is then defined as a line // The boundary delimiter line is then defined as a line
// consisting entirely of two hyphen characters ("-", // consisting entirely of two hyphen characters ("-",
@ -245,32 +268,52 @@ func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {
if !bytes.HasPrefix(line, mr.dashBoundary) { if !bytes.HasPrefix(line, mr.dashBoundary) {
return false return false
} }
if bytes.HasSuffix(line, mr.nl) { rest := line[len(mr.dashBoundary):]
return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-len(mr.nl)]) 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 return bytes.Equal(rest, mr.nl)
// 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
} }
func onlyHorizontalWhitespace(s []byte) bool { // peekBufferIsEmptyPart returns whether the provided peek-ahead
for _, b := range s { // buffer represents an empty part. This is only called if we've not
if b != ' ' && b != '\t' { // already read any bytes in this part and checks for the case of MIME
return false // 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 { // skipLWSPChar returns b with leading spaces and tabs removed.
return bytes.HasPrefix(s, prefix) && // RFC 822 defines:
(len(s) == len(prefix)+1 && s[len(s)-1] == '\n' || // LWSP-char = SPACE / HTAB
len(s) == len(prefix)+2 && bytes.HasSuffix(s, crlf)) func skipLWSPChar(b []byte) []byte {
for len(b) > 0 && (b[0] == ' ' || b[0] == '\t') {
b = b[1:]
}
return b
} }

View File

@ -10,20 +10,13 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/textproto"
"os" "os"
"reflect"
"strings" "strings"
"testing" "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) { func TestBoundaryLine(t *testing.T) {
mr := NewReader(strings.NewReader(""), "myBoundary") mr := NewReader(strings.NewReader(""), "myBoundary")
if !mr.isBoundaryDelimiterLine([]byte("--myBoundary\r\n")) { 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 { type slowReader struct {
r io.Reader r io.Reader
} }
@ -427,3 +397,214 @@ func TestNested(t *testing.T) {
t.Fatalf("final outer NextPart = %v; want io.EOF", err) 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
}

View File

@ -89,8 +89,8 @@ func FileConn(f *os.File) (c Conn, err error) {
// FileListener returns a copy of the network listener corresponding // FileListener returns a copy of the network listener corresponding
// to the open file f. It is the caller's responsibility to close l // 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 // when finished. Closing l does not affect f, and closing f does not
// affect c. // affect l.
func FileListener(f *os.File) (l Listener, err error) { func FileListener(f *os.File) (l Listener, err error) {
fd, err := newFileFD(f) fd, err := newFileFD(f)
if err != nil { if err != nil {

View File

@ -278,6 +278,11 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response,
return nil, err return nil, err
} }
req.Header.Set("Content-Type", bodyType) 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) r, err = send(req, c.Transport)
if err == nil && c.Jar != nil { if err == nil && c.Jar != nil {
c.Jar.SetCookies(req.URL, r.Cookies()) c.Jar.SetCookies(req.URL, r.Cookies())

View 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 // Just enough correctness for our redirect tests. Uses the URL.Host as the
// scope of all cookies. // scope of all cookies.
type TestJar struct { type TestJar struct {

View File

@ -5,6 +5,7 @@
package http package http
import ( import (
"net/url"
"os" "os"
"testing" "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)
}
}
}

View File

@ -202,9 +202,12 @@ func (r *Response) Write(w io.Writer) error {
text = "status code " + strconv.Itoa(r.StatusCode) text = "status code " + strconv.Itoa(r.StatusCode)
} }
} }
io.WriteString(w, "HTTP/"+strconv.Itoa(r.ProtoMajor)+".") protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)
io.WriteString(w, strconv.Itoa(r.ProtoMinor)+" ") statusCode := strconv.Itoa(r.StatusCode) + " "
io.WriteString(w, strconv.Itoa(r.StatusCode)+" "+text+"\r\n") if strings.HasPrefix(text, statusCode) {
text = text[len(statusCode):]
}
io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n")
// Process Body,ContentLength,Close,Trailer // Process Body,ContentLength,Close,Trailer
tw, err := newTransferWriter(r) tw, err := newTransferWriter(r)

View File

@ -14,6 +14,7 @@ import (
"io/ioutil" "io/ioutil"
"net/url" "net/url"
"reflect" "reflect"
"strings"
"testing" "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())
}
}

View File

@ -31,7 +31,7 @@ import (
// Errors introduced by the HTTP server. // Errors introduced by the HTTP server.
var ( var (
ErrWriteAfterFlush = errors.New("Conn.Write called after Flush") 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") ErrHijacked = errors.New("Conn has been hijacked")
ErrContentLength = errors.New("Conn.Write wrote more than the declared Content-Length") ErrContentLength = errors.New("Conn.Write wrote more than the declared Content-Length")
) )

View File

@ -71,7 +71,9 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) {
} }
} }
case *Response: case *Response:
t.Method = rr.Request.Method if rr.Request != nil {
t.Method = rr.Request.Method
}
t.Body = rr.Body t.Body = rr.Body
t.BodyCloser = rr.Body t.BodyCloser = rr.Body
t.ContentLength = rr.ContentLength t.ContentLength = rr.ContentLength
@ -79,7 +81,7 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) {
t.TransferEncoding = rr.TransferEncoding t.TransferEncoding = rr.TransferEncoding
t.Trailer = rr.Trailer t.Trailer = rr.Trailer
atLeastHTTP11 = rr.ProtoAtLeast(1, 1) atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
t.ResponseToHEAD = noBodyExpected(rr.Request.Method) t.ResponseToHEAD = noBodyExpected(t.Method)
} }
// Sanitize Body,ContentLength,TransferEncoding // Sanitize Body,ContentLength,TransferEncoding

View File

@ -450,10 +450,14 @@ type connectMethod struct {
func (ck *connectMethod) String() string { func (ck *connectMethod) String() string {
proxyStr := "" proxyStr := ""
targetAddr := ck.targetAddr
if ck.proxyURL != nil { if ck.proxyURL != nil {
proxyStr = ck.proxyURL.String() 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. // addr returns the first hop "host:port" to which we need to TCP connect.

View File

@ -69,11 +69,12 @@ var dateLayouts []string
func init() { func init() {
// Generate layouts based on RFC 5322, section 3.3. // Generate layouts based on RFC 5322, section 3.3.
dows := [...]string{"", "Mon, "} // day-of-week dows := [...]string{"", "Mon, "} // day-of-week
days := [...]string{"2", "02"} // day = 1*2DIGIT days := [...]string{"2", "02"} // day = 1*2DIGIT
years := [...]string{"2006", "06"} // year = 4*DIGIT / 2*DIGIT years := [...]string{"2006", "06"} // year = 4*DIGIT / 2*DIGIT
seconds := [...]string{":05", ""} // second seconds := [...]string{":05", ""} // second
zones := [...]string{"-0700", "MST"} // zone = (("+" / "-") 4DIGIT) / "GMT" / ... // "-0700 (MST)" is not in RFC 5322, but is common.
zones := [...]string{"-0700", "MST", "-0700 (MST)"} // zone = (("+" / "-") 4DIGIT) / "GMT" / ...
for _, dow := range dows { for _, dow := range dows {
for _, day := range days { for _, day := range days {

View File

@ -95,6 +95,11 @@ func TestDateParsing(t *testing.T) {
"21 Nov 97 09:55:06 GMT", "21 Nov 97 09:55:06 GMT",
time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("GMT", 0)), 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 { for _, test := range tests {
hdr := Header{ hdr := Header{

View File

@ -401,11 +401,12 @@ Error:
} }
func parseAuthority(authority string) (user *Userinfo, host string, err 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 host = authority
return return
} }
userinfo, host := split(authority, '@', true) userinfo, host := authority[:i], authority[i+1:]
if strings.Index(userinfo, ":") < 0 { if strings.Index(userinfo, ":") < 0 {
if userinfo, err = unescape(userinfo, encodeUserPassword); err != nil { if userinfo, err = unescape(userinfo, encodeUserPassword); err != nil {
return return

View File

@ -188,6 +188,37 @@ var urltests = []URLTest{
}, },
"http://user:password@google.com", "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", "http://www.google.com/?q=go+language#foo",
&URL{ &URL{

View File

@ -204,6 +204,12 @@ func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) {
return pw, nil 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. // Run starts the specified command and waits for it to complete.
// //
// The returned error is nil if the command runs, has no problems // 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} { for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {
fd, err := setupFd(c) fd, err := setupFd(c)
if err != nil { if err != nil {
c.closeDescriptors(c.closeAfterStart)
c.closeDescriptors(c.closeAfterWait)
return err return err
} }
c.childFiles = append(c.childFiles, fd) c.childFiles = append(c.childFiles, fd)
@ -247,12 +255,12 @@ func (c *Cmd) Start() error {
Sys: c.SysProcAttr, Sys: c.SysProcAttr,
}) })
if err != nil { if err != nil {
c.closeDescriptors(c.closeAfterStart)
c.closeDescriptors(c.closeAfterWait)
return err return err
} }
for _, fd := range c.closeAfterStart { c.closeDescriptors(c.closeAfterStart)
fd.Close()
}
c.errch = make(chan error, len(c.goroutine)) c.errch = make(chan error, len(c.goroutine))
for _, fn := range c.goroutine { for _, fn := range c.goroutine {
@ -301,9 +309,7 @@ func (c *Cmd) Wait() error {
} }
} }
for _, fd := range c.closeAfterWait { c.closeDescriptors(c.closeAfterWait)
fd.Close()
}
if err != nil { if err != nil {
return err return err

View File

@ -320,8 +320,11 @@ func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
} }
for _, fileInfo := range list { for _, fileInfo := range list {
if err = walk(Join(path, fileInfo.Name()), fileInfo, walkFn); err != nil { err = walk(Join(path, fileInfo.Name()), fileInfo, walkFn)
return err if err != nil {
if !fileInfo.IsDir() || err != SkipDir {
return err
}
} }
} }
return nil return nil

View File

@ -12,7 +12,7 @@ func IsAbs(path string) bool {
} }
// VolumeName returns the leading volume name on Windows. // VolumeName returns the leading volume name on Windows.
// It returns "" elsewhere // It returns "" elsewhere.
func VolumeName(path string) string { func VolumeName(path string) string {
return "" return ""
} }

View File

@ -869,3 +869,34 @@ func TestDriveLetterInEvalSymlinks(t *testing.T) {
t.Errorf("Results of EvalSymlinks do not match: %q and %q", flp, fup) 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)
}
}
*/

View 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 // 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. // in Expand, so for instance $1 represents the text of the first submatch.
func (re *Regexp) ReplaceAll(src, repl []byte) []byte { func (re *Regexp) ReplaceAll(src, repl []byte) []byte {
n := 2 n := 2
@ -726,7 +726,7 @@ func (re *Regexp) FindSubmatch(b []byte) [][]byte {
// the submatch with the corresponding index; other names refer to // the submatch with the corresponding index; other names refer to
// capturing parentheses named with the (?P<name>...) syntax. A // capturing parentheses named with the (?P<name>...) syntax. A
// reference to an out of range or unmatched index or a name that is not // 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 // 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. // equivalent to ${1x}, not ${1}x, and, $10 is equivalent to ${10}, not ${1}0.

View File

@ -48,6 +48,9 @@ const (
ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression" ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression"
) )
// TODO: Export for Go 1.1.
const errUnexpectedParen ErrorCode = "unexpected )"
func (e ErrorCode) String() string { func (e ErrorCode) String() string {
return string(e) return string(e)
} }
@ -1168,13 +1171,13 @@ func (p *parser) parseRightParen() error {
n := len(p.stack) n := len(p.stack)
if n < 2 { if n < 2 {
return &Error{ErrInternalError, ""} return &Error{errUnexpectedParen, p.wholeRegexp}
} }
re1 := p.stack[n-1] re1 := p.stack[n-1]
re2 := p.stack[n-2] re2 := p.stack[n-2]
p.stack = p.stack[:n-2] p.stack = p.stack[:n-2]
if re2.Op != opLeftParen { if re2.Op != opLeftParen {
return &Error{ErrMissingParen, p.wholeRegexp} return &Error{errUnexpectedParen, p.wholeRegexp}
} }
// Restore flags at time of paren. // Restore flags at time of paren.
p.flags = re2.Flags p.flags = re2.Flags

View File

@ -442,10 +442,18 @@ var invalidRegexps = []string{
`(`, `(`,
`)`, `)`,
`(a`, `(a`,
`a)`,
`(a))`,
`(a|b|`, `(a|b|`,
`a|b|)`,
`(a|b|))`,
`(a|b`, `(a|b`,
`a|b)`,
`(a|b))`,
`[a-z`, `[a-z`,
`([a-z)`, `([a-z)`,
`[a-z)`,
`([a-z]))`,
`x{1001}`, `x{1001}`,
`x{9876543210}`, `x{9876543210}`,
`x{2,1}`, `x{2,1}`,

View File

@ -20,7 +20,7 @@ func Goexit()
// Caller reports file and line number information about function invocations on // 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 // 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 // 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 // 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. // 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 // 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 // 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. // It returns the number of entries written to pc.
func Callers(skip int, pc []uintptr) int func Callers(skip int, pc []uintptr) int

View File

@ -4,13 +4,17 @@
package strconv 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 { func FormatUint(i uint64, base int) string {
_, s := formatBits(nil, i, base, false, false) _, s := formatBits(nil, i, base, false, false)
return s 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 { func FormatInt(i int64, base int) string {
_, s := formatBits(nil, uint64(i), base, i < 0, false) _, s := formatBits(nil, uint64(i), base, i < 0, false)
return s return s

View File

@ -41,7 +41,6 @@ func ExampleContainsAny() {
func ExampleCount() { func ExampleCount() {
fmt.Println(strings.Count("cheese", "e")) fmt.Println(strings.Count("cheese", "e"))
fmt.Println(strings.Count("five", "")) // before & after each rune fmt.Println(strings.Count("five", "")) // before & after each rune
// Output: // Output:
// 3 // 3
// 5 // 5

View File

@ -518,6 +518,13 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu
} }
} }
if !value.Type().AssignableTo(typ) { 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 // Does one dereference or indirection work? We could do more, as we
// do with method receivers, but that gets messy and method receivers // do with method receivers, but that gets messy and method receivers
// are much more constrained, so it makes more sense there than here. // are much more constrained, so it makes more sense there than here.

View File

@ -311,6 +311,7 @@ var execTests = []execTest{
{".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=<he+llo>", tVal, true}, {".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 .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}, {"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). // Erroneous function calls (check args).
{".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false}, {".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false},
@ -507,6 +508,10 @@ func vfunc(V, *V) string {
return "vfunc" return "vfunc"
} }
func stringer(s fmt.Stringer) string {
return s.String()
}
func testExecute(execTests []execTest, template *Template, t *testing.T) { func testExecute(execTests []execTest, template *Template, t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
funcs := FuncMap{ funcs := FuncMap{
@ -516,6 +521,7 @@ func testExecute(execTests []execTest, template *Template, t *testing.T) {
"typeOf": typeOf, "typeOf": typeOf,
"vfunc": vfunc, "vfunc": vfunc,
"zeroArgs": zeroArgs, "zeroArgs": zeroArgs,
"stringer": stringer,
} }
for _, test := range execTests { for _, test := range execTests {
var tmpl *Template var tmpl *Template

View File

@ -224,3 +224,25 @@ func TestTimerStopStress(t *testing.T) {
} }
Sleep(3 * Second) 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
}

View File

@ -2701,7 +2701,7 @@ var _Zs = &RangeTable{
}, },
} }
// The following variables are of type *RangeTable: // These variables have type *RangeTable.
var ( var (
Cc = _Cc // Cc is the set of Unicode characters in category Cc. Cc = _Cc // Cc is the set of Unicode characters in category Cc.
Cf = _Cf // Cf is the set of Unicode characters in category Cf. 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 ( var (
Arabic = _Arabic // Arabic is the set of Unicode characters in script Arabic. Arabic = _Arabic // Arabic is the set of Unicode characters in script Arabic.
Armenian = _Armenian // Armenian is the set of Unicode characters in script Armenian. 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 ( var (
ASCII_Hex_Digit = _ASCII_Hex_Digit // ASCII_Hex_Digit is the set of Unicode characters with property ASCII_Hex_Digit. 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. Bidi_Control = _Bidi_Control // Bidi_Control is the set of Unicode characters with property Bidi_Control.

View File

@ -168,6 +168,7 @@ __go_free(void *v)
c->local_by_size[sizeclass].nfree++; c->local_by_size[sizeclass].nfree++;
runtime_MCache_Free(c, v, sizeclass, size); runtime_MCache_Free(c, v, sizeclass, size);
} }
c->local_nfree++;
c->local_alloc -= size; c->local_alloc -= size;
if(prof) if(prof)
runtime_MProf_Free(v, size); runtime_MProf_Free(v, size);

View File

@ -150,8 +150,7 @@ runtime_addfinalizer(void *p, void (*f)(void*), const struct __go_func_type *ft)
tab = TAB(p); tab = TAB(p);
runtime_lock(tab); runtime_lock(tab);
if(f == nil) { if(f == nil) {
if(lookfintab(tab, p, true, nil)) lookfintab(tab, p, true, nil);
runtime_setblockspecial(p, false);
runtime_unlock(tab); runtime_unlock(tab);
return true; return true;
} }

View File

@ -1132,7 +1132,6 @@ runfinq(void* dummy __attribute__ ((unused)))
f = &fb->fin[i]; f = &fb->fin[i];
params[0] = &f->arg; params[0] = &f->arg;
runtime_setblockspecial(f->arg, false);
reflect_call(f->ft, (void*)f->fn, 0, 0, params, nil); reflect_call(f->ft, (void*)f->fn, 0, 0, params, nil);
f->fn = nil; f->fn = nil;
f->arg = nil; f->arg = nil;

View File

@ -61,15 +61,21 @@ ready(int64 now, Eface e)
void void
runtime_tsleep(int64 ns) runtime_tsleep(int64 ns)
{ {
G* g;
Timer t; Timer t;
if(ns <= 0) g = runtime_g();
if(ns <= 0) {
g->status = Grunning;
g->waitreason = nil;
return; return;
}
t.when = runtime_nanotime() + ns; t.when = runtime_nanotime() + ns;
t.period = 0; t.period = 0;
t.f = ready; t.f = ready;
t.arg.__object = runtime_g(); t.arg.__object = g;
addtimer(&t); addtimer(&t);
runtime_gosched(); runtime_gosched();
} }