libgo: Update to revision 15193:6fdc1974457c of master library.

From-SVN: r194692
This commit is contained in:
Ian Lance Taylor 2012-12-22 01:15:33 +00:00
parent 7e9268b4cf
commit 409a5e7eb4
104 changed files with 2434 additions and 686 deletions

View File

@ -1,4 +1,4 @@
c031aa767edf
6fdc1974457c
The first line of this file holds the Mercurial revision number of the
last merge done from the master library sources.

View File

@ -502,6 +502,7 @@ runtime_files = \
runtime/go-unwind.c \
runtime/chan.c \
runtime/cpuprof.c \
runtime/env_posix.c \
runtime/lfstack.c \
$(runtime_lock_files) \
runtime/mcache.c \
@ -1657,6 +1658,13 @@ else
syscall_lsf_file =
endif
# GNU/Linux specific utimesnano support.
if LIBGO_IS_LINUX
syscall_utimesnano_file = go/syscall/libcall_linux_utimesnano.go
else
syscall_utimesnano_file = go/syscall/libcall_posix_utimesnano.go
endif
go_base_syscall_files = \
go/syscall/env_unix.go \
go/syscall/syscall_errno.go \
@ -1679,6 +1687,7 @@ go_base_syscall_files = \
$(syscall_uname_file) \
$(syscall_netlink_file) \
$(syscall_lsf_file) \
$(syscall_utimesnano_file) \
$(GO_LIBCALL_OS_FILE) \
$(GO_LIBCALL_OS_ARCH_FILE) \
$(GO_SYSCALL_OS_FILE) \

View File

@ -213,12 +213,13 @@ am__objects_5 = go-append.lo go-assert.lo go-assert-interface.lo \
go-type-float.lo go-type-identity.lo go-type-interface.lo \
go-type-string.lo go-typedesc-equal.lo go-typestring.lo \
go-unsafe-new.lo go-unsafe-newarray.lo go-unsafe-pointer.lo \
go-unwind.lo chan.lo cpuprof.lo lfstack.lo $(am__objects_1) \
mcache.lo mcentral.lo $(am__objects_2) mfinal.lo mfixalloc.lo \
mgc0.lo mheap.lo msize.lo panic.lo parfor.lo print.lo proc.lo \
runtime.lo signal_unix.lo thread.lo yield.lo $(am__objects_3) \
iface.lo malloc.lo map.lo mprof.lo reflect.lo runtime1.lo \
sema.lo sigqueue.lo string.lo time.lo $(am__objects_4)
go-unwind.lo chan.lo cpuprof.lo env_posix.lo lfstack.lo \
$(am__objects_1) mcache.lo mcentral.lo $(am__objects_2) \
mfinal.lo mfixalloc.lo mgc0.lo mheap.lo msize.lo panic.lo \
parfor.lo print.lo proc.lo runtime.lo signal_unix.lo thread.lo \
yield.lo $(am__objects_3) iface.lo malloc.lo map.lo mprof.lo \
reflect.lo runtime1.lo sema.lo sigqueue.lo string.lo time.lo \
$(am__objects_4)
am_libgo_la_OBJECTS = $(am__objects_5)
libgo_la_OBJECTS = $(am_libgo_la_OBJECTS)
libgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
@ -835,6 +836,7 @@ runtime_files = \
runtime/go-unwind.c \
runtime/chan.c \
runtime/cpuprof.c \
runtime/env_posix.c \
runtime/lfstack.c \
$(runtime_lock_files) \
runtime/mcache.c \
@ -1840,6 +1842,10 @@ go_unicode_utf8_files = \
# GNU/Linux specific socket filters.
@LIBGO_IS_LINUX_TRUE@syscall_lsf_file = go/syscall/lsf_linux.go
@LIBGO_IS_LINUX_FALSE@syscall_utimesnano_file = go/syscall/libcall_posix_utimesnano.go
# GNU/Linux specific utimesnano support.
@LIBGO_IS_LINUX_TRUE@syscall_utimesnano_file = go/syscall/libcall_linux_utimesnano.go
go_base_syscall_files = \
go/syscall/env_unix.go \
go/syscall/syscall_errno.go \
@ -1862,6 +1868,7 @@ go_base_syscall_files = \
$(syscall_uname_file) \
$(syscall_netlink_file) \
$(syscall_lsf_file) \
$(syscall_utimesnano_file) \
$(GO_LIBCALL_OS_FILE) \
$(GO_LIBCALL_OS_ARCH_FILE) \
$(GO_SYSCALL_OS_FILE) \
@ -2418,6 +2425,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chan.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpuprof.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/env_posix.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getncpu-bsd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getncpu-irix.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getncpu-linux.Plo@am__quote@
@ -3027,6 +3035,13 @@ cpuprof.lo: runtime/cpuprof.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cpuprof.lo `test -f 'runtime/cpuprof.c' || echo '$(srcdir)/'`runtime/cpuprof.c
env_posix.lo: runtime/env_posix.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT env_posix.lo -MD -MP -MF $(DEPDIR)/env_posix.Tpo -c -o env_posix.lo `test -f 'runtime/env_posix.c' || echo '$(srcdir)/'`runtime/env_posix.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/env_posix.Tpo $(DEPDIR)/env_posix.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/env_posix.c' object='env_posix.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o env_posix.lo `test -f 'runtime/env_posix.c' || echo '$(srcdir)/'`runtime/env_posix.c
lfstack.lo: runtime/lfstack.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lfstack.lo -MD -MP -MF $(DEPDIR)/lfstack.Tpo -c -o lfstack.lo `test -f 'runtime/lfstack.c' || echo '$(srcdir)/'`runtime/lfstack.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/lfstack.Tpo $(DEPDIR)/lfstack.Plo

View File

@ -124,8 +124,8 @@ func append(slice []Type, elems ...Type) []Type
func copy(dst, src []Type) int
// The delete built-in function deletes the element with the specified key
// (m[key]) from the map. If there is no such element, delete is a no-op.
// If m is nil, delete panics.
// (m[key]) from the map. If m is nil or there is no such element, delete
// is a no-op.
func delete(m map[Type]Type1, key Type)
// The len built-in function returns the length of v, according to its type:

View File

@ -22,7 +22,7 @@ const (
logMaxOffsetSize = 15 // Standard DEFLATE
minMatchLength = 3 // The smallest match that the compressor looks for
maxMatchLength = 258 // The longest match for the compressor
minOffsetSize = 1 // The shortest offset that makes any sence
minOffsetSize = 1 // The shortest offset that makes any sense
// The maximum number of tokens we put into a single flat block, just too
// stop things from getting too large.

View File

@ -241,7 +241,7 @@ func ExampleStreamReader() {
// Note that this example is simplistic in that it omits any
// authentication of the encrypted data. It you were actually to use
// StreamReader in this manner, an attacker could flip arbitary bits in
// StreamReader in this manner, an attacker could flip arbitrary bits in
// the output.
}
@ -278,6 +278,6 @@ func ExampleStreamWriter() {
// Note that this example is simplistic in that it omits any
// authentication of the encrypted data. It you were actually to use
// StreamReader in this manner, an attacker could flip arbitary bits in
// StreamReader in this manner, an attacker could flip arbitrary bits in
// the decrypted result.
}

View File

@ -439,7 +439,7 @@ func TestECDSA(t *testing.T) {
t.Errorf("%d: public key algorithm is %v, want ECDSA", i, pka)
}
if err = cert.CheckSignatureFrom(cert); err != nil {
t.Errorf("%d: certificate verfication failed: %s", i, err)
t.Errorf("%d: certificate verification failed: %s", i, err)
}
}
}
@ -519,7 +519,7 @@ func TestVerifyCertificateWithDSASignature(t *testing.T) {
}
// test cert is self-signed
if err = cert.CheckSignatureFrom(cert); err != nil {
t.Fatalf("DSA Certificate verfication failed: %s", err)
t.Fatalf("DSA Certificate verification failed: %s", err)
}
}

View File

