From 409a5e7eb4cca107037fafa4a7eea92603edb83d Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sat, 22 Dec 2012 01:15:33 +0000 Subject: [PATCH] libgo: Update to revision 15193:6fdc1974457c of master library. From-SVN: r194692 --- libgo/MERGE | 2 +- libgo/Makefile.am | 9 + libgo/Makefile.in | 27 +- libgo/go/builtin/builtin.go | 4 +- libgo/go/compress/flate/deflate.go | 2 +- libgo/go/crypto/cipher/example_test.go | 4 +- libgo/go/crypto/x509/x509_test.go | 4 +- libgo/go/database/sql/fakedb_test.go | 32 +- libgo/go/database/sql/sql.go | 4 +- libgo/go/database/sql/sql_test.go | 33 ++ libgo/go/debug/elf/file.go | 7 +- libgo/go/debug/elf/file_test.go | 59 ++- libgo/go/debug/elf/testdata/hello-world-core.gz | Bin 0 -> 12678 bytes libgo/go/encoding/csv/writer.go | 7 + libgo/go/encoding/csv/writer_test.go | 28 ++ libgo/go/encoding/gob/decode.go | 6 +- libgo/go/encoding/json/decode.go | 36 +- libgo/go/exp/gotype/gotype_test.go | 54 +-- libgo/go/exp/locale/collate/build/trie.go | 2 +- libgo/go/exp/norm/iter.go | 2 +- libgo/go/exp/norm/normalize_test.go | 2 +- libgo/go/exp/norm/triegen.go | 2 +- libgo/go/exp/types/builtins.go | 51 ++- libgo/go/exp/types/const.go | 126 ++++-- libgo/go/exp/types/errors.go | 11 +- libgo/go/exp/types/expr.go | 156 +++++-- libgo/go/exp/types/operand.go | 9 +- libgo/go/exp/types/predicates.go | 15 +- libgo/go/exp/types/stmt.go | 72 +++- libgo/go/exp/types/testdata/builtins.src | 31 +- libgo/go/exp/types/testdata/decls1.src | 5 +- libgo/go/exp/types/testdata/expr0.src | 4 +- libgo/go/exp/types/testdata/expr2.src | 11 + libgo/go/exp/types/testdata/expr3.src | 61 +++ libgo/go/exp/types/testdata/stmt0.src | 26 +- libgo/go/exp/types/types.go | 10 +- libgo/go/exp/types/types_test.go | 2 +- libgo/go/fmt/fmt_test.go | 5 + libgo/go/fmt/format.go | 6 +- libgo/go/fmt/scan.go | 5 +- libgo/go/go/ast/ast.go | 2 +- libgo/go/go/build/build.go | 32 +- libgo/go/go/doc/comment.go | 3 +- libgo/go/go/doc/example.go | 35 +- libgo/go/go/format/format.go | 2 +- libgo/go/go/printer/nodes.go | 2 +- libgo/go/go/token/position.go | 18 +- libgo/go/go/token/position_test.go | 51 +++ libgo/go/io/io.go | 5 + libgo/go/io/io_test.go | 32 ++ libgo/go/log/syslog/syslog_test.go | 10 +- libgo/go/math/all_test.go | 7 + libgo/go/math/big/int.go | 20 + libgo/go/math/big/int_test.go | 30 ++ libgo/go/math/big/nat.go | 2 +- libgo/go/math/log10.go | 3 +- libgo/go/net/cgo_openbsd.go | 14 + libgo/go/net/cgo_unix.go | 2 +- libgo/go/net/conn_test.go | 16 +- libgo/go/net/dial.go | 2 +- libgo/go/net/dial_test.go | 3 +- libgo/go/net/http/client.go | 34 +- libgo/go/net/http/client_test.go | 118 ++++- libgo/go/net/http/pprof/pprof.go | 5 +- libgo/go/net/http/readrequest_test.go | 48 +++ libgo/go/net/http/serve_test.go | 70 ++- libgo/go/net/http/server.go | 44 +- libgo/go/net/http/transport.go | 4 + libgo/go/net/http/transport_test.go | 53 +++ libgo/go/net/packetconn_test.go | 21 +- libgo/go/net/protoconn_test.go | 43 +- libgo/go/net/smtp/smtp.go | 65 ++- libgo/go/net/smtp/smtp_test.go | 229 +++++++++- libgo/go/net/unixsock_plan9.go | 21 +- libgo/go/net/unixsock_posix.go | 147 +++---- libgo/go/net/url/url.go | 19 +- libgo/go/net/url/url_test.go | 14 +- libgo/go/os/env.go | 2 +- libgo/go/os/file_posix.go | 10 +- libgo/go/os/user/lookup_unix.go | 2 +- libgo/go/regexp/all_test.go | 47 +- libgo/go/regexp/syntax/parse.go | 8 +- libgo/go/strconv/ftoa.go | 4 +- libgo/go/syscall/env_plan9.go | 36 +- libgo/go/syscall/libcall_linux_utimesnano.go | 29 ++ libgo/go/syscall/libcall_posix_utimesnano.go | 24 ++ libgo/go/syscall/syscall.go | 13 +- libgo/go/testing/example.go | 12 +- libgo/go/time/format.go | 15 +- libgo/go/time/time_test.go | 14 +- libgo/go/time/zoneinfo_read.go | 2 +- libgo/merge.sh | 2 +- libgo/runtime/env_posix.c | 37 ++ libgo/runtime/go-trampoline.c | 4 +- libgo/runtime/malloc.goc | 22 +- libgo/runtime/malloc.h | 15 +- libgo/runtime/mfinal.c | 6 +- libgo/runtime/mgc0.c | 550 +++++++++++++++++------- libgo/runtime/mgc0.h | 42 ++ libgo/runtime/mprof.goc | 22 +- libgo/runtime/proc.c | 8 +- libgo/runtime/runtime.c | 27 -- libgo/runtime/runtime.h | 3 - libgo/runtime/time.goc | 4 +- 104 files changed, 2433 insertions(+), 685 deletions(-) create mode 100644 libgo/go/debug/elf/testdata/hello-world-core.gz create mode 100644 libgo/go/net/cgo_openbsd.go create mode 100644 libgo/go/syscall/libcall_linux_utimesnano.go create mode 100644 libgo/go/syscall/libcall_posix_utimesnano.go create mode 100644 libgo/runtime/env_posix.c create mode 100644 libgo/runtime/mgc0.h diff --git a/libgo/MERGE b/libgo/MERGE index deeb596..52537f9 100644 --- a/libgo/MERGE +++ b/libgo/MERGE @@ -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. diff --git a/libgo/Makefile.am b/libgo/Makefile.am index 3da2cb4..b97a82e 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -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) \ diff --git a/libgo/Makefile.in b/libgo/Makefile.in index 869addf..fe18b74 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -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 diff --git a/libgo/go/builtin/builtin.go b/libgo/go/builtin/builtin.go index a30943b..91d263a 100644 --- a/libgo/go/builtin/builtin.go +++ b/libgo/go/builtin/builtin.go @@ -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: diff --git a/libgo/go/compress/flate/deflate.go b/libgo/go/compress/flate/deflate.go index e511b50..d357fe3 100644 --- a/libgo/go/compress/flate/deflate.go +++ b/libgo/go/compress/flate/deflate.go @@ -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. diff --git a/libgo/go/crypto/cipher/example_test.go b/libgo/go/crypto/cipher/example_test.go index c888eb2..e0027ca 100644 --- a/libgo/go/crypto/cipher/example_test.go +++ b/libgo/go/crypto/cipher/example_test.go @@ -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. } diff --git a/libgo/go/crypto/x509/x509_test.go b/libgo/go/crypto/x509/x509_test.go index a13f459..b2d6fe3 100644 --- a/libgo/go/crypto/x509/x509_test.go +++ b/libgo/go/crypto/x509/x509_test.go @@ -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) } } diff --git a/libgo/go/database/sql/fakedb_test.go b/libgo/go/database/sql/fakedb_test.go index aec5727..c38ba7c 100644 --- a/libgo/go/database/sql/fakedb_test.go +++ b/libgo/go/database/sql/fakedb_test.go @@ -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: // -// ; (no currently supported options) +// ; (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") } diff --git a/libgo/go/database/sql/sql.go b/libgo/go/database/sql/sql.go index b0cba94..e7c7780 100644 --- a/libgo/go/database/sql/sql.go +++ b/libgo/go/database/sql/sql.go @@ -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, diff --git a/libgo/go/database/sql/sql_test.go b/libgo/go/database/sql/sql_test.go index 1bfb590..b702b85 100644 --- a/libgo/go/database/sql/sql_test.go +++ b/libgo/go/database/sql/sql_test.go @@ -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) { diff --git a/libgo/go/debug/elf/file.go b/libgo/go/debug/elf/file.go index f462375..eb3d72f 100644 --- a/libgo/go/debug/elf/file.go +++ b/libgo/go/debug/elf/file.go @@ -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 { diff --git a/libgo/go/debug/elf/file_test.go b/libgo/go/debug/elf/file_test.go index 810a2f9..f9aa726 100644 --- a/libgo/go/debug/elf/file_test.go +++ b/libgo/go/debug/elf/file_test.go @@ -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 diff --git a/libgo/go/debug/elf/testdata/hello-world-core.gz b/libgo/go/debug/elf/testdata/hello-world-core.gz new file mode 100644 index 0000000000000000000000000000000000000000..806af6edbc23bd0db45a9b1adfae9418ca6a969a GIT binary patch literal 12678 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 Y-g)s?BF`+qef#Ve(D~239)f%S3*17p82|tP literal 0 HcmV?d00001 diff --git a/libgo/go/encoding/csv/writer.go b/libgo/go/encoding/csv/writer.go index 17e4850..1faecb6 100644 --- a/libgo/go/encoding/csv/writer.go +++ b/libgo/go/encoding/csv/writer.go @@ -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 { diff --git a/libgo/go/encoding/csv/writer_test.go b/libgo/go/encoding/csv/writer_test.go index 5789590..03ca6b0 100644 --- a/libgo/go/encoding/csv/writer_test.go +++ b/libgo/go/encoding/csv/writer_test.go @@ -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") + } +} diff --git a/libgo/go/encoding/gob/decode.go b/libgo/go/encoding/gob/decode.go index 900c69d..a80d9f9 100644 --- a/libgo/go/encoding/gob/decode.go +++ b/libgo/go/encoding/gob/decode.go @@ -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 diff --git a/libgo/go/encoding/json/decode.go b/libgo/go/encoding/json/decode.go index 1e0c8d4..b46dac9 100644 --- a/libgo/go/encoding/json/decode.go +++ b/libgo/go/encoding/json/decode.go @@ -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 }. diff --git a/libgo/go/exp/gotype/gotype_test.go b/libgo/go/exp/gotype/gotype_test.go index 2d58f32..c0c2e32 100644 --- a/libgo/go/exp/gotype/gotype_test.go +++ b/libgo/go/exp/gotype/gotype_test.go @@ -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", diff --git a/libgo/go/exp/locale/collate/build/trie.go b/libgo/go/exp/locale/collate/build/trie.go index f521427..9404a34 100644 --- a/libgo/go/exp/locale/collate/build/trie.go +++ b/libgo/go/exp/locale/collate/build/trie.go @@ -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 { diff --git a/libgo/go/exp/norm/iter.go b/libgo/go/exp/norm/iter.go index c0ab25e..def822d 100644 --- a/libgo/go/exp/norm/iter.go +++ b/libgo/go/exp/norm/iter.go @@ -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) } diff --git a/libgo/go/exp/norm/normalize_test.go b/libgo/go/exp/norm/normalize_test.go index 8b97059..1a118f2 100644 --- a/libgo/go/exp/norm/normalize_test.go +++ b/libgo/go/exp/norm/normalize_test.go @@ -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 { diff --git a/libgo/go/exp/norm/triegen.go b/libgo/go/exp/norm/triegen.go index 1780ac7..52c88b0 100644 --- a/libgo/go/exp/norm/triegen.go +++ b/libgo/go/exp/norm/triegen.go @@ -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 ) diff --git a/libgo/go/exp/types/builtins.go b/libgo/go/exp/types/builtins.go index 8826704..ed636ee 100644 --- a/libgo/go/exp/types/builtins.go +++ b/libgo/go/exp/types/builtins.go @@ -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 +} diff --git a/libgo/go/exp/types/const.go b/libgo/go/exp/types/const.go index c678e47..d44c8fb 100644 --- a/libgo/go/exp/types/const.go +++ b/libgo/go/exp/types/const.go @@ -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)< 0 { - buf.WriteString("; ") - } - writeType(buf, typ) - } - buf.WriteByte(')') + case *Result: + writeParams(buf, t.Values, false) case *Signature: buf.WriteString("func") diff --git a/libgo/go/exp/types/expr.go b/libgo/go/exp/types/expr.go index e1f627b..2f5f2b3 100644 --- a/libgo/go/exp/types/expr.go +++ b/libgo/go/exp/types/expr.go @@ -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 + } } - // determine result - x.mode = value - if len(sig.Results) == 1 { - x.typ = sig.Results[0].Type.(Type) + // 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 { - // 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) + // 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) } - x.typ = &tuple{list: list} + } + + // 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 + switch len(sig.Results) { + case 0: + x.mode = novalue + case 1: + x.mode = value + x.typ = sig.Results[0].Type.(Type) + 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) diff --git a/libgo/go/exp/types/operand.go b/libgo/go/exp/types/operand.go index 1a5e517..f8ddd84 100644 --- a/libgo/go/exp/types/operand.go +++ b/libgo/go/exp/types/operand.go @@ -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: diff --git a/libgo/go/exp/types/predicates.go b/libgo/go/exp/types/predicates.go index 2c1a991..ff6825b 100644 --- a/libgo/go/exp/types/predicates.go +++ b/libgo/go/exp/types/predicates.go @@ -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] } diff --git a/libgo/go/exp/types/stmt.go b/libgo/go/exp/types/stmt.go index e2c6448..7f9d45e 100644 --- a/libgo/go/exp/types/stmt.go +++ b/libgo/go/exp/types/stmt.go @@ -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) } diff --git a/libgo/go/exp/types/testdata/builtins.src b/libgo/go/exp/types/testdata/builtins.src index a07af89..a951853 100644 --- a/libgo/go/exp/types/testdata/builtins.src +++ b/libgo/go/exp/types/testdata/builtins.src @@ -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) } diff --git a/libgo/go/exp/types/testdata/decls1.src b/libgo/go/exp/types/testdata/decls1.src index be92709..3baed67 100644 --- a/libgo/go/exp/types/testdata/decls1.src +++ b/libgo/go/exp/types/testdata/decls1.src @@ -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 diff --git a/libgo/go/exp/types/testdata/expr0.src b/libgo/go/exp/types/testdata/expr0.src index 0ed314a..8b2eb04 100644 --- a/libgo/go/exp/types/testdata/expr0.src +++ b/libgo/go/exp/types/testdata/expr0.src @@ -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" */ - -) \ No newline at end of file +) diff --git a/libgo/go/exp/types/testdata/expr2.src b/libgo/go/exp/types/testdata/expr2.src index 4bc2769..674be40 100644 --- a/libgo/go/exp/types/testdata/expr2.src +++ b/libgo/go/exp/types/testdata/expr2.src @@ -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 diff --git a/libgo/go/exp/types/testdata/expr3.src b/libgo/go/exp/types/testdata/expr3.src index a5ea4d2..35905c4 100644 --- a/libgo/go/exp/types/testdata/expr3.src +++ b/libgo/go/exp/types/testdata/expr3.src @@ -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" */ ()) +} \ No newline at end of file diff --git a/libgo/go/exp/types/testdata/stmt0.src b/libgo/go/exp/types/testdata/stmt0.src index d3cc3ac..c0e0236 100644 --- a/libgo/go/exp/types/testdata/stmt0.src +++ b/libgo/go/exp/types/testdata/stmt0.src @@ -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 { diff --git a/libgo/go/exp/types/types.go b/libgo/go/exp/types/types.go index 83a0826..6e4a987 100644 --- a/libgo/go/exp/types/types.go +++ b/libgo/go/exp/types/types.go @@ -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 diff --git a/libgo/go/exp/types/types_test.go b/libgo/go/exp/types/types_test.go index 62ca19b..361f636 100644 --- a/libgo/go/exp/types/types_test.go +++ b/libgo/go/exp/types/types_test.go @@ -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.(...)"}, diff --git a/libgo/go/fmt/fmt_test.go b/libgo/go/fmt/fmt_test.go index 3406113..ab06465 100644 --- a/libgo/go/fmt/fmt_test.go +++ b/libgo/go/fmt/fmt_test.go @@ -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) { diff --git a/libgo/go/fmt/format.go b/libgo/go/fmt/format.go index ce80116..c3d7605 100644 --- a/libgo/go/fmt/format.go +++ b/libgo/go/fmt/format.go @@ -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) } diff --git a/libgo/go/fmt/scan.go b/libgo/go/fmt/scan.go index 62de3a2..6a282c8 100644 --- a/libgo/go/fmt/scan.go +++ b/libgo/go/fmt/scan.go @@ -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 } diff --git a/libgo/go/go/ast/ast.go b/libgo/go/go/ast/ast.go index e1582c3..bf533d1 100644 --- a/libgo/go/go/ast/ast.go +++ b/libgo/go/go/ast/ast.go @@ -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. diff --git a/libgo/go/go/build/build.go b/libgo/go/go/build/build.go index e65d845..a164425 100644 --- a/libgo/go/go/build/build.go +++ b/libgo/go/go/build/build.go @@ -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) + } + + // 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("import %q: cannot find package", path) + return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, strings.Join(paths, "\n")) } Found: diff --git a/libgo/go/go/doc/comment.go b/libgo/go/go/doc/comment.go index 51e2bf7..c4b7e6a 100644 --- a/libgo/go/go/doc/comment.go +++ b/libgo/go/go/doc/comment.go @@ -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. diff --git a/libgo/go/go/doc/example.go b/libgo/go/go/doc/example.go index e5752bb..9fc0b41 100644 --- a/libgo/go/go/doc/example.go +++ b/libgo/go/go/doc/example.go @@ -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 { diff --git a/libgo/go/go/format/format.go b/libgo/go/go/format/format.go index 286296e..65b0e4e 100644 --- a/libgo/go/go/format/format.go +++ b/libgo/go/go/format/format.go @@ -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 { diff --git a/libgo/go/go/printer/nodes.go b/libgo/go/go/printer/nodes.go index cd5b67b..3bed0cc 100644 --- a/libgo/go/go/printer/nodes.go +++ b/libgo/go/go/printer/nodes.go @@ -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 } diff --git a/libgo/go/go/token/position.go b/libgo/go/go/token/position.go index fc45c1e..f5d9995 100644 --- a/libgo/go/go/token/position.go +++ b/libgo/go/go/token/position.go @@ -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 } diff --git a/libgo/go/go/token/position_test.go b/libgo/go/go/token/position_test.go index 160107d..1d36c22 100644 --- a/libgo/go/go/token/position_test.go +++ b/libgo/go/go/token/position_test.go @@ -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() +} diff --git a/libgo/go/io/io.go b/libgo/go/io/io.go index bddb701..859adaf 100644 --- a/libgo/go/io/io.go +++ b/libgo/go/io/io.go @@ -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) } diff --git a/libgo/go/io/io_test.go b/libgo/go/io/io_test.go index 1e671b5..f3ec050 100644 --- a/libgo/go/io/io_test.go +++ b/libgo/go/io/io_test.go @@ -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) + } + } +} diff --git a/libgo/go/log/syslog/syslog_test.go b/libgo/go/log/syslog/syslog_test.go index 4c0bf1f..67d7103 100644 --- a/libgo/go/log/syslog/syslog_test.go +++ b/libgo/go/log/syslog/syslog_test.go @@ -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) } diff --git a/libgo/go/math/all_test.go b/libgo/go/math/all_test.go index cdea803..0d8b10f 100644 --- a/libgo/go/math/all_test.go +++ b/libgo/go/math/all_test.go @@ -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) { diff --git a/libgo/go/math/big/int.go b/libgo/go/math/big/int.go index 95c0d58..63a4536 100644 --- a/libgo/go/math/big/int.go +++ b/libgo/go/math/big/int.go @@ -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. diff --git a/libgo/go/math/big/int_test.go b/libgo/go/math/big/int_test.go index d3c5a0e..fd6d152 100644 --- a/libgo/go/math/big/int_test.go +++ b/libgo/go/math/big/int_test.go @@ -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 diff --git a/libgo/go/math/big/nat.go b/libgo/go/math/big/nat.go index 13a623a..9d09f97 100644 --- a/libgo/go/math/big/nat.go +++ b/libgo/go/math/big/nat.go @@ -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. diff --git a/libgo/go/math/log10.go b/libgo/go/math/log10.go index 3d2cec6..d880ec204 100644 --- a/libgo/go/math/log10.go +++ b/libgo/go/math/log10.go @@ -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) } diff --git a/libgo/go/net/cgo_openbsd.go b/libgo/go/net/cgo_openbsd.go new file mode 100644 index 0000000..aeaf8e5 --- /dev/null +++ b/libgo/go/net/cgo_openbsd.go @@ -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 +*/ +import "C" + +func cgoAddrInfoFlags() C.int { + return C.AI_CANONNAME +} diff --git a/libgo/go/net/cgo_unix.go b/libgo/go/net/cgo_unix.go index 69daedc..a4d96a8 100644 --- a/libgo/go/net/cgo_unix.go +++ b/libgo/go/net/cgo_unix.go @@ -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 diff --git a/libgo/go/net/conn_test.go b/libgo/go/net/conn_test.go index 037ce80..f733a81 100644 --- a/libgo/go/net/conn_test.go +++ b/libgo/go/net/conn_test.go @@ -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) - } } } diff --git a/libgo/go/net/dial.go b/libgo/go/net/dial.go index 0c46084..c1eb983 100644 --- a/libgo/go/net/dial.go +++ b/libgo/go/net/dial.go @@ -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) } diff --git a/libgo/go/net/dial_test.go b/libgo/go/net/dial_test.go index 865dd62..f30dee3 100644 --- a/libgo/go/net/dial_test.go +++ b/libgo/go/net/dial_test.go @@ -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() { diff --git a/libgo/go/net/http/client.go b/libgo/go/net/http/client.go index 2f957d2..5ee0804 100644 --- a/libgo/go/net/http/client.go +++ b/libgo/go/net/http/client.go @@ -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) } diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go index f4ba6a9..9514a4b 100644 --- a/libgo/go/net/http/client_test.go +++ b/libgo/go/net/http/client_test.go @@ -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) { diff --git a/libgo/go/net/http/pprof/pprof.go b/libgo/go/net/http/pprof/pprof.go index d70bf4e..0c03e5b 100644 --- a/libgo/go/net/http/pprof/pprof.go +++ b/libgo/go/net/http/pprof/pprof.go @@ -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 // diff --git a/libgo/go/net/http/readrequest_test.go b/libgo/go/net/http/readrequest_test.go index 2e03c65..ffdd6a8 100644 --- a/libgo/go/net/http/readrequest_test.go +++ b/libgo/go/net/http/readrequest_test.go @@ -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) { diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go index 8ca227f..1de4171 100644 --- a/libgo/go/net/http/serve_test.go +++ b/libgo/go/net/http/serve_test.go @@ -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) diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go index c4ddbec..89a46f0 100644 --- a/libgo/go/net/http/server.go +++ b/libgo/go/net/http/server.go @@ -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 diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go index 7b4afeb..d0505bf 100644 --- a/libgo/go/net/http/transport.go +++ b/libgo/go/net/http/transport.go @@ -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: diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go index f1d4158..c37ef13 100644 --- a/libgo/go/net/http/transport_test.go +++ b/libgo/go/net/http/transport_test.go @@ -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 diff --git a/libgo/go/net/packetconn_test.go b/libgo/go/net/packetconn_test.go index 5075baa..ff29e24 100644 --- a/libgo/go/net/packetconn_test.go +++ b/libgo/go/net/packetconn_test.go @@ -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) - } } } diff --git a/libgo/go/net/protoconn_test.go b/libgo/go/net/protoconn_test.go index f249372..d99de3f 100644 --- a/libgo/go/net/protoconn_test.go +++ b/libgo/go/net/protoconn_test.go @@ -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) diff --git a/libgo/go/net/smtp/smtp.go b/libgo/go/net/smtp/smtp.go index 59f6449..4b91778 100644 --- a/libgo/go/net/smtp/smtp.go +++ b/libgo/go/net/smtp/smtp.go @@ -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.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") } - return c, err + 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 diff --git a/libgo/go/net/smtp/smtp_test.go b/libgo/go/net/smtp/smtp_test.go index c315d18..8317428 100644 --- a/libgo/go/net/smtp/smtp_test.go +++ b/libgo/go/net/smtp/smtp_test.go @@ -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:\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: +RCPT TO: +DATA +From: test@example.com +To: other@example.com +Subject: SendMail test + +SendMail is working for me. +. +QUIT +` diff --git a/libgo/go/net/unixsock_plan9.go b/libgo/go/net/unixsock_plan9.go index f7be5d2..713820c 100644 --- a/libgo/go/net/unixsock_plan9.go +++ b/libgo/go/net/unixsock_plan9.go @@ -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 } diff --git a/libgo/go/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go index 16ebd58..653190c 100644 --- a/libgo/go/net/unixsock_posix.go +++ b/libgo/go/net/unixsock_posix.go @@ -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 } diff --git a/libgo/go/net/url/url.go b/libgo/go/net/url/url.go index 692a7fd..71758fe 100644 --- a/libgo/go/net/url/url.go +++ b/libgo/go/net/url/url.go @@ -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, "/") diff --git a/libgo/go/net/url/url_test.go b/libgo/go/net/url/url_test.go index 64f1170..4d3545d 100644 --- a/libgo/go/net/url/url_test.go +++ b/libgo/go/net/url/url_test.go @@ -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"}, diff --git a/libgo/go/os/env.go b/libgo/go/os/env.go index eb265f2..db7fc72 100644 --- a/libgo/go/os/env.go +++ b/libgo/go/os/env.go @@ -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. diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go index 1ba3293..b979fed 100644 --- a/libgo/go/os/file_posix.go +++ b/libgo/go/os/file_posix.go @@ -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 diff --git a/libgo/go/os/user/lookup_unix.go b/libgo/go/os/user/lookup_unix.go index 7e67495..e1e2777 100644 --- a/libgo/go/os/user/lookup_unix.go +++ b/libgo/go/os/user/lookup_unix.go @@ -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 diff --git a/libgo/go/regexp/all_test.go b/libgo/go/regexp/all_test.go index b7e4f04..a6a659c 100644 --- a/libgo/go/regexp/all_test.go +++ b/libgo/go/regexp/all_test.go @@ -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}, -} -*/ - -func compileTest(t *testing.T, expr string, error error) *Regexp { + {`*`, "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 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 } diff --git a/libgo/go/regexp/syntax/parse.go b/libgo/go/regexp/syntax/parse.go index 0bf5799..335f739 100644 --- a/libgo/go/regexp/syntax/parse.go +++ b/libgo/go/regexp/syntax/parse.go @@ -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 diff --git a/libgo/go/strconv/ftoa.go b/libgo/go/strconv/ftoa.go index 8067881..1a9c41b 100644 --- a/libgo/go/strconv/ftoa.go +++ b/libgo/go/strconv/ftoa.go @@ -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<> 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 { diff --git a/libgo/go/time/time_test.go b/libgo/go/time/time_test.go index 1fd575b..0224fed 100644 --- a/libgo/go/time/time_test.go +++ b/libgo/go/time/time_test.go @@ -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) { diff --git a/libgo/go/time/zoneinfo_read.go b/libgo/go/time/zoneinfo_read.go index 0eb20c7..a5a2de2 100644 --- a/libgo/go/time/zoneinfo_read.go +++ b/libgo/go/time/zoneinfo_read.go @@ -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, diff --git a/libgo/merge.sh b/libgo/merge.sh index 79ece62..ca358ac 100755 --- a/libgo/merge.sh +++ b/libgo/merge.sh @@ -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 diff --git a/libgo/runtime/env_posix.c b/libgo/runtime/env_posix.c new file mode 100644 index 0000000..31f4179 --- /dev/null +++ b/libgo/runtime/env_posix.c @@ -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; isettype_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) { diff --git a/libgo/runtime/malloc.h b/libgo/runtime/malloc.h index b3baaec..710484e 100644 --- a/libgo/runtime/malloc.h +++ b/libgo/runtime/malloc.h @@ -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)); diff --git a/libgo/runtime/mfinal.c b/libgo/runtime/mfinal.c index f2f640a..7c906da 100644 --- a/libgo/runtime/mfinal.c +++ b/libgo/runtime/mfinal.c @@ -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]); } } diff --git a/libgo/runtime/mgc0.c b/libgo/runtime/mgc0.c index 45f8a56..698400b 100644 --- a/libgo/runtime/mgc0.c +++ b/libgo/runtime/mgc0.c @@ -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. +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 +{ + 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 -scanblock(byte *b, uintptr n) +flushptrbuf(struct PtrTarget *ptrbuf, uintptr n, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj, struct BitTarget *bitbuf) { - byte *obj, *arena_start, *arena_used, *p; - void **vp; - uintptr size, *bitp, bits, shift, i, j, x, xbits, off, nobj, nproc; + 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; - - if((intptr)n < 0) { - runtime_printf("scanblock %p %D\n", b, (int64)n); - runtime_throw("scanblock"); - } + struct PtrTarget *ptrbuf_end; + struct BitTarget *bitbufpos, *bt; - // 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<start<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<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. - // Fetch b from the work buffer. if(nobj == 0) { - if(!keepworking) { - if(wbuf) - putempty(wbuf); - return; + 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; } - // Emptied our buffer: refill. - wbuf = getfull(wbuf); - if(wbuf == nil) - return; - nobj = wbuf->nobj; - wp = (void**)(wbuf->obj + wbuf->nobj); } - b = *--wp; - 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<sizeclass]; + // Fetch b from the work buffer. + --wp; + b = wp->p; + n = wp->n; + nobj--; } + +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<elemsize; if(s->sizeclass == 0) { obj = p; - size = (uintptr)s->npages<= (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; igcstats, sizeof(m->gcstats)); + runtime_memclr((byte*)&mp->gcstats, sizeof(mp->gcstats)); } for(i=0; ilocal_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; ialllink) + 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. diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c index d90bb2c..9b229d6 100644 --- a/libgo/runtime/proc.c +++ b/libgo/runtime/proc.c @@ -555,13 +555,13 @@ schedlock(void) static void schedunlock(void) { - M *m; + M *mp; - m = mwakeup; + mp = mwakeup; mwakeup = nil; runtime_unlock(&runtime_sched); - if(m != nil) - runtime_notewakeup(&m->havenextg); + if(mp != nil) + runtime_notewakeup(&mp->havenextg); } void diff --git a/libgo/runtime/runtime.c b/libgo/runtime/runtime.c index 211edc0..ecd804d 100644 --- a/libgo/runtime/runtime.c +++ b/libgo/runtime/runtime.c @@ -79,33 +79,6 @@ runtime_goenvs_unix(void) syscall_Envs.__capacity = n; } -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