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