@ -42,9 +42,10 @@ type fakeDriver struct {
type fakeDB struct {
name string
mu sync.Mutex
free []*fakeConn
tables map[string]*table
mu sync.Mutex
free []*fakeConn
tables map[string]*table
badConn bool
}
type table struct {
@ -83,6 +84,7 @@ type fakeConn struct {
stmtsMade int
stmtsClosed int
numPrepare int
bad bool
}
func (c *fakeConn) incrStat(v *int) {
@ -122,7 +124,9 @@ func init() {
// Supports dsn forms:
// <dbname>
// <dbname>;<opts> (no currently supported options)
// <dbname>;<opts> (only currently supported option is `badConn`,
// which causes driver.ErrBadConn to be returned on
// every other conn.Begin())
func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
parts := strings.Split(dsn, ";")
if len(parts) < 1 {
@ -135,7 +139,12 @@ func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
d.mu.Lock()
d.openCount++
d.mu.Unlock()
return &fakeConn{db: db}, nil
conn := &fakeConn{db: db}
if len(parts) >= 2 && parts[1] == "badConn" {
conn.bad = true
}
return conn, nil
}
func (d *fakeDriver) getDB(name string) *fakeDB {
@ -199,7 +208,20 @@ func (db *fakeDB) columnType(table, column string) (typ string, ok bool) {
return "", false
}
func (c *fakeConn) isBad() bool {
// if not simulating bad conn, do nothing
if !c.bad {
return false
}
// alternate between bad conn and not bad conn
c.db.badConn = !c.db.badConn
return c.db.badConn
}
func (c *fakeConn) Begin() (driver.Tx, error) {
if c.isBad() {
return nil, driver.ErrBadConn
}
if c.currTx != nil {
return nil, errors.New("already in a transaction")
}

View File

@ -266,7 +266,7 @@ func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) {
var putConnHook func(*DB, driver.Conn)
// putConn adds a connection to the db's free pool.
// err is optionally the last error that occured on this connection.
// err is optionally the last error that occurred on this connection.
func (db *DB) putConn(c driver.Conn, err error) {
if err == driver.ErrBadConn {
// Don't reuse bad connections.
@ -426,7 +426,7 @@ func (db *DB) begin() (tx *Tx, err error) {
txi, err := ci.Begin()
if err != nil {
db.putConn(ci, err)
return nil, fmt.Errorf("sql: failed to Begin transaction: %v", err)
return nil, err
}
return &Tx{
db: db,

View File

@ -402,6 +402,39 @@ func TestTxQueryInvalid(t *testing.T) {
}
}
// Tests fix for issue 4433, that retries in Begin happen when
// conn.Begin() returns ErrBadConn
func TestTxErrBadConn(t *testing.T) {
db, err := Open("test", fakeDBName+";badConn")
if err != nil {
t.Fatalf("Open: %v", err)
}
if _, err := db.Exec("WIPE"); err != nil {
t.Fatalf("exec wipe: %v", err)
}
defer closeDB(t, db)
exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool")
stmt, err := db.Prepare("INSERT|t1|name=?,age=?")
if err != nil {
t.Fatalf("Stmt, err = %v, %v", stmt, err)
}
defer stmt.Close()
tx, err := db.Begin()
if err != nil {
t.Fatalf("Begin = %v", err)
}
txs := tx.Stmt(stmt)
defer txs.Close()
_, err = txs.Exec("Bobby", 7)
if err != nil {
t.Fatalf("Exec = %v", err)
}
err = tx.Commit()
if err != nil {
t.Fatalf("Commit = %v", err)
}
}
// Tests fix for issue 2542, that we release a lock when querying on
// a closed connection.
func TestIssue2542Deadlock(t *testing.T) {

View File

@ -272,7 +272,8 @@ func NewFile(r io.ReaderAt) (*File, error) {
shnum = int(hdr.Shnum)
shstrndx = int(hdr.Shstrndx)
}
if shstrndx < 0 || shstrndx >= shnum {
if shnum > 0 && shoff > 0 && (shstrndx < 0 || shstrndx >= shnum) {
return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx}
}
@ -367,6 +368,10 @@ func NewFile(r io.ReaderAt) (*File, error) {
f.Sections[i] = s
}
if len(f.Sections) == 0 {
return f, nil
}
// Load section header string table.
shstrtab, err := f.Sections[shstrndx].Data()
if err != nil {

View File

@ -5,10 +5,14 @@
package elf
import (
"bytes"
"compress/gzip"
"debug/dwarf"
"encoding/binary"
"io"
"net"
"os"
"path"
"reflect"
"runtime"
"testing"
@ -121,15 +125,49 @@ var fileTests = []fileTest{
},
[]string{"libc.so.6"},
},
{
"testdata/hello-world-core.gz",
FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0x0, binary.LittleEndian, ET_CORE, EM_X86_64, 0x0},
[]SectionHeader{},
[]ProgHeader{
{Type: PT_NOTE, Flags: 0x0, Off: 0x3f8, Vaddr: 0x0, Paddr: 0x0, Filesz: 0x8ac, Memsz: 0x0, Align: 0x0},
{Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x1000, Vaddr: 0x400000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1000, Align: 0x1000},
{Type: PT_LOAD, Flags: PF_R, Off: 0x1000, Vaddr: 0x401000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
{Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x2000, Vaddr: 0x402000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
{Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3000, Vaddr: 0x7f54078b8000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1b5000, Align: 0x1000},
{Type: PT_LOAD, Flags: 0x0, Off: 0x3000, Vaddr: 0x7f5407a6d000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1ff000, Align: 0x1000},
{Type: PT_LOAD, Flags: PF_R, Off: 0x3000, Vaddr: 0x7f5407c6c000, Paddr: 0x0, Filesz: 0x4000, Memsz: 0x4000, Align: 0x1000},
{Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x7000, Vaddr: 0x7f5407c70000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000},
{Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x9000, Vaddr: 0x7f5407c72000, Paddr: 0x0, Filesz: 0x5000, Memsz: 0x5000, Align: 0x1000},
{Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0xe000, Vaddr: 0x7f5407c77000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x22000, Align: 0x1000},
{Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0xe000, Vaddr: 0x7f5407e81000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000},
{Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x11000, Vaddr: 0x7f5407e96000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000},
{Type: PT_LOAD, Flags: PF_R, Off: 0x14000, Vaddr: 0x7f5407e99000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
{Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x15000, Vaddr: 0x7f5407e9a000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000},
{Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x17000, Vaddr: 0x7fff79972000, Paddr: 0x0, Filesz: 0x23000, Memsz: 0x23000, Align: 0x1000},
{Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3a000, Vaddr: 0x7fff799f8000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
{Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3b000, Vaddr: 0xffffffffff600000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
},
nil,
},
}
func TestOpen(t *testing.T) {
for i := range fileTests {
tt := &fileTests[i]
f, err := Open(tt.file)
var f *File
var err error
if path.Ext(tt.file) == ".gz" {
var r io.ReaderAt
if r, err = decompress(tt.file); err == nil {
f, err = NewFile(r)
}
} else {
f, err = Open(tt.file)
}
if err != nil {
t.Error(err)
t.Errorf("cannot open file %s: %v", tt.file, err)
continue
}
if !reflect.DeepEqual(f.FileHeader, tt.hdr) {
@ -175,6 +213,23 @@ func TestOpen(t *testing.T) {
}
}
// elf.NewFile requires io.ReaderAt, which compress/gzip cannot
// provide. Decompress the file to a bytes.Reader.
func decompress(gz string) (io.ReaderAt, error) {
in, err := os.Open(gz)
if err != nil {
return nil, err
}
defer in.Close()
r, err := gzip.NewReader(in)
if err != nil {
return nil, err
}
var out bytes.Buffer
_, err = io.Copy(&out, r)
return bytes.NewReader(out.Bytes()), err
}
type relocationTestEntry struct {
entryNumber int
entry *dwarf.Entry

Binary file not shown.

View File

@ -92,10 +92,17 @@ func (w *Writer) Write(record []string) (err error) {
}
// Flush writes any buffered data to the underlying io.Writer.
// To check if an error occurred during the Flush, call Error.
func (w *Writer) Flush() {
w.w.Flush()
}
// Error reports any error that has occurred during a previous Write or Flush.
func (w *Writer) Error() error {
_, err := w.w.Write(nil)
return err
}
// WriteAll writes multiple CSV records to w using Write and then calls Flush.
func (w *Writer) WriteAll(records [][]string) (err error) {
for _, record := range records {

View File

@ -6,6 +6,7 @@ package csv
import (
"bytes"
"errors"
"testing"
)
@ -42,3 +43,30 @@ func TestWrite(t *testing.T) {
}
}
}
type errorWriter struct{}
func (e errorWriter) Write(b []byte) (int, error) {
return 0, errors.New("Test")
}
func TestError(t *testing.T) {
b := &bytes.Buffer{}
f := NewWriter(b)
f.Write([]string{"abc"})
f.Flush()
err := f.Error()
if err != nil {
t.Errorf("Unexpected error: %s\n", err)
}
f = NewWriter(errorWriter{})
f.Write([]string{"abc"})
f.Flush()
err = f.Error()
if err == nil {
t.Error("Error should not be nil")
}
}

View File

@ -62,15 +62,15 @@ func overflow(name string) error {
// Used only by the Decoder to read the message length.
func decodeUintReader(r io.Reader, buf []byte) (x uint64, width int, err error) {
width = 1
_, err = r.Read(buf[0:width])
if err != nil {
n, err := io.ReadFull(r, buf[0:width])
if n == 0 {
return
}
b := buf[0]
if b <= 0x7f {
return uint64(b), width, nil
}
n := -int(int8(b))
n = -int(int8(b))
if n > uint64Size {
err = errBadUint
return

View File

@ -125,13 +125,12 @@ func (d *decodeState) unmarshal(v interface{}) (err error) {
}()
rv := reflect.ValueOf(v)
pv := rv
if pv.Kind() != reflect.Ptr || pv.IsNil() {
if rv.Kind() != reflect.Ptr || rv.IsNil() {
return &InvalidUnmarshalError{reflect.TypeOf(v)}
}
d.scan.reset()
// We decode rv not pv.Elem because the Unmarshaler interface
// We decode rv not rv.Elem because the Unmarshaler interface
// test must be applied at the top level of the value.
d.value(rv)
return d.savedError
@ -423,17 +422,12 @@ func (d *decodeState) object(v reflect.Value) {
v = pv
// Decoding into nil interface? Switch to non-reflect code.
iv := v
if iv.Kind() == reflect.Interface {
iv.Set(reflect.ValueOf(d.objectInterface()))
if v.Kind() == reflect.Interface {
v.Set(reflect.ValueOf(d.objectInterface()))
return
}
// Check type of target: struct or map[string]T
var (
mv reflect.Value
sv reflect.Value
)
switch v.Kind() {
case reflect.Map:
// map must have string type
@ -442,17 +436,15 @@ func (d *decodeState) object(v reflect.Value) {
d.saveError(&UnmarshalTypeError{"object", v.Type()})
break
}
mv = v
if mv.IsNil() {
mv.Set(reflect.MakeMap(t))
if v.IsNil() {
v.Set(reflect.MakeMap(t))
}
case reflect.Struct:
sv = v
default:
d.saveError(&UnmarshalTypeError{"object", v.Type()})
}
if !mv.IsValid() && !sv.IsValid() {
if !v.IsValid() {
d.off--
d.next() // skip over { } in input
return
@ -484,8 +476,8 @@ func (d *decodeState) object(v reflect.Value) {
var subv reflect.Value
destring := false // whether the value is wrapped in a string to be decoded first
if mv.IsValid() {
elemType := mv.Type().Elem()
if v.Kind() == reflect.Map {
elemType := v.Type().Elem()
if !mapElem.IsValid() {
mapElem = reflect.New(elemType).Elem()
} else {
@ -494,7 +486,7 @@ func (d *decodeState) object(v reflect.Value) {
subv = mapElem
} else {
var f *field
fields := cachedTypeFields(sv.Type())
fields := cachedTypeFields(v.Type())
for i := range fields {
ff := &fields[i]
if ff.name == key {
@ -506,7 +498,7 @@ func (d *decodeState) object(v reflect.Value) {
}
}
if f != nil {
subv = sv
subv = v
destring = f.quoted
for _, i := range f.index {
if subv.Kind() == reflect.Ptr {
@ -519,7 +511,7 @@ func (d *decodeState) object(v reflect.Value) {
}
} else {
// To give a good error, a quick scan for unexported fields in top level.
st := sv.Type()
st := v.Type()
for i := 0; i < st.NumField(); i++ {
f := st.Field(i)
if f.PkgPath != "" && strings.EqualFold(f.Name, key) {
@ -546,8 +538,8 @@ func (d *decodeState) object(v reflect.Value) {
}
// Write value back to map;
// if using struct, subv points into struct already.
if mv.IsValid() {
mv.SetMapIndex(reflect.ValueOf(key), subv)
if v.Kind() == reflect.Map {
v.SetMapIndex(reflect.ValueOf(key), subv)
}
// Next token must be , or }.

View File

@ -25,7 +25,9 @@ func runTest(t *testing.T, path string) {
} else {
// package directory
// TODO(gri) gotype should use the build package instead
pkg, err := build.Import(path, "", 0)
ctxt := build.Default
ctxt.CgoEnabled = false
pkg, err := ctxt.Import(path, "", 0)
if err != nil {
t.Errorf("build.Import error for path = %s: %s", path, err)
return
@ -50,7 +52,7 @@ var tests = []string{
// directories
// Note: packages that don't typecheck yet are commented out
// "archive/tar", // investigate
"archive/tar",
"archive/zip",
"bufio",
@ -77,13 +79,13 @@ var tests = []string{
"crypto/md5",
"crypto/rand",
"crypto/rc4",
// "crypto/rsa", // investigate (GOARCH=386)
// "crypto/rsa", // intermittent failure: /home/gri/go2/src/pkg/crypto/rsa/pkcs1v15.go:21:27: undeclared name: io
"crypto/sha1",
"crypto/sha256",
"crypto/sha512",
"crypto/subtle",
"crypto/tls",
// "crypto/x509", // investigate
"crypto/x509",
"crypto/x509/pkix",
"database/sql",
@ -99,9 +101,9 @@ var tests = []string{
"encoding/asn1",
"encoding/base32",
"encoding/base64",
// "encoding/binary", // complex() doesn't work yet
"encoding/binary",
"encoding/csv",
// "encoding/gob", // complex() doesn't work yet
"encoding/gob",
"encoding/hex",
"encoding/json",
"encoding/pem",
@ -117,7 +119,7 @@ var tests = []string{
"go/ast",
"go/build",
// "go/doc", // variadic parameters don't work yet fully
"go/doc",
"go/format",
"go/parser",
"go/printer",
@ -125,7 +127,7 @@ var tests = []string{
"go/token",
"hash/adler32",
// "hash/crc32", // investigate
"hash/crc32",
"hash/crc64",
"hash/fnv",
@ -139,54 +141,54 @@ var tests = []string{
"index/suffixarray",
"io",
// "io/ioutil", // investigate
"io/ioutil",
"log",
"log/syslog",
"math",
// "math/big", // investigate
// "math/cmplx", // complex doesn't work yet
"math/big",
"math/cmplx",
"math/rand",
"mime",
"mime/multipart",
// "net", // depends on C files
// "net", // c:\go\root\src\pkg\net\interface_windows.go:54:13: invalid operation: division by zero
"net/http",
"net/http/cgi",
// "net/http/fcgi", // investigate
"net/http/fcgi",
"net/http/httptest",
"net/http/httputil",
// "net/http/pprof", // investigate
"net/http/pprof",
"net/mail",
// "net/rpc", // investigate
"net/rpc",
"net/rpc/jsonrpc",
"net/smtp",
"net/textproto",
"net/url",
// "path", // variadic parameters don't work yet fully
// "path/filepath", // investigate
"path",
"path/filepath",
// "reflect", // investigate
// "reflect", // unsafe.Sizeof must return size > 0 for pointer types
"regexp",
"regexp/syntax",
"runtime",
// "runtime/cgo", // import "C"
"runtime/cgo",
"runtime/debug",
"runtime/pprof",
"sort",
// "strconv", // investigate
// "strconv", // bug in switch case duplicate detection
"strings",
// "sync", // platform-specific files
// "sync/atomic", // platform-specific files
"sync",
"sync/atomic",
// "syscall", // platform-specific files
// "syscall", c:\go\root\src\pkg\syscall\syscall_windows.go:35:16: cannot convert EINVAL (constant 536870951) to error
"testing",
"testing/iotest",
@ -194,10 +196,10 @@ var tests = []string{
"text/scanner",
"text/tabwriter",
// "text/template", // variadic parameters don't work yet fully
// "text/template/parse", // variadic parameters don't work yet fully
"text/template",
"text/template/parse",
// "time", // platform-specific files
// "time", // local const decls without initialization expressions
"unicode",
"unicode/utf16",
"unicode/utf8",

View File

@ -20,7 +20,7 @@ import (
const (
blockSize = 64
blockOffset = 2 // Substract 2 blocks to compensate for the 0x80 added to continuation bytes.
blockOffset = 2 // Subtract 2 blocks to compensate for the 0x80 added to continuation bytes.
)
type trieHandle struct {

View File

@ -179,7 +179,7 @@ doNorm:
i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p)
if !i.rb.insertDecomposed(out[i.outStart:outp]) {
// Start over to prevent decompositions from crossing segment boundaries.
// This is a rare occurance.
// This is a rare occurrence.
i.p = i.inStart
i.info = i.rb.f.info(i.rb.src, i.p)
}

View File

@ -31,7 +31,7 @@ func runPosTests(t *testing.T, name string, f Form, fn positionFunc, tests []Pos
}
runes := []rune(test.buffer)
if rb.nrune != len(runes) {
t.Errorf("%s:%d: reorder buffer lenght is %d; want %d", name, i, rb.nrune, len(runes))
t.Errorf("%s:%d: reorder buffer length is %d; want %d", name, i, rb.nrune, len(runes))
continue
}
for j, want := range runes {

View File

@ -21,7 +21,7 @@ import (
const (
blockSize = 64
blockOffset = 2 // Substract two blocks to compensate for the 0x80 added to continuation bytes.
blockOffset = 2 // Subtract two blocks to compensate for the 0x80 added to continuation bytes.
maxSparseEntries = 16
)

View File

@ -128,13 +128,51 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
x.mode = novalue
case _Complex:
if !check.complexArg(x) {
goto Error
}
var y operand
check.expr(&y, args[1], nil, iota)
if y.mode == invalid {
goto Error
}
// TODO(gri) handle complex(a, b) like (a + toImag(b))
unimplemented()
if !check.complexArg(&y) {
goto Error
}
check.convertUntyped(x, y.typ)
if x.mode == invalid {
goto Error
}
check.convertUntyped(&y, x.typ)
if y.mode == invalid {
goto Error
}
if !isIdentical(x.typ, y.typ) {
check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
goto Error
}
typ := underlying(x.typ).(*Basic)
if x.mode == constant && y.mode == constant {
x.val = binaryOpConst(x.val, toImagConst(y.val), token.ADD, typ)
} else {
x.mode = value
}
switch typ.Kind {
case Float32:
x.typ = Typ[Complex64]
case Float64:
x.typ = Typ[Complex128]
case UntypedInt, UntypedRune, UntypedFloat:
x.typ = Typ[UntypedComplex]
default:
check.invalidArg(x.pos(), "float32 or float64 arguments expected")
goto Error
}
case _Copy:
// TODO(gri) implements checks
@ -361,3 +399,12 @@ func unparen(x ast.Expr) ast.Expr {
}
return x
}
func (check *checker) complexArg(x *operand) bool {
t, _ := underlying(x.typ).(*Basic)
if t != nil && (t.Info&IsFloat != 0 || t.Kind == UntypedInt || t.Kind == UntypedRune) {
return true
}
check.invalidArg(x.pos(), "%s must be a float32, float64, or an untyped non-complex numeric constant", x)
return false
}

View File

@ -49,12 +49,17 @@ func (nilType) String() string {
return "nil"
}
// Frequently used constants.
// Implementation-specific constants.
// TODO(gri) These need to go elsewhere.
const (
intBits = 32
ptrBits = 64
)
// Frequently used values.
var (
zeroConst = int64(0)
oneConst = int64(1)
minusOneConst = int64(-1)
nilConst = nilType{}
nilConst = nilType{}
zeroConst = int64(0)
)
// int64 bounds
@ -74,7 +79,7 @@ func normalizeIntConst(x *big.Int) interface{} {
}
// normalizeRatConst returns the smallest constant representation
// for the specific value of x; either an int64, *big.Int value,
// for the specific value of x; either an int64, *big.Int,
// or *big.Rat value.
//
func normalizeRatConst(x *big.Rat) interface{} {
@ -84,15 +89,15 @@ func normalizeRatConst(x *big.Rat) interface{} {
return x
}
// normalizeComplexConst returns the smallest constant representation
// for the specific value of x; either an int64, *big.Int value, *big.Rat,
// or complex value.
// newComplex returns the smallest constant representation
// for the specific value re + im*i; either an int64, *big.Int,
// *big.Rat, or complex value.
//
func normalizeComplexConst(x complex) interface{} {
if x.im.Sign() == 0 {
return normalizeRatConst(x.re)
func newComplex(re, im *big.Rat) interface{} {
if im.Sign() == 0 {
return normalizeRatConst(re)
}
return x
return complex{re, im}
}
// makeRuneConst returns the int64 code point for the rune literal
@ -140,7 +145,7 @@ func makeComplexConst(lit string) interface{} {
n := len(lit)
if n > 0 && lit[n-1] == 'i' {
if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok {
return normalizeComplexConst(complex{big.NewRat(0, 1), im})
return newComplex(big.NewRat(0, 1), im)
}
}
return nil
@ -157,6 +162,22 @@ func makeStringConst(lit string) interface{} {
return nil
}
// toImagConst returns the constant complex(0, x) for a non-complex x.
func toImagConst(x interface{}) interface{} {
var im *big.Rat
switch x := x.(type) {
case int64:
im = big.NewRat(x, 1)
case *big.Int:
im = new(big.Rat).SetFrac(x, int1)
case *big.Rat:
im = x
default:
unreachable()
}
return complex{rat0, im}
}
// isZeroConst reports whether the value of constant x is 0.
// x must be normalized.
//
@ -186,9 +207,6 @@ func isNegConst(x interface{}) bool {
// of precision.
//
func isRepresentableConst(x interface{}, as BasicKind) bool {
const intBits = 32 // TODO(gri) implementation-specific constant
const ptrBits = 64 // TODO(gri) implementation-specific constant
switch x := x.(type) {
case bool:
return as == Bool || as == UntypedBool
@ -370,13 +388,71 @@ func is63bit(x int64) bool {
return -1<<62 <= x && x <= 1<<62-1
}
// unaryOpConst returns the result of the constant evaluation op x where x is of the given type.
func unaryOpConst(x interface{}, op token.Token, typ *Basic) interface{} {
switch op {
case token.ADD:
return x // nothing to do
case token.SUB:
switch x := x.(type) {
case int64:
if z := -x; z != x {
return z // no overflow
}
// overflow - need to convert to big.Int
return normalizeIntConst(new(big.Int).Neg(big.NewInt(x)))
case *big.Int:
return normalizeIntConst(new(big.Int).Neg(x))
case *big.Rat:
return normalizeRatConst(new(big.Rat).Neg(x))
case complex:
return newComplex(new(big.Rat).Neg(x.re), new(big.Rat).Neg(x.im))
}
case token.XOR:
var z big.Int
switch x := x.(type) {
case int64:
z.Not(big.NewInt(x))
case *big.Int:
z.Not(x)
default:
unreachable()
}
// For unsigned types, the result will be negative and
// thus "too large": We must limit the result size to
// the type's size.
if typ.Info&IsUnsigned != 0 {
s := uint(typ.Size) * 8
if s == 0 {
// platform-specific type
// TODO(gri) this needs to be factored out
switch typ.Kind {
case Uint:
s = intBits
case Uintptr:
s = ptrBits
default:
unreachable()
}
}
// z &^= (-1)<<s
z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s))
}
return normalizeIntConst(&z)
case token.NOT:
return !x.(bool)
}
unreachable()
return nil
}
// binaryOpConst returns the result of the constant evaluation x op y;
// both operands must be of the same "kind" (boolean, numeric, or string).
// If intDiv is true, division (op == token.QUO) is using integer division
// both operands must be of the same constant "kind" (boolean, numeric, or string).
// If typ is an integer type, division (op == token.QUO) is using integer division
// (and the result is guaranteed to be integer) rather than floating-point
// division. Division by zero leads to a run-time panic.
//
func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
func binaryOpConst(x, y interface{}, op token.Token, typ *Basic) interface{} {
x, y = matchConst(x, y)
switch x := x.(type) {
@ -387,8 +463,6 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
return x && y
case token.LOR:
return x || y
default:
unreachable()
}
case int64:
@ -415,7 +489,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
case token.REM:
return x % y
case token.QUO:
if intDiv {
if typ.Info&IsInteger != 0 {
return x / y
}
return normalizeRatConst(new(big.Rat).SetFrac(big.NewInt(x), big.NewInt(y)))
@ -427,8 +501,6 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
return x ^ y
case token.AND_NOT:
return x &^ y
default:
unreachable()
}
case *big.Int:
@ -444,7 +516,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
case token.REM:
z.Rem(x, y)
case token.QUO:
if intDiv {
if typ.Info&IsInteger != 0 {
z.Quo(x, y)
} else {
return normalizeRatConst(new(big.Rat).SetFrac(x, y))
@ -517,7 +589,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
default:
unreachable()
}
return normalizeComplexConst(complex{&re, &im})
return newComplex(&re, &im)
case string:
if op == token.ADD {

View File

@ -266,15 +266,8 @@ func writeType(buf *bytes.Buffer, typ Type) {
buf.WriteByte('*')
writeType(buf, t.Base)
case *tuple:
buf.WriteByte('(')
for i, typ := range t.list {
if i > 0 {
buf.WriteString("; ")
}
writeType(buf, typ)
}
buf.WriteByte(')')
case *Result:
writeParams(buf, t.Values, false)
case *Signature:
buf.WriteString("func")

View File

@ -19,7 +19,6 @@ import (
// - at the moment, iota is passed around almost everywhere - in many places we know it cannot be used
// TODO(gri) API issues
// - clients need access to result type information (tuples)
// - clients need access to constant values
// - clients need access to built-in type information
@ -212,21 +211,11 @@ func (check *checker) unary(x *operand, op token.Token) {
}
if x.mode == constant {
switch op {
case token.ADD:
// nothing to do
case token.SUB:
x.val = binaryOpConst(zeroConst, x.val, token.SUB, false)
case token.XOR:
x.val = binaryOpConst(minusOneConst, x.val, token.XOR, false)
case token.NOT:
x.val = !x.val.(bool)
default:
unreachable() // operators where checked by check.op
}
typ := underlying(x.typ).(*Basic)
x.val = unaryOpConst(x.val, op, typ)
// Typed constants must be representable in
// their type after each constant operation.
check.isRepresentable(x, underlying(x.typ).(*Basic))
check.isRepresentable(x, typ)
return
}
@ -304,6 +293,8 @@ func (check *checker) convertUntyped(x *operand, target Type) {
if !x.isNil() {
goto Error
}
default:
unreachable()
}
x.typ = target
@ -332,7 +323,7 @@ func (check *checker) comparison(x, y *operand, op token.Token) {
}
if !valid {
check.invalidOp(x.pos(), "cannot compare %s and %s", x, y)
check.invalidOp(x.pos(), "cannot compare %s %s %s", x, op, y)
x.mode = invalid
return
}
@ -465,10 +456,11 @@ func (check *checker) binary(x, y *operand, op token.Token, hint Type) {
}
if x.mode == constant && y.mode == constant {
x.val = binaryOpConst(x.val, y.val, op, isInteger(x.typ))
typ := underlying(x.typ).(*Basic)
x.val = binaryOpConst(x.val, y.val, op, typ)
// Typed constants must be representable in
// their type after each constant operation.
check.isRepresentable(x, underlying(x.typ).(*Basic))
check.isRepresentable(x, typ)
return
}
@ -554,9 +546,15 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota
return max
}
func (check *checker) argument(sig *Signature, i int, arg ast.Expr) {
// argument typechecks passing an argument arg (if arg != nil) or
// x (if arg == nil) to the i'th parameter of the given signature.
// If passSlice is set, the argument is followed by ... in the call.
//
func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, passSlice bool) {
// determine parameter
var par *ast.Object
if n := len(sig.Params); i < n {
n := len(sig.Params)
if i < n {
par = sig.Params[i]
} else if sig.IsVariadic {
par = sig.Params[n-1]
@ -565,16 +563,32 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr) {
return
}
// TODO(gri) deal with ... last argument
var z, x operand
// determine argument
var z operand
z.mode = variable
z.expr = nil // TODO(gri) can we do better here?
z.typ = par.Type.(Type) // TODO(gri) should become something like checkObj(&z, ...) eventually
check.expr(&x, arg, z.typ, -1)
z.expr = nil // TODO(gri) can we do better here? (for good error messages)
z.typ = par.Type.(Type)
if arg != nil {
check.expr(x, arg, z.typ, -1)
}
if x.mode == invalid {
return // ignore this argument
}
check.assignOperand(&z, &x)
// check last argument of the form x...
if passSlice {
if i+1 != n {
check.errorf(x.pos(), "can only use ... with matching parameter")
return // ignore this argument
}
// spec: "If the final argument is assignable to a slice type []T,
// it may be passed unchanged as the value for a ...T parameter if
// the argument is followed by ..."
z.typ = &Slice{Elt: z.typ} // change final parameter type to []T
}
check.assignOperand(&z, x)
}
func (check *checker) recordType(x *operand) {
@ -584,7 +598,7 @@ func (check *checker) recordType(x *operand) {
}
// rawExpr typechecks expression e and initializes x with the expression
// value or type. If an error occured, x.mode is set to invalid.
// value or type. If an error occurred, x.mode is set to invalid.
// A hint != nil is used as operand type for untyped shifted operands;
// iota >= 0 indicates that the expression is part of a constant declaration.
// cycleOk indicates whether it is ok for a type expression to refer to itself.
@ -653,7 +667,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
x.typ = obj.Type.(Type)
case *ast.Ellipsis:
// ellipses are handled explictly where they are legal
// ellipses are handled explicitly where they are legal
// (array composite literals and parameter lists)
check.errorf(e.Pos(), "invalid use of '...'")
goto Error
@ -1052,25 +1066,79 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
check.conversion(x, e, x.typ, iota)
} else if sig, ok := underlying(x.typ).(*Signature); ok {
// check parameters
// TODO(gri)
// - deal with single multi-valued function arguments: f(g())
// - variadic functions only partially addressed
for i, arg := range e.Args {
check.argument(sig, i, arg)
// If we have a trailing ... at the end of the parameter
// list, the last argument must match the parameter type
// []T of a variadic function parameter x ...T.
passSlice := false
if e.Ellipsis.IsValid() {
if sig.IsVariadic {
passSlice = true
} else {
check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun)
// ok to continue
}
}
// If we have a single argument that is a function call
// we need to handle it separately. Determine if this
// is the case without checking the argument.
var call *ast.CallExpr
if len(e.Args) == 1 {
call, _ = unparen(e.Args[0]).(*ast.CallExpr)
}
n := 0 // parameter count
if call != nil {
// We have a single argument that is a function call.
check.expr(x, call, nil, -1)
if x.mode == invalid {
goto Error // TODO(gri): we can do better
}
if t, _ := x.typ.(*Result); t != nil {
// multiple result values
n = len(t.Values)
for i, obj := range t.Values {
x.mode = value
x.expr = nil // TODO(gri) can we do better here? (for good error messages)
x.typ = obj.Type.(Type)
check.argument(sig, i, nil, x, passSlice && i+1 == n)
}
} else {
// single result value
n = 1
check.argument(sig, 0, nil, x, passSlice)
}
} else {
// We don't have a single argument or it is not a function call.
n = len(e.Args)
for i, arg := range e.Args {
check.argument(sig, i, arg, x, passSlice && i+1 == n)
}
}
// determine if we have enough arguments
if sig.IsVariadic {
// a variadic function accepts an "empty"
// last argument: count one extra
n++
}
if n < len(sig.Params) {
check.errorf(e.Fun.Pos(), "too few arguments in call to %s", e.Fun)
// ok to continue
}
// determine result
x.mode = value
if len(sig.Results) == 1 {
switch len(sig.Results) {
case 0:
x.mode = novalue
case 1:
x.mode = value
x.typ = sig.Results[0].Type.(Type)
} else {
// TODO(gri) change Signature representation to use tuples,
// then this conversion is not required
list := make([]Type, len(sig.Results))
for i, obj := range sig.Results {
list[i] = obj.Type.(Type)
}
x.typ = &tuple{list: list}
default:
x.mode = value
x.typ = &Result{Values: sig.Results}
}
} else if bin, ok := x.typ.(*builtin); ok {
@ -1216,14 +1284,14 @@ func (check *checker) rawTyp(e ast.Expr, cycleOk, nilOk bool) Type {
}
// typOrNil is like rawExpr but reports an error if e doesn't represents a type or the predeclared value nil.
// It returns e's type, nil, or Typ[Invalid] if an error occured.
// It returns e's type, nil, or Typ[Invalid] if an error occurred.
//
func (check *checker) typOrNil(e ast.Expr, cycleOk bool) Type {
return check.rawTyp(e, cycleOk, true)
}
// typ is like rawExpr but reports an error if e doesn't represents a type.
// It returns e's type, or Typ[Invalid] if an error occured.
// It returns e's type, or Typ[Invalid] if an error occurred.
//
func (check *checker) typ(e ast.Expr, cycleOk bool) Type {
return check.rawTyp(e, cycleOk, false)

View File

@ -182,7 +182,14 @@ func (x *operand) isAssignable(T Type) bool {
if isUntyped(Vu) {
switch t := Tu.(type) {
case *Basic:
return x.mode == constant && isRepresentableConst(x.val, t.Kind)
if x.mode == constant {
return isRepresentableConst(x.val, t.Kind)
}
// The result of a comparison is an untyped boolean,
// but may not be a constant.
if Vb, _ := Vu.(*Basic); Vb != nil {
return Vb.Kind == UntypedBool && isBoolean(Tu)
}
case *Interface:
return x.isNil() || len(t.Methods) == 0
case *Pointer, *Signature, *Slice, *Map, *Chan:

View File

@ -225,25 +225,28 @@ func deref(typ Type) Type {
}
// defaultType returns the default "typed" type for an "untyped" type;
// it returns the argument typ for all other types.
// it returns the incoming type for all other types. If there is no
// corresponding untyped type, the result is Typ[Invalid].
//
func defaultType(typ Type) Type {
if t, ok := typ.(*Basic); ok {
var k BasicKind
k := Invalid
switch t.Kind {
// case UntypedNil:
// There is no default type for nil. For a good error message,
// catch this case before calling this function.
case UntypedBool:
k = Bool
case UntypedRune:
k = Rune
case UntypedInt:
k = Int
case UntypedRune:
k = Rune
case UntypedFloat:
k = Float64
case UntypedComplex:
k = Complex128
case UntypedString:
k = String
default:
unreachable()
}
typ = Typ[k]
}

View File

@ -12,9 +12,9 @@ import (
)
func (check *checker) assignOperand(z, x *operand) {
if t, ok := x.typ.(*tuple); ok {
if t, ok := x.typ.(*Result); ok {
// TODO(gri) elsewhere we use "assignment count mismatch" (consolidate)
check.errorf(x.pos(), "%d-valued expression %s used as single value", len(t.list), x)
check.errorf(x.pos(), "%d-valued expression %s used as single value", len(t.Values), x)
x.mode = invalid
return
}
@ -95,7 +95,12 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
if x.mode != invalid {
typ = x.typ
if obj.Kind == ast.Var && isUntyped(typ) {
typ = defaultType(typ)
if x.isNil() {
check.errorf(x.pos(), "use of untyped nil")
x.mode = invalid
} else {
typ = defaultType(typ)
}
}
}
obj.Type = typ
@ -177,12 +182,12 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
return
}
if t, ok := x.typ.(*tuple); ok && len(lhs) == len(t.list) {
if t, ok := x.typ.(*Result); ok && len(lhs) == len(t.Values) {
// function result
x.mode = value
for i, typ := range t.list {
for i, obj := range t.Values {
x.expr = nil // TODO(gri) should do better here
x.typ = typ
x.typ = obj.Type.(Type)
check.assign1to1(lhs[i], nil, &x, decl, iota)
}
return
@ -427,25 +432,58 @@ func (check *checker) stmt(s ast.Stmt) {
case *ast.SwitchStmt:
check.optionalStmt(s.Init)
var x operand
if s.Tag != nil {
check.expr(&x, s.Tag, nil, -1)
} else {
// TODO(gri) should provide a position (see IncDec) for good error messages
x.mode = constant
x.typ = Typ[UntypedBool]
x.val = true
tag := s.Tag
if tag == nil {
// use fake true tag value and position it at the opening { of the switch
tag = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true", Obj: Universe.Lookup("true")}
}
check.expr(&x, tag, nil, -1)
check.multipleDefaults(s.Body.List)
seen := make(map[interface{}]token.Pos)
for _, s := range s.Body.List {
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
if x.mode != invalid {
for _, expr := range clause.List {
x := x // copy of x (don't modify original)
var y operand
check.expr(&y, expr, nil, -1)
if y.mode == invalid {
continue // error reported before
}
// If we have a constant case value, it must appear only
// once in the switch statement. Determine if there is a
// duplicate entry, but only report an error if there are
// no other errors.
var dupl token.Pos
if y.mode == constant {
// TODO(gri) This code doesn't work correctly for
// large integer, floating point, or
// complex values - the respective struct
// comparisons are shallow. Need to use a
// hash function to index the map.
dupl = seen[y.val]
seen[y.val] = y.pos()
}
// TODO(gri) The convertUntyped call pair below appears in other places. Factor!
// Order matters: By comparing y against x, error positions are at the case values.
check.convertUntyped(&y, x.typ)
if y.mode == invalid {
continue // error reported before
}
check.convertUntyped(&x, y.typ)
if x.mode == invalid {
continue // error reported before
}
check.comparison(&y, &x, token.EQL)
if y.mode != invalid && dupl.IsValid() {
check.errorf(y.pos(), "%s is duplicate case (previous at %s)",
&y, check.fset.Position(dupl))
}
}
}
check.stmtList(clause.Body)
}

View File

@ -46,10 +46,33 @@ func _close() {
}
func _complex() {
_0 := complex /* ERROR "argument" */ ()
_1 := complex /* ERROR "argument" */ (1)
_2 := complex(1, 2)
// TODO(gri) add tests checking types
var i32 int32
var f32 float32
var f64 float64
var c64 complex64
_ = complex /* ERROR "argument" */ ()
_ = complex /* ERROR "argument" */ (1)
_ = complex(true /* ERROR "invalid argument" */ , 0)
_ = complex(i32 /* ERROR "invalid argument" */ , 0)
_ = complex("foo" /* ERROR "invalid argument" */ , 0)
_ = complex(c64 /* ERROR "invalid argument" */ , 0)
_ = complex(0, true /* ERROR "invalid argument" */ )
_ = complex(0, i32 /* ERROR "invalid argument" */ )
_ = complex(0, "foo" /* ERROR "invalid argument" */ )
_ = complex(0, c64 /* ERROR "invalid argument" */ )
_ = complex(f32, f32)
_ = complex(f32, 1)
_ = complex(f32, 1.0)
_ = complex(f32, 'a')
_ = complex(f64, f64)
_ = complex(f64, 1)
_ = complex(f64, 1.0)
_ = complex(f64, 'a')
_ = complex(f32 /* ERROR "mismatched types" */, f64)
_ = complex(f64 /* ERROR "mismatched types" */, f32)
_ = complex(1, 1)
_ = complex(1, 1.1)
_ = complex(1, 'a')
complex /* ERROR "not used" */ (1, 2)
}

View File

@ -46,7 +46,7 @@ var (
s14 = i << j /* ERROR "must be unsigned" */
s18 = math.Pi * 10.0
s19 = s1 /* ERROR "cannot call" */ ()
s20 = f0 /* ERROR "used as single value" */ ()
s20 = f0 /* ERROR "no value" */ ()
s21 = f6(1, s1, i)
s22 = f6(1, s1, uu /* ERROR "cannot assign" */ )
@ -68,7 +68,7 @@ var (
t17 math /* ERROR "not a type" */ .Pi
t18 float64 = math.Pi * 10.0
t19 int = t1 /* ERROR "cannot call" */ ()
t20 int = f0 /* ERROR "used as single value" */ ()
t20 int = f0 /* ERROR "no value" */ ()
)
// Various more complex expressions
@ -94,6 +94,7 @@ var (
v10 byte = 1024 /* ERROR "overflows" */
v11 = xx/yy*yy - xx
v12 = true && false
v13 = nil /* ERROR "use of untyped nil" */
)
// Multiple assignment expressions

View File

@ -63,6 +63,7 @@ var (
u16 = &u0
u17 = *u16
u18 = <-u16 /* ERROR "cannot receive" */
u19 = ^uint(0)
// float64
f0 = float64(1)
@ -131,5 +132,4 @@ var (
ch7 = <-ch
ch8 = <-rc
ch9 = <-sc /* ERROR "cannot receive" */
)
)

View File

@ -6,6 +6,17 @@
package expr2
func _bool() {
const t = true == true
const f = true == false
_ = t /* ERROR "cannot compare" */ < f
_ = 0 /* ERROR "cannot convert" */ == t
var b bool
var x, y float32
b = x < y
_ = struct{b bool}{x < y}
}
// corner cases
var (
v0 = nil /* ERROR "cannot compare" */ == nil

View File

@ -286,3 +286,64 @@ func type_asserts() {
_ = t.(T2 /* ERROR "wrong type for method m" */ )
_ = t.(I2 /* ERROR "wrong type for method m" */ )
}
func f0() {}
func f1(x int) {}
func f2(u float32, s string) {}
func fs(s []byte) {}
func fv(x ...int) {}
func fi(x ... interface{}) {}
func g0() {}
func g1() int { return 0}
func g2() (u float32, s string) { return }
func gs() []byte { return nil }
func _calls() {
var x int
var y float32
var s []int
f0()
_ = f0 /* ERROR "used as value" */ ()
f0(g0 /* ERROR "too many arguments" */ )
f1(0)
f1(x)
f1(10.0)
f1 /* ERROR "too few arguments" */ ()
f1(x, y /* ERROR "too many arguments" */ )
f1(s /* ERROR "cannot assign" */ )
f1(x ... /* ERROR "cannot use ..." */ )
f1(g0 /* ERROR "used as value" */ ())
f1(g1())
// f1(g2()) // TODO(gri) missing position in error message
f2 /* ERROR "too few arguments" */ ()
f2 /* ERROR "too few arguments" */ (3.14)
f2(3.14, "foo")
f2(x /* ERROR "cannot assign" */ , "foo")
f2(g0 /* ERROR "used as value" */ ())
f2 /* ERROR "too few arguments" */ (g1 /* ERROR "cannot assign" */ ())
f2(g2())
fs /* ERROR "too few arguments" */ ()
fs(g0 /* ERROR "used as value" */ ())
fs(g1 /* ERROR "cannot assign" */ ())
// fs(g2()) // TODO(gri) missing position in error message
fs(gs())
fv()
fv(1, 2.0, x)
fv(s /* ERROR "cannot assign" */ )
fv(s...)
fv(1, s /* ERROR "can only use ... with matching parameter" */ ...)
fv(gs /* ERROR "cannot assign" */ ())
fv(gs /* ERROR "cannot assign" */ ()...)
fi()
fi(1, 2.0, x, 3.14, "foo")
fi(g2())
fi(0, g2)
fi(0, g2 /* ERROR "2-valued expression" */ ())
}

View File

@ -101,7 +101,31 @@ func _switches() {
default /* ERROR "multiple defaults" */ :
}
// TODO(gri) more tests
switch {
case 1 /* ERROR "cannot convert" */ :
}
switch int32(x) {
case 1, 2:
case x /* ERROR "cannot compare" */ :
}
switch x {
case 1 /* ERROR "overflows int" */ << 100:
}
switch x {
case 1:
case 1 /* ERROR "duplicate case" */ :
case 2, 3, 4:
case 1 /* ERROR "duplicate case" */ :
}
// TODO(gri) duplicate 64bit values that don't fit into an int64 are not yet detected
switch uint64(x) {
case 1<<64-1:
case 1<<64-1:
}
}
type I interface {

View File

@ -141,15 +141,15 @@ type Pointer struct {
Base Type
}
// A tuple represents a multi-value function return.
// TODO(gri) use better name to avoid confusion (Go doesn't have tuples).
type tuple struct {
// A Result represents a (multi-value) function call result.
// TODO(gri) consider using an empty Result (Values == nil)
// as representation for the novalue operand mode.
type Result struct {
implementsType
list []Type
Values ObjList // Signature.Results of the function called
}
// A Signature represents a user-defined function type func(...) (...).
// TODO(gri) consider using "tuples" to represent parameters and results (see comment on tuples).
type Signature struct {
implementsType
Recv *ast.Object // nil if not a method

View File

@ -155,7 +155,7 @@ var testExprs = []testEntry{
dup("-f(10, 20)"),
dup("f(x + y, +3.1415)"),
{"func(a, b int) {}", "(func literal)"},
{"func(a, b int) []int {}()[x]", "(func literal)()[x]"},
{"func(a, b int) []int {}(1, 2)[x]", "(func literal)(1, 2)[x]"},
{"[]int{1, 2, 3}", "(composite literal)"},
{"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"},
{"i.([]string)", "i.(...)"},

View File

@ -476,6 +476,11 @@ var fmttests = []struct {
// Used to crash because nByte didn't allow for a sign.
{"%b", int64(-1 << 63), "-1000000000000000000000000000000000000000000000000000000000000000"},
// Complex fmt used to leave the plus flag set for future entries in the array
// causing +2+0i and +3+0i instead of 2+0i and 3+0i.
{"%v", []complex64{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"},
{"%v", []complex128{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"},
}
func TestSprintf(t *testing.T) {

View File

@ -396,7 +396,7 @@ func (f *fmt) fmt_f64(v float64) { f.formatFloat(v, 'f', doPrec(f, 6), 64) }
// fmt_g64 formats a float64 in the 'f' or 'e' form according to size.
func (f *fmt) fmt_g64(v float64) { f.formatFloat(v, 'g', doPrec(f, -1), 64) }
// fmt_g64 formats a float64 in the 'f' or 'E' form according to size.
// fmt_G64 formats a float64 in the 'f' or 'E' form according to size.
func (f *fmt) fmt_G64(v float64) { f.formatFloat(v, 'G', doPrec(f, -1), 64) }
// fmt_fb64 formats a float64 in the form -123p3 (exponent is power of 2).
@ -428,6 +428,7 @@ func (f *fmt) fmt_fb32(v float32) { f.formatFloat(float64(v), 'b', 0, 32) }
func (f *fmt) fmt_c64(v complex64, verb rune) {
f.buf.WriteByte('(')
r := real(v)
oldPlus := f.plus
for i := 0; ; i++ {
switch verb {
case 'e':
@ -447,6 +448,7 @@ func (f *fmt) fmt_c64(v complex64, verb rune) {
f.plus = true
r = imag(v)
}
f.plus = oldPlus
f.buf.Write(irparenBytes)
}
@ -454,6 +456,7 @@ func (f *fmt) fmt_c64(v complex64, verb rune) {
func (f *fmt) fmt_c128(v complex128, verb rune) {
f.buf.WriteByte('(')
r := real(v)
oldPlus := f.plus
for i := 0; ; i++ {
switch verb {
case 'e':
@ -473,5 +476,6 @@ func (f *fmt) fmt_c128(v complex128, verb rune) {
f.plus = true
r = imag(v)
}
f.plus = oldPlus
f.buf.Write(irparenBytes)
}

View File

@ -337,7 +337,10 @@ func (r *readRune) readByte() (b byte, err error) {
r.pending--
return
}
_, err = r.reader.Read(r.pendBuf[0:1])
n, err := io.ReadFull(r.reader, r.pendBuf[0:1])
if n != 1 {
return 0, err
}
return r.pendBuf[0], err
}

View File

@ -555,7 +555,7 @@ type (
// A DeclStmt node represents a declaration in a statement list.
DeclStmt struct {
Decl Decl
Decl Decl // *GenDecl with CONST, TYPE, or VAR token
}
// An EmptyStmt node represents an empty statement.

View File

@ -222,6 +222,8 @@ var cgoEnabled = map[string]bool{
"linux/arm": true,
"netbsd/386": true,
"netbsd/amd64": true,
"openbsd/386": true,
"openbsd/amd64": true,
"windows/386": true,
"windows/amd64": true,
}
@ -424,6 +426,13 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
if strings.HasPrefix(path, "/") {
return p, fmt.Errorf("import %q: cannot import absolute path", path)
}
// tried records the location of unsucsessful package lookups
var tried struct {
goroot string
gopath []string
}
// Determine directory from import path.
if ctxt.GOROOT != "" {
dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", path)
@ -435,6 +444,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
p.Root = ctxt.GOROOT
goto Found
}
tried.goroot = dir
}
for _, root := range ctxt.gopath() {
dir := ctxt.joinPath(root, "src", path)
@ -445,8 +455,28 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
p.Root = root
goto Found
}
tried.gopath = append(tried.gopath, dir)
}
return p, fmt.Errorf("import %q: cannot find package", path)
// package was not found
var paths []string
if tried.goroot != "" {
paths = append(paths, fmt.Sprintf("\t%s (from $GOROOT)", tried.goroot))
} else {
paths = append(paths, "\t($GOROOT not set)")
}
var i int
var format = "\t%s (from $GOPATH)"
for ; i < len(tried.gopath); i++ {
if i > 0 {
format = "\t%s"
}
paths = append(paths, fmt.Sprintf(format, tried.gopath[i]))
}
if i == 0 {
paths = append(paths, "\t($GOPATH not set)")
}
return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, strings.Join(paths, "\n"))
}
Found:

View File

@ -229,7 +229,8 @@ type block struct {
var nonAlphaNumRx = regexp.MustCompile(`[^a-zA-Z0-9]`)
func anchorID(line string) string {
return nonAlphaNumRx.ReplaceAllString(line, "_")
// Add a "hdr-" prefix to avoid conflicting with IDs used for package symbols.
return "hdr-" + nonAlphaNumRx.ReplaceAllString(line, "_")
}
// ToHTML converts comment text to formatted HTML.

View File

@ -119,8 +119,29 @@ func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
return nil
}
// Find unresolved identifiers
// Find top-level declarations in the file.
topDecls := make(map[*ast.Object]bool)
for _, decl := range file.Decls {
switch d := decl.(type) {
case *ast.FuncDecl:
topDecls[d.Name.Obj] = true
case *ast.GenDecl:
for _, spec := range d.Specs {
switch s := spec.(type) {
case *ast.TypeSpec:
topDecls[s.Name.Obj] = true
case *ast.ValueSpec:
for _, id := range s.Names {
topDecls[id.Obj] = true
}
}
}
}
}
// Find unresolved identifiers and uses of top-level declarations.
unresolved := make(map[string]bool)
usesTopDecl := false
ast.Inspect(body, func(n ast.Node) bool {
// For an expression like fmt.Println, only add "fmt" to the
// set of unresolved names.
@ -130,11 +151,19 @@ func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
}
return false
}
if id, ok := n.(*ast.Ident); ok && id.Obj == nil {
unresolved[id.Name] = true
if id, ok := n.(*ast.Ident); ok {
if id.Obj == nil {
unresolved[id.Name] = true
} else if topDecls[id.Obj] {
usesTopDecl = true
}
}
return true
})
if usesTopDecl {
// We don't support examples that are not self-contained (yet).
return nil
}
// Remove predeclared identifiers from unresolved list.
for n := range unresolved {

View File

@ -46,7 +46,7 @@ func Node(dst io.Writer, fset *token.FileSet, node interface{}) error {
// 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.
// TODO(gri) Do this more efficiently.
var buf bytes.Buffer
err := config.Fprint(&buf, fset, file)
if err != nil {

View File

@ -83,7 +83,7 @@ func (p *printer) setComment(g *ast.CommentGroup) {
// don't overwrite any pending comment in the p.comment cache
// (there may be a pending comment when a line comment is
// immediately followed by a lead comment with no other
// tokens inbetween)
// tokens between)
if p.commentOffset == infinity {
p.nextComment() // get comment ready for use
}

View File

@ -295,9 +295,9 @@ type FileSet struct {
// NewFileSet creates a new file set.
func NewFileSet() *FileSet {
s := new(FileSet)
s.base = 1 // 0 == NoPos
return s
return &FileSet{
base: 1, // 0 == NoPos
}
}
// Base returns the minimum base offset that must be provided to
@ -367,8 +367,10 @@ func searchFiles(a []*File, x int) int {
}
func (s *FileSet) file(p Pos) *File {
s.mutex.RLock()
// common case: p is in last file
if f := s.last; f != nil && f.base <= int(p) && int(p) <= f.base+f.size {
s.mutex.RUnlock()
return f
}
// p is not in last file - search all files
@ -376,10 +378,14 @@ func (s *FileSet) file(p Pos) *File {
f := s.files[i]
// f.base <= int(p) by definition of searchFiles
if int(p) <= f.base+f.size {
s.last = f
s.mutex.RUnlock()
s.mutex.Lock()
s.last = f // race is ok - s.last is only a cache
s.mutex.Unlock()
return f
}
}
s.mutex.RUnlock()
return nil
}
@ -389,9 +395,7 @@ func (s *FileSet) file(p Pos) *File {
//
func (s *FileSet) File(p Pos) (f *File) {
if p != NoPos {
s.mutex.RLock()
f = s.file(p)
s.mutex.RUnlock()
}
return
}
@ -399,11 +403,9 @@ func (s *FileSet) File(p Pos) (f *File) {
// Position converts a Pos in the fileset into a general Position.
func (s *FileSet) Position(p Pos) (pos Position) {
if p != NoPos {
s.mutex.RLock()
if f := s.file(p); f != nil {
pos = f.position(p)
}
s.mutex.RUnlock()
}
return
}

View File

@ -6,6 +6,8 @@ package token
import (
"fmt"
"math/rand"
"sync"
"testing"
)
@ -179,3 +181,52 @@ func TestFiles(t *testing.T) {
}
}
}
// FileSet.File should return nil if Pos is past the end of the FileSet.
func TestFileSetPastEnd(t *testing.T) {
fset := NewFileSet()
for _, test := range tests {
fset.AddFile(test.filename, fset.Base(), test.size)
}
if f := fset.File(Pos(fset.Base())); f != nil {
t.Errorf("expected nil, got %v", f)
}
}
func TestFileSetCacheUnlikely(t *testing.T) {
fset := NewFileSet()
offsets := make(map[string]int)
for _, test := range tests {
offsets[test.filename] = fset.Base()
fset.AddFile(test.filename, fset.Base(), test.size)
}
for file, pos := range offsets {
f := fset.File(Pos(pos))
if f.Name() != file {
t.Errorf("expecting %q at position %d, got %q", file, pos, f.Name())
}
}
}
// issue 4345. Test concurrent use of FileSet.Pos does not trigger a
// race in the FileSet position cache.
func TestFileSetRace(t *testing.T) {
fset := NewFileSet()
for i := 0; i < 100; i++ {
fset.AddFile(fmt.Sprintf("file-%d", i), fset.Base(), 1031)
}
max := int32(fset.Base())
var stop sync.WaitGroup
r := rand.New(rand.NewSource(7))
for i := 0; i < 2; i++ {
r := rand.New(rand.NewSource(r.Int63()))
stop.Add(1)
go func() {
for i := 0; i < 1000; i++ {
fset.Position(Pos(r.Int31n(max)))
}
stop.Done()
}()
}
stop.Wait()
}

View File

@ -468,6 +468,11 @@ func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error) {
off += s.base
if max := s.limit - off; int64(len(p)) > max {
p = p[0:max]
n, err = s.r.ReadAt(p, off)
if err == nil {
err = EOF
}
return n, err
}
return s.r.ReadAt(p, off)
}

View File

@ -203,3 +203,35 @@ func TestTeeReader(t *testing.T) {
t.Errorf("closed tee: ReadFull(r, dst) = %d, %v; want 0, EPIPE", n, err)
}
}
func TestSectionReader_ReadAt(tst *testing.T) {
dat := "a long sample data, 1234567890"
tests := []struct {
data string
off int
n int
bufLen int
at int
exp string
err error
}{
{data: "", off: 0, n: 10, bufLen: 2, at: 0, exp: "", err: EOF},
{data: dat, off: 0, n: len(dat), bufLen: 0, at: 0, exp: "", err: nil},
{data: dat, off: len(dat), n: 1, bufLen: 1, at: 0, exp: "", err: EOF},
{data: dat, off: 0, n: len(dat) + 2, bufLen: len(dat), at: 0, exp: dat, err: nil},
{data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 0, exp: dat[:len(dat)/2], err: nil},
{data: dat, off: 0, n: len(dat), bufLen: len(dat), at: 0, exp: dat, err: nil},
{data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[2 : 2+len(dat)/2], err: nil},
{data: dat, off: 3, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[5 : 5+len(dat)/2], err: nil},
{data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 - 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: nil},
{data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 + 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: EOF},
}
for i, t := range tests {
r := strings.NewReader(t.data)
s := NewSectionReader(r, int64(t.off), int64(t.n))
buf := make([]byte, t.bufLen)
if n, err := s.ReadAt(buf, int64(t.at)); n != len(t.exp) || string(buf[:n]) != t.exp || err != t.err {
tst.Fatalf("%d: ReadAt(%d) = %q, %v; expected %q, %v", i, t.at, buf[:n], err, t.exp, t.err)
}
}
}

View File

@ -20,13 +20,14 @@ var serverAddr string
func runSyslog(c net.PacketConn, done chan<- string) {
var buf [4096]byte
var rcvd string = ""
var rcvd string
for {
n, _, err := c.ReadFrom(buf[0:])
if err != nil || n == 0 {
c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
n, _, err := c.ReadFrom(buf[:])
rcvd += string(buf[:n])
if err != nil {
break
}
rcvd += string(buf[0:n])
}
done <- rcvd
}
@ -37,7 +38,6 @@ func startServer(done chan<- string) {
log.Fatalf("net.ListenPacket failed udp :0 %v", e)
}
serverAddr = c.LocalAddr().String()
c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
go runSyslog(c, done)
}

View File

@ -2281,6 +2281,13 @@ func TestLog2(t *testing.T) {
t.Errorf("Log2(%g) = %g, want %g", vflogSC[i], f, logSC[i])
}
}
for i := -1074; i <= 1023; i++ {
f := Ldexp(1, i)
l := Log2(f)
if l != float64(i) {
t.Errorf("Log2(2**%d) = %g, want %d", i, l, i)
}
}
}
func TestModf(t *testing.T) {

View File

@ -51,6 +51,13 @@ func (z *Int) SetInt64(x int64) *Int {
return z
}
// SetUint64 sets z to x and returns z.
func (z *Int) SetUint64(x uint64) *Int {
z.abs = z.abs.setUint64(uint64(x))
z.neg = false
return z
}
// NewInt allocates and returns a new Int set to x.
func NewInt(x int64) *Int {
return new(Int).SetInt64(x)
@ -519,6 +526,19 @@ func (x *Int) Int64() int64 {
return v
}
// Uint64 returns the int64 representation of x.
// If x cannot be represented in an uint64, the result is undefined.
func (x *Int) Uint64() uint64 {
if len(x.abs) == 0 {
return 0
}
v := uint64(x.abs[0])
if _W == 32 && len(x.abs) > 1 {
v |= uint64(x.abs[1]) << 32
}
return v
}
// SetString sets z to the value of s, interpreted in the given base,
// and returns z and a boolean indicating success. If SetString fails,
// the value of z is undefined but the returned value is nil.

View File

@ -1135,6 +1135,36 @@ func TestInt64(t *testing.T) {
}
}
var uint64Tests = []uint64{
0,
1,
4294967295,
4294967296,
8589934591,
8589934592,
9223372036854775807,
9223372036854775808,
18446744073709551615, // 1<<64 - 1
}
func TestUint64(t *testing.T) {
in := new(Int)
for i, testVal := range uint64Tests {
in.SetUint64(testVal)
out := in.Uint64()
if out != testVal {
t.Errorf("#%d got %d want %d", i, out, testVal)
}
str := fmt.Sprint(testVal)
strOut := in.String()
if strOut != str {
t.Errorf("#%d.String got %s want %s", i, strOut, str)
}
}
}
var bitwiseTests = []struct {
x, y string
and, or, xor, andNot string

View File

@ -826,7 +826,7 @@ func (x nat) string(charset string) string {
// Convert words of q to base b digits in s. If q is large, it is recursively "split in half"
// by nat/nat division using tabulated divisors. Otherwise, it is converted iteratively using
// repeated nat/Word divison.
// repeated nat/Word division.
//
// The iterative method processes n Words by n divW() calls, each of which visits every Word in the
// incrementally shortened q for a total of n + (n-1) + (n-2) ... + 2 + 1, or n(n+1)/2 divW()'s.

View File

@ -26,5 +26,6 @@ func Log2(x float64) float64 {
}
func log2(x float64) float64 {
return Log(x) * (1 / Ln2)
frac, exp := Frexp(x)
return Log(frac)*(1/Ln2) + float64(exp)
}

View File

@ -0,0 +1,14 @@
// Copyright 2011 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 net
/*
#include <netdb.h>
*/
import "C"
func cgoAddrInfoFlags() C.int {
return C.AI_CANONNAME
}

View File

@ -2,7 +2,7 @@
// 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
// +build darwin freebsd linux netbsd openbsd
package net

View File

@ -17,8 +17,8 @@ var connTests = []struct {
addr string
}{
{"tcp", "127.0.0.1:0"},
{"unix", "/tmp/gotest.net"},
{"unixpacket", "/tmp/gotest.net"},
{"unix", "/tmp/gotest.net1"},
{"unixpacket", "/tmp/gotest.net2"},
}
func TestConnAndListener(t *testing.T) {
@ -41,7 +41,13 @@ func TestConnAndListener(t *testing.T) {
return
}
ln.Addr()
defer ln.Close()
defer func(ln net.Listener, net, addr string) {
ln.Close()
switch net {
case "unix", "unixpacket":
os.Remove(addr)
}
}(ln, tt.net, tt.addr)
done := make(chan int)
go transponder(t, ln, done)
@ -68,10 +74,6 @@ func TestConnAndListener(t *testing.T) {
}
<-done
switch tt.net {
case "unix", "unixpacket":
os.Remove(tt.addr)
}
}
}

View File

@ -238,7 +238,7 @@ func ListenPacket(net, laddr string) (PacketConn, error) {
if a != nil {
la = a.(*UnixAddr)
}
return DialUnix(net, la, nil)
return ListenUnixgram(net, la)
}
return nil, UnknownNetworkError(net)
}

View File

@ -240,7 +240,8 @@ func TestDialTimeoutFDLeak(t *testing.T) {
err error
}
dials := listenerBacklog + 100
maxGoodConnect := listenerBacklog + 5 // empirically 131 good ones (of 128). who knows?
// used to be listenerBacklog + 5, but was found to be unreliable, issue 4384.
maxGoodConnect := listenerBacklog + runtime.NumCPU()*10
resc := make(chan connErr)
for i := 0; i < dials; i++ {
go func() {

View File

@ -98,7 +98,9 @@ func (c *Client) send(req *Request) (*Response, error) {
return nil, err
}
if c.Jar != nil {
c.Jar.SetCookies(req.URL, resp.Cookies())
if rc := resp.Cookies(); len(rc) > 0 {
c.Jar.SetCookies(req.URL, rc)
}
}
return resp, err
}
@ -120,7 +122,10 @@ func (c *Client) send(req *Request) (*Response, error) {
// Generally Get, Post, or PostForm will be used instead of Do.
func (c *Client) Do(req *Request) (resp *Response, err error) {
if req.Method == "GET" || req.Method == "HEAD" {
return c.doFollowingRedirects(req)
return c.doFollowingRedirects(req, shouldRedirectGet)
}
if req.Method == "POST" || req.Method == "PUT" {
return c.doFollowingRedirects(req, shouldRedirectPost)
}
return c.send(req)
}
@ -166,7 +171,7 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) {
// True if the specified HTTP status code is one for which the Get utility should
// automatically redirect.
func shouldRedirect(statusCode int) bool {
func shouldRedirectGet(statusCode int) bool {
switch statusCode {
case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
return true
@ -174,6 +179,16 @@ func shouldRedirect(statusCode int) bool {
return false
}
// True if the specified HTTP status code is one for which the Post utility should
// automatically redirect.
func shouldRedirectPost(statusCode int) bool {
switch statusCode {
case StatusFound, StatusSeeOther:
return true
}
return false
}
// Get issues a GET to the specified URL. If the response is one of the following
// redirect codes, Get follows the redirect, up to a maximum of 10 redirects:
//
@ -214,12 +229,10 @@ func (c *Client) Get(url string) (resp *Response, err error) {
if err != nil {
return nil, err
}
return c.doFollowingRedirects(req)
return c.doFollowingRedirects(req, shouldRedirectGet)
}
func (c *Client) doFollowingRedirects(ireq *Request) (resp *Response, err error) {
// TODO: if/when we add cookie support, the redirected request shouldn't
// necessarily supply the same cookies as the original.
func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bool) (resp *Response, err error) {
var base *url.URL
redirectChecker := c.CheckRedirect
if redirectChecker == nil {
@ -238,6 +251,9 @@ func (c *Client) doFollowingRedirects(ireq *Request) (resp *Response, err error)
if redirect != 0 {
req = new(Request)
req.Method = ireq.Method
if ireq.Method == "POST" || ireq.Method == "PUT" {
req.Method = "GET"
}
req.Header = make(Header)
req.URL, err = base.Parse(urlStr)
if err != nil {
@ -321,7 +337,7 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Respon
return nil, err
}
req.Header.Set("Content-Type", bodyType)
return c.send(req)
return c.doFollowingRedirects(req, shouldRedirectPost)
}
// PostForm issues a POST to the specified URL, with data's keys and
@ -371,5 +387,5 @@ func (c *Client) Head(url string) (resp *Response, err error) {
if err != nil {
return nil, err
}
return c.doFollowingRedirects(req)
return c.doFollowingRedirects(req, shouldRedirectGet)
}

View File

@ -7,6 +7,7 @@
package http_test
import (
"bytes"
"crypto/tls"
"crypto/x509"
"errors"
@ -246,6 +247,52 @@ func TestRedirects(t *testing.T) {
}
}
func TestPostRedirects(t *testing.T) {
var log struct {
sync.Mutex
bytes.Buffer
}
var ts *httptest.Server
ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
log.Lock()
fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI)
log.Unlock()
if v := r.URL.Query().Get("code"); v != "" {
code, _ := strconv.Atoi(v)
if code/100 == 3 {
w.Header().Set("Location", ts.URL)
}
w.WriteHeader(code)
}
}))
tests := []struct {
suffix string
want int // response code
}{
{"/", 200},
{"/?code=301", 301},
{"/?code=302", 200},
{"/?code=303", 200},
{"/?code=404", 404},
}
for _, tt := range tests {
res, err := Post(ts.URL+tt.suffix, "text/plain", strings.NewReader("Some content"))
if err != nil {
t.Fatal(err)
}
if res.StatusCode != tt.want {
t.Errorf("POST %s: status code = %d; want %d", tt.suffix, res.StatusCode, tt.want)
}
}
log.Lock()
got := log.String()
log.Unlock()
want := "POST / POST /?code=301 POST /?code=302 GET / POST /?code=303 GET / POST /?code=404 "
if got != want {
t.Errorf("Log differs.\n Got: %q\nWant: %q", got, want)
}
}
var expectedCookies = []*Cookie{
{Name: "ChocolateChip", Value: "tasty"},
{Name: "First", Value: "Hit"},
@ -304,6 +351,9 @@ type TestJar struct {
func (j *TestJar) SetCookies(u *url.URL, cookies []*Cookie) {
j.m.Lock()
defer j.m.Unlock()
if j.perURL == nil {
j.perURL = make(map[string][]*Cookie)
}
j.perURL[u.Host] = cookies
}
@ -334,8 +384,9 @@ func TestRedirectCookiesJar(t *testing.T) {
var ts *httptest.Server
ts = httptest.NewServer(echoCookiesRedirectHandler)
defer ts.Close()
c := &Client{}
c.Jar = &TestJar{perURL: make(map[string][]*Cookie)}
c := &Client{
Jar: new(TestJar),
}
u, _ := url.Parse(ts.URL)
c.Jar.SetCookies(u, []*Cookie{expectedCookies[0]})
resp, err := c.Get(ts.URL)
@ -364,6 +415,69 @@ func matchReturnedCookies(t *testing.T, expected, given []*Cookie) {
}
}
func TestJarCalls(t *testing.T) {
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
pathSuffix := r.RequestURI[1:]
if r.RequestURI == "/nosetcookie" {
return // dont set cookies for this path
}
SetCookie(w, &Cookie{Name: "name" + pathSuffix, Value: "val" + pathSuffix})
if r.RequestURI == "/" {
Redirect(w, r, "http://secondhost.fake/secondpath", 302)
}
}))
defer ts.Close()
jar := new(RecordingJar)
c := &Client{
Jar: jar,
Transport: &Transport{
Dial: func(_ string, _ string) (net.Conn, error) {
return net.Dial("tcp", ts.Listener.Addr().String())
},
},
}
_, err := c.Get("http://firsthost.fake/")
if err != nil {
t.Fatal(err)
}
_, err = c.Get("http://firsthost.fake/nosetcookie")
if err != nil {
t.Fatal(err)
}
got := jar.log.String()
want := `Cookies("http://firsthost.fake/")
SetCookie("http://firsthost.fake/", [name=val])
Cookies("http://secondhost.fake/secondpath")
SetCookie("http://secondhost.fake/secondpath", [namesecondpath=valsecondpath])
Cookies("http://firsthost.fake/nosetcookie")
`
if got != want {
t.Errorf("Got Jar calls:\n%s\nWant:\n%s", got, want)
}
}
// RecordingJar keeps a log of calls made to it, without
// tracking any cookies.
type RecordingJar struct {
mu sync.Mutex
log bytes.Buffer
}
func (j *RecordingJar) SetCookies(u *url.URL, cookies []*Cookie) {
j.logf("SetCookie(%q, %v)\n", u, cookies)
}
func (j *RecordingJar) Cookies(u *url.URL) []*Cookie {
j.logf("Cookies(%q)\n", u)
return nil
}
func (j *RecordingJar) logf(format string, args ...interface{}) {
j.mu.Lock()
defer j.mu.Unlock()
fmt.Fprintf(&j.log, format, args...)
}
func TestStreamingGet(t *testing.T) {
say := make(chan string)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {

View File

@ -34,9 +34,8 @@
//
// go tool pprof http://localhost:6060/debug/pprof/block
//
// Or to view all available profiles:
//
// go tool pprof http://localhost:6060/debug/pprof/
// To view all available profiles, open http://localhost:6060/debug/pprof/
// in your browser.
//
// For a study of the facility in action, visit
//

View File

@ -247,6 +247,54 @@ var reqTests = []reqTest{
noTrailer,
noError,
},
// SSDP Notify request. golang.org/issue/3692
{
"NOTIFY * HTTP/1.1\r\nServer: foo\r\n\r\n",
&Request{
Method: "NOTIFY",
URL: &url.URL{
Path: "*",
},
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Header: Header{
"Server": []string{"foo"},
},
Close: false,
ContentLength: 0,
RequestURI: "*",
},
noBody,
noTrailer,
noError,
},
// OPTIONS request. Similar to golang.org/issue/3692
{
"OPTIONS * HTTP/1.1\r\nServer: foo\r\n\r\n",
&Request{
Method: "OPTIONS",
URL: &url.URL{
Path: "*",
},
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Header: Header{
"Server": []string{"foo"},
},
Close: false,
ContentLength: 0,
RequestURI: "*",
},
noBody,
noTrailer,
noError,
},
}
func TestReadRequest(t *testing.T) {

View File

@ -918,15 +918,19 @@ func TestZeroLengthPostAndResponse(t *testing.T) {
}
}
func TestHandlerPanicNil(t *testing.T) {
testHandlerPanic(t, false, nil)
}
func TestHandlerPanic(t *testing.T) {
testHandlerPanic(t, false)
testHandlerPanic(t, false, "intentional death for testing")
}
func TestHandlerPanicWithHijack(t *testing.T) {
testHandlerPanic(t, true)
testHandlerPanic(t, true, "intentional death for testing")
}
func testHandlerPanic(t *testing.T, withHijack bool) {
func testHandlerPanic(t *testing.T, withHijack bool, panicValue interface{}) {
// Unlike the other tests that set the log output to ioutil.Discard
// to quiet the output, this test uses a pipe. The pipe serves three
// purposes:
@ -955,7 +959,7 @@ func testHandlerPanic(t *testing.T, withHijack bool) {
}
defer rwc.Close()
}
panic("intentional death for testing")
panic(panicValue)
}))
defer ts.Close()
@ -968,7 +972,7 @@ func testHandlerPanic(t *testing.T, withHijack bool) {
_, err := pr.Read(buf)
pr.Close()
if err != nil {
t.Fatal(err)
t.Error(err)
}
done <- true
}()
@ -978,6 +982,10 @@ func testHandlerPanic(t *testing.T, withHijack bool) {
t.Logf("expected an error")
}
if panicValue == nil {
return
}
select {
case <-done:
return
@ -1288,6 +1296,58 @@ For:
ts.Close()
}
func TestOptions(t *testing.T) {
uric := make(chan string, 2) // only expect 1, but leave space for 2
mux := NewServeMux()
mux.HandleFunc("/", func(w ResponseWriter, r *Request) {
uric <- r.RequestURI
})
ts := httptest.NewServer(mux)
defer ts.Close()
conn, err := net.Dial("tcp", ts.Listener.Addr().String())
if err != nil {
t.Fatal(err)
}
defer conn.Close()
// An OPTIONS * request should succeed.
_, err = conn.Write([]byte("OPTIONS * HTTP/1.1\r\nHost: foo.com\r\n\r\n"))
if err != nil {
t.Fatal(err)
}
br := bufio.NewReader(conn)
res, err := ReadResponse(br, &Request{Method: "OPTIONS"})
if err != nil {
t.Fatal(err)
}
if res.StatusCode != 200 {
t.Errorf("Got non-200 response to OPTIONS *: %#v", res)
}
// A GET * request on a ServeMux should fail.
_, err = conn.Write([]byte("GET * HTTP/1.1\r\nHost: foo.com\r\n\r\n"))
if err != nil {
t.Fatal(err)
}
res, err = ReadResponse(br, &Request{Method: "GET"})
if err != nil {
t.Fatal(err)
}
if res.StatusCode != 400 {
t.Errorf("Got non-400 response to GET *: %#v", res)
}
res, err = Get(ts.URL + "/second")
if err != nil {
t.Fatal(err)
}
res.Body.Close()
if got := <-uric; got != "/second" {
t.Errorf("Handler saw request for %q; want /second", got)
}
}
// goTimeout runs f, failing t if f takes more than ns to complete.
func goTimeout(t *testing.T, d time.Duration, f func()) {
ch := make(chan bool, 2)

View File

@ -702,24 +702,19 @@ func (c *conn) closeWriteAndWait() {
// Serve a new connection.
func (c *conn) serve() {
defer func() {
err := recover()
if err == nil {
return
if err := recover(); err != nil {
const size = 4096
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
}
const size = 4096
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
if c.rwc != nil { // may be nil if connection hijacked
c.rwc.Close()
if !c.hijacked() {
c.close()
}
}()
if tlsConn, ok := c.rwc.(*tls.Conn); ok {
if err := tlsConn.Handshake(); err != nil {
c.close()
return
}
c.tlsState = new(tls.ConnectionState)
@ -770,6 +765,9 @@ func (c *conn) serve() {
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
// HTTP cannot have multiple simultaneous active requests.[*]
// Until the server replies to this request, it can't read another,
@ -788,7 +786,6 @@ func (c *conn) serve() {
break
}
}
c.close()
}
func (w *response) sendExpectationFailed() {
@ -1085,6 +1082,11 @@ func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
w.Header().Set("Connection", "close")
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
@ -1408,6 +1410,22 @@ func (tw *timeoutWriter) WriteHeader(code int) {
tw.w.WriteHeader(code)
}
// globalOptionsHandler responds to "OPTIONS *" requests.
type globalOptionsHandler struct{}
func (globalOptionsHandler) ServeHTTP(w ResponseWriter, r *Request) {
w.Header().Set("Content-Length", "0")
if r.ContentLength != 0 {
// Read up to 4KB of OPTIONS body (as mentioned in the
// spec as being reserved for future use), but anything
// over that is considered a waste of server resources
// (or an attack) and we abort and close the connection,
// courtesy of MaxBytesReader's EOF behavior.
mb := MaxBytesReader(w, r.Body, 4<<10)
io.Copy(ioutil.Discard, mb)
}
}
// loggingConn is used for debugging.
type loggingConn struct {
name string

View File

@ -144,6 +144,9 @@ func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) {
}
return rt.RoundTrip(req)
}
if req.URL.Host == "" {
return nil, errors.New("http: no Host in request URL")
}
treq := &transportRequest{Request: req}
cm, err := t.connectMethodForRequest(treq)
if err != nil {
@ -739,6 +742,7 @@ WaitResponse:
case err := <-writeErrCh:
if err != nil {
re = responseAndError{nil, err}
pc.close()
break WaitResponse
}
case <-pconnDeadCh:

View File

@ -778,6 +778,45 @@ func TestTransportPersistConnLeak(t *testing.T) {
}
}
// golang.org/issue/4531: Transport leaks goroutines when
// request.ContentLength is explicitly short
func TestTransportPersistConnLeakShortBody(t *testing.T) {
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
}))
defer ts.Close()
tr := &Transport{}
c := &Client{Transport: tr}
n0 := runtime.NumGoroutine()
body := []byte("Hello")
for i := 0; i < 20; i++ {
req, err := NewRequest("POST", ts.URL, bytes.NewReader(body))
if err != nil {
t.Fatal(err)
}
req.ContentLength = int64(len(body) - 2) // explicitly short
_, err = c.Do(req)
if err == nil {
t.Fatal("Expect an error from writing too long of a body.")
}
}
nhigh := runtime.NumGoroutine()
tr.CloseIdleConnections()
time.Sleep(50 * time.Millisecond)
runtime.GC()
nfinal := runtime.NumGoroutine()
growth := nfinal - n0
// We expect 0 or 1 extra goroutine, empirically. Allow up to 5.
// Previously we were leaking one per numReq.
t.Logf("goroutine growth: %d -> %d -> %d (delta: %d)", n0, nhigh, nfinal, growth)
if int(growth) > 5 {
t.Error("too many new goroutines")
}
}
// This used to crash; http://golang.org/issue/3266
func TestTransportIdleConnCrash(t *testing.T) {
tr := &Transport{}
@ -1062,6 +1101,20 @@ func TestTransportAltProto(t *testing.T) {
}
}
func TestTransportNoHost(t *testing.T) {
tr := &Transport{}
_, err := tr.RoundTrip(&Request{
Header: make(Header),
URL: &url.URL{
Scheme: "http",
},
})
want := "http: no Host in request URL"
if got := fmt.Sprint(err); got != want {
t.Errorf("error = %v; want %q", err, want)
}
}
var proxyFromEnvTests = []struct {
env string
wanturl string

View File

@ -24,6 +24,15 @@ var packetConnTests = []struct {
}
func TestPacketConn(t *testing.T) {
closer := func(c net.PacketConn, net, addr1, addr2 string) {
c.Close()
switch net {
case "unixgram":
os.Remove(addr1)
os.Remove(addr2)
}
}
for _, tt := range packetConnTests {
var wb []byte
netstr := strings.Split(tt.net, ":")
@ -39,7 +48,7 @@ func TestPacketConn(t *testing.T) {
continue
}
id := os.Getpid() & 0xffff
wb = newICMPEchoRequest(id, 1, 128, []byte("IP PACKETCONN TEST "))
wb = newICMPEchoRequest(id, 1, 128, []byte("IP PACKETCONN TEST"))
case "unixgram":
switch runtime.GOOS {
case "plan9", "windows":
@ -60,7 +69,7 @@ func TestPacketConn(t *testing.T) {
c1.SetDeadline(time.Now().Add(100 * time.Millisecond))
c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
c1.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
defer c1.Close()
defer closer(c1, netstr[0], tt.addr1, tt.addr2)
c2, err := net.ListenPacket(tt.net, tt.addr2)
if err != nil {
@ -70,7 +79,7 @@ func TestPacketConn(t *testing.T) {
c2.SetDeadline(time.Now().Add(100 * time.Millisecond))
c2.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
c2.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
defer c2.Close()
defer closer(c2, netstr[0], tt.addr1, tt.addr2)
if _, err := c1.WriteTo(wb, c2.LocalAddr()); err != nil {
t.Fatalf("net.PacketConn.WriteTo failed: %v", err)
@ -86,12 +95,6 @@ func TestPacketConn(t *testing.T) {
if _, _, err := c1.ReadFrom(rb1); err != nil {
t.Fatalf("net.PacketConn.ReadFrom failed: %v", err)
}
switch netstr[0] {
case "unixgram":
os.Remove(tt.addr1)
os.Remove(tt.addr2)
}
}
}

View File

@ -263,9 +263,10 @@ func TestUnixConnSpecificMethods(t *testing.T) {
return
}
p1, p2 := "/tmp/gotest.net1", "/tmp/gotest.net2"
p1, p2, p3 := "/tmp/gotest.net1", "/tmp/gotest.net2", "/tmp/gotest.net3"
os.Remove(p1)
os.Remove(p2)
os.Remove(p3)
a1, err := net.ResolveUnixAddr("unixgram", p1)
if err != nil {
@ -305,9 +306,30 @@ func TestUnixConnSpecificMethods(t *testing.T) {
defer c2.Close()
defer os.Remove(p2)
a3, err := net.ResolveUnixAddr("unixgram", p3)
if err != nil {
t.Errorf("net.ResolveUnixAddr failed: %v", err)
return
}
c3, err := net.ListenUnixgram("unixgram", a3)
if err != nil {
t.Errorf("net.ListenUnixgram failed: %v", err)
return
}
c3.LocalAddr()
c3.RemoteAddr()
c3.SetDeadline(time.Now().Add(100 * time.Millisecond))
c3.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
c3.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
c3.SetReadBuffer(2048)
c3.SetWriteBuffer(2048)
defer c3.Close()
defer os.Remove(p3)
wb := []byte("UNIXCONN TEST")
rb1 := make([]byte, 128)
rb2 := make([]byte, 128)
rb3 := make([]byte, 128)
if _, _, err := c1.WriteMsgUnix(wb, nil, a2); err != nil {
t.Errorf("net.UnixConn.WriteMsgUnix failed: %v", err)
return
@ -324,9 +346,22 @@ func TestUnixConnSpecificMethods(t *testing.T) {
t.Errorf("net.UnixConn.ReadFromUnix failed: %v", err)
return
}
// TODO: http://golang.org/issue/3875
net.ListenUnixgram("unixgram", nil)
if _, err := c3.WriteToUnix(wb, a1); err != nil {
t.Errorf("net.UnixConn.WriteToUnix failed: %v", err)
return
}
if _, _, err := c1.ReadFromUnix(rb1); err != nil {
t.Errorf("net.UnixConn.ReadFromUnix failed: %v", err)
return
}
if _, err := c2.WriteToUnix(wb, a3); err != nil {
t.Errorf("net.UnixConn.WriteToUnix failed: %v", err)
return
}
if _, _, err := c3.ReadFromUnix(rb3); err != nil {
t.Errorf("net.UnixConn.ReadFromUnix failed: %v", err)
return
}
if f, err := c1.File(); err != nil {
t.Errorf("net.UnixConn.File failed: %v", err)

View File

@ -13,6 +13,7 @@ package smtp
import (
"crypto/tls"
"encoding/base64"
"errors"
"io"
"net"
"net/textproto"
@ -33,7 +34,10 @@ type Client struct {
// map of supported extensions
ext map[string]string
// supported auth mechanisms
auth []string
auth []string
localName string // the name to use in HELO/EHLO
didHello bool // whether we've said HELO/EHLO
helloError error // the error from the hello
}
// Dial returns a new Client connected to an SMTP server at addr.
@ -55,12 +59,33 @@ func NewClient(conn net.Conn, host string) (*Client, error) {
text.Close()
return nil, err
}
c := &Client{Text: text, conn: conn, serverName: host}
err = c.ehlo()
if err != nil {
err = c.helo()
c := &Client{Text: text, conn: conn, serverName: host, localName: "localhost"}
return c, nil
}
// hello runs a hello exchange if needed.
func (c *Client) hello() error {
if !c.didHello {
c.didHello = true
err := c.ehlo()
if err != nil {
c.helloError = c.helo()
}
}
return c, err
return c.helloError
}
// Hello sends a HELO or EHLO to the server as the given host name.
// Calling this method is only necessary if the client needs control
// over the host name used. The client will introduce itself as "localhost"
// automatically otherwise. If Hello is called, it must be called before
// any of the other methods.
func (c *Client) Hello(localName string) error {
if c.didHello {
return errors.New("smtp: Hello called after other methods")
}
c.localName = localName
return c.hello()
}
// cmd is a convenience function that sends a command and returns the response
@ -79,14 +104,14 @@ func (c *Client) cmd(expectCode int, format string, args ...interface{}) (int, s
// server does not support ehlo.
func (c *Client) helo() error {
c.ext = nil
_, _, err := c.cmd(250, "HELO localhost")
_, _, err := c.cmd(250, "HELO %s", c.localName)
return err
}
// ehlo sends the EHLO (extended hello) greeting to the server. It
// should be the preferred greeting for servers that support it.
func (c *Client) ehlo() error {
_, msg, err := c.cmd(250, "EHLO localhost")
_, msg, err := c.cmd(250, "EHLO %s", c.localName)
if err != nil {
return err
}
@ -113,6 +138,9 @@ func (c *Client) ehlo() error {
// StartTLS sends the STARTTLS command and encrypts all further communication.
// Only servers that advertise the STARTTLS extension support this function.
func (c *Client) StartTLS(config *tls.Config) error {
if err := c.hello(); err != nil {
return err
}
_, _, err := c.cmd(220, "STARTTLS")
if err != nil {
return err
@ -128,6 +156,9 @@ func (c *Client) StartTLS(config *tls.Config) error {
// does not necessarily indicate an invalid address. Many servers
// will not verify addresses for security reasons.
func (c *Client) Verify(addr string) error {
if err := c.hello(); err != nil {
return err
}
_, _, err := c.cmd(250, "VRFY %s", addr)
return err
}
@ -136,6 +167,9 @@ func (c *Client) Verify(addr string) error {
// A failed authentication closes the connection.
// Only servers that advertise the AUTH extension support this function.
func (c *Client) Auth(a Auth) error {
if err := c.hello(); err != nil {
return err
}
encoding := base64.StdEncoding
mech, resp, err := a.Start(&ServerInfo{c.serverName, c.tls, c.auth})
if err != nil {
@ -178,6 +212,9 @@ func (c *Client) Auth(a Auth) error {
// parameter.
// This initiates a mail transaction and is followed by one or more Rcpt calls.
func (c *Client) Mail(from string) error {
if err := c.hello(); err != nil {
return err
}
cmdStr := "MAIL FROM:<%s>"
if c.ext != nil {
if _, ok := c.ext["8BITMIME"]; ok {
@ -227,6 +264,9 @@ func SendMail(addr string, a Auth, from string, to []string, msg []byte) error {
if err != nil {
return err
}
if err := c.hello(); err != nil {
return err
}
if ok, _ := c.Extension("STARTTLS"); ok {
if err = c.StartTLS(nil); err != nil {
return err
@ -267,6 +307,9 @@ func SendMail(addr string, a Auth, from string, to []string, msg []byte) error {
// Extension also returns a string that contains any parameters the
// server specifies for the extension.
func (c *Client) Extension(ext string) (bool, string) {
if err := c.hello(); err != nil {
return false, ""
}
if c.ext == nil {
return false, ""
}
@ -278,12 +321,18 @@ func (c *Client) Extension(ext string) (bool, string) {
// Reset sends the RSET command to the server, aborting the current mail
// transaction.
func (c *Client) Reset() error {
if err := c.hello(); err != nil {
return err
}
_, _, err := c.cmd(250, "RSET")
return err
}
// Quit sends the QUIT command and closes the connection to the server.
func (c *Client) Quit() error {
if err := c.hello(); err != nil {
return err
}
_, _, err := c.cmd(221, "QUIT")
if err != nil {
return err

View File

@ -69,14 +69,14 @@ func (f faker) SetReadDeadline(time.Time) error { return nil }
func (f faker) SetWriteDeadline(time.Time) error { return nil }
func TestBasic(t *testing.T) {
basicServer = strings.Join(strings.Split(basicServer, "\n"), "\r\n")
basicClient = strings.Join(strings.Split(basicClient, "\n"), "\r\n")
server := strings.Join(strings.Split(basicServer, "\n"), "\r\n")
client := strings.Join(strings.Split(basicClient, "\n"), "\r\n")
var cmdbuf bytes.Buffer
bcmdbuf := bufio.NewWriter(&cmdbuf)
var fake faker
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(basicServer)), bcmdbuf)
c := &Client{Text: textproto.NewConn(fake)}
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
c := &Client{Text: textproto.NewConn(fake), localName: "localhost"}
if err := c.helo(); err != nil {
t.Fatalf("HELO failed: %s", err)
@ -88,6 +88,7 @@ func TestBasic(t *testing.T) {
t.Fatalf("Second EHLO failed: %s", err)
}
c.didHello = true
if ok, args := c.Extension("aUtH"); !ok || args != "LOGIN PLAIN" {
t.Fatalf("Expected AUTH supported")
}
@ -143,8 +144,8 @@ Goodbye.`
bcmdbuf.Flush()
actualcmds := cmdbuf.String()
if basicClient != actualcmds {
t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, basicClient)
if client != actualcmds {
t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
}
}
@ -187,8 +188,8 @@ QUIT
`
func TestNewClient(t *testing.T) {
newClientServer = strings.Join(strings.Split(newClientServer, "\n"), "\r\n")
newClientClient = strings.Join(strings.Split(newClientClient, "\n"), "\r\n")
server := strings.Join(strings.Split(newClientServer, "\n"), "\r\n")
client := strings.Join(strings.Split(newClientClient, "\n"), "\r\n")
var cmdbuf bytes.Buffer
bcmdbuf := bufio.NewWriter(&cmdbuf)
@ -197,7 +198,7 @@ func TestNewClient(t *testing.T) {
return cmdbuf.String()
}
var fake faker
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(newClientServer)), bcmdbuf)
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
c, err := NewClient(fake, "fake.host")
if err != nil {
t.Fatalf("NewClient: %v\n(after %v)", err, out())
@ -213,8 +214,8 @@ func TestNewClient(t *testing.T) {
}
actualcmds := out()
if newClientClient != actualcmds {
t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, newClientClient)
if client != actualcmds {
t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
}
}
@ -231,13 +232,13 @@ QUIT
`
func TestNewClient2(t *testing.T) {
newClient2Server = strings.Join(strings.Split(newClient2Server, "\n"), "\r\n")
newClient2Client = strings.Join(strings.Split(newClient2Client, "\n"), "\r\n")
server := strings.Join(strings.Split(newClient2Server, "\n"), "\r\n")
client := strings.Join(strings.Split(newClient2Client, "\n"), "\r\n")
var cmdbuf bytes.Buffer
bcmdbuf := bufio.NewWriter(&cmdbuf)
var fake faker
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(newClient2Server)), bcmdbuf)
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
c, err := NewClient(fake, "fake.host")
if err != nil {
t.Fatalf("NewClient: %v", err)
@ -251,8 +252,8 @@ func TestNewClient2(t *testing.T) {
bcmdbuf.Flush()
actualcmds := cmdbuf.String()
if newClient2Client != actualcmds {
t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, newClient2Client)
if client != actualcmds {
t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
}
}
@ -269,3 +270,199 @@ var newClient2Client = `EHLO localhost
HELO localhost
QUIT
`
func TestHello(t *testing.T) {
if len(helloServer) != len(helloClient) {
t.Fatalf("Hello server and client size mismatch")
}
for i := 0; i < len(helloServer); i++ {
server := strings.Join(strings.Split(baseHelloServer+helloServer[i], "\n"), "\r\n")
client := strings.Join(strings.Split(baseHelloClient+helloClient[i], "\n"), "\r\n")
var cmdbuf bytes.Buffer
bcmdbuf := bufio.NewWriter(&cmdbuf)
var fake faker
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
c, err := NewClient(fake, "fake.host")
if err != nil {
t.Fatalf("NewClient: %v", err)
}
c.localName = "customhost"
err = nil
switch i {
case 0:
err = c.Hello("customhost")
case 1:
err = c.StartTLS(nil)
if err.Error() == "502 Not implemented" {
err = nil
}
case 2:
err = c.Verify("test@example.com")
case 3:
c.tls = true
c.serverName = "smtp.google.com"
err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com"))
case 4:
err = c.Mail("test@example.com")
case 5:
ok, _ := c.Extension("feature")
if ok {
t.Errorf("Expected FEATURE not to be supported")
}
case 6:
err = c.Reset()
case 7:
err = c.Quit()
case 8:
err = c.Verify("test@example.com")
if err != nil {
err = c.Hello("customhost")
if err != nil {
t.Errorf("Want error, got none")
}
}
default:
t.Fatalf("Unhandled command")
}
if err != nil {
t.Errorf("Command %d failed: %v", i, err)
}
bcmdbuf.Flush()
actualcmds := cmdbuf.String()
if client != actualcmds {
t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client)
}
}
}
var baseHelloServer = `220 hello world
502 EH?
250-mx.google.com at your service
250 FEATURE
`
var helloServer = []string{
"",
"502 Not implemented\n",
"250 User is valid\n",
"235 Accepted\n",
"250 Sender ok\n",
"",
"250 Reset ok\n",
"221 Goodbye\n",
"250 Sender ok\n",
}
var baseHelloClient = `EHLO customhost
HELO customhost
`
var helloClient = []string{
"",
"STARTTLS\n",
"VRFY test@example.com\n",
"AUTH PLAIN AHVzZXIAcGFzcw==\n",
"MAIL FROM:<test@example.com>\n",
"",
"RSET\n",
"QUIT\n",
"VRFY test@example.com\n",
}
func TestSendMail(t *testing.T) {
server := strings.Join(strings.Split(sendMailServer, "\n"), "\r\n")
client := strings.Join(strings.Split(sendMailClient, "\n"), "\r\n")
var cmdbuf bytes.Buffer
bcmdbuf := bufio.NewWriter(&cmdbuf)
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to to create listener: %v", err)
}
defer l.Close()
// prevent data race on bcmdbuf
var done = make(chan struct{})
go func(data []string) {
defer close(done)
conn, err := l.Accept()
if err != nil {
t.Errorf("Accept error: %v", err)
return
}
defer conn.Close()
tc := textproto.NewConn(conn)
for i := 0; i < len(data) && data[i] != ""; i++ {
tc.PrintfLine(data[i])
for len(data[i]) >= 4 && data[i][3] == '-' {
i++
tc.PrintfLine(data[i])
}
if data[i] == "221 Goodbye" {
return
}
read := false
for !read || data[i] == "354 Go ahead" {
msg, err := tc.ReadLine()
bcmdbuf.Write([]byte(msg + "\r\n"))
read = true
if err != nil {
t.Errorf("Read error: %v", err)
return
}
if data[i] == "354 Go ahead" && msg == "." {
break
}
}
}
}(strings.Split(server, "\r\n"))
err = SendMail(l.Addr().String(), nil, "test@example.com", []string{"other@example.com"}, []byte(strings.Replace(`From: test@example.com
To: other@example.com
Subject: SendMail test
SendMail is working for me.
`, "\n", "\r\n", -1)))
if err != nil {
t.Errorf("%v", err)
}
<-done
bcmdbuf.Flush()
actualcmds := cmdbuf.String()
if client != actualcmds {
t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client)
}
}
var sendMailServer = `220 hello world
502 EH?
250 mx.google.com at your service
250 Sender ok
250 Receiver ok
354 Go ahead
250 Data ok
221 Goodbye
`
var sendMailClient = `EHLO localhost
HELO localhost
MAIL FROM:<test@example.com>
RCPT TO:<other@example.com>
DATA
From: test@example.com
To: other@example.com
Subject: SendMail test
SendMail is working for me.
.
QUIT
`

View File

@ -64,21 +64,21 @@ func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err
return 0, 0, syscall.EPLAN9
}
// CloseRead shuts down the reading side of the Unix domain
// connection. Most callers should just use Close.
// CloseRead shuts down the reading side of the Unix domain connection.
// Most callers should just use Close.
func (c *UnixConn) CloseRead() error {
return syscall.EPLAN9
}
// CloseWrite shuts down the writing side of the Unix domain
// connection. Most callers should just use Close.
// CloseWrite shuts down the writing side of the Unix domain connection.
// Most callers should just use Close.
func (c *UnixConn) CloseWrite() error {
return syscall.EPLAN9
}
// DialUnix connects to the remote address raddr on the network net,
// which must be "unix" or "unixgram". If laddr is not nil, it is
// used as the local address for the connection.
// which must be "unix", "unixgram" or "unixpacket". If laddr is not
// nil, it is used as the local address for the connection.
func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
return dialUnix(net, laddr, raddr, noDeadline)
}
@ -93,7 +93,8 @@ func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn
type UnixListener struct{}
// ListenUnix announces on the Unix domain socket laddr and returns a
// Unix listener. Net must be "unix" (stream sockets).
// Unix listener. The network net must be "unix", "unixgram" or
// "unixpacket".
func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
return nil, syscall.EPLAN9
}
@ -134,8 +135,8 @@ func (l *UnixListener) File() (*os.File, error) {
// ListenUnixgram listens for incoming Unix datagram packets addressed
// to the local address laddr. The returned connection c's ReadFrom
// and WriteTo methods can be used to receive and send UDP packets
// with per-packet addressing. The network net must be "unixgram".
func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) {
// and WriteTo methods can be used to receive and send packets with
// per-packet addressing. The network net must be "unixgram".
func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) {
return nil, syscall.EPLAN9
}

View File

@ -9,29 +9,27 @@
package net
import (
"errors"
"os"
"syscall"
"time"
)
func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.Time) (fd *netFD, err error) {
func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.Time) (*netFD, error) {
var sotype int
switch net {
default:
return nil, UnknownNetworkError(net)
case "unix":
sotype = syscall.SOCK_STREAM
case "unixgram":
sotype = syscall.SOCK_DGRAM
case "unixpacket":
sotype = syscall.SOCK_SEQPACKET
default:
return nil, UnknownNetworkError(net)
}
var la, ra syscall.Sockaddr
switch mode {
default:
panic("unixSocket mode " + mode)
case "dial":
if laddr != nil {
la = &syscall.SockaddrUnix{Name: laddr.Name}
@ -41,15 +39,10 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.T
} else if sotype != syscall.SOCK_DGRAM || laddr == nil {
return nil, &OpError{Op: mode, Net: net, Err: errMissingAddress}
}
case "listen":
if laddr == nil {
return nil, &OpError{mode, net, nil, errMissingAddress}
}
la = &syscall.SockaddrUnix{Name: laddr.Name}
if raddr != nil {
return nil, &OpError{Op: mode, Net: net, Addr: raddr, Err: &AddrError{Err: "unexpected remote address", Addr: raddr.String()}}
}
default:
return nil, errors.New("unknown mode: " + mode)
}
f := sockaddrToUnix
@ -59,15 +52,16 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.T
f = sockaddrToUnixpacket
}
fd, err = socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, deadline, f)
fd, err := socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, deadline, f)
if err != nil {
goto Error
goto error
}
return fd, nil
Error:
error:
addr := raddr
if mode == "listen" {
switch mode {
case "listen":
addr = laddr
}
return nil, &OpError{Op: mode, Net: net, Addr: addr, Err: err}
@ -108,21 +102,21 @@ func sotypeToNet(sotype int) string {
return ""
}
// UnixConn is an implementation of the Conn interface
// for connections to Unix domain sockets.
// UnixConn is an implementation of the Conn interface for connections
// to Unix domain sockets.
type UnixConn struct {
conn
}
func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{conn{fd}} }
// ReadFromUnix reads a packet from c, copying the payload into b.
// It returns the number of bytes copied into b and the source address
// of the packet.
// ReadFromUnix reads a packet from c, copying the payload into b. It
// returns the number of bytes copied into b and the source address of
// the packet.
//
// ReadFromUnix can be made to time out and return
// an error with Timeout() == true after a fixed time limit;
// see SetDeadline and SetReadDeadline.
// ReadFromUnix can be made to time out and return an error with
// Timeout() == true after a fixed time limit; see SetDeadline and
// SetReadDeadline.
func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) {
if !c.ok() {
return 0, nil, syscall.EINVAL
@ -144,12 +138,28 @@ func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
return n, uaddr.toAddr(), err
}
// ReadMsgUnix reads a packet from c, copying the payload into b and
// the associated out-of-band data into oob. It returns the number of
// bytes copied into b, the number of bytes copied into oob, the flags
// that were set on the packet, and the source address of the packet.
func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
if !c.ok() {
return 0, 0, 0, nil, syscall.EINVAL
}
n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob)
switch sa := sa.(type) {
case *syscall.SockaddrUnix:
addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)}
}
return
}
// WriteToUnix writes a packet to addr via c, copying the payload from b.
//
// WriteToUnix can be made to time out and return
// an error with Timeout() == true after a fixed time limit;
// see SetDeadline and SetWriteDeadline.
// On packet-oriented connections, write timeouts are rare.
// WriteToUnix can be made to time out and return an error with
// Timeout() == true after a fixed time limit; see SetDeadline and
// SetWriteDeadline. On packet-oriented connections, write timeouts
// are rare.
func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) {
if !c.ok() {
return 0, syscall.EINVAL
@ -173,26 +183,9 @@ func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) {
return c.WriteToUnix(b, a)
}
// ReadMsgUnix reads a packet from c, copying the payload into b
// and the associated out-of-band data into oob.
// It returns the number of bytes copied into b, the number of
// bytes copied into oob, the flags that were set on the packet,
// and the source address of the packet.
func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
if !c.ok() {
return 0, 0, 0, nil, syscall.EINVAL
}
n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob)
switch sa := sa.(type) {
case *syscall.SockaddrUnix:
addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)}
}
return
}
// WriteMsgUnix writes a packet to addr via c, copying the payload from b
// and the associated out-of-band data from oob. It returns the number
// of payload and out-of-band bytes written.
// WriteMsgUnix writes a packet to addr via c, copying the payload
// from b and the associated out-of-band data from oob. It returns
// the number of payload and out-of-band bytes written.
func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
if !c.ok() {
return 0, 0, syscall.EINVAL
@ -226,13 +219,18 @@ func (c *UnixConn) CloseWrite() error {
}
// DialUnix connects to the remote address raddr on the network net,
// which must be "unix" or "unixgram". If laddr is not nil, it is used
// as the local address for the connection.
// which must be "unix", "unixgram" or "unixpacket". If laddr is not
// nil, it is used as the local address for the connection.
func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
return dialUnix(net, laddr, raddr, noDeadline)
}
func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) {
switch net {
case "unix", "unixgram", "unixpacket":
default:
return nil, UnknownNetworkError(net)
}
fd, err := unixSocket(net, laddr, raddr, "dial", deadline)
if err != nil {
return nil, err
@ -240,22 +238,25 @@ func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn
return newUnixConn(fd), nil
}
// UnixListener is a Unix domain socket listener.
// Clients should typically use variables of type Listener
// instead of assuming Unix domain sockets.
// UnixListener is a Unix domain socket listener. Clients should
// typically use variables of type Listener instead of assuming Unix
// domain sockets.
type UnixListener struct {
fd *netFD
path string
}
// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener.
// Net must be "unix" (stream sockets).
// ListenUnix announces on the Unix domain socket laddr and returns a
// Unix listener. The network net must be "unix", "unixgram" or
// "unixpacket".
func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
if net != "unix" && net != "unixgram" && net != "unixpacket" {
switch net {
case "unix", "unixgram", "unixpacket":
default:
return nil, UnknownNetworkError(net)
}
if laddr != nil {
laddr = &UnixAddr{laddr.Name, net} // make our own copy
if laddr == nil {
return nil, &OpError{"listen", net, nil, errMissingAddress}
}
fd, err := unixSocket(net, laddr, nil, "listen", noDeadline)
if err != nil {
@ -269,8 +270,8 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
return &UnixListener{fd, laddr.Name}, nil
}
// AcceptUnix accepts the next incoming call and returns the new connection
// and the remote address.
// AcceptUnix accepts the next incoming call and returns the new
// connection and the remote address.
func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
if l == nil || l.fd == nil {
return nil, syscall.EINVAL
@ -283,8 +284,8 @@ func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
return c, nil
}
// Accept implements the Accept method in the Listener interface;
// it waits for the next call and returns a generic Conn.
// Accept implements the Accept method in the Listener interface; it
// waits for the next call and returns a generic Conn.
func (l *UnixListener) Accept() (c Conn, err error) {
c1, err := l.AcceptUnix()
if err != nil {
@ -293,8 +294,8 @@ func (l *UnixListener) Accept() (c Conn, err error) {
return c1, nil
}
// Close stops listening on the Unix address.
// Already accepted connections are not closed.
// Close stops listening on the Unix address. Already accepted
// connections are not closed.
func (l *UnixListener) Close() error {
if l == nil || l.fd == nil {
return syscall.EINVAL
@ -328,16 +329,16 @@ func (l *UnixListener) SetDeadline(t time.Time) (err error) {
return setDeadline(l.fd, t)
}
// File returns a copy of the underlying os.File, set to blocking mode.
// It is the caller's responsibility to close f when finished.
// File returns a copy of the underlying os.File, set to blocking
// mode. It is the caller's responsibility to close f when finished.
// Closing l does not affect f, and closing f does not affect l.
func (l *UnixListener) File() (f *os.File, err error) { return l.fd.dup() }
// ListenUnixgram listens for incoming Unix datagram packets addressed to the
// local address laddr. The returned connection c's ReadFrom
// and WriteTo methods can be used to receive and send UDP
// packets with per-packet addressing. The network net must be "unixgram".
func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) {
// ListenUnixgram listens for incoming Unix datagram packets addressed
// to the local address laddr. The returned connection c's ReadFrom
// and WriteTo methods can be used to receive and send packets with
// per-packet addressing. The network net must be "unixgram".
func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) {
switch net {
case "unixgram":
default:
@ -350,5 +351,5 @@ func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) {
if err != nil {
return nil, err
}
return newUDPConn(fd), nil
return newUnixConn(fd), nil
}

View File

@ -361,6 +361,11 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) {
}
url = new(URL)
if rawurl == "*" {
url.Path = "*"
return
}
// Split off possible leading "http:", "mailto:", etc.
// Cannot contain escaped characters.
if url.Scheme, rest, err = getscheme(rawurl); err != nil {
@ -572,23 +577,33 @@ func resolvePath(basepath string, refpath string) string {
if len(base) == 0 {
base = []string{""}
}
rm := true
for idx, ref := range refs {
switch {
case ref == ".":
base[len(base)-1] = ""
if idx == 0 {
base[len(base)-1] = ""
rm = true
} else {
rm = false
}
case ref == "..":
newLen := len(base) - 1
if newLen < 1 {
newLen = 1
}
base = base[0:newLen]
base[len(base)-1] = ""
if rm {
base[len(base)-1] = ""
}
default:
if idx == 0 || base[len(base)-1] == "" {
base[len(base)-1] = ref
} else {
base = append(base, ref)
}
rm = false
}
}
return strings.Join(base, "/")

View File

@ -277,7 +277,7 @@ func TestParse(t *testing.T) {
const pathThatLooksSchemeRelative = "//not.a.user@not.a.host/just/a/path"
var parseRequestUrlTests = []struct {
var parseRequestURLTests = []struct {
url string
expectedValid bool
}{
@ -289,10 +289,11 @@ var parseRequestUrlTests = []struct {
{"//not.a.user@%66%6f%6f.com/just/a/path/also", true},
{"foo.html", false},
{"../dir/", false},
{"*", true},
}
func TestParseRequestURI(t *testing.T) {
for _, test := range parseRequestUrlTests {
for _, test := range parseRequestURLTests {
_, err := ParseRequestURI(test.url)
valid := err == nil
if valid != test.expectedValid {
@ -536,6 +537,15 @@ var resolveReferenceTests = []struct {
{"http://foo.com/bar/baz", "../../../../../quux", "http://foo.com/quux"},
{"http://foo.com/bar", "..", "http://foo.com/"},
{"http://foo.com/bar/baz", "./..", "http://foo.com/"},
// ".." in the middle (issue 3560)
{"http://foo.com/bar/baz", "quux/dotdot/../tail", "http://foo.com/bar/quux/tail"},
{"http://foo.com/bar/baz", "quux/./dotdot/../tail", "http://foo.com/bar/quux/tail"},
{"http://foo.com/bar/baz", "quux/./dotdot/.././tail", "http://foo.com/bar/quux/tail"},
{"http://foo.com/bar/baz", "quux/./dotdot/./../tail", "http://foo.com/bar/quux/tail"},
{"http://foo.com/bar/baz", "quux/./dotdot/dotdot/././../../tail", "http://foo.com/bar/quux/tail"},
{"http://foo.com/bar/baz", "quux/./dotdot/dotdot/./.././../tail", "http://foo.com/bar/quux/tail"},
{"http://foo.com/bar/baz", "quux/./dotdot/dotdot/dotdot/./../../.././././tail", "http://foo.com/bar/quux/tail"},
{"http://foo.com/bar/baz", "quux/./dotdot/../dotdot/../dot/./tail/..", "http://foo.com/bar/quux/dot"},
// "." and ".." in the base aren't special
{"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/./dotdot/../baz"},

View File

@ -9,7 +9,7 @@ package os
import "syscall"
// Expand replaces ${var} or $var in the string based on the mapping function.
// Invocations of undefined variables are replaced with the empty string.
// For example, os.ExpandEnv(s) is equivalent to os.Expand(s, os.Getenv).
func Expand(s string, mapping func(string) string) string {
buf := make([]byte, 0, 2*len(s))
// ${} is all ASCII, so bytes are fine for this operation.

View File

@ -153,12 +153,10 @@ func (f *File) Sync() (err error) {
// less precise time unit.
// If there is an error, it will be of type *PathError.
func Chtimes(name string, atime time.Time, mtime time.Time) error {
var utimes [2]syscall.Timeval
atime_ns := atime.Unix()*1e9 + int64(atime.Nanosecond())
mtime_ns := mtime.Unix()*1e9 + int64(mtime.Nanosecond())
utimes[0] = syscall.NsecToTimeval(atime_ns)
utimes[1] = syscall.NsecToTimeval(mtime_ns)
if e := syscall.Utimes(name, utimes[0:]); e != nil {
var utimes [2]syscall.Timespec
utimes[0] = syscall.NsecToTimespec(atime.UnixNano())
utimes[1] = syscall.NsecToTimespec(mtime.UnixNano())
if e := syscall.UtimesNano(name, utimes[0:]); e != nil {
return &PathError{"chtimes", name, e}
}
return nil

View File

@ -2,7 +2,7 @@
// 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
// +build darwin freebsd linux netbsd openbsd
// +build cgo
package user

View File

@ -31,53 +31,52 @@ var good_re = []string{
`\!\\`,
}
/*
type stringError struct {
re string
err error
err string
}
var bad_re = []stringError{
{`*`, ErrBareClosure},
{`+`, ErrBareClosure},
{`?`, ErrBareClosure},
{`(abc`, ErrUnmatchedLpar},
{`abc)`, ErrUnmatchedRpar},
{`x[a-z`, ErrUnmatchedLbkt},
{`abc]`, ErrUnmatchedRbkt},
{`[z-a]`, ErrBadRange},
{`abc\`, ErrExtraneousBackslash},
{`a**`, ErrBadClosure},
{`a*+`, ErrBadClosure},
{`a??`, ErrBadClosure},
{`\x`, ErrBadBackslash},
{`*`, "missing argument to repetition operator: `*`"},
{`+`, "missing argument to repetition operator: `+`"},
{`?`, "missing argument to repetition operator: `?`"},
{`(abc`, "missing closing ): `(abc`"},
{`abc)`, "unexpected ): `abc)`"},
{`x[a-z`, "missing closing ]: `[a-z`"},
{`[z-a]`, "invalid character class range: `z-a`"},
{`abc\`, "trailing backslash at end of expression"},
{`a**`, "invalid nested repetition operator: `**`"},
{`a*+`, "invalid nested repetition operator: `*+`"},
{`\x`, "invalid escape sequence: `\\x`"},
}
*/
func compileTest(t *testing.T, expr string, error error) *Regexp {
func compileTest(t *testing.T, expr string, error string) *Regexp {
re, err := Compile(expr)
if err != error {
if error == "" && err != nil {
t.Error("compiling `", expr, "`; unexpected error: ", err.Error())
}
if error != "" && err == nil {
t.Error("compiling `", expr, "`; missing error")
} else if error != "" && !strings.Contains(err.Error(), error) {
t.Error("compiling `", expr, "`; wrong error: ", err.Error(), "; want ", error)
}
return re
}
func TestGoodCompile(t *testing.T) {
for i := 0; i < len(good_re); i++ {
compileTest(t, good_re[i], nil)
compileTest(t, good_re[i], "")
}
}
/*
func TestBadCompile(t *testing.T) {
for i := 0; i < len(bad_re); i++ {
compileTest(t, bad_re[i].re, bad_re[i].err)
}
}
*/
func matchTest(t *testing.T, test *FindTest) {
re := compileTest(t, test.pat, nil)
re := compileTest(t, test.pat, "")
if re == nil {
return
}

View File

@ -42,11 +42,9 @@ const (
ErrMissingParen ErrorCode = "missing closing )"
ErrMissingRepeatArgument ErrorCode = "missing argument to repetition operator"
ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression"
ErrUnexpectedParen ErrorCode = "unexpected )"
)
// TODO: Export for Go 1.1.
const errUnexpectedParen ErrorCode = "unexpected )"
func (e ErrorCode) String() string {
return string(e)
}
@ -1167,13 +1165,13 @@ func (p *parser) parseRightParen() error {
n := len(p.stack)
if n < 2 {
return &Error{errUnexpectedParen, p.wholeRegexp}
return &Error{ErrUnexpectedParen, p.wholeRegexp}
}
re1 := p.stack[n-1]
re2 := p.stack[n-2]
p.stack = p.stack[:n-2]
if re2.Op != opLeftParen {
return &Error{errUnexpectedParen, p.wholeRegexp}
return &Error{ErrUnexpectedParen, p.wholeRegexp}
}
// Restore flags at time of paren.
p.flags = re2.Flags

View File

@ -255,7 +255,7 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
// d = mant << (exp - mantbits)
// Next highest floating point number is mant+1 << exp-mantbits.
// Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1.
// Our upper bound is halfway between, mant*2+1 << exp-mantbits-1.
upper := new(decimal)
upper.Assign(mant*2 + 1)
upper.Shift(exp - int(flt.mantbits) - 1)
@ -265,7 +265,7 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
// unless mant-1 drops the significant bit and exp is not the minimum exp,
// in which case the next lowest is mant*2-1 << exp-mantbits-1.
// Either way, call it mantlo << explo-mantbits.
// Our lower bound is halfway inbetween, mantlo*2+1 << explo-mantbits-1.
// Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1.
var mantlo uint64
var explo int
if mant > 1<<flt.mantbits || exp == minexp {

View File

@ -12,14 +12,17 @@ import (
)
var (
// envOnce guards initialization by copyenv, which populates env.
// envOnce guards copyenv, which populates env.
envOnce sync.Once
// envLock guards env.
envLock sync.RWMutex
// env maps from an environment variable to its value.
env map[string]string
env = make(map[string]string)
errZeroLengthKey = errors.New("zero length key")
errShortWrite = errors.New("i/o count too small")
)
func readenv(key string) (string, error) {
@ -47,12 +50,18 @@ func writeenv(key, value string) error {
return err
}
defer Close(fd)
_, err = Write(fd, []byte(value))
return err
b := []byte(value)
n, err := Write(fd, b)
if err != nil {
return err
}
if n != len(b) {
return errShortWrite
}
return nil
}
func copyenv() {
env = make(map[string]string)
fd, err := Open("/env", O_RDONLY)
if err != nil {
return
@ -72,7 +81,6 @@ func copyenv() {
}
func Getenv(key string) (value string, found bool) {
envOnce.Do(copyenv)
if len(key) == 0 {
return "", false
}
@ -80,17 +88,20 @@ func Getenv(key string) (value string, found bool) {
envLock.RLock()
defer envLock.RUnlock()
v, ok := env[key]
if !ok {
if v, ok := env[key]; ok {
return v, true
}
v, err := readenv(key)
if err != nil {
return "", false
}
env[key] = v
return v, true
}
func Setenv(key, value string) error {
envOnce.Do(copyenv)
if len(key) == 0 {
return errors.New("zero length key")
return errZeroLengthKey
}
envLock.Lock()
@ -105,8 +116,6 @@ func Setenv(key, value string) error {
}
func Clearenv() {
envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv
envLock.Lock()
defer envLock.Unlock()
@ -115,9 +124,10 @@ func Clearenv() {
}
func Environ() []string {
envOnce.Do(copyenv)
envLock.RLock()
defer envLock.RUnlock()
envOnce.Do(copyenv)
a := make([]string, len(env))
i := 0
for k, v := range env {

View File

@ -0,0 +1,29 @@
// 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.
// GNU/Linux version of UtimesNano.
package syscall
import "unsafe"
//sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error)
//utimensat(dirfd int, path *byte, times *[2]Timespec, flags int) int
func UtimesNano(path string, ts []Timespec) (err error) {
if len(ts) != 2 {
return EINVAL
}
err = utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0)
if err != ENOSYS {
return err
}
// If the utimensat syscall isn't available (utimensat was added to Linux
// in 2.6.22, Released, 8 July 2007) then fall back to utimes
var tv [2]Timeval
for i := 0; i < 2; i++ {
tv[i].Sec = Timeval_sec_t(ts[i].Sec)
tv[i].Usec = Timeval_usec_t(ts[i].Nsec / 1000)
}
return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
}

View File

@ -0,0 +1,24 @@
// 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.
// General POSIX version of UtimesNano.
package syscall
import "unsafe"
func UtimesNano(path string, ts []Timespec) error {
// TODO: The BSDs can do utimensat with SYS_UTIMENSAT but it
// isn't supported by darwin so this uses utimes instead
if len(ts) != 2 {
return EINVAL
}
// Not as efficient as it could be because Timespec and
// Timeval have different types in the different OSes
tv := [2]Timeval{
NsecToTimeval(TimespecToNsec(ts[0])),
NsecToTimeval(TimespecToNsec(ts[1])),
}
return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
}

View File

@ -3,10 +3,15 @@
// license that can be found in the LICENSE file.
// Package syscall contains an interface to the low-level operating system
// primitives. The details vary depending on the underlying system.
// Its primary use is inside other packages that provide a more portable
// interface to the system, such as "os", "time" and "net". Use those
// packages rather than this one if you can.
// primitives. The details vary depending on the underlying system, and
// by default, godoc will display the syscall documentation for the current
// system. If you want godoc to display syscall documentation for another
// system, set $GOOS and $GOARCH to the desired system. For example, if
// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS
// to freebsd and $GOARCH to arm.
// The primary use of syscall is inside other packages that provide a more
// portable interface to the system, such as "os", "time" and "net". Use
// those packages rather than this one if you can.
// For details of the functions and data types in this package consult
// the manuals for the appropriate operating system.
// These calls return err == nil to indicate success; otherwise

View File

@ -24,7 +24,7 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int
var eg InternalExample
stdout, stderr := os.Stdout, os.Stderr
stdout := os.Stdout
for _, eg = range examples {
matched, err := matchString(*match, eg.Name)
@ -39,19 +39,19 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int
fmt.Printf("=== RUN: %s\n", eg.Name)
}
// capture stdout and stderr
// capture stdout
r, w, err := os.Pipe()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Stdout, os.Stderr = w, w
os.Stdout = w
outC := make(chan string)
go func() {
buf := new(bytes.Buffer)
_, err := io.Copy(buf, r)
if err != nil {
fmt.Fprintf(stderr, "testing: copying pipe: %v\n", err)
fmt.Fprintf(os.Stderr, "testing: copying pipe: %v\n", err)
os.Exit(1)
}
outC <- buf.String()
@ -62,9 +62,9 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int
eg.F()
dt := time.Now().Sub(t0)
// close pipe, restore stdout/stderr, get output
// close pipe, restore stdout, get output
w.Close()
os.Stdout, os.Stderr = stdout, stderr
os.Stdout = stdout
out := <-outC
// report any errors

View File

@ -854,9 +854,15 @@ func Parse(layout, value string) (Time, error) {
zoneName = p
case stdFracSecond0:
ndigit := std >> stdArgShift
nsec, rangeErrString, err = parseNanoseconds(value, 1+ndigit)
value = value[1+ndigit:]
// stdFracSecond0 requires the exact number of digits as specified in
// the layout.
ndigit := 1 + (std >> stdArgShift)
if len(value) < ndigit {
err = errBad
break
}
nsec, rangeErrString, err = parseNanoseconds(value, ndigit)
value = value[ndigit:]
case stdFracSecond9:
if len(value) < 2 || value[0] != '.' || value[1] < '0' || '9' < value[1] {
@ -934,8 +940,7 @@ func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string,
err = errBad
return
}
ns, err = atoi(value[1:nbytes])
if err != nil {
if ns, err = atoi(value[1:nbytes]); err != nil {
return
}
if ns < 0 || 1e9 <= ns {

View File

@ -469,7 +469,7 @@ type ParseTest struct {
value string
hasTZ bool // contains a time zone
hasWD bool // contains a weekday
yearSign int // sign of year
yearSign int // sign of year, -1 indicates the year is not present in the format
fracDigits int // number of digits of fractional second
}
@ -514,6 +514,13 @@ var parseTests = []ParseTest{
{"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
{"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
{"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
// issue 4502.
{"", StampNano, "Feb 4 21:00:57.012345678", false, false, -1, 9},
{"", "Jan _2 15:04:05.999", "Feb 4 21:00:57.012300000", false, false, -1, 4},
{"", "Jan _2 15:04:05.999", "Feb 4 21:00:57.012345678", false, false, -1, 9},
{"", "Jan _2 15:04:05.999999999", "Feb 4 21:00:57.0123", false, false, -1, 4},
{"", "Jan _2 15:04:05.999999999", "Feb 4 21:00:57.012345678", false, false, -1, 9},
}
func TestParse(t *testing.T) {
@ -549,7 +556,7 @@ func TestRubyParse(t *testing.T) {
func checkTime(time Time, test *ParseTest, t *testing.T) {
// The time should be Thu Feb 4 21:00:57 PST 2010
if test.yearSign*time.Year() != 2010 {
if test.yearSign >= 0 && test.yearSign*time.Year() != 2010 {
t.Errorf("%s: bad year: %d not %d", test.name, time.Year(), 2010)
}
if time.Month() != February {
@ -630,6 +637,9 @@ var parseErrorTests = []ParseErrorTest{
{"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59x01 2010", "cannot parse"},
{"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59.xxx 2010", "cannot parse"},
{"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59.-123 2010", "fractional second out of range"},
// issue 4502. StampNano requires exactly 9 digits of precision.
{StampNano, "Dec 7 11:22:01.000000", `cannot parse ".000000" as ".000000000"`},
{StampNano, "Dec 7 11:22:01.0000000000", "extra text: 0"},
}
func TestParseErrors(t *testing.T) {

View File

@ -174,7 +174,7 @@ func loadZoneData(bytes []byte) (l *Location, err error) {
}
}
// Commited to succeed.
// Committed to succeed.
l = &Location{zone: zone, tx: tx}
// Fill in the cache with information about right now,

View File

@ -163,7 +163,7 @@ done
done
done
runtime="chan.c cpuprof.c lock_futex.c lock_sema.c mcache.c mcentral.c mfinal.c mfixalloc.c mgc0.c mheap.c msize.c panic.c print.c proc.c race.h runtime.c runtime.h signal_unix.c malloc.h malloc.goc mprof.goc parfor.c runtime1.goc sema.goc sigqueue.goc string.goc time.goc"
runtime="chan.c cpuprof.c env_posix.c lock_futex.c lock_sema.c mcache.c mcentral.c mfinal.c mfixalloc.c mgc0.c mgc0.h mheap.c msize.c panic.c print.c proc.c race.h runtime.c runtime.h signal_unix.c malloc.h malloc.goc mprof.goc parfor.c runtime1.goc sema.goc sigqueue.goc string.goc time.goc"
for f in $runtime; do
merge_c $f $f
done

37
libgo/runtime/env_posix.c Normal file
View File

@ -0,0 +1,37 @@
// 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
#include "runtime.h"
#include "array.h"
extern Slice syscall_Envs asm ("syscall.Envs");
const byte*
runtime_getenv(const char *s)
{
int32 i, j, len;
const byte *v, *bs;
String* envv;
int32 envc;
bs = (const byte*)s;
len = runtime_findnull(bs);
envv = (String*)syscall_Envs.__values;
envc = syscall_Envs.__count;
for(i=0; i<envc; i++){
if(envv[i].len <= len)
continue;
v = (const byte*)envv[i].str;
for(j=0; j<len; j++)
if(bs[j] != v[j])
goto nomatch;
if(v[len] != '=')
goto nomatch;
return v+len+1;
nomatch:;
}
return nil;
}

View File

@ -106,8 +106,8 @@ __go_allocate_trampoline (uintptr_t size, void *closure)
no other references to it. */
void
runtime_trampoline_scan (void (*addroot) (byte *, uintptr))
runtime_trampoline_scan (void (*addroot) (Obj))
{
if (trampoline_page != NULL)
addroot ((byte *) &trampoline_page, sizeof trampoline_page);
addroot ((Obj){(byte *) &trampoline_page, sizeof trampoline_page, 0});
}

View File

@ -491,7 +491,7 @@ runtime_MHeap_SysAlloc(MHeap *h, uintptr n)
static Lock settype_lock;
void
runtime_settype_flush(M *m, bool sysalloc)
runtime_settype_flush(M *mp, bool sysalloc)
{
uintptr *buf, *endbuf;
uintptr size, ofs, j, t;
@ -503,8 +503,8 @@ runtime_settype_flush(M *m, bool sysalloc)
uintptr typ, p;
MSpan *s;
buf = m->settype_buf;
endbuf = buf + m->settype_bufsize;
buf = mp->settype_buf;
endbuf = buf + mp->settype_bufsize;
runtime_lock(&settype_lock);
while(buf < endbuf) {
@ -602,7 +602,7 @@ runtime_settype_flush(M *m, bool sysalloc)
}
runtime_unlock(&settype_lock);
m->settype_bufsize = 0;
mp->settype_bufsize = 0;
}
// It is forbidden to use this function if it is possible that
@ -610,7 +610,7 @@ runtime_settype_flush(M *m, bool sysalloc)
void
runtime_settype(void *v, uintptr t)
{
M *m1;
M *mp;
uintptr *buf;
uintptr i;
MSpan *s;
@ -618,16 +618,16 @@ runtime_settype(void *v, uintptr t)
if(t == 0)
runtime_throw("settype: zero type");
m1 = runtime_m();
buf = m1->settype_buf;
i = m1->settype_bufsize;
mp = runtime_m();
buf = mp->settype_buf;
i = mp->settype_bufsize;
buf[i+0] = (uintptr)v;
buf[i+1] = t;
i += 2;
m1->settype_bufsize = i;
mp->settype_bufsize = i;
if(i == nelem(m1->settype_buf)) {
runtime_settype_flush(m1, false);
if(i == nelem(mp->settype_buf)) {
runtime_settype_flush(mp, false);
}
if(DebugTypeAtBlockEnd) {

View File

@ -468,17 +468,25 @@ enum
FlagNoGC = 1<<2, // must not free or scan for pointers
};
typedef struct Obj Obj;
struct Obj
{
byte *p; // data pointer
uintptr n; // size of data in bytes
uintptr ti; // type info
};
void runtime_MProf_Malloc(void*, uintptr);
void runtime_MProf_Free(void*, uintptr);
void runtime_MProf_GC(void);
void runtime_MProf_Mark(void (*addroot)(byte *, uintptr));
void runtime_MProf_Mark(void (*addroot)(Obj));
int32 runtime_gcprocs(void);
void runtime_helpgc(int32 nproc);
void runtime_gchelper(void);
struct __go_func_type;
bool runtime_getfinalizer(void *p, bool del, void (**fn)(void*), const struct __go_func_type **ft);
void runtime_walkfintab(void (*fn)(void*), void (*scan)(byte *, uintptr));
void runtime_walkfintab(void (*fn)(void*), void (*scan)(Obj));
enum
{
@ -494,3 +502,6 @@ enum
void runtime_gc_m_ptr(Eface*);
void runtime_memorydump(void);
void runtime_time_scan(void (*)(Obj));
void runtime_trampoline_scan(void (*)(Obj));

View File

@ -193,7 +193,7 @@ runtime_getfinalizer(void *p, bool del, void (**fn)(void*), const struct __go_fu
}
void
runtime_walkfintab(void (*fn)(void*), void (*addroot)(byte *, uintptr))
runtime_walkfintab(void (*fn)(void*), void (*addroot)(Obj))
{
void **key;
void **ekey;
@ -206,8 +206,8 @@ runtime_walkfintab(void (*fn)(void*), void (*addroot)(byte *, uintptr))
for(; key < ekey; key++)
if(*key != nil && *key != ((void*)-1))
fn(*key);
addroot((byte*)&fintab[i].fkey, sizeof(void*));
addroot((byte*)&fintab[i].val, sizeof(void*));
addroot((Obj){(byte*)&fintab[i].fkey, sizeof(void*), 0});
addroot((Obj){(byte*)&fintab[i].val, sizeof(void*), 0});
runtime_unlock(&fintab[i]);
}
}

View File

@ -9,6 +9,7 @@
#include "runtime.h"
#include "arch.h"
#include "malloc.h"
#include "mgc0.h"
#include "race.h"
#ifdef USING_SPLIT_STACK
@ -24,11 +25,13 @@ extern void * __splitstack_find_context (void *context[10], size_t *, void **,
enum {
Debug = 0,
DebugMark = 0, // run second pass to check mark
DataBlock = 8*1024,
// Four bits per word (see #defines below).
wordsPerBitmapWord = sizeof(void*)*8/4,
bitShift = sizeof(void*)*8/4,
handoffThreshold = 4,
IntermediateBufferCapacity = 64,
};
// Bits in per-word bitmap.
@ -81,12 +84,16 @@ uint32 runtime_worldsema = 1;
static int32 gctrace;
// The size of Workbuf is N*PageSize.
typedef struct Workbuf Workbuf;
struct Workbuf
{
LFNode node; // must be first
#define SIZE (2*PageSize-sizeof(LFNode)-sizeof(uintptr))
LFNode node; // must be first
uintptr nobj;
byte *obj[512-(sizeof(LFNode)+sizeof(uintptr))/sizeof(byte*)];
Obj obj[SIZE/sizeof(Obj) - 1];
uint8 _padding[SIZE%sizeof(Obj) + sizeof(Obj)];
#undef SIZE
};
typedef struct Finalizer Finalizer;
@ -120,13 +127,6 @@ static Workbuf* getfull(Workbuf*);
static void putempty(Workbuf*);
static Workbuf* handoff(Workbuf*);
typedef struct GcRoot GcRoot;
struct GcRoot
{
byte *p;
uintptr n;
};
static struct {
uint64 full; // lock-free list of full blocks
uint64 empty; // lock-free list of empty blocks
@ -143,77 +143,122 @@ static struct {
byte *chunk;
uintptr nchunk;
GcRoot *roots;
Obj *roots;
uint32 nroot;
uint32 rootcap;
} work;
// scanblock scans a block of n bytes starting at pointer b for references
// to other objects, scanning any it finds recursively until there are no
// unscanned objects left. Instead of using an explicit recursion, it keeps
// a work list in the Workbuf* structures and loops in the main function
// body. Keeping an explicit work list is easier on the stack allocator and
// more efficient.
static void
scanblock(byte *b, uintptr n)
enum {
// TODO(atom): to be expanded in a next CL
GC_DEFAULT_PTR = GC_NUM_INSTR,
};
// PtrTarget and BitTarget are structures used by intermediate buffers.
// The intermediate buffers hold GC data before it
// is moved/flushed to the work buffer (Workbuf).
// The size of an intermediate buffer is very small,
// such as 32 or 64 elements.
struct PtrTarget
{
byte *obj, *arena_start, *arena_used, *p;
void **vp;
uintptr size, *bitp, bits, shift, i, j, x, xbits, off, nobj, nproc;
void *p;
uintptr ti;
};
struct BitTarget
{
void *p;
uintptr ti;
uintptr *bitp, shift;
};
struct BufferList
{
struct PtrTarget ptrtarget[IntermediateBufferCapacity];
struct BitTarget bittarget[IntermediateBufferCapacity];
struct BufferList *next;
};
static struct BufferList *bufferList;
static Lock lock;
// flushptrbuf moves data from the PtrTarget buffer to the work buffer.
// The PtrTarget buffer contains blocks irrespective of whether the blocks have been marked or scanned,
// while the work buffer contains blocks which have been marked
// and are prepared to be scanned by the garbage collector.
//
// _wp, _wbuf, _nobj are input/output parameters and are specifying the work buffer.
// bitbuf holds temporary data generated by this function.
//
// A simplified drawing explaining how the todo-list moves from a structure to another:
//
// scanblock
// (find pointers)
// Obj ------> PtrTarget (pointer targets)
// ↑ |
// | | flushptrbuf (1st part,
// | | find block start)
// | ↓
// `--------- BitTarget (pointer targets and the corresponding locations in bitmap)
// flushptrbuf
// (2nd part, mark and enqueue)
static void
flushptrbuf(struct PtrTarget *ptrbuf, uintptr n, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj, struct BitTarget *bitbuf)
{
byte *p, *arena_start, *obj;
uintptr size, *bitp, bits, shift, j, x, xbits, off, nobj, ti;
MSpan *s;
PageID k;
void **wp;
Obj *wp;
Workbuf *wbuf;
bool keepworking;
struct PtrTarget *ptrbuf_end;
struct BitTarget *bitbufpos, *bt;
if((intptr)n < 0) {
runtime_printf("scanblock %p %D\n", b, (int64)n);
runtime_throw("scanblock");
}
// Memory arena parameters.
arena_start = runtime_mheap.arena_start;
arena_used = runtime_mheap.arena_used;
nproc = work.nproc;
wbuf = nil; // current work buffer
wp = nil; // storage for next queued pointer (write pointer)
nobj = 0; // number of queued objects
wp = *_wp;
wbuf = *_wbuf;
nobj = *_nobj;
// Scanblock helpers pass b==nil.
// Procs needs to return to make more
// calls to scanblock. But if work.nproc==1 then
// might as well process blocks as soon as we
// have them.
keepworking = b == nil || work.nproc == 1;
ptrbuf_end = ptrbuf + n;
// Align b to a word boundary.
off = (uintptr)b & (PtrSize-1);
if(off != 0) {
b += PtrSize - off;
n -= PtrSize - off;
// If buffer is nearly full, get a new one.
if(wbuf == nil || nobj+n >= nelem(wbuf->obj)) {
if(wbuf != nil)
wbuf->nobj = nobj;
wbuf = getempty(wbuf);
wp = wbuf->obj;
nobj = 0;
if(n >= nelem(wbuf->obj))
runtime_throw("ptrbuf has to be smaller than WorkBuf");
}
for(;;) {
// Each iteration scans the block b of length n, queueing pointers in
// the work buffer.
if(Debug > 1)
runtime_printf("scanblock %p %D\n", b, (int64)n);
// TODO(atom): This block is a branch of an if-then-else statement.
// The single-threaded branch may be added in a next CL.
{
// Multi-threaded version.
vp = (void**)b;
n >>= (2+PtrSize/8); /* n /= PtrSize (4 or 8) */
for(i=0; i<(uintptr)n; i++) {
obj = (byte*)vp[i];
bitbufpos = bitbuf;
// Words outside the arena cannot be pointers.
if((byte*)obj < arena_start || (byte*)obj >= arena_used)
continue;
while(ptrbuf < ptrbuf_end) {
obj = ptrbuf->p;
ti = ptrbuf->ti;
ptrbuf++;
// obj belongs to interval [mheap.arena_start, mheap.arena_used).
if(Debug > 1) {
if(obj < runtime_mheap.arena_start || obj >= runtime_mheap.arena_used)
runtime_throw("object is outside of mheap");
}
// obj may be a pointer to a live object.
// Try to find the beginning of the object.
// Round down to word boundary.
obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1));
if(((uintptr)obj & ((uintptr)PtrSize-1)) != 0) {
obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1));
ti = 0;
}
// Find bits for this word.
off = (uintptr*)obj - (uintptr*)arena_start;
@ -226,6 +271,8 @@ scanblock(byte *b, uintptr n)
if((bits & (bitAllocated|bitBlockBoundary)) != 0)
goto found;
ti = 0;
// Pointing just past the beginning?
// Scan backward a little to find a block boundary.
for(j=shift; j-->0; ) {
@ -246,13 +293,13 @@ scanblock(byte *b, uintptr n)
s = runtime_mheap.map[x];
if(s == nil || k < s->start || k - s->start >= s->npages || s->state != MSpanInUse)
continue;
p = (byte*)((uintptr)s->start<<PageShift);
p = (byte*)((uintptr)s->start<<PageShift);
if(s->sizeclass == 0) {
obj = p;
} else {
if((byte*)obj >= (byte*)s->limit)
continue;
size = runtime_class_to_size[s->sizeclass];
size = s->elemsize;
int32 i = ((byte*)obj - p)/size;
obj = p+i*size;
}
@ -265,81 +312,203 @@ scanblock(byte *b, uintptr n)
bits = xbits >> shift;
found:
// If another proc wants a pointer, give it some.
if(work.nwait > 0 && nobj > 4 && work.full == 0) {
wbuf->nobj = nobj;
wbuf = handoff(wbuf);
nobj = wbuf->nobj;
wp = (void**)(wbuf->obj + nobj);
}
// Now we have bits, bitp, and shift correct for
// obj pointing at the base of the object.
// Only care about allocated and not marked.
if((bits & (bitAllocated|bitMarked)) != bitAllocated)
continue;
if(nproc == 1)
*bitp |= bitMarked<<shift;
else {
for(;;) {
x = *bitp;
if(x & (bitMarked<<shift))
goto continue_obj;
if(runtime_casp((void**)bitp, (void*)x, (void*)(x|(bitMarked<<shift))))
break;
}
}
*bitbufpos = (struct BitTarget){obj, ti, bitp, shift};
bitbufpos++;
}
runtime_lock(&lock);
for(bt=bitbuf; bt<bitbufpos; bt++){
xbits = *bt->bitp;
bits = xbits >> bt->shift;
if((bits & bitMarked) != 0)
continue;
// Mark the block
*bt->bitp = xbits | (bitMarked << bt->shift);
// If object has no pointers, don't need to scan further.
if((bits & bitNoPointers) != 0)
continue;
obj = bt->p;
// Ask span about size class.
// (Manually inlined copy of MHeap_Lookup.)
x = (uintptr)obj >> PageShift;
if(sizeof(void*) == 8)
x -= (uintptr)arena_start>>PageShift;
s = runtime_mheap.map[x];
PREFETCH(obj);
// If buffer is full, get a new one.
if(wbuf == nil || nobj >= nelem(wbuf->obj)) {
if(wbuf != nil)
wbuf->nobj = nobj;
wbuf = getempty(wbuf);
wp = (void**)(wbuf->obj);
nobj = 0;
}
*wp++ = obj;
*wp = (Obj){obj, s->elemsize, bt->ti};
wp++;
nobj++;
continue_obj:;
}
runtime_unlock(&lock);
// If another proc wants a pointer, give it some.
if(work.nwait > 0 && nobj > handoffThreshold && work.full == 0) {
wbuf->nobj = nobj;
wbuf = handoff(wbuf);
nobj = wbuf->nobj;
wp = wbuf->obj + nobj;
}
}
*_wp = wp;
*_wbuf = wbuf;
*_nobj = nobj;
}
// Program that scans the whole block and treats every block element as a potential pointer
static uintptr defaultProg[2] = {PtrSize, GC_DEFAULT_PTR};
// scanblock scans a block of n bytes starting at pointer b for references
// to other objects, scanning any it finds recursively until there are no
// unscanned objects left. Instead of using an explicit recursion, it keeps
// a work list in the Workbuf* structures and loops in the main function
// body. Keeping an explicit work list is easier on the stack allocator and
// more efficient.
//
// wbuf: current work buffer
// wp: storage for next queued pointer (write pointer)
// nobj: number of queued objects
static void
scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
{
byte *b, *arena_start, *arena_used;
uintptr n, i, end_b;
void *obj;
// TODO(atom): to be expanded in a next CL
struct Frame {uintptr count, b; uintptr *loop_or_ret;};
struct Frame stack_top;
uintptr *pc;
struct BufferList *scanbuffers;
struct PtrTarget *ptrbuf, *ptrbuf_end;
struct BitTarget *bitbuf;
struct PtrTarget *ptrbufpos;
// End of local variable declarations.
if(sizeof(Workbuf) % PageSize != 0)
runtime_throw("scanblock: size of Workbuf is suboptimal");
// Memory arena parameters.
arena_start = runtime_mheap.arena_start;
arena_used = runtime_mheap.arena_used;
// Allocate ptrbuf, bitbuf
{
runtime_lock(&lock);
if(bufferList == nil) {
bufferList = runtime_SysAlloc(sizeof(*bufferList));
bufferList->next = nil;
}
scanbuffers = bufferList;
bufferList = bufferList->next;
ptrbuf = &scanbuffers->ptrtarget[0];
ptrbuf_end = &scanbuffers->ptrtarget[0] + nelem(scanbuffers->ptrtarget);
bitbuf = &scanbuffers->bittarget[0];
runtime_unlock(&lock);
}
ptrbufpos = ptrbuf;
goto next_block;
for(;;) {
// Each iteration scans the block b of length n, queueing pointers in
// the work buffer.
if(Debug > 1) {
runtime_printf("scanblock %p %D\n", b, (int64)n);
}
// TODO(atom): to be replaced in a next CL
pc = defaultProg;
pc++;
stack_top.b = (uintptr)b;
end_b = (uintptr)b + n - PtrSize;
next_instr:
// TODO(atom): to be expanded in a next CL
switch(pc[0]) {
case GC_DEFAULT_PTR:
while(true) {
i = stack_top.b;
if(i > end_b)
goto next_block;
stack_top.b += PtrSize;
obj = *(byte**)i;
if((byte*)obj >= arena_start && (byte*)obj < arena_used) {
*ptrbufpos = (struct PtrTarget){obj, 0};
ptrbufpos++;
if(ptrbufpos == ptrbuf_end)
goto flush_buffers;
}
}
default:
runtime_throw("scanblock: invalid GC instruction");
return;
}
flush_buffers:
flushptrbuf(ptrbuf, ptrbufpos-ptrbuf, &wp, &wbuf, &nobj, bitbuf);
ptrbufpos = ptrbuf;
goto next_instr;
next_block:
// Done scanning [b, b+n). Prepare for the next iteration of
// the loop by setting b and n to the parameters for the next block.
// the loop by setting b, n to the parameters for the next block.
if(nobj == 0) {
flushptrbuf(ptrbuf, ptrbufpos-ptrbuf, &wp, &wbuf, &nobj, bitbuf);
ptrbufpos = ptrbuf;
if(nobj == 0) {
if(!keepworking) {
if(wbuf)
putempty(wbuf);
goto endscan;
}
// Emptied our buffer: refill.
wbuf = getfull(wbuf);
if(wbuf == nil)
goto endscan;
nobj = wbuf->nobj;
wp = wbuf->obj + wbuf->nobj;
}
}
// Fetch b from the work buffer.
if(nobj == 0) {
if(!keepworking) {
if(wbuf)
putempty(wbuf);
return;
}
// Emptied our buffer: refill.
wbuf = getfull(wbuf);
if(wbuf == nil)
return;
nobj = wbuf->nobj;
wp = (void**)(wbuf->obj + wbuf->nobj);
}
b = *--wp;
--wp;
b = wp->p;
n = wp->n;
nobj--;
// Ask span about size class.
// (Manually inlined copy of MHeap_Lookup.)
x = (uintptr)b>>PageShift;
if(sizeof(void*) == 8)
x -= (uintptr)arena_start>>PageShift;
s = runtime_mheap.map[x];
if(s->sizeclass == 0)
n = s->npages<<PageShift;
else
n = runtime_class_to_size[s->sizeclass];
}
endscan:
runtime_lock(&lock);
scanbuffers->next = bufferList;
bufferList = scanbuffers;
runtime_unlock(&lock);
}
// debug_scanblock is the debug copy of scanblock.
@ -386,13 +555,12 @@ debug_scanblock(byte *b, uintptr n)
continue;
p = (byte*)((uintptr)s->start<<PageShift);
size = s->elemsize;
if(s->sizeclass == 0) {
obj = p;
size = (uintptr)s->npages<<PageShift;
} else {
if((byte*)obj >= (byte*)s->limit)
continue;
size = runtime_class_to_size[s->sizeclass];
int32 i = ((byte*)obj - p)/size;
obj = p+i*size;
}
@ -421,11 +589,74 @@ debug_scanblock(byte *b, uintptr n)
}
}
// Append obj to the work buffer.
// _wbuf, _wp, _nobj are input/output parameters and are specifying the work buffer.
static void
enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj)
{
uintptr nobj, off;
Obj *wp;
Workbuf *wbuf;
if(Debug > 1)
runtime_printf("append obj(%p %D %p)\n", obj.p, (int64)obj.n, obj.ti);
// Align obj.b to a word boundary.
off = (uintptr)obj.p & (PtrSize-1);
if(off != 0) {
obj.p += PtrSize - off;
obj.n -= PtrSize - off;
obj.ti = 0;
}
if(obj.p == nil || obj.n == 0)
return;
// Load work buffer state
wp = *_wp;
wbuf = *_wbuf;
nobj = *_nobj;
// If another proc wants a pointer, give it some.
if(work.nwait > 0 && nobj > handoffThreshold && work.full == 0) {
wbuf->nobj = nobj;
wbuf = handoff(wbuf);
nobj = wbuf->nobj;
wp = wbuf->obj + nobj;
}
// If buffer is full, get a new one.
if(wbuf == nil || nobj >= nelem(wbuf->obj)) {
if(wbuf != nil)
wbuf->nobj = nobj;
wbuf = getempty(wbuf);
wp = wbuf->obj;
nobj = 0;
}
*wp = obj;
wp++;
nobj++;
// Save work buffer state
*_wp = wp;
*_wbuf = wbuf;
*_nobj = nobj;
}
static void
markroot(ParFor *desc, uint32 i)
{
Obj *wp;
Workbuf *wbuf;
uintptr nobj;
USED(&desc);
scanblock(work.roots[i].p, work.roots[i].n);
wp = nil;
wbuf = nil;
nobj = 0;
enqueue(work.roots[i], &wbuf, &wp, &nobj);
scanblock(wbuf, wp, nobj, false);
}
// Get an empty work buffer off the work.empty list,
@ -520,25 +751,24 @@ handoff(Workbuf *b)
}
static void
addroot(byte *p, uintptr n)
addroot(Obj obj)
{
uint32 cap;
GcRoot *new;
Obj *new;
if(work.nroot >= work.rootcap) {
cap = PageSize/sizeof(GcRoot);
cap = PageSize/sizeof(Obj);
if(cap < 2*work.rootcap)
cap = 2*work.rootcap;
new = (GcRoot*)runtime_SysAlloc(cap*sizeof(GcRoot));
new = (Obj*)runtime_SysAlloc(cap*sizeof(Obj));
if(work.roots != nil) {
runtime_memmove(new, work.roots, work.rootcap*sizeof(GcRoot));
runtime_SysFree(work.roots, work.rootcap*sizeof(GcRoot));
runtime_memmove(new, work.roots, work.rootcap*sizeof(Obj));
runtime_SysFree(work.roots, work.rootcap*sizeof(Obj));
}
work.roots = new;
work.rootcap = cap;
}
work.roots[work.nroot].p = p;
work.roots[work.nroot].n = n;
work.roots[work.nroot] = obj;
work.nroot++;
}
@ -582,11 +812,11 @@ addstackroots(G *gp)
}
}
if(sp != nil) {
addroot(sp, spsize);
addroot((Obj){sp, spsize, 0});
while((sp = __splitstack_find(next_segment, next_sp,
&spsize, &next_segment,
&next_sp, &initial_sp)) != nil)
addroot(sp, spsize);
addroot((Obj){sp, spsize, 0});
}
#else
M *mp;
@ -608,9 +838,9 @@ addstackroots(G *gp)
}
top = (byte*)gp->gcinitial_sp + gp->gcstack_size;
if(top > bottom)
addroot(bottom, top - bottom);
addroot((Obj){bottom, top - bottom, 0});
else
addroot(top, bottom - top);
addroot((Obj){top, bottom - top, 0});
#endif
}
@ -624,7 +854,7 @@ addfinroots(void *v)
runtime_throw("mark - finalizer inconsistency");
// do not mark the finalizer block itself. just mark the things it points at.
addroot(v, size);
addroot((Obj){v, size, 0});
}
static struct root_list* roots;
@ -656,15 +886,15 @@ addroots(void)
void *decl = pr->decl;
if(decl == nil)
break;
addroot(decl, pr->size);
addroot((Obj){decl, pr->size, 0});
pr++;
}
}
addroot((byte*)&runtime_m0, sizeof runtime_m0);
addroot((byte*)&runtime_g0, sizeof runtime_g0);
addroot((byte*)&runtime_allg, sizeof runtime_allg);
addroot((byte*)&runtime_allm, sizeof runtime_allm);
addroot((Obj){(byte*)&runtime_m0, sizeof runtime_m0, 0});
addroot((Obj){(byte*)&runtime_g0, sizeof runtime_g0, 0});
addroot((Obj){(byte*)&runtime_allg, sizeof runtime_allg, 0});
addroot((Obj){(byte*)&runtime_allm, sizeof runtime_allm, 0});
runtime_MProf_Mark(addroot);
runtime_time_scan(addroot);
runtime_trampoline_scan(addroot);
@ -680,12 +910,14 @@ addroots(void)
break;
case MTypes_Words:
case MTypes_Bytes:
addroot((byte*)&s->types.data, sizeof(void*));
// TODO(atom): consider using defaultProg instead of 0
addroot((Obj){(byte*)&s->types.data, sizeof(void*), 0});
break;
}
}
}
// stacks
for(gp=runtime_allg; gp!=nil; gp=gp->alllink) {
switch(gp->status){
default:
@ -709,9 +941,9 @@ addroots(void)
runtime_walkfintab(addfinroots, addroot);
for(fb=allfin; fb; fb=fb->alllink)
addroot((byte*)fb->fin, fb->cnt*sizeof(fb->fin[0]));
addroot((Obj){(byte*)fb->fin, fb->cnt*sizeof(fb->fin[0]), 0});
addroot((byte*)&work, sizeof work);
addroot((Obj){(byte*)&work, sizeof work, 0});
}
static bool
@ -955,8 +1187,9 @@ runtime_gchelper(void)
{
// parallel mark for over gc roots
runtime_parfordo(work.markfor);
// help other threads scan secondary blocks
scanblock(nil, 0);
scanblock(nil, nil, 0, true);
if(DebugMark) {
// wait while the main thread executes mark(debug_scanblock)
@ -983,16 +1216,16 @@ static int32 gcpercent = -2;
static void
stealcache(void)
{
M *m;
M *mp;
for(m=runtime_allm; m; m=m->alllink)
runtime_MCache_ReleaseAll(m->mcache);
for(mp=runtime_allm; mp; mp=mp->alllink)
runtime_MCache_ReleaseAll(mp->mcache);
}
static void
cachestats(GCStats *stats)
{
M *m;
M *mp;
MCache *c;
uint32 i;
uint64 stacks_inuse;
@ -1003,17 +1236,17 @@ cachestats(GCStats *stats)
runtime_memclr((byte*)stats, sizeof(*stats));
stacks_inuse = 0;
stacks_sys = runtime_stacks_sys;
for(m=runtime_allm; m; m=m->alllink) {
c = m->mcache;
for(mp=runtime_allm; mp; mp=mp->alllink) {
c = mp->mcache;
runtime_purgecachedstats(c);
// stacks_inuse += m->stackalloc->inuse;
// stacks_sys += m->stackalloc->sys;
// stacks_inuse += mp->stackalloc->inuse;
// stacks_sys += mp->stackalloc->sys;
if(stats) {
src = (uint64*)&m->gcstats;
src = (uint64*)&mp->gcstats;
dst = (uint64*)stats;
for(i=0; i<sizeof(*stats)/sizeof(uint64); i++)
dst[i] += src[i];
runtime_memclr((byte*)&m->gcstats, sizeof(m->gcstats));
runtime_memclr((byte*)&mp->gcstats, sizeof(mp->gcstats));
}
for(i=0; i<nelem(c->local_by_size); i++) {
mstats.by_size[i].nmalloc += c->local_by_size[i].nmalloc;
@ -1100,7 +1333,7 @@ gc(struct gc_args *args)
int64 t0, t1, t2, t3;
uint64 heap0, heap1, obj0, obj1;
GCStats stats;
M *m1;
M *mp;
uint32 i;
runtime_semacquire(&runtime_worldsema);
@ -1116,8 +1349,8 @@ gc(struct gc_args *args)
m->gcing = 1;
runtime_stoptheworld();
for(m1=runtime_allm; m1; m1=m1->alllink)
runtime_settype_flush(m1, false);
for(mp=runtime_allm; mp; mp=mp->alllink)
runtime_settype_flush(mp, false);
heap0 = 0;
obj0 = 0;
@ -1127,26 +1360,27 @@ gc(struct gc_args *args)
obj0 = mstats.nmalloc - mstats.nfree;
}
m->locks++; // disable gc during mallocs in parforalloc
if(work.markfor == nil)
work.markfor = runtime_parforalloc(MaxGcproc);
if(work.sweepfor == nil)
work.sweepfor = runtime_parforalloc(MaxGcproc);
m->locks--;
work.nwait = 0;
work.ndone = 0;
work.debugmarkdone = 0;
work.nproc = runtime_gcprocs();
addroots();
m->locks++; // disable gc during mallocs in parforalloc
if(work.markfor == nil)
work.markfor = runtime_parforalloc(MaxGcproc);
runtime_parforsetup(work.markfor, work.nproc, work.nroot, nil, false, markroot);
if(work.sweepfor == nil)
work.sweepfor = runtime_parforalloc(MaxGcproc);
runtime_parforsetup(work.sweepfor, work.nproc, runtime_mheap.nspan, nil, true, sweepspan);
m->locks--;
if(work.nproc > 1) {
runtime_noteclear(&work.alldone);
runtime_helpgc(work.nproc);
}
runtime_parfordo(work.markfor);
scanblock(nil, 0);
scanblock(nil, nil, 0, true);
if(DebugMark) {
for(i=0; i<work.nroot; i++)

42
libgo/runtime/mgc0.h Normal file
View File

@ -0,0 +1,42 @@
// 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.
// Garbage collector (GC)
// GC instruction opcodes.
//
// The opcode of an instruction is followed by zero or more
// arguments to the instruction.
//
// Meaning of arguments:
// off Offset (in bytes) from the start of the current object
// objgc Pointer to GC info of an object
// len Length of an array
// elemsize Size (in bytes) of an element
// size Size (in bytes)
enum {
GC_END, // End of object, loop or subroutine. Args: none
GC_PTR, // A typed pointer. Args: (off, objgc)
GC_APTR, // Pointer to an arbitrary object. Args: (off)
GC_ARRAY_START, // Start an array with a fixed length. Args: (off, len, elemsize)
GC_ARRAY_NEXT, // The next element of an array. Args: none
GC_CALL, // Call a subroutine. Args: (off, objgc)
GC_MAP_PTR, // Go map. Args: (off, MapType*)
GC_STRING, // Go string. Args: (off)
GC_EFACE, // interface{}. Args: (off)
GC_IFACE, // interface{...}. Args: (off)
GC_SLICE, // Go slice. Args: (off, objgc)
GC_REGION, // A region/part of the current object. Args: (off, size, objgc)
GC_NUM_INSTR, // Number of instruction opcodes
};
enum {
// Size of GC's fixed stack.
//
// The current GC implementation permits:
// - at most 1 stack allocation because of GC_CALL
// - at most GC_STACK_CAPACITY allocations because of GC_ARRAY_START
GC_STACK_CAPACITY = 8,
};

View File

@ -362,13 +362,13 @@ func MemProfile(p Slice, include_inuse_zero bool) (n int, ok bool) {
}
void
runtime_MProf_Mark(void (*addroot)(byte *, uintptr))
runtime_MProf_Mark(void (*addroot)(Obj))
{
// buckhash is not allocated via mallocgc.
addroot((byte*)&mbuckets, sizeof mbuckets);
addroot((byte*)&bbuckets, sizeof bbuckets);
addroot((byte*)&addrhash, sizeof addrhash);
addroot((byte*)&addrfree, sizeof addrfree);
addroot((Obj){(byte*)&mbuckets, sizeof mbuckets, 0});
addroot((Obj){(byte*)&bbuckets, sizeof bbuckets, 0});
addroot((Obj){(byte*)&addrhash, sizeof addrhash, 0});
addroot((Obj){(byte*)&addrfree, sizeof addrfree, 0});
}
// Must match BlockProfileRecord in debug.go.
@ -412,18 +412,18 @@ struct TRecord {
func ThreadCreateProfile(p Slice) (n int, ok bool) {
TRecord *r;
M *first, *m;
M *first, *mp;
first = runtime_atomicloadp(&runtime_allm);
n = 0;
for(m=first; m; m=m->alllink)
for(mp=first; mp; mp=mp->alllink)
n++;
ok = false;
if(n <= p.__count) {
ok = true;
r = (TRecord*)p.__values;
for(m=first; m; m=m->alllink) {
runtime_memmove(r->stk, m->createstack, sizeof r->stk);
for(mp=first; mp; mp=mp->alllink) {
runtime_memmove(r->stk, mp->createstack, sizeof r->stk);
r++;
}
}
@ -471,11 +471,11 @@ func Stack(b Slice, all bool) (n int) {
}
static void
saveg(G *g, TRecord *r)
saveg(G *gp, TRecord *r)
{
int32 n;
if(g == runtime_g())
if(gp == runtime_g())
n = runtime_callers(0, r->stk, nelem(r->stk));
else {
// FIXME: Not implemented.

Some files were not shown because too many files have changed in this diff Show More