libgo: Update to Go 1.0.2 release.

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

View File

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

View File

@ -32,6 +32,7 @@ const (
hashSize = 1 << hashBits
hashMask = (1 << hashBits) - 1
hashShift = (hashBits + minMatchLength - 1) / minMatchLength
maxHashOffset = 1 << 24
skipNever = math.MaxInt32
)
@ -106,6 +107,25 @@ func (d *compressor) fillDeflate(b []byte) int {
d.blockStart = math.MaxInt32
}
d.hashOffset += windowSize
if d.hashOffset > maxHashOffset {
delta := d.hashOffset - 1
d.hashOffset -= delta
d.chainHead -= delta
for i, v := range d.hashPrev {
if v > delta {
d.hashPrev[i] -= delta
} else {
d.hashPrev[i] = 0
}
}
for i, v := range d.hashHead {
if v > delta {
d.hashHead[i] -= delta
} else {
d.hashHead[i] = 0
}
}
}
}
n := copy(d.window[d.windowEnd:], b)
d.windowEnd += n

View File

@ -94,6 +94,50 @@ func TestDeflate(t *testing.T) {
}
}
// A sparseReader returns a stream consisting of 0s followed by 1<<16 1s.
// This tests missing hash references in a very large input.
type sparseReader struct {
l int64
cur int64
}
func (r *sparseReader) Read(b []byte) (n int, err error) {
if r.cur >= r.l {
return 0, io.EOF
}
n = len(b)
cur := r.cur + int64(n)
if cur > r.l {
n -= int(cur - r.l)
cur = r.l
}
for i := range b[0:n] {
if r.cur+int64(i) >= r.l-1<<16 {
b[i] = 1
} else {
b[i] = 0
}
}
r.cur = cur
return
}
func TestVeryLongSparseChunk(t *testing.T) {
if testing.Short() {
t.Logf("skipping sparse chunk during short test")
return
}
w, err := NewWriter(ioutil.Discard, 1)
if err != nil {
t.Errorf("NewWriter: %v", err)
return
}
if _, err = io.Copy(w, &sparseReader{l: 23E8}); err != nil {
t.Errorf("Compress failed: %v", err)
return
}
}
type syncBuffer struct {
buf bytes.Buffer
mu sync.RWMutex

View File

@ -11,11 +11,11 @@ package aes
// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf
// AES is based on the mathematical behavior of binary polynomials
// (polynomials over GF(2)) modulo the irreducible polynomial x⁸ + 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{

View File

@ -66,7 +66,9 @@ func GenerateKey(c elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error)
// hashToInt converts a hash value to an integer. There is some disagreement
// 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))
}

View File

