libgo: Update to revision 15193:6fdc1974457c of master library.
From-SVN: r194692
This commit is contained in:
parent
7e9268b4cf
commit
409a5e7eb4
104 changed files with 2434 additions and 686 deletions
|
@ -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.
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
BIN
libgo/go/debug/elf/testdata/hello-world-core.gz
vendored
Normal file
BIN
libgo/go/debug/elf/testdata/hello-world-core.gz
vendored
Normal file
Binary file not shown.
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }.
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
31
libgo/go/exp/types/testdata/builtins.src
vendored
31
libgo/go/exp/types/testdata/builtins.src
vendored
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
5
libgo/go/exp/types/testdata/decls1.src
vendored
5
libgo/go/exp/types/testdata/decls1.src
vendored
|
@ -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
|
||||
|
|
4
libgo/go/exp/types/testdata/expr0.src
vendored
4
libgo/go/exp/types/testdata/expr0.src
vendored
|
@ -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" */
|
||||
|
||||
)
|
||||
)
|
||||
|
|
11
libgo/go/exp/types/testdata/expr2.src
vendored
11
libgo/go/exp/types/testdata/expr2.src
vendored
|
@ -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
|
||||
|
|
61
libgo/go/exp/types/testdata/expr3.src
vendored
61
libgo/go/exp/types/testdata/expr3.src
vendored
|
@ -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" */ ())
|
||||
}
|
26
libgo/go/exp/types/testdata/stmt0.src
vendored
26
libgo/go/exp/types/testdata/stmt0.src
vendored
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.(...)"},
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
14
libgo/go/net/cgo_openbsd.go
Normal file
14
libgo/go/net/cgo_openbsd.go
Normal 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
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
`
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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, "/")
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
29
libgo/go/syscall/libcall_linux_utimesnano.go
Normal file
29
libgo/go/syscall/libcall_linux_utimesnano.go
Normal 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])))
|
||||
}
|
24
libgo/go/syscall/libcall_posix_utimesnano.go
Normal file
24
libgo/go/syscall/libcall_posix_utimesnano.go
Normal 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])))
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
37
libgo/runtime/env_posix.c
Normal 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;
|
||||
}
|
|
@ -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});
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
42
libgo/runtime/mgc0.h
Normal 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,
|
||||
};
|
|
@ -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
Loading…
Add table
Reference in a new issue