diff options
Diffstat (limited to 'src/pkg')
70 files changed, 1427 insertions, 279 deletions
diff --git a/src/pkg/compress/flate/deflate.go b/src/pkg/compress/flate/deflate.go index 20408409c..e511b50fd 100644 --- a/src/pkg/compress/flate/deflate.go +++ b/src/pkg/compress/flate/deflate.go @@ -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 diff --git a/src/pkg/compress/flate/deflate_test.go b/src/pkg/compress/flate/deflate_test.go index 543c59505..f1e6db2ac 100644 --- a/src/pkg/compress/flate/deflate_test.go +++ b/src/pkg/compress/flate/deflate_test.go @@ -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 diff --git a/src/pkg/crypto/aes/const.go b/src/pkg/crypto/aes/const.go index f0b4eabf6..aee73a7c5 100644 --- a/src/pkg/crypto/aes/const.go +++ b/src/pkg/crypto/aes/const.go @@ -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⁸ + 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. // 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⁸ + 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). var powx = [16]byte{ diff --git a/src/pkg/crypto/ecdsa/ecdsa.go b/src/pkg/crypto/ecdsa/ecdsa.go index b28239b78..8508e3b4f 100644 --- a/src/pkg/crypto/ecdsa/ecdsa.go +++ b/src/pkg/crypto/ecdsa/ecdsa.go @@ -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)) } diff --git a/src/pkg/crypto/rsa/pkcs1v15.go b/src/pkg/crypto/rsa/pkcs1v15.go index 254f4a3da..a32236e47 100644 --- a/src/pkg/crypto/rsa/pkcs1v15.go +++ b/src/pkg/crypto/rsa/pkcs1v15.go @@ -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}, diff --git a/src/pkg/crypto/tls/handshake_messages.go b/src/pkg/crypto/tls/handshake_messages.go index e1517cc79..54c7a3e63 100644 --- a/src/pkg/crypto/tls/handshake_messages.go +++ b/src/pkg/crypto/tls/handshake_messages.go @@ -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:] } diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go index 8dae7e7fc..c4d85e67f 100644 --- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -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) diff --git a/src/pkg/debug/gosym/pclntab_test.go b/src/pkg/debug/gosym/pclntab_test.go index b2400bb3b..ade704335 100644 --- a/src/pkg/debug/gosym/pclntab_test.go +++ b/src/pkg/debug/gosym/pclntab_test.go @@ -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") diff --git a/src/pkg/encoding/base64/base64.go b/src/pkg/encoding/base64/base64.go index f8a51a4e7..0b842f066 100644 --- a/src/pkg/encoding/base64/base64.go +++ b/src/pkg/encoding/base64/base64.go @@ -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 } diff --git a/src/pkg/encoding/base64/base64_test.go b/src/pkg/encoding/base64/base64_test.go index 9c3537259..f9b863c36 100644 --- a/src/pkg/encoding/base64/base64_test.go +++ b/src/pkg/encoding/base64/base64_test.go @@ -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") + } +} diff --git a/src/pkg/encoding/gob/doc.go b/src/pkg/encoding/gob/doc.go index 96885f8de..821d9a3fe 100644 --- a/src/pkg/encoding/gob/doc.go +++ b/src/pkg/encoding/gob/doc.go @@ -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. diff --git a/src/pkg/encoding/json/decode.go b/src/pkg/encoding/json/decode.go index 110c6fd62..d61f88706 100644 --- a/src/pkg/encoding/json/decode.go +++ b/src/pkg/encoding/json/decode.go @@ -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 { diff --git a/src/pkg/encoding/json/decode_test.go b/src/pkg/encoding/json/decode_test.go index d758758d9..6fac22c4a 100644 --- a/src/pkg/encoding/json/decode_test.go +++ b/src/pkg/encoding/json/decode_test.go @@ -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) + } + } +} diff --git a/src/pkg/encoding/json/encode.go b/src/pkg/encoding/json/encode.go index 842672c39..b6e1cb16e 100644 --- a/src/pkg/encoding/json/encode.go +++ b/src/pkg/encoding/json/encode.go @@ -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 } diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go index f0842a18a..5444ad141 100644 --- a/src/pkg/flag/flag.go +++ b/src/pkg/flag/flag.go @@ -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) diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go index 9660370c2..a9b9c9d0c 100644 --- a/src/pkg/fmt/doc.go +++ b/src/pkg/fmt/doc.go @@ -136,7 +136,7 @@ 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. diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go index 7123fe58f..d2e75dc1c 100644 --- a/src/pkg/go/ast/ast.go +++ b/src/pkg/go/ast/ast.go @@ -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/src/pkg/go/ast/ast_test.go b/src/pkg/go/ast/ast_test.go new file mode 100644 index 000000000..1a6a283f2 --- /dev/null +++ b/src/pkg/go/ast/ast_test.go @@ -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) + } + } +} diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go index d749aef15..7a81d5030 100644 --- a/src/pkg/go/build/build.go +++ b/src/pkg/go/build/build.go @@ -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) { diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go index e362e13a7..20e505d97 100644 --- a/src/pkg/go/parser/parser.go +++ b/src/pkg/go/parser/parser.go @@ -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} } } diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go index 5e45acd00..1b7a41b1b 100644 --- a/src/pkg/go/parser/parser_test.go +++ b/src/pkg/go/parser/parser_test.go @@ -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") + } +} diff --git a/src/pkg/go/printer/nodes.go b/src/pkg/go/printer/nodes.go index 727d2a371..f13f9a5a8 100644 --- a/src/pkg/go/printer/nodes.go +++ b/src/pkg/go/printer/nodes.go @@ -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 diff --git a/src/pkg/log/log.go b/src/pkg/log/log.go index 1d7f209d1..d37e4375e 100644 --- a/src/pkg/log/log.go +++ b/src/pkg/log/log.go @@ -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. diff --git a/src/pkg/math/big/nat.go b/src/pkg/math/big/nat.go index 0bc6572b9..eaa6ff066 100644 --- a/src/pkg/math/big/nat.go +++ b/src/pkg/math/big/nat.go @@ -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 // diff --git a/src/pkg/math/big/nat_test.go b/src/pkg/math/big/nat_test.go index 7f3f76dc3..becde5d17 100644 --- a/src/pkg/math/big/nat_test.go +++ b/src/pkg/math/big/nat_test.go @@ -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) } diff --git a/src/pkg/mime/multipart/multipart.go b/src/pkg/mime/multipart/multipart.go index 6ace4be56..e9e337b92 100644 --- a/src/pkg/mime/multipart/multipart.go +++ b/src/pkg/mime/multipart/multipart.go @@ -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 } diff --git a/src/pkg/mime/multipart/multipart_test.go b/src/pkg/mime/multipart/multipart_test.go index ca7108d7a..cd65e177e 100644 --- a/src/pkg/mime/multipart/multipart_test.go +++ b/src/pkg/mime/multipart/multipart_test.go @@ -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 +} diff --git a/src/pkg/net/file.go b/src/pkg/net/file.go index c95d16d64..fc6c6fad8 100644 --- a/src/pkg/net/file.go +++ b/src/pkg/net/file.go @@ -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 { diff --git a/src/pkg/net/http/client.go b/src/pkg/net/http/client.go index 5d450258b..54564e098 100644 --- a/src/pkg/net/http/client.go +++ b/src/pkg/net/http/client.go @@ -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()) diff --git a/src/pkg/net/http/client_test.go b/src/pkg/net/http/client_test.go index e00b62e59..9b4261b9f 100644 --- a/src/pkg/net/http/client_test.go +++ b/src/pkg/net/http/client_test.go @@ -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 { diff --git a/src/pkg/net/http/proxy_test.go b/src/pkg/net/http/proxy_test.go index 9b320b3aa..5ecffafac 100644 --- a/src/pkg/net/http/proxy_test.go +++ b/src/pkg/net/http/proxy_test.go @@ -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) + } + } +} diff --git a/src/pkg/net/http/response.go b/src/pkg/net/http/response.go index b79022097..945ecd8a4 100644 --- a/src/pkg/net/http/response.go +++ b/src/pkg/net/http/response.go @@ -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) diff --git a/src/pkg/net/http/response_test.go b/src/pkg/net/http/response_test.go index 165ec3624..6eed4887d 100644 --- a/src/pkg/net/http/response_test.go +++ b/src/pkg/net/http/response_test.go @@ -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()) + } +} diff --git a/src/pkg/net/http/server.go b/src/pkg/net/http/server.go index 924ffd348..0572b4ae3 100644 --- a/src/pkg/net/http/server.go +++ b/src/pkg/net/http/server.go @@ -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") ) diff --git a/src/pkg/net/http/transfer.go b/src/pkg/net/http/transfer.go index 3c8fe7f5b..9e9d84172 100644 --- a/src/pkg/net/http/transfer.go +++ b/src/pkg/net/http/transfer.go @@ -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 diff --git a/src/pkg/net/http/transport.go b/src/pkg/net/http/transport.go index 024975946..6efe191eb 100644 --- a/src/pkg/net/http/transport.go +++ b/src/pkg/net/http/transport.go @@ -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. diff --git a/src/pkg/net/mail/message.go b/src/pkg/net/mail/message.go index 0917bbedf..b610ccf3f 100644 --- a/src/pkg/net/mail/message.go +++ b/src/pkg/net/mail/message.go @@ -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 { diff --git a/src/pkg/net/mail/message_test.go b/src/pkg/net/mail/message_test.go index 671ff2efa..fd17eb414 100644 --- a/src/pkg/net/mail/message_test.go +++ b/src/pkg/net/mail/message_test.go @@ -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{ diff --git a/src/pkg/net/url/url.go b/src/pkg/net/url/url.go index b6e79adc2..17bf0d3a3 100644 --- a/src/pkg/net/url/url.go +++ b/src/pkg/net/url/url.go @@ -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 diff --git a/src/pkg/net/url/url_test.go b/src/pkg/net/url/url_test.go index d8b253142..75e8abe4e 100644 --- a/src/pkg/net/url/url_test.go +++ b/src/pkg/net/url/url_test.go @@ -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{ diff --git a/src/pkg/os/exec/exec.go b/src/pkg/os/exec/exec.go index bbd04902b..9a8e18170 100644 --- a/src/pkg/os/exec/exec.go +++ b/src/pkg/os/exec/exec.go @@ -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 diff --git a/src/pkg/path/filepath/path.go b/src/pkg/path/filepath/path.go index a4e429bae..815021bd0 100644 --- a/src/pkg/path/filepath/path.go +++ b/src/pkg/path/filepath/path.go @@ -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 diff --git a/src/pkg/path/filepath/path_plan9.go b/src/pkg/path/filepath/path_plan9.go index cf028a75c..59a5812dd 100644 --- a/src/pkg/path/filepath/path_plan9.go +++ b/src/pkg/path/filepath/path_plan9.go @@ -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 "" } diff --git a/src/pkg/path/filepath/path_test.go b/src/pkg/path/filepath/path_test.go index b8766588c..070905fd3 100644 --- a/src/pkg/path/filepath/path_test.go +++ b/src/pkg/path/filepath/path_test.go @@ -869,3 +869,29 @@ func TestDriveLetterInEvalSymlinks(t *testing.T) { t.Errorf("Results of EvalSymlinks do not match: %q and %q", flp, fup) } } + +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) + } +} diff --git a/src/pkg/regexp/regexp.go b/src/pkg/regexp/regexp.go index 54c53776c..87e6b1c61 100644 --- a/src/pkg/regexp/regexp.go +++ b/src/pkg/regexp/regexp.go @@ -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. diff --git a/src/pkg/regexp/syntax/parse.go b/src/pkg/regexp/syntax/parse.go index 2df775025..4924e9453 100644 --- a/src/pkg/regexp/syntax/parse.go +++ b/src/pkg/regexp/syntax/parse.go @@ -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 diff --git a/src/pkg/regexp/syntax/parse_test.go b/src/pkg/regexp/syntax/parse_test.go index c6e63392c..81fd9dc01 100644 --- a/src/pkg/regexp/syntax/parse_test.go +++ b/src/pkg/regexp/syntax/parse_test.go @@ -441,10 +441,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}`, diff --git a/src/pkg/runtime/alg.c b/src/pkg/runtime/alg.c index e3c42916e..36973eba3 100644 --- a/src/pkg/runtime/alg.c +++ b/src/pkg/runtime/alg.c @@ -5,6 +5,9 @@ #include "runtime.h" #include "type.h" +#define M0 (sizeof(uintptr)==4 ? 2860486313UL : 33054211828000289ULL) +#define M1 (sizeof(uintptr)==4 ? 3267000013UL : 23344194077549503ULL) + /* * map and chan helpers for * dealing with unknown types @@ -16,19 +19,13 @@ runtime·memhash(uintptr *h, uintptr s, void *a) uintptr hash; b = a; - if(sizeof(hash) == 4) - hash = 2860486313U; - else - hash = 33054211828000289ULL; + hash = M0; while(s > 0) { - if(sizeof(hash) == 4) - hash = (hash ^ *b) * 3267000013UL; - else - hash = (hash ^ *b) * 23344194077549503ULL; + hash = (hash ^ *b) * M1; b++; s--; } - *h ^= hash; + *h = (*h ^ hash) * M1; } void @@ -252,7 +249,7 @@ runtime·f32hash(uintptr *h, uintptr s, void *a) hash = runtime·fastrand1(); // any kind of NaN else hash = *(uint32*)a; - *h ^= (*h ^ hash ^ 2860486313U) * 3267000013U; + *h = (*h ^ hash ^ M0) * M1; } void @@ -271,14 +268,11 @@ runtime·f64hash(uintptr *h, uintptr s, void *a) else { u = *(uint64*)a; if(sizeof(uintptr) == 4) - hash = ((uint32)(u>>32) * 3267000013UL) ^ (uint32)u; + hash = ((uint32)(u>>32) * M1) ^ (uint32)u; else hash = u; } - if(sizeof(uintptr) == 4) - *h = (*h ^ hash ^ 2860486313U) * 3267000013U; - else - *h = (*h ^ hash ^ 33054211828000289ULL) * 23344194077549503ULL; + *h = (*h ^ hash ^ M0) * M1; } void @@ -357,7 +351,7 @@ void runtime·interhash(uintptr *h, uintptr s, void *a) { USED(s); - *h ^= runtime·ifacehash(*(Iface*)a); + *h = (*h ^ runtime·ifacehash(*(Iface*)a)) * M1; } void @@ -391,7 +385,7 @@ void runtime·nilinterhash(uintptr *h, uintptr s, void *a) { USED(s); - *h ^= runtime·efacehash(*(Eface*)a); + *h = (*h ^ runtime·efacehash(*(Eface*)a)) * M1; } void diff --git a/src/pkg/runtime/extern.go b/src/pkg/runtime/extern.go index f9c5b8e3d..d93259d7b 100644 --- a/src/pkg/runtime/extern.go +++ b/src/pkg/runtime/extern.go @@ -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 diff --git a/src/pkg/runtime/hashmap.c b/src/pkg/runtime/hashmap.c index 1def96727..63ed4e2a3 100644 --- a/src/pkg/runtime/hashmap.c +++ b/src/pkg/runtime/hashmap.c @@ -6,17 +6,24 @@ #include "hashmap.h" #include "type.h" +/* Hmap flag values */ +#define IndirectVal (1<<0) /* storing pointers to values */ +#define IndirectKey (1<<1) /* storing pointers to keys */ +#define CanFreeTable (1<<2) /* okay to free subtables */ +#define CanFreeKey (1<<3) /* okay to free pointers to keys */ + struct Hmap { /* a hash table; initialize with hash_init() */ uint32 count; /* elements in table - must be first */ uint8 datasize; /* amount of data to store in entry */ - uint8 max_power; /* max power of 2 to create sub-tables */ - uint8 indirectval; /* storing pointers to values */ + uint8 flag; uint8 valoff; /* offset of value in key+value data block */ int32 changes; /* inc'ed whenever a subtable is created/grown */ uintptr hash0; /* hash seed */ struct hash_subtable *st; /* first-level table */ }; +#define MaxData 255 + struct hash_entry { hash_hash_t hash; /* hash value of data */ byte data[1]; /* user data has "datasize" bytes */ @@ -54,6 +61,7 @@ struct hash_subtable { ((struct hash_entry *) (((byte *) (base)) + (byte_offset))) #define HASH_MAX_PROBES 15 /* max entries to probe before rehashing */ +#define HASH_MAX_POWER 12 /* max power of 2 to create sub-tables */ /* return a hash layer with 2**power empty entries */ static struct hash_subtable * @@ -82,7 +90,7 @@ hash_subtable_new (Hmap *h, int32 power, int32 used) } static void -init_sizes (int64 hint, int32 *init_power, int32 *max_power) +init_sizes (int64 hint, int32 *init_power) { int32 log = 0; int32 i; @@ -98,24 +106,20 @@ init_sizes (int64 hint, int32 *init_power, int32 *max_power) } else { *init_power = 12; } - *max_power = 12; } static void hash_init (Hmap *h, int32 datasize, int64 hint) { int32 init_power; - int32 max_power; if(datasize < sizeof (void *)) datasize = sizeof (void *); datasize = runtime·rnd(datasize, sizeof (void *)); - init_sizes (hint, &init_power, &max_power); + init_sizes (hint, &init_power); h->datasize = datasize; - h->max_power = max_power; assert (h->datasize == datasize); - assert (h->max_power == max_power); - assert (sizeof (void *) <= h->datasize || h->max_power == 255); + assert (sizeof (void *) <= h->datasize); h->count = 0; h->changes = 0; h->st = hash_subtable_new (h, init_power, 0); @@ -253,7 +257,8 @@ hash_grow (MapType *t, Hmap *h, struct hash_subtable **pst, int32 flags) used++; } } - free (old_st); + if (h->flag & CanFreeTable) + free (old_st); } static int32 @@ -266,6 +271,7 @@ hash_lookup (MapType *t, Hmap *h, void *data, void **pres) hash_hash_t e_hash; struct hash_entry *e; struct hash_entry *end_e; + void *key; bool eq; hash = h->hash0; @@ -290,7 +296,10 @@ hash_lookup (MapType *t, Hmap *h, void *data, void **pres) e = HASH_OFFSET (e, elemsize); } while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) { - if (HASH_DATA_EQ (eq, t, h, data, e->data)) { /* a match */ + key = e->data; + if (h->flag & IndirectKey) + key = *(void**)e->data; + if (HASH_DATA_EQ (eq, t, h, data, key)) { /* a match */ *pres = e->data; return (1); } @@ -312,6 +321,7 @@ hash_remove (MapType *t, Hmap *h, void *data) struct hash_entry *e; struct hash_entry *end_e; bool eq; + void *key; hash = h->hash0; (*t->key->alg->hash) (&hash, t->key->size, data); @@ -335,8 +345,20 @@ hash_remove (MapType *t, Hmap *h, void *data) e = HASH_OFFSET (e, elemsize); } while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) { - if (HASH_DATA_EQ (eq, t, h, data, e->data)) { /* a match */ - if (h->indirectval) + key = e->data; + if (h->flag & IndirectKey) + key = *(void**)e->data; + if (HASH_DATA_EQ (eq, t, h, data, key)) { /* a match */ + // Free key if indirect, but only if reflect can't be + // holding a pointer to it. Deletions are rare, + // indirect (large) keys are rare, reflect on maps + // is rare. So in the rare, rare, rare case of deleting + // an indirect key from a map that has been reflected on, + // we leave the key for garbage collection instead of + // freeing it here. + if (h->flag & CanFreeKey) + free (key); + if (h->flag & IndirectVal) free (*(void**)((byte*)e->data + h->valoff)); hash_remove_n (st, e, 1); h->count--; @@ -385,8 +407,12 @@ hash_insert_internal (MapType *t, struct hash_subtable **pst, int32 flags, hash_ struct hash_entry *ins_e = e; int32 ins_i = i; hash_hash_t ins_e_hash; + void *key; while (ins_e != end_e && ((e_hash = ins_e->hash) ^ hash) < HASH_SUBHASH) { - if (HASH_DATA_EQ (eq, t, h, data, ins_e->data)) { /* a match */ + key = ins_e->data; + if (h->flag & IndirectKey) + key = *(void**)key; + if (HASH_DATA_EQ (eq, t, h, data, key)) { /* a match */ *pres = ins_e->data; return (1); } @@ -423,7 +449,7 @@ hash_insert_internal (MapType *t, struct hash_subtable **pst, int32 flags, hash_ return (0); } h->changes++; - if (st->power < h->max_power) { + if (st->power < HASH_MAX_POWER) { hash_grow (t, h, pst, flags); } else { hash_conv (t, h, st, flags, hash, start_e); @@ -606,7 +632,7 @@ hash_iter_init (MapType *t, Hmap *h, struct hash_iter *it) } static void -clean_st (struct hash_subtable *st, int32 *slots, int32 *used) +clean_st (Hmap *h, struct hash_subtable *st, int32 *slots, int32 *used) { int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); struct hash_entry *e = st->entry; @@ -617,13 +643,14 @@ clean_st (struct hash_subtable *st, int32 *slots, int32 *used) while (e <= last) { hash_hash_t hash = e->hash; if ((hash & HASH_MASK) == HASH_SUBHASH) { - clean_st (*(struct hash_subtable **)e->data, slots, used); + clean_st (h, *(struct hash_subtable **)e->data, slots, used); } else { lused += (hash != HASH_NIL); } e = HASH_OFFSET (e, elemsize); } - free (st); + if (h->flag & CanFreeTable) + free (st); *slots += lslots; *used += lused; } @@ -634,7 +661,7 @@ hash_destroy (Hmap *h) int32 slots = 0; int32 used = 0; - clean_st (h->st, &slots, &used); + clean_st (h, h->st, &slots, &used); free (h); } @@ -677,20 +704,23 @@ hash_visit (Hmap *h, void (*data_visit) (void *arg, int32 level, void *data), vo /// interfaces to go runtime // -// hash requires < 256 bytes of data (key+value) stored inline. -// Only basic types can be key - biggest is complex128 (16 bytes). -// Leave some room to grow, just in case. -enum { - MaxValsize = 256 - 64 -}; +static void** +hash_valptr(Hmap *h, void *p) +{ + p = (byte*)p + h->valoff; + if(h->flag & IndirectVal) + p = *(void**)p; + return p; +} + static void** -hash_indirect(Hmap *h, void *p) +hash_keyptr(Hmap *h, void *p) { - if(h->indirectval) + if(h->flag & IndirectKey) p = *(void**)p; return p; -} +} static int32 debug = 0; @@ -699,8 +729,8 @@ Hmap* runtime·makemap_c(MapType *typ, int64 hint) { Hmap *h; - int32 valsize_in_hash; Type *key, *val; + uintptr ksize, vsize; key = typ->key; val = typ->elem; @@ -712,19 +742,29 @@ runtime·makemap_c(MapType *typ, int64 hint) runtime·throw("runtime.makemap: unsupported map key type"); h = runtime·mal(sizeof(*h)); + h->flag |= CanFreeTable; /* until reflect gets involved, free is okay */ + + ksize = runtime·rnd(key->size, sizeof(void*)); + vsize = runtime·rnd(val->size, sizeof(void*)); + if(ksize > MaxData || vsize > MaxData || ksize+vsize > MaxData) { + // Either key is too big, or value is, or combined they are. + // Prefer to keep the key if possible, because we look at + // keys more often than values. + if(ksize > MaxData - sizeof(void*)) { + // No choice but to indirect the key. + h->flag |= IndirectKey; + h->flag |= CanFreeKey; /* until reflect gets involved, free is okay */ + ksize = sizeof(void*); + } + if(vsize > MaxData - ksize) { + // Have to indirect the value. + h->flag |= IndirectVal; + vsize = sizeof(void*); + } + } - valsize_in_hash = val->size; - if (val->size > MaxValsize) { - h->indirectval = 1; - valsize_in_hash = sizeof(void*); - } - - // Align value inside data so that mark-sweep gc can find it. - h->valoff = key->size; - if(valsize_in_hash >= sizeof(void*)) - h->valoff = runtime·rnd(key->size, sizeof(void*)); - - hash_init(h, h->valoff+valsize_in_hash, hint); + h->valoff = ksize; + hash_init(h, ksize+vsize, hint); // these calculations are compiler dependent. // figure out offsets of map call arguments. @@ -773,7 +813,7 @@ runtime·mapaccess(MapType *t, Hmap *h, byte *ak, byte *av, bool *pres) res = nil; if(hash_lookup(t, h, ak, (void**)&res)) { *pres = true; - elem->alg->copy(elem->size, av, hash_indirect(h, res+h->valoff)); + elem->alg->copy(elem->size, av, hash_valptr(h, res)); } else { *pres = false; elem->alg->copy(elem->size, av, nil); @@ -877,10 +917,14 @@ runtime·mapassign(MapType *t, Hmap *h, byte *ak, byte *av) res = nil; hit = hash_insert(t, h, ak, (void**)&res); - if(!hit && h->indirectval) - *(void**)(res+h->valoff) = runtime·mal(t->elem->size); - t->key->alg->copy(t->key->size, res, ak); - t->elem->alg->copy(t->elem->size, hash_indirect(h, res+h->valoff), av); + if(!hit) { + if(h->flag & IndirectKey) + *(void**)res = runtime·mal(t->key->size); + if(h->flag & IndirectVal) + *(void**)(res+h->valoff) = runtime·mal(t->elem->size); + } + t->key->alg->copy(t->key->size, hash_keyptr(h, res), ak); + t->elem->alg->copy(t->elem->size, hash_valptr(h, res), av); if(debug) { runtime·prints("mapassign: map="); @@ -985,6 +1029,22 @@ runtime·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it) void reflect·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it) { + uint8 flag; + + if(h != nil && t->key->size > sizeof(void*)) { + // reflect·mapiterkey returns pointers to key data, + // and reflect holds them, so we cannot free key data + // eagerly anymore. Updating h->flag now is racy, + // but it's okay because this is the only possible store + // after creation. + flag = h->flag; + if(flag & IndirectKey) + flag &= ~CanFreeKey; + else + flag &= ~CanFreeTable; + h->flag = flag; + } + it = runtime·mal(sizeof *it); FLUSH(&it); runtime·mapiterinit(t, h, it); @@ -1032,7 +1092,7 @@ runtime·mapiter1(struct hash_iter *it, ...) runtime·throw("runtime.mapiter1: key:val nil pointer"); key = it->t->key; - key->alg->copy(key->size, ak, res); + key->alg->copy(key->size, ak, hash_keyptr(h, res)); if(debug) { runtime·prints("mapiter2: iter="); @@ -1053,7 +1113,7 @@ runtime·mapiterkey(struct hash_iter *it, void *ak) if(res == nil) return false; key = it->t->key; - key->alg->copy(key->size, ak, res); + key->alg->copy(key->size, ak, hash_keyptr(it->h, res)); return true; } @@ -1076,6 +1136,7 @@ reflect·mapiterkey(struct hash_iter *it, uintptr key, bool ok) } else { tkey = it->t->key; key = 0; + res = (byte*)hash_keyptr(it->h, res); if(tkey->size <= sizeof(key)) tkey->alg->copy(tkey->size, (byte*)&key, res); else @@ -1117,8 +1178,8 @@ runtime·mapiter2(struct hash_iter *it, ...) runtime·throw("runtime.mapiter2: key:val nil pointer"); h = it->h; - t->key->alg->copy(t->key->size, ak, res); - t->elem->alg->copy(t->elem->size, av, hash_indirect(h, res+h->valoff)); + t->key->alg->copy(t->key->size, ak, hash_keyptr(h, res)); + t->elem->alg->copy(t->elem->size, av, hash_valptr(h, res)); if(debug) { runtime·prints("mapiter2: iter="); diff --git a/src/pkg/runtime/malloc.goc b/src/pkg/runtime/malloc.goc index fbdd6bb02..9ae3a9d61 100644 --- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -154,6 +154,7 @@ runtime·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); diff --git a/src/pkg/runtime/mfinal.c b/src/pkg/runtime/mfinal.c index c6f2b5421..1fa5ea401 100644 --- a/src/pkg/runtime/mfinal.c +++ b/src/pkg/runtime/mfinal.c @@ -150,8 +150,7 @@ runtime·addfinalizer(void *p, void (*f)(void*), int32 nret) 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; } diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c index e043864c1..e8fb266f4 100644 --- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -1066,7 +1066,6 @@ runfinq(void) framecap = framesz; } *(void**)frame = f->arg; - runtime·setblockspecial(f->arg, false); reflect·call((byte*)f->fn, frame, sizeof(uintptr) + f->nret); f->fn = nil; f->arg = nil; diff --git a/src/pkg/runtime/softfloat_arm.c b/src/pkg/runtime/softfloat_arm.c index fbe0b0413..bd73cb15b 100644 --- a/src/pkg/runtime/softfloat_arm.c +++ b/src/pkg/runtime/softfloat_arm.c @@ -9,10 +9,10 @@ #include "runtime.h" #define CPSR 14 -#define FLAGS_N (1 << 31) -#define FLAGS_Z (1 << 30) -#define FLAGS_C (1 << 29) -#define FLAGS_V (1 << 28) +#define FLAGS_N (1U << 31) +#define FLAGS_Z (1U << 30) +#define FLAGS_C (1U << 29) +#define FLAGS_V (1U << 28) void runtime·abort(void); void math·sqrtC(uint64, uint64*); @@ -86,12 +86,24 @@ fstatus(bool nan, int32 cmp) return FLAGS_C; } +// conditions array record the required CPSR cond field for the +// first 5 pairs of conditional execution opcodes +// higher 4 bits are must set, lower 4 bits are must clear +static const uint8 conditions[10/2] = { + [0/2] = (FLAGS_Z >> 24) | 0, // 0: EQ (Z set), 1: NE (Z clear) + [2/2] = (FLAGS_C >> 24) | 0, // 2: CS/HS (C set), 3: CC/LO (C clear) + [4/2] = (FLAGS_N >> 24) | 0, // 4: MI (N set), 5: PL (N clear) + [6/2] = (FLAGS_V >> 24) | 0, // 6: VS (V set), 7: VC (V clear) + [8/2] = (FLAGS_C >> 24) | + (FLAGS_Z >> 28), // 8: HI (C set and Z clear), 9: LS (C clear and Z set) +}; + // returns number of words that the fp instruction // is occupying, 0 if next instruction isn't float. static uint32 stepflt(uint32 *pc, uint32 *regs) { - uint32 i, regd, regm, regn; + uint32 i, opc, regd, regm, regn, cpsr; int32 delta; uint32 *addr; uint64 uval; @@ -102,8 +114,49 @@ stepflt(uint32 *pc, uint32 *regs) i = *pc; if(trace) - runtime·printf("stepflt %p %x\n", pc, i); + runtime·printf("stepflt %p %x (cpsr %x)\n", pc, i, regs[CPSR] >> 28); + + opc = i >> 28; + if(opc == 14) // common case first + goto execute; + cpsr = regs[CPSR] >> 28; + switch(opc) { + case 0: case 1: case 2: case 3: case 4: + case 5: case 6: case 7: case 8: case 9: + if(((cpsr & (conditions[opc/2] >> 4)) == (conditions[opc/2] >> 4)) && + ((cpsr & (conditions[opc/2] & 0xf)) == 0)) { + if(opc & 1) return 1; + } else { + if(!(opc & 1)) return 1; + } + break; + case 10: // GE (N == V) + case 11: // LT (N != V) + if((cpsr & (FLAGS_N >> 28)) == (cpsr & (FLAGS_V >> 28))) { + if(opc & 1) return 1; + } else { + if(!(opc & 1)) return 1; + } + break; + case 12: // GT (N == V and Z == 0) + case 13: // LE (N != V or Z == 1) + if((cpsr & (FLAGS_N >> 28)) == (cpsr & (FLAGS_V >> 28)) && + (cpsr & (FLAGS_Z >> 28)) == 0) { + if(opc & 1) return 1; + } else { + if(!(opc & 1)) return 1; + } + break; + case 14: // AL + break; + case 15: // shouldn't happen + return 0; + } + if(trace) + runtime·printf("conditional %x (cpsr %x) pass\n", opc, cpsr); + i = (0xeU << 28) | (i & 0xfffffff); +execute: // special cases if((i&0xfffff000) == 0xe59fb000) { // load r11 from pc-relative address. diff --git a/src/pkg/runtime/time.goc b/src/pkg/runtime/time.goc index a6b835247..b18902f00 100644 --- a/src/pkg/runtime/time.goc +++ b/src/pkg/runtime/time.goc @@ -61,8 +61,11 @@ runtime·tsleep(int64 ns) { Timer t; - if(ns <= 0) + if(ns <= 0) { + g->status = Grunning; + g->waitreason = nil; return; + } t.when = runtime·nanotime() + ns; t.period = 0; diff --git a/src/pkg/runtime/vlrt_arm.c b/src/pkg/runtime/vlrt_arm.c index 50f33710b..ab8050177 100644 --- a/src/pkg/runtime/vlrt_arm.c +++ b/src/pkg/runtime/vlrt_arm.c @@ -197,12 +197,14 @@ void runtime·int64tofloat64(Vlong y, double d) { d = _v2d(y); + USED(&d); // FLUSH } void runtime·uint64tofloat64(Vlong y, double d) { d = _ul2d(y.hi)*4294967296. + _ul2d(y.lo); + USED(&d); // FLUSH } static void diff --git a/src/pkg/strconv/itoa.go b/src/pkg/strconv/itoa.go index ca40dd7ef..67f17d866 100644 --- a/src/pkg/strconv/itoa.go +++ b/src/pkg/strconv/itoa.go @@ -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 diff --git a/src/pkg/strings/example_test.go b/src/pkg/strings/example_test.go index 114171072..733caf5f2 100644 --- a/src/pkg/strings/example_test.go +++ b/src/pkg/strings/example_test.go @@ -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 diff --git a/src/pkg/syscall/syscall_linux_arm.go b/src/pkg/syscall/syscall_linux_arm.go index 48b5d31d7..2f2907945 100644 --- a/src/pkg/syscall/syscall_linux_arm.go +++ b/src/pkg/syscall/syscall_linux_arm.go @@ -41,26 +41,28 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) //sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error) //sys sendmsg(s int, msg *Msghdr, flags int) (err error) -//sys Chown(path string, uid int, gid int) (err error) -//sys Fchown(fd int, uid int, gid int) (err error) +// 64-bit file system and 32-bit uid calls +// (16-bit uid calls are not always supported in newer kernels) +//sys Chown(path string, uid int, gid int) (err error) = SYS_CHOWN32 +//sys Fchown(fd int, uid int, gid int) (err error) = SYS_FCHOWN32 //sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64 //sys Fstatfs(fd int, buf *Statfs_t) (err error) = SYS_FSTATFS64 -//sysnb Getegid() (egid int) -//sysnb Geteuid() (euid int) -//sysnb Getgid() (gid int) -//sysnb Getuid() (uid int) -//sys Lchown(path string, uid int, gid int) (err error) +//sysnb Getegid() (egid int) = SYS_GETEGID32 +//sysnb Geteuid() (euid int) = SYS_GETEUID32 +//sysnb Getgid() (gid int) = SYS_GETGID32 +//sysnb Getuid() (uid int) = SYS_GETUID32 +//sys Lchown(path string, uid int, gid int) (err error) = SYS_LCHOWN32 //sys Listen(s int, n int) (err error) //sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64 //sys Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64 //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT -//sys Setfsgid(gid int) (err error) -//sys Setfsuid(uid int) (err error) -//sysnb Setgid(gid int) (err error) -//sysnb Setregid(rgid int, egid int) (err error) -//sysnb Setresgid(rgid int, egid int, sgid int) (err error) -//sysnb Setresuid(ruid int, euid int, suid int) (err error) -//sysnb Setreuid(ruid int, euid int) (err error) +//sys Setfsgid(gid int) (err error) = SYS_SETFSGID32 +//sys Setfsuid(uid int) (err error) = SYS_SETFSUID32 +//sysnb Setgid(gid int) (err error) = SYS_SETGID32 +//sysnb Setregid(rgid int, egid int) (err error) = SYS_SETREGID32 +//sysnb Setresgid(rgid int, egid int, sgid int) (err error) = SYS_SETRESGID32 +//sysnb Setresuid(ruid int, euid int, suid int) (err error) = SYS_SETRESUID32 +//sysnb Setreuid(ruid int, euid int) (err error) = SYS_SETREUID32 //sys Shutdown(fd int, how int) (err error) //sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error) //sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64 diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go index 47209da8f..6b544f1d4 100644 --- a/src/pkg/syscall/syscall_windows.go +++ b/src/pkg/syscall/syscall_windows.go @@ -90,7 +90,9 @@ func (e Errno) Error() string { b := make([]uint16, 300) n, err := FormatMessage(flags, 0, uint32(e), langid(LANG_ENGLISH, SUBLANG_ENGLISH_US), b, nil) if err != nil { - return "error " + itoa(int(e)) + " (FormatMessage failed with err=" + itoa(int(err.(Errno))) + ")" + // TODO(brainman): Call FormatMessage again asking for "native" error message. + // http://code.google.com/p/go/issues/detail?id=3376 must be resolved first. + return "winapi error #" + itoa(int(e)) } // trim terminating \r and \n for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- { @@ -127,8 +129,8 @@ func NewCallback(fn interface{}) uintptr //sys SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, err error) [failretval==0xffffffff] //sys CloseHandle(handle Handle) (err error) //sys GetStdHandle(stdhandle int) (handle Handle, err error) [failretval==InvalidHandle] -//sys FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) [failretval==InvalidHandle] = FindFirstFileW -//sys FindNextFile(handle Handle, data *Win32finddata) (err error) = FindNextFileW +//sys findFirstFile1(name *uint16, data *win32finddata1) (handle Handle, err error) [failretval==InvalidHandle] = FindFirstFileW +//sys findNextFile1(handle Handle, data *win32finddata1) (err error) = FindNextFileW //sys FindClose(handle Handle) (err error) //sys GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (err error) //sys GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, err error) = GetCurrentDirectoryW @@ -199,6 +201,7 @@ func NewCallback(fn interface{}) uintptr //sys RegQueryInfoKey(key Handle, class *uint16, classLen *uint32, reserved *uint32, subkeysLen *uint32, maxSubkeyLen *uint32, maxClassLen *uint32, valuesLen *uint32, maxValueNameLen *uint32, maxValueLen *uint32, saLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegQueryInfoKeyW //sys RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegEnumKeyExW //sys RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegQueryValueExW +//sys getCurrentProcessId() (pid uint32) = kernel32.getCurrentProcessId // syscall interface implementation for other packages @@ -681,9 +684,35 @@ func SetsockoptIPMreq(fd Handle, level, opt int, mreq *IPMreq) (err error) { } func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (err error) { return EWINDOWS } -// TODO(brainman): fix all needed for os +func Getpid() (pid int) { return int(getCurrentProcessId()) } + +func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) { + // NOTE(rsc): The Win32finddata struct is wrong for the system call: + // the two paths are each one uint16 short. Use the correct struct, + // a win32finddata1, and then copy the results out. + // There is no loss of expressivity here, because the final + // uint16, if it is used, is supposed to be a NUL, and Go doesn't need that. + // For Go 1.1, we might avoid the allocation of win32finddata1 here + // by adding a final Bug [2]uint16 field to the struct and then + // adjusting the fields in the result directly. + var data1 win32finddata1 + handle, err = findFirstFile1(name, &data1) + if err == nil { + copyFindData(data, &data1) + } + return +} -func Getpid() (pid int) { return -1 } +func FindNextFile(handle Handle, data *Win32finddata) (err error) { + var data1 win32finddata1 + err = findNextFile1(handle, &data1) + if err == nil { + copyFindData(data, &data1) + } + return +} + +// TODO(brainman): fix all needed for os func Getppid() (ppid int) { return -1 } func Fchdir(fd Handle) (err error) { return EWINDOWS } diff --git a/src/pkg/syscall/syscall_windows_test.go b/src/pkg/syscall/syscall_windows_test.go new file mode 100644 index 000000000..79cd8f869 --- /dev/null +++ b/src/pkg/syscall/syscall_windows_test.go @@ -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 syscall_test + +import ( + "io/ioutil" + "os" + "path/filepath" + "syscall" + "testing" +) + +func TestWin32finddata(t *testing.T) { + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("failed to create temp directory: %v", err) + } + defer os.RemoveAll(dir) + + path := filepath.Join(dir, "long_name.and_extension") + f, err := os.Create(path) + if err != nil { + t.Fatalf("failed to create %v: %v", path, err) + } + f.Close() + + type X struct { + fd syscall.Win32finddata + got byte + pad [10]byte // to protect ourselves + + } + var want byte = 2 // it is unlikely to have this character in the filename + x := X{got: want} + + h, err := syscall.FindFirstFile(syscall.StringToUTF16Ptr(path), &(x.fd)) + if err != nil { + t.Fatalf("FindFirstFile failed: %v", err) + } + err = syscall.FindClose(h) + if err != nil { + t.Fatalf("FindClose failed: %v", err) + } + + if x.got != want { + t.Fatalf("memory corruption: want=%d got=%d", want, x.got) + } +} diff --git a/src/pkg/syscall/zsyscall_linux_arm.go b/src/pkg/syscall/zsyscall_linux_arm.go index 1e86d3b7f..b1a59a69e 100644 --- a/src/pkg/syscall/zsyscall_linux_arm.go +++ b/src/pkg/syscall/zsyscall_linux_arm.go @@ -1203,7 +1203,7 @@ func sendmsg(s int, msg *Msghdr, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + _, _, e1 := Syscall(SYS_CHOWN32, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -1213,7 +1213,7 @@ func Chown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchown(fd int, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) + _, _, e1 := Syscall(SYS_FCHOWN32, uintptr(fd), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -1243,7 +1243,7 @@ func Fstatfs(fd int, buf *Statfs_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Getegid() (egid int) { - r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0) + r0, _, _ := RawSyscall(SYS_GETEGID32, 0, 0, 0) egid = int(r0) return } @@ -1251,7 +1251,7 @@ func Getegid() (egid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Geteuid() (euid int) { - r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0) + r0, _, _ := RawSyscall(SYS_GETEUID32, 0, 0, 0) euid = int(r0) return } @@ -1259,7 +1259,7 @@ func Geteuid() (euid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Getgid() (gid int) { - r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0) + r0, _, _ := RawSyscall(SYS_GETGID32, 0, 0, 0) gid = int(r0) return } @@ -1267,7 +1267,7 @@ func Getgid() (gid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Getuid() (uid int) { - r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0) + r0, _, _ := RawSyscall(SYS_GETUID32, 0, 0, 0) uid = int(r0) return } @@ -1275,7 +1275,7 @@ func Getuid() (uid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lchown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + _, _, e1 := Syscall(SYS_LCHOWN32, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -1327,7 +1327,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Setfsgid(gid int) (err error) { - _, _, e1 := Syscall(SYS_SETFSGID, uintptr(gid), 0, 0) + _, _, e1 := Syscall(SYS_SETFSGID32, uintptr(gid), 0, 0) if e1 != 0 { err = e1 } @@ -1337,7 +1337,7 @@ func Setfsgid(gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Setfsuid(uid int) (err error) { - _, _, e1 := Syscall(SYS_SETFSUID, uintptr(uid), 0, 0) + _, _, e1 := Syscall(SYS_SETFSUID32, uintptr(uid), 0, 0) if e1 != 0 { err = e1 } @@ -1347,7 +1347,7 @@ func Setfsuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Setgid(gid int) (err error) { - _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0) + _, _, e1 := RawSyscall(SYS_SETGID32, uintptr(gid), 0, 0) if e1 != 0 { err = e1 } @@ -1357,7 +1357,7 @@ func Setgid(gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Setregid(rgid int, egid int) (err error) { - _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0) + _, _, e1 := RawSyscall(SYS_SETREGID32, uintptr(rgid), uintptr(egid), 0) if e1 != 0 { err = e1 } @@ -1367,7 +1367,7 @@ func Setregid(rgid int, egid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Setresgid(rgid int, egid int, sgid int) (err error) { - _, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid)) + _, _, e1 := RawSyscall(SYS_SETRESGID32, uintptr(rgid), uintptr(egid), uintptr(sgid)) if e1 != 0 { err = e1 } @@ -1377,7 +1377,7 @@ func Setresgid(rgid int, egid int, sgid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Setresuid(ruid int, euid int, suid int) (err error) { - _, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid)) + _, _, e1 := RawSyscall(SYS_SETRESUID32, uintptr(ruid), uintptr(euid), uintptr(suid)) if e1 != 0 { err = e1 } @@ -1387,7 +1387,7 @@ func Setresuid(ruid int, euid int, suid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Setreuid(ruid int, euid int) (err error) { - _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0) + _, _, e1 := RawSyscall(SYS_SETREUID32, uintptr(ruid), uintptr(euid), 0) if e1 != 0 { err = e1 } diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go index 8e6afcf0a..3022ce2e9 100644 --- a/src/pkg/syscall/zsyscall_windows_386.go +++ b/src/pkg/syscall/zsyscall_windows_386.go @@ -142,6 +142,7 @@ var ( procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken") procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation") procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW") + procgetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId") ) func GetLastError() (lasterr error) { @@ -308,7 +309,7 @@ func GetStdHandle(stdhandle int) (handle Handle, err error) { return } -func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) { +func findFirstFile1(name *uint16, data *win32finddata1) (handle Handle, err error) { r0, _, e1 := Syscall(procFindFirstFileW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(data)), 0) handle = Handle(r0) if handle == InvalidHandle { @@ -321,7 +322,7 @@ func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) return } -func FindNextFile(handle Handle, data *Win32finddata) (err error) { +func findNextFile1(handle Handle, data *win32finddata1) (err error) { r1, _, e1 := Syscall(procFindNextFileW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) if int(r1) == 0 { if e1 != 0 { @@ -1600,3 +1601,9 @@ func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) { } return } + +func getCurrentProcessId() (pid uint32) { + r0, _, _ := Syscall(procgetCurrentProcessId.Addr(), 0, 0, 0, 0) + pid = uint32(r0) + return +} diff --git a/src/pkg/syscall/zsyscall_windows_amd64.go b/src/pkg/syscall/zsyscall_windows_amd64.go index c6ff6fe02..ea0000c32 100644 --- a/src/pkg/syscall/zsyscall_windows_amd64.go +++ b/src/pkg/syscall/zsyscall_windows_amd64.go @@ -142,6 +142,7 @@ var ( procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken") procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation") procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW") + procgetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId") ) func GetLastError() (lasterr error) { @@ -308,7 +309,7 @@ func GetStdHandle(stdhandle int) (handle Handle, err error) { return } -func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) { +func findFirstFile1(name *uint16, data *win32finddata1) (handle Handle, err error) { r0, _, e1 := Syscall(procFindFirstFileW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(data)), 0) handle = Handle(r0) if handle == InvalidHandle { @@ -321,7 +322,7 @@ func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) return } -func FindNextFile(handle Handle, data *Win32finddata) (err error) { +func findNextFile1(handle Handle, data *win32finddata1) (err error) { r1, _, e1 := Syscall(procFindNextFileW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) if int(r1) == 0 { if e1 != 0 { @@ -1600,3 +1601,9 @@ func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) { } return } + +func getCurrentProcessId() (pid uint32) { + r0, _, _ := Syscall(procgetCurrentProcessId.Addr(), 0, 0, 0, 0) + pid = uint32(r0) + return +} diff --git a/src/pkg/syscall/ztypes_windows.go b/src/pkg/syscall/ztypes_windows.go index 54168bb98..b2dc12e1a 100644 --- a/src/pkg/syscall/ztypes_windows.go +++ b/src/pkg/syscall/ztypes_windows.go @@ -347,6 +347,38 @@ type Win32finddata struct { AlternateFileName [13]uint16 } +// This is the actual system call structure. +// Win32finddata is what we committed to in Go 1. +type win32finddata1 struct { + FileAttributes uint32 + CreationTime Filetime + LastAccessTime Filetime + LastWriteTime Filetime + FileSizeHigh uint32 + FileSizeLow uint32 + Reserved0 uint32 + Reserved1 uint32 + FileName [MAX_PATH]uint16 + AlternateFileName [14]uint16 +} + +func copyFindData(dst *Win32finddata, src *win32finddata1) { + dst.FileAttributes = src.FileAttributes + dst.CreationTime = src.CreationTime + dst.LastAccessTime = src.LastAccessTime + dst.LastWriteTime = src.LastWriteTime + dst.FileSizeHigh = src.FileSizeHigh + dst.FileSizeLow = src.FileSizeLow + dst.Reserved0 = src.Reserved0 + dst.Reserved1 = src.Reserved1 + + // The src is 1 element shorter than dst. Zero that last one. + copy(dst.FileName[:], src.FileName[:]) + dst.FileName[len(dst.FileName)-1] = 0 + copy(dst.AlternateFileName[:], src.AlternateFileName[:]) + src.AlternateFileName[len(dst.AlternateFileName)-1] = 0 +} + type ByHandleFileInformation struct { FileAttributes uint32 CreationTime Filetime diff --git a/src/pkg/text/template/exec.go b/src/pkg/text/template/exec.go index feb434a3b..aba21ce28 100644 --- a/src/pkg/text/template/exec.go +++ b/src/pkg/text/template/exec.go @@ -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. diff --git a/src/pkg/text/template/exec_test.go b/src/pkg/text/template/exec_test.go index 37d25f470..f4ae50f0e 100644 --- a/src/pkg/text/template/exec_test.go +++ b/src/pkg/text/template/exec_test.go @@ -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 diff --git a/src/pkg/time/sleep_test.go b/src/pkg/time/sleep_test.go index 526d58d75..e05773df6 100644 --- a/src/pkg/time/sleep_test.go +++ b/src/pkg/time/sleep_test.go @@ -223,3 +223,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 +} diff --git a/src/pkg/unicode/maketables.go b/src/pkg/unicode/maketables.go index 16bc83cea..fcd14fc73 100644 --- a/src/pkg/unicode/maketables.go +++ b/src/pkg/unicode/maketables.go @@ -488,7 +488,7 @@ func printCategories() { func(code rune) bool { return chars[code].category == name }) } decl.Sort() - fmt.Println("// The following variables are of type *RangeTable:") + fmt.Println("// These variables have type *RangeTable.") fmt.Println("var (") for _, d := range decl { fmt.Print(d) @@ -771,7 +771,7 @@ func printScriptOrProperty(doProps bool) { fmt.Print("}\n\n") } decl.Sort() - fmt.Println("// The following variables are of type *RangeTable:") + fmt.Println("// These variables have type *RangeTable.") fmt.Println("var (") for _, d := range decl { fmt.Print(d) diff --git a/src/pkg/unicode/tables.go b/src/pkg/unicode/tables.go index 5009e6b98..ebd169b09 100644 --- a/src/pkg/unicode/tables.go +++ b/src/pkg/unicode/tables.go @@ -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. |