@ -151,6 +151,7 @@ func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) {
var hashPrefixes = map[crypto.Hash][]byte{
crypto.MD5: {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
crypto.SHA1: {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
crypto.SHA224: {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c},
crypto.SHA256: {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
crypto.SHA384: {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
crypto.SHA512: {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},

View File

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

View File

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

View File

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

View File

@ -318,7 +318,7 @@ func (d *decoder) Read(p []byte) (n int, err error) {
}
nn, d.err = io.ReadAtLeast(d.r, d.buf[d.nbuf:nn], 4-d.nbuf)
d.nbuf += nn
if d.nbuf < 4 {
if d.err != nil || d.nbuf < 4 {
return 0, d.err
}

View File

@ -6,9 +6,11 @@ package base64
import (
"bytes"
"errors"
"io"
"io/ioutil"
"testing"
"time"
)
type testpair struct {
@ -226,3 +228,50 @@ func TestNewLineCharacters(t *testing.T) {
}
}
}
type nextRead struct {
n int // bytes to return
err error // error to return
}
// faultInjectReader returns data from source, rate-limited
// and with the errors as written to nextc.
type faultInjectReader struct {
source string
nextc <-chan nextRead
}
func (r *faultInjectReader) Read(p []byte) (int, error) {
nr := <-r.nextc
if len(p) > nr.n {
p = p[:nr.n]
}
n := copy(p, r.source)
r.source = r.source[n:]
return n, nr.err
}
// tests that we don't ignore errors from our underlying reader
func TestDecoderIssue3577(t *testing.T) {
next := make(chan nextRead, 10)
wantErr := errors.New("my error")
next <- nextRead{5, nil}
next <- nextRead{10, wantErr}
d := NewDecoder(StdEncoding, &faultInjectReader{
source: "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==", // twas brillig...
nextc: next,
})
errc := make(chan error)
go func() {
_, err := ioutil.ReadAll(d)
errc <- err
}()
select {
case err := <-errc:
if err != wantErr {
t.Errorf("got error %v; want %v", err, wantErr)
}
case <-time.After(5 * time.Second):
t.Errorf("timeout; Decoder blocked without returning an error")
}
}

View File

@ -116,7 +116,7 @@ uninterpreted bytes of the value.
All other slices and arrays are sent as an unsigned count followed by that many
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.

View File

@ -273,9 +273,14 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler,
_, isUnmarshaler = v.Interface().(Unmarshaler)
}
// Load value from interface, but only if the result will be
// usefully addressable.
if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() {
v = iv.Elem()
continue
e := iv.Elem()
if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
v = e
continue
}
}
pv := v
@ -588,6 +593,11 @@ func (d *decodeState) literal(v reflect.Value) {
// produce more helpful error messages.
func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) {
// Check for unmarshaler.
if len(item) == 0 {
//Empty string given
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
return
}
wantptr := item[0] == 'n' // null
unmarshaler, pv := d.indirect(v, wantptr)
if unmarshaler != nil {

View File

@ -638,3 +638,68 @@ func TestAnonymous(t *testing.T) {
t.Fatal("Unmarshal: did set T.Y")
}
}
// Test that the empty string doesn't panic decoding when ,string is specified
// Issue 3450
func TestEmptyString(t *testing.T) {
type T2 struct {
Number1 int `json:",string"`
Number2 int `json:",string"`
}
data := `{"Number1":"1", "Number2":""}`
dec := NewDecoder(strings.NewReader(data))
var t2 T2
err := dec.Decode(&t2)
if err == nil {
t.Fatal("Decode: did not return error")
}
if t2.Number1 != 1 {
t.Fatal("Decode: did not set Number1")
}
}
func intp(x int) *int {
p := new(int)
*p = x
return p
}
func intpp(x *int) **int {
pp := new(*int)
*pp = x
return pp
}
var interfaceSetTests = []struct {
pre interface{}
json string
post interface{}
}{
{"foo", `"bar"`, "bar"},
{"foo", `2`, 2.0},
{"foo", `true`, true},
{"foo", `null`, nil},
{nil, `null`, nil},
{new(int), `null`, nil},
{(*int)(nil), `null`, nil},
{new(*int), `null`, new(*int)},
{(**int)(nil), `null`, nil},
{intp(1), `null`, nil},
{intpp(nil), `null`, intpp(nil)},
{intpp(intp(1)), `null`, intpp(nil)},
}
func TestInterfaceSet(t *testing.T) {
for _, tt := range interfaceSetTests {
b := struct{ X interface{} }{tt.pre}
blob := `{"X":` + tt.json + `}`
if err := Unmarshal([]byte(blob), &b); err != nil {
t.Errorf("Unmarshal %#q: %v", blob, err)
continue
}
if !reflect.DeepEqual(b.X, tt.post) {
t.Errorf("Unmarshal %#q into %#v: X=%#v, want %#v", blob, tt.pre, b.X, tt.post)
}
}
}

View File

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

View File

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

View File

@ -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.

View File

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

View File

@ -0,0 +1,50 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ast
import (
"testing"
)
var comments = []struct {
list []string
text string
}{
{[]string{"//"}, ""},
{[]string{"// "}, ""},
{[]string{"//", "//", "// "}, ""},
{[]string{"// foo "}, "foo\n"},
{[]string{"//", "//", "// foo"}, "foo\n"},
{[]string{"// foo bar "}, "foo bar\n"},
{[]string{"// foo", "// bar"}, "foo\nbar\n"},
{[]string{"// foo", "//", "//", "//", "// bar"}, "foo\n\nbar\n"},
{[]string{"// foo", "/* bar */"}, "foo\n bar\n"},
{[]string{"//", "//", "//", "// foo", "//", "//", "//"}, "foo\n"},
{[]string{"/**/"}, ""},
{[]string{"/* */"}, ""},
{[]string{"/**/", "/**/", "/* */"}, ""},
{[]string{"/* Foo */"}, " Foo\n"},
{[]string{"/* Foo Bar */"}, " Foo Bar\n"},
{[]string{"/* Foo*/", "/* Bar*/"}, " Foo\n Bar\n"},
{[]string{"/* Foo*/", "/**/", "/**/", "/**/", "// Bar"}, " Foo\n\nBar\n"},
{[]string{"/* Foo*/", "/*\n*/", "//", "/*\n*/", "// Bar"}, " Foo\n\nBar\n"},
{[]string{"/* Foo*/", "// Bar"}, " Foo\nBar\n"},
{[]string{"/* Foo\n Bar*/"}, " Foo\n Bar\n"},
}
func TestCommentText(t *testing.T) {
for i, c := range comments {
list := make([]*Comment, len(c.list))
for i, s := range c.list {
list[i] = &Comment{Text: s}
}
text := (&CommentGroup{list}).Text()
if text != c.text {
t.Errorf("case %d: got %q; expected %q", i, text, c.text)
}
}
}

View File

@ -68,7 +68,7 @@ type Context struct {
// ReadDir returns a slice of os.FileInfo, sorted by Name,
// 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) {

View File

@ -267,13 +267,13 @@ func (p *parser) consumeComment() (comment *ast.Comment, endline int) {
// Consume a group of adjacent comments, add it to the parser's
// 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}
}
}

View File

@ -5,10 +5,12 @@
package parser
import (
"bytes"
"fmt"
"go/ast"
"go/token"
"os"
"strings"
"testing"
)
@ -25,7 +27,7 @@ func TestParse(t *testing.T) {
for _, filename := range validFiles {
_, err := ParseFile(fset, filename, nil, DeclarationErrors)
if err != nil {
t.Errorf("ParseFile(%s): %v", filename, err)
t.Fatalf("ParseFile(%s): %v", filename, err)
}
}
}
@ -70,7 +72,7 @@ func TestParseExpr(t *testing.T) {
src := "a + b"
x, err := ParseExpr(src)
if err != nil {
t.Errorf("ParseExpr(%s): %v", src, err)
t.Fatalf("ParseExpr(%s): %v", src, err)
}
// sanity check
if _, ok := x.(*ast.BinaryExpr); !ok {
@ -81,7 +83,7 @@ func TestParseExpr(t *testing.T) {
src = "a + *"
_, err = ParseExpr(src)
if err == nil {
t.Errorf("ParseExpr(%s): %v", src, err)
t.Fatalf("ParseExpr(%s): %v", src, err)
}
// it must not crash
@ -93,7 +95,7 @@ func TestParseExpr(t *testing.T) {
func TestColonEqualsScope(t *testing.T) {
f, err := ParseFile(fset, "", `package p; func f() { x, y, z := x, y, z }`, 0)
if err != nil {
t.Errorf("parse: %s", err)
t.Fatal(err)
}
// RHS refers to undefined globals; LHS does not.
@ -115,7 +117,7 @@ func TestColonEqualsScope(t *testing.T) {
func TestVarScope(t *testing.T) {
f, err := ParseFile(fset, "", `package p; func f() { var x, y, z = x, y, z }`, 0)
if err != nil {
t.Errorf("parse: %s", err)
t.Fatal(err)
}
// RHS refers to undefined globals; LHS does not.
@ -133,6 +135,67 @@ func TestVarScope(t *testing.T) {
}
}
func TestUnresolved(t *testing.T) {
f, err := ParseFile(fset, "", `
package p
//
func f1a(int)
func f2a(byte, int, float)
func f3a(a, b int, c float)
func f4a(...complex)
func f5a(a s1a, b ...complex)
//
func f1b(*int)
func f2b([]byte, (int), *float)
func f3b(a, b *int, c []float)
func f4b(...*complex)
func f5b(a s1a, b ...[]complex)
//
type s1a struct { int }
type s2a struct { byte; int; s1a }
type s3a struct { a, b int; c float }
//
type s1b struct { *int }
type s2b struct { byte; int; *float }
type s3b struct { a, b *s3b; c []float }
`, 0)
if err != nil {
t.Fatal(err)
}
want := "int " + // f1a
"byte int float " + // f2a
"int float " + // f3a
"complex " + // f4a
"complex " + // f5a
//
"int " + // f1b
"byte int float " + // f2b
"int float " + // f3b
"complex " + // f4b
"complex " + // f5b
//
"int " + // s1a
"byte int " + // s2a
"int float " + // s3a
//
"int " + // s1a
"byte int float " + // s2a
"float " // s3a
// collect unresolved identifiers
var buf bytes.Buffer
for _, u := range f.Unresolved {
buf.WriteString(u.Name)
buf.WriteByte(' ')
}
got := buf.String()
if got != want {
t.Errorf("\ngot: %s\nwant: %s", got, want)
}
}
var imports = map[string]bool{
`"a"`: true,
"`a`": true,
@ -177,3 +240,125 @@ func TestImports(t *testing.T) {
}
}
}
func TestCommentGroups(t *testing.T) {
f, err := ParseFile(fset, "", `
package p /* 1a */ /* 1b */ /* 1c */ // 1d
/* 2a
*/
// 2b
const pi = 3.1415
/* 3a */ // 3b
/* 3c */ const e = 2.7182
// Example from issue 3139
func ExampleCount() {
fmt.Println(strings.Count("cheese", "e"))
fmt.Println(strings.Count("five", "")) // before & after each rune
// Output:
// 3
// 5
}
`, ParseComments)
if err != nil {
t.Fatal(err)
}
expected := [][]string{
{"/* 1a */", "/* 1b */", "/* 1c */", "// 1d"},
{"/* 2a\n*/", "// 2b"},
{"/* 3a */", "// 3b", "/* 3c */"},
{"// Example from issue 3139"},
{"// before & after each rune"},
{"// Output:", "// 3", "// 5"},
}
if len(f.Comments) != len(expected) {
t.Fatalf("got %d comment groups; expected %d", len(f.Comments), len(expected))
}
for i, exp := range expected {
got := f.Comments[i].List
if len(got) != len(exp) {
t.Errorf("got %d comments in group %d; expected %d", len(got), i, len(exp))
continue
}
for j, exp := range exp {
got := got[j].Text
if got != exp {
t.Errorf("got %q in group %d; expected %q", got, i, exp)
}
}
}
}
func getField(file *ast.File, fieldname string) *ast.Field {
parts := strings.Split(fieldname, ".")
for _, d := range file.Decls {
if d, ok := d.(*ast.GenDecl); ok && d.Tok == token.TYPE {
for _, s := range d.Specs {
if s, ok := s.(*ast.TypeSpec); ok && s.Name.Name == parts[0] {
if s, ok := s.Type.(*ast.StructType); ok {
for _, f := range s.Fields.List {
for _, name := range f.Names {
if name.Name == parts[1] {
return f
}
}
}
}
}
}
}
}
return nil
}
// Don't use ast.CommentGroup.Text() - we want to see exact comment text.
func commentText(c *ast.CommentGroup) string {
var buf bytes.Buffer
if c != nil {
for _, c := range c.List {
buf.WriteString(c.Text)
}
}
return buf.String()
}
func checkFieldComments(t *testing.T, file *ast.File, fieldname, lead, line string) {
f := getField(file, fieldname)
if f == nil {
t.Fatalf("field not found: %s", fieldname)
}
if got := commentText(f.Doc); got != lead {
t.Errorf("got lead comment %q; expected %q", got, lead)
}
if got := commentText(f.Comment); got != line {
t.Errorf("got line comment %q; expected %q", got, line)
}
}
func TestLeadAndLineComments(t *testing.T) {
f, err := ParseFile(fset, "", `
package p
type T struct {
/* F1 lead comment */
//
F1 int /* F1 */ // line comment
// F2 lead
// comment
F2 int // F2 line comment
// f3 lead comment
f3 int // f3 line comment
}
`, ParseComments)
if err != nil {
t.Fatal(err)
}
checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
checkFieldComments(t, f, "T.f3", "// f3 lead comment", "// f3 line comment")
ast.FileExports(f)
checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
if getField(f, "T.f3") != nil {
t.Error("not expected to find T.f3")
}
}

View File

@ -60,8 +60,8 @@ func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (prin
// setComment sets g as the next comment if g != nil and if node comments
// 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

View File

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

View File

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

View File

@ -661,3 +661,21 @@ func TestExpNN(t *testing.T) {
}
}
}
func ExpHelper(b *testing.B, x, y Word) {
var z nat
for i := 0; i < b.N; i++ {
z.expWW(x, y)
}
}
func BenchmarkExp3Power0x10(b *testing.B) { ExpHelper(b, 3, 0x10) }
func BenchmarkExp3Power0x40(b *testing.B) { ExpHelper(b, 3, 0x40) }
func BenchmarkExp3Power0x100(b *testing.B) { ExpHelper(b, 3, 0x100) }
func BenchmarkExp3Power0x400(b *testing.B) { ExpHelper(b, 3, 0x400) }
func BenchmarkExp3Power0x1000(b *testing.B) { ExpHelper(b, 3, 0x1000) }
func BenchmarkExp3Power0x4000(b *testing.B) { ExpHelper(b, 3, 0x4000) }
func BenchmarkExp3Power0x10000(b *testing.B) { ExpHelper(b, 3, 0x10000) }
func BenchmarkExp3Power0x40000(b *testing.B) { ExpHelper(b, 3, 0x40000) }
func BenchmarkExp3Power0x100000(b *testing.B) { ExpHelper(b, 3, 0x100000) }
func BenchmarkExp3Power0x400000(b *testing.B) { ExpHelper(b, 3, 0x400000) }

View File

@ -22,11 +22,6 @@ import (
"net/textproto"
)
// 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
}

View File

@ -10,20 +10,13 @@ import (
"fmt"
"io"
"io/ioutil"
"net/textproto"
"os"
"reflect"
"strings"
"testing"
)
func TestHorizontalWhitespace(t *testing.T) {
if !onlyHorizontalWhitespace([]byte(" \t")) {
t.Error("expected pass")
}
if onlyHorizontalWhitespace([]byte("foo bar")) {
t.Error("expected failure")
}
}
func TestBoundaryLine(t *testing.T) {
mr := NewReader(strings.NewReader(""), "myBoundary")
if !mr.isBoundaryDelimiterLine([]byte("--myBoundary\r\n")) {
@ -319,29 +312,6 @@ Oh no, premature EOF!
}
}
func TestZeroLengthBody(t *testing.T) {
testBody := strings.Replace(`
This is a multi-part message. This line is ignored.
--MyBoundary
foo: bar
--MyBoundary--
`, "\n", "\r\n", -1)
r := NewReader(strings.NewReader(testBody), "MyBoundary")
part, err := r.NextPart()
if err != nil {
t.Fatalf("didn't get a part")
}
n, err := io.Copy(ioutil.Discard, part)
if err != nil {
t.Errorf("error reading part: %v", err)
}
if n != 0 {
t.Errorf("read %d bytes; expected 0", n)
}
}
type slowReader struct {
r io.Reader
}
@ -427,3 +397,214 @@ func TestNested(t *testing.T) {
t.Fatalf("final outer NextPart = %v; want io.EOF", err)
}
}
type headerBody struct {
header textproto.MIMEHeader
body string
}
func formData(key, value string) headerBody {
return headerBody{
textproto.MIMEHeader{
"Content-Type": {"text/plain; charset=ISO-8859-1"},
"Content-Disposition": {"form-data; name=" + key},
},
value,
}
}
type parseTest struct {
name string
in, sep string
want []headerBody
}
var parseTests = []parseTest{
// Actual body from App Engine on a blob upload. The final part (the
// Content-Type: message/external-body) is what App Engine replaces
// the uploaded file with. The other form fields (prefixed with
// "other" in their form-data name) are unchanged. A bug was
// reported with blob uploads failing when the other fields were
// empty. This was the MIME POST body that previously failed.
{
name: "App Engine post",
sep: "00151757727e9583fd04bfbca4c6",
in: "--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherEmpty1\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherFoo1\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherFoo2\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherEmpty2\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatFoo\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatFoo\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatEmpty\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatEmpty\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=submit\r\n\r\nSubmit\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: message/external-body; charset=ISO-8859-1; blob-key=AHAZQqG84qllx7HUqO_oou5EvdYQNS3Mbbkb0RjjBoM_Kc1UqEN2ygDxWiyCPulIhpHRPx-VbpB6RX4MrsqhWAi_ZxJ48O9P2cTIACbvATHvg7IgbvZytyGMpL7xO1tlIvgwcM47JNfv_tGhy1XwyEUO8oldjPqg5Q\r\nContent-Disposition: form-data; name=file; filename=\"fall.png\"\r\n\r\nContent-Type: image/png\r\nContent-Length: 232303\r\nX-AppEngine-Upload-Creation: 2012-05-10 23:14:02.715173\r\nContent-MD5: MzRjODU1ZDZhZGU1NmRlOWEwZmMwMDdlODBmZTA0NzA=\r\nContent-Disposition: form-data; name=file; filename=\"fall.png\"\r\n\r\n\r\n--00151757727e9583fd04bfbca4c6--",
want: []headerBody{
formData("otherEmpty1", ""),
formData("otherFoo1", "foo"),
formData("otherFoo2", "foo"),
formData("otherEmpty2", ""),
formData("otherRepeatFoo", "foo"),
formData("otherRepeatFoo", "foo"),
formData("otherRepeatEmpty", ""),
formData("otherRepeatEmpty", ""),
formData("submit", "Submit"),
{textproto.MIMEHeader{
"Content-Type": {"message/external-body; charset=ISO-8859-1; blob-key=AHAZQqG84qllx7HUqO_oou5EvdYQNS3Mbbkb0RjjBoM_Kc1UqEN2ygDxWiyCPulIhpHRPx-VbpB6RX4MrsqhWAi_ZxJ48O9P2cTIACbvATHvg7IgbvZytyGMpL7xO1tlIvgwcM47JNfv_tGhy1XwyEUO8oldjPqg5Q"},
"Content-Disposition": {"form-data; name=file; filename=\"fall.png\""},
}, "Content-Type: image/png\r\nContent-Length: 232303\r\nX-AppEngine-Upload-Creation: 2012-05-10 23:14:02.715173\r\nContent-MD5: MzRjODU1ZDZhZGU1NmRlOWEwZmMwMDdlODBmZTA0NzA=\r\nContent-Disposition: form-data; name=file; filename=\"fall.png\"\r\n\r\n"},
},
},
// Single empty part, ended with --boundary immediately after headers.
{
name: "single empty part, --boundary",
sep: "abc",
in: "--abc\r\nFoo: bar\r\n\r\n--abc--",
want: []headerBody{
{textproto.MIMEHeader{"Foo": {"bar"}}, ""},
},
},
// Single empty part, ended with \r\n--boundary immediately after headers.
{
name: "single empty part, \r\n--boundary",
sep: "abc",
in: "--abc\r\nFoo: bar\r\n\r\n\r\n--abc--",
want: []headerBody{
{textproto.MIMEHeader{"Foo": {"bar"}}, ""},
},
},
// Final part empty.
{
name: "final part empty",
sep: "abc",
in: "--abc\r\nFoo: bar\r\n\r\n--abc\r\nFoo2: bar2\r\n\r\n--abc--",
want: []headerBody{
{textproto.MIMEHeader{"Foo": {"bar"}}, ""},
{textproto.MIMEHeader{"Foo2": {"bar2"}}, ""},
},
},
// Final part empty with newlines after final separator.
{
name: "final part empty then crlf",
sep: "abc",
in: "--abc\r\nFoo: bar\r\n\r\n--abc--\r\n",
want: []headerBody{
{textproto.MIMEHeader{"Foo": {"bar"}}, ""},
},
},
// Final part empty with lwsp-chars after final separator.
{
name: "final part empty then lwsp",
sep: "abc",
in: "--abc\r\nFoo: bar\r\n\r\n--abc-- \t",
want: []headerBody{
{textproto.MIMEHeader{"Foo": {"bar"}}, ""},
},
},
// No parts (empty form as submitted by Chrome)
{
name: "no parts",
sep: "----WebKitFormBoundaryQfEAfzFOiSemeHfA",
in: "------WebKitFormBoundaryQfEAfzFOiSemeHfA--\r\n",
want: []headerBody{},
},
// Part containing data starting with the boundary, but with additional suffix.
{
name: "fake separator as data",
sep: "sep",
in: "--sep\r\nFoo: bar\r\n\r\n--sepFAKE\r\n--sep--",
want: []headerBody{
{textproto.MIMEHeader{"Foo": {"bar"}}, "--sepFAKE"},
},
},
// Part containing a boundary with whitespace following it.
{
name: "boundary with whitespace",
sep: "sep",
in: "--sep \r\nFoo: bar\r\n\r\ntext\r\n--sep--",
want: []headerBody{
{textproto.MIMEHeader{"Foo": {"bar"}}, "text"},
},
},
// With ignored leading line.
{
name: "leading line",
sep: "MyBoundary",
in: strings.Replace(`This is a multi-part message. This line is ignored.
--MyBoundary
foo: bar
--MyBoundary--`, "\n", "\r\n", -1),
want: []headerBody{
{textproto.MIMEHeader{"Foo": {"bar"}}, ""},
},
},
roundTripParseTest(),
}
func TestParse(t *testing.T) {
Cases:
for _, tt := range parseTests {
r := NewReader(strings.NewReader(tt.in), tt.sep)
got := []headerBody{}
for {
p, err := r.NextPart()
if err == io.EOF {
break
}
if err != nil {
t.Errorf("in test %q, NextPart: %v", tt.name, err)
continue Cases
}
pbody, err := ioutil.ReadAll(p)
if err != nil {
t.Errorf("in test %q, error reading part: %v", tt.name, err)
continue Cases
}
got = append(got, headerBody{p.Header, string(pbody)})
}
if !reflect.DeepEqual(tt.want, got) {
t.Errorf("test %q:\n got: %v\nwant: %v", tt.name, got, tt.want)
if len(tt.want) != len(got) {
t.Errorf("test %q: got %d parts, want %d", tt.name, len(got), len(tt.want))
} else if len(got) > 1 {
for pi, wantPart := range tt.want {
if !reflect.DeepEqual(wantPart, got[pi]) {
t.Errorf("test %q, part %d:\n got: %v\nwant: %v", tt.name, pi, got[pi], wantPart)
}
}
}
}
}
}
func roundTripParseTest() parseTest {
t := parseTest{
name: "round trip",
want: []headerBody{
formData("empty", ""),
formData("lf", "\n"),
formData("cr", "\r"),
formData("crlf", "\r\n"),
formData("foo", "bar"),
},
}
var buf bytes.Buffer
w := NewWriter(&buf)
for _, p := range t.want {
pw, err := w.CreatePart(p.header)
if err != nil {
panic(err)
}
_, err = pw.Write([]byte(p.body))
if err != nil {
panic(err)
}
}
w.Close()
t.in = buf.String()
t.sep = w.Boundary()
return t
}

View File

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

View File

@ -278,6 +278,11 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response,
return nil, err
}
req.Header.Set("Content-Type", bodyType)
if c.Jar != nil {
for _, cookie := range c.Jar.Cookies(req.URL) {
req.AddCookie(cookie)
}
}
r, err = send(req, c.Transport)
if err == nil && c.Jar != nil {
c.Jar.SetCookies(req.URL, r.Cookies())

View File

@ -256,6 +256,31 @@ var echoCookiesRedirectHandler = HandlerFunc(func(w ResponseWriter, r *Request)
}
})
func TestClientSendsCookieFromJar(t *testing.T) {
tr := &recordingTransport{}
client := &Client{Transport: tr}
client.Jar = &TestJar{perURL: make(map[string][]*Cookie)}
us := "http://dummy.faketld/"
u, _ := url.Parse(us)
client.Jar.SetCookies(u, expectedCookies)
client.Get(us) // Note: doesn't hit network
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
client.Head(us) // Note: doesn't hit network
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
client.Post(us, "text/plain", strings.NewReader("body")) // Note: doesn't hit network
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
client.PostForm(us, url.Values{}) // Note: doesn't hit network
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
req, _ := NewRequest("GET", us, nil)
client.Do(req) // Note: doesn't hit network
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
}
// Just enough correctness for our redirect tests. Uses the URL.Host as the
// scope of all cookies.
type TestJar struct {

View File

@ -5,6 +5,7 @@
package http
import (
"net/url"
"os"
"testing"
)
@ -46,3 +47,32 @@ func TestUseProxy(t *testing.T) {
}
}
}
var cacheKeysTests = []struct {
proxy string
scheme string
addr string
key string
}{
{"", "http", "foo.com", "|http|foo.com"},
{"", "https", "foo.com", "|https|foo.com"},
{"http://foo.com", "http", "foo.com", "http://foo.com|http|"},
{"http://foo.com", "https", "foo.com", "http://foo.com|https|foo.com"},
}
func TestCacheKeys(t *testing.T) {
for _, tt := range cacheKeysTests {
var proxy *url.URL
if tt.proxy != "" {
u, err := url.Parse(tt.proxy)
if err != nil {
t.Fatal(err)
}
proxy = u
}
cm := connectMethod{proxy, tt.scheme, tt.addr}
if cm.String() != tt.key {
t.Fatalf("{%q, %q, %q} cache key %q; want %q", tt.proxy, tt.scheme, tt.addr, cm.String(), tt.key)
}
}
}

View File

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

View File

@ -14,6 +14,7 @@ import (
"io/ioutil"
"net/url"
"reflect"
"strings"
"testing"
)
@ -444,3 +445,17 @@ func TestLocationResponse(t *testing.T) {
}
}
}
func TestResponseStatusStutter(t *testing.T) {
r := &Response{
Status: "123 some status",
StatusCode: 123,
ProtoMajor: 1,
ProtoMinor: 3,
}
var buf bytes.Buffer
r.Write(&buf)
if strings.Contains(buf.String(), "123 123") {
t.Errorf("stutter in status: %s", buf.String())
}
}

View File

@ -31,7 +31,7 @@ import (
// Errors introduced by the HTTP server.
var (
ErrWriteAfterFlush = errors.New("Conn.Write called after Flush")
ErrBodyNotAllowed = errors.New("http: response status code does not allow body")
ErrBodyNotAllowed = errors.New("http: request method or response status code does not allow body")
ErrHijacked = errors.New("Conn has been hijacked")
ErrContentLength = errors.New("Conn.Write wrote more than the declared Content-Length")
)

View File

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

View File

@ -450,10 +450,14 @@ type connectMethod struct {
func (ck *connectMethod) String() string {
proxyStr := ""
targetAddr := ck.targetAddr
if ck.proxyURL != nil {
proxyStr = ck.proxyURL.String()
if ck.targetScheme == "http" {
targetAddr = ""
}
}
return strings.Join([]string{proxyStr, ck.targetScheme, ck.targetAddr}, "|")
return strings.Join([]string{proxyStr, ck.targetScheme, targetAddr}, "|")
}
// addr returns the first hop "host:port" to which we need to TCP connect.

View File

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

View File

@ -95,6 +95,11 @@ func TestDateParsing(t *testing.T) {
"21 Nov 97 09:55:06 GMT",
time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("GMT", 0)),
},
// Commonly found format not specified by RFC 5322.
{
"Fri, 21 Nov 1997 09:55:06 -0600 (MDT)",
time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
},
}
for _, test := range tests {
hdr := Header{

View File

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

View File

@ -188,6 +188,37 @@ var urltests = []URLTest{
},
"http://user:password@google.com",
},
// unescaped @ in username should not confuse host
{
"http://j@ne:password@google.com",
&URL{
Scheme: "http",
User: UserPassword("j@ne", "password"),
Host: "google.com",
},
"http://j%40ne:password@google.com",
},
// unescaped @ in password should not confuse host
{
"http://jane:p@ssword@google.com",
&URL{
Scheme: "http",
User: UserPassword("jane", "p@ssword"),
Host: "google.com",
},
"http://jane:p%40ssword@google.com",
},
{
"http://j@ne:password@google.com/p@th?q=@go",
&URL{
Scheme: "http",
User: UserPassword("j@ne", "password"),
Host: "google.com",
Path: "/p@th",
RawQuery: "q=@go",
},
"http://j%40ne:password@google.com/p@th?q=@go",
},
{
"http://www.google.com/?q=go+language#foo",
&URL{

View File

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

View File

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

View File

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

View File

@ -869,3 +869,34 @@ func TestDriveLetterInEvalSymlinks(t *testing.T) {
t.Errorf("Results of EvalSymlinks do not match: %q and %q", flp, fup)
}
}
/* This test does not work gccgo, since the sources are arranged
differently.
func TestBug3486(t *testing.T) { // http://code.google.com/p/go/issues/detail?id=3486
root, err := filepath.EvalSymlinks(os.Getenv("GOROOT"))
if err != nil {
t.Fatal(err)
}
lib := filepath.Join(root, "lib")
src := filepath.Join(root, "src")
seenSrc := false
filepath.Walk(root, func(pth string, info os.FileInfo, err error) error {
if err != nil {
t.Fatal(err)
}
switch pth {
case lib:
return filepath.SkipDir
case src:
seenSrc = true
}
return nil
})
if !seenSrc {
t.Fatalf("%q not seen", src)
}
}
*/

View File

@ -512,7 +512,7 @@ func (re *Regexp) replaceAll(bsrc []byte, src string, nmatch int, repl func(dst
}
// ReplaceAll returns a copy of src, replacing matches of the Regexp
// 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.

View File

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

View File

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

View File

@ -20,7 +20,7 @@ func Goexit()
// Caller reports file and line number information about function invocations on
// the calling goroutine's stack. The argument skip is the number of stack frames
// to ascend, with 1 identifying the caller of Caller. (For historical reasons the
// to ascend, with 0 identifying the caller of Caller. (For historical reasons the
// meaning of skip differs between Caller and Callers.) The return values report the
// program counter, file name, and line number within the file of the corresponding
// call. The boolean ok is false if it was not possible to recover the information.
@ -28,7 +28,8 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool)
// Callers fills the slice pc with the program counters of function invocations
// on the calling goroutine's stack. The argument skip is the number of stack frames
// to skip before recording in pc, with 0 starting at the caller of Callers.
// to skip before recording in pc, with 0 identifying the frame for Callers itself and
// 1 identifying the caller of Callers.
// It returns the number of entries written to pc.
func Callers(skip int, pc []uintptr) int

View File

@ -4,13 +4,17 @@
package strconv
// FormatUint returns the string representation of i in the given base.
// FormatUint returns the string representation of i in the given base,
// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
// for digit values >= 10.
func FormatUint(i uint64, base int) string {
_, s := formatBits(nil, i, base, false, false)
return s
}
// FormatInt returns the string representation of i in the given base.
// FormatInt returns the string representation of i in the given base,
// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
// for digit values >= 10.
func FormatInt(i int64, base int) string {
_, s := formatBits(nil, uint64(i), base, i < 0, false)
return s

View File

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

View File

@ -518,6 +518,13 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu
}
}
if !value.Type().AssignableTo(typ) {
if value.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.

View File

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

View File

@ -224,3 +224,25 @@ func TestTimerStopStress(t *testing.T) {
}
Sleep(3 * Second)
}
func TestSleepZeroDeadlock(t *testing.T) {
// Sleep(0) used to hang, the sequence of events was as follows.
// Sleep(0) sets G's status to Gwaiting, but then immediately returns leaving the status.
// Then the goroutine calls e.g. new and falls down into the scheduler due to pending GC.
// After the GC nobody wakes up the goroutine from Gwaiting status.
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
c := make(chan bool)
go func() {
for i := 0; i < 100; i++ {
runtime.GC()
}
c <- true
}()
for i := 0; i < 100; i++ {
Sleep(0)
tmp := make(chan bool, 1)
tmp <- true
<-tmp
}
<-c
}

View File

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

View File

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

View File

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

View File

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

View File

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