Commit a42a906c by Ian Lance Taylor

libgo: Update to current master library sources.

From-SVN: r194460
parent bc77608b
a070de932857
c031aa767edf
The first line of this file holds the Mercurial revision number of the
last merge done from the master library sources.
......@@ -221,6 +221,7 @@ endif
toolexeclibgoexpdir = $(toolexeclibgodir)/exp
toolexeclibgoexp_DATA = \
exp/cookiejar.gox \
exp/ebnf.gox \
exp/html.gox \
$(exp_inotify_gox) \
......@@ -251,6 +252,7 @@ toolexeclibgogo_DATA = \
go/ast.gox \
go/build.gox \
go/doc.gox \
go/format.gox \
go/parser.gox \
go/printer.gox \
go/scanner.gox \
......@@ -1194,6 +1196,9 @@ go_encoding_xml_files = \
go/encoding/xml/typeinfo.go \
go/encoding/xml/xml.go
go_exp_cookiejar_files = \
go/exp/cookiejar/jar.go \
go/exp/cookiejar/storage.go
go_exp_ebnf_files = \
go/exp/ebnf/ebnf.go \
go/exp/ebnf/parser.go
......@@ -1284,6 +1289,8 @@ go_go_doc_files = \
go/go/doc/filter.go \
go/go/doc/reader.go \
go/go/doc/synopsis.go
go_go_format_files = \
go/go/format/format.go
go_go_parser_files = \
go/go/parser/interface.go \
go/go/parser/parser.go
......@@ -1384,6 +1391,7 @@ go_math_rand_files = \
go_mime_multipart_files = \
go/mime/multipart/formdata.go \
go/mime/multipart/multipart.go \
go/mime/multipart/quotedprintable.go \
go/mime/multipart/writer.go
go_net_http_files = \
......@@ -1456,6 +1464,7 @@ go_os_signal_files = \
go_os_user_files = \
go/os/user/user.go \
go/os/user/lookup.go \
go/os/user/lookup_unix.go
go_path_filepath_files = \
......@@ -1822,6 +1831,7 @@ libgo_go_objs = \
encoding/json.lo \
encoding/pem.lo \
encoding/xml.lo \
exp/cookiejar.lo \
exp/ebnf.lo \
exp/html.lo \
exp/html/atom.lo \
......@@ -1836,6 +1846,7 @@ libgo_go_objs = \
go/ast.lo \
go/build.lo \
go/doc.lo \
go/format.lo \
go/parser.lo \
go/printer.lo \
go/scanner.lo \
......@@ -2658,6 +2669,15 @@ encoding/xml/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: encoding/xml/check
@go_include@ exp/cookiejar.lo.dep
exp/cookiejar.lo.dep: $(go_exp_cookiejar_files)
$(BUILDDEPS)
exp/cookiejar.lo: $(go_exp_cookiejar_files)
$(BUILDPACKAGE)
exp/cookiejar/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: exp/cookiejar/check
@go_include@ exp/ebnf.lo.dep
exp/ebnf.lo.dep: $(go_exp_ebnf_files)
$(BUILDDEPS)
......@@ -2802,6 +2822,15 @@ go/doc/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: go/doc/check
@go_include@ go/format.lo.dep
go/format.lo.dep: $(go_go_format_files)
$(BUILDDEPS)
go/format.lo: $(go_go_format_files)
$(BUILDPACKAGE)
go/format/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: go/format/check
@go_include@ go/parser.lo.dep
go/parser.lo.dep: $(go_go_parser_files)
$(BUILDDEPS)
......@@ -3450,6 +3479,8 @@ encoding/pem.gox: encoding/pem.lo
encoding/xml.gox: encoding/xml.lo
$(BUILDGOX)
exp/cookiejar.gox: exp/cookiejar.lo
$(BUILDGOX)
exp/ebnf.gox: exp/ebnf.lo
$(BUILDGOX)
exp/html.gox: exp/html.lo
......@@ -3482,6 +3513,8 @@ go/build.gox: go/build.lo
$(BUILDGOX)
go/doc.gox: go/doc.lo
$(BUILDGOX)
go/format.gox: go/format.lo
$(BUILDGOX)
go/parser.gox: go/parser.lo
$(BUILDGOX)
go/printer.gox: go/printer.lo
......@@ -3681,6 +3714,7 @@ TEST_PACKAGES = \
encoding/json/check \
encoding/pem/check \
encoding/xml/check \
exp/cookiejar/check \
exp/ebnf/check \
exp/html/check \
exp/html/atom/check \
......@@ -3696,6 +3730,7 @@ TEST_PACKAGES = \
go/ast/check \
$(go_build_check_omitted_since_it_calls_6g) \
go/doc/check \
go/format/check \
go/parser/check \
go/printer/check \
go/scanner/check \
......
......@@ -153,26 +153,27 @@ am__DEPENDENCIES_2 = bufio.lo bytes.lo bytes/index.lo crypto.lo \
debug/pe.lo encoding/ascii85.lo encoding/asn1.lo \
encoding/base32.lo encoding/base64.lo encoding/binary.lo \
encoding/csv.lo encoding/gob.lo encoding/hex.lo \
encoding/json.lo encoding/pem.lo encoding/xml.lo exp/ebnf.lo \
exp/html.lo exp/html/atom.lo exp/locale/collate.lo \
exp/locale/collate/build.lo exp/norm.lo exp/proxy.lo \
exp/terminal.lo exp/types.lo exp/utf8string.lo \
html/template.lo go/ast.lo go/build.lo go/doc.lo go/parser.lo \
go/printer.lo go/scanner.lo go/token.lo hash/adler32.lo \
hash/crc32.lo hash/crc64.lo hash/fnv.lo net/http/cgi.lo \
net/http/fcgi.lo net/http/httptest.lo net/http/httputil.lo \
net/http/pprof.lo image/color.lo image/draw.lo image/gif.lo \
image/jpeg.lo image/png.lo index/suffixarray.lo io/ioutil.lo \
log/syslog.lo log/syslog/syslog_c.lo math/big.lo math/cmplx.lo \
math/rand.lo mime/multipart.lo net/http.lo net/mail.lo \
net/rpc.lo net/smtp.lo net/textproto.lo net/url.lo \
old/netchan.lo old/regexp.lo old/template.lo os/exec.lo \
$(am__DEPENDENCIES_1) os/signal.lo os/user.lo path/filepath.lo \
regexp/syntax.lo net/rpc/jsonrpc.lo runtime/debug.lo \
runtime/pprof.lo sync/atomic.lo sync/atomic_c.lo \
text/scanner.lo text/tabwriter.lo text/template.lo \
text/template/parse.lo testing/iotest.lo testing/quick.lo \
unicode/utf16.lo unicode/utf8.lo
encoding/json.lo encoding/pem.lo encoding/xml.lo \
exp/cookiejar.lo exp/ebnf.lo exp/html.lo exp/html/atom.lo \
exp/locale/collate.lo exp/locale/collate/build.lo exp/norm.lo \
exp/proxy.lo exp/terminal.lo exp/types.lo exp/utf8string.lo \
html/template.lo go/ast.lo go/build.lo go/doc.lo go/format.lo \
go/parser.lo go/printer.lo go/scanner.lo go/token.lo \
hash/adler32.lo hash/crc32.lo hash/crc64.lo hash/fnv.lo \
net/http/cgi.lo net/http/fcgi.lo net/http/httptest.lo \
net/http/httputil.lo net/http/pprof.lo image/color.lo \
image/draw.lo image/gif.lo image/jpeg.lo image/png.lo \
index/suffixarray.lo io/ioutil.lo log/syslog.lo \
log/syslog/syslog_c.lo math/big.lo math/cmplx.lo math/rand.lo \
mime/multipart.lo net/http.lo net/mail.lo net/rpc.lo \
net/smtp.lo net/textproto.lo net/url.lo old/netchan.lo \
old/regexp.lo old/template.lo os/exec.lo $(am__DEPENDENCIES_1) \
os/signal.lo os/user.lo path/filepath.lo regexp/syntax.lo \
net/rpc/jsonrpc.lo runtime/debug.lo runtime/pprof.lo \
sync/atomic.lo sync/atomic_c.lo text/scanner.lo \
text/tabwriter.lo text/template.lo text/template/parse.lo \
testing/iotest.lo testing/quick.lo unicode/utf16.lo \
unicode/utf8.lo
libgo_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \
../libbacktrace/libbacktrace.la $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
......@@ -608,6 +609,7 @@ toolexeclibgoencoding_DATA = \
@LIBGO_IS_LINUX_TRUE@exp_inotify_gox =
toolexeclibgoexpdir = $(toolexeclibgodir)/exp
toolexeclibgoexp_DATA = \
exp/cookiejar.gox \
exp/ebnf.gox \
exp/html.gox \
$(exp_inotify_gox) \
......@@ -634,6 +636,7 @@ toolexeclibgogo_DATA = \
go/ast.gox \
go/build.gox \
go/doc.gox \
go/format.gox \
go/parser.gox \
go/printer.gox \
go/scanner.gox \
......@@ -1403,6 +1406,10 @@ go_encoding_xml_files = \
go/encoding/xml/typeinfo.go \
go/encoding/xml/xml.go
go_exp_cookiejar_files = \
go/exp/cookiejar/jar.go \
go/exp/cookiejar/storage.go
go_exp_ebnf_files = \
go/exp/ebnf/ebnf.go \
go/exp/ebnf/parser.go
......@@ -1506,6 +1513,9 @@ go_go_doc_files = \
go/go/doc/reader.go \
go/go/doc/synopsis.go
go_go_format_files = \
go/go/format/format.go
go_go_parser_files = \
go/go/parser/interface.go \
go/go/parser/parser.go
......@@ -1614,6 +1624,7 @@ go_math_rand_files = \
go_mime_multipart_files = \
go/mime/multipart/formdata.go \
go/mime/multipart/multipart.go \
go/mime/multipart/quotedprintable.go \
go/mime/multipart/writer.go
go_net_http_files = \
......@@ -1695,6 +1706,7 @@ go_os_signal_files = \
go_os_user_files = \
go/os/user/user.go \
go/os/user/lookup.go \
go/os/user/lookup_unix.go
go_path_filepath_files = \
......@@ -1948,6 +1960,7 @@ libgo_go_objs = \
encoding/json.lo \
encoding/pem.lo \
encoding/xml.lo \
exp/cookiejar.lo \
exp/ebnf.lo \
exp/html.lo \
exp/html/atom.lo \
......@@ -1962,6 +1975,7 @@ libgo_go_objs = \
go/ast.lo \
go/build.lo \
go/doc.lo \
go/format.lo \
go/parser.lo \
go/printer.lo \
go/scanner.lo \
......@@ -2200,6 +2214,7 @@ TEST_PACKAGES = \
encoding/json/check \
encoding/pem/check \
encoding/xml/check \
exp/cookiejar/check \
exp/ebnf/check \
exp/html/check \
exp/html/atom/check \
......@@ -2215,6 +2230,7 @@ TEST_PACKAGES = \
go/ast/check \
$(go_build_check_omitted_since_it_calls_6g) \
go/doc/check \
go/format/check \
go/parser/check \
go/printer/check \
go/scanner/check \
......@@ -5096,6 +5112,15 @@ encoding/xml/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: encoding/xml/check
@go_include@ exp/cookiejar.lo.dep
exp/cookiejar.lo.dep: $(go_exp_cookiejar_files)
$(BUILDDEPS)
exp/cookiejar.lo: $(go_exp_cookiejar_files)
$(BUILDPACKAGE)
exp/cookiejar/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: exp/cookiejar/check
@go_include@ exp/ebnf.lo.dep
exp/ebnf.lo.dep: $(go_exp_ebnf_files)
$(BUILDDEPS)
......@@ -5240,6 +5265,15 @@ go/doc/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: go/doc/check
@go_include@ go/format.lo.dep
go/format.lo.dep: $(go_go_format_files)
$(BUILDDEPS)
go/format.lo: $(go_go_format_files)
$(BUILDPACKAGE)
go/format/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: go/format/check
@go_include@ go/parser.lo.dep
go/parser.lo.dep: $(go_go_parser_files)
$(BUILDDEPS)
......@@ -5880,6 +5914,8 @@ encoding/pem.gox: encoding/pem.lo
encoding/xml.gox: encoding/xml.lo
$(BUILDGOX)
exp/cookiejar.gox: exp/cookiejar.lo
$(BUILDGOX)
exp/ebnf.gox: exp/ebnf.lo
$(BUILDGOX)
exp/html.gox: exp/html.lo
......@@ -5912,6 +5948,8 @@ go/build.gox: go/build.lo
$(BUILDGOX)
go/doc.gox: go/doc.lo
$(BUILDGOX)
go/format.gox: go/format.lo
$(BUILDGOX)
go/parser.gox: go/parser.lo
$(BUILDGOX)
go/printer.gox: go/printer.lo
......
......@@ -64,6 +64,8 @@ func NewReader(rd io.Reader) *Reader {
return NewReaderSize(rd, defaultBufSize)
}
var errNegativeRead = errors.New("bufio: reader returned negative count from Read")
// fill reads a new chunk into the buffer.
func (b *Reader) fill() {
// Slide existing data to beginning.
......@@ -75,6 +77,9 @@ func (b *Reader) fill() {
// Read new data.
n, e := b.rd.Read(b.buf[b.w:])
if n < 0 {
panic(errNegativeRead)
}
b.w += n
if e != nil {
b.err = e
......@@ -282,6 +287,9 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
// of the line. The returned buffer is only valid until the next call to
// ReadLine. ReadLine either returns a non-nil line or it returns an error,
// never both.
//
// The text returned from ReadLine does not include the line end ("\r\n" or "\n").
// No indication or error is given if the input ends without a final line end.
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) {
line, err = b.ReadSlice('\n')
if err == ErrBufferFull {
......
......@@ -939,6 +939,29 @@ func (w *writeCountingDiscard) Write(p []byte) (int, error) {
return len(p), nil
}
type negativeReader int
func (r *negativeReader) Read([]byte) (int, error) { return -1, nil }
func TestNegativeRead(t *testing.T) {
// should panic with a description pointing at the reader, not at itself.
// (should NOT panic with slice index error, for example.)
b := NewReader(new(negativeReader))
defer func() {
switch err := recover().(type) {
case nil:
t.Fatal("read did not panic")
case error:
if !strings.Contains(err.Error(), "reader returned negative count from Read") {
t.Fatal("wrong panic: %v", err)
}
default:
t.Fatalf("unexpected panic value: %T(%v)", err, err)
}
}()
b.Read(make([]byte, 100))
}
// An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have.
type onlyReader struct {
r io.Reader
......
......@@ -360,16 +360,24 @@ func (b *Buffer) UnreadByte() error {
// ReadBytes returns err != nil if and only if the returned data does not end in
// delim.
func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) {
slice, err := b.readSlice(delim)
// return a copy of slice. The buffer's backing array may
// be overwritten by later calls.
line = append(line, slice...)
return
}
// readSlice is like readBytes but returns a reference to internal buffer data.
func (b *Buffer) readSlice(delim byte) (line []byte, err error) {
i := IndexByte(b.buf[b.off:], delim)
size := i + 1
end := b.off + i + 1
if i < 0 {
size = len(b.buf) - b.off
end = len(b.buf)
err = io.EOF
}
line = make([]byte, size)
copy(line, b.buf[b.off:])
b.off += size
return
line = b.buf[b.off:end]
b.off = end
return line, err
}
// ReadString reads until the first occurrence of delim in the input,
......@@ -379,8 +387,8 @@ func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) {
// ReadString returns err != nil if and only if the returned data does not end
// in delim.
func (b *Buffer) ReadString(delim byte) (line string, err error) {
bytes, err := b.ReadBytes(delim)
return string(bytes), err
slice, err := b.readSlice(delim)
return string(slice), err
}
// NewBuffer creates and initializes a new Buffer using buf as its initial
......
......@@ -375,6 +375,41 @@ func TestReadBytes(t *testing.T) {
}
}
func TestReadString(t *testing.T) {
for _, test := range readBytesTests {
buf := NewBufferString(test.buffer)
var err error
for _, expected := range test.expected {
var s string
s, err = buf.ReadString(test.delim)
if s != expected {
t.Errorf("expected %q, got %q", expected, s)
}
if err != nil {
break
}
}
if err != test.err {
t.Errorf("expected error %v, got %v", test.err, err)
}
}
}
func BenchmarkReadString(b *testing.B) {
const n = 32 << 10
data := make([]byte, n)
data[n-1] = 'x'
b.SetBytes(int64(n))
for i := 0; i < b.N; i++ {
buf := NewBuffer(data)
_, err := buf.ReadString('x')
if err != nil {
b.Fatal(err)
}
}
}
func TestGrow(t *testing.T) {
x := []byte{'x'}
y := []byte{'y'}
......
......@@ -125,7 +125,7 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {
func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
r.prevRune = -1
if r.i >= len(r.s) {
return 0, io.EOF
return 0, nil
}
b := r.s[r.i:]
m, err := w.Write(b)
......
......@@ -8,6 +8,7 @@ import (
. "bytes"
"fmt"
"io"
"io/ioutil"
"os"
"testing"
)
......@@ -88,16 +89,20 @@ func TestReaderAt(t *testing.T) {
}
func TestReaderWriteTo(t *testing.T) {
for i := 3; i < 30; i += 3 {
s := data[:len(data)/i]
r := NewReader(testBytes[:len(testBytes)/i])
for i := 0; i < 30; i += 3 {
var l int
if i > 0 {
l = len(data) / i
}
s := data[:l]
r := NewReader(testBytes[:l])
var b Buffer
n, err := r.WriteTo(&b)
if expect := int64(len(s)); n != expect {
t.Errorf("got %v; want %v", n, expect)
}
if err != nil {
t.Errorf("got error = %v; want nil", err)
t.Errorf("for length %d: got error = %v; want nil", l, err)
}
if b.String() != s {
t.Errorf("got string %q; want %q", b.String(), s)
......@@ -107,3 +112,26 @@ func TestReaderWriteTo(t *testing.T) {
}
}
}
// verify that copying from an empty reader always has the same results,
// regardless of the presence of a WriteTo method.
func TestReaderCopyNothing(t *testing.T) {
type nErr struct {
n int64
err error
}
type justReader struct {
io.Reader
}
type justWriter struct {
io.Writer
}
discard := justWriter{ioutil.Discard} // hide ReadFrom
var with, withOut nErr
with.n, with.err = io.Copy(discard, NewReader(nil))
withOut.n, withOut.err = io.Copy(discard, justReader{NewReader(nil)})
if with != withOut {
t.Errorf("behavior differs: with = %#v; without: %#v", with, withOut)
}
}
......@@ -37,15 +37,10 @@ func (pq PriorityQueue) Swap(i, j int) {
func (pq *PriorityQueue) Push(x interface{}) {
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
// To simplify indexing expressions in these methods, we save a copy of the
// slice object. We could instead write (*pq)[i].
a := *pq
n := len(a)
a = a[0 : n+1]
n := len(*pq)
item := x.(*Item)
item.index = n
a[n] = item
*pq = a
*pq = append(*pq, item)
}
func (pq *PriorityQueue) Pop() interface{} {
......
......@@ -74,7 +74,7 @@ func New(n int) *Ring {
return r
}
// Link connects ring r with with ring s such that r.Next()
// Link connects ring r with ring s such that r.Next()
// becomes s and returns the original value for r.Next().
// r must not be empty.
//
......
......@@ -758,8 +758,28 @@ func (c *Conn) Write(b []byte) (int, error) {
return 0, alertInternalError
}
// SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext
// attack when using block mode ciphers due to predictable IVs.
// This can be prevented by splitting each Application Data
// record into two records, effectively randomizing the IV.
//
// http://www.openssl.org/~bodo/tls-cbc.txt
// https://bugzilla.mozilla.org/show_bug.cgi?id=665814
// http://www.imperialviolet.org/2012/01/15/beastfollowup.html
var m int
if len(b) > 1 && c.vers <= versionTLS10 {
if _, ok := c.out.cipher.(cipher.BlockMode); ok {
n, err := c.writeRecord(recordTypeApplicationData, b[:1])
if err != nil {
return n, c.setError(err)
}
m, b = 1, b[1:]
}
}
n, err := c.writeRecord(recordTypeApplicationData, b)
return n, c.setError(err)
return n + m, c.setError(err)
}
// Read can be made to time out and return a net.Error with Timeout() == true
......
......@@ -155,7 +155,7 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error)
err = errors.New("crypto/tls: failed to parse key PEM data")
return
}
if strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
break
}
}
......
......@@ -33,6 +33,19 @@ D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g==
-----END RSA PRIVATE KEY-----
`
// keyPEM is the same as rsaKeyPEM, but declares itself as just
// "PRIVATE KEY", not "RSA PRIVATE KEY". http://golang.org/issue/4477
var keyPEM = `-----BEGIN PRIVATE KEY-----
MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo
k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G
6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N
MQIhAPW+eyZo7ay3lMz1V01WVjNKK9QSn1MJlb06h/LuYv9FAiEA25WPedKgVyCW
SmUwbPw8fnTcpqDWE3yTO3vKcebqMSsCIBF3UmVue8YU3jybC3NxuXq3wNm34R8T
xVLHwDXh/6NJAiEAl2oHGGLz64BuAfjKrqwz7qMYr9HCLIe/YsoWq/olzScCIQDi
D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g==
-----END PRIVATE KEY-----
`
var ecdsaCertPEM = `-----BEGIN CERTIFICATE-----
MIIB/jCCAWICCQDscdUxw16XFDAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw
EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0
......@@ -62,21 +75,22 @@ kohxS/xfFg/TEwRSSws+roJr4JFKpO2t3/be5OdqmQ==
var keyPairTests = []struct {
algo string
cert *string
key *string
cert string
key string
}{
{"ECDSA", &ecdsaCertPEM, &ecdsaKeyPEM},
{"RSA", &rsaCertPEM, &rsaKeyPEM},
{"ECDSA", ecdsaCertPEM, ecdsaKeyPEM},
{"RSA", rsaCertPEM, rsaKeyPEM},
{"RSA-untyped", rsaCertPEM, keyPEM}, // golang.org/issue/4477
}
func TestX509KeyPair(t *testing.T) {
var pem []byte
for _, test := range keyPairTests {
pem = []byte(*test.cert + *test.key)
pem = []byte(test.cert + test.key)
if _, err := X509KeyPair(pem, pem); err != nil {
t.Errorf("Failed to load %s cert followed by %s key: %s", test.algo, test.algo, err)
}
pem = []byte(*test.key + *test.cert)
pem = []byte(test.key + test.cert)
if _, err := X509KeyPair(pem, pem); err != nil {
t.Errorf("Failed to load %s key followed by %s cert: %s", test.algo, test.algo, err)
}
......
......@@ -184,7 +184,7 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error {
// values, or a pointer to such data.
// Bytes written to w are encoded using the specified byte order
// and read from successive fields of the data.
// When writing structs, zero values are are written for fields
// When writing structs, zero values are written for fields
// with blank (_) field names.
func Write(w io.Writer, order ByteOrder, data interface{}) error {
// Fast path for basic types.
......
......@@ -22,7 +22,7 @@ import (
//
// If UseCRLF is true, the Writer ends each record with \r\n instead of \n.
type Writer struct {
Comma rune // Field delimiter (set to to ',' by NewWriter)
Comma rune // Field delimiter (set to ',' by NewWriter)
UseCRLF bool // True to use \r\n as the line terminator
w *bufio.Writer
}
......
......@@ -328,7 +328,7 @@ reserved).
01 // Add 1 to get field number 0: field[1].name
01 // 1 byte
59 // structType.field[1].name = "Y"
01 // Add 1 to get field number 1: field[0].id
01 // Add 1 to get field number 1: field[1].id
04 // struct.Type.field[1].typeId is 2 (signed int).
00 // End of structType.field[1]; end of structType.field.
00 // end of wireType.structType structure
......
......@@ -50,6 +50,7 @@ func BenchmarkEndToEndByteBuffer(b *testing.B) {
}
func TestCountEncodeMallocs(t *testing.T) {
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
var buf bytes.Buffer
enc := NewEncoder(&buf)
bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")}
......@@ -69,6 +70,7 @@ func TestCountEncodeMallocs(t *testing.T) {
}
func TestCountDecodeMallocs(t *testing.T) {
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
var buf bytes.Buffer
enc := NewEncoder(&buf)
bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")}
......
......@@ -45,7 +45,7 @@ const (
// - a field with tag "name,attr" becomes an attribute with
// the given name in the XML element.
// - a field with tag ",attr" becomes an attribute with the
// field name in the in the XML element.
// field name in the XML element.
// - a field with tag ",chardata" is written as character data,
// not as an XML element.
// - a field with tag ",innerxml" is written verbatim, not subject
......
// 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 cookiejar implements an RFC 6265-compliant http.CookieJar.
//
// TODO: example code to create a memory-backed cookie jar with the default
// public suffix list.
package cookiejar
import (
"net/http"
"net/url"
)
// PublicSuffixList provides the public suffix of a domain. For example:
// - the public suffix of "example.com" is "com",
// - the public suffix of "foo1.foo2.foo3.co.uk" is "co.uk", and
// - the public suffix of "bar.pvt.k12.wy.us" is "pvt.k12.wy.us".
//
// Implementations of PublicSuffixList must be safe for concurrent use by
// multiple goroutines.
//
// An implementation that always returns "" is valid and may be useful for
// testing but it is not secure: it means that the HTTP server for foo.com can
// set a cookie for bar.com.
type PublicSuffixList interface {
// PublicSuffix returns the public suffix of domain.
//
// TODO: specify which of the caller and callee is responsible for IP
// addresses, for leading and trailing dots, for case sensitivity, and
// for IDN/Punycode.
PublicSuffix(domain string) string
// String returns a description of the source of this public suffix list.
// A Jar will store its PublicSuffixList's description in its storage,
// and update the stored cookies if its list has a different description
// than the stored list. The description will typically contain something
// like a time stamp or version number.
String() string
}
// Options are the options for creating a new Jar.
type Options struct {
// Storage is the cookie jar storage. It may not be nil.
Storage Storage
// PublicSuffixList is the public suffix list that determines whether an
// HTTP server can set a cookie for a domain. It may not be nil.
PublicSuffixList PublicSuffixList
// TODO: ErrorFunc for handling storage errors?
}
// Jar implements the http.CookieJar interface from the net/http package.
type Jar struct {
storage Storage
psList PublicSuffixList
}
// New returns a new cookie jar.
func New(o *Options) *Jar {
return &Jar{
storage: o.Storage,
psList: o.PublicSuffixList,
}
}
// TODO(nigeltao): how do we reject HttpOnly cookies? Do we post-process the
// return value from Jar.Cookies?
//
// HttpOnly cookies are those for regular HTTP(S) requests but should not be
// visible from JavaScript. The HttpOnly bit mitigates XSS attacks; it's not
// for HTTP vs HTTPS vs FTP transports.
// Cookies implements the Cookies method of the http.CookieJar interface.
//
// It returns an empty slice if the URL's scheme is not HTTP or HTTPS.
func (j *Jar) Cookies(u *url.URL) []*http.Cookie {
// TODO.
return nil
}
// SetCookies implements the SetCookies method of the http.CookieJar interface.
//
// It does nothing if the URL's scheme is not HTTP or HTTPS.
func (j *Jar) SetCookies(u *url.URL, cookies []*http.Cookie) {
// TODO.
}
// 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 cookiejar
import (
"time"
)
// Storage is a Jar's storage. It is a multi-map, mapping keys to one or more
// entries. Each entry consists of a subkey, creation time, last access time,
// and some arbitrary data.
//
// The Add and Delete methods have undefined behavior if the key is invalid.
// A valid key must use only bytes in the character class [a-z0-9.-] and
// must have at least one non-. byte. Note that this excludes any key
// containing a capital ASCII letter as well as the empty string.
type Storage interface {
// A client must call Lock before calling other methods and must call
// Unlock when finished. Between the calls to Lock and Unlock, a client
// can assume that other clients are not modifying the Storage.
Lock()
Unlock()
// Add adds entries to the storage. Each entry's Subkey and Data must
// both be non-empty.
//
// If the Storage already contains an entry with the same key and
// subkey then the new entry is stored with the creation time of the
// old entry, and the old entry is deleted.
//
// Adding entries may cause other entries to be deleted, to maintain an
// implementation-specific storage constraint.
Add(key string, entries ...Entry) error
// Delete deletes all entries for the given key.
Delete(key string) error
// Entries calls f for each entry stored for that key. If f returns a
// non-nil error then the iteration stops and Entries returns that
// error. Iteration is not guaranteed to be in any particular order.
//
// If f returns an Update action then that stored entry's LastAccess
// time will be set to the time that f returned. If f returns a Delete
// action then that entry will be deleted from the Storage.
//
// f may call a Storage's Add and Delete methods; those modifications
// will not affect the list of entries visited in this call to Entries.
Entries(key string, f func(entry Entry) (Action, time.Time, error)) error
// Keys calls f for each key stored. f will not be called on a key with
// zero entries. If f returns a non-nil error then the iteration stops
// and Keys returns that error. Iteration is not guaranteed to be in any
// particular order.
//
// f may call a Storage's Add, Delete and Entries methods; those
// modifications will not affect the list of keys visited in this call
// to Keys.
Keys(f func(key string) error) error
}
// Entry is an entry in a Storage.
type Entry struct {
Subkey string
Data string
Creation time.Time
LastAccess time.Time
}
// Action is an action returned by the function passed to Entries.
type Action int
const (
// Pass means to take no further action with an Entry.
Pass Action = iota
// Update means to update the LastAccess time of an Entry.
Update
// Delete means to delete an Entry.
Delete
)
// ValidStorageKey returns whether the given key is valid for a Storage.
func ValidStorageKey(key string) bool {
hasNonDot := false
for i := 0; i < len(key); i++ {
switch c := key[i]; {
case 'a' <= c && c <= 'z':
fallthrough
case '0' <= c && c <= '9':
fallthrough
case c == '-':
hasNonDot = true
case c == '.':
// No-op.
default:
return false
}
}
return hasNonDot
}
// 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 cookiejar
import (
"testing"
)
var validStorageKeyTests = map[string]bool{
"": false,
".": false,
"..": false,
"/": false,
"EXAMPLE.com": false,
"\n": false,
"\r": false,
"\r\n": false,
"\x00": false,
"back\\slash": false,
"co:lon": false,
"com,ma": false,
"semi;colon": false,
"sl/ash": false,
"sp ace": false,
"under_score": false,
"π": false,
"-": true,
".dot": true,
".dot.": true,
".metadata": true,
".x..y..z...": true,
"dot.": true,
"example.com": true,
"foo": true,
"hy-phen": true,
"xn--bcher-kva.ch": true,
}
func TestValidStorageKey(t *testing.T) {
for key, want := range validStorageKeyTests {
if got := ValidStorageKey(key); got != want {
t.Errorf("%q: got %v, want %v", key, got, want)
}
}
}
......@@ -5,20 +5,38 @@
package main
import (
"go/build"
"path/filepath"
"runtime"
"strings"
"testing"
)
func runTest(t *testing.T, path, pkg string) {
func runTest(t *testing.T, path string) {
exitCode = 0
*pkgName = pkg
*recursive = false
if pkg == "" {
*recursive = false
if suffix := ".go"; strings.HasSuffix(path, suffix) {
// single file
path = filepath.Join(runtime.GOROOT(), "src/pkg", path)
path, file := filepath.Split(path)
*pkgName = file[:len(file)-len(suffix)]
processFiles([]string{path}, true)
} else {
processDirectory(path)
// package directory
// TODO(gri) gotype should use the build package instead
pkg, err := build.Import(path, "", 0)
if err != nil {
t.Errorf("build.Import error for path = %s: %s", path, err)
return
}
// TODO(gri) there ought to be a more direct way using the build package...
files := make([]string, len(pkg.GoFiles))
for i, file := range pkg.GoFiles {
files[i] = filepath.Join(pkg.Dir, file)
}
*pkgName = pkg.Name
processFiles(files, true)
}
if exitCode != 0 {
......@@ -26,26 +44,167 @@ func runTest(t *testing.T, path, pkg string) {
}
}
var tests = []struct {
path string
pkg string
}{
var tests = []string{
// individual files
{"testdata/test1.go", ""},
"exp/gotype/testdata/test1.go",
// directories
{filepath.Join(runtime.GOROOT(), "src/pkg/go/ast"), "ast"},
{filepath.Join(runtime.GOROOT(), "src/pkg/go/build"), "build"},
{filepath.Join(runtime.GOROOT(), "src/pkg/go/doc"), "doc"},
{filepath.Join(runtime.GOROOT(), "src/pkg/go/parser"), "parser"},
{filepath.Join(runtime.GOROOT(), "src/pkg/go/printer"), "printer"},
{filepath.Join(runtime.GOROOT(), "src/pkg/go/scanner"), "scanner"},
{filepath.Join(runtime.GOROOT(), "src/pkg/go/token"), "token"},
{filepath.Join(runtime.GOROOT(), "src/pkg/exp/types"), "types"},
// Note: packages that don't typecheck yet are commented out
// "archive/tar", // investigate
"archive/zip",
"bufio",
"bytes",
"compress/bzip2",
"compress/flate",
"compress/gzip",
"compress/lzw",
"compress/zlib",
"container/heap",
"container/list",
"container/ring",
"crypto",
"crypto/aes",
"crypto/cipher",
"crypto/des",
"crypto/dsa",
"crypto/ecdsa",
"crypto/elliptic",
"crypto/hmac",
"crypto/md5",
"crypto/rand",
"crypto/rc4",
// "crypto/rsa", // investigate (GOARCH=386)
"crypto/sha1",
"crypto/sha256",
"crypto/sha512",
"crypto/subtle",
"crypto/tls",
// "crypto/x509", // investigate
"crypto/x509/pkix",
"database/sql",
"database/sql/driver",
"debug/dwarf",
"debug/elf",
"debug/gosym",
"debug/macho",
"debug/pe",
"encoding/ascii85",
"encoding/asn1",
"encoding/base32",
"encoding/base64",
// "encoding/binary", // complex() doesn't work yet
"encoding/csv",
// "encoding/gob", // complex() doesn't work yet
"encoding/hex",
"encoding/json",
"encoding/pem",
"encoding/xml",
"errors",
"expvar",
"flag",
"fmt",
"exp/types",
"exp/gotype",
"go/ast",
"go/build",
// "go/doc", // variadic parameters don't work yet fully
"go/format",
"go/parser",
"go/printer",
"go/scanner",
"go/token",
"hash/adler32",
// "hash/crc32", // investigate
"hash/crc64",
"hash/fnv",
"image",
"image/color",
"image/draw",
"image/gif",
"image/jpeg",
"image/png",
"index/suffixarray",
"io",
// "io/ioutil", // investigate
"log",
"log/syslog",
"math",
// "math/big", // investigate
// "math/cmplx", // complex doesn't work yet
"math/rand",
"mime",
"mime/multipart",
// "net", // depends on C files
"net/http",
"net/http/cgi",
// "net/http/fcgi", // investigate
"net/http/httptest",
"net/http/httputil",
// "net/http/pprof", // investigate
"net/mail",
// "net/rpc", // investigate
"net/rpc/jsonrpc",
"net/smtp",
"net/textproto",
"net/url",
// "path", // variadic parameters don't work yet fully
// "path/filepath", // investigate
// "reflect", // investigate
"regexp",
"regexp/syntax",
"runtime",
// "runtime/cgo", // import "C"
"runtime/debug",
"runtime/pprof",
"sort",
// "strconv", // investigate
"strings",
// "sync", // platform-specific files
// "sync/atomic", // platform-specific files
// "syscall", // platform-specific files
"testing",
"testing/iotest",
"testing/quick",
"text/scanner",
"text/tabwriter",
// "text/template", // variadic parameters don't work yet fully
// "text/template/parse", // variadic parameters don't work yet fully
// "time", // platform-specific files
"unicode",
"unicode/utf16",
"unicode/utf8",
}
func Test(t *testing.T) {
for _, test := range tests {
runTest(t, test.path, test.pkg)
runTest(t, test)
}
}
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
package test1
func _() {
// the scope of a local type declaration starts immediately after the type name
......
......@@ -450,7 +450,7 @@ func (c *Collator) keyFromElems(buf *Buffer, ws []colElem) {
}
// Derive the quaternary weights from the options and other levels.
// Note that we represent maxQuaternary as 0xFF. The first byte of the
// representation of a a primary weight is always smaller than 0xFF,
// representation of a primary weight is always smaller than 0xFF,
// so using this single byte value will compare correctly.
if Quaternary <= c.Strength && c.Alternate >= AltShifted {
if c.Alternate == AltShiftTrimmed {
......
......@@ -36,7 +36,7 @@ type checker struct {
//
// TODO(gri) This is very similar to the declare function in go/parser; it
// is only used to associate methods with their respective receiver base types.
// In a future version, it might be simpler and cleaner do to all the resolution
// In a future version, it might be simpler and cleaner to do all the resolution
// in the type-checking phase. It would simplify the parser, AST, and also
// reduce some amount of code duplication.
//
......@@ -188,14 +188,13 @@ func (check *checker) object(obj *ast.Object, cycleOk bool) {
case ast.Fun:
fdecl := obj.Decl.(*ast.FuncDecl)
if fdecl.Recv != nil {
// This will ensure that the method base type is
// type-checked
check.collectFields(token.FUNC, fdecl.Recv, true)
}
check.collectParams(fdecl.Recv, false) // ensure method base is type-checked
ftyp := check.typ(fdecl.Type, cycleOk).(*Signature)
obj.Type = ftyp
check.function(ftyp, fdecl.Body)
// functions implemented elsewhere (say in assembly) have no body
if fdecl.Body != nil {
check.function(ftyp, fdecl.Body)
}
default:
panic("unreachable")
......@@ -355,12 +354,19 @@ func check(fset *token.FileSet, pkg *ast.Package, errh func(token.Pos, string),
check.mapf = f
check.initexprs = make(map[*ast.ValueSpec][]ast.Expr)
// handle bailouts
// handle panics
defer func() {
if p := recover(); p != nil {
_ = p.(bailout) // re-panic if not a bailout
switch p := recover().(type) {
case nil:
// normal return - nothing to do
case bailout:
// early exit
err = check.firsterr
default:
// unexpected panic: don't crash clients
// panic(p) // enable for debugging
err = fmt.Errorf("types.check internal error: %v", p)
}
err = check.firsterr
}()
// determine missing constant initialization expressions
......
......@@ -49,6 +49,7 @@ var tests = []struct {
{"decls0", []string{"testdata/decls0.src"}},
{"decls1", []string{"testdata/decls1.src"}},
{"decls2", []string{"testdata/decls2a.src", "testdata/decls2b.src"}},
{"decls3", []string{"testdata/decls3.src"}},
{"const0", []string{"testdata/const0.src"}},
{"expr0", []string{"testdata/expr0.src"}},
{"expr1", []string{"testdata/expr1.src"}},
......
......@@ -278,7 +278,7 @@ func isRepresentableConst(x interface{}, as BasicKind) bool {
return as == String || as == UntypedString
case nilType:
return as == UntypedNil
return as == UntypedNil || as == UnsafePointer
default:
unreachable()
......
......@@ -119,21 +119,15 @@ func (x *operand) setConst(tok token.Token, lit string) {
}
}
// implements reports whether x implements interface T.
func (x *operand) implements(T *Interface) bool {
if x.mode == invalid {
return true // avoid spurious errors
}
unimplemented()
return true
}
// isNil reports whether x is the predeclared nil constant.
func (x *operand) isNil() bool {
return x.mode == constant && x.val == nilConst
}
// TODO(gri) The functions operand.isAssignable, checker.convertUntyped,
// checker.isRepresentable, and checker.assignOperand are
// overlapping in functionality. Need to simplify and clean up.
// isAssignable reports whether x is assignable to a variable of type T.
func (x *operand) isAssignable(T Type) bool {
if x.mode == invalid || T == Typ[Invalid] {
......@@ -157,8 +151,10 @@ func (x *operand) isAssignable(T Type) bool {
}
// T is an interface type and x implements T
if Ti, ok := Tu.(*Interface); ok && x.implements(Ti) {
return true
if Ti, ok := Tu.(*Interface); ok {
if m, _ := missingMethod(x.typ, Ti); m == nil {
return true
}
}
// x is a bidirectional channel value, T is a channel
......@@ -181,8 +177,18 @@ func (x *operand) isAssignable(T Type) bool {
}
// x is an untyped constant representable by a value of type T
// - this is taken care of in the assignment check
// TODO(gri) double-check - isAssignable is used elsewhere
// TODO(gri) This is borrowing from checker.convertUntyped and
// checker.isRepresentable. Need to clean up.
if isUntyped(Vu) {
switch t := Tu.(type) {
case *Basic:
return x.mode == constant && isRepresentableConst(x.val, t.Kind)
case *Interface:
return x.isNil() || len(t.Methods) == 0
case *Pointer, *Signature, *Slice, *Map, *Chan:
return x.isNil()
}
}
return false
}
......@@ -199,35 +205,50 @@ type lookupResult struct {
typ Type
}
// lookupFieldRecursive is similar to FieldByNameFunc in reflect/type.go
// TODO(gri): FieldByNameFunc seems more complex - what are we missing?
func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) {
// visited records the types that have been searched already
visited := make(map[Type]bool)
type embeddedType struct {
typ *NamedType
multiples bool // if set, typ is embedded multiple times at the same level
}
// lookupFieldBreadthFirst searches all types in list for a single entry (field
// or method) of the given name. If such a field is found, the result describes
// the field mode and type; otherwise the result mode is invalid.
// (This function is similar in structure to FieldByNameFunc in reflect/type.go)
//
func lookupFieldBreadthFirst(list []embeddedType, name string) (res lookupResult) {
// visited records the types that have been searched already.
visited := make(map[*NamedType]bool)
// embedded types of the next lower level
var next []*NamedType
var next []embeddedType
potentialMatch := func(mode operandMode, typ Type) bool {
if res.mode != invalid {
// name appeared multiple times at this level - annihilate
// potentialMatch is invoked every time a match is found.
potentialMatch := func(multiples bool, mode operandMode, typ Type) bool {
if multiples || res.mode != invalid {
// name appeared already at this level - annihilate
res.mode = invalid
return false
}
// first appearance of name
res.mode = mode
res.typ = typ
return true
}
// look for name in all types of this level
// Search the current level if there is any work to do and collect
// embedded types of the next lower level in the next list.
for len(list) > 0 {
// The res.mode indicates whether we have found a match already
// on this level (mode != invalid), or not (mode == invalid).
assert(res.mode == invalid)
for _, typ := range list {
// start with empty next list (don't waste underlying array)
next = next[:0]
// look for name in all types at this level
for _, e := range list {
typ := e.typ
if visited[typ] {
// We have seen this type before, at a higher level.
// That higher level shadows the lower level we are
// at now, and either we would have found or not
// found the field before. Ignore this type now.
continue
}
visited[typ] = true
......@@ -236,7 +257,7 @@ func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) {
if data := typ.Obj.Data; data != nil {
if obj := data.(*ast.Scope).Lookup(name); obj != nil {
assert(obj.Type != nil)
if !potentialMatch(value, obj.Type.(Type)) {
if !potentialMatch(e.multiples, value, obj.Type.(Type)) {
return // name collision
}
}
......@@ -244,21 +265,26 @@ func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) {
switch typ := underlying(typ).(type) {
case *Struct:
// look for a matching fieldm and collect embedded types
// look for a matching field and collect embedded types
for _, f := range typ.Fields {
if f.Name == name {
assert(f.Type != nil)
if !potentialMatch(variable, f.Type) {
if !potentialMatch(e.multiples, variable, f.Type) {
return // name collision
}
continue
}
// Collect embedded struct fields for searching the next
// lower level, but only if we have not seen a match yet.
// lower level, but only if we have not seen a match yet
// (if we have a match it is either the desired field or
// we have a name collision on the same level; in either
// case we don't need to look further).
// Embedded fields are always of the form T or *T where
// T is a named type.
// T is a named type. If typ appeared multiple times at
// this level, f.Type appears multiple times at the next
// level.
if f.IsAnonymous && res.mode == invalid {
next = append(next, deref(f.Type).(*NamedType))
next = append(next, embeddedType{deref(f.Type).(*NamedType), e.multiples})
}
}
......@@ -267,7 +293,7 @@ func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) {
for _, obj := range typ.Methods {
if obj.Name == name {
assert(obj.Type != nil)
if !potentialMatch(value, obj.Type.(Type)) {
if !potentialMatch(e.multiples, value, obj.Type.(Type)) {
return // name collision
}
}
......@@ -276,17 +302,41 @@ func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) {
}
if res.mode != invalid {
// we found a match on this level
// we found a single match on this level
return
}
// search the next level
list = append(list[:0], next...) // don't waste underlying arrays
next = next[:0]
// No match and no collision so far.
// Compute the list to search for the next level.
list = list[:0] // don't waste underlying array
for _, e := range next {
// Instead of adding the same type multiple times, look for
// it in the list and mark it as multiple if it was added
// before.
// We use a sequential search (instead of a map for next)
// because the lists tend to be small, can easily be reused,
// and explicit search appears to be faster in this case.
if alt := findType(list, e.typ); alt != nil {
alt.multiples = true
} else {
list = append(list, e)
}
}
}
return
}
func findType(list []embeddedType, typ *NamedType) *embeddedType {
for i := range list {
if p := &list[i]; p.typ == typ {
return p
}
}
return nil
}
func lookupField(typ Type, name string) (operandMode, Type) {
typ = deref(typ)
......@@ -301,17 +351,20 @@ func lookupField(typ Type, name string) (operandMode, Type) {
switch typ := underlying(typ).(type) {
case *Struct:
var list []*NamedType
var next []embeddedType
for _, f := range typ.Fields {
if f.Name == name {
return variable, f.Type
}
if f.IsAnonymous {
list = append(list, deref(f.Type).(*NamedType))
// Possible optimization: If the embedded type
// is a pointer to the current type we could
// ignore it.
next = append(next, embeddedType{typ: deref(f.Type).(*NamedType)})
}
}
if len(list) > 0 {
res := lookupFieldRecursive(list, name)
if len(next) > 0 {
res := lookupFieldBreadthFirst(next, name)
return res.mode, res.typ
}
......
......@@ -6,6 +6,8 @@
package types
import "go/ast"
func isNamed(typ Type) bool {
if _, ok := typ.(*Basic); ok {
return ok
......@@ -247,3 +249,34 @@ func defaultType(typ Type) Type {
}
return typ
}
// missingMethod returns (nil, false) if typ implements T, otherwise
// it returns the first missing method required by T and whether it
// is missing or simply has the wrong type.
//
func missingMethod(typ Type, T *Interface) (method *ast.Object, wrongType bool) {
// TODO(gri): distinguish pointer and non-pointer receivers
// an interface type implements T if it has no methods with conflicting signatures
// Note: This is stronger than the current spec. Should the spec require this?
if ityp, _ := underlying(typ).(*Interface); ityp != nil {
for _, m := range T.Methods {
mode, sig := lookupField(ityp, m.Name) // TODO(gri) no need to go via lookupField
if mode != invalid && !isIdentical(sig, m.Type.(Type)) {
return m, true
}
}
return
}
// a concrete type implements T if it implements all methods of T.
for _, m := range T.Methods {
mode, sig := lookupField(typ, m.Name)
if mode == invalid {
return m, false
}
if !isIdentical(sig, m.Type.(Type)) {
return m, true
}
}
return
}
......@@ -27,8 +27,8 @@ func (check *checker) assignOperand(z, x *operand) {
}
}
// assign1to1 typechecks a single assignment of the form lhs := rhs (if rhs != nil),
// or lhs := x (if rhs == nil). If decl is set, the lhs operand must be an identifier.
// assign1to1 typechecks a single assignment of the form lhs = rhs (if rhs != nil),
// or lhs = x (if rhs == nil). If decl is set, the lhs operand must be an identifier.
// If its type is not set, it is deduced from the type or value of x. If lhs has a
// type it is used as a hint when evaluating rhs, if present.
//
......@@ -226,6 +226,38 @@ func (check *checker) stmtList(list []ast.Stmt) {
}
}
func (check *checker) call(call *ast.CallExpr) {
var x operand
check.rawExpr(&x, call, nil, -1, false) // don't check if value is used
// TODO(gri) If a builtin is called, the builtin must be valid in statement context.
}
func (check *checker) multipleDefaults(list []ast.Stmt) {
var first ast.Stmt
for _, s := range list {
var d ast.Stmt
switch c := s.(type) {
case *ast.CaseClause:
if len(c.List) == 0 {
d = s
}
case *ast.CommClause:
if c.Comm == nil {
d = s
}
default:
check.invalidAST(s.Pos(), "case/communication clause expected")
}
if d != nil {
if first != nil {
check.errorf(d.Pos(), "multiple defaults (first at %s)", first.Pos())
} else {
first = d
}
}
}
}
// stmt typechecks statement s.
func (check *checker) stmt(s ast.Stmt) {
switch s := s.(type) {
......@@ -265,7 +297,7 @@ func (check *checker) stmt(s ast.Stmt) {
}
check.rawExpr(&x, s.X, nil, -1, false)
if x.mode == typexpr {
check.errorf(x.pos(), "%s is not an expression", x)
check.errorf(x.pos(), "%s is not an expression", &x)
}
case *ast.SendStmt:
......@@ -347,10 +379,10 @@ func (check *checker) stmt(s ast.Stmt) {
}
case *ast.GoStmt:
unimplemented()
check.call(s.Call)
case *ast.DeferStmt:
unimplemented()
check.call(s.Call)
case *ast.ReturnStmt:
sig := check.functypes[len(check.functypes)-1]
......@@ -403,31 +435,122 @@ func (check *checker) stmt(s ast.Stmt) {
x.typ = Typ[UntypedBool]
x.val = true
}
check.multipleDefaults(s.Body.List)
for _, s := range s.Body.List {
if clause, ok := s.(*ast.CaseClause); ok {
for _, expr := range clause.List {
var y operand
check.expr(&y, expr, nil, -1)
// TODO(gri) x and y must be comparable
}
check.stmtList(clause.Body)
} else {
check.errorf(s.Pos(), "invalid AST: case clause expected")
clause, _ := s.(*ast.CaseClause)
if clause == nil {
continue // error reported before
}
for _, expr := range clause.List {
var y operand
check.expr(&y, expr, nil, -1)
// TODO(gri) x and y must be comparable
}
check.stmtList(clause.Body)
}
case *ast.TypeSwitchStmt:
unimplemented()
check.optionalStmt(s.Init)
// A type switch guard must be of the form:
//
// TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
//
// The parser is checking syntactic correctness;
// remaining syntactic errors are considered AST errors here.
// TODO(gri) better factoring of error handling (invalid ASTs)
//
var lhs *ast.Object // lhs identifier object or nil
var rhs ast.Expr
switch guard := s.Assign.(type) {
case *ast.ExprStmt:
rhs = guard.X
case *ast.AssignStmt:
if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 {
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
return
}
ident, _ := guard.Lhs[0].(*ast.Ident)
if ident == nil {
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
return
}
lhs = ident.Obj
rhs = guard.Rhs[0]
default:
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
return
}
// rhs must be of the form: expr.(type) and expr must be an interface
expr, _ := rhs.(*ast.TypeAssertExpr)
if expr == nil || expr.Type != nil {
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
return
}
var x operand
check.expr(&x, expr.X, nil, -1)
if x.mode == invalid {
return
}
var T *Interface
if T, _ = underlying(x.typ).(*Interface); T == nil {
check.errorf(x.pos(), "%s is not an interface", &x)
return
}
check.multipleDefaults(s.Body.List)
for _, s := range s.Body.List {
clause, _ := s.(*ast.CaseClause)
if clause == nil {
continue // error reported before
}
// Check each type in this type switch case.
var typ Type
for _, expr := range clause.List {
typ = check.typOrNil(expr, false)
if typ != nil && typ != Typ[Invalid] {
if method, wrongType := missingMethod(typ, T); method != nil {
var msg string
if wrongType {
msg = "%s cannot have dynamic type %s (wrong type for method %s)"
} else {
msg = "%s cannot have dynamic type %s (missing method %s)"
}
check.errorf(expr.Pos(), msg, &x, typ, method.Name)
// ok to continue
}
}
}
// If lhs exists, set its type for each clause.
if lhs != nil {
// In clauses with a case listing exactly one type, the variable has that type;
// otherwise, the variable has the type of the expression in the TypeSwitchGuard.
if len(clause.List) != 1 || typ == nil {
typ = x.typ
}
lhs.Type = typ
}
check.stmtList(clause.Body)
}
// There is only one object (lhs) associated with a lhs identifier, but that object
// assumes different types for different clauses. Set it to nil when we are done so
// that the type cannot be used by mistake.
if lhs != nil {
lhs.Type = nil
}
case *ast.SelectStmt:
check.multipleDefaults(s.Body.List)
for _, s := range s.Body.List {
c, ok := s.(*ast.CommClause)
if !ok {
check.invalidAST(s.Pos(), "communication clause expected")
continue
clause, _ := s.(*ast.CommClause)
if clause == nil {
continue // error reported before
}
check.optionalStmt(c.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt)
check.stmtList(c.Body)
check.optionalStmt(clause.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt)
check.stmtList(clause.Body)
}
case *ast.ForStmt:
......@@ -443,7 +566,79 @@ func (check *checker) stmt(s ast.Stmt) {
check.stmt(s.Body)
case *ast.RangeStmt:
unimplemented()
// check expression to iterate over
decl := s.Tok == token.DEFINE
var x operand
check.expr(&x, s.X, nil, -1)
if x.mode == invalid {
// if we don't have a declaration, we can still check the loop's body
if !decl {
check.stmt(s.Body)
}
return
}
// determine key/value types
var key, val Type
switch typ := underlying(x.typ).(type) {
case *Basic:
if isString(typ) {
key = Typ[UntypedInt]
val = Typ[UntypedRune]
}
case *Array:
key = Typ[UntypedInt]
val = typ.Elt
case *Slice:
key = Typ[UntypedInt]
val = typ.Elt
case *Pointer:
if typ, _ := underlying(typ.Base).(*Array); typ != nil {
key = Typ[UntypedInt]
val = typ.Elt
}
case *Map:
key = typ.Key
val = typ.Elt
case *Chan:
key = typ.Elt
if typ.Dir&ast.RECV == 0 {
check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
// ok to continue
}
if s.Value != nil {
check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x)
// ok to continue
}
}
if key == nil {
check.errorf(x.pos(), "cannot range over %s", &x)
// if we don't have a declaration, we can still check the loop's body
if !decl {
check.stmt(s.Body)
}
return
}
// check assignment to/declaration of iteration variables
// TODO(gri) The error messages/positions are not great here,
// they refer to the expression in the range clause.
// Should give better messages w/o too much code
// duplication (assignment checking).
if s.Key != nil {
x.typ = key
check.assign1to1(s.Key, nil, &x, decl, -1)
} else {
check.invalidAST(s.Pos(), "range clause requires index iteration variable")
// ok to continue
}
if s.Value != nil {
x.typ = val
check.assign1to1(s.Value, nil, &x, decl, -1)
}
check.stmt(s.Body)
default:
check.errorf(s.Pos(), "invalid statement")
......
......@@ -40,8 +40,17 @@ type (
)
// invalid array types
type (
p1 pi /* ERROR "no field or method foo" */ .foo
iA0 [... /* ERROR "invalid use of '...'" */ ]byte
iA1 [1 /* ERROR "invalid array length" */ <<100]int
iA2 [- /* ERROR "invalid array length" */ 1]complex128
iA3 ["foo" /* ERROR "invalid array length" */ ]string
)
type (
p1 pi /* ERROR "no single field or method foo" */ .foo
p2 unsafe.Pointer
)
......@@ -131,7 +140,7 @@ type (
m1(I5)
}
I6 interface {
S0 /* ERROR "non-interface" */
S0 /* ERROR "not an interface" */
}
I7 interface {
I1
......
......@@ -73,7 +73,7 @@ var (
// Various more complex expressions
var (
u1 = x /* ERROR "non-interface type" */ .(int)
u1 = x /* ERROR "not an interface" */ .(int)
u2 = iface.([]int)
u3 = iface.(a /* ERROR "not a type" */ )
u4, ok = iface.(int)
......
......@@ -6,43 +6,43 @@
package expr3
// TODO(gri) Move the code below into function "shifts" once we check
// declarations with initilizations inside functions.
var (
i0 int
u0 uint
)
var (
v0 = 1<<0
v1 = 1<<i0 /* ERROR "must be unsigned" */
v2 = 1<<u0
v3 = 1<<"foo" /* ERROR "must be unsigned" */
v4 = 1<<- /* ERROR "stupid shift" */ 1
v5 = 1<<1025 /* ERROR "stupid shift" */
v6 = 1 /* ERROR "overflows" */ <<100
v10 uint = 1 << 0
v11 uint = 1 << u0
v12 float32 = 1 /* ERROR "must be integer" */ << u0
)
// TODO(gri) enable commented out tests below.
// from the spec
var (
s uint = 33
i = 1<<s // 1 has type int
j int32 = 1<<s // 1 has type int32; j == 0
k = uint64(1<<s) // 1 has type uint64; k == 1<<33
m int = 1.0<<s // 1.0 has type int
// n = 1.0<<s != 0 // 1.0 has type int; n == false if ints are 32bits in size
o = 1<<s == 2<<s // 1 and 2 have type int; o == true if ints are 32bits in size
// p = 1<<s == 1 /* ERROR "overflows" */ <<33 // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int
u = 1.0 /* ERROR "must be integer" */ <<s // illegal: 1.0 has type float64, cannot shift
v float32 = 1 /* ERROR "must be integer" */ <<s // illegal: 1 has type float32, cannot shift
w int64 = 1.0<<33 // 1.0<<33 is a constant shift expression
)
func shifts1() {
var (
i0 int
u0 uint
)
var (
v0 = 1<<0
v1 = 1<<i0 /* ERROR "must be unsigned" */
v2 = 1<<u0
v3 = 1<<"foo" /* ERROR "must be unsigned" */
v4 = 1<<- /* ERROR "stupid shift" */ 1
v5 = 1<<1025 /* ERROR "stupid shift" */
v6 = 1 /* ERROR "overflows" */ <<100
v10 uint = 1 << 0
v11 uint = 1 << u0
v12 float32 = 1 /* ERROR "must be integer" */ << u0
)
}
func shifts2() {
// TODO(gri) enable commented out tests below.
var (
s uint = 33
i = 1<<s // 1 has type int
j int32 = 1<<s // 1 has type int32; j == 0
k = uint64(1<<s) // 1 has type uint64; k == 1<<33
m int = 1.0<<s // 1.0 has type int
// n = 1.0<<s != 0 // 1.0 has type int; n == false if ints are 32bits in size
o = 1<<s == 2<<s // 1 and 2 have type int; o == true if ints are 32bits in size
// p = 1<<s == 1 /* ERROR "overflows" */ <<33 // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int
u = 1.0 /* ERROR "must be integer" */ <<s // illegal: 1.0 has type float64, cannot shift
v float32 = 1 /* ERROR "must be integer" */ <<s // illegal: 1 has type float32, cannot shift
w int64 = 1.0<<33 // 1.0<<33 is a constant shift expression
)
}
// TODO(gri) The error messages below depond on adjusting the spec
// to reflect what gc is doing at the moment (the spec
......@@ -67,11 +67,24 @@ func indexes() {
a1 = a /* ERROR "cannot assign" */ [1]
_ = a[9]
_ = a[10 /* ERROR "index .* out of bounds" */ ]
_ = a[1 /* ERROR "stupid index" */ <<100]
_ = a[10:]
_ = a[:10]
_ = a[10:10]
_ = a[11 /* ERROR "index .* out of bounds" */ :]
_ = a[: 11 /* ERROR "index .* out of bounds" */ ]
_ = a[: 1 /* ERROR "stupid index" */ <<100]
pa := &a
_ = pa[9]
_ = pa[10 /* ERROR "index .* out of bounds" */ ]
_ = pa[1 /* ERROR "stupid index" */ <<100]
_ = pa[10:]
_ = pa[:10]
_ = pa[10:10]
_ = pa[11 /* ERROR "index .* out of bounds" */ :]
_ = pa[: 11 /* ERROR "index .* out of bounds" */ ]
_ = pa[: 1 /* ERROR "stupid index" */ <<100]
var b [0]int
_ = b[0 /* ERROR "index .* out of bounds" */ ]
......@@ -88,11 +101,9 @@ func indexes() {
_ = s[1 : 2]
_ = s[2 /* ERROR "inverted slice range" */ : 1]
_ = s[2 :]
_ = s[: 1<<100]
_ = s[1<<100 :]
_ = s[1<<100 : 1<<100]
_ = s[1 /* ERROR "inverted slice range" */ <<100+1 : 1<<100]
_ = s[1 /* ERROR "inverted slice range" */ <<100+1 : 10]
_ = s[: 1 /* ERROR "stupid index" */ <<100]
_ = s[1 /* ERROR "stupid index" */ <<100 :]
_ = s[1 /* ERROR "stupid index" */ <<100 : 1 /* ERROR "stupid index" */ <<100]
var t string
_ = t[- /* ERROR "index .* negative" */ 1]
......@@ -126,9 +137,152 @@ type T struct {
func (*T) m() {}
func method_expressions() {
_ = T /* ERROR "no field or method" */ .a
_ = T /* ERROR "no single field or method" */ .a
_ = T /* ERROR "has no method" */ .x
_ = T.m
var f func(*T) = (*T).m
var g func(*T) = ( /* ERROR "cannot assign" */ T).m
}
\ No newline at end of file
}
func struct_literals() {
type T0 struct {
a, b, c int
}
type T1 struct {
T0
a, b int
u float64
s string
}
// keyed elements
_ = T1{}
_ = T1{a: 0, 1 /* ERROR "mixture of .* elements" */ }
_ = T1{aa /* ERROR "unknown field" */ : 0}
_ = T1{1 /* ERROR "invalid field name" */ : 0}
_ = T1{a: 0, s: "foo", u: 0, a /* ERROR "duplicate field" */: 10}
_ = T1{a: "foo" /* ERROR "cannot use" */ }
_ = T1{c /* ERROR "unknown field" */ : 0}
_ = T1{T0: { /* ERROR "missing type" */ }}
_ = T1{T0: T0{}}
_ = T1{T0 /* ERROR "invalid field name" */ .a: 0}
// unkeyed elements
_ = T0{1, 2, 3}
_ = T0{1, b /* ERROR "mixture" */ : 2, 3}
_ = T0{1, 2} /* ERROR "too few values" */
_ = T0{1, 2, 3, 4 /* ERROR "too many values" */ }
_ = T0{1, "foo" /* ERROR "cannot use" */, 3.4 /* ERROR "cannot use" */}
}
func array_literals() {
type A0 [0]int
_ = A0{}
_ = A0{0 /* ERROR "index .* out of bounds" */}
_ = A0{0 /* ERROR "index .* out of bounds" */ : 0}
type A1 [10]int
_ = A1{}
_ = A1{0, 1, 2}
_ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
_ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /* ERROR "index .* out of bounds" */ }
_ = A1{- /* ERROR "index .* negative" */ 1: 0}
_ = A1{8: 8, 9}
_ = A1{8: 8, 9, 10 /* ERROR "index .* out of bounds" */ }
_ = A1{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
_ = A1{5: 5, 6, 7, 3: 3, 4}
_ = A1{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ }
_ = A1{10 /* ERROR "index .* out of bounds" */ : 10, 10 /* ERROR "index .* out of bounds" */ : 10}
_ = A1{5: 5, 6, 7, 3: 3, 1 /* ERROR "stupid index" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
_ = A1{5: 5, 6, 7, 4: 4, 1 /* ERROR "stupid index" */ <<100: 4}
_ = A1{2.0}
_ = A1{2.1 /* ERROR "cannot use" */ }
_ = A1{"foo" /* ERROR "cannot use" */ }
a0 := [...]int{}
assert(len(a0) == 0)
a1 := [...]int{0, 1, 2}
assert(len(a1) == 3)
var a13 [3]int
var a14 [4]int
a13 = a1
a14 = a1 /* ERROR "cannot assign" */
a2 := [...]int{- /* ERROR "index .* negative" */ 1: 0}
a3 := [...]int{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
assert(len(a3) == 5) // somewhat arbitrary
a4 := [...]complex128{0, 1, 2, 1<<10-2: -1i, 1i, 400: 10, 12, 14}
assert(len(a4) == 1024)
// from the spec
type Point struct { x, y float32 }
_ = [...]Point{Point{1.5, -3.5}, Point{0, 0}}
_ = [...]Point{{1.5, -3.5}, {0, 0}}
_ = [][]int{[]int{1, 2, 3}, []int{4, 5}}
_ = [][]int{{1, 2, 3}, {4, 5}}
_ = [...]*Point{&Point{1.5, -3.5}, &Point{0, 0}}
_ = [...]*Point{{1.5, -3.5}, {0, 0}}
}
func slice_literals() {
type S0 []int
_ = S0{}
_ = S0{0, 1, 2}
_ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
_ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
_ = S0{- /* ERROR "index .* negative" */ 1: 0}
_ = S0{8: 8, 9}
_ = S0{8: 8, 9, 10}
_ = S0{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
_ = S0{5: 5, 6, 7, 3: 3, 4}
_ = S0{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ }
_ = S0{10: 10, 10 /* ERROR "duplicate index" */ : 10}
_ = S0{5: 5, 6, 7, 3: 3, 1 /* ERROR "stupid index" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
_ = S0{5: 5, 6, 7, 4: 4, 1 /* ERROR "stupid index" */ <<100: 4}
_ = S0{2.0}
_ = S0{2.1 /* ERROR "cannot use" */ }
_ = S0{"foo" /* ERROR "cannot use" */ }
}
func map_literals() {
type M0 map[string]int
_ = M0{}
_ = M0{1 /* ERROR "missing key" */ }
_ = M0{1 /* ERROR "cannot use .* as string key" */ : 2}
_ = M0{"foo": "bar" /* ERROR "cannot use .* as int value" */ }
_ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 }
}
type I interface {
m()
}
type I2 interface {
m(int)
}
type T1 struct{}
type T2 struct{}
func (T2) m(int) {}
func type_asserts() {
var x int
_ = x /* ERROR "not an interface" */ .(int)
var e interface{}
var ok bool
x, ok = e.(int)
var t I
_ = t /* ERROR "use of .* outside type switch" */ .(type)
_ = t.(T)
_ = t.(T1 /* ERROR "missing method m" */ )
_ = t.(T2 /* ERROR "wrong type for method m" */ )
_ = t.(I2 /* ERROR "wrong type for method m" */ )
}
......@@ -71,4 +71,170 @@ func _selects() {
x = t
case <-sc /* ERROR "cannot receive from send-only channel" */ :
}
select {
default:
default /* ERROR "multiple defaults" */ :
}
}
func _gos() {
go 1 /* ERROR "expected function/method call" */
go _gos()
var c chan int
go close(c)
go len(c) // TODO(gri) this should not be legal
}
func _defers() {
defer 1 /* ERROR "expected function/method call" */
defer _defers()
var c chan int
defer close(c)
defer len(c) // TODO(gri) this should not be legal
}
func _switches() {
var x int
switch x {
default:
default /* ERROR "multiple defaults" */ :
}
// TODO(gri) more tests
}
type I interface {
m()
}
type I2 interface {
m(int)
}
type T struct{}
type T1 struct{}
type T2 struct{}
func (T) m() {}
func (T2) m(int) {}
func _typeswitches() {
var i int
var x interface{}
switch x.(type) {}
switch (x /* ERROR "outside type switch" */ .(type)) {}
switch x.(type) {
default:
default /* ERROR "multiple defaults" */ :
}
switch x := x.(type) {}
switch x := x.(type) {
case int:
var y int = x
}
switch x := i /* ERROR "not an interface" */ .(type) {}
switch t := x.(type) {
case nil:
var v bool = t /* ERROR "cannot assign" */
case int:
var v int = t
case float32, complex64:
var v float32 = t /* ERROR "cannot assign" */
default:
var v float32 = t /* ERROR "cannot assign" */
}
var t I
switch t.(type) {
case T:
case T1 /* ERROR "missing method m" */ :
case T2 /* ERROR "wrong type for method m" */ :
case I2 /* ERROR "wrong type for method m" */ :
}
}
func _rangeloops() {
var (
x int
a [10]float32
b []string
p *[10]complex128
pp **[10]complex128
s string
m map[int]bool
c chan int
sc chan<- int
rc <-chan int
)
for _ = range x /* ERROR "cannot range over" */ {}
for i := range x /* ERROR "cannot range over" */ {}
for i := range a {
var ii int
ii = i
}
for i, x := range a {
var ii int
ii = i
var xx float64
xx = x /* ERROR "cannot assign" */
}
var ii int
var xx float32
for ii, xx := range a {}
for i := range b {
var ii int
ii = i
}
for i, x := range b {
var ii int
ii = i
var xx string
xx = x
}
for i := range s {
var ii int
ii = i
}
for i, x := range s {
var ii int
ii = i
var xx rune
xx = x
}
for _, x := range p {
var xx complex128
xx = x
}
for _, x := range pp /* ERROR "cannot range over" */ {}
for k := range m {
var kk int32
kk = k /* ERROR "cannot assign" */
}
for k, v := range m {
var kk int
kk = k
if v {}
}
for _, _ /* ERROR "only one iteration variable" */ = range c {}
for e := range c {
var ee int
ee = e
}
for _ = range sc /* ERROR "cannot range over send-only channel" */ {}
for _ = range rc {}
}
\ No newline at end of file
......@@ -126,6 +126,15 @@ type Struct struct {
Fields []*StructField
}
func (typ *Struct) fieldIndex(name string) int {
for i, f := range typ.Fields {
if f.Name == name {
return i
}
}
return -1
}
// A Pointer represents a pointer type *Base.
type Pointer struct {
implementsType
......
......@@ -116,10 +116,12 @@ func init() {
// error type
{
res := ast.NewObj(ast.Var, "")
res.Type = Typ[String]
err := ast.NewObj(ast.Fun, "Error")
err.Type = &Signature{Results: ObjList{res}}
obj := def(ast.Typ, "error")
// TODO(gri) set up correct interface type
typ := &NamedType{Underlying: &Interface{}, Obj: obj}
obj.Type = typ
obj.Type = &NamedType{Underlying: &Interface{Methods: ObjList{err}}, Obj: obj}
}
// predeclared constants
......
......@@ -9,6 +9,7 @@ package winfsnotify
import (
"io/ioutil"
"os"
"sync/atomic"
"testing"
"time"
)
......@@ -105,14 +106,14 @@ func TestNotifyClose(t *testing.T) {
watcher, _ := NewWatcher()
watcher.Close()
done := false
var done int32
go func() {
watcher.Close()
done = true
atomic.StoreInt32(&done, 1)
}()
time.Sleep(50 * time.Millisecond)
if !done {
if atomic.LoadInt32(&done) == 0 {
t.Fatal("double Close() test failed: second Close() call didn't return")
}
......
......@@ -583,6 +583,7 @@ var mallocTest = []struct {
var _ bytes.Buffer
func TestCountMallocs(t *testing.T) {
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
for _, mt := range mallocTest {
const N = 100
memstats := new(runtime.MemStats)
......
......@@ -20,7 +20,7 @@ func SortImports(fset *token.FileSet, f *File) {
break
}
if d.Lparen == token.NoPos {
if !d.Lparen.IsValid() {
// Not a block: sorted by default.
continue
}
......
// 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 format implements standard formatting of Go source.
package format
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"io"
"strings"
)
var config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
// Node formats node in canonical gofmt style and writes the result to dst.
//
// The node type must be *ast.File, *printer.CommentedNode, []ast.Decl,
// []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec,
// or ast.Stmt. Node does not modify node. Imports are not sorted for
// nodes representing partial source files (i.e., if the node is not an
// *ast.File or a *printer.CommentedNode not wrapping an *ast.File).
//
// The function may return early (before the entire result is written)
// and return a formatting error, for instance due to an incorrect AST.
//
func Node(dst io.Writer, fset *token.FileSet, node interface{}) error {
// Determine if we have a complete source file (file != nil).
var file *ast.File
var cnode *printer.CommentedNode
switch n := node.(type) {
case *ast.File:
file = n
case *printer.CommentedNode:
if f, ok := n.Node.(*ast.File); ok {
file = f
cnode = n
}
}
// Sort imports if necessary.
if file != nil && hasUnsortedImports(file) {
// Make a copy of the AST because ast.SortImports is destructive.
// TODO(gri) Do this more efficently.
var buf bytes.Buffer
err := config.Fprint(&buf, fset, file)
if err != nil {
return err
}
file, err = parser.ParseFile(fset, "", buf.Bytes(), parser.ParseComments)
if err != nil {
// We should never get here. If we do, provide good diagnostic.
return fmt.Errorf("format.Node internal error (%s)", err)
}
ast.SortImports(fset, file)
// Use new file with sorted imports.
node = file
if cnode != nil {
node = &printer.CommentedNode{Node: file, Comments: cnode.Comments}
}
}
return config.Fprint(dst, fset, node)
}
// Source formats src in canonical gofmt style and writes the result to dst
// or returns an I/O or syntax error. src is expected to be a syntactically
// correct Go source file, or a list of Go declarations or statements.
//
// If src is a partial source file, the leading and trailing space of src
// is applied to the result (such that it has the same leading and trailing
// space as src), and the formatted src is indented by the same amount as
// the first line of src containing code. Imports are not sorted for partial
// source files.
//
func Source(src []byte) ([]byte, error) {
fset := token.NewFileSet()
node, err := parse(fset, src)
if err != nil {
return nil, err
}
var buf bytes.Buffer
if file, ok := node.(*ast.File); ok {
// Complete source file.
ast.SortImports(fset, file)
err := config.Fprint(&buf, fset, file)
if err != nil {
return nil, err
}
} else {
// Partial source file.
// Determine and prepend leading space.
i, j := 0, 0
for j < len(src) && isSpace(src[j]) {
if src[j] == '\n' {
i = j + 1 // index of last line in leading space
}
j++
}
buf.Write(src[:i])
// Determine indentation of first code line.
// Spaces are ignored unless there are no tabs,
// in which case spaces count as one tab.
indent := 0
hasSpace := false
for _, b := range src[i:j] {
switch b {
case ' ':
hasSpace = true
case '\t':
indent++
}
}
if indent == 0 && hasSpace {
indent = 1
}
// Format the source.
cfg := config
cfg.Indent = indent
err := cfg.Fprint(&buf, fset, node)
if err != nil {
return nil, err
}
// Determine and append trailing space.
i = len(src)
for i > 0 && isSpace(src[i-1]) {
i--
}
buf.Write(src[i:])
}
return buf.Bytes(), nil
}
func hasUnsortedImports(file *ast.File) bool {
for _, d := range file.Decls {
d, ok := d.(*ast.GenDecl)
if !ok || d.Tok != token.IMPORT {
// Not an import declaration, so we're done.
// Imports are always first.
return false
}
if d.Lparen.IsValid() {
// For now assume all grouped imports are unsorted.
// TODO(gri) Should check if they are sorted already.
return true
}
// Ungrouped imports are sorted by default.
}
return false
}
func isSpace(b byte) bool {
return b == ' ' || b == '\t' || b == '\n' || b == '\r'
}
func parse(fset *token.FileSet, src []byte) (interface{}, error) {
// Try as a complete source file.
file, err := parser.ParseFile(fset, "", src, parser.ParseComments)
if err == nil {
return file, nil
}
// If the source is missing a package clause, try as a source fragment; otherwise fail.
if !strings.Contains(err.Error(), "expected 'package'") {
return nil, err
}
// Try as a declaration list by prepending a package clause in front of src.
// Use ';' not '\n' to keep line numbers intact.
psrc := append([]byte("package p;"), src...)
file, err = parser.ParseFile(fset, "", psrc, parser.ParseComments)
if err == nil {
return file.Decls, nil
}
// If the source is missing a declaration, try as a statement list; otherwise fail.
if !strings.Contains(err.Error(), "expected declaration") {
return nil, err
}
// Try as statement list by wrapping a function around src.
fsrc := append(append([]byte("package p; func _() {"), src...), '}')
file, err = parser.ParseFile(fset, "", fsrc, parser.ParseComments)
if err == nil {
return file.Decls[0].(*ast.FuncDecl).Body.List, nil
}
// Failed, and out of options.
return nil, err
}
// 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 format
import (
"bytes"
"go/parser"
"go/token"
"io/ioutil"
"strings"
"testing"
)
const testfile = "format_test.go"
func diff(t *testing.T, dst, src []byte) {
line := 1
offs := 0 // line offset
for i := 0; i < len(dst) && i < len(src); i++ {
d := dst[i]
s := src[i]
if d != s {
t.Errorf("dst:%d: %s\n", line, dst[offs:i+1])
t.Errorf("src:%d: %s\n", line, src[offs:i+1])
return
}
if s == '\n' {
line++
offs = i + 1
}
}
if len(dst) != len(src) {
t.Errorf("len(dst) = %d, len(src) = %d\nsrc = %q", len(dst), len(src), src)
}
}
func TestNode(t *testing.T) {
src, err := ioutil.ReadFile(testfile)
if err != nil {
t.Fatal(err)
}
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, testfile, src, parser.ParseComments)
if err != nil {
t.Fatal(err)
}
var buf bytes.Buffer
if err = Node(&buf, fset, file); err != nil {
t.Fatal("Node failed:", err)
}
diff(t, buf.Bytes(), src)
}
func TestSource(t *testing.T) {
src, err := ioutil.ReadFile(testfile)
if err != nil {
t.Fatal(err)
}
res, err := Source(src)
if err != nil {
t.Fatal("Source failed:", err)
}
diff(t, res, src)
}
// Test cases that are expected to fail are marked by the prefix "ERROR".
var tests = []string{
// declaration lists
`import "go/format"`,
"var x int",
"var x int\n\ntype T struct{}",
// statement lists
"x := 0",
"f(a, b, c)\nvar x int = f(1, 2, 3)",
// indentation, leading and trailing space
"\tx := 0\n\tgo f()",
"\tx := 0\n\tgo f()\n\n\n",
"\n\t\t\n\n\tx := 0\n\tgo f()\n\n\n",
"\n\t\t\n\n\t\t\tx := 0\n\t\t\tgo f()\n\n\n",
"\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\nfoo\n`\n\n\n", // no indentation inside raw strings
// erroneous programs
"ERRORvar x",
"ERROR1 + 2 +",
"ERRORx := 0",
}
func String(s string) (string, error) {
res, err := Source([]byte(s))
if err != nil {
return "", err
}
return string(res), nil
}
func TestPartial(t *testing.T) {
for _, src := range tests {
if strings.HasPrefix(src, "ERROR") {
// test expected to fail
src = src[5:] // remove ERROR prefix
res, err := String(src)
if err == nil && res == src {
t.Errorf("formatting succeeded but was expected to fail:\n%q", src)
}
} else {
// test expected to succeed
res, err := String(src)
if err != nil {
t.Errorf("formatting failed (%s):\n%q", err, src)
} else if res != src {
t.Errorf("formatting incorrect:\nsource: %q\nresult: %q", src, res)
}
}
}
}
......@@ -578,14 +578,15 @@ func (p *parser) parseTypeName() ast.Expr {
return ident
}
func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr {
func (p *parser) parseArrayType() ast.Expr {
if p.trace {
defer un(trace(p, "ArrayType"))
}
lbrack := p.expect(token.LBRACK)
var len ast.Expr
if ellipsisOk && p.tok == token.ELLIPSIS {
// always permit ellipsis for more fault-tolerant parsing
if p.tok == token.ELLIPSIS {
len = &ast.Ellipsis{Ellipsis: p.pos}
p.next()
} else if p.tok != token.RBRACK {
......@@ -697,7 +698,7 @@ 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
typ := p.tryIdentOrType() // don't use parseType so we can provide better error message
if typ != nil {
p.resolve(typ)
} else {
......@@ -706,7 +707,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
}
return &ast.Ellipsis{Ellipsis: pos, Elt: typ}
}
return p.tryIdentOrType(false)
return p.tryIdentOrType()
}
// If the result is an identifier, it is not resolved.
......@@ -943,12 +944,12 @@ func (p *parser) parseChanType() *ast.ChanType {
}
// If the result is an identifier, it is not resolved.
func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr {
func (p *parser) tryIdentOrType() ast.Expr {
switch p.tok {
case token.IDENT:
return p.parseTypeName()
case token.LBRACK:
return p.parseArrayType(ellipsisOk)
return p.parseArrayType()
case token.STRUCT:
return p.parseStructType()
case token.MUL:
......@@ -975,7 +976,7 @@ func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr {
}
func (p *parser) tryType() ast.Expr {
typ := p.tryIdentOrType(false)
typ := p.tryIdentOrType()
if typ != nil {
p.resolve(typ)
}
......@@ -1083,7 +1084,7 @@ func (p *parser) parseOperand(lhs bool) ast.Expr {
return p.parseFuncTypeOrLit()
}
if typ := p.tryIdentOrType(true); typ != nil {
if typ := p.tryIdentOrType(); typ != nil {
// could be type for composite literal or conversion
_, isIdent := typ.(*ast.Ident)
assert(!isIdent, "type cannot be identifier")
......@@ -1802,7 +1803,7 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
//
// switch t := 0; t := x.(T) { ... }
//
// (this code is not valid Go because the first t will
// (this code is not valid Go because the first t
// cannot be accessed and thus is never used, the extra
// scope is needed for the correct error message).
//
......
......@@ -730,7 +730,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
case *ast.FuncLit:
p.expr(x.Type)
p.funcBody(x.Body, p.distance(x.Type.Pos(), p.pos), true)
p.adjBlock(p.distanceFrom(x.Type.Pos()), blank, x.Body)
case *ast.ParenExpr:
if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
......@@ -900,7 +900,11 @@ func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) {
if _, isEmpty := s.(*ast.EmptyStmt); !isEmpty {
// _indent == 0 only for lists of switch/select case clauses;
// in those cases each clause is a new section
p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || multiLine)
if len(p.output) > 0 {
// only print line break if we are not at the beginning of the output
// (i.e., we are not printing only a partial program)
p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || multiLine)
}
p.stmt(s, nextIsRBrace && i == len(list)-1)
multiLine = p.isMultiLine(s)
i++
......@@ -912,11 +916,11 @@ func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) {
}
// block prints an *ast.BlockStmt; it always spans at least two lines.
func (p *printer) block(s *ast.BlockStmt, nindent int) {
p.print(s.Pos(), token.LBRACE)
p.stmtList(s.List, nindent, true)
p.linebreak(p.lineFor(s.Rbrace), 1, ignore, true)
p.print(s.Rbrace, token.RBRACE)
func (p *printer) block(b *ast.BlockStmt, nindent int) {
p.print(b.Lbrace, token.LBRACE)
p.stmtList(b.List, nindent, true)
p.linebreak(p.lineFor(b.Rbrace), 1, ignore, true)
p.print(b.Rbrace, token.RBRACE)
}
func isTypeName(x ast.Expr) bool {
......@@ -1421,19 +1425,19 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
return
}
func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
// bodySize is like nodeSize but it is specialized for *ast.BlockStmt's.
func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
pos1 := b.Pos()
pos2 := b.Rbrace
if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) {
// opening and closing brace are on different lines - don't make it a one-liner
return false
return maxSize + 1
}
if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) {
// too many statements or there is a comment inside - don't make it a one-liner
return false
return maxSize + 1
}
// otherwise, estimate body size
const maxSize = 100
bodySize := 0
for i, s := range b.List {
if i > 0 {
......@@ -1441,19 +1445,23 @@ func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
}
bodySize += p.nodeSize(s, maxSize)
}
return headerSize+bodySize <= maxSize
return bodySize
}
func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool) {
// adjBlock prints an "adjacent" block (e.g., a for-loop or function body) following
// a header (e.g., a for-loop control clause or function signature) of given headerSize.
// If the header's and block's size are "small enough" and the block is "simple enough",
// the block is printed on the current line, without line breaks, spaced from the header
// by sep. Otherwise the block's opening "{" is printed on the current line, followed by
// lines for the block's statements and its closing "}".
//
func (p *printer) adjBlock(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
if b == nil {
return
}
if p.isOneLineFunc(b, headerSize) {
sep := vtab
if isLit {
sep = blank
}
const maxSize = 100
if headerSize+p.bodySize(b, maxSize) <= maxSize {
p.print(sep, b.Lbrace, token.LBRACE)
if len(b.List) > 0 {
p.print(blank)
......@@ -1469,17 +1477,20 @@ func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool) {
return
}
p.print(blank)
if sep != ignore {
p.print(blank) // always use blank
}
p.block(b, 1)
}
// distance returns the column difference between from and to if both
// are on the same line; if they are on different lines (or unknown)
// the result is infinity.
func (p *printer) distance(from0 token.Pos, to token.Position) int {
from := p.posFor(from0)
if from.IsValid() && to.IsValid() && from.Line == to.Line {
return to.Column - from.Column
// distanceFrom returns the column difference between from and p.pos (the current
// estimated position) if both are on the same line; if they are on different lines
// (or unknown) the result is infinity.
func (p *printer) distanceFrom(from token.Pos) int {
if from.IsValid() && p.pos.IsValid() {
if f := p.posFor(from); f.Line == p.pos.Line {
return p.pos.Column - f.Column
}
}
return infinity
}
......@@ -1493,7 +1504,7 @@ func (p *printer) funcDecl(d *ast.FuncDecl) {
}
p.expr(d.Name)
p.signature(d.Type.Params, d.Type.Results)
p.funcBody(d.Body, p.distance(d.Pos(), p.pos), false)
p.adjBlock(p.distanceFrom(d.Pos()), vtab, d.Body)
}
func (p *printer) decl(decl ast.Decl) {
......@@ -1523,31 +1534,35 @@ func declToken(decl ast.Decl) (tok token.Token) {
return
}
func (p *printer) file(src *ast.File) {
p.setComment(src.Doc)
p.print(src.Pos(), token.PACKAGE, blank)
p.expr(src.Name)
if len(src.Decls) > 0 {
tok := token.ILLEGAL
for _, d := range src.Decls {
prev := tok
tok = declToken(d)
// if the declaration token changed (e.g., from CONST to TYPE)
// or the next declaration has documentation associated with it,
// print an empty line between top-level declarations
// (because p.linebreak is called with the position of d, which
// is past any documentation, the minimum requirement is satisfied
// even w/o the extra getDoc(d) nil-check - leave it in case the
// linebreak logic improves - there's already a TODO).
func (p *printer) declList(list []ast.Decl) {
tok := token.ILLEGAL
for _, d := range list {
prev := tok
tok = declToken(d)
// If the declaration token changed (e.g., from CONST to TYPE)
// or the next declaration has documentation associated with it,
// print an empty line between top-level declarations.
// (because p.linebreak is called with the position of d, which
// is past any documentation, the minimum requirement is satisfied
// even w/o the extra getDoc(d) nil-check - leave it in case the
// linebreak logic improves - there's already a TODO).
if len(p.output) > 0 {
// only print line break if we are not at the beginning of the output
// (i.e., we are not printing only a partial program)
min := 1
if prev != tok || getDoc(d) != nil {
min = 2
}
p.linebreak(p.lineFor(d.Pos()), min, ignore, false)
p.decl(d)
}
p.decl(d)
}
}
func (p *printer) file(src *ast.File) {
p.setComment(src.Doc)
p.print(src.Pos(), token.PACKAGE, blank)
p.expr(src.Name)
p.declList(src.Decls)
p.print(newline)
}
......@@ -20,7 +20,7 @@ import (
var testfile *ast.File
func testprint(out io.Writer, file *ast.File) {
if err := (&Config{TabIndent | UseSpaces, 8}).Fprint(out, fset, file); err != nil {
if err := (&Config{TabIndent | UseSpaces, 8, 0}).Fprint(out, fset, file); err != nil {
log.Fatalf("print error: %s", err)
}
}
......
......@@ -165,15 +165,15 @@ func (p *printer) atLineBegin(pos token.Position) {
// write indentation
// use "hard" htabs - indentation columns
// must not be discarded by the tabwriter
for i := 0; i < p.indent; i++ {
n := p.Config.Indent + p.indent // include base indentation
for i := 0; i < n; i++ {
p.output = append(p.output, '\t')
}
// update positions
i := p.indent
p.pos.Offset += i
p.pos.Column += i
p.out.Column += i
p.pos.Offset += n
p.pos.Column += n
p.out.Column += n
}
// writeByte writes ch n times to p.output and updates p.pos.
......@@ -452,7 +452,7 @@ func trimRight(s string) string {
// stripCommonPrefix removes a common prefix from /*-style comment lines (unless no
// comment line is indented, all but the first line have some form of space prefix).
// The prefix is computed using heuristics such that is is likely that the comment
// The prefix is computed using heuristics such that is likely that the comment
// contents are nicely laid out after re-printing each line using the printer's
// current indentation.
//
......@@ -1032,9 +1032,9 @@ func (p *printer) printNode(node interface{}) error {
case ast.Expr:
p.expr(n)
case ast.Stmt:
// A labeled statement will un-indent to position the
// label. Set indent to 1 so we don't get indent "underflow".
if _, labeledStmt := n.(*ast.LabeledStmt); labeledStmt {
// A labeled statement will un-indent to position the label.
// Set p.indent to 1 so we don't get indent "underflow".
if _, ok := n.(*ast.LabeledStmt); ok {
p.indent = 1
}
p.stmt(n, false)
......@@ -1042,6 +1042,17 @@ func (p *printer) printNode(node interface{}) error {
p.decl(n)
case ast.Spec:
p.spec(n, 1, false)
case []ast.Stmt:
// A labeled statement will un-indent to position the label.
// Set p.indent to 1 so we don't get indent "underflow".
for _, s := range n {
if _, ok := s.(*ast.LabeledStmt); ok {
p.indent = 1
}
}
p.stmtList(n, 0, false)
case []ast.Decl:
p.declList(n)
case *ast.File:
p.file(n)
default:
......@@ -1174,6 +1185,7 @@ const (
type Config struct {
Mode Mode // default: 0
Tabwidth int // default: 8
Indent int // default: 0 (all code is indented at least by this much)
}
// fprint implements Fprint and takes a nodesSizes map for setting up the printer state.
......@@ -1235,8 +1247,8 @@ type CommentedNode struct {
// Fprint "pretty-prints" an AST node to output for a given configuration cfg.
// Position information is interpreted relative to the file set fset.
// The node type must be *ast.File, *CommentedNode, or assignment-compatible
// to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt.
// The node type must be *ast.File, *CommentedNode, []ast.Decl, []ast.Stmt,
// or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt.
//
func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) error {
return cfg.fprint(output, fset, node, make(map[ast.Node]int))
......
......@@ -434,6 +434,98 @@ func (t *t) foo(a, b, c int) int {
}
}
var decls = []string{
`import "fmt"`,
"const pi = 3.1415\nconst e = 2.71828\n\nvar x = pi",
"func sum(x, y int) int\t{ return x + y }",
}
func TestDeclLists(t *testing.T) {
for _, src := range decls {
file, err := parser.ParseFile(fset, "", "package p;"+src, parser.ParseComments)
if err != nil {
panic(err) // error in test
}
var buf bytes.Buffer
err = Fprint(&buf, fset, file.Decls) // only print declarations
if err != nil {
panic(err) // error in test
}
out := buf.String()
if out != src {
t.Errorf("\ngot : %q\nwant: %q\n", out, src)
}
}
}
var stmts = []string{
"i := 0",
"select {}\nvar a, b = 1, 2\nreturn a + b",
"go f()\ndefer func() {}()",
}
func TestStmtLists(t *testing.T) {
for _, src := range stmts {
file, err := parser.ParseFile(fset, "", "package p; func _() {"+src+"}", parser.ParseComments)
if err != nil {
panic(err) // error in test
}
var buf bytes.Buffer
err = Fprint(&buf, fset, file.Decls[0].(*ast.FuncDecl).Body.List) // only print statements
if err != nil {
panic(err) // error in test
}
out := buf.String()
if out != src {
t.Errorf("\ngot : %q\nwant: %q\n", out, src)
}
}
}
func TestBaseIndent(t *testing.T) {
// The testfile must not contain multi-line raw strings since those
// are not indented (because their values must not change) and make
// this test fail.
const filename = "printer.go"
src, err := ioutil.ReadFile(filename)
if err != nil {
panic(err) // error in test
}
file, err := parser.ParseFile(fset, filename, src, 0)
if err != nil {
panic(err) // error in test
}
var buf bytes.Buffer
for indent := 0; indent < 4; indent++ {
buf.Reset()
(&Config{Tabwidth: tabwidth, Indent: indent}).Fprint(&buf, fset, file)
// all code must be indented by at least 'indent' tabs
lines := bytes.Split(buf.Bytes(), []byte{'\n'})
for i, line := range lines {
if len(line) == 0 {
continue // empty lines don't have indentation
}
n := 0
for j, b := range line {
if b != '\t' {
// end of indentation
n = j
break
}
}
if n < indent {
t.Errorf("line %d: got only %d tabs; want at least %d: %q", i, n, indent, line)
}
}
}
}
// TestFuncType tests that an ast.FuncType with a nil Params field
// can be printed (per go/ast specification). Test case for issue 3870.
func TestFuncType(t *testing.T) {
......
......@@ -241,7 +241,7 @@ func _() {
}
}
// Formatting of for-statement headers.
// Formatting of for-statement headers for single-line for-loops.
func _() {
for {
}
......@@ -279,6 +279,86 @@ func _() {
} // no parens printed
}
// Formatting of for-statement headers for multi-line for-loops.
func _() {
for {
}
for expr {
}
for expr {
} // no parens printed
for {
} // no semicolons printed
for x := expr; ; {
use(x)
}
for expr {
} // no semicolons printed
for expr {
} // no semicolons and parens printed
for ; ; expr = false {
}
for x := expr; expr; {
use(x)
}
for x := expr; ; expr = false {
use(x)
}
for ; expr; expr = false {
}
for x := expr; expr; expr = false {
use(x)
}
for x := range []int{} {
use(x)
}
for x := range []int{} {
use(x)
} // no parens printed
}
// Formatting of selected short single- and multi-line statements.
func _() {
if cond {
}
if cond {
} // multiple lines
if cond {
} else {
} // else clause always requires multiple lines
for {
}
for i := 0; i < len(a); 1++ {
}
for i := 0; i < len(a); 1++ {
a[i] = i
}
for i := 0; i < len(a); 1++ {
a[i] = i
} // multiple lines
for i := range a {
}
for i := range a {
a[i] = i
}
for i := range a {
a[i] = i
} // multiple lines
go func() {
for {
a <- <-b
}
}()
defer func() {
if x := recover(); x != nil {
err = fmt.Sprintf("error: %s", x.msg)
}
}()
}
// Don't remove mandatory parentheses around composite literals in control clauses.
func _() {
// strip parentheses - no composite literals or composite literals don't start with a type name
......
......@@ -223,7 +223,7 @@ func _() {
}
// Formatting of for-statement headers.
// Formatting of for-statement headers for single-line for-loops.
func _() {
for{}
for expr {}
......@@ -235,14 +235,70 @@ func _() {
for; ; expr = false {}
for x :=expr; expr; {use(x)}
for x := expr;; expr=false {use(x)}
for;expr;expr =false {
}
for;expr;expr =false {}
for x := expr;expr;expr = false { use(x) }
for x := range []int{} { use(x) }
for x := range (([]int{})) { use(x) } // no parens printed
}
// Formatting of for-statement headers for multi-line for-loops.
func _() {
for{
}
for expr {
}
for (expr) {
} // no parens printed
for;;{
} // no semicolons printed
for x :=expr;; {use( x)
}
for; expr;{
} // no semicolons printed
for; ((expr));{
} // no semicolons and parens printed
for; ; expr = false {
}
for x :=expr; expr; {use(x)
}
for x := expr;; expr=false {use(x)
}
for;expr;expr =false {
}
for x := expr;expr;expr = false {
use(x)
}
for x := range []int{} {
use(x) }
for x := range (([]int{})) {
use(x) } // no parens printed
}
// Formatting of selected short single- and multi-line statements.
func _() {
if cond {}
if cond {
} // multiple lines
if cond {} else {} // else clause always requires multiple lines
for {}
for i := 0; i < len(a); 1++ {}
for i := 0; i < len(a); 1++ { a[i] = i }
for i := 0; i < len(a); 1++ { a[i] = i
} // multiple lines
for i := range a {}
for i := range a { a[i] = i }
for i := range a { a[i] = i
} // multiple lines
go func() { for { a <- <-b } }()
defer func() { if x := recover(); x != nil { err = fmt.Sprintf("error: %s", x.msg) } }()
}
// Don't remove mandatory parentheses around composite literals in control clauses.
func _() {
// strip parentheses - no composite literals or composite literals don't start with a type name
......
......@@ -14,7 +14,7 @@ import (
)
// nextJSCtx returns the context that determines whether a slash after the
// given run of tokens tokens starts a regular expression instead of a division
// given run of tokens starts a regular expression instead of a division
// operator: / or /=.
//
// This assumes that the token run does not include any string tokens, comment
......
......@@ -163,7 +163,7 @@ func (d *decoder) processDHT(n int) error {
// Returns the next Huffman-coded value from the bit stream, decoded according to h.
// TODO(nigeltao): This decoding algorithm is simple, but slow. A lookahead table, instead of always
// peeling off only 1 bit at at time, ought to be faster.
// peeling off only 1 bit at time, ought to be faster.
func (d *decoder) decodeHuffman(h *huffman) (uint8, error) {
if h.length == 0 {
return 0, FormatError("uninitialized Huffman table")
......
......@@ -4,9 +4,9 @@
// +build !windows,!plan9
// Package syslog provides a simple interface to the system log service. It
// can send messages to the syslog daemon using UNIX domain sockets, UDP, or
// TCP connections.
// Package syslog provides a simple interface to the system log
// service. It can send messages to the syslog daemon using UNIX
// domain sockets, UDP, or TCP connections.
package syslog
import (
......@@ -15,11 +15,21 @@ import (
"log"
"net"
"os"
"time"
)
// The Priority is a combination of the syslog facility and
// severity. For example, LOG_ALERT | LOG_FTP sends an alert severity
// message from the FTP facility. The default severity is LOG_EMERG;
// the default facility is LOG_KERN.
type Priority int
const severityMask = 0x07
const facilityMask = 0xf8
const (
// Severity.
// From /usr/include/sys/syslog.h.
// These are the same on Linux, BSD, and OS X.
LOG_EMERG Priority = iota
......@@ -32,16 +42,47 @@ const (
LOG_DEBUG
)
const (
// Facility.
// From /usr/include/sys/syslog.h.
// These are the same up to LOG_FTP on Linux, BSD, and OS X.
LOG_KERN Priority = iota << 3
LOG_USER
LOG_MAIL
LOG_DAEMON
LOG_AUTH
LOG_SYSLOG
LOG_LPR
LOG_NEWS
LOG_UUCP
LOG_CRON
LOG_AUTHPRIV
LOG_FTP
_ // unused
_ // unused
_ // unused
_ // unused
LOG_LOCAL0
LOG_LOCAL1
LOG_LOCAL2
LOG_LOCAL3
LOG_LOCAL4
LOG_LOCAL5
LOG_LOCAL6
LOG_LOCAL7
)
// A Writer is a connection to a syslog server.
type Writer struct {
priority Priority
prefix string
tag string
hostname string
conn serverConn
}
type serverConn interface {
writeBytes(p Priority, prefix string, b []byte) (int, error)
writeString(p Priority, prefix string, s string) (int, error)
writeString(p Priority, hostname, tag, s string) (int, error)
close() error
}
......@@ -49,116 +90,130 @@ type netConn struct {
conn net.Conn
}
// New establishes a new connection to the system log daemon.
// Each write to the returned writer sends a log message with
// the given priority and prefix.
func New(priority Priority, prefix string) (w *Writer, err error) {
return Dial("", "", priority, prefix)
// New establishes a new connection to the system log daemon. Each
// write to the returned writer sends a log message with the given
// priority and prefix.
func New(priority Priority, tag string) (w *Writer, err error) {
return Dial("", "", priority, tag)
}
// Dial establishes a connection to a log daemon by connecting
// to address raddr on the network net.
// Each write to the returned writer sends a log message with
// the given priority and prefix.
func Dial(network, raddr string, priority Priority, prefix string) (w *Writer, err error) {
if prefix == "" {
prefix = os.Args[0]
// Dial establishes a connection to a log daemon by connecting to
// address raddr on the network net. Each write to the returned
// writer sends a log message with the given facility, severity and
// tag.
func Dial(network, raddr string, priority Priority, tag string) (w *Writer, err error) {
if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG {
return nil, errors.New("log/syslog: invalid priority")
}
if tag == "" {
tag = os.Args[0]
}
hostname, _ := os.Hostname()
var conn serverConn
if network == "" {
conn, err = unixSyslog()
if hostname == "" {
hostname = "localhost"
}
} else {
var c net.Conn
c, err = net.Dial(network, raddr)
conn = netConn{c}
if hostname == "" {
hostname = c.LocalAddr().String()
}
}
if err != nil {
return nil, err
}
return &Writer{priority, prefix, conn}, err
return &Writer{priority: priority, tag: tag, hostname: hostname, conn: conn}, nil
}
// Write sends a log message to the syslog daemon.
func (w *Writer) Write(b []byte) (int, error) {
if w.priority > LOG_DEBUG || w.priority < LOG_EMERG {
return 0, errors.New("log/syslog: invalid priority")
}
return w.conn.writeBytes(w.priority, w.prefix, b)
}
func (w *Writer) writeString(p Priority, s string) (int, error) {
return w.conn.writeString(p, w.prefix, s)
return w.writeString(w.priority, string(b))
}
func (w *Writer) Close() error { return w.conn.close() }
// Emerg logs a message using the LOG_EMERG priority.
// Emerg logs a message with severity LOG_EMERG, ignoring the severity
// passed to New.
func (w *Writer) Emerg(m string) (err error) {
_, err = w.writeString(LOG_EMERG, m)
return err
}
// Alert logs a message using the LOG_ALERT priority.
// Alert logs a message with severity LOG_ALERT, ignoring the severity
// passed to New.
func (w *Writer) Alert(m string) (err error) {
_, err = w.writeString(LOG_ALERT, m)
return err
}
// Crit logs a message using the LOG_CRIT priority.
// Crit logs a message with severity LOG_CRIT, ignoring the severity
// passed to New.
func (w *Writer) Crit(m string) (err error) {
_, err = w.writeString(LOG_CRIT, m)
return err
}
// Err logs a message using the LOG_ERR priority.
// Err logs a message with severity LOG_ERR, ignoring the severity
// passed to New.
func (w *Writer) Err(m string) (err error) {
_, err = w.writeString(LOG_ERR, m)
return err
}
// Warning logs a message using the LOG_WARNING priority.
// Wanring logs a message with severity LOG_WARNING, ignoring the
// severity passed to New.
func (w *Writer) Warning(m string) (err error) {
_, err = w.writeString(LOG_WARNING, m)
return err
}
// Notice logs a message using the LOG_NOTICE priority.
// Notice logs a message with severity LOG_NOTICE, ignoring the
// severity passed to New.
func (w *Writer) Notice(m string) (err error) {
_, err = w.writeString(LOG_NOTICE, m)
return err
}
// Info logs a message using the LOG_INFO priority.
// Info logs a message with severity LOG_INFO, ignoring the severity
// passed to New.
func (w *Writer) Info(m string) (err error) {
_, err = w.writeString(LOG_INFO, m)
return err
}
// Debug logs a message using the LOG_DEBUG priority.
// Debug logs a message with severity LOG_DEBUG, ignoring the severity
// passed to New.
func (w *Writer) Debug(m string) (err error) {
_, err = w.writeString(LOG_DEBUG, m)
return err
}
func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, error) {
nl := ""
if len(b) == 0 || b[len(b)-1] != '\n' {
nl = "\n"
}
_, err := fmt.Fprintf(n.conn, "<%d>%s: %s%s", p, prefix, b, nl)
if err != nil {
return 0, err
}
return len(b), nil
func (w *Writer) writeString(p Priority, s string) (int, error) {
return w.conn.writeString((w.priority&facilityMask)|(p&severityMask),
w.hostname, w.tag, s)
}
func (n netConn) writeString(p Priority, prefix string, s string) (int, error) {
// writeString: generates and writes a syslog formatted string. The
// format is as follows: <PRI>1 TIMESTAMP HOSTNAME TAG[PID]: MSG
func (n netConn) writeString(p Priority, hostname, tag, msg string) (int, error) {
nl := ""
if len(s) == 0 || s[len(s)-1] != '\n' {
if len(msg) == 0 || msg[len(msg)-1] != '\n' {
nl = "\n"
}
_, err := fmt.Fprintf(n.conn, "<%d>%s: %s%s", p, prefix, s, nl)
if err != nil {
timestamp := time.Now().Format(time.RFC3339)
if _, err := fmt.Fprintf(n.conn, "<%d>1 %s %s %s[%d]: %s%s", p, timestamp, hostname,
tag, os.Getpid(), msg, nl); err != nil {
return 0, err
}
return len(s), nil
return len(msg), nil
}
func (n netConn) close() error {
......
......@@ -7,9 +7,11 @@
package syslog
import (
"fmt"
"io"
"log"
"net"
"os"
"testing"
"time"
)
......@@ -49,10 +51,14 @@ func skipNetTest(t *testing.T) bool {
}
func TestNew(t *testing.T) {
if LOG_LOCAL7 != 23<<3 {
t.Fatalf("LOG_LOCAL7 has wrong value")
}
if skipNetTest(t) {
return
}
s, err := New(LOG_INFO, "")
s, err := New(LOG_INFO|LOG_USER, "")
if err != nil {
t.Fatalf("New() failed: %s", err)
}
......@@ -64,7 +70,7 @@ func TestNewLogger(t *testing.T) {
if skipNetTest(t) {
return
}
f, err := NewLogger(LOG_INFO, 0)
f, err := NewLogger(LOG_USER|LOG_INFO, 0)
if f == nil {
t.Error(err)
}
......@@ -74,7 +80,15 @@ func TestDial(t *testing.T) {
if skipNetTest(t) {
return
}
l, err := Dial("", "", LOG_ERR, "syslog_test")
f, err := Dial("", "", (LOG_LOCAL7|LOG_DEBUG)+1, "syslog_test")
if f != nil {
t.Fatalf("Should have trapped bad priority")
}
f, err = Dial("", "", -1, "syslog_test")
if f != nil {
t.Fatalf("Should have trapped bad priority")
}
l, err := Dial("", "", LOG_USER|LOG_ERR, "syslog_test")
if err != nil {
t.Fatalf("Dial() failed: %s", err)
}
......@@ -84,16 +98,23 @@ func TestDial(t *testing.T) {
func TestUDPDial(t *testing.T) {
done := make(chan string)
startServer(done)
l, err := Dial("udp", serverAddr, LOG_INFO, "syslog_test")
l, err := Dial("udp", serverAddr, LOG_USER|LOG_INFO, "syslog_test")
if err != nil {
t.Fatalf("syslog.Dial() failed: %s", err)
}
msg := "udp test"
l.Info(msg)
expected := "<6>syslog_test: udp test\n"
expected := fmt.Sprintf("<%d>1 ", LOG_USER+LOG_INFO) + "%s %s syslog_test[%d]: udp test\n"
rcvd := <-done
if rcvd != expected {
t.Fatalf("s.Info() = '%q', but wanted '%q'", rcvd, expected)
var parsedHostname, timestamp string
var pid int
if hostname, err := os.Hostname(); err != nil {
t.Fatalf("Error retrieving hostname")
} else {
if n, err := fmt.Sscanf(rcvd, expected, &timestamp, &parsedHostname, &pid); n != 3 ||
err != nil || hostname != parsedHostname {
t.Fatalf("s.Info() = '%q', didn't match '%q'", rcvd, expected)
}
}
}
......@@ -104,26 +125,34 @@ func TestWrite(t *testing.T) {
msg string
exp string
}{
{LOG_ERR, "syslog_test", "", "<3>syslog_test: \n"},
{LOG_ERR, "syslog_test", "write test", "<3>syslog_test: write test\n"},
{LOG_USER | LOG_ERR, "syslog_test", "", "%s %s syslog_test[%d]: \n"},
{LOG_USER | LOG_ERR, "syslog_test", "write test", "%s %s syslog_test[%d]: write test\n"},
// Write should not add \n if there already is one
{LOG_ERR, "syslog_test", "write test 2\n", "<3>syslog_test: write test 2\n"},
{LOG_USER | LOG_ERR, "syslog_test", "write test 2\n", "%s %s syslog_test[%d]: write test 2\n"},
}
for _, test := range tests {
done := make(chan string)
startServer(done)
l, err := Dial("udp", serverAddr, test.pri, test.pre)
if err != nil {
t.Fatalf("syslog.Dial() failed: %s", err)
}
_, err = io.WriteString(l, test.msg)
if err != nil {
t.Fatalf("WriteString() failed: %s", err)
}
rcvd := <-done
if rcvd != test.exp {
t.Fatalf("s.Info() = '%q', but wanted '%q'", rcvd, test.exp)
if hostname, err := os.Hostname(); err != nil {
t.Fatalf("Error retrieving hostname")
} else {
for _, test := range tests {
done := make(chan string)
startServer(done)
l, err := Dial("udp", serverAddr, test.pri, test.pre)
if err != nil {
t.Fatalf("syslog.Dial() failed: %s", err)
}
_, err = io.WriteString(l, test.msg)
if err != nil {
t.Fatalf("WriteString() failed: %s", err)
}
rcvd := <-done
test.exp = fmt.Sprintf("<%d>1 ", test.pri) + test.exp
var parsedHostname, timestamp string
var pid int
if n, err := fmt.Sscanf(rcvd, test.exp, &timestamp, &parsedHostname, &pid); n != 3 ||
err != nil || hostname != parsedHostname {
t.Fatalf("s.Info() = '%q', didn't match '%q'", rcvd, test.exp)
}
}
}
}
......@@ -180,6 +180,7 @@ func allocBytes(f func()) uint64 {
// does not cause deep recursion and in turn allocate too much memory.
// Test case for issue 3807.
func TestMulUnbalanced(t *testing.T) {
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
x := rndNat(50000)
y := rndNat(40)
allocSize := allocBytes(func() {
......
......@@ -37,6 +37,11 @@ type Part struct {
disposition string
dispositionParams map[string]string
// r is either a reader directly reading from mr, or it's a
// wrapper around such a reader, decoding the
// Content-Transfer-Encoding
r io.Reader
}
// FormName returns the name parameter if p has a Content-Disposition
......@@ -94,6 +99,12 @@ func newPart(mr *Reader) (*Part, error) {
if err := bp.populateHeaders(); err != nil {
return nil, err
}
bp.r = partReader{bp}
const cte = "Content-Transfer-Encoding"
if bp.Header.Get(cte) == "quoted-printable" {
bp.Header.Del(cte)
bp.r = newQuotedPrintableReader(bp.r)
}
return bp, nil
}
......@@ -109,6 +120,17 @@ 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) {
return p.r.Read(d)
}
// partReader implements io.Reader by reading raw bytes directly from the
// wrapped *Part, without doing any Transfer-Encoding decoding.
type partReader struct {
p *Part
}
func (pr partReader) Read(d []byte) (n int, err error) {
p := pr.p
defer func() {
p.bytesRead += n
}()
......
......@@ -339,9 +339,10 @@ func TestLineContinuation(t *testing.T) {
if err != nil {
t.Fatalf("didn't get a part")
}
n, err := io.Copy(ioutil.Discard, part)
var buf bytes.Buffer
n, err := io.Copy(&buf, part)
if err != nil {
t.Errorf("error reading part: %v", err)
t.Errorf("error reading part: %v\nread so far: %q", err, buf.String())
}
if n <= 0 {
t.Errorf("read %d bytes; expected >0", n)
......@@ -349,6 +350,29 @@ func TestLineContinuation(t *testing.T) {
}
}
func TestQuotedPrintableEncoding(t *testing.T) {
// From http://golang.org/issue/4411
body := "--0016e68ee29c5d515f04cedf6733\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=text\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nwords words words words words words words words words words words words wor=\r\nds words words words words words words words words words words words words =\r\nwords words words words words words words words words words words words wor=\r\nds words words words words words words words words words words words words =\r\nwords words words words words words words words words\r\n--0016e68ee29c5d515f04cedf6733\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=submit\r\n\r\nSubmit\r\n--0016e68ee29c5d515f04cedf6733--"
r := NewReader(strings.NewReader(body), "0016e68ee29c5d515f04cedf6733")
part, err := r.NextPart()
if err != nil {
t.Fatal(err)
}
if te, ok := part.Header["Content-Transfer-Encoding"]; ok {
t.Errorf("unexpected Content-Transfer-Encoding of %q", te)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, part)
if err != nil {
t.Error(err)
}
got := buf.String()
want := "words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words"
if got != want {
t.Errorf("wrong part value:\n got: %q\nwant: %q", got, want)
}
}
// Test parsing an image attachment from gmail, which previously failed.
func TestNested(t *testing.T) {
// nested-mime is the body part of a multipart/mixed email
......
// 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.
// The file define a quoted-printable decoder, as specified in RFC 2045.
package multipart
import (
"bufio"
"bytes"
"fmt"
"io"
)
type qpReader struct {
br *bufio.Reader
rerr error // last read error
line []byte // to be consumed before more of br
}
func newQuotedPrintableReader(r io.Reader) io.Reader {
return &qpReader{
br: bufio.NewReader(r),
}
}
func fromHex(b byte) (byte, error) {
switch {
case b >= '0' && b <= '9':
return b - '0', nil
case b >= 'A' && b <= 'F':
return b - 'A' + 10, nil
}
return 0, fmt.Errorf("multipart: invalid quoted-printable hex byte 0x%02x", b)
}
func (q *qpReader) readHexByte(v []byte) (b byte, err error) {
if len(v) < 2 {
return 0, io.ErrUnexpectedEOF
}
var hb, lb byte
if hb, err = fromHex(v[0]); err != nil {
return 0, err
}
if lb, err = fromHex(v[1]); err != nil {
return 0, err
}
return hb<<4 | lb, nil
}
func isQPDiscardWhitespace(r rune) bool {
switch r {
case '\n', '\r', ' ', '\t':
return true
}
return false
}
func (q *qpReader) Read(p []byte) (n int, err error) {
for len(p) > 0 {
if len(q.line) == 0 {
if q.rerr != nil {
return n, q.rerr
}
q.line, q.rerr = q.br.ReadSlice('\n')
q.line = bytes.TrimRightFunc(q.line, isQPDiscardWhitespace)
continue
}
if len(q.line) == 1 && q.line[0] == '=' {
// Soft newline; skipped.
q.line = nil
continue
}
b := q.line[0]
switch {
case b == '=':
b, err = q.readHexByte(q.line[1:])
if err != nil {
return n, err
}
q.line = q.line[2:] // 2 of the 3; other 1 is done below
case b != '\t' && (b < ' ' || b > '~'):
return n, fmt.Errorf("multipart: invalid unescaped byte 0x%02x in quoted-printable body", b)
}
p[0] = b
p = p[1:]
q.line = q.line[1:]
n++
}
return n, nil
}
// 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 multipart
import (
"bytes"
"fmt"
"io"
"strings"
"testing"
)
func TestQuotedPrintable(t *testing.T) {
tests := []struct {
in, want string
err interface{}
}{
{in: "foo bar", want: "foo bar"},
{in: "foo bar=3D", want: "foo bar="},
{in: "foo bar=0", want: "foo bar", err: io.ErrUnexpectedEOF},
{in: "foo bar=ab", want: "foo bar", err: "multipart: invalid quoted-printable hex byte 0x61"},
{in: "foo bar=0D=0A", want: "foo bar\r\n"},
{in: "foo bar=\r\n baz", want: "foo bar baz"},
{in: "foo=\nbar", want: "foobar"},
{in: "foo\x00bar", want: "foo", err: "multipart: invalid unescaped byte 0x00 in quoted-printable body"},
{in: "foo bar\xff", want: "foo bar", err: "multipart: invalid unescaped byte 0xff in quoted-printable body"},
}
for _, tt := range tests {
var buf bytes.Buffer
_, err := io.Copy(&buf, newQuotedPrintableReader(strings.NewReader(tt.in)))
if got := buf.String(); got != tt.want {
t.Errorf("for %q, got %q; want %q", tt.in, got, tt.want)
}
switch verr := tt.err.(type) {
case nil:
if err != nil {
t.Errorf("for %q, got unexpected error: %v", tt.in, err)
}
case string:
if got := fmt.Sprint(err); got != verr {
t.Errorf("for %q, got error %q; want %q", tt.in, got, verr)
}
case error:
if err != verr {
t.Errorf("for %q, got error %q; want %q", tt.in, err, verr)
}
}
}
}
......@@ -30,11 +30,38 @@ func NewWriter(w io.Writer) *Writer {
}
}
// Boundary returns the Writer's randomly selected boundary string.
// Boundary returns the Writer's boundary.
func (w *Writer) Boundary() string {
return w.boundary
}
// SetBoundary overrides the Writer's default randomly-generated
// boundary separator with an explicit value.
//
// SetBoundary must be called before any parts are created, may only
// contain certain ASCII characters, and must be 1-69 bytes long.
func (w *Writer) SetBoundary(boundary string) error {
if w.lastpart != nil {
return errors.New("mime: SetBoundary called after write")
}
// rfc2046#section-5.1.1
if len(boundary) < 1 || len(boundary) > 69 {
return errors.New("mime: invalid boundary length")
}
for _, b := range boundary {
if 'A' <= b && b <= 'Z' || 'a' <= b && b <= 'z' || '0' <= b && b <= '9' {
continue
}
switch b {
case '\'', '(', ')', '+', '_', ',', '-', '.', '/', ':', '=', '?':
continue
}
return errors.New("mime: invalid boundary character")
}
w.boundary = boundary
return nil
}
// FormDataContentType returns the Content-Type for an HTTP
// multipart/form-data with this Writer's Boundary.
func (w *Writer) FormDataContentType() string {
......
......@@ -7,6 +7,7 @@ package multipart
import (
"bytes"
"io/ioutil"
"strings"
"testing"
)
......@@ -76,3 +77,37 @@ func TestWriter(t *testing.T) {
t.Fatalf("expected end of parts; got %v, %v", part, err)
}
}
func TestWriterSetBoundary(t *testing.T) {
var b bytes.Buffer
w := NewWriter(&b)
tests := []struct {
b string
ok bool
}{
{"abc", true},
{"", false},
{"ungültig", false},
{"!", false},
{strings.Repeat("x", 69), true},
{strings.Repeat("x", 70), false},
{"bad!ascii!", false},
{"my-separator", true},
}
for i, tt := range tests {
err := w.SetBoundary(tt.b)
got := err == nil
if got != tt.ok {
t.Errorf("%d. boundary %q = %v (%v); want %v", i, tt.b, got, err, tt.ok)
} else if tt.ok {
got := w.Boundary()
if got != tt.b {
t.Errorf("boundary = %q; want %q", got, tt.b)
}
}
}
w.Close()
if got := b.String(); !strings.Contains(got, "\r\n--my-separator--\r\n") {
t.Errorf("expected my-separator in output. got: %q", got)
}
}
......@@ -15,6 +15,7 @@ func parseDialNetwork(net string) (afnet string, proto int, err error) {
switch net {
case "tcp", "tcp4", "tcp6":
case "udp", "udp4", "udp6":
case "ip", "ip4", "ip6":
case "unix", "unixgram", "unixpacket":
default:
return "", 0, UnknownNetworkError(net)
......@@ -54,12 +55,8 @@ func resolveAfnetAddr(afnet, addr string, deadline time.Time) (Addr, error) {
return nil, nil
}
switch afnet {
case "tcp", "tcp4", "tcp6":
return resolveTCPAddr(afnet, addr, deadline)
case "udp", "udp4", "udp6":
return resolveUDPAddr(afnet, addr, deadline)
case "ip", "ip4", "ip6":
return resolveIPAddr(afnet, addr, deadline)
case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "ip", "ip4", "ip6":
return resolveInternetAddr(afnet, addr, deadline)
case "unix", "unixgram", "unixpacket":
return ResolveUnixAddr(afnet, addr)
}
......@@ -218,8 +215,8 @@ func Listen(net, laddr string) (Listener, error) {
// ListenPacket announces on the local network address laddr.
// The network string net must be a packet-oriented network:
// "udp", "udp4", "udp6", "ip", "ip4", "ip6" or "unixgram".
func ListenPacket(net, addr string) (PacketConn, error) {
afnet, a, err := resolveNetAddr("listen", net, addr, noDeadline)
func ListenPacket(net, laddr string) (PacketConn, error) {
afnet, a, err := resolveNetAddr("listen", net, laddr, noDeadline)
if err != nil {
return nil, err
}
......
// 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.
// +build darwin freebsd linux netbsd openbsd windows
package net
import (
"testing"
"time"
)
var deadlineSetTimeTests = []struct {
input time.Time
expected int64
}{
{time.Time{}, 0},
{time.Date(2009, 11, 10, 23, 00, 00, 00, time.UTC), 1257894000000000000}, // 2009-11-10 23:00:00 +0000 UTC
}
func TestDeadlineSetTime(t *testing.T) {
for _, tt := range deadlineSetTimeTests {
var d deadline
d.setTime(tt.input)
actual := d.value()
expected := int64(0)
if !tt.input.IsZero() {
expected = tt.input.UnixNano()
}
if actual != expected {
t.Errorf("set/value failed: expected %v, actual %v", expected, actual)
}
}
}
var deadlineExpiredTests = []struct {
deadline time.Time
expired bool
}{
// note, times are relative to the start of the test run, not
// the start of TestDeadlineExpired
{time.Now().Add(5 * time.Minute), false},
{time.Now().Add(-5 * time.Minute), true},
{time.Time{}, false}, // no deadline set
}
func TestDeadlineExpired(t *testing.T) {
for _, tt := range deadlineExpiredTests {
var d deadline
d.set(tt.deadline.UnixNano())
expired := d.expired()
if expired != tt.expired {
t.Errorf("expire failed: expected %v, actual %v", tt.expired, expired)
}
}
}
......@@ -37,11 +37,11 @@ type netFD struct {
laddr Addr
raddr Addr
// owned by client
rdeadline int64
rio sync.Mutex
wdeadline int64
wio sync.Mutex
// serialize access to Read and Write methods
rio, wio sync.Mutex
// read and write deadlines
rdeadline, wdeadline deadline
// owned by fd wait server
ncr, ncw int
......@@ -82,11 +82,11 @@ func (s *pollServer) AddFD(fd *netFD, mode int) error {
key := intfd << 1
if mode == 'r' {
fd.ncr++
t = fd.rdeadline
t = fd.rdeadline.value()
} else {
fd.ncw++
key++
t = fd.wdeadline
t = fd.wdeadline.value()
}
s.pending[key] = fd
doWakeup := false
......@@ -153,12 +153,8 @@ func (s *pollServer) WakeFD(fd *netFD, mode int, err error) {
}
}
func (s *pollServer) Now() int64 {
return time.Now().UnixNano()
}
func (s *pollServer) CheckDeadlines() {
now := s.Now()
now := time.Now().UnixNano()
// TODO(rsc): This will need to be handled more efficiently,
// probably with a heap indexed by wakeup time.
......@@ -172,21 +168,19 @@ func (s *pollServer) CheckDeadlines() {
mode = 'w'
}
if mode == 'r' {
t = fd.rdeadline
t = fd.rdeadline.value()
} else {
t = fd.wdeadline
t = fd.wdeadline.value()
}
if t > 0 {
if t <= now {
delete(s.pending, key)
if mode == 'r' {
s.poll.DelFD(fd.sysfd, mode)
fd.rdeadline = -1
} else {
s.poll.DelFD(fd.sysfd, mode)
fd.wdeadline = -1
}
s.WakeFD(fd, mode, nil)
s.WakeFD(fd, mode, errTimeout)
} else if nextDeadline == 0 || t < nextDeadline {
nextDeadline = t
}
......@@ -200,15 +194,15 @@ func (s *pollServer) Run() {
s.Lock()
defer s.Unlock()
for {
var t = s.deadline
if t > 0 {
t = t - s.Now()
if t <= 0 {
var timeout int64 // nsec to wait for or 0 for none
if s.deadline > 0 {
timeout = s.deadline - time.Now().UnixNano()
if timeout <= 0 {
s.CheckDeadlines()
continue
}
}
fd, mode, err := s.poll.WaitFD(s, t)
fd, mode, err := s.poll.WaitFD(s, timeout)
if err != nil {
print("pollServer WaitFD: ", err.Error(), "\n")
return
......@@ -329,14 +323,10 @@ func (fd *netFD) name() string {
func (fd *netFD) connect(ra syscall.Sockaddr) error {
err := syscall.Connect(fd.sysfd, ra)
hadTimeout := fd.wdeadline > 0
if err == syscall.EINPROGRESS {
if err = fd.pollServer.WaitWrite(fd); err != nil {
return err
}
if hadTimeout && fd.wdeadline < 0 {
return errTimeout
}
var e int
e, err = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
if err != nil {
......@@ -381,8 +371,8 @@ func (fd *netFD) decref() {
func (fd *netFD) Close() error {
fd.pollServer.Lock() // needed for both fd.incref(true) and pollserver.Evict
defer fd.pollServer.Unlock()
if err := fd.incref(true); err != nil {
fd.pollServer.Unlock()
return err
}
// Unblock any I/O. Once it all unblocks and returns,
......@@ -391,6 +381,7 @@ func (fd *netFD) Close() error {
// fairly quickly, since all the I/O is non-blocking, and any
// attempts to block in the pollserver will return errClosing.
fd.pollServer.Evict(fd)
fd.pollServer.Unlock()
fd.decref()
return nil
}
......@@ -423,20 +414,20 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
}
defer fd.decref()
for {
n, err = syscall.Read(int(fd.sysfd), p)
if err == syscall.EAGAIN {
if fd.rdeadline.expired() {
err = errTimeout
if fd.rdeadline >= 0 {
break
}
n, err = syscall.Read(int(fd.sysfd), p)
if err != nil {
n = 0
if err == syscall.EAGAIN {
if err = fd.pollServer.WaitRead(fd); err == nil {
continue
}
}
}
if err != nil {
n = 0
} else if n == 0 && err == nil && fd.sotype != syscall.SOCK_DGRAM {
err = io.EOF
}
err = chkReadErr(n, err, fd)
break
}
if err != nil && err != io.EOF {
......@@ -453,18 +444,20 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
}
defer fd.decref()
for {
n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0)
if err == syscall.EAGAIN {
if fd.rdeadline.expired() {
err = errTimeout
if fd.rdeadline >= 0 {
break
}
n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0)
if err != nil {
n = 0
if err == syscall.EAGAIN {
if err = fd.pollServer.WaitRead(fd); err == nil {
continue
}
}
}
if err != nil {
n = 0
}
err = chkReadErr(n, err, fd)
break
}
if err != nil && err != io.EOF {
......@@ -481,41 +474,47 @@ func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S
}
defer fd.decref()
for {
n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
if err == syscall.EAGAIN {
if fd.rdeadline.expired() {
err = errTimeout
if fd.rdeadline >= 0 {
break
}
n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
if err != nil {
// TODO(dfc) should n and oobn be set to 0
if err == syscall.EAGAIN {
if err = fd.pollServer.WaitRead(fd); err == nil {
continue
}
}
}
if err == nil && n == 0 {
err = io.EOF
}
err = chkReadErr(n, err, fd)
break
}
if err != nil && err != io.EOF {
err = &OpError{"read", fd.net, fd.laddr, err}
return
}
return
}
func (fd *netFD) Write(p []byte) (int, error) {
func chkReadErr(n int, err error, fd *netFD) error {
if n == 0 && err == nil && fd.sotype != syscall.SOCK_DGRAM && fd.sotype != syscall.SOCK_RAW {
return io.EOF
}
return err
}
func (fd *netFD) Write(p []byte) (nn int, err error) {
fd.wio.Lock()
defer fd.wio.Unlock()
if err := fd.incref(false); err != nil {
return 0, err
}
defer fd.decref()
if fd.sysfile == nil {
return 0, syscall.EINVAL
}
var err error
nn := 0
for {
if fd.wdeadline.expired() {
err = errTimeout
break
}
var n int
n, err = syscall.Write(int(fd.sysfd), p[nn:])
if n > 0 {
......@@ -525,11 +524,8 @@ func (fd *netFD) Write(p []byte) (int, error) {
break
}
if err == syscall.EAGAIN {
err = errTimeout
if fd.wdeadline >= 0 {
if err = fd.pollServer.WaitWrite(fd); err == nil {
continue
}
if err = fd.pollServer.WaitWrite(fd); err == nil {
continue
}
}
if err != nil {
......@@ -555,13 +551,14 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
}
defer fd.decref()
for {
if fd.wdeadline.expired() {
err = errTimeout
break
}
err = syscall.Sendto(fd.sysfd, p, 0, sa)
if err == syscall.EAGAIN {
err = errTimeout
if fd.wdeadline >= 0 {
if err = fd.pollServer.WaitWrite(fd); err == nil {
continue
}
if err = fd.pollServer.WaitWrite(fd); err == nil {
continue
}
}
break
......@@ -582,13 +579,14 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
}
defer fd.decref()
for {
if fd.wdeadline.expired() {
err = errTimeout
break
}
err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0)
if err == syscall.EAGAIN {
err = errTimeout
if fd.wdeadline >= 0 {
if err = fd.pollServer.WaitWrite(fd); err == nil {
continue
}
if err = fd.pollServer.WaitWrite(fd); err == nil {
continue
}
}
break
......@@ -619,11 +617,8 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e
if err != nil {
syscall.ForkLock.RUnlock()
if err == syscall.EAGAIN {
err = errTimeout
if fd.rdeadline >= 0 {
if err = fd.pollServer.WaitRead(fd); err == nil {
continue
}
if err = fd.pollServer.WaitRead(fd); err == nil {
continue
}
} else if err == syscall.ECONNABORTED {
// This means that a socket on the listen queue was closed
......
......@@ -7,33 +7,34 @@
package net
import (
"io"
"syscall"
"testing"
)
// Issue 3590. netFd.AddFD should return an error
// from the underlying pollster rather than panicing.
func TestAddFDReturnsError(t *testing.T) {
l, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer l.Close()
ln := newLocalListener(t).(*TCPListener)
defer ln.Close()
connected := make(chan bool)
go func() {
for {
c, err := l.Accept()
c, err := ln.Accept()
if err != nil {
return
}
connected <- true
defer c.Close()
}
}()
c, err := Dial("tcp", l.Addr().String())
c, err := DialTCP("tcp", nil, ln.Addr().(*TCPAddr))
if err != nil {
t.Fatal(err)
}
defer c.Close()
<-connected
// replace c's pollServer with a closed version.
ps, err := newPollServer()
......@@ -41,7 +42,7 @@ func TestAddFDReturnsError(t *testing.T) {
t.Fatal(err)
}
ps.poll.Close()
c.(*TCPConn).conn.fd.pollServer = ps
c.conn.fd.pollServer = ps
var b [1]byte
_, err = c.Read(b[:])
......@@ -56,5 +57,50 @@ func TestAddFDReturnsError(t *testing.T) {
}
}
}
t.Error(err)
t.Error("unexpected error:", err)
}
var chkReadErrTests = []struct {
n int
err error
fd *netFD
expected error
}{
{100, nil, &netFD{sotype: syscall.SOCK_STREAM}, nil},
{100, io.EOF, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF},
{100, errClosing, &netFD{sotype: syscall.SOCK_STREAM}, errClosing},
{0, nil, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF},
{0, io.EOF, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF},
{0, errClosing, &netFD{sotype: syscall.SOCK_STREAM}, errClosing},
{100, nil, &netFD{sotype: syscall.SOCK_DGRAM}, nil},
{100, io.EOF, &netFD{sotype: syscall.SOCK_DGRAM}, io.EOF},
{100, errClosing, &netFD{sotype: syscall.SOCK_DGRAM}, errClosing},
{0, nil, &netFD{sotype: syscall.SOCK_DGRAM}, nil},
{0, io.EOF, &netFD{sotype: syscall.SOCK_DGRAM}, io.EOF},
{0, errClosing, &netFD{sotype: syscall.SOCK_DGRAM}, errClosing},
{100, nil, &netFD{sotype: syscall.SOCK_SEQPACKET}, nil},
{100, io.EOF, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF},
{100, errClosing, &netFD{sotype: syscall.SOCK_SEQPACKET}, errClosing},
{0, nil, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF},
{0, io.EOF, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF},
{0, errClosing, &netFD{sotype: syscall.SOCK_SEQPACKET}, errClosing},
{100, nil, &netFD{sotype: syscall.SOCK_RAW}, nil},
{100, io.EOF, &netFD{sotype: syscall.SOCK_RAW}, io.EOF},
{100, errClosing, &netFD{sotype: syscall.SOCK_RAW}, errClosing},
{0, nil, &netFD{sotype: syscall.SOCK_RAW}, nil},
{0, io.EOF, &netFD{sotype: syscall.SOCK_RAW}, io.EOF},
{0, errClosing, &netFD{sotype: syscall.SOCK_RAW}, errClosing},
}
func TestChkReadErr(t *testing.T) {
for _, tt := range chkReadErrTests {
actual := chkReadErr(tt.n, tt.err, tt.fd)
if actual != tt.expected {
t.Errorf("chkReadError(%v, %v, %v): expected %v, actual %v", tt.n, tt.err, tt.fd.sotype, tt.expected, actual)
}
}
}
......@@ -169,6 +169,15 @@ func (s *ioSrv) ProcessRemoteIO() {
func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) {
var err error
o := oi.Op()
// Calculate timeout delta.
var delta int64
if deadline != 0 {
delta = deadline - time.Now().UnixNano()
if delta <= 0 {
return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, errTimeout}
}
}
// Start IO.
if canCancelIO {
err = oi.Submit()
} else {
......@@ -188,12 +197,8 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) {
}
// Setup timer, if deadline is given.
var timer <-chan time.Time
if deadline != 0 {
dt := deadline - time.Now().UnixNano()
if dt < 1 {
dt = 1
}
t := time.NewTimer(time.Duration(dt) * time.Nanosecond)
if delta > 0 {
t := time.NewTimer(time.Duration(delta) * time.Nanosecond)
defer t.Stop()
timer = t.C
}
......@@ -280,11 +285,11 @@ type netFD struct {
errnoc [2]chan error // read/write submit or cancel operation errors
closec chan bool // used by Close to cancel pending IO
// owned by client
rdeadline int64
rio sync.Mutex
wdeadline int64
wio sync.Mutex
// serialize access to Read and Write methods
rio, wio sync.Mutex
// read and write deadlines
rdeadline, wdeadline deadline
}
func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD {
......@@ -295,7 +300,6 @@ func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD {
net: net,
closec: make(chan bool),
}
runtime.SetFinalizer(netfd, (*netFD).Close)
return netfd
}
......@@ -314,6 +318,7 @@ func newFD(fd syscall.Handle, family, proto int, net string) (*netFD, error) {
func (fd *netFD) setAddr(laddr, raddr Addr) {
fd.laddr = laddr
fd.raddr = raddr
runtime.SetFinalizer(fd, (*netFD).closesocket)
}
func (fd *netFD) connect(ra syscall.Sockaddr) error {
......@@ -393,6 +398,10 @@ func (fd *netFD) CloseWrite() error {
return fd.shutdown(syscall.SHUT_WR)
}
func (fd *netFD) closesocket() error {
return closesocket(fd.sysfd)
}
// Read from network.
type readOp struct {
......@@ -417,7 +426,7 @@ func (fd *netFD) Read(buf []byte) (int, error) {
defer fd.rio.Unlock()
var o readOp
o.Init(fd, buf, 'r')
n, err := iosrv.ExecIO(&o, fd.rdeadline)
n, err := iosrv.ExecIO(&o, fd.rdeadline.value())
if err == nil && n == 0 {
err = io.EOF
}
......@@ -454,7 +463,7 @@ func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
var o readFromOp
o.Init(fd, buf, 'r')
o.rsan = int32(unsafe.Sizeof(o.rsa))
n, err = iosrv.ExecIO(&o, fd.rdeadline)
n, err = iosrv.ExecIO(&o, fd.rdeadline.value())
if err != nil {
return 0, nil, err
}
......@@ -486,7 +495,7 @@ func (fd *netFD) Write(buf []byte) (int, error) {
defer fd.wio.Unlock()
var o writeOp
o.Init(fd, buf, 'w')
return iosrv.ExecIO(&o, fd.wdeadline)
return iosrv.ExecIO(&o, fd.wdeadline.value())
}
// WriteTo to network.
......@@ -518,7 +527,7 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {
var o writeToOp
o.Init(fd, buf, 'w')
o.sa = sa
return iosrv.ExecIO(&o, fd.wdeadline)
return iosrv.ExecIO(&o, fd.wdeadline.value())
}
// Accept new network connections.
......@@ -552,7 +561,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
s, err := syscall.Socket(fd.family, fd.sotype, 0)
if err != nil {
syscall.ForkLock.RUnlock()
return nil, err
return nil, &OpError{"socket", fd.net, fd.laddr, err}
}
syscall.CloseOnExec(s)
syscall.ForkLock.RUnlock()
......@@ -560,6 +569,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
// Associate our new socket with IOCP.
onceStartServer.Do(startServer)
if _, err := syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); err != nil {
closesocket(s)
return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, err}
}
......@@ -567,7 +577,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
var o acceptOp
o.Init(fd, 'r')
o.newsock = s
_, err = iosrv.ExecIO(&o, fd.rdeadline)
_, err = iosrv.ExecIO(&o, fd.rdeadline.value())
if err != nil {
closesocket(s)
return nil, err
......@@ -577,7 +587,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd)))
if err != nil {
closesocket(s)
return nil, err
return nil, &OpError{"Setsockopt", fd.net, fd.laddr, err}
}
// Get local and peer addr out of AcceptEx buffer.
......
......@@ -19,8 +19,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) {
return nil, syscall.EPLAN9
}
......
......@@ -405,7 +405,8 @@ func TestDirUnix(t *testing.T) {
}
func TestDirWindows(t *testing.T) {
if skipTest(t) || runtime.GOOS != "windows" {
if runtime.GOOS != "windows" {
t.Logf("Skipping windows specific test.")
return
}
......@@ -415,6 +416,7 @@ func TestDirWindows(t *testing.T) {
var err error
perl, err = exec.LookPath("perl")
if err != nil {
t.Logf("Skipping test: perl not found.")
return
}
perl, _ = filepath.Abs(perl)
......@@ -457,6 +459,7 @@ func TestEnvOverride(t *testing.T) {
var err error
perl, err = exec.LookPath("perl")
if err != nil {
t.Logf("Skipping test: perl not found.")
return
}
perl, _ = filepath.Abs(perl)
......
......@@ -10,23 +10,6 @@ use Cwd;
binmode STDOUT;
sub on_windows {
return $^O eq 'MSWin32' || $^O eq 'msys';
}
# normalize_windows_path normalizes the various Windows Perl path
# formats into Go's format.
sub normalize_windows_path {
my $dir = shift;
return $dir unless on_windows();
$dir =~ s!^[a-z]:!uc($&)!e;
if ($dir =~ s!^/([a-zA-Z])/!!) {
$dir = uc($1) . ":\\$dir";
}
$dir =~ s!/!\\!g;
return $dir;
}
my $q = MiniCGI->new;
my $params = $q->Vars;
......@@ -35,40 +18,43 @@ if ($params->{"loc"}) {
exit(0);
}
my $NL = "\r\n";
$NL = "\n" if $params->{mode} eq "NL";
my $p = sub {
print "$_[0]$NL";
};
# With carriage returns
$p->("Content-Type: text/html");
$p->("X-CGI-Pid: $$");
$p->("X-Test-Header: X-Test-Value");
$p->("");
print "Content-Type: text/html\r\n";
print "X-CGI-Pid: $$\r\n";
print "X-Test-Header: X-Test-Value\r\n";
print "\r\n";
if ($params->{"bigresponse"}) {
for (1..1024) {
print "A" x 1024, "\n";
print "A" x 1024, "\r\n";
}
exit 0;
}
print "test=Hello CGI\n";
print "test=Hello CGI\r\n";
foreach my $k (sort keys %$params) {
print "param-$k=$params->{$k}\n";
print "param-$k=$params->{$k}\r\n";
}
foreach my $k (sort keys %ENV) {
my $clean_env = $ENV{$k};
$clean_env =~ s/[\n\r]//g;
print "env-$k=$clean_env\n";
print "env-$k=$clean_env\r\n";
}
my $dir = normalize_windows_path(getcwd());
print "cwd=$dir\n";
# NOTE: msys perl returns /c/go/src/... not C:\go\....
my $dir = getcwd();
if ($^O eq 'MSWin32' || $^O eq 'msys') {
if ($dir =~ /^.:/) {
$dir =~ s!/!\\!g;
} else {
my $cmd = $ENV{'COMSPEC'} || 'c:\\windows\\system32\\cmd.exe';
$cmd =~ s!\\!/!g;
$dir = `$cmd /c cd`;
chomp $dir;
}
}
print "cwd=$dir\r\n";
# A minimal version of CGI.pm, for people without the perl-modules
# package installed. (CGI.pm used to be part of the Perl core, but
......@@ -102,24 +88,3 @@ sub _urldecode {
$v =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
return $v;
}
package Tests;
sub test_normalize_windows_paths {
my @tests = (
{in => "C:\\foo\\bar", want => "C:\\foo\\bar"},
{in => "C:/foo/bar", want => "C:\\foo\\bar"},
{in => "c:/foo/bar", want => "C:\\foo\\bar"},
{in => "/c/foo/bar", want => "C:\\foo\\bar"},
);
foreach my $tt (@tests) {
my $got = ::normalize_windows_path($tt->{in});
unless ($got eq $tt->{want}) {
die "For path $tt->{in}, normalize = $got; want $tt->{want}\n";
}
}
}
BEGIN {
test_normalize_windows_paths() if ::on_windows();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment