libgo: Update to revision 15193:6fdc1974457c of master library.
authorIan Lance Taylor <ian@gcc.gnu.org>
Sat, 22 Dec 2012 01:15:33 +0000 (01:15 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Sat, 22 Dec 2012 01:15:33 +0000 (01:15 +0000)
From-SVN: r194692

104 files changed:
libgo/MERGE
libgo/Makefile.am
libgo/Makefile.in
libgo/go/builtin/builtin.go
libgo/go/compress/flate/deflate.go
libgo/go/crypto/cipher/example_test.go
libgo/go/crypto/x509/x509_test.go
libgo/go/database/sql/fakedb_test.go
libgo/go/database/sql/sql.go
libgo/go/database/sql/sql_test.go
libgo/go/debug/elf/file.go
libgo/go/debug/elf/file_test.go
libgo/go/debug/elf/testdata/hello-world-core.gz [new file with mode: 0644]
libgo/go/encoding/csv/writer.go
libgo/go/encoding/csv/writer_test.go
libgo/go/encoding/gob/decode.go
libgo/go/encoding/json/decode.go
libgo/go/exp/gotype/gotype_test.go
libgo/go/exp/locale/collate/build/trie.go
libgo/go/exp/norm/iter.go
libgo/go/exp/norm/normalize_test.go
libgo/go/exp/norm/triegen.go
libgo/go/exp/types/builtins.go
libgo/go/exp/types/const.go
libgo/go/exp/types/errors.go
libgo/go/exp/types/expr.go
libgo/go/exp/types/operand.go
libgo/go/exp/types/predicates.go
libgo/go/exp/types/stmt.go
libgo/go/exp/types/testdata/builtins.src
libgo/go/exp/types/testdata/decls1.src
libgo/go/exp/types/testdata/expr0.src
libgo/go/exp/types/testdata/expr2.src
libgo/go/exp/types/testdata/expr3.src
libgo/go/exp/types/testdata/stmt0.src
libgo/go/exp/types/types.go
libgo/go/exp/types/types_test.go
libgo/go/fmt/fmt_test.go
libgo/go/fmt/format.go
libgo/go/fmt/scan.go
libgo/go/go/ast/ast.go
libgo/go/go/build/build.go
libgo/go/go/doc/comment.go
libgo/go/go/doc/example.go
libgo/go/go/format/format.go
libgo/go/go/printer/nodes.go
libgo/go/go/token/position.go
libgo/go/go/token/position_test.go
libgo/go/io/io.go
libgo/go/io/io_test.go
libgo/go/log/syslog/syslog_test.go
libgo/go/math/all_test.go
libgo/go/math/big/int.go
libgo/go/math/big/int_test.go
libgo/go/math/big/nat.go
libgo/go/math/log10.go
libgo/go/net/cgo_openbsd.go [new file with mode: 0644]
libgo/go/net/cgo_unix.go
libgo/go/net/conn_test.go
libgo/go/net/dial.go
libgo/go/net/dial_test.go
libgo/go/net/http/client.go
libgo/go/net/http/client_test.go
libgo/go/net/http/pprof/pprof.go
libgo/go/net/http/readrequest_test.go
libgo/go/net/http/serve_test.go
libgo/go/net/http/server.go
libgo/go/net/http/transport.go
libgo/go/net/http/transport_test.go
libgo/go/net/packetconn_test.go
libgo/go/net/protoconn_test.go
libgo/go/net/smtp/smtp.go
libgo/go/net/smtp/smtp_test.go
libgo/go/net/unixsock_plan9.go
libgo/go/net/unixsock_posix.go
libgo/go/net/url/url.go
libgo/go/net/url/url_test.go
libgo/go/os/env.go
libgo/go/os/file_posix.go
libgo/go/os/user/lookup_unix.go
libgo/go/regexp/all_test.go
libgo/go/regexp/syntax/parse.go
libgo/go/strconv/ftoa.go
libgo/go/syscall/env_plan9.go
libgo/go/syscall/libcall_linux_utimesnano.go [new file with mode: 0644]
libgo/go/syscall/libcall_posix_utimesnano.go [new file with mode: 0644]
libgo/go/syscall/syscall.go
libgo/go/testing/example.go
libgo/go/time/format.go
libgo/go/time/time_test.go
libgo/go/time/zoneinfo_read.go
libgo/merge.sh
libgo/runtime/env_posix.c [new file with mode: 0644]
libgo/runtime/go-trampoline.c
libgo/runtime/malloc.goc
libgo/runtime/malloc.h
libgo/runtime/mfinal.c
libgo/runtime/mgc0.c
libgo/runtime/mgc0.h [new file with mode: 0644]
libgo/runtime/mprof.goc
libgo/runtime/proc.c
libgo/runtime/runtime.c
libgo/runtime/runtime.h
libgo/runtime/time.goc

index deeb596..52537f9 100644 (file)
@@ -1,4 +1,4 @@
-c031aa767edf
+6fdc1974457c
 
 The first line of this file holds the Mercurial revision number of the
 last merge done from the master library sources.
index 3da2cb4..b97a82e 100644 (file)
@@ -502,6 +502,7 @@ runtime_files = \
        runtime/go-unwind.c \
        runtime/chan.c \
        runtime/cpuprof.c \
+       runtime/env_posix.c \
        runtime/lfstack.c \
        $(runtime_lock_files) \
        runtime/mcache.c \
@@ -1657,6 +1658,13 @@ else
 syscall_lsf_file =
 endif
 
+# GNU/Linux specific utimesnano support.
+if LIBGO_IS_LINUX
+syscall_utimesnano_file = go/syscall/libcall_linux_utimesnano.go
+else
+syscall_utimesnano_file = go/syscall/libcall_posix_utimesnano.go
+endif
+
 go_base_syscall_files = \
        go/syscall/env_unix.go \
        go/syscall/syscall_errno.go \
@@ -1679,6 +1687,7 @@ go_base_syscall_files = \
        $(syscall_uname_file) \
        $(syscall_netlink_file) \
        $(syscall_lsf_file) \
+       $(syscall_utimesnano_file) \
        $(GO_LIBCALL_OS_FILE) \
        $(GO_LIBCALL_OS_ARCH_FILE) \
        $(GO_SYSCALL_OS_FILE) \
index 869addf..fe18b74 100644 (file)
@@ -213,12 +213,13 @@ am__objects_5 = go-append.lo go-assert.lo go-assert-interface.lo \
        go-type-float.lo go-type-identity.lo go-type-interface.lo \
        go-type-string.lo go-typedesc-equal.lo go-typestring.lo \
        go-unsafe-new.lo go-unsafe-newarray.lo go-unsafe-pointer.lo \
-       go-unwind.lo chan.lo cpuprof.lo lfstack.lo $(am__objects_1) \
-       mcache.lo mcentral.lo $(am__objects_2) mfinal.lo mfixalloc.lo \
-       mgc0.lo mheap.lo msize.lo panic.lo parfor.lo print.lo proc.lo \
-       runtime.lo signal_unix.lo thread.lo yield.lo $(am__objects_3) \
-       iface.lo malloc.lo map.lo mprof.lo reflect.lo runtime1.lo \
-       sema.lo sigqueue.lo string.lo time.lo $(am__objects_4)
+       go-unwind.lo chan.lo cpuprof.lo env_posix.lo lfstack.lo \
+       $(am__objects_1) mcache.lo mcentral.lo $(am__objects_2) \
+       mfinal.lo mfixalloc.lo mgc0.lo mheap.lo msize.lo panic.lo \
+       parfor.lo print.lo proc.lo runtime.lo signal_unix.lo thread.lo \
+       yield.lo $(am__objects_3) iface.lo malloc.lo map.lo mprof.lo \
+       reflect.lo runtime1.lo sema.lo sigqueue.lo string.lo time.lo \
+       $(am__objects_4)
 am_libgo_la_OBJECTS = $(am__objects_5)
 libgo_la_OBJECTS = $(am_libgo_la_OBJECTS)
 libgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
@@ -835,6 +836,7 @@ runtime_files = \
        runtime/go-unwind.c \
        runtime/chan.c \
        runtime/cpuprof.c \
+       runtime/env_posix.c \
        runtime/lfstack.c \
        $(runtime_lock_files) \
        runtime/mcache.c \
@@ -1840,6 +1842,10 @@ go_unicode_utf8_files = \
 
 # GNU/Linux specific socket filters.
 @LIBGO_IS_LINUX_TRUE@syscall_lsf_file = go/syscall/lsf_linux.go
+@LIBGO_IS_LINUX_FALSE@syscall_utimesnano_file = go/syscall/libcall_posix_utimesnano.go
+
+# GNU/Linux specific utimesnano support.
+@LIBGO_IS_LINUX_TRUE@syscall_utimesnano_file = go/syscall/libcall_linux_utimesnano.go
 go_base_syscall_files = \
        go/syscall/env_unix.go \
        go/syscall/syscall_errno.go \
@@ -1862,6 +1868,7 @@ go_base_syscall_files = \
        $(syscall_uname_file) \
        $(syscall_netlink_file) \
        $(syscall_lsf_file) \
+       $(syscall_utimesnano_file) \
        $(GO_LIBCALL_OS_FILE) \
        $(GO_LIBCALL_OS_ARCH_FILE) \
        $(GO_SYSCALL_OS_FILE) \
@@ -2418,6 +2425,7 @@ distclean-compile:
 
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chan.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpuprof.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/env_posix.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getncpu-bsd.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getncpu-irix.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getncpu-linux.Plo@am__quote@
@@ -3027,6 +3035,13 @@ cpuprof.lo: runtime/cpuprof.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cpuprof.lo `test -f 'runtime/cpuprof.c' || echo '$(srcdir)/'`runtime/cpuprof.c
 
+env_posix.lo: runtime/env_posix.c
+@am__fastdepCC_TRUE@   $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT env_posix.lo -MD -MP -MF $(DEPDIR)/env_posix.Tpo -c -o env_posix.lo `test -f 'runtime/env_posix.c' || echo '$(srcdir)/'`runtime/env_posix.c
+@am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/env_posix.Tpo $(DEPDIR)/env_posix.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='runtime/env_posix.c' object='env_posix.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o env_posix.lo `test -f 'runtime/env_posix.c' || echo '$(srcdir)/'`runtime/env_posix.c
+
 lfstack.lo: runtime/lfstack.c
 @am__fastdepCC_TRUE@   $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lfstack.lo -MD -MP -MF $(DEPDIR)/lfstack.Tpo -c -o lfstack.lo `test -f 'runtime/lfstack.c' || echo '$(srcdir)/'`runtime/lfstack.c
 @am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/lfstack.Tpo $(DEPDIR)/lfstack.Plo
index a30943b..91d263a 100644 (file)
@@ -124,8 +124,8 @@ func append(slice []Type, elems ...Type) []Type
 func copy(dst, src []Type) int
 
 // The delete built-in function deletes the element with the specified key
-// (m[key]) from the map. If there is no such element, delete is a no-op.
-// If m is nil, delete panics.
+// (m[key]) from the map. If m is nil or there is no such element, delete
+// is a no-op.
 func delete(m map[Type]Type1, key Type)
 
 // The len built-in function returns the length of v, according to its type:
index e511b50..d357fe3 100644 (file)
@@ -22,7 +22,7 @@ const (
        logMaxOffsetSize   = 15  // Standard DEFLATE
        minMatchLength     = 3   // The smallest match that the compressor looks for
        maxMatchLength     = 258 // The longest match for the compressor
-       minOffsetSize      = 1   // The shortest offset that makes any sence
+       minOffsetSize      = 1   // The shortest offset that makes any sense
 
        // The maximum number of tokens we put into a single flat block, just too
        // stop things from getting too large.
index c888eb2..e0027ca 100644 (file)
@@ -241,7 +241,7 @@ func ExampleStreamReader() {
 
        // Note that this example is simplistic in that it omits any
        // authentication of the encrypted data. It you were actually to use
-       // StreamReader in this manner, an attacker could flip arbitary bits in
+       // StreamReader in this manner, an attacker could flip arbitrary bits in
        // the output.
 }
 
@@ -278,6 +278,6 @@ func ExampleStreamWriter() {
 
        // Note that this example is simplistic in that it omits any
        // authentication of the encrypted data. It you were actually to use
-       // StreamReader in this manner, an attacker could flip arbitary bits in
+       // StreamReader in this manner, an attacker could flip arbitrary bits in
        // the decrypted result.
 }
index a13f459..b2d6fe3 100644 (file)
@@ -439,7 +439,7 @@ func TestECDSA(t *testing.T) {
                        t.Errorf("%d: public key algorithm is %v, want ECDSA", i, pka)
                }
                if err = cert.CheckSignatureFrom(cert); err != nil {
-                       t.Errorf("%d: certificate verfication failed: %s", i, err)
+                       t.Errorf("%d: certificate verification failed: %s", i, err)
                }
        }
 }
@@ -519,7 +519,7 @@ func TestVerifyCertificateWithDSASignature(t *testing.T) {
        }
        // test cert is self-signed
        if err = cert.CheckSignatureFrom(cert); err != nil {
-               t.Fatalf("DSA Certificate verfication failed: %s", err)
+               t.Fatalf("DSA Certificate verification failed: %s", err)
        }
 }
 
index aec5727..c38ba7c 100644 (file)
@@ -42,9 +42,10 @@ type fakeDriver struct {
 type fakeDB struct {
        name string
 
-       mu     sync.Mutex
-       free   []*fakeConn
-       tables map[string]*table
+       mu      sync.Mutex
+       free    []*fakeConn
+       tables  map[string]*table
+       badConn bool
 }
 
 type table struct {
@@ -83,6 +84,7 @@ type fakeConn struct {
        stmtsMade   int
        stmtsClosed int
        numPrepare  int
+       bad         bool
 }
 
 func (c *fakeConn) incrStat(v *int) {
@@ -122,7 +124,9 @@ func init() {
 
 // Supports dsn forms:
 //    <dbname>
-//    <dbname>;<opts>  (no currently supported options)
+//    <dbname>;<opts>  (only currently supported option is `badConn`,
+//                      which causes driver.ErrBadConn to be returned on
+//                      every other conn.Begin())
 func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
        parts := strings.Split(dsn, ";")
        if len(parts) < 1 {
@@ -135,7 +139,12 @@ func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
        d.mu.Lock()
        d.openCount++
        d.mu.Unlock()
-       return &fakeConn{db: db}, nil
+       conn := &fakeConn{db: db}
+
+       if len(parts) >= 2 && parts[1] == "badConn" {
+               conn.bad = true
+       }
+       return conn, nil
 }
 
 func (d *fakeDriver) getDB(name string) *fakeDB {
@@ -199,7 +208,20 @@ func (db *fakeDB) columnType(table, column string) (typ string, ok bool) {
        return "", false
 }
 
+func (c *fakeConn) isBad() bool {
+       // if not simulating bad conn, do nothing
+       if !c.bad {
+               return false
+       }
+       // alternate between bad conn and not bad conn
+       c.db.badConn = !c.db.badConn
+       return c.db.badConn
+}
+
 func (c *fakeConn) Begin() (driver.Tx, error) {
+       if c.isBad() {
+               return nil, driver.ErrBadConn
+       }
        if c.currTx != nil {
                return nil, errors.New("already in a transaction")
        }
index b0cba94..e7c7780 100644 (file)
@@ -266,7 +266,7 @@ func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) {
 var putConnHook func(*DB, driver.Conn)
 
 // putConn adds a connection to the db's free pool.
-// err is optionally the last error that occured on this connection.
+// err is optionally the last error that occurred on this connection.
 func (db *DB) putConn(c driver.Conn, err error) {
        if err == driver.ErrBadConn {
                // Don't reuse bad connections.
@@ -426,7 +426,7 @@ func (db *DB) begin() (tx *Tx, err error) {
        txi, err := ci.Begin()
        if err != nil {
                db.putConn(ci, err)
-               return nil, fmt.Errorf("sql: failed to Begin transaction: %v", err)
+               return nil, err
        }
        return &Tx{
                db:  db,
index 1bfb590..b702b85 100644 (file)
@@ -402,6 +402,39 @@ func TestTxQueryInvalid(t *testing.T) {
        }
 }
 
+// Tests fix for issue 4433, that retries in Begin happen when
+// conn.Begin() returns ErrBadConn
+func TestTxErrBadConn(t *testing.T) {
+       db, err := Open("test", fakeDBName+";badConn")
+       if err != nil {
+               t.Fatalf("Open: %v", err)
+       }
+       if _, err := db.Exec("WIPE"); err != nil {
+               t.Fatalf("exec wipe: %v", err)
+       }
+       defer closeDB(t, db)
+       exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool")
+       stmt, err := db.Prepare("INSERT|t1|name=?,age=?")
+       if err != nil {
+               t.Fatalf("Stmt, err = %v, %v", stmt, err)
+       }
+       defer stmt.Close()
+       tx, err := db.Begin()
+       if err != nil {
+               t.Fatalf("Begin = %v", err)
+       }
+       txs := tx.Stmt(stmt)
+       defer txs.Close()
+       _, err = txs.Exec("Bobby", 7)
+       if err != nil {
+               t.Fatalf("Exec = %v", err)
+       }
+       err = tx.Commit()
+       if err != nil {
+               t.Fatalf("Commit = %v", err)
+       }
+}
+
 // Tests fix for issue 2542, that we release a lock when querying on
 // a closed connection.
 func TestIssue2542Deadlock(t *testing.T) {
index f462375..eb3d72f 100644 (file)
@@ -272,7 +272,8 @@ func NewFile(r io.ReaderAt) (*File, error) {
                shnum = int(hdr.Shnum)
                shstrndx = int(hdr.Shstrndx)
        }
-       if shstrndx < 0 || shstrndx >= shnum {
+
+       if shnum > 0 && shoff > 0 && (shstrndx < 0 || shstrndx >= shnum) {
                return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx}
        }
 
@@ -367,6 +368,10 @@ func NewFile(r io.ReaderAt) (*File, error) {
                f.Sections[i] = s
        }
 
+       if len(f.Sections) == 0 {
+               return f, nil
+       }
+
        // Load section header string table.
        shstrtab, err := f.Sections[shstrndx].Data()
        if err != nil {
index 810a2f9..f9aa726 100644 (file)
@@ -5,10 +5,14 @@
 package elf
 
 import (
+       "bytes"
+       "compress/gzip"
        "debug/dwarf"
        "encoding/binary"
+       "io"
        "net"
        "os"
+       "path"
        "reflect"
        "runtime"
        "testing"
@@ -121,15 +125,49 @@ var fileTests = []fileTest{
                },
                []string{"libc.so.6"},
        },
+       {
+               "testdata/hello-world-core.gz",
+               FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0x0, binary.LittleEndian, ET_CORE, EM_X86_64, 0x0},
+               []SectionHeader{},
+               []ProgHeader{
+                       {Type: PT_NOTE, Flags: 0x0, Off: 0x3f8, Vaddr: 0x0, Paddr: 0x0, Filesz: 0x8ac, Memsz: 0x0, Align: 0x0},
+                       {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x1000, Vaddr: 0x400000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: PF_R, Off: 0x1000, Vaddr: 0x401000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x2000, Vaddr: 0x402000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3000, Vaddr: 0x7f54078b8000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1b5000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: 0x0, Off: 0x3000, Vaddr: 0x7f5407a6d000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1ff000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: PF_R, Off: 0x3000, Vaddr: 0x7f5407c6c000, Paddr: 0x0, Filesz: 0x4000, Memsz: 0x4000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x7000, Vaddr: 0x7f5407c70000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x9000, Vaddr: 0x7f5407c72000, Paddr: 0x0, Filesz: 0x5000, Memsz: 0x5000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0xe000, Vaddr: 0x7f5407c77000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x22000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0xe000, Vaddr: 0x7f5407e81000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x11000, Vaddr: 0x7f5407e96000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: PF_R, Off: 0x14000, Vaddr: 0x7f5407e99000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x15000, Vaddr: 0x7f5407e9a000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x17000, Vaddr: 0x7fff79972000, Paddr: 0x0, Filesz: 0x23000, Memsz: 0x23000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3a000, Vaddr: 0x7fff799f8000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
+                       {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3b000, Vaddr: 0xffffffffff600000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
+               },
+               nil,
+       },
 }
 
 func TestOpen(t *testing.T) {
        for i := range fileTests {
                tt := &fileTests[i]
 
-               f, err := Open(tt.file)
+               var f *File
+               var err error
+               if path.Ext(tt.file) == ".gz" {
+                       var r io.ReaderAt
+                       if r, err = decompress(tt.file); err == nil {
+                               f, err = NewFile(r)
+                       }
+               } else {
+                       f, err = Open(tt.file)
+               }
                if err != nil {
-                       t.Error(err)
+                       t.Errorf("cannot open file %s: %v", tt.file, err)
                        continue
                }
                if !reflect.DeepEqual(f.FileHeader, tt.hdr) {
@@ -175,6 +213,23 @@ func TestOpen(t *testing.T) {
        }
 }
 
+// elf.NewFile requires io.ReaderAt, which compress/gzip cannot
+// provide. Decompress the file to a bytes.Reader.
+func decompress(gz string) (io.ReaderAt, error) {
+       in, err := os.Open(gz)
+       if err != nil {
+               return nil, err
+       }
+       defer in.Close()
+       r, err := gzip.NewReader(in)
+       if err != nil {
+               return nil, err
+       }
+       var out bytes.Buffer
+       _, err = io.Copy(&out, r)
+       return bytes.NewReader(out.Bytes()), err
+}
+
 type relocationTestEntry struct {
        entryNumber int
        entry       *dwarf.Entry
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 (file)
index 0000000..806af6e
Binary files /dev/null and b/libgo/go/debug/elf/testdata/hello-world-core.gz differ
index 17e4850..1faecb6 100644 (file)
@@ -92,10 +92,17 @@ func (w *Writer) Write(record []string) (err error) {
 }
 
 // Flush writes any buffered data to the underlying io.Writer.
+// To check if an error occurred during the Flush, call Error.
 func (w *Writer) Flush() {
        w.w.Flush()
 }
 
+// Error reports any error that has occurred during a previous Write or Flush.
+func (w *Writer) Error() error {
+       _, err := w.w.Write(nil)
+       return err
+}
+
 // WriteAll writes multiple CSV records to w using Write and then calls Flush.
 func (w *Writer) WriteAll(records [][]string) (err error) {
        for _, record := range records {
index 5789590..03ca6b0 100644 (file)
@@ -6,6 +6,7 @@ package csv
 
 import (
        "bytes"
+       "errors"
        "testing"
 )
 
@@ -42,3 +43,30 @@ func TestWrite(t *testing.T) {
                }
        }
 }
+
+type errorWriter struct{}
+
+func (e errorWriter) Write(b []byte) (int, error) {
+       return 0, errors.New("Test")
+}
+
+func TestError(t *testing.T) {
+       b := &bytes.Buffer{}
+       f := NewWriter(b)
+       f.Write([]string{"abc"})
+       f.Flush()
+       err := f.Error()
+
+       if err != nil {
+               t.Errorf("Unexpected error: %s\n", err)
+       }
+
+       f = NewWriter(errorWriter{})
+       f.Write([]string{"abc"})
+       f.Flush()
+       err = f.Error()
+
+       if err == nil {
+               t.Error("Error should not be nil")
+       }
+}
index 900c69d..a80d9f9 100644 (file)
@@ -62,15 +62,15 @@ func overflow(name string) error {
 // Used only by the Decoder to read the message length.
 func decodeUintReader(r io.Reader, buf []byte) (x uint64, width int, err error) {
        width = 1
-       _, err = r.Read(buf[0:width])
-       if err != nil {
+       n, err := io.ReadFull(r, buf[0:width])
+       if n == 0 {
                return
        }
        b := buf[0]
        if b <= 0x7f {
                return uint64(b), width, nil
        }
-       n := -int(int8(b))
+       n = -int(int8(b))
        if n > uint64Size {
                err = errBadUint
                return
index 1e0c8d4..b46dac9 100644 (file)
@@ -125,13 +125,12 @@ func (d *decodeState) unmarshal(v interface{}) (err error) {
        }()
 
        rv := reflect.ValueOf(v)
-       pv := rv
-       if pv.Kind() != reflect.Ptr || pv.IsNil() {
+       if rv.Kind() != reflect.Ptr || rv.IsNil() {
                return &InvalidUnmarshalError{reflect.TypeOf(v)}
        }
 
        d.scan.reset()
-       // We decode rv not pv.Elem because the Unmarshaler interface
+       // We decode rv not rv.Elem because the Unmarshaler interface
        // test must be applied at the top level of the value.
        d.value(rv)
        return d.savedError
@@ -423,17 +422,12 @@ func (d *decodeState) object(v reflect.Value) {
        v = pv
 
        // Decoding into nil interface?  Switch to non-reflect code.
-       iv := v
-       if iv.Kind() == reflect.Interface {
-               iv.Set(reflect.ValueOf(d.objectInterface()))
+       if v.Kind() == reflect.Interface {
+               v.Set(reflect.ValueOf(d.objectInterface()))
                return
        }
 
        // Check type of target: struct or map[string]T
-       var (
-               mv reflect.Value
-               sv reflect.Value
-       )
        switch v.Kind() {
        case reflect.Map:
                // map must have string type
@@ -442,17 +436,15 @@ func (d *decodeState) object(v reflect.Value) {
                        d.saveError(&UnmarshalTypeError{"object", v.Type()})
                        break
                }
-               mv = v
-               if mv.IsNil() {
-                       mv.Set(reflect.MakeMap(t))
+               if v.IsNil() {
+                       v.Set(reflect.MakeMap(t))
                }
        case reflect.Struct:
-               sv = v
        default:
                d.saveError(&UnmarshalTypeError{"object", v.Type()})
        }
 
-       if !mv.IsValid() && !sv.IsValid() {
+       if !v.IsValid() {
                d.off--
                d.next() // skip over { } in input
                return
@@ -484,8 +476,8 @@ func (d *decodeState) object(v reflect.Value) {
                var subv reflect.Value
                destring := false // whether the value is wrapped in a string to be decoded first
 
-               if mv.IsValid() {
-                       elemType := mv.Type().Elem()
+               if v.Kind() == reflect.Map {
+                       elemType := v.Type().Elem()
                        if !mapElem.IsValid() {
                                mapElem = reflect.New(elemType).Elem()
                        } else {
@@ -494,7 +486,7 @@ func (d *decodeState) object(v reflect.Value) {
                        subv = mapElem
                } else {
                        var f *field
-                       fields := cachedTypeFields(sv.Type())
+                       fields := cachedTypeFields(v.Type())
                        for i := range fields {
                                ff := &fields[i]
                                if ff.name == key {
@@ -506,7 +498,7 @@ func (d *decodeState) object(v reflect.Value) {
                                }
                        }
                        if f != nil {
-                               subv = sv
+                               subv = v
                                destring = f.quoted
                                for _, i := range f.index {
                                        if subv.Kind() == reflect.Ptr {
@@ -519,7 +511,7 @@ func (d *decodeState) object(v reflect.Value) {
                                }
                        } else {
                                // To give a good error, a quick scan for unexported fields in top level.
-                               st := sv.Type()
+                               st := v.Type()
                                for i := 0; i < st.NumField(); i++ {
                                        f := st.Field(i)
                                        if f.PkgPath != "" && strings.EqualFold(f.Name, key) {
@@ -546,8 +538,8 @@ func (d *decodeState) object(v reflect.Value) {
                }
                // Write value back to map;
                // if using struct, subv points into struct already.
-               if mv.IsValid() {
-                       mv.SetMapIndex(reflect.ValueOf(key), subv)
+               if v.Kind() == reflect.Map {
+                       v.SetMapIndex(reflect.ValueOf(key), subv)
                }
 
                // Next token must be , or }.
index 2d58f32..c0c2e32 100644 (file)
@@ -25,7 +25,9 @@ func runTest(t *testing.T, path string) {
        } else {
                // package directory
                // TODO(gri) gotype should use the build package instead
-               pkg, err := build.Import(path, "", 0)
+               ctxt := build.Default
+               ctxt.CgoEnabled = false
+               pkg, err := ctxt.Import(path, "", 0)
                if err != nil {
                        t.Errorf("build.Import error for path = %s: %s", path, err)
                        return
@@ -50,7 +52,7 @@ var tests = []string{
 
        // directories
        // Note: packages that don't typecheck yet are commented out
-       // "archive/tar", // investigate
+       "archive/tar",
        "archive/zip",
 
        "bufio",
@@ -77,13 +79,13 @@ var tests = []string{
        "crypto/md5",
        "crypto/rand",
        "crypto/rc4",
-       // "crypto/rsa", // investigate (GOARCH=386)
+       // "crypto/rsa", // intermittent failure: /home/gri/go2/src/pkg/crypto/rsa/pkcs1v15.go:21:27: undeclared name: io
        "crypto/sha1",
        "crypto/sha256",
        "crypto/sha512",
        "crypto/subtle",
        "crypto/tls",
-       // "crypto/x509", // investigate
+       "crypto/x509",
        "crypto/x509/pkix",
 
        "database/sql",
@@ -99,9 +101,9 @@ var tests = []string{
        "encoding/asn1",
        "encoding/base32",
        "encoding/base64",
-       // "encoding/binary", // complex() doesn't work yet
+       "encoding/binary",
        "encoding/csv",
-       // "encoding/gob", // complex() doesn't work yet
+       "encoding/gob",
        "encoding/hex",
        "encoding/json",
        "encoding/pem",
@@ -117,7 +119,7 @@ var tests = []string{
 
        "go/ast",
        "go/build",
-       // "go/doc", // variadic parameters don't work yet fully
+       "go/doc",
        "go/format",
        "go/parser",
        "go/printer",
@@ -125,7 +127,7 @@ var tests = []string{
        "go/token",
 
        "hash/adler32",
-       // "hash/crc32", // investigate
+       "hash/crc32",
        "hash/crc64",
        "hash/fnv",
 
@@ -139,54 +141,54 @@ var tests = []string{
        "index/suffixarray",
 
        "io",
-       // "io/ioutil", // investigate
+       "io/ioutil",
 
        "log",
        "log/syslog",
 
        "math",
-       // "math/big", // investigate
-       // "math/cmplx", // complex doesn't work yet
+       "math/big",
+       "math/cmplx",
        "math/rand",
 
        "mime",
        "mime/multipart",
 
-       // "net", // depends on C files
+       // "net", // c:\go\root\src\pkg\net\interface_windows.go:54:13: invalid operation: division by zero
        "net/http",
        "net/http/cgi",
-       // "net/http/fcgi", // investigate
+       "net/http/fcgi",
        "net/http/httptest",
        "net/http/httputil",
-       // "net/http/pprof", // investigate
+       "net/http/pprof",
        "net/mail",
-       // "net/rpc", // investigate
+       "net/rpc",
        "net/rpc/jsonrpc",
        "net/smtp",
        "net/textproto",
        "net/url",
 
-       // "path", // variadic parameters don't work yet fully
-       // "path/filepath", // investigate
+       "path",
+       "path/filepath",
 
-       // "reflect", // investigate
+       // "reflect", // unsafe.Sizeof must return size > 0 for pointer types
 
        "regexp",
        "regexp/syntax",
 
        "runtime",
-       // "runtime/cgo", // import "C"
+       "runtime/cgo",
        "runtime/debug",
        "runtime/pprof",
 
        "sort",
-       // "strconv", // investigate
+       // "strconv", // bug in switch case duplicate detection
        "strings",
 
-       // "sync", // platform-specific files
-       // "sync/atomic", // platform-specific files
+       "sync",
+       "sync/atomic",
 
-       // "syscall", // platform-specific files
+       // "syscall", c:\go\root\src\pkg\syscall\syscall_windows.go:35:16: cannot convert EINVAL (constant 536870951) to error
 
        "testing",
        "testing/iotest",
@@ -194,10 +196,10 @@ var tests = []string{
 
        "text/scanner",
        "text/tabwriter",
-       // "text/template", // variadic parameters don't work yet fully
-       // "text/template/parse", // variadic parameters don't work yet fully
+       "text/template",
+       "text/template/parse",
 
-       // "time", // platform-specific files
+       // "time", // local const decls without initialization expressions
        "unicode",
        "unicode/utf16",
        "unicode/utf8",
index f521427..9404a34 100644 (file)
@@ -20,7 +20,7 @@ import (
 
 const (
        blockSize   = 64
-       blockOffset = 2 // Substract 2 blocks to compensate for the 0x80 added to continuation bytes.
+       blockOffset = 2 // Subtract 2 blocks to compensate for the 0x80 added to continuation bytes.
 )
 
 type trieHandle struct {
index c0ab25e..def822d 100644 (file)
@@ -179,7 +179,7 @@ doNorm:
        i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p)
        if !i.rb.insertDecomposed(out[i.outStart:outp]) {
                // Start over to prevent decompositions from crossing segment boundaries.
-               // This is a rare occurance.
+               // This is a rare occurrence.
                i.p = i.inStart
                i.info = i.rb.f.info(i.rb.src, i.p)
        }
index 8b97059..1a118f2 100644 (file)
@@ -31,7 +31,7 @@ func runPosTests(t *testing.T, name string, f Form, fn positionFunc, tests []Pos
                }
                runes := []rune(test.buffer)
                if rb.nrune != len(runes) {
-                       t.Errorf("%s:%d: reorder buffer lenght is %d; want %d", name, i, rb.nrune, len(runes))
+                       t.Errorf("%s:%d: reorder buffer length is %d; want %d", name, i, rb.nrune, len(runes))
                        continue
                }
                for j, want := range runes {
index 1780ac7..52c88b0 100644 (file)
@@ -21,7 +21,7 @@ import (
 
 const (
        blockSize        = 64
-       blockOffset      = 2 // Substract two blocks to compensate for the 0x80 added to continuation bytes.
+       blockOffset      = 2 // Subtract two blocks to compensate for the 0x80 added to continuation bytes.
        maxSparseEntries = 16
 )
 
index 8826704..ed636ee 100644 (file)
@@ -128,13 +128,51 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
                x.mode = novalue
 
        case _Complex:
+               if !check.complexArg(x) {
+                       goto Error
+               }
+
                var y operand
                check.expr(&y, args[1], nil, iota)
                if y.mode == invalid {
                        goto Error
                }
-               // TODO(gri) handle complex(a, b) like (a + toImag(b))
-               unimplemented()
+               if !check.complexArg(&y) {
+                       goto Error
+               }
+
+               check.convertUntyped(x, y.typ)
+               if x.mode == invalid {
+                       goto Error
+               }
+               check.convertUntyped(&y, x.typ)
+               if y.mode == invalid {
+                       goto Error
+               }
+
+               if !isIdentical(x.typ, y.typ) {
+                       check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
+                       goto Error
+               }
+
+               typ := underlying(x.typ).(*Basic)
+               if x.mode == constant && y.mode == constant {
+                       x.val = binaryOpConst(x.val, toImagConst(y.val), token.ADD, typ)
+               } else {
+                       x.mode = value
+               }
+
+               switch typ.Kind {
+               case Float32:
+                       x.typ = Typ[Complex64]
+               case Float64:
+                       x.typ = Typ[Complex128]
+               case UntypedInt, UntypedRune, UntypedFloat:
+                       x.typ = Typ[UntypedComplex]
+               default:
+                       check.invalidArg(x.pos(), "float32 or float64 arguments expected")
+                       goto Error
+               }
 
        case _Copy:
                // TODO(gri) implements checks
@@ -361,3 +399,12 @@ func unparen(x ast.Expr) ast.Expr {
        }
        return x
 }
+
+func (check *checker) complexArg(x *operand) bool {
+       t, _ := underlying(x.typ).(*Basic)
+       if t != nil && (t.Info&IsFloat != 0 || t.Kind == UntypedInt || t.Kind == UntypedRune) {
+               return true
+       }
+       check.invalidArg(x.pos(), "%s must be a float32, float64, or an untyped non-complex numeric constant", x)
+       return false
+}
index c678e47..d44c8fb 100644 (file)
@@ -49,12 +49,17 @@ func (nilType) String() string {
        return "nil"
 }
 
-// Frequently used constants.
+// Implementation-specific constants.
+// TODO(gri) These need to go elsewhere.
+const (
+       intBits = 32
+       ptrBits = 64
+)
+
+// Frequently used values.
 var (
-       zeroConst     = int64(0)
-       oneConst      = int64(1)
-       minusOneConst = int64(-1)
-       nilConst      = nilType{}
+       nilConst  = nilType{}
+       zeroConst = int64(0)
 )
 
 // int64 bounds
@@ -74,7 +79,7 @@ func normalizeIntConst(x *big.Int) interface{} {
 }
 
 // normalizeRatConst returns the smallest constant representation
-// for the specific value of x; either an int64, *big.Int value,
+// for the specific value of x; either an int64, *big.Int,
 // or *big.Rat value.
 //
 func normalizeRatConst(x *big.Rat) interface{} {
@@ -84,15 +89,15 @@ func normalizeRatConst(x *big.Rat) interface{} {
        return x
 }
 
-// normalizeComplexConst returns the smallest constant representation
-// for the specific value of x; either an int64, *big.Int value, *big.Rat,
-// or complex value.
+// newComplex returns the smallest constant representation
+// for the specific value re + im*i; either an int64, *big.Int,
+// *big.Rat, or complex value.
 //
-func normalizeComplexConst(x complex) interface{} {
-       if x.im.Sign() == 0 {
-               return normalizeRatConst(x.re)
+func newComplex(re, im *big.Rat) interface{} {
+       if im.Sign() == 0 {
+               return normalizeRatConst(re)
        }
-       return x
+       return complex{re, im}
 }
 
 // makeRuneConst returns the int64 code point for the rune literal
@@ -140,7 +145,7 @@ func makeComplexConst(lit string) interface{} {
        n := len(lit)
        if n > 0 && lit[n-1] == 'i' {
                if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok {
-                       return normalizeComplexConst(complex{big.NewRat(0, 1), im})
+                       return newComplex(big.NewRat(0, 1), im)
                }
        }
        return nil
@@ -157,6 +162,22 @@ func makeStringConst(lit string) interface{} {
        return nil
 }
 
+// toImagConst returns the constant complex(0, x) for a non-complex x.
+func toImagConst(x interface{}) interface{} {
+       var im *big.Rat
+       switch x := x.(type) {
+       case int64:
+               im = big.NewRat(x, 1)
+       case *big.Int:
+               im = new(big.Rat).SetFrac(x, int1)
+       case *big.Rat:
+               im = x
+       default:
+               unreachable()
+       }
+       return complex{rat0, im}
+}
+
 // isZeroConst reports whether the value of constant x is 0.
 // x must be normalized.
 //
@@ -186,9 +207,6 @@ func isNegConst(x interface{}) bool {
 // of precision.
 //
 func isRepresentableConst(x interface{}, as BasicKind) bool {
-       const intBits = 32 // TODO(gri) implementation-specific constant
-       const ptrBits = 64 // TODO(gri) implementation-specific constant
-
        switch x := x.(type) {
        case bool:
                return as == Bool || as == UntypedBool
@@ -370,13 +388,71 @@ func is63bit(x int64) bool {
        return -1<<62 <= x && x <= 1<<62-1
 }
 
+// unaryOpConst returns the result of the constant evaluation op x where x is of the given type.
+func unaryOpConst(x interface{}, op token.Token, typ *Basic) interface{} {
+       switch op {
+       case token.ADD:
+               return x // nothing to do
+       case token.SUB:
+               switch x := x.(type) {
+               case int64:
+                       if z := -x; z != x {
+                               return z // no overflow
+                       }
+                       // overflow - need to convert to big.Int
+                       return normalizeIntConst(new(big.Int).Neg(big.NewInt(x)))
+               case *big.Int:
+                       return normalizeIntConst(new(big.Int).Neg(x))
+               case *big.Rat:
+                       return normalizeRatConst(new(big.Rat).Neg(x))
+               case complex:
+                       return newComplex(new(big.Rat).Neg(x.re), new(big.Rat).Neg(x.im))
+               }
+       case token.XOR:
+               var z big.Int
+               switch x := x.(type) {
+               case int64:
+                       z.Not(big.NewInt(x))
+               case *big.Int:
+                       z.Not(x)
+               default:
+                       unreachable()
+               }
+               // For unsigned types, the result will be negative and
+               // thus "too large": We must limit the result size to
+               // the type's size.
+               if typ.Info&IsUnsigned != 0 {
+                       s := uint(typ.Size) * 8
+                       if s == 0 {
+                               // platform-specific type
+                               // TODO(gri) this needs to be factored out
+                               switch typ.Kind {
+                               case Uint:
+                                       s = intBits
+                               case Uintptr:
+                                       s = ptrBits
+                               default:
+                                       unreachable()
+                               }
+                       }
+                       // z &^= (-1)<<s
+                       z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s))
+               }
+               return normalizeIntConst(&z)
+       case token.NOT:
+               return !x.(bool)
+       }
+       unreachable()
+       return nil
+}
+
 // binaryOpConst returns the result of the constant evaluation x op y;
-// both operands must be of the same "kind" (boolean, numeric, or string).
-// If intDiv is true, division (op == token.QUO) is using integer division
+// both operands must be of the same constant "kind" (boolean, numeric, or string).
+// If typ is an integer type, division (op == token.QUO) is using integer division
 // (and the result is guaranteed to be integer) rather than floating-point
 // division. Division by zero leads to a run-time panic.
 //
-func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
+func binaryOpConst(x, y interface{}, op token.Token, typ *Basic) interface{} {
        x, y = matchConst(x, y)
 
        switch x := x.(type) {
@@ -387,8 +463,6 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
                        return x && y
                case token.LOR:
                        return x || y
-               default:
-                       unreachable()
                }
 
        case int64:
@@ -415,7 +489,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
                case token.REM:
                        return x % y
                case token.QUO:
-                       if intDiv {
+                       if typ.Info&IsInteger != 0 {
                                return x / y
                        }
                        return normalizeRatConst(new(big.Rat).SetFrac(big.NewInt(x), big.NewInt(y)))
@@ -427,8 +501,6 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
                        return x ^ y
                case token.AND_NOT:
                        return x &^ y
-               default:
-                       unreachable()
                }
 
        case *big.Int:
@@ -444,7 +516,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
                case token.REM:
                        z.Rem(x, y)
                case token.QUO:
-                       if intDiv {
+                       if typ.Info&IsInteger != 0 {
                                z.Quo(x, y)
                        } else {
                                return normalizeRatConst(new(big.Rat).SetFrac(x, y))
@@ -517,7 +589,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
                default:
                        unreachable()
                }
-               return normalizeComplexConst(complex{&re, &im})
+               return newComplex(&re, &im)
 
        case string:
                if op == token.ADD {
index 1a16595..b1b6436 100644 (file)
@@ -266,15 +266,8 @@ func writeType(buf *bytes.Buffer, typ Type) {
                buf.WriteByte('*')
                writeType(buf, t.Base)
 
-       case *tuple:
-               buf.WriteByte('(')
-               for i, typ := range t.list {
-                       if i > 0 {
-                               buf.WriteString("; ")
-                       }
-                       writeType(buf, typ)
-               }
-               buf.WriteByte(')')
+       case *Result:
+               writeParams(buf, t.Values, false)
 
        case *Signature:
                buf.WriteString("func")
index e1f627b..2f5f2b3 100644 (file)
@@ -19,7 +19,6 @@ import (
 // - at the moment, iota is passed around almost everywhere - in many places we know it cannot be used
 
 // TODO(gri) API issues
-// - clients need access to result type information (tuples)
 // - clients need access to constant values
 // - clients need access to built-in type information
 
@@ -212,21 +211,11 @@ func (check *checker) unary(x *operand, op token.Token) {
        }
 
        if x.mode == constant {
-               switch op {
-               case token.ADD:
-                       // nothing to do
-               case token.SUB:
-                       x.val = binaryOpConst(zeroConst, x.val, token.SUB, false)
-               case token.XOR:
-                       x.val = binaryOpConst(minusOneConst, x.val, token.XOR, false)
-               case token.NOT:
-                       x.val = !x.val.(bool)
-               default:
-                       unreachable() // operators where checked by check.op
-               }
+               typ := underlying(x.typ).(*Basic)
+               x.val = unaryOpConst(x.val, op, typ)
                // Typed constants must be representable in
                // their type after each constant operation.
-               check.isRepresentable(x, underlying(x.typ).(*Basic))
+               check.isRepresentable(x, typ)
                return
        }
 
@@ -304,6 +293,8 @@ func (check *checker) convertUntyped(x *operand, target Type) {
                if !x.isNil() {
                        goto Error
                }
+       default:
+               unreachable()
        }
 
        x.typ = target
@@ -332,7 +323,7 @@ func (check *checker) comparison(x, y *operand, op token.Token) {
        }
 
        if !valid {
-               check.invalidOp(x.pos(), "cannot compare %s and %s", x, y)
+               check.invalidOp(x.pos(), "cannot compare %s %s %s", x, op, y)
                x.mode = invalid
                return
        }
@@ -465,10 +456,11 @@ func (check *checker) binary(x, y *operand, op token.Token, hint Type) {
        }
 
        if x.mode == constant && y.mode == constant {
-               x.val = binaryOpConst(x.val, y.val, op, isInteger(x.typ))
+               typ := underlying(x.typ).(*Basic)
+               x.val = binaryOpConst(x.val, y.val, op, typ)
                // Typed constants must be representable in
                // their type after each constant operation.
-               check.isRepresentable(x, underlying(x.typ).(*Basic))
+               check.isRepresentable(x, typ)
                return
        }
 
@@ -554,9 +546,15 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota
        return max
 }
 
-func (check *checker) argument(sig *Signature, i int, arg ast.Expr) {
+// argument typechecks passing an argument arg (if arg != nil) or
+// x (if arg == nil) to the i'th parameter of the given signature.
+// If passSlice is set, the argument is followed by ... in the call.
+//
+func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, passSlice bool) {
+       // determine parameter
        var par *ast.Object
-       if n := len(sig.Params); i < n {
+       n := len(sig.Params)
+       if i < n {
                par = sig.Params[i]
        } else if sig.IsVariadic {
                par = sig.Params[n-1]
@@ -565,16 +563,32 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr) {
                return
        }
 
-       // TODO(gri) deal with ... last argument
-       var z, x operand
+       // determine argument
+       var z operand
        z.mode = variable
-       z.expr = nil            // TODO(gri) can we do better here?
-       z.typ = par.Type.(Type) // TODO(gri) should become something like checkObj(&z, ...) eventually
-       check.expr(&x, arg, z.typ, -1)
+       z.expr = nil // TODO(gri) can we do better here? (for good error messages)
+       z.typ = par.Type.(Type)
+
+       if arg != nil {
+               check.expr(x, arg, z.typ, -1)
+       }
        if x.mode == invalid {
                return // ignore this argument
        }
-       check.assignOperand(&z, &x)
+
+       // check last argument of the form x...
+       if passSlice {
+               if i+1 != n {
+                       check.errorf(x.pos(), "can only use ... with matching parameter")
+                       return // ignore this argument
+               }
+               // spec: "If the final argument is assignable to a slice type []T,
+               // it may be passed unchanged as the value for a ...T parameter if
+               // the argument is followed by ..."
+               z.typ = &Slice{Elt: z.typ} // change final parameter type to []T
+       }
+
+       check.assignOperand(&z, x)
 }
 
 func (check *checker) recordType(x *operand) {
@@ -584,7 +598,7 @@ func (check *checker) recordType(x *operand) {
 }
 
 // rawExpr typechecks expression e and initializes x with the expression
-// value or type. If an error occured, x.mode is set to invalid.
+// value or type. If an error occurred, x.mode is set to invalid.
 // A hint != nil is used as operand type for untyped shifted operands;
 // iota >= 0 indicates that the expression is part of a constant declaration.
 // cycleOk indicates whether it is ok for a type expression to refer to itself.
@@ -653,7 +667,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                x.typ = obj.Type.(Type)
 
        case *ast.Ellipsis:
-               // ellipses are handled explictly where they are legal
+               // ellipses are handled explicitly where they are legal
                // (array composite literals and parameter lists)
                check.errorf(e.Pos(), "invalid use of '...'")
                goto Error
@@ -1052,25 +1066,79 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                        check.conversion(x, e, x.typ, iota)
                } else if sig, ok := underlying(x.typ).(*Signature); ok {
                        // check parameters
-                       // TODO(gri)
-                       // - deal with single multi-valued function arguments: f(g())
-                       // - variadic functions only partially addressed
-                       for i, arg := range e.Args {
-                               check.argument(sig, i, arg)
+
+                       // If we have a trailing ... at the end of the parameter
+                       // list, the last argument must match the parameter type
+                       // []T of a variadic function parameter x ...T.
+                       passSlice := false
+                       if e.Ellipsis.IsValid() {
+                               if sig.IsVariadic {
+                                       passSlice = true
+                               } else {
+                                       check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun)
+                                       // ok to continue
+                               }
                        }
 
-                       // 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)
index 1a5e517..f8ddd84 100644 (file)
@@ -182,7 +182,14 @@ func (x *operand) isAssignable(T Type) bool {
        if isUntyped(Vu) {
                switch t := Tu.(type) {
                case *Basic:
-                       return x.mode == constant && isRepresentableConst(x.val, t.Kind)
+                       if x.mode == constant {
+                               return isRepresentableConst(x.val, t.Kind)
+                       }
+                       // The result of a comparison is an untyped boolean,
+                       // but may not be a constant.
+                       if Vb, _ := Vu.(*Basic); Vb != nil {
+                               return Vb.Kind == UntypedBool && isBoolean(Tu)
+                       }
                case *Interface:
                        return x.isNil() || len(t.Methods) == 0
                case *Pointer, *Signature, *Slice, *Map, *Chan:
index 2c1a991..ff6825b 100644 (file)
@@ -225,25 +225,28 @@ func deref(typ Type) Type {
 }
 
 // defaultType returns the default "typed" type for an "untyped" type;
-// it returns the argument typ for all other types.
+// it returns the incoming type for all other types. If there is no
+// corresponding untyped type, the result is Typ[Invalid].
+//
 func defaultType(typ Type) Type {
        if t, ok := typ.(*Basic); ok {
-               var k BasicKind
+               k := Invalid
                switch t.Kind {
+               // case UntypedNil:
+               //      There is no default type for nil. For a good error message,
+               //      catch this case before calling this function.
                case UntypedBool:
                        k = Bool
-               case UntypedRune:
-                       k = Rune
                case UntypedInt:
                        k = Int
+               case UntypedRune:
+                       k = Rune
                case UntypedFloat:
                        k = Float64
                case UntypedComplex:
                        k = Complex128
                case UntypedString:
                        k = String
-               default:
-                       unreachable()
                }
                typ = Typ[k]
        }
index e2c6448..7f9d45e 100644 (file)
@@ -12,9 +12,9 @@ import (
 )
 
 func (check *checker) assignOperand(z, x *operand) {
-       if t, ok := x.typ.(*tuple); ok {
+       if t, ok := x.typ.(*Result); ok {
                // TODO(gri) elsewhere we use "assignment count mismatch" (consolidate)
-               check.errorf(x.pos(), "%d-valued expression %s used as single value", len(t.list), x)
+               check.errorf(x.pos(), "%d-valued expression %s used as single value", len(t.Values), x)
                x.mode = invalid
                return
        }
@@ -95,7 +95,12 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
                if x.mode != invalid {
                        typ = x.typ
                        if obj.Kind == ast.Var && isUntyped(typ) {
-                               typ = defaultType(typ)
+                               if x.isNil() {
+                                       check.errorf(x.pos(), "use of untyped nil")
+                                       x.mode = invalid
+                               } else {
+                                       typ = defaultType(typ)
+                               }
                        }
                }
                obj.Type = typ
@@ -177,12 +182,12 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
                        return
                }
 
-               if t, ok := x.typ.(*tuple); ok && len(lhs) == len(t.list) {
+               if t, ok := x.typ.(*Result); ok && len(lhs) == len(t.Values) {
                        // function result
                        x.mode = value
-                       for i, typ := range t.list {
+                       for i, obj := range t.Values {
                                x.expr = nil // TODO(gri) should do better here
-                               x.typ = typ
+                               x.typ = obj.Type.(Type)
                                check.assign1to1(lhs[i], nil, &x, decl, iota)
                        }
                        return
@@ -427,25 +432,58 @@ func (check *checker) stmt(s ast.Stmt) {
        case *ast.SwitchStmt:
                check.optionalStmt(s.Init)
                var x operand
-               if s.Tag != nil {
-                       check.expr(&x, s.Tag, nil, -1)
-               } else {
-                       // TODO(gri) should provide a position (see IncDec) for good error messages
-                       x.mode = constant
-                       x.typ = Typ[UntypedBool]
-                       x.val = true
+               tag := s.Tag
+               if tag == nil {
+                       // use fake true tag value and position it at the opening { of the switch
+                       tag = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true", Obj: Universe.Lookup("true")}
                }
+               check.expr(&x, tag, nil, -1)
 
                check.multipleDefaults(s.Body.List)
+               seen := make(map[interface{}]token.Pos)
                for _, s := range s.Body.List {
                        clause, _ := s.(*ast.CaseClause)
                        if clause == nil {
                                continue // error reported before
                        }
-                       for _, expr := range clause.List {
-                               var y operand
-                               check.expr(&y, expr, nil, -1)
-                               // TODO(gri) x and y must be comparable
+                       if x.mode != invalid {
+                               for _, expr := range clause.List {
+                                       x := x // copy of x (don't modify original)
+                                       var y operand
+                                       check.expr(&y, expr, nil, -1)
+                                       if y.mode == invalid {
+                                               continue // error reported before
+                                       }
+                                       // If we have a constant case value, it must appear only
+                                       // once in the switch statement. Determine if there is a
+                                       // duplicate entry, but only report an error if there are
+                                       // no other errors.
+                                       var dupl token.Pos
+                                       if y.mode == constant {
+                                               // TODO(gri) This code doesn't work correctly for
+                                               //           large integer, floating point, or
+                                               //           complex values - the respective struct
+                                               //           comparisons are shallow. Need to use a
+                                               //           hash function to index the map.
+                                               dupl = seen[y.val]
+                                               seen[y.val] = y.pos()
+                                       }
+                                       // TODO(gri) The convertUntyped call pair below appears in other places. Factor!
+                                       // Order matters: By comparing y against x, error positions are at the case values.
+                                       check.convertUntyped(&y, x.typ)
+                                       if y.mode == invalid {
+                                               continue // error reported before
+                                       }
+                                       check.convertUntyped(&x, y.typ)
+                                       if x.mode == invalid {
+                                               continue // error reported before
+                                       }
+                                       check.comparison(&y, &x, token.EQL)
+                                       if y.mode != invalid && dupl.IsValid() {
+                                               check.errorf(y.pos(), "%s is duplicate case (previous at %s)",
+                                                       &y, check.fset.Position(dupl))
+                                       }
+                               }
                        }
                        check.stmtList(clause.Body)
                }
index a07af89..a951853 100644 (file)
@@ -46,10 +46,33 @@ func _close() {
 }
 
 func _complex() {
-       _0 := complex /* ERROR "argument" */ ()
-       _1 := complex /* ERROR "argument" */ (1)
-       _2 := complex(1, 2)
-       // TODO(gri) add tests checking types
+       var i32 int32
+       var f32 float32
+       var f64 float64
+       var c64 complex64
+       _ = complex /* ERROR "argument" */ ()
+       _ = complex /* ERROR "argument" */ (1)
+       _ = complex(true /* ERROR "invalid argument" */ , 0)
+       _ = complex(i32 /* ERROR "invalid argument" */ , 0)
+       _ = complex("foo" /* ERROR "invalid argument" */ , 0)
+       _ = complex(c64 /* ERROR "invalid argument" */ , 0)
+       _ = complex(0, true /* ERROR "invalid argument" */ )
+       _ = complex(0, i32 /* ERROR "invalid argument" */ )
+       _ = complex(0, "foo" /* ERROR "invalid argument" */ )
+       _ = complex(0, c64 /* ERROR "invalid argument" */ )
+       _ = complex(f32, f32)
+       _ = complex(f32, 1)
+       _ = complex(f32, 1.0)
+       _ = complex(f32, 'a')
+       _ = complex(f64, f64)
+       _ = complex(f64, 1)
+       _ = complex(f64, 1.0)
+       _ = complex(f64, 'a')
+       _ = complex(f32 /* ERROR "mismatched types" */, f64)
+       _ = complex(f64 /* ERROR "mismatched types" */, f32)
+       _ = complex(1, 1)
+       _ = complex(1, 1.1)
+       _ = complex(1, 'a')
        complex /* ERROR "not used" */ (1, 2)
 }
 
index be92709..3baed67 100644 (file)
@@ -46,7 +46,7 @@ var (
        s14 = i << j /* ERROR "must be unsigned" */ 
        s18 = math.Pi * 10.0
        s19 = s1 /* ERROR "cannot call" */ ()
-       s20 = f0 /* ERROR "used as single value" */ ()
+       s20 = f0 /* ERROR "no value" */ ()
        s21 = f6(1, s1, i)
        s22 = f6(1, s1, uu /* ERROR "cannot assign" */ )
        
@@ -68,7 +68,7 @@ var (
        t17 math /* ERROR "not a type" */ .Pi
        t18 float64 = math.Pi * 10.0
        t19 int = t1 /* ERROR "cannot call" */ ()
-       t20 int = f0 /* ERROR "used as single value" */ ()
+       t20 int = f0 /* ERROR "no value" */ ()
 )
 
 // Various more complex expressions
@@ -94,6 +94,7 @@ var (
        v10 byte = 1024 /* ERROR "overflows" */
        v11 = xx/yy*yy - xx
        v12 = true && false
+       v13 = nil /* ERROR "use of untyped nil" */
 )
 
 // Multiple assignment expressions
index 0ed314a..8b2eb04 100644 (file)
@@ -63,6 +63,7 @@ var (
        u16 = &u0
        u17 = *u16
        u18 = <-u16 /* ERROR "cannot receive" */
+       u19 = ^uint(0)
 
        // float64
        f0 = float64(1)
@@ -131,5 +132,4 @@ var (
        ch7 = <-ch
        ch8 = <-rc
        ch9 = <-sc /* ERROR "cannot receive" */
-
-)
\ No newline at end of file
+)
index 4bc2769..674be40 100644 (file)
@@ -6,6 +6,17 @@
 
 package expr2
 
+func _bool() {
+       const t = true == true
+       const f = true == false
+       _ = t /* ERROR "cannot compare" */ < f
+       _ = 0 /* ERROR "cannot convert" */ == t
+       var b bool
+       var x, y float32
+       b = x < y
+       _ = struct{b bool}{x < y}
+}
+
 // corner cases
 var (
        v0 = nil /* ERROR "cannot compare" */ == nil
index a5ea4d2..35905c4 100644 (file)
@@ -286,3 +286,64 @@ func type_asserts() {
        _ = t.(T2 /* ERROR "wrong type for method m" */ )
        _ = t.(I2 /* ERROR "wrong type for method m" */ )
 }
+
+func f0() {}
+func f1(x int) {}
+func f2(u float32, s string) {}
+func fs(s []byte) {}
+func fv(x ...int) {}
+func fi(x ... interface{}) {}
+
+func g0() {}
+func g1() int { return 0}
+func g2() (u float32, s string) { return }
+func gs() []byte { return nil }
+
+func _calls() {
+       var x int
+       var y float32
+       var s []int
+
+       f0()
+       _ = f0 /* ERROR "used as value" */ ()
+       f0(g0 /* ERROR "too many arguments" */ )
+
+       f1(0)
+       f1(x)
+       f1(10.0)
+       f1 /* ERROR "too few arguments" */ ()
+       f1(x, y /* ERROR "too many arguments" */ )
+       f1(s /* ERROR "cannot assign" */ )
+       f1(x ... /* ERROR "cannot use ..." */ )
+       f1(g0 /* ERROR "used as value" */ ())
+       f1(g1())
+       // f1(g2()) // TODO(gri) missing position in error message
+
+       f2 /* ERROR "too few arguments" */ ()
+       f2 /* ERROR "too few arguments" */ (3.14)
+       f2(3.14, "foo")
+       f2(x /* ERROR "cannot assign" */ , "foo")
+       f2(g0 /* ERROR "used as value" */ ())
+       f2 /* ERROR "too few arguments" */ (g1 /* ERROR "cannot assign" */ ())
+       f2(g2())
+
+       fs /* ERROR "too few arguments" */ ()
+       fs(g0 /* ERROR "used as value" */ ())
+       fs(g1 /* ERROR "cannot assign" */ ())
+       // fs(g2()) // TODO(gri) missing position in error message
+       fs(gs())
+
+       fv()
+       fv(1, 2.0, x)
+       fv(s /* ERROR "cannot assign" */ )
+       fv(s...)
+       fv(1, s /* ERROR "can only use ... with matching parameter" */ ...)
+       fv(gs /* ERROR "cannot assign" */ ())
+       fv(gs /* ERROR "cannot assign" */ ()...)
+
+       fi()
+       fi(1, 2.0, x, 3.14, "foo")
+       fi(g2())
+       fi(0, g2)
+       fi(0, g2 /* ERROR "2-valued expression" */ ())
+}
\ No newline at end of file
index d3cc3ac..c0e0236 100644 (file)
@@ -101,7 +101,31 @@ func _switches() {
        default /* ERROR "multiple defaults" */ :
        }
 
-       // TODO(gri) more tests
+       switch {
+       case 1  /* ERROR "cannot convert" */ :
+       }
+
+       switch int32(x) {
+       case 1, 2:
+       case x /* ERROR "cannot compare" */ :
+       }
+
+       switch x {
+       case 1 /* ERROR "overflows int" */ << 100:
+       }
+
+       switch x {
+       case 1:
+       case 1 /* ERROR "duplicate case" */ :
+       case 2, 3, 4:
+       case 1 /* ERROR "duplicate case" */ :
+       }
+
+       // TODO(gri) duplicate 64bit values that don't fit into an int64 are not yet detected
+       switch uint64(x) {
+       case 1<<64-1:
+       case 1<<64-1:
+       }
 }
 
 type I interface {
index 83a0826..6e4a987 100644 (file)
@@ -141,15 +141,15 @@ type Pointer struct {
        Base Type
 }
 
-// A tuple represents a multi-value function return.
-// TODO(gri) use better name to avoid confusion (Go doesn't have tuples).
-type tuple struct {
+// A Result represents a (multi-value) function call result.
+// TODO(gri) consider using an empty Result (Values == nil)
+//           as representation for the novalue operand mode.
+type Result struct {
        implementsType
-       list []Type
+       Values ObjList // Signature.Results of the function called
 }
 
 // A Signature represents a user-defined function type func(...) (...).
-// TODO(gri) consider using "tuples" to represent parameters and results (see comment on tuples).
 type Signature struct {
        implementsType
        Recv       *ast.Object // nil if not a method
index 62ca19b..361f636 100644 (file)
@@ -155,7 +155,7 @@ var testExprs = []testEntry{
        dup("-f(10, 20)"),
        dup("f(x + y, +3.1415)"),
        {"func(a, b int) {}", "(func literal)"},
-       {"func(a, b int) []int {}()[x]", "(func literal)()[x]"},
+       {"func(a, b int) []int {}(1, 2)[x]", "(func literal)(1, 2)[x]"},
        {"[]int{1, 2, 3}", "(composite literal)"},
        {"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"},
        {"i.([]string)", "i.(...)"},
index 3406113..ab06465 100644 (file)
@@ -476,6 +476,11 @@ var fmttests = []struct {
 
        // Used to crash because nByte didn't allow for a sign.
        {"%b", int64(-1 << 63), "-1000000000000000000000000000000000000000000000000000000000000000"},
+
+       // Complex fmt used to leave the plus flag set for future entries in the array
+       // causing +2+0i and +3+0i instead of 2+0i and 3+0i.
+       {"%v", []complex64{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"},
+       {"%v", []complex128{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"},
 }
 
 func TestSprintf(t *testing.T) {
index ce80116..c3d7605 100644 (file)
@@ -396,7 +396,7 @@ func (f *fmt) fmt_f64(v float64) { f.formatFloat(v, 'f', doPrec(f, 6), 64) }
 // fmt_g64 formats a float64 in the 'f' or 'e' form according to size.
 func (f *fmt) fmt_g64(v float64) { f.formatFloat(v, 'g', doPrec(f, -1), 64) }
 
-// fmt_g64 formats a float64 in the 'f' or 'E' form according to size.
+// fmt_G64 formats a float64 in the 'f' or 'E' form according to size.
 func (f *fmt) fmt_G64(v float64) { f.formatFloat(v, 'G', doPrec(f, -1), 64) }
 
 // fmt_fb64 formats a float64 in the form -123p3 (exponent is power of 2).
@@ -428,6 +428,7 @@ func (f *fmt) fmt_fb32(v float32) { f.formatFloat(float64(v), 'b', 0, 32) }
 func (f *fmt) fmt_c64(v complex64, verb rune) {
        f.buf.WriteByte('(')
        r := real(v)
+       oldPlus := f.plus
        for i := 0; ; i++ {
                switch verb {
                case 'e':
@@ -447,6 +448,7 @@ func (f *fmt) fmt_c64(v complex64, verb rune) {
                f.plus = true
                r = imag(v)
        }
+       f.plus = oldPlus
        f.buf.Write(irparenBytes)
 }
 
@@ -454,6 +456,7 @@ func (f *fmt) fmt_c64(v complex64, verb rune) {
 func (f *fmt) fmt_c128(v complex128, verb rune) {
        f.buf.WriteByte('(')
        r := real(v)
+       oldPlus := f.plus
        for i := 0; ; i++ {
                switch verb {
                case 'e':
@@ -473,5 +476,6 @@ func (f *fmt) fmt_c128(v complex128, verb rune) {
                f.plus = true
                r = imag(v)
        }
+       f.plus = oldPlus
        f.buf.Write(irparenBytes)
 }
index 62de3a2..6a282c8 100644 (file)
@@ -337,7 +337,10 @@ func (r *readRune) readByte() (b byte, err error) {
                r.pending--
                return
        }
-       _, err = r.reader.Read(r.pendBuf[0:1])
+       n, err := io.ReadFull(r.reader, r.pendBuf[0:1])
+       if n != 1 {
+               return 0, err
+       }
        return r.pendBuf[0], err
 }
 
index e1582c3..bf533d1 100644 (file)
@@ -555,7 +555,7 @@ type (
 
        // A DeclStmt node represents a declaration in a statement list.
        DeclStmt struct {
-               Decl Decl
+               Decl Decl // *GenDecl with CONST, TYPE, or VAR token
        }
 
        // An EmptyStmt node represents an empty statement.
index e65d845..a164425 100644 (file)
@@ -222,6 +222,8 @@ var cgoEnabled = map[string]bool{
        "linux/arm":     true,
        "netbsd/386":    true,
        "netbsd/amd64":  true,
+       "openbsd/386":   true,
+       "openbsd/amd64": true,
        "windows/386":   true,
        "windows/amd64": true,
 }
@@ -424,6 +426,13 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
                if strings.HasPrefix(path, "/") {
                        return p, fmt.Errorf("import %q: cannot import absolute path", path)
                }
+
+               // tried records the location of unsucsessful package lookups
+               var tried struct {
+                       goroot string
+                       gopath []string
+               }
+
                // Determine directory from import path.
                if ctxt.GOROOT != "" {
                        dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", path)
@@ -435,6 +444,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
                                p.Root = ctxt.GOROOT
                                goto Found
                        }
+                       tried.goroot = dir
                }
                for _, root := range ctxt.gopath() {
                        dir := ctxt.joinPath(root, "src", path)
@@ -445,8 +455,28 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
                                p.Root = root
                                goto Found
                        }
+                       tried.gopath = append(tried.gopath, dir)
+               }
+
+               // 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:
index 51e2bf7..c4b7e6a 100644 (file)
@@ -229,7 +229,8 @@ type block struct {
 var nonAlphaNumRx = regexp.MustCompile(`[^a-zA-Z0-9]`)
 
 func anchorID(line string) string {
-       return nonAlphaNumRx.ReplaceAllString(line, "_")
+       // Add a "hdr-" prefix to avoid conflicting with IDs used for package symbols.
+       return "hdr-" + nonAlphaNumRx.ReplaceAllString(line, "_")
 }
 
 // ToHTML converts comment text to formatted HTML.
index e5752bb..9fc0b41 100644 (file)
@@ -119,8 +119,29 @@ func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
                return nil
        }
 
-       // Find unresolved identifiers
+       // Find top-level declarations in the file.
+       topDecls := make(map[*ast.Object]bool)
+       for _, decl := range file.Decls {
+               switch d := decl.(type) {
+               case *ast.FuncDecl:
+                       topDecls[d.Name.Obj] = true
+               case *ast.GenDecl:
+                       for _, spec := range d.Specs {
+                               switch s := spec.(type) {
+                               case *ast.TypeSpec:
+                                       topDecls[s.Name.Obj] = true
+                               case *ast.ValueSpec:
+                                       for _, id := range s.Names {
+                                               topDecls[id.Obj] = true
+                                       }
+                               }
+                       }
+               }
+       }
+
+       // Find unresolved identifiers and uses of top-level declarations.
        unresolved := make(map[string]bool)
+       usesTopDecl := false
        ast.Inspect(body, func(n ast.Node) bool {
                // For an expression like fmt.Println, only add "fmt" to the
                // set of unresolved names.
@@ -130,11 +151,19 @@ func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
                        }
                        return false
                }
-               if id, ok := n.(*ast.Ident); ok && id.Obj == nil {
-                       unresolved[id.Name] = true
+               if id, ok := n.(*ast.Ident); ok {
+                       if id.Obj == nil {
+                               unresolved[id.Name] = true
+                       } else if topDecls[id.Obj] {
+                               usesTopDecl = true
+                       }
                }
                return true
        })
+       if usesTopDecl {
+               // We don't support examples that are not self-contained (yet).
+               return nil
+       }
 
        // Remove predeclared identifiers from unresolved list.
        for n := range unresolved {
index 286296e..65b0e4e 100644 (file)
@@ -46,7 +46,7 @@ func Node(dst io.Writer, fset *token.FileSet, node interface{}) error {
        // Sort imports if necessary.
        if file != nil && hasUnsortedImports(file) {
                // Make a copy of the AST because ast.SortImports is destructive.
-               // TODO(gri) Do this more efficently.
+               // TODO(gri) Do this more efficiently.
                var buf bytes.Buffer
                err := config.Fprint(&buf, fset, file)
                if err != nil {
index cd5b67b..3bed0cc 100644 (file)
@@ -83,7 +83,7 @@ func (p *printer) setComment(g *ast.CommentGroup) {
        // don't overwrite any pending comment in the p.comment cache
        // (there may be a pending comment when a line comment is
        // immediately followed by a lead comment with no other
-       // tokens inbetween)
+       // tokens between)
        if p.commentOffset == infinity {
                p.nextComment() // get comment ready for use
        }
index fc45c1e..f5d9995 100644 (file)
@@ -295,9 +295,9 @@ type FileSet struct {
 
 // NewFileSet creates a new file set.
 func NewFileSet() *FileSet {
-       s := new(FileSet)
-       s.base = 1 // 0 == NoPos
-       return s
+       return &FileSet{
+               base: 1, // 0 == NoPos
+       }
 }
 
 // Base returns the minimum base offset that must be provided to
@@ -367,8 +367,10 @@ func searchFiles(a []*File, x int) int {
 }
 
 func (s *FileSet) file(p Pos) *File {
+       s.mutex.RLock()
        // common case: p is in last file
        if f := s.last; f != nil && f.base <= int(p) && int(p) <= f.base+f.size {
+               s.mutex.RUnlock()
                return f
        }
        // p is not in last file - search all files
@@ -376,10 +378,14 @@ func (s *FileSet) file(p Pos) *File {
                f := s.files[i]
                // f.base <= int(p) by definition of searchFiles
                if int(p) <= f.base+f.size {
-                       s.last = f
+                       s.mutex.RUnlock()
+                       s.mutex.Lock()
+                       s.last = f // race is ok - s.last is only a cache
+                       s.mutex.Unlock()
                        return f
                }
        }
+       s.mutex.RUnlock()
        return nil
 }
 
@@ -389,9 +395,7 @@ func (s *FileSet) file(p Pos) *File {
 //
 func (s *FileSet) File(p Pos) (f *File) {
        if p != NoPos {
-               s.mutex.RLock()
                f = s.file(p)
-               s.mutex.RUnlock()
        }
        return
 }
@@ -399,11 +403,9 @@ func (s *FileSet) File(p Pos) (f *File) {
 // Position converts a Pos in the fileset into a general Position.
 func (s *FileSet) Position(p Pos) (pos Position) {
        if p != NoPos {
-               s.mutex.RLock()
                if f := s.file(p); f != nil {
                        pos = f.position(p)
                }
-               s.mutex.RUnlock()
        }
        return
 }
index 160107d..1d36c22 100644 (file)
@@ -6,6 +6,8 @@ package token
 
 import (
        "fmt"
+       "math/rand"
+       "sync"
        "testing"
 )
 
@@ -179,3 +181,52 @@ func TestFiles(t *testing.T) {
                }
        }
 }
+
+// FileSet.File should return nil if Pos is past the end of the FileSet.
+func TestFileSetPastEnd(t *testing.T) {
+       fset := NewFileSet()
+       for _, test := range tests {
+               fset.AddFile(test.filename, fset.Base(), test.size)
+       }
+       if f := fset.File(Pos(fset.Base())); f != nil {
+               t.Errorf("expected nil, got %v", f)
+       }
+}
+
+func TestFileSetCacheUnlikely(t *testing.T) {
+       fset := NewFileSet()
+       offsets := make(map[string]int)
+       for _, test := range tests {
+               offsets[test.filename] = fset.Base()
+               fset.AddFile(test.filename, fset.Base(), test.size)
+       }
+       for file, pos := range offsets {
+               f := fset.File(Pos(pos))
+               if f.Name() != file {
+                       t.Errorf("expecting %q at position %d, got %q", file, pos, f.Name())
+               }
+       }
+}
+
+// issue 4345. Test concurrent use of FileSet.Pos does not trigger a
+// race in the FileSet position cache.
+func TestFileSetRace(t *testing.T) {
+       fset := NewFileSet()
+       for i := 0; i < 100; i++ {
+               fset.AddFile(fmt.Sprintf("file-%d", i), fset.Base(), 1031)
+       }
+       max := int32(fset.Base())
+       var stop sync.WaitGroup
+       r := rand.New(rand.NewSource(7))
+       for i := 0; i < 2; i++ {
+               r := rand.New(rand.NewSource(r.Int63()))
+               stop.Add(1)
+               go func() {
+                       for i := 0; i < 1000; i++ {
+                               fset.Position(Pos(r.Int31n(max)))
+                       }
+                       stop.Done()
+               }()
+       }
+       stop.Wait()
+}
index bddb701..859adaf 100644 (file)
@@ -468,6 +468,11 @@ func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error) {
        off += s.base
        if max := s.limit - off; int64(len(p)) > max {
                p = p[0:max]
+               n, err = s.r.ReadAt(p, off)
+               if err == nil {
+                       err = EOF
+               }
+               return n, err
        }
        return s.r.ReadAt(p, off)
 }
index 1e671b5..f3ec050 100644 (file)
@@ -203,3 +203,35 @@ func TestTeeReader(t *testing.T) {
                t.Errorf("closed tee: ReadFull(r, dst) = %d, %v; want 0, EPIPE", n, err)
        }
 }
+
+func TestSectionReader_ReadAt(tst *testing.T) {
+       dat := "a long sample data, 1234567890"
+       tests := []struct {
+               data   string
+               off    int
+               n      int
+               bufLen int
+               at     int
+               exp    string
+               err    error
+       }{
+               {data: "", off: 0, n: 10, bufLen: 2, at: 0, exp: "", err: EOF},
+               {data: dat, off: 0, n: len(dat), bufLen: 0, at: 0, exp: "", err: nil},
+               {data: dat, off: len(dat), n: 1, bufLen: 1, at: 0, exp: "", err: EOF},
+               {data: dat, off: 0, n: len(dat) + 2, bufLen: len(dat), at: 0, exp: dat, err: nil},
+               {data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 0, exp: dat[:len(dat)/2], err: nil},
+               {data: dat, off: 0, n: len(dat), bufLen: len(dat), at: 0, exp: dat, err: nil},
+               {data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[2 : 2+len(dat)/2], err: nil},
+               {data: dat, off: 3, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[5 : 5+len(dat)/2], err: nil},
+               {data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 - 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: nil},
+               {data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 + 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: EOF},
+       }
+       for i, t := range tests {
+               r := strings.NewReader(t.data)
+               s := NewSectionReader(r, int64(t.off), int64(t.n))
+               buf := make([]byte, t.bufLen)
+               if n, err := s.ReadAt(buf, int64(t.at)); n != len(t.exp) || string(buf[:n]) != t.exp || err != t.err {
+                       tst.Fatalf("%d: ReadAt(%d) = %q, %v; expected %q, %v", i, t.at, buf[:n], err, t.exp, t.err)
+               }
+       }
+}
index 4c0bf1f..67d7103 100644 (file)
@@ -20,13 +20,14 @@ var serverAddr string
 
 func runSyslog(c net.PacketConn, done chan<- string) {
        var buf [4096]byte
-       var rcvd string = ""
+       var rcvd string
        for {
-               n, _, err := c.ReadFrom(buf[0:])
-               if err != nil || n == 0 {
+               c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+               n, _, err := c.ReadFrom(buf[:])
+               rcvd += string(buf[:n])
+               if err != nil {
                        break
                }
-               rcvd += string(buf[0:n])
        }
        done <- rcvd
 }
@@ -37,7 +38,6 @@ func startServer(done chan<- string) {
                log.Fatalf("net.ListenPacket failed udp :0 %v", e)
        }
        serverAddr = c.LocalAddr().String()
-       c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
        go runSyslog(c, done)
 }
 
index cdea803..0d8b10f 100644 (file)
@@ -2281,6 +2281,13 @@ func TestLog2(t *testing.T) {
                        t.Errorf("Log2(%g) = %g, want %g", vflogSC[i], f, logSC[i])
                }
        }
+       for i := -1074; i <= 1023; i++ {
+               f := Ldexp(1, i)
+               l := Log2(f)
+               if l != float64(i) {
+                       t.Errorf("Log2(2**%d) = %g, want %d", i, l, i)
+               }
+       }
 }
 
 func TestModf(t *testing.T) {
index 95c0d58..63a4536 100644 (file)
@@ -51,6 +51,13 @@ func (z *Int) SetInt64(x int64) *Int {
        return z
 }
 
+// SetUint64 sets z to x and returns z.
+func (z *Int) SetUint64(x uint64) *Int {
+       z.abs = z.abs.setUint64(uint64(x))
+       z.neg = false
+       return z
+}
+
 // NewInt allocates and returns a new Int set to x.
 func NewInt(x int64) *Int {
        return new(Int).SetInt64(x)
@@ -519,6 +526,19 @@ func (x *Int) Int64() int64 {
        return v
 }
 
+// Uint64 returns the int64 representation of x.
+// If x cannot be represented in an uint64, the result is undefined.
+func (x *Int) Uint64() uint64 {
+       if len(x.abs) == 0 {
+               return 0
+       }
+       v := uint64(x.abs[0])
+       if _W == 32 && len(x.abs) > 1 {
+               v |= uint64(x.abs[1]) << 32
+       }
+       return v
+}
+
 // SetString sets z to the value of s, interpreted in the given base,
 // and returns z and a boolean indicating success. If SetString fails,
 // the value of z is undefined but the returned value is nil.
index d3c5a0e..fd6d152 100644 (file)
@@ -1135,6 +1135,36 @@ func TestInt64(t *testing.T) {
        }
 }
 
+var uint64Tests = []uint64{
+       0,
+       1,
+       4294967295,
+       4294967296,
+       8589934591,
+       8589934592,
+       9223372036854775807,
+       9223372036854775808,
+       18446744073709551615, // 1<<64 - 1
+}
+
+func TestUint64(t *testing.T) {
+       in := new(Int)
+       for i, testVal := range uint64Tests {
+               in.SetUint64(testVal)
+               out := in.Uint64()
+
+               if out != testVal {
+                       t.Errorf("#%d got %d want %d", i, out, testVal)
+               }
+
+               str := fmt.Sprint(testVal)
+               strOut := in.String()
+               if strOut != str {
+                       t.Errorf("#%d.String got %s want %s", i, strOut, str)
+               }
+       }
+}
+
 var bitwiseTests = []struct {
        x, y                 string
        and, or, xor, andNot string
index 13a623a..9d09f97 100644 (file)
@@ -826,7 +826,7 @@ func (x nat) string(charset string) string {
 
 // Convert words of q to base b digits in s. If q is large, it is recursively "split in half"
 // by nat/nat division using tabulated divisors. Otherwise, it is converted iteratively using
-// repeated nat/Word divison.
+// repeated nat/Word division.
 //
 // The iterative method processes n Words by n divW() calls, each of which visits every Word in the
 // incrementally shortened q for a total of n + (n-1) + (n-2) ... + 2 + 1, or n(n+1)/2 divW()'s.
index 3d2cec6..d880ec2 100644 (file)
@@ -26,5 +26,6 @@ func Log2(x float64) float64 {
 }
 
 func log2(x float64) float64 {
-       return Log(x) * (1 / Ln2)
+       frac, exp := Frexp(x)
+       return Log(frac)*(1/Ln2) + float64(exp)
 }
diff --git a/libgo/go/net/cgo_openbsd.go b/libgo/go/net/cgo_openbsd.go
new file mode 100644 (file)
index 0000000..aeaf8e5
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+/*
+#include <netdb.h>
+*/
+import "C"
+
+func cgoAddrInfoFlags() C.int {
+       return C.AI_CANONNAME
+}
index 69daedc..a4d96a8 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin freebsd linux netbsd
+// +build darwin freebsd linux netbsd openbsd
 
 package net
 
index 037ce80..f733a81 100644 (file)
@@ -17,8 +17,8 @@ var connTests = []struct {
        addr string
 }{
        {"tcp", "127.0.0.1:0"},
-       {"unix", "/tmp/gotest.net"},
-       {"unixpacket", "/tmp/gotest.net"},
+       {"unix", "/tmp/gotest.net1"},
+       {"unixpacket", "/tmp/gotest.net2"},
 }
 
 func TestConnAndListener(t *testing.T) {
@@ -41,7 +41,13 @@ func TestConnAndListener(t *testing.T) {
                        return
                }
                ln.Addr()
-               defer ln.Close()
+               defer func(ln net.Listener, net, addr string) {
+                       ln.Close()
+                       switch net {
+                       case "unix", "unixpacket":
+                               os.Remove(addr)
+                       }
+               }(ln, tt.net, tt.addr)
 
                done := make(chan int)
                go transponder(t, ln, done)
@@ -68,10 +74,6 @@ func TestConnAndListener(t *testing.T) {
                }
 
                <-done
-               switch tt.net {
-               case "unix", "unixpacket":
-                       os.Remove(tt.addr)
-               }
        }
 }
 
index 0c46084..c1eb983 100644 (file)
@@ -238,7 +238,7 @@ func ListenPacket(net, laddr string) (PacketConn, error) {
                if a != nil {
                        la = a.(*UnixAddr)
                }
-               return DialUnix(net, la, nil)
+               return ListenUnixgram(net, la)
        }
        return nil, UnknownNetworkError(net)
 }
index 865dd62..f30dee3 100644 (file)
@@ -240,7 +240,8 @@ func TestDialTimeoutFDLeak(t *testing.T) {
                err  error
        }
        dials := listenerBacklog + 100
-       maxGoodConnect := listenerBacklog + 5 // empirically 131 good ones (of 128). who knows?
+       // used to be listenerBacklog + 5, but was found to be unreliable, issue 4384.
+       maxGoodConnect := listenerBacklog + runtime.NumCPU()*10
        resc := make(chan connErr)
        for i := 0; i < dials; i++ {
                go func() {
index 2f957d2..5ee0804 100644 (file)
@@ -98,7 +98,9 @@ func (c *Client) send(req *Request) (*Response, error) {
                return nil, err
        }
        if c.Jar != nil {
-               c.Jar.SetCookies(req.URL, resp.Cookies())
+               if rc := resp.Cookies(); len(rc) > 0 {
+                       c.Jar.SetCookies(req.URL, rc)
+               }
        }
        return resp, err
 }
@@ -120,7 +122,10 @@ func (c *Client) send(req *Request) (*Response, error) {
 // Generally Get, Post, or PostForm will be used instead of Do.
 func (c *Client) Do(req *Request) (resp *Response, err error) {
        if req.Method == "GET" || req.Method == "HEAD" {
-               return c.doFollowingRedirects(req)
+               return c.doFollowingRedirects(req, shouldRedirectGet)
+       }
+       if req.Method == "POST" || req.Method == "PUT" {
+               return c.doFollowingRedirects(req, shouldRedirectPost)
        }
        return c.send(req)
 }
@@ -166,7 +171,7 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) {
 
 // True if the specified HTTP status code is one for which the Get utility should
 // automatically redirect.
-func shouldRedirect(statusCode int) bool {
+func shouldRedirectGet(statusCode int) bool {
        switch statusCode {
        case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
                return true
@@ -174,6 +179,16 @@ func shouldRedirect(statusCode int) bool {
        return false
 }
 
+// True if the specified HTTP status code is one for which the Post utility should
+// automatically redirect.
+func shouldRedirectPost(statusCode int) bool {
+       switch statusCode {
+       case StatusFound, StatusSeeOther:
+               return true
+       }
+       return false
+}
+
 // Get issues a GET to the specified URL.  If the response is one of the following
 // redirect codes, Get follows the redirect, up to a maximum of 10 redirects:
 //
@@ -214,12 +229,10 @@ func (c *Client) Get(url string) (resp *Response, err error) {
        if err != nil {
                return nil, err
        }
-       return c.doFollowingRedirects(req)
+       return c.doFollowingRedirects(req, shouldRedirectGet)
 }
 
-func (c *Client) doFollowingRedirects(ireq *Request) (resp *Response, err error) {
-       // TODO: if/when we add cookie support, the redirected request shouldn't
-       // necessarily supply the same cookies as the original.
+func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bool) (resp *Response, err error) {
        var base *url.URL
        redirectChecker := c.CheckRedirect
        if redirectChecker == nil {
@@ -238,6 +251,9 @@ func (c *Client) doFollowingRedirects(ireq *Request) (resp *Response, err error)
                if redirect != 0 {
                        req = new(Request)
                        req.Method = ireq.Method
+                       if ireq.Method == "POST" || ireq.Method == "PUT" {
+                               req.Method = "GET"
+                       }
                        req.Header = make(Header)
                        req.URL, err = base.Parse(urlStr)
                        if err != nil {
@@ -321,7 +337,7 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Respon
                return nil, err
        }
        req.Header.Set("Content-Type", bodyType)
-       return c.send(req)
+       return c.doFollowingRedirects(req, shouldRedirectPost)
 }
 
 // PostForm issues a POST to the specified URL, with data's keys and
@@ -371,5 +387,5 @@ func (c *Client) Head(url string) (resp *Response, err error) {
        if err != nil {
                return nil, err
        }
-       return c.doFollowingRedirects(req)
+       return c.doFollowingRedirects(req, shouldRedirectGet)
 }
index f4ba6a9..9514a4b 100644 (file)
@@ -7,6 +7,7 @@
 package http_test
 
 import (
+       "bytes"
        "crypto/tls"
        "crypto/x509"
        "errors"
@@ -246,6 +247,52 @@ func TestRedirects(t *testing.T) {
        }
 }
 
+func TestPostRedirects(t *testing.T) {
+       var log struct {
+               sync.Mutex
+               bytes.Buffer
+       }
+       var ts *httptest.Server
+       ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               log.Lock()
+               fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI)
+               log.Unlock()
+               if v := r.URL.Query().Get("code"); v != "" {
+                       code, _ := strconv.Atoi(v)
+                       if code/100 == 3 {
+                               w.Header().Set("Location", ts.URL)
+                       }
+                       w.WriteHeader(code)
+               }
+       }))
+       tests := []struct {
+               suffix string
+               want   int // response code
+       }{
+               {"/", 200},
+               {"/?code=301", 301},
+               {"/?code=302", 200},
+               {"/?code=303", 200},
+               {"/?code=404", 404},
+       }
+       for _, tt := range tests {
+               res, err := Post(ts.URL+tt.suffix, "text/plain", strings.NewReader("Some content"))
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if res.StatusCode != tt.want {
+                       t.Errorf("POST %s: status code = %d; want %d", tt.suffix, res.StatusCode, tt.want)
+               }
+       }
+       log.Lock()
+       got := log.String()
+       log.Unlock()
+       want := "POST / POST /?code=301 POST /?code=302 GET / POST /?code=303 GET / POST /?code=404 "
+       if got != want {
+               t.Errorf("Log differs.\n Got: %q\nWant: %q", got, want)
+       }
+}
+
 var expectedCookies = []*Cookie{
        {Name: "ChocolateChip", Value: "tasty"},
        {Name: "First", Value: "Hit"},
@@ -304,6 +351,9 @@ type TestJar struct {
 func (j *TestJar) SetCookies(u *url.URL, cookies []*Cookie) {
        j.m.Lock()
        defer j.m.Unlock()
+       if j.perURL == nil {
+               j.perURL = make(map[string][]*Cookie)
+       }
        j.perURL[u.Host] = cookies
 }
 
@@ -334,8 +384,9 @@ func TestRedirectCookiesJar(t *testing.T) {
        var ts *httptest.Server
        ts = httptest.NewServer(echoCookiesRedirectHandler)
        defer ts.Close()
-       c := &Client{}
-       c.Jar = &TestJar{perURL: make(map[string][]*Cookie)}
+       c := &Client{
+               Jar: new(TestJar),
+       }
        u, _ := url.Parse(ts.URL)
        c.Jar.SetCookies(u, []*Cookie{expectedCookies[0]})
        resp, err := c.Get(ts.URL)
@@ -364,6 +415,69 @@ func matchReturnedCookies(t *testing.T, expected, given []*Cookie) {
        }
 }
 
+func TestJarCalls(t *testing.T) {
+       ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               pathSuffix := r.RequestURI[1:]
+               if r.RequestURI == "/nosetcookie" {
+                       return // dont set cookies for this path
+               }
+               SetCookie(w, &Cookie{Name: "name" + pathSuffix, Value: "val" + pathSuffix})
+               if r.RequestURI == "/" {
+                       Redirect(w, r, "http://secondhost.fake/secondpath", 302)
+               }
+       }))
+       defer ts.Close()
+       jar := new(RecordingJar)
+       c := &Client{
+               Jar: jar,
+               Transport: &Transport{
+                       Dial: func(_ string, _ string) (net.Conn, error) {
+                               return net.Dial("tcp", ts.Listener.Addr().String())
+                       },
+               },
+       }
+       _, err := c.Get("http://firsthost.fake/")
+       if err != nil {
+               t.Fatal(err)
+       }
+       _, err = c.Get("http://firsthost.fake/nosetcookie")
+       if err != nil {
+               t.Fatal(err)
+       }
+       got := jar.log.String()
+       want := `Cookies("http://firsthost.fake/")
+SetCookie("http://firsthost.fake/", [name=val])
+Cookies("http://secondhost.fake/secondpath")
+SetCookie("http://secondhost.fake/secondpath", [namesecondpath=valsecondpath])
+Cookies("http://firsthost.fake/nosetcookie")
+`
+       if got != want {
+               t.Errorf("Got Jar calls:\n%s\nWant:\n%s", got, want)
+       }
+}
+
+// RecordingJar keeps a log of calls made to it, without
+// tracking any cookies.
+type RecordingJar struct {
+       mu  sync.Mutex
+       log bytes.Buffer
+}
+
+func (j *RecordingJar) SetCookies(u *url.URL, cookies []*Cookie) {
+       j.logf("SetCookie(%q, %v)\n", u, cookies)
+}
+
+func (j *RecordingJar) Cookies(u *url.URL) []*Cookie {
+       j.logf("Cookies(%q)\n", u)
+       return nil
+}
+
+func (j *RecordingJar) logf(format string, args ...interface{}) {
+       j.mu.Lock()
+       defer j.mu.Unlock()
+       fmt.Fprintf(&j.log, format, args...)
+}
+
 func TestStreamingGet(t *testing.T) {
        say := make(chan string)
        ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
index d70bf4e..0c03e5b 100644 (file)
@@ -34,9 +34,8 @@
 //
 //     go tool pprof http://localhost:6060/debug/pprof/block
 //
-// Or to view all available profiles:
-//
-//     go tool pprof http://localhost:6060/debug/pprof/
+// To view all available profiles, open http://localhost:6060/debug/pprof/
+// in your browser.
 //
 // For a study of the facility in action, visit
 //
index 2e03c65..ffdd6a8 100644 (file)
@@ -247,6 +247,54 @@ var reqTests = []reqTest{
                noTrailer,
                noError,
        },
+
+       // SSDP Notify request. golang.org/issue/3692
+       {
+               "NOTIFY * HTTP/1.1\r\nServer: foo\r\n\r\n",
+               &Request{
+                       Method: "NOTIFY",
+                       URL: &url.URL{
+                               Path: "*",
+                       },
+                       Proto:      "HTTP/1.1",
+                       ProtoMajor: 1,
+                       ProtoMinor: 1,
+                       Header: Header{
+                               "Server": []string{"foo"},
+                       },
+                       Close:         false,
+                       ContentLength: 0,
+                       RequestURI:    "*",
+               },
+
+               noBody,
+               noTrailer,
+               noError,
+       },
+
+       // OPTIONS request. Similar to golang.org/issue/3692
+       {
+               "OPTIONS * HTTP/1.1\r\nServer: foo\r\n\r\n",
+               &Request{
+                       Method: "OPTIONS",
+                       URL: &url.URL{
+                               Path: "*",
+                       },
+                       Proto:      "HTTP/1.1",
+                       ProtoMajor: 1,
+                       ProtoMinor: 1,
+                       Header: Header{
+                               "Server": []string{"foo"},
+                       },
+                       Close:         false,
+                       ContentLength: 0,
+                       RequestURI:    "*",
+               },
+
+               noBody,
+               noTrailer,
+               noError,
+       },
 }
 
 func TestReadRequest(t *testing.T) {
index 8ca227f..1de4171 100644 (file)
@@ -918,15 +918,19 @@ func TestZeroLengthPostAndResponse(t *testing.T) {
        }
 }
 
+func TestHandlerPanicNil(t *testing.T) {
+       testHandlerPanic(t, false, nil)
+}
+
 func TestHandlerPanic(t *testing.T) {
-       testHandlerPanic(t, false)
+       testHandlerPanic(t, false, "intentional death for testing")
 }
 
 func TestHandlerPanicWithHijack(t *testing.T) {
-       testHandlerPanic(t, true)
+       testHandlerPanic(t, true, "intentional death for testing")
 }
 
-func testHandlerPanic(t *testing.T, withHijack bool) {
+func testHandlerPanic(t *testing.T, withHijack bool, panicValue interface{}) {
        // Unlike the other tests that set the log output to ioutil.Discard
        // to quiet the output, this test uses a pipe.  The pipe serves three
        // purposes:
@@ -955,7 +959,7 @@ func testHandlerPanic(t *testing.T, withHijack bool) {
                        }
                        defer rwc.Close()
                }
-               panic("intentional death for testing")
+               panic(panicValue)
        }))
        defer ts.Close()
 
@@ -968,7 +972,7 @@ func testHandlerPanic(t *testing.T, withHijack bool) {
                _, err := pr.Read(buf)
                pr.Close()
                if err != nil {
-                       t.Fatal(err)
+                       t.Error(err)
                }
                done <- true
        }()
@@ -978,6 +982,10 @@ func testHandlerPanic(t *testing.T, withHijack bool) {
                t.Logf("expected an error")
        }
 
+       if panicValue == nil {
+               return
+       }
+
        select {
        case <-done:
                return
@@ -1288,6 +1296,58 @@ For:
        ts.Close()
 }
 
+func TestOptions(t *testing.T) {
+       uric := make(chan string, 2) // only expect 1, but leave space for 2
+       mux := NewServeMux()
+       mux.HandleFunc("/", func(w ResponseWriter, r *Request) {
+               uric <- r.RequestURI
+       })
+       ts := httptest.NewServer(mux)
+       defer ts.Close()
+
+       conn, err := net.Dial("tcp", ts.Listener.Addr().String())
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer conn.Close()
+
+       // An OPTIONS * request should succeed.
+       _, err = conn.Write([]byte("OPTIONS * HTTP/1.1\r\nHost: foo.com\r\n\r\n"))
+       if err != nil {
+               t.Fatal(err)
+       }
+       br := bufio.NewReader(conn)
+       res, err := ReadResponse(br, &Request{Method: "OPTIONS"})
+       if err != nil {
+               t.Fatal(err)
+       }
+       if res.StatusCode != 200 {
+               t.Errorf("Got non-200 response to OPTIONS *: %#v", res)
+       }
+
+       // A GET * request on a ServeMux should fail.
+       _, err = conn.Write([]byte("GET * HTTP/1.1\r\nHost: foo.com\r\n\r\n"))
+       if err != nil {
+               t.Fatal(err)
+       }
+       res, err = ReadResponse(br, &Request{Method: "GET"})
+       if err != nil {
+               t.Fatal(err)
+       }
+       if res.StatusCode != 400 {
+               t.Errorf("Got non-400 response to GET *: %#v", res)
+       }
+
+       res, err = Get(ts.URL + "/second")
+       if err != nil {
+               t.Fatal(err)
+       }
+       res.Body.Close()
+       if got := <-uric; got != "/second" {
+               t.Errorf("Handler saw request for %q; want /second", got)
+       }
+}
+
 // goTimeout runs f, failing t if f takes more than ns to complete.
 func goTimeout(t *testing.T, d time.Duration, f func()) {
        ch := make(chan bool, 2)
index c4ddbec..89a46f0 100644 (file)
@@ -702,24 +702,19 @@ func (c *conn) closeWriteAndWait() {
 // Serve a new connection.
 func (c *conn) serve() {
        defer func() {
-               err := recover()
-               if err == nil {
-                       return
+               if err := recover(); err != nil {
+                       const size = 4096
+                       buf := make([]byte, size)
+                       buf = buf[:runtime.Stack(buf, false)]
+                       log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
                }
-
-               const size = 4096
-               buf := make([]byte, size)
-               buf = buf[:runtime.Stack(buf, false)]
-               log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
-
-               if c.rwc != nil { // may be nil if connection hijacked
-                       c.rwc.Close()
+               if !c.hijacked() {
+                       c.close()
                }
        }()
 
        if tlsConn, ok := c.rwc.(*tls.Conn); ok {
                if err := tlsConn.Handshake(); err != nil {
-                       c.close()
                        return
                }
                c.tlsState = new(tls.ConnectionState)
@@ -770,6 +765,9 @@ func (c *conn) serve() {
                if handler == nil {
                        handler = DefaultServeMux
                }
+               if req.RequestURI == "*" && req.Method == "OPTIONS" {
+                       handler = globalOptionsHandler{}
+               }
 
                // HTTP cannot have multiple simultaneous active requests.[*]
                // Until the server replies to this request, it can't read another,
@@ -788,7 +786,6 @@ func (c *conn) serve() {
                        break
                }
        }
-       c.close()
 }
 
 func (w *response) sendExpectationFailed() {
@@ -1085,6 +1082,11 @@ func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
 // ServeHTTP dispatches the request to the handler whose
 // pattern most closely matches the request URL.
 func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
+       if r.RequestURI == "*" {
+               w.Header().Set("Connection", "close")
+               w.WriteHeader(StatusBadRequest)
+               return
+       }
        h, _ := mux.Handler(r)
        h.ServeHTTP(w, r)
 }
@@ -1408,6 +1410,22 @@ func (tw *timeoutWriter) WriteHeader(code int) {
        tw.w.WriteHeader(code)
 }
 
+// globalOptionsHandler responds to "OPTIONS *" requests.
+type globalOptionsHandler struct{}
+
+func (globalOptionsHandler) ServeHTTP(w ResponseWriter, r *Request) {
+       w.Header().Set("Content-Length", "0")
+       if r.ContentLength != 0 {
+               // Read up to 4KB of OPTIONS body (as mentioned in the
+               // spec as being reserved for future use), but anything
+               // over that is considered a waste of server resources
+               // (or an attack) and we abort and close the connection,
+               // courtesy of MaxBytesReader's EOF behavior.
+               mb := MaxBytesReader(w, r.Body, 4<<10)
+               io.Copy(ioutil.Discard, mb)
+       }
+}
+
 // loggingConn is used for debugging.
 type loggingConn struct {
        name string
index 7b4afeb..d0505bf 100644 (file)
@@ -144,6 +144,9 @@ func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) {
                }
                return rt.RoundTrip(req)
        }
+       if req.URL.Host == "" {
+               return nil, errors.New("http: no Host in request URL")
+       }
        treq := &transportRequest{Request: req}
        cm, err := t.connectMethodForRequest(treq)
        if err != nil {
@@ -739,6 +742,7 @@ WaitResponse:
                case err := <-writeErrCh:
                        if err != nil {
                                re = responseAndError{nil, err}
+                               pc.close()
                                break WaitResponse
                        }
                case <-pconnDeadCh:
index f1d4158..c37ef13 100644 (file)
@@ -778,6 +778,45 @@ func TestTransportPersistConnLeak(t *testing.T) {
        }
 }
 
+// golang.org/issue/4531: Transport leaks goroutines when
+// request.ContentLength is explicitly short
+func TestTransportPersistConnLeakShortBody(t *testing.T) {
+       ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+       }))
+       defer ts.Close()
+
+       tr := &Transport{}
+       c := &Client{Transport: tr}
+
+       n0 := runtime.NumGoroutine()
+       body := []byte("Hello")
+       for i := 0; i < 20; i++ {
+               req, err := NewRequest("POST", ts.URL, bytes.NewReader(body))
+               if err != nil {
+                       t.Fatal(err)
+               }
+               req.ContentLength = int64(len(body) - 2) // explicitly short
+               _, err = c.Do(req)
+               if err == nil {
+                       t.Fatal("Expect an error from writing too long of a body.")
+               }
+       }
+       nhigh := runtime.NumGoroutine()
+       tr.CloseIdleConnections()
+       time.Sleep(50 * time.Millisecond)
+       runtime.GC()
+       nfinal := runtime.NumGoroutine()
+
+       growth := nfinal - n0
+
+       // We expect 0 or 1 extra goroutine, empirically.  Allow up to 5.
+       // Previously we were leaking one per numReq.
+       t.Logf("goroutine growth: %d -> %d -> %d (delta: %d)", n0, nhigh, nfinal, growth)
+       if int(growth) > 5 {
+               t.Error("too many new goroutines")
+       }
+}
+
 // This used to crash; http://golang.org/issue/3266
 func TestTransportIdleConnCrash(t *testing.T) {
        tr := &Transport{}
@@ -1062,6 +1101,20 @@ func TestTransportAltProto(t *testing.T) {
        }
 }
 
+func TestTransportNoHost(t *testing.T) {
+       tr := &Transport{}
+       _, err := tr.RoundTrip(&Request{
+               Header: make(Header),
+               URL: &url.URL{
+                       Scheme: "http",
+               },
+       })
+       want := "http: no Host in request URL"
+       if got := fmt.Sprint(err); got != want {
+               t.Errorf("error = %v; want %q", err, want)
+       }
+}
+
 var proxyFromEnvTests = []struct {
        env     string
        wanturl string
index 5075baa..ff29e24 100644 (file)
@@ -24,6 +24,15 @@ var packetConnTests = []struct {
 }
 
 func TestPacketConn(t *testing.T) {
+       closer := func(c net.PacketConn, net, addr1, addr2 string) {
+               c.Close()
+               switch net {
+               case "unixgram":
+                       os.Remove(addr1)
+                       os.Remove(addr2)
+               }
+       }
+
        for _, tt := range packetConnTests {
                var wb []byte
                netstr := strings.Split(tt.net, ":")
@@ -39,7 +48,7 @@ func TestPacketConn(t *testing.T) {
                                continue
                        }
                        id := os.Getpid() & 0xffff
-                       wb = newICMPEchoRequest(id, 1, 128, []byte("IP PACKETCONN TEST "))
+                       wb = newICMPEchoRequest(id, 1, 128, []byte("IP PACKETCONN TEST"))
                case "unixgram":
                        switch runtime.GOOS {
                        case "plan9", "windows":
@@ -60,7 +69,7 @@ func TestPacketConn(t *testing.T) {
                c1.SetDeadline(time.Now().Add(100 * time.Millisecond))
                c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
                c1.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
-               defer c1.Close()
+               defer closer(c1, netstr[0], tt.addr1, tt.addr2)
 
                c2, err := net.ListenPacket(tt.net, tt.addr2)
                if err != nil {
@@ -70,7 +79,7 @@ func TestPacketConn(t *testing.T) {
                c2.SetDeadline(time.Now().Add(100 * time.Millisecond))
                c2.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
                c2.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
-               defer c2.Close()
+               defer closer(c2, netstr[0], tt.addr1, tt.addr2)
 
                if _, err := c1.WriteTo(wb, c2.LocalAddr()); err != nil {
                        t.Fatalf("net.PacketConn.WriteTo failed: %v", err)
@@ -86,12 +95,6 @@ func TestPacketConn(t *testing.T) {
                if _, _, err := c1.ReadFrom(rb1); err != nil {
                        t.Fatalf("net.PacketConn.ReadFrom failed: %v", err)
                }
-
-               switch netstr[0] {
-               case "unixgram":
-                       os.Remove(tt.addr1)
-                       os.Remove(tt.addr2)
-               }
        }
 }
 
index f249372..d99de3f 100644 (file)
@@ -263,9 +263,10 @@ func TestUnixConnSpecificMethods(t *testing.T) {
                return
        }
 
-       p1, p2 := "/tmp/gotest.net1", "/tmp/gotest.net2"
+       p1, p2, p3 := "/tmp/gotest.net1", "/tmp/gotest.net2", "/tmp/gotest.net3"
        os.Remove(p1)
        os.Remove(p2)
+       os.Remove(p3)
 
        a1, err := net.ResolveUnixAddr("unixgram", p1)
        if err != nil {
@@ -305,9 +306,30 @@ func TestUnixConnSpecificMethods(t *testing.T) {
        defer c2.Close()
        defer os.Remove(p2)
 
+       a3, err := net.ResolveUnixAddr("unixgram", p3)
+       if err != nil {
+               t.Errorf("net.ResolveUnixAddr failed: %v", err)
+               return
+       }
+       c3, err := net.ListenUnixgram("unixgram", a3)
+       if err != nil {
+               t.Errorf("net.ListenUnixgram failed: %v", err)
+               return
+       }
+       c3.LocalAddr()
+       c3.RemoteAddr()
+       c3.SetDeadline(time.Now().Add(100 * time.Millisecond))
+       c3.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+       c3.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+       c3.SetReadBuffer(2048)
+       c3.SetWriteBuffer(2048)
+       defer c3.Close()
+       defer os.Remove(p3)
+
        wb := []byte("UNIXCONN TEST")
        rb1 := make([]byte, 128)
        rb2 := make([]byte, 128)
+       rb3 := make([]byte, 128)
        if _, _, err := c1.WriteMsgUnix(wb, nil, a2); err != nil {
                t.Errorf("net.UnixConn.WriteMsgUnix failed: %v", err)
                return
@@ -324,9 +346,22 @@ func TestUnixConnSpecificMethods(t *testing.T) {
                t.Errorf("net.UnixConn.ReadFromUnix failed: %v", err)
                return
        }
-
-       // TODO: http://golang.org/issue/3875
-       net.ListenUnixgram("unixgram", nil)
+       if _, err := c3.WriteToUnix(wb, a1); err != nil {
+               t.Errorf("net.UnixConn.WriteToUnix failed: %v", err)
+               return
+       }
+       if _, _, err := c1.ReadFromUnix(rb1); err != nil {
+               t.Errorf("net.UnixConn.ReadFromUnix failed: %v", err)
+               return
+       }
+       if _, err := c2.WriteToUnix(wb, a3); err != nil {
+               t.Errorf("net.UnixConn.WriteToUnix failed: %v", err)
+               return
+       }
+       if _, _, err := c3.ReadFromUnix(rb3); err != nil {
+               t.Errorf("net.UnixConn.ReadFromUnix failed: %v", err)
+               return
+       }
 
        if f, err := c1.File(); err != nil {
                t.Errorf("net.UnixConn.File failed: %v", err)
index 59f6449..4b91778 100644 (file)
@@ -13,6 +13,7 @@ package smtp
 import (
        "crypto/tls"
        "encoding/base64"
+       "errors"
        "io"
        "net"
        "net/textproto"
@@ -33,7 +34,10 @@ type Client struct {
        // map of supported extensions
        ext map[string]string
        // supported auth mechanisms
-       auth []string
+       auth       []string
+       localName  string // the name to use in HELO/EHLO
+       didHello   bool   // whether we've said HELO/EHLO
+       helloError error  // the error from the hello
 }
 
 // Dial returns a new Client connected to an SMTP server at addr.
@@ -55,12 +59,33 @@ func NewClient(conn net.Conn, host string) (*Client, error) {
                text.Close()
                return nil, err
        }
-       c := &Client{Text: text, conn: conn, serverName: host}
-       err = c.ehlo()
-       if err != nil {
-               err = c.helo()
+       c := &Client{Text: text, conn: conn, serverName: host, localName: "localhost"}
+       return c, nil
+}
+
+// hello runs a hello exchange if needed.
+func (c *Client) hello() error {
+       if !c.didHello {
+               c.didHello = true
+               err := c.ehlo()
+               if err != nil {
+                       c.helloError = c.helo()
+               }
+       }
+       return c.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
index c315d18..8317428 100644 (file)
@@ -69,14 +69,14 @@ func (f faker) SetReadDeadline(time.Time) error  { return nil }
 func (f faker) SetWriteDeadline(time.Time) error { return nil }
 
 func TestBasic(t *testing.T) {
-       basicServer = strings.Join(strings.Split(basicServer, "\n"), "\r\n")
-       basicClient = strings.Join(strings.Split(basicClient, "\n"), "\r\n")
+       server := strings.Join(strings.Split(basicServer, "\n"), "\r\n")
+       client := strings.Join(strings.Split(basicClient, "\n"), "\r\n")
 
        var cmdbuf bytes.Buffer
        bcmdbuf := bufio.NewWriter(&cmdbuf)
        var fake faker
-       fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(basicServer)), bcmdbuf)
-       c := &Client{Text: textproto.NewConn(fake)}
+       fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
+       c := &Client{Text: textproto.NewConn(fake), localName: "localhost"}
 
        if err := c.helo(); err != nil {
                t.Fatalf("HELO failed: %s", err)
@@ -88,6 +88,7 @@ func TestBasic(t *testing.T) {
                t.Fatalf("Second EHLO failed: %s", err)
        }
 
+       c.didHello = true
        if ok, args := c.Extension("aUtH"); !ok || args != "LOGIN PLAIN" {
                t.Fatalf("Expected AUTH supported")
        }
@@ -143,8 +144,8 @@ Goodbye.`
 
        bcmdbuf.Flush()
        actualcmds := cmdbuf.String()
-       if basicClient != actualcmds {
-               t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, basicClient)
+       if client != actualcmds {
+               t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
        }
 }
 
@@ -187,8 +188,8 @@ QUIT
 `
 
 func TestNewClient(t *testing.T) {
-       newClientServer = strings.Join(strings.Split(newClientServer, "\n"), "\r\n")
-       newClientClient = strings.Join(strings.Split(newClientClient, "\n"), "\r\n")
+       server := strings.Join(strings.Split(newClientServer, "\n"), "\r\n")
+       client := strings.Join(strings.Split(newClientClient, "\n"), "\r\n")
 
        var cmdbuf bytes.Buffer
        bcmdbuf := bufio.NewWriter(&cmdbuf)
@@ -197,7 +198,7 @@ func TestNewClient(t *testing.T) {
                return cmdbuf.String()
        }
        var fake faker
-       fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(newClientServer)), bcmdbuf)
+       fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
        c, err := NewClient(fake, "fake.host")
        if err != nil {
                t.Fatalf("NewClient: %v\n(after %v)", err, out())
@@ -213,8 +214,8 @@ func TestNewClient(t *testing.T) {
        }
 
        actualcmds := out()
-       if newClientClient != actualcmds {
-               t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, newClientClient)
+       if client != actualcmds {
+               t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
        }
 }
 
@@ -231,13 +232,13 @@ QUIT
 `
 
 func TestNewClient2(t *testing.T) {
-       newClient2Server = strings.Join(strings.Split(newClient2Server, "\n"), "\r\n")
-       newClient2Client = strings.Join(strings.Split(newClient2Client, "\n"), "\r\n")
+       server := strings.Join(strings.Split(newClient2Server, "\n"), "\r\n")
+       client := strings.Join(strings.Split(newClient2Client, "\n"), "\r\n")
 
        var cmdbuf bytes.Buffer
        bcmdbuf := bufio.NewWriter(&cmdbuf)
        var fake faker
-       fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(newClient2Server)), bcmdbuf)
+       fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
        c, err := NewClient(fake, "fake.host")
        if err != nil {
                t.Fatalf("NewClient: %v", err)
@@ -251,8 +252,8 @@ func TestNewClient2(t *testing.T) {
 
        bcmdbuf.Flush()
        actualcmds := cmdbuf.String()
-       if newClient2Client != actualcmds {
-               t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, newClient2Client)
+       if client != actualcmds {
+               t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
        }
 }
 
@@ -269,3 +270,199 @@ var newClient2Client = `EHLO localhost
 HELO localhost
 QUIT
 `
+
+func TestHello(t *testing.T) {
+
+       if len(helloServer) != len(helloClient) {
+               t.Fatalf("Hello server and client size mismatch")
+       }
+
+       for i := 0; i < len(helloServer); i++ {
+               server := strings.Join(strings.Split(baseHelloServer+helloServer[i], "\n"), "\r\n")
+               client := strings.Join(strings.Split(baseHelloClient+helloClient[i], "\n"), "\r\n")
+               var cmdbuf bytes.Buffer
+               bcmdbuf := bufio.NewWriter(&cmdbuf)
+               var fake faker
+               fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
+               c, err := NewClient(fake, "fake.host")
+               if err != nil {
+                       t.Fatalf("NewClient: %v", err)
+               }
+               c.localName = "customhost"
+               err = nil
+
+               switch i {
+               case 0:
+                       err = c.Hello("customhost")
+               case 1:
+                       err = c.StartTLS(nil)
+                       if err.Error() == "502 Not implemented" {
+                               err = nil
+                       }
+               case 2:
+                       err = c.Verify("test@example.com")
+               case 3:
+                       c.tls = true
+                       c.serverName = "smtp.google.com"
+                       err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com"))
+               case 4:
+                       err = c.Mail("test@example.com")
+               case 5:
+                       ok, _ := c.Extension("feature")
+                       if ok {
+                               t.Errorf("Expected FEATURE not to be supported")
+                       }
+               case 6:
+                       err = c.Reset()
+               case 7:
+                       err = c.Quit()
+               case 8:
+                       err = c.Verify("test@example.com")
+                       if err != nil {
+                               err = c.Hello("customhost")
+                               if err != nil {
+                                       t.Errorf("Want error, got none")
+                               }
+                       }
+               default:
+                       t.Fatalf("Unhandled command")
+               }
+
+               if err != nil {
+                       t.Errorf("Command %d failed: %v", i, err)
+               }
+
+               bcmdbuf.Flush()
+               actualcmds := cmdbuf.String()
+               if client != actualcmds {
+                       t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client)
+               }
+       }
+}
+
+var baseHelloServer = `220 hello world
+502 EH?
+250-mx.google.com at your service
+250 FEATURE
+`
+
+var helloServer = []string{
+       "",
+       "502 Not implemented\n",
+       "250 User is valid\n",
+       "235 Accepted\n",
+       "250 Sender ok\n",
+       "",
+       "250 Reset ok\n",
+       "221 Goodbye\n",
+       "250 Sender ok\n",
+}
+
+var baseHelloClient = `EHLO customhost
+HELO customhost
+`
+
+var helloClient = []string{
+       "",
+       "STARTTLS\n",
+       "VRFY test@example.com\n",
+       "AUTH PLAIN AHVzZXIAcGFzcw==\n",
+       "MAIL FROM:<test@example.com>\n",
+       "",
+       "RSET\n",
+       "QUIT\n",
+       "VRFY test@example.com\n",
+}
+
+func TestSendMail(t *testing.T) {
+       server := strings.Join(strings.Split(sendMailServer, "\n"), "\r\n")
+       client := strings.Join(strings.Split(sendMailClient, "\n"), "\r\n")
+       var cmdbuf bytes.Buffer
+       bcmdbuf := bufio.NewWriter(&cmdbuf)
+       l, err := net.Listen("tcp", "127.0.0.1:0")
+       if err != nil {
+               t.Fatalf("Unable to to create listener: %v", err)
+       }
+       defer l.Close()
+
+       // prevent data race on bcmdbuf
+       var done = make(chan struct{})
+       go func(data []string) {
+
+               defer close(done)
+
+               conn, err := l.Accept()
+               if err != nil {
+                       t.Errorf("Accept error: %v", err)
+                       return
+               }
+               defer conn.Close()
+
+               tc := textproto.NewConn(conn)
+               for i := 0; i < len(data) && data[i] != ""; i++ {
+                       tc.PrintfLine(data[i])
+                       for len(data[i]) >= 4 && data[i][3] == '-' {
+                               i++
+                               tc.PrintfLine(data[i])
+                       }
+                       if data[i] == "221 Goodbye" {
+                               return
+                       }
+                       read := false
+                       for !read || data[i] == "354 Go ahead" {
+                               msg, err := tc.ReadLine()
+                               bcmdbuf.Write([]byte(msg + "\r\n"))
+                               read = true
+                               if err != nil {
+                                       t.Errorf("Read error: %v", err)
+                                       return
+                               }
+                               if data[i] == "354 Go ahead" && msg == "." {
+                                       break
+                               }
+                       }
+               }
+       }(strings.Split(server, "\r\n"))
+
+       err = SendMail(l.Addr().String(), nil, "test@example.com", []string{"other@example.com"}, []byte(strings.Replace(`From: test@example.com
+To: other@example.com
+Subject: SendMail test
+
+SendMail is working for me.
+`, "\n", "\r\n", -1)))
+
+       if err != nil {
+               t.Errorf("%v", err)
+       }
+
+       <-done
+       bcmdbuf.Flush()
+       actualcmds := cmdbuf.String()
+       if client != actualcmds {
+               t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client)
+       }
+}
+
+var sendMailServer = `220 hello world
+502 EH?
+250 mx.google.com at your service
+250 Sender ok
+250 Receiver ok
+354 Go ahead
+250 Data ok
+221 Goodbye
+`
+
+var sendMailClient = `EHLO localhost
+HELO localhost
+MAIL FROM:<test@example.com>
+RCPT TO:<other@example.com>
+DATA
+From: test@example.com
+To: other@example.com
+Subject: SendMail test
+
+SendMail is working for me.
+.
+QUIT
+`
index f7be5d2..713820c 100644 (file)
@@ -64,21 +64,21 @@ func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err
        return 0, 0, syscall.EPLAN9
 }
 
-// CloseRead shuts down the reading side of the Unix domain
-// connection.  Most callers should just use Close.
+// CloseRead shuts down the reading side of the Unix domain connection.
+// Most callers should just use Close.
 func (c *UnixConn) CloseRead() error {
        return syscall.EPLAN9
 }
 
-// CloseWrite shuts down the writing side of the Unix domain
-// connection.  Most callers should just use Close.
+// CloseWrite shuts down the writing side of the Unix domain connection.
+// Most callers should just use Close.
 func (c *UnixConn) CloseWrite() error {
        return syscall.EPLAN9
 }
 
 // DialUnix connects to the remote address raddr on the network net,
-// which must be "unix" or "unixgram".  If laddr is not nil, it is
-// used as the local address for the connection.
+// which must be "unix", "unixgram" or "unixpacket".  If laddr is not
+// nil, it is used as the local address for the connection.
 func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
        return dialUnix(net, laddr, raddr, noDeadline)
 }
@@ -93,7 +93,8 @@ func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn
 type UnixListener struct{}
 
 // ListenUnix announces on the Unix domain socket laddr and returns a
-// Unix listener.  Net must be "unix" (stream sockets).
+// Unix listener.  The network net must be "unix", "unixgram" or
+// "unixpacket".
 func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
        return nil, syscall.EPLAN9
 }
@@ -134,8 +135,8 @@ func (l *UnixListener) File() (*os.File, error) {
 
 // ListenUnixgram listens for incoming Unix datagram packets addressed
 // to the local address laddr.  The returned connection c's ReadFrom
-// and WriteTo methods can be used to receive and send UDP packets
-// with per-packet addressing.  The network net must be "unixgram".
-func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) {
+// and WriteTo methods can be used to receive and send packets with
+// per-packet addressing.  The network net must be "unixgram".
+func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) {
        return nil, syscall.EPLAN9
 }
index 16ebd58..653190c 100644 (file)
@@ -9,29 +9,27 @@
 package net
 
 import (
+       "errors"
        "os"
        "syscall"
        "time"
 )
 
-func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.Time) (fd *netFD, err error) {
+func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.Time) (*netFD, error) {
        var sotype int
        switch net {
-       default:
-               return nil, UnknownNetworkError(net)
        case "unix":
                sotype = syscall.SOCK_STREAM
        case "unixgram":
                sotype = syscall.SOCK_DGRAM
        case "unixpacket":
                sotype = syscall.SOCK_SEQPACKET
+       default:
+               return nil, UnknownNetworkError(net)
        }
 
        var la, ra syscall.Sockaddr
        switch mode {
-       default:
-               panic("unixSocket mode " + mode)
-
        case "dial":
                if laddr != nil {
                        la = &syscall.SockaddrUnix{Name: laddr.Name}
@@ -41,15 +39,10 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.T
                } else if sotype != syscall.SOCK_DGRAM || laddr == nil {
                        return nil, &OpError{Op: mode, Net: net, Err: errMissingAddress}
                }
-
        case "listen":
-               if laddr == nil {
-                       return nil, &OpError{mode, net, nil, errMissingAddress}
-               }
                la = &syscall.SockaddrUnix{Name: laddr.Name}
-               if raddr != nil {
-                       return nil, &OpError{Op: mode, Net: net, Addr: raddr, Err: &AddrError{Err: "unexpected remote address", Addr: raddr.String()}}
-               }
+       default:
+               return nil, errors.New("unknown mode: " + mode)
        }
 
        f := sockaddrToUnix
@@ -59,15 +52,16 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.T
                f = sockaddrToUnixpacket
        }
 
-       fd, err = socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, deadline, f)
+       fd, err := socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, deadline, f)
        if err != nil {
-               goto Error
+               goto error
        }
        return fd, nil
 
-Error:
+error:
        addr := raddr
-       if mode == "listen" {
+       switch mode {
+       case "listen":
                addr = laddr
        }
        return nil, &OpError{Op: mode, Net: net, Addr: addr, Err: err}
@@ -108,21 +102,21 @@ func sotypeToNet(sotype int) string {
        return ""
 }
 
-// UnixConn is an implementation of the Conn interface
-// for connections to Unix domain sockets.
+// UnixConn is an implementation of the Conn interface for connections
+// to Unix domain sockets.
 type UnixConn struct {
        conn
 }
 
 func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{conn{fd}} }
 
-// ReadFromUnix reads a packet from c, copying the payload into b.
-// It returns the number of bytes copied into b and the source address
-// of the packet.
+// ReadFromUnix reads a packet from c, copying the payload into b.  It
+// returns the number of bytes copied into b and the source address of
+// the packet.
 //
-// ReadFromUnix can be made to time out and return
-// an error with Timeout() == true after a fixed time limit;
-// see SetDeadline and SetReadDeadline.
+// ReadFromUnix can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetReadDeadline.
 func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) {
        if !c.ok() {
                return 0, nil, syscall.EINVAL
@@ -144,12 +138,28 @@ func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
        return n, uaddr.toAddr(), err
 }
 
+// ReadMsgUnix reads a packet from c, copying the payload into b and
+// the associated out-of-band data into oob.  It returns the number of
+// bytes copied into b, the number of bytes copied into oob, the flags
+// that were set on the packet, and the source address of the packet.
+func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
+       if !c.ok() {
+               return 0, 0, 0, nil, syscall.EINVAL
+       }
+       n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob)
+       switch sa := sa.(type) {
+       case *syscall.SockaddrUnix:
+               addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)}
+       }
+       return
+}
+
 // WriteToUnix writes a packet to addr via c, copying the payload from b.
 //
-// WriteToUnix can be made to time out and return
-// an error with Timeout() == true after a fixed time limit;
-// see SetDeadline and SetWriteDeadline.
-// On packet-oriented connections, write timeouts are rare.
+// WriteToUnix can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetWriteDeadline.  On packet-oriented connections, write timeouts
+// are rare.
 func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) {
        if !c.ok() {
                return 0, syscall.EINVAL
@@ -173,26 +183,9 @@ func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) {
        return c.WriteToUnix(b, a)
 }
 
-// ReadMsgUnix reads a packet from c, copying the payload into b
-// and the associated out-of-band data into oob.
-// It returns the number of bytes copied into b, the number of
-// bytes copied into oob, the flags that were set on the packet,
-// and the source address of the packet.
-func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
-       if !c.ok() {
-               return 0, 0, 0, nil, syscall.EINVAL
-       }
-       n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob)
-       switch sa := sa.(type) {
-       case *syscall.SockaddrUnix:
-               addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)}
-       }
-       return
-}
-
-// WriteMsgUnix writes a packet to addr via c, copying the payload from b
-// and the associated out-of-band data from oob.  It returns the number
-// of payload and out-of-band bytes written.
+// WriteMsgUnix writes a packet to addr via c, copying the payload
+// from b and the associated out-of-band data from oob.  It returns
+// the number of payload and out-of-band bytes written.
 func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
        if !c.ok() {
                return 0, 0, syscall.EINVAL
@@ -226,13 +219,18 @@ func (c *UnixConn) CloseWrite() error {
 }
 
 // DialUnix connects to the remote address raddr on the network net,
-// which must be "unix" or "unixgram".  If laddr is not nil, it is used
-// as the local address for the connection.
+// which must be "unix", "unixgram" or "unixpacket".  If laddr is not
+// nil, it is used as the local address for the connection.
 func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
        return dialUnix(net, laddr, raddr, noDeadline)
 }
 
 func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) {
+       switch net {
+       case "unix", "unixgram", "unixpacket":
+       default:
+               return nil, UnknownNetworkError(net)
+       }
        fd, err := unixSocket(net, laddr, raddr, "dial", deadline)
        if err != nil {
                return nil, err
@@ -240,22 +238,25 @@ func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn
        return newUnixConn(fd), nil
 }
 
-// UnixListener is a Unix domain socket listener.
-// Clients should typically use variables of type Listener
-// instead of assuming Unix domain sockets.
+// UnixListener is a Unix domain socket listener.  Clients should
+// typically use variables of type Listener instead of assuming Unix
+// domain sockets.
 type UnixListener struct {
        fd   *netFD
        path string
 }
 
-// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener.
-// Net must be "unix" (stream sockets).
+// ListenUnix announces on the Unix domain socket laddr and returns a
+// Unix listener.  The network net must be "unix", "unixgram" or
+// "unixpacket".
 func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
-       if net != "unix" && net != "unixgram" && net != "unixpacket" {
+       switch net {
+       case "unix", "unixgram", "unixpacket":
+       default:
                return nil, UnknownNetworkError(net)
        }
-       if laddr != nil {
-               laddr = &UnixAddr{laddr.Name, net} // make our own copy
+       if laddr == nil {
+               return nil, &OpError{"listen", net, nil, errMissingAddress}
        }
        fd, err := unixSocket(net, laddr, nil, "listen", noDeadline)
        if err != nil {
@@ -269,8 +270,8 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
        return &UnixListener{fd, laddr.Name}, nil
 }
 
-// AcceptUnix accepts the next incoming call and returns the new connection
-// and the remote address.
+// AcceptUnix accepts the next incoming call and returns the new
+// connection and the remote address.
 func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
        if l == nil || l.fd == nil {
                return nil, syscall.EINVAL
@@ -283,8 +284,8 @@ func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
        return c, nil
 }
 
-// Accept implements the Accept method in the Listener interface;
-// it waits for the next call and returns a generic Conn.
+// Accept implements the Accept method in the Listener interface; it
+// waits for the next call and returns a generic Conn.
 func (l *UnixListener) Accept() (c Conn, err error) {
        c1, err := l.AcceptUnix()
        if err != nil {
@@ -293,8 +294,8 @@ func (l *UnixListener) Accept() (c Conn, err error) {
        return c1, nil
 }
 
-// Close stops listening on the Unix address.
-// Already accepted connections are not closed.
+// Close stops listening on the Unix address.  Already accepted
+// connections are not closed.
 func (l *UnixListener) Close() error {
        if l == nil || l.fd == nil {
                return syscall.EINVAL
@@ -328,16 +329,16 @@ func (l *UnixListener) SetDeadline(t time.Time) (err error) {
        return setDeadline(l.fd, t)
 }
 
-// File returns a copy of the underlying os.File, set to blocking mode.
-// It is the caller's responsibility to close f when finished.
+// File returns a copy of the underlying os.File, set to blocking
+// mode.  It is the caller's responsibility to close f when finished.
 // Closing l does not affect f, and closing f does not affect l.
 func (l *UnixListener) File() (f *os.File, err error) { return l.fd.dup() }
 
-// ListenUnixgram listens for incoming Unix datagram packets addressed to the
-// local address laddr.  The returned connection c's ReadFrom
-// and WriteTo methods can be used to receive and send UDP
-// packets with per-packet addressing.  The network net must be "unixgram".
-func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) {
+// ListenUnixgram listens for incoming Unix datagram packets addressed
+// to the local address laddr.  The returned connection c's ReadFrom
+// and WriteTo methods can be used to receive and send packets with
+// per-packet addressing.  The network net must be "unixgram".
+func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) {
        switch net {
        case "unixgram":
        default:
@@ -350,5 +351,5 @@ func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) {
        if err != nil {
                return nil, err
        }
-       return newUDPConn(fd), nil
+       return newUnixConn(fd), nil
 }
index 692a7fd..71758fe 100644 (file)
@@ -361,6 +361,11 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) {
        }
        url = new(URL)
 
+       if rawurl == "*" {
+               url.Path = "*"
+               return
+       }
+
        // Split off possible leading "http:", "mailto:", etc.
        // Cannot contain escaped characters.
        if url.Scheme, rest, err = getscheme(rawurl); err != nil {
@@ -572,23 +577,33 @@ func resolvePath(basepath string, refpath string) string {
        if len(base) == 0 {
                base = []string{""}
        }
+
+       rm := true
        for idx, ref := range refs {
                switch {
                case ref == ".":
-                       base[len(base)-1] = ""
+                       if idx == 0 {
+                               base[len(base)-1] = ""
+                               rm = true
+                       } else {
+                               rm = false
+                       }
                case ref == "..":
                        newLen := len(base) - 1
                        if newLen < 1 {
                                newLen = 1
                        }
                        base = base[0:newLen]
-                       base[len(base)-1] = ""
+                       if rm {
+                               base[len(base)-1] = ""
+                       }
                default:
                        if idx == 0 || base[len(base)-1] == "" {
                                base[len(base)-1] = ref
                        } else {
                                base = append(base, ref)
                        }
+                       rm = false
                }
        }
        return strings.Join(base, "/")
index 64f1170..4d3545d 100644 (file)
@@ -277,7 +277,7 @@ func TestParse(t *testing.T) {
 
 const pathThatLooksSchemeRelative = "//not.a.user@not.a.host/just/a/path"
 
-var parseRequestUrlTests = []struct {
+var parseRequestURLTests = []struct {
        url           string
        expectedValid bool
 }{
@@ -289,10 +289,11 @@ var parseRequestUrlTests = []struct {
        {"//not.a.user@%66%6f%6f.com/just/a/path/also", true},
        {"foo.html", false},
        {"../dir/", false},
+       {"*", true},
 }
 
 func TestParseRequestURI(t *testing.T) {
-       for _, test := range parseRequestUrlTests {
+       for _, test := range parseRequestURLTests {
                _, err := ParseRequestURI(test.url)
                valid := err == nil
                if valid != test.expectedValid {
@@ -536,6 +537,15 @@ var resolveReferenceTests = []struct {
        {"http://foo.com/bar/baz", "../../../../../quux", "http://foo.com/quux"},
        {"http://foo.com/bar", "..", "http://foo.com/"},
        {"http://foo.com/bar/baz", "./..", "http://foo.com/"},
+       // ".." in the middle (issue 3560)
+       {"http://foo.com/bar/baz", "quux/dotdot/../tail", "http://foo.com/bar/quux/tail"},
+       {"http://foo.com/bar/baz", "quux/./dotdot/../tail", "http://foo.com/bar/quux/tail"},
+       {"http://foo.com/bar/baz", "quux/./dotdot/.././tail", "http://foo.com/bar/quux/tail"},
+       {"http://foo.com/bar/baz", "quux/./dotdot/./../tail", "http://foo.com/bar/quux/tail"},
+       {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/././../../tail", "http://foo.com/bar/quux/tail"},
+       {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/./.././../tail", "http://foo.com/bar/quux/tail"},
+       {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/dotdot/./../../.././././tail", "http://foo.com/bar/quux/tail"},
+       {"http://foo.com/bar/baz", "quux/./dotdot/../dotdot/../dot/./tail/..", "http://foo.com/bar/quux/dot"},
 
        // "." and ".." in the base aren't special
        {"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/./dotdot/../baz"},
index eb265f2..db7fc72 100644 (file)
@@ -9,7 +9,7 @@ package os
 import "syscall"
 
 // Expand replaces ${var} or $var in the string based on the mapping function.
-// Invocations of undefined variables are replaced with the empty string.
+// For example, os.ExpandEnv(s) is equivalent to os.Expand(s, os.Getenv).
 func Expand(s string, mapping func(string) string) string {
        buf := make([]byte, 0, 2*len(s))
        // ${} is all ASCII, so bytes are fine for this operation.
index 1ba3293..b979fed 100644 (file)
@@ -153,12 +153,10 @@ func (f *File) Sync() (err error) {
 // less precise time unit.
 // If there is an error, it will be of type *PathError.
 func Chtimes(name string, atime time.Time, mtime time.Time) error {
-       var utimes [2]syscall.Timeval
-       atime_ns := atime.Unix()*1e9 + int64(atime.Nanosecond())
-       mtime_ns := mtime.Unix()*1e9 + int64(mtime.Nanosecond())
-       utimes[0] = syscall.NsecToTimeval(atime_ns)
-       utimes[1] = syscall.NsecToTimeval(mtime_ns)
-       if e := syscall.Utimes(name, utimes[0:]); e != nil {
+       var utimes [2]syscall.Timespec
+       utimes[0] = syscall.NsecToTimespec(atime.UnixNano())
+       utimes[1] = syscall.NsecToTimespec(mtime.UnixNano())
+       if e := syscall.UtimesNano(name, utimes[0:]); e != nil {
                return &PathError{"chtimes", name, e}
        }
        return nil
index 7e67495..e1e2777 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin freebsd linux netbsd
+// +build darwin freebsd linux netbsd openbsd
 // +build cgo
 
 package user
index b7e4f04..a6a659c 100644 (file)
@@ -31,53 +31,52 @@ var good_re = []string{
        `\!\\`,
 }
 
-/*
 type stringError struct {
        re  string
-       err error
+       err string
 }
 
 var bad_re = []stringError{
-       {`*`, ErrBareClosure},
-       {`+`, ErrBareClosure},
-       {`?`, ErrBareClosure},
-       {`(abc`, ErrUnmatchedLpar},
-       {`abc)`, ErrUnmatchedRpar},
-       {`x[a-z`, ErrUnmatchedLbkt},
-       {`abc]`, ErrUnmatchedRbkt},
-       {`[z-a]`, ErrBadRange},
-       {`abc\`, ErrExtraneousBackslash},
-       {`a**`, ErrBadClosure},
-       {`a*+`, ErrBadClosure},
-       {`a??`, ErrBadClosure},
-       {`\x`, ErrBadBackslash},
-}
-*/
-
-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
        }
index 0bf5799..335f739 100644 (file)
@@ -42,11 +42,9 @@ const (
        ErrMissingParen          ErrorCode = "missing closing )"
        ErrMissingRepeatArgument ErrorCode = "missing argument to repetition operator"
        ErrTrailingBackslash     ErrorCode = "trailing backslash at end of expression"
+       ErrUnexpectedParen       ErrorCode = "unexpected )"
 )
 
-// TODO: Export for Go 1.1.
-const errUnexpectedParen ErrorCode = "unexpected )"
-
 func (e ErrorCode) String() string {
        return string(e)
 }
@@ -1167,13 +1165,13 @@ func (p *parser) parseRightParen() error {
 
        n := len(p.stack)
        if n < 2 {
-               return &Error{errUnexpectedParen, p.wholeRegexp}
+               return &Error{ErrUnexpectedParen, p.wholeRegexp}
        }
        re1 := p.stack[n-1]
        re2 := p.stack[n-2]
        p.stack = p.stack[:n-2]
        if re2.Op != opLeftParen {
-               return &Error{errUnexpectedParen, p.wholeRegexp}
+               return &Error{ErrUnexpectedParen, p.wholeRegexp}
        }
        // Restore flags at time of paren.
        p.flags = re2.Flags
index 8067881..1a9c41b 100644 (file)
@@ -255,7 +255,7 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
 
        // d = mant << (exp - mantbits)
        // Next highest floating point number is mant+1 << exp-mantbits.
-       // Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1.
+       // Our upper bound is halfway between, mant*2+1 << exp-mantbits-1.
        upper := new(decimal)
        upper.Assign(mant*2 + 1)
        upper.Shift(exp - int(flt.mantbits) - 1)
@@ -265,7 +265,7 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
        // unless mant-1 drops the significant bit and exp is not the minimum exp,
        // in which case the next lowest is mant*2-1 << exp-mantbits-1.
        // Either way, call it mantlo << explo-mantbits.
-       // Our lower bound is halfway inbetween, mantlo*2+1 << explo-mantbits-1.
+       // Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1.
        var mantlo uint64
        var explo int
        if mant > 1<<flt.mantbits || exp == minexp {
index 2848d9b..0f89aa9 100644 (file)
@@ -12,14 +12,17 @@ import (
 )
 
 var (
-       // envOnce guards initialization by copyenv, which populates env.
+       // envOnce guards copyenv, which populates env.
        envOnce sync.Once
 
        // envLock guards env.
        envLock sync.RWMutex
 
        // env maps from an environment variable to its value.
-       env map[string]string
+       env = make(map[string]string)
+
+       errZeroLengthKey = errors.New("zero length key")
+       errShortWrite    = errors.New("i/o count too small")
 )
 
 func readenv(key string) (string, error) {
@@ -47,12 +50,18 @@ func writeenv(key, value string) error {
                return err
        }
        defer Close(fd)
-       _, err = Write(fd, []byte(value))
-       return err
+       b := []byte(value)
+       n, err := Write(fd, b)
+       if err != nil {
+               return err
+       }
+       if n != len(b) {
+               return errShortWrite
+       }
+       return nil
 }
 
 func copyenv() {
-       env = make(map[string]string)
        fd, err := Open("/env", O_RDONLY)
        if err != nil {
                return
@@ -72,7 +81,6 @@ func copyenv() {
 }
 
 func Getenv(key string) (value string, found bool) {
-       envOnce.Do(copyenv)
        if len(key) == 0 {
                return "", false
        }
@@ -80,17 +88,20 @@ func Getenv(key string) (value string, found bool) {
        envLock.RLock()
        defer envLock.RUnlock()
 
-       v, ok := env[key]
-       if !ok {
+       if v, ok := env[key]; ok {
+               return v, true
+       }
+       v, err := readenv(key)
+       if err != nil {
                return "", false
        }
+       env[key] = v
        return v, true
 }
 
 func Setenv(key, value string) error {
-       envOnce.Do(copyenv)
        if len(key) == 0 {
-               return errors.New("zero length key")
+               return errZeroLengthKey
        }
 
        envLock.Lock()
@@ -105,8 +116,6 @@ func Setenv(key, value string) error {
 }
 
 func Clearenv() {
-       envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv
-
        envLock.Lock()
        defer envLock.Unlock()
 
@@ -115,9 +124,10 @@ func Clearenv() {
 }
 
 func Environ() []string {
-       envOnce.Do(copyenv)
        envLock.RLock()
        defer envLock.RUnlock()
+
+       envOnce.Do(copyenv)
        a := make([]string, len(env))
        i := 0
        for k, v := range env {
diff --git a/libgo/go/syscall/libcall_linux_utimesnano.go b/libgo/go/syscall/libcall_linux_utimesnano.go
new file mode 100644 (file)
index 0000000..043ab0d
--- /dev/null
@@ -0,0 +1,29 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// GNU/Linux version of UtimesNano.
+
+package syscall
+
+import "unsafe"
+
+//sys  utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error)
+//utimensat(dirfd int, path *byte, times *[2]Timespec, flags int) int
+func UtimesNano(path string, ts []Timespec) (err error) {
+       if len(ts) != 2 {
+               return EINVAL
+       }
+       err = utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0)
+       if err != ENOSYS {
+               return err
+       }
+       // If the utimensat syscall isn't available (utimensat was added to Linux
+       // in 2.6.22, Released, 8 July 2007) then fall back to utimes
+       var tv [2]Timeval
+       for i := 0; i < 2; i++ {
+               tv[i].Sec = Timeval_sec_t(ts[i].Sec)
+               tv[i].Usec = Timeval_usec_t(ts[i].Nsec / 1000)
+       }
+       return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
+}
diff --git a/libgo/go/syscall/libcall_posix_utimesnano.go b/libgo/go/syscall/libcall_posix_utimesnano.go
new file mode 100644 (file)
index 0000000..e0751f5
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// General POSIX version of UtimesNano.
+
+package syscall
+
+import "unsafe"
+
+func UtimesNano(path string, ts []Timespec) error {
+       // TODO: The BSDs can do utimensat with SYS_UTIMENSAT but it
+       // isn't supported by darwin so this uses utimes instead
+       if len(ts) != 2 {
+               return EINVAL
+       }
+       // Not as efficient as it could be because Timespec and
+       // Timeval have different types in the different OSes
+       tv := [2]Timeval{
+               NsecToTimeval(TimespecToNsec(ts[0])),
+               NsecToTimeval(TimespecToNsec(ts[1])),
+       }
+       return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
+}
index 56296c8..c4f2125 100644 (file)
@@ -3,10 +3,15 @@
 // license that can be found in the LICENSE file.
 
 // Package syscall contains an interface to the low-level operating system
-// primitives.  The details vary depending on the underlying system.
-// Its primary use is inside other packages that provide a more portable
-// interface to the system, such as "os", "time" and "net".  Use those
-// packages rather than this one if you can.
+// primitives.  The details vary depending on the underlying system, and
+// by default, godoc will display the syscall documentation for the current
+// system.  If you want godoc to display syscall documentation for another
+// system, set $GOOS and $GOARCH to the desired system.  For example, if
+// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS
+// to freebsd and $GOARCH to arm.
+// The primary use of syscall is inside other packages that provide a more
+// portable interface to the system, such as "os", "time" and "net".  Use
+// those packages rather than this one if you can.
 // For details of the functions and data types in this package consult
 // the manuals for the appropriate operating system.
 // These calls return err == nil to indicate success; otherwise
index 671c798..dc97255 100644 (file)
@@ -24,7 +24,7 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int
 
        var eg InternalExample
 
-       stdout, stderr := os.Stdout, os.Stderr
+       stdout := os.Stdout
 
        for _, eg = range examples {
                matched, err := matchString(*match, eg.Name)
@@ -39,19 +39,19 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int
                        fmt.Printf("=== RUN: %s\n", eg.Name)
                }
 
-               // capture stdout and stderr
+               // capture stdout
                r, w, err := os.Pipe()
                if err != nil {
                        fmt.Fprintln(os.Stderr, err)
                        os.Exit(1)
                }
-               os.Stdout, os.Stderr = w, w
+               os.Stdout = w
                outC := make(chan string)
                go func() {
                        buf := new(bytes.Buffer)
                        _, err := io.Copy(buf, r)
                        if err != nil {
-                               fmt.Fprintf(stderr, "testing: copying pipe: %v\n", err)
+                               fmt.Fprintf(os.Stderr, "testing: copying pipe: %v\n", err)
                                os.Exit(1)
                        }
                        outC <- buf.String()
@@ -62,9 +62,9 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int
                eg.F()
                dt := time.Now().Sub(t0)
 
-               // close pipe, restore stdout/stderr, get output
+               // close pipe, restore stdout, get output
                w.Close()
-               os.Stdout, os.Stderr = stdout, stderr
+               os.Stdout = stdout
                out := <-outC
 
                // report any errors
index aab4a4d..417e8f8 100644 (file)
@@ -854,9 +854,15 @@ func Parse(layout, value string) (Time, error) {
                        zoneName = p
 
                case stdFracSecond0:
-                       ndigit := std >> stdArgShift
-                       nsec, rangeErrString, err = parseNanoseconds(value, 1+ndigit)
-                       value = value[1+ndigit:]
+                       // stdFracSecond0 requires the exact number of digits as specified in
+                       // the layout.
+                       ndigit := 1 + (std >> stdArgShift)
+                       if len(value) < ndigit {
+                               err = errBad
+                               break
+                       }
+                       nsec, rangeErrString, err = parseNanoseconds(value, ndigit)
+                       value = value[ndigit:]
 
                case stdFracSecond9:
                        if len(value) < 2 || value[0] != '.' || value[1] < '0' || '9' < value[1] {
@@ -934,8 +940,7 @@ func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string,
                err = errBad
                return
        }
-       ns, err = atoi(value[1:nbytes])
-       if err != nil {
+       if ns, err = atoi(value[1:nbytes]); err != nil {
                return
        }
        if ns < 0 || 1e9 <= ns {
index 1fd575b..0224fed 100644 (file)
@@ -469,7 +469,7 @@ type ParseTest struct {
        value      string
        hasTZ      bool // contains a time zone
        hasWD      bool // contains a weekday
-       yearSign   int  // sign of year
+       yearSign   int  // sign of year, -1 indicates the year is not present in the format
        fracDigits int  // number of digits of fractional second
 }
 
@@ -514,6 +514,13 @@ var parseTests = []ParseTest{
        {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
        {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
        {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
+
+       // issue 4502.
+       {"", StampNano, "Feb  4 21:00:57.012345678", false, false, -1, 9},
+       {"", "Jan _2 15:04:05.999", "Feb  4 21:00:57.012300000", false, false, -1, 4},
+       {"", "Jan _2 15:04:05.999", "Feb  4 21:00:57.012345678", false, false, -1, 9},
+       {"", "Jan _2 15:04:05.999999999", "Feb  4 21:00:57.0123", false, false, -1, 4},
+       {"", "Jan _2 15:04:05.999999999", "Feb  4 21:00:57.012345678", false, false, -1, 9},
 }
 
 func TestParse(t *testing.T) {
@@ -549,7 +556,7 @@ func TestRubyParse(t *testing.T) {
 
 func checkTime(time Time, test *ParseTest, t *testing.T) {
        // The time should be Thu Feb  4 21:00:57 PST 2010
-       if test.yearSign*time.Year() != 2010 {
+       if test.yearSign >= 0 && test.yearSign*time.Year() != 2010 {
                t.Errorf("%s: bad year: %d not %d", test.name, time.Year(), 2010)
        }
        if time.Month() != February {
@@ -630,6 +637,9 @@ var parseErrorTests = []ParseErrorTest{
        {"Mon Jan _2 15:04:05.000 2006", "Thu Feb  4 23:00:59x01 2010", "cannot parse"},
        {"Mon Jan _2 15:04:05.000 2006", "Thu Feb  4 23:00:59.xxx 2010", "cannot parse"},
        {"Mon Jan _2 15:04:05.000 2006", "Thu Feb  4 23:00:59.-123 2010", "fractional second out of range"},
+       // issue 4502. StampNano requires exactly 9 digits of precision.
+       {StampNano, "Dec  7 11:22:01.000000", `cannot parse ".000000" as ".000000000"`},
+       {StampNano, "Dec  7 11:22:01.0000000000", "extra text: 0"},
 }
 
 func TestParseErrors(t *testing.T) {
index 0eb20c7..a5a2de2 100644 (file)
@@ -174,7 +174,7 @@ func loadZoneData(bytes []byte) (l *Location, err error) {
                }
        }
 
-       // Commited to succeed.
+       // Committed to succeed.
        l = &Location{zone: zone, tx: tx}
 
        // Fill in the cache with information about right now,
index 79ece62..ca358ac 100755 (executable)
@@ -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 (file)
index 0000000..31f4179
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux netbsd openbsd windows
+
+#include "runtime.h"
+#include "array.h"
+
+extern Slice syscall_Envs asm ("syscall.Envs");
+
+const byte*
+runtime_getenv(const char *s)
+{
+       int32 i, j, len;
+       const byte *v, *bs;
+       String* envv;
+       int32 envc;
+
+       bs = (const byte*)s;
+       len = runtime_findnull(bs);
+       envv = (String*)syscall_Envs.__values;
+       envc = syscall_Envs.__count;
+       for(i=0; i<envc; i++){
+               if(envv[i].len <= len)
+                       continue;
+               v = (const byte*)envv[i].str;
+               for(j=0; j<len; j++)
+                       if(bs[j] != v[j])
+                               goto nomatch;
+               if(v[len] != '=')
+                       goto nomatch;
+               return v+len+1;
+       nomatch:;
+       }
+       return nil;
+}
index 2d8d9e6..17f73d4 100644 (file)
@@ -106,8 +106,8 @@ __go_allocate_trampoline (uintptr_t size, void *closure)
    no other references to it.  */
 
 void
-runtime_trampoline_scan (void (*addroot) (byte *, uintptr))
+runtime_trampoline_scan (void (*addroot) (Obj))
 {
   if (trampoline_page != NULL)
-    addroot ((byte *) &trampoline_page, sizeof trampoline_page);
+    addroot ((Obj){(byte *) &trampoline_page, sizeof trampoline_page, 0});
 }
index f1f3fcd..2a614e5 100644 (file)
@@ -491,7 +491,7 @@ runtime_MHeap_SysAlloc(MHeap *h, uintptr n)
 static Lock settype_lock;
 
 void
-runtime_settype_flush(M *m, bool sysalloc)
+runtime_settype_flush(M *mp, bool sysalloc)
 {
        uintptr *buf, *endbuf;
        uintptr size, ofs, j, t;
@@ -503,8 +503,8 @@ runtime_settype_flush(M *m, bool sysalloc)
        uintptr typ, p;
        MSpan *s;
 
-       buf = m->settype_buf;
-       endbuf = buf + m->settype_bufsize;
+       buf = mp->settype_buf;
+       endbuf = buf + mp->settype_bufsize;
 
        runtime_lock(&settype_lock);
        while(buf < endbuf) {
@@ -602,7 +602,7 @@ runtime_settype_flush(M *m, bool sysalloc)
        }
        runtime_unlock(&settype_lock);
 
-       m->settype_bufsize = 0;
+       mp->settype_bufsize = 0;
 }
 
 // It is forbidden to use this function if it is possible that
@@ -610,7 +610,7 @@ runtime_settype_flush(M *m, bool sysalloc)
 void
 runtime_settype(void *v, uintptr t)
 {
-       M *m1;
+       M *mp;
        uintptr *buf;
        uintptr i;
        MSpan *s;
@@ -618,16 +618,16 @@ runtime_settype(void *v, uintptr t)
        if(t == 0)
                runtime_throw("settype: zero type");
 
-       m1 = runtime_m();
-       buf = m1->settype_buf;
-       i = m1->settype_bufsize;
+       mp = runtime_m();
+       buf = mp->settype_buf;
+       i = mp->settype_bufsize;
        buf[i+0] = (uintptr)v;
        buf[i+1] = t;
        i += 2;
-       m1->settype_bufsize = i;
+       mp->settype_bufsize = i;
 
-       if(i == nelem(m1->settype_buf)) {
-               runtime_settype_flush(m1, false);
+       if(i == nelem(mp->settype_buf)) {
+               runtime_settype_flush(mp, false);
        }
 
        if(DebugTypeAtBlockEnd) {
index b3baaec..710484e 100644 (file)
@@ -468,17 +468,25 @@ enum
        FlagNoGC = 1<<2,        // must not free or scan for pointers
 };
 
+typedef struct Obj Obj;
+struct Obj
+{
+       byte    *p;     // data pointer
+       uintptr n;      // size of data in bytes
+       uintptr ti;     // type info
+};
+
 void   runtime_MProf_Malloc(void*, uintptr);
 void   runtime_MProf_Free(void*, uintptr);
 void   runtime_MProf_GC(void);
-void   runtime_MProf_Mark(void (*addroot)(byte *, uintptr));
+void   runtime_MProf_Mark(void (*addroot)(Obj));
 int32  runtime_gcprocs(void);
 void   runtime_helpgc(int32 nproc);
 void   runtime_gchelper(void);
 
 struct __go_func_type;
 bool   runtime_getfinalizer(void *p, bool del, void (**fn)(void*), const struct __go_func_type **ft);
-void   runtime_walkfintab(void (*fn)(void*), void (*scan)(byte *, uintptr));
+void   runtime_walkfintab(void (*fn)(void*), void (*scan)(Obj));
 
 enum
 {
@@ -494,3 +502,6 @@ enum
 void   runtime_gc_m_ptr(Eface*);
 
 void   runtime_memorydump(void);
+
+void   runtime_time_scan(void (*)(Obj));
+void   runtime_trampoline_scan(void (*)(Obj));
index f2f640a..7c906da 100644 (file)
@@ -193,7 +193,7 @@ runtime_getfinalizer(void *p, bool del, void (**fn)(void*), const struct __go_fu
 }
 
 void
-runtime_walkfintab(void (*fn)(void*), void (*addroot)(byte *, uintptr))
+runtime_walkfintab(void (*fn)(void*), void (*addroot)(Obj))
 {
        void **key;
        void **ekey;
@@ -206,8 +206,8 @@ runtime_walkfintab(void (*fn)(void*), void (*addroot)(byte *, uintptr))
                for(; key < ekey; key++)
                        if(*key != nil && *key != ((void*)-1))
                                fn(*key);
-               addroot((byte*)&fintab[i].fkey, sizeof(void*));
-               addroot((byte*)&fintab[i].val, sizeof(void*));
+               addroot((Obj){(byte*)&fintab[i].fkey, sizeof(void*), 0});
+               addroot((Obj){(byte*)&fintab[i].val, sizeof(void*), 0});
                runtime_unlock(&fintab[i]);
        }
 }
index 45f8a56..698400b 100644 (file)
@@ -9,6 +9,7 @@
 #include "runtime.h"
 #include "arch.h"
 #include "malloc.h"
+#include "mgc0.h"
 #include "race.h"
 
 #ifdef USING_SPLIT_STACK
@@ -24,11 +25,13 @@ extern void * __splitstack_find_context (void *context[10], size_t *, void **,
 enum {
        Debug = 0,
        DebugMark = 0,  // run second pass to check mark
-       DataBlock = 8*1024,
 
        // Four bits per word (see #defines below).
        wordsPerBitmapWord = sizeof(void*)*8/4,
        bitShift = sizeof(void*)*8/4,
+
+       handoffThreshold = 4,
+       IntermediateBufferCapacity = 64,
 };
 
 // Bits in per-word bitmap.
@@ -81,12 +84,16 @@ uint32 runtime_worldsema = 1;
 
 static int32 gctrace;
 
+// The size of Workbuf is N*PageSize.
 typedef struct Workbuf Workbuf;
 struct Workbuf
 {
-       LFNode node; // must be first
+#define SIZE (2*PageSize-sizeof(LFNode)-sizeof(uintptr))
+       LFNode  node; // must be first
        uintptr nobj;
-       byte *obj[512-(sizeof(LFNode)+sizeof(uintptr))/sizeof(byte*)];
+       Obj     obj[SIZE/sizeof(Obj) - 1];
+       uint8   _padding[SIZE%sizeof(Obj) + sizeof(Obj)];
+#undef SIZE
 };
 
 typedef struct Finalizer Finalizer;
@@ -120,13 +127,6 @@ static Workbuf* getfull(Workbuf*);
 static void    putempty(Workbuf*);
 static Workbuf* handoff(Workbuf*);
 
-typedef struct GcRoot GcRoot;
-struct GcRoot
-{
-       byte *p;
-       uintptr n;
-};
-
 static struct {
        uint64  full;  // lock-free list of full blocks
        uint64  empty; // lock-free list of empty blocks
@@ -143,77 +143,122 @@ static struct {
        byte    *chunk;
        uintptr nchunk;
 
-       GcRoot  *roots;
+       Obj     *roots;
        uint32  nroot;
        uint32  rootcap;
 } work;
 
-// scanblock scans a block of n bytes starting at pointer b for references
-// to other objects, scanning any it finds recursively until there are no
-// unscanned objects left.  Instead of using an explicit recursion, it keeps
-// a work list in the Workbuf* structures and loops in the main function
-// body.  Keeping an explicit work list is easier on the stack allocator and
-// more efficient.
+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<<PageShift);
+                       p = (byte*)((uintptr)s->start<<PageShift);
                        if(s->sizeclass == 0) {
                                obj = p;
                        } else {
                                if((byte*)obj >= (byte*)s->limit)
                                        continue;
-                               size = runtime_class_to_size[s->sizeclass];
+                               size = s->elemsize;
                                int32 i = ((byte*)obj - p)/size;
                                obj = p+i*size;
                        }
@@ -265,81 +312,203 @@ scanblock(byte *b, uintptr n)
                        bits = xbits >> shift;
 
                found:
-                       // If another proc wants a pointer, give it some.
-                       if(work.nwait > 0 && nobj > 4 && work.full == 0) {
-                               wbuf->nobj = nobj;
-                               wbuf = handoff(wbuf);
-                               nobj = wbuf->nobj;
-                               wp = (void**)(wbuf->obj + nobj);
-                       }
-
                        // Now we have bits, bitp, and shift correct for
                        // obj pointing at the base of the object.
                        // Only care about allocated and not marked.
                        if((bits & (bitAllocated|bitMarked)) != bitAllocated)
                                continue;
-                       if(nproc == 1)
-                               *bitp |= bitMarked<<shift;
-                       else {
-                               for(;;) {
-                                       x = *bitp;
-                                       if(x & (bitMarked<<shift))
-                                               goto continue_obj;
-                                       if(runtime_casp((void**)bitp, (void*)x, (void*)(x|(bitMarked<<shift))))
-                                               break;
-                               }
-                       }
+
+                       *bitbufpos = (struct BitTarget){obj, ti, bitp, shift};
+                       bitbufpos++;
+               }
+
+               runtime_lock(&lock);
+               for(bt=bitbuf; bt<bitbufpos; bt++){
+                       xbits = *bt->bitp;
+                       bits = xbits >> bt->shift;
+                       if((bits & bitMarked) != 0)
+                               continue;
+
+                       // Mark the block
+                       *bt->bitp = xbits | (bitMarked << bt->shift);
 
                        // If object has no pointers, don't need to scan further.
                        if((bits & bitNoPointers) != 0)
                                continue;
 
+                       obj = bt->p;
+
+                       // Ask span about size class.
+                       // (Manually inlined copy of MHeap_Lookup.)
+                       x = (uintptr)obj >> PageShift;
+                       if(sizeof(void*) == 8)
+                               x -= (uintptr)arena_start>>PageShift;
+                       s = runtime_mheap.map[x];
+
                        PREFETCH(obj);
 
-                       // If buffer is full, get a new one.
-                       if(wbuf == nil || nobj >= nelem(wbuf->obj)) {
-                               if(wbuf != nil)
-                                       wbuf->nobj = nobj;
-                               wbuf = getempty(wbuf);
-                               wp = (void**)(wbuf->obj);
-                               nobj = 0;
-                       }
-                       *wp++ = obj;
+                       *wp = (Obj){obj, s->elemsize, bt->ti};
+                       wp++;
                        nobj++;
-               continue_obj:;
                }
+               runtime_unlock(&lock);
+
+               // If another proc wants a pointer, give it some.
+               if(work.nwait > 0 && nobj > handoffThreshold && work.full == 0) {
+                       wbuf->nobj = nobj;
+                       wbuf = handoff(wbuf);
+                       nobj = wbuf->nobj;
+                       wp = wbuf->obj + nobj;
+               }
+       }
+
+       *_wp = wp;
+       *_wbuf = wbuf;
+       *_nobj = nobj;
+}
+
+// Program that scans the whole block and treats every block element as a potential pointer
+static uintptr defaultProg[2] = {PtrSize, GC_DEFAULT_PTR};
+
+// scanblock scans a block of n bytes starting at pointer b for references
+// to other objects, scanning any it finds recursively until there are no
+// unscanned objects left.  Instead of using an explicit recursion, it keeps
+// a work list in the Workbuf* structures and loops in the main function
+// body.  Keeping an explicit work list is easier on the stack allocator and
+// more efficient.
+//
+// wbuf: current work buffer
+// wp:   storage for next queued pointer (write pointer)
+// nobj: number of queued objects
+static void
+scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
+{
+       byte *b, *arena_start, *arena_used;
+       uintptr n, i, end_b;
+       void *obj;
+
+       // TODO(atom): to be expanded in a next CL
+       struct Frame {uintptr count, b; uintptr *loop_or_ret;};
+       struct Frame stack_top;
+
+       uintptr *pc;
+
+       struct BufferList *scanbuffers;
+       struct PtrTarget *ptrbuf, *ptrbuf_end;
+       struct BitTarget *bitbuf;
+
+       struct PtrTarget *ptrbufpos;
+
+       // End of local variable declarations.
+
+       if(sizeof(Workbuf) % PageSize != 0)
+               runtime_throw("scanblock: size of Workbuf is suboptimal");
+
+       // Memory arena parameters.
+       arena_start = runtime_mheap.arena_start;
+       arena_used = runtime_mheap.arena_used;
+
+       // Allocate ptrbuf, bitbuf
+       {
+               runtime_lock(&lock);
+
+               if(bufferList == nil) {
+                       bufferList = runtime_SysAlloc(sizeof(*bufferList));
+                       bufferList->next = nil;
+               }
+               scanbuffers = bufferList;
+               bufferList = bufferList->next;
+
+               ptrbuf = &scanbuffers->ptrtarget[0];
+               ptrbuf_end = &scanbuffers->ptrtarget[0] + nelem(scanbuffers->ptrtarget);
+               bitbuf = &scanbuffers->bittarget[0];
+
+               runtime_unlock(&lock);
+       }
+
+       ptrbufpos = ptrbuf;
+
+       goto next_block;
 
+       for(;;) {
+               // Each iteration scans the block b of length n, queueing pointers in
+               // the work buffer.
+               if(Debug > 1) {
+                       runtime_printf("scanblock %p %D\n", b, (int64)n);
+               }
+
+               // TODO(atom): to be replaced in a next CL
+               pc = defaultProg;
+
+               pc++;
+               stack_top.b = (uintptr)b;
+
+               end_b = (uintptr)b + n - PtrSize;
+
+       next_instr:
+               // TODO(atom): to be expanded in a next CL
+               switch(pc[0]) {
+               case GC_DEFAULT_PTR:
+                       while(true) {
+                               i = stack_top.b;
+                               if(i > end_b)
+                                       goto next_block;
+                               stack_top.b += PtrSize;
+
+                               obj = *(byte**)i;
+                               if((byte*)obj >= arena_start && (byte*)obj < arena_used) {
+                                       *ptrbufpos = (struct PtrTarget){obj, 0};
+                                       ptrbufpos++;
+                                       if(ptrbufpos == ptrbuf_end)
+                                               goto flush_buffers;
+                               }
+                       }
+
+               default:
+                       runtime_throw("scanblock: invalid GC instruction");
+                       return;
+               }
+
+       flush_buffers:
+               flushptrbuf(ptrbuf, ptrbufpos-ptrbuf, &wp, &wbuf, &nobj, bitbuf);
+               ptrbufpos = ptrbuf;
+               goto next_instr;
+
+       next_block:
                // Done scanning [b, b+n).  Prepare for the next iteration of
-               // the loop by setting b and n to the parameters for the next block.
+               // the loop by setting b, n to the parameters for the next block.
 
-               // 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<<PageShift;
-               else
-                       n = runtime_class_to_size[s->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<<PageShift);
+               size = s->elemsize;
                if(s->sizeclass == 0) {
                        obj = p;
-                       size = (uintptr)s->npages<<PageShift;
                } else {
                        if((byte*)obj >= (byte*)s->limit)
                                continue;
-                       size = runtime_class_to_size[s->sizeclass];
                        int32 i = ((byte*)obj - p)/size;
                        obj = p+i*size;
                }
@@ -421,11 +589,74 @@ debug_scanblock(byte *b, uintptr n)
        }
 }
 
+// Append obj to the work buffer.
+// _wbuf, _wp, _nobj are input/output parameters and are specifying the work buffer.
+static void
+enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj)
+{
+       uintptr nobj, off;
+       Obj *wp;
+       Workbuf *wbuf;
+
+       if(Debug > 1)
+               runtime_printf("append obj(%p %D %p)\n", obj.p, (int64)obj.n, obj.ti);
+
+       // Align obj.b to a word boundary.
+       off = (uintptr)obj.p & (PtrSize-1);
+       if(off != 0) {
+               obj.p += PtrSize - off;
+               obj.n -= PtrSize - off;
+               obj.ti = 0;
+       }
+
+       if(obj.p == nil || obj.n == 0)
+               return;
+
+       // Load work buffer state
+       wp = *_wp;
+       wbuf = *_wbuf;
+       nobj = *_nobj;
+
+       // If another proc wants a pointer, give it some.
+       if(work.nwait > 0 && nobj > handoffThreshold && work.full == 0) {
+               wbuf->nobj = nobj;
+               wbuf = handoff(wbuf);
+               nobj = wbuf->nobj;
+               wp = wbuf->obj + nobj;
+       }
+
+       // If buffer is full, get a new one.
+       if(wbuf == nil || nobj >= nelem(wbuf->obj)) {
+               if(wbuf != nil)
+                       wbuf->nobj = nobj;
+               wbuf = getempty(wbuf);
+               wp = wbuf->obj;
+               nobj = 0;
+       }
+
+       *wp = obj;
+       wp++;
+       nobj++;
+
+       // Save work buffer state
+       *_wp = wp;
+       *_wbuf = wbuf;
+       *_nobj = nobj;
+}
+
 static void
 markroot(ParFor *desc, uint32 i)
 {
+       Obj *wp;
+       Workbuf *wbuf;
+       uintptr nobj;
+
        USED(&desc);
-       scanblock(work.roots[i].p, work.roots[i].n);
+       wp = nil;
+       wbuf = nil;
+       nobj = 0;
+       enqueue(work.roots[i], &wbuf, &wp, &nobj);
+       scanblock(wbuf, wp, nobj, false);
 }
 
 // Get an empty work buffer off the work.empty list,
@@ -520,25 +751,24 @@ handoff(Workbuf *b)
 }
 
 static void
-addroot(byte *p, uintptr n)
+addroot(Obj obj)
 {
        uint32 cap;
-       GcRoot *new;
+       Obj *new;
 
        if(work.nroot >= work.rootcap) {
-               cap = PageSize/sizeof(GcRoot);
+               cap = PageSize/sizeof(Obj);
                if(cap < 2*work.rootcap)
                        cap = 2*work.rootcap;
-               new = (GcRoot*)runtime_SysAlloc(cap*sizeof(GcRoot));
+               new = (Obj*)runtime_SysAlloc(cap*sizeof(Obj));
                if(work.roots != nil) {
-                       runtime_memmove(new, work.roots, work.rootcap*sizeof(GcRoot));
-                       runtime_SysFree(work.roots, work.rootcap*sizeof(GcRoot));
+                       runtime_memmove(new, work.roots, work.rootcap*sizeof(Obj));
+                       runtime_SysFree(work.roots, work.rootcap*sizeof(Obj));
                }
                work.roots = new;
                work.rootcap = cap;
        }
-       work.roots[work.nroot].p = p;
-       work.roots[work.nroot].n = n;
+       work.roots[work.nroot] = obj;
        work.nroot++;
 }
 
@@ -582,11 +812,11 @@ addstackroots(G *gp)
                }
        }
        if(sp != nil) {
-               addroot(sp, spsize);
+               addroot((Obj){sp, spsize, 0});
                while((sp = __splitstack_find(next_segment, next_sp,
                                              &spsize, &next_segment,
                                              &next_sp, &initial_sp)) != nil)
-                       addroot(sp, spsize);
+                       addroot((Obj){sp, spsize, 0});
        }
 #else
        M *mp;
@@ -608,9 +838,9 @@ addstackroots(G *gp)
        }
        top = (byte*)gp->gcinitial_sp + gp->gcstack_size;
        if(top > bottom)
-               addroot(bottom, top - bottom);
+               addroot((Obj){bottom, top - bottom, 0});
        else
-               addroot(top, bottom - top);
+               addroot((Obj){top, bottom - top, 0});
 #endif
 }
 
@@ -624,7 +854,7 @@ addfinroots(void *v)
                runtime_throw("mark - finalizer inconsistency");
 
        // do not mark the finalizer block itself.  just mark the things it points at.
-       addroot(v, size);
+       addroot((Obj){v, size, 0});
 }
 
 static struct root_list* roots;
@@ -656,15 +886,15 @@ addroots(void)
                        void *decl = pr->decl;
                        if(decl == nil)
                                break;
-                       addroot(decl, pr->size);
+                       addroot((Obj){decl, pr->size, 0});
                        pr++;
                }
        }
 
-       addroot((byte*)&runtime_m0, sizeof runtime_m0);
-       addroot((byte*)&runtime_g0, sizeof runtime_g0);
-       addroot((byte*)&runtime_allg, sizeof runtime_allg);
-       addroot((byte*)&runtime_allm, sizeof runtime_allm);
+       addroot((Obj){(byte*)&runtime_m0, sizeof runtime_m0, 0});
+       addroot((Obj){(byte*)&runtime_g0, sizeof runtime_g0, 0});
+       addroot((Obj){(byte*)&runtime_allg, sizeof runtime_allg, 0});
+       addroot((Obj){(byte*)&runtime_allm, sizeof runtime_allm, 0});
        runtime_MProf_Mark(addroot);
        runtime_time_scan(addroot);
        runtime_trampoline_scan(addroot);
@@ -680,12 +910,14 @@ addroots(void)
                                break;
                        case MTypes_Words:
                        case MTypes_Bytes:
-                               addroot((byte*)&s->types.data, sizeof(void*));
+                               // TODO(atom): consider using defaultProg instead of 0
+                               addroot((Obj){(byte*)&s->types.data, sizeof(void*), 0});
                                break;
                        }
                }
        }
 
+       // stacks
        for(gp=runtime_allg; gp!=nil; gp=gp->alllink) {
                switch(gp->status){
                default:
@@ -709,9 +941,9 @@ addroots(void)
        runtime_walkfintab(addfinroots, addroot);
 
        for(fb=allfin; fb; fb=fb->alllink)
-               addroot((byte*)fb->fin, fb->cnt*sizeof(fb->fin[0]));
+               addroot((Obj){(byte*)fb->fin, fb->cnt*sizeof(fb->fin[0]), 0});
 
-       addroot((byte*)&work, sizeof work);
+       addroot((Obj){(byte*)&work, sizeof work, 0});
 }
 
 static bool
@@ -955,8 +1187,9 @@ runtime_gchelper(void)
 {
        // parallel mark for over gc roots
        runtime_parfordo(work.markfor);
+
        // help other threads scan secondary blocks
-       scanblock(nil, 0);
+       scanblock(nil, nil, 0, true);
 
        if(DebugMark) {
                // wait while the main thread executes mark(debug_scanblock)
@@ -983,16 +1216,16 @@ static int32 gcpercent = -2;
 static void
 stealcache(void)
 {
-       M *m;
+       M *mp;
 
-       for(m=runtime_allm; m; m=m->alllink)
-               runtime_MCache_ReleaseAll(m->mcache);
+       for(mp=runtime_allm; mp; mp=mp->alllink)
+               runtime_MCache_ReleaseAll(mp->mcache);
 }
 
 static void
 cachestats(GCStats *stats)
 {
-       M *m;
+       M *mp;
        MCache *c;
        uint32 i;
        uint64 stacks_inuse;
@@ -1003,17 +1236,17 @@ cachestats(GCStats *stats)
                runtime_memclr((byte*)stats, sizeof(*stats));
        stacks_inuse = 0;
        stacks_sys = runtime_stacks_sys;
-       for(m=runtime_allm; m; m=m->alllink) {
-               c = m->mcache;
+       for(mp=runtime_allm; mp; mp=mp->alllink) {
+               c = mp->mcache;
                runtime_purgecachedstats(c);
-               // stacks_inuse += m->stackalloc->inuse;
-               // stacks_sys += m->stackalloc->sys;
+               // stacks_inuse += mp->stackalloc->inuse;
+               // stacks_sys += mp->stackalloc->sys;
                if(stats) {
-                       src = (uint64*)&m->gcstats;
+                       src = (uint64*)&mp->gcstats;
                        dst = (uint64*)stats;
                        for(i=0; i<sizeof(*stats)/sizeof(uint64); i++)
                                dst[i] += src[i];
-                       runtime_memclr((byte*)&m->gcstats, sizeof(m->gcstats));
+                       runtime_memclr((byte*)&mp->gcstats, sizeof(mp->gcstats));
                }
                for(i=0; i<nelem(c->local_by_size); i++) {
                        mstats.by_size[i].nmalloc += c->local_by_size[i].nmalloc;
@@ -1100,7 +1333,7 @@ gc(struct gc_args *args)
        int64 t0, t1, t2, t3;
        uint64 heap0, heap1, obj0, obj1;
        GCStats stats;
-       M *m1;
+       M *mp;
        uint32 i;
 
        runtime_semacquire(&runtime_worldsema);
@@ -1116,8 +1349,8 @@ gc(struct gc_args *args)
        m->gcing = 1;
        runtime_stoptheworld();
 
-       for(m1=runtime_allm; m1; m1=m1->alllink)
-               runtime_settype_flush(m1, false);
+       for(mp=runtime_allm; mp; mp=mp->alllink)
+               runtime_settype_flush(mp, false);
 
        heap0 = 0;
        obj0 = 0;
@@ -1127,26 +1360,27 @@ gc(struct gc_args *args)
                obj0 = mstats.nmalloc - mstats.nfree;
        }
 
+       m->locks++;     // disable gc during mallocs in parforalloc
+       if(work.markfor == nil)
+               work.markfor = runtime_parforalloc(MaxGcproc);
+       if(work.sweepfor == nil)
+               work.sweepfor = runtime_parforalloc(MaxGcproc);
+       m->locks--;
+
        work.nwait = 0;
        work.ndone = 0;
        work.debugmarkdone = 0;
        work.nproc = runtime_gcprocs();
        addroots();
-       m->locks++;     // disable gc during mallocs in parforalloc
-       if(work.markfor == nil)
-               work.markfor = runtime_parforalloc(MaxGcproc);
        runtime_parforsetup(work.markfor, work.nproc, work.nroot, nil, false, markroot);
-       if(work.sweepfor == nil)
-               work.sweepfor = runtime_parforalloc(MaxGcproc);
        runtime_parforsetup(work.sweepfor, work.nproc, runtime_mheap.nspan, nil, true, sweepspan);
-       m->locks--;
        if(work.nproc > 1) {
                runtime_noteclear(&work.alldone);
                runtime_helpgc(work.nproc);
        }
 
        runtime_parfordo(work.markfor);
-       scanblock(nil, 0);
+       scanblock(nil, nil, 0, true);
 
        if(DebugMark) {
                for(i=0; i<work.nroot; i++)
diff --git a/libgo/runtime/mgc0.h b/libgo/runtime/mgc0.h
new file mode 100644 (file)
index 0000000..a2798ef
--- /dev/null
@@ -0,0 +1,42 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Garbage collector (GC)
+
+// GC instruction opcodes.
+//
+// The opcode of an instruction is followed by zero or more
+// arguments to the instruction.
+//
+// Meaning of arguments:
+//   off      Offset (in bytes) from the start of the current object
+//   objgc    Pointer to GC info of an object
+//   len      Length of an array
+//   elemsize Size (in bytes) of an element
+//   size     Size (in bytes)
+enum {
+       GC_END,         // End of object, loop or subroutine. Args: none
+       GC_PTR,         // A typed pointer. Args: (off, objgc)
+       GC_APTR,        // Pointer to an arbitrary object. Args: (off)
+       GC_ARRAY_START, // Start an array with a fixed length. Args: (off, len, elemsize)
+       GC_ARRAY_NEXT,  // The next element of an array. Args: none
+       GC_CALL,        // Call a subroutine. Args: (off, objgc)
+       GC_MAP_PTR,     // Go map. Args: (off, MapType*)
+       GC_STRING,      // Go string. Args: (off)
+       GC_EFACE,       // interface{}. Args: (off)
+       GC_IFACE,       // interface{...}. Args: (off)
+       GC_SLICE,       // Go slice. Args: (off, objgc)
+       GC_REGION,      // A region/part of the current object. Args: (off, size, objgc)
+
+       GC_NUM_INSTR,   // Number of instruction opcodes
+};
+
+enum {
+       // Size of GC's fixed stack.
+       //
+       // The current GC implementation permits:
+       //  - at most 1 stack allocation because of GC_CALL
+       //  - at most GC_STACK_CAPACITY allocations because of GC_ARRAY_START
+       GC_STACK_CAPACITY = 8,  
+};
index a92ef40..8b3a195 100644 (file)
@@ -362,13 +362,13 @@ func MemProfile(p Slice, include_inuse_zero bool) (n int, ok bool) {
 }
 
 void
-runtime_MProf_Mark(void (*addroot)(byte *, uintptr))
+runtime_MProf_Mark(void (*addroot)(Obj))
 {
        // buckhash is not allocated via mallocgc.
-       addroot((byte*)&mbuckets, sizeof mbuckets);
-       addroot((byte*)&bbuckets, sizeof bbuckets);
-       addroot((byte*)&addrhash, sizeof addrhash);
-       addroot((byte*)&addrfree, sizeof addrfree);
+       addroot((Obj){(byte*)&mbuckets, sizeof mbuckets, 0});
+       addroot((Obj){(byte*)&bbuckets, sizeof bbuckets, 0});
+       addroot((Obj){(byte*)&addrhash, sizeof addrhash, 0});
+       addroot((Obj){(byte*)&addrfree, sizeof addrfree, 0});
 }
 
 // Must match BlockProfileRecord in debug.go.
@@ -412,18 +412,18 @@ struct TRecord {
 
 func ThreadCreateProfile(p Slice) (n int, ok bool) {
        TRecord *r;
-       M *first, *m;
+       M *first, *mp;
        
        first = runtime_atomicloadp(&runtime_allm);
        n = 0;
-       for(m=first; m; m=m->alllink)
+       for(mp=first; mp; mp=mp->alllink)
                n++;
        ok = false;
        if(n <= p.__count) {
                ok = true;
                r = (TRecord*)p.__values;
-               for(m=first; m; m=m->alllink) {
-                       runtime_memmove(r->stk, m->createstack, sizeof r->stk);
+               for(mp=first; mp; mp=mp->alllink) {
+                       runtime_memmove(r->stk, mp->createstack, sizeof r->stk);
                        r++;
                }
        }
@@ -471,11 +471,11 @@ func Stack(b Slice, all bool) (n int) {
 }
 
 static void
-saveg(G *g, TRecord *r)
+saveg(G *gp, TRecord *r)
 {
        int32 n;
 
-       if(g == runtime_g())
+       if(gp == runtime_g())
                n = runtime_callers(0, r->stk, nelem(r->stk));
        else {
                // FIXME: Not implemented.
index d90bb2c..9b229d6 100644 (file)
@@ -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
index 211edc0..ecd804d 100644 (file)
@@ -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<envc; i++){
-               if(envv[i].len <= len)
-                       continue;
-               v = (const byte*)envv[i].str;
-               for(j=0; j<len; j++)
-                       if(bs[j] != v[j])
-                               goto nomatch;
-               if(v[len] != '=')
-                       goto nomatch;
-               return v+len+1;
-       nomatch:;
-       }
-       return nil;
-}
-
 int32
 runtime_atoi(const byte *p)
 {
index 977ae49..c5cd3a0 100644 (file)
@@ -599,9 +599,6 @@ enum
        UseSpanType = 1,
 };
 
-void   runtime_time_scan(void (*)(byte*, uintptr));
-void   runtime_trampoline_scan(void (*)(byte *, uintptr));
-
 void   runtime_setsig(int32, bool, bool);
 #define runtime_setitimer setitimer
 
index 5077b71..d497361 100644 (file)
@@ -255,7 +255,7 @@ siftdown(int32 i)
 }
 
 void
-runtime_time_scan(void (*addroot)(byte*, uintptr))
+runtime_time_scan(void (*addroot)(Obj))
 {
-       addroot((byte*)&timers, sizeof timers);
+       addroot((Obj){(byte*)&timers, sizeof timers, 0});
 }