OSDN Git Service

libgo: Update to weekly.2011-12-22.
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 13 Jan 2012 05:11:45 +0000 (05:11 +0000)
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 13 Jan 2012 05:11:45 +0000 (05:11 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@183150 138bc75d-0d04-0410-961f-82ee72b054a4

195 files changed:
gcc/testsuite/go.test/test/fixedbugs/bug229.go
libgo/MERGE
libgo/Makefile.am
libgo/Makefile.in
libgo/configure
libgo/configure.ac
libgo/go/archive/zip/reader_test.go
libgo/go/archive/zip/struct.go
libgo/go/archive/zip/zip_test.go
libgo/go/bytes/buffer_test.go
libgo/go/bytes/bytes_test.go
libgo/go/bytes/example_test.go [new file with mode: 0644]
libgo/go/crypto/aes/aes_test.go
libgo/go/crypto/crypto.go
libgo/go/crypto/openpgp/keys.go
libgo/go/crypto/rand/rand_unix.go
libgo/go/crypto/tls/common.go
libgo/go/crypto/tls/handshake_client.go
libgo/go/crypto/tls/key_agreement.go
libgo/go/crypto/tls/root_darwin.go
libgo/go/crypto/tls/root_stub.go
libgo/go/crypto/x509/cert_pool.go
libgo/go/crypto/x509/verify_test.go
libgo/go/crypto/x509/x509.go
libgo/go/debug/gosym/pclntab_test.go
libgo/go/encoding/binary/binary_test.go
libgo/go/encoding/binary/varint_test.go
libgo/go/encoding/gob/codec_test.go
libgo/go/encoding/gob/encode.go
libgo/go/encoding/gob/gobencdec_test.go
libgo/go/encoding/gob/timing_test.go
libgo/go/encoding/gob/type.go
libgo/go/encoding/json/bench_test.go
libgo/go/encoding/json/decode.go
libgo/go/encoding/json/decode_test.go
libgo/go/encoding/json/encode.go
libgo/go/encoding/xml/marshal_test.go
libgo/go/exp/inotify/inotify_linux_test.go
libgo/go/exp/sql/sql.go
libgo/go/exp/sql/sql_test.go
libgo/go/exp/ssh/client_auth.go
libgo/go/exp/ssh/client_auth_test.go
libgo/go/exp/ssh/client_func_test.go
libgo/go/exp/ssh/server.go
libgo/go/exp/ssh/session.go
libgo/go/exp/ssh/session_test.go
libgo/go/exp/ssh/tcpip.go
libgo/go/exp/terminal/terminal.go
libgo/go/exp/terminal/terminal_test.go
libgo/go/exp/terminal/util.go
libgo/go/exp/winfsnotify/winfsnotify.go
libgo/go/exp/winfsnotify/winfsnotify_test.go
libgo/go/exp/wingui/gui.go
libgo/go/exp/wingui/winapi.go
libgo/go/exp/wingui/zwinapi.go
libgo/go/fmt/fmt_test.go
libgo/go/fmt/format.go
libgo/go/fmt/print.go
libgo/go/go/ast/scope.go
libgo/go/go/build/build_test.go
libgo/go/go/build/dir.go
libgo/go/go/build/path.go
libgo/go/go/doc/doc.go
libgo/go/go/doc/exports.go [new file with mode: 0644]
libgo/go/go/doc/filter.go [new file with mode: 0644]
libgo/go/go/parser/parser.go
libgo/go/go/printer/nodes.go
libgo/go/go/printer/printer.go
libgo/go/go/printer/testdata/comments.golden
libgo/go/go/printer/testdata/comments.input
libgo/go/go/printer/testdata/declarations.golden
libgo/go/go/printer/testdata/declarations.input
libgo/go/go/printer/testdata/statements.golden
libgo/go/go/scanner/scanner.go
libgo/go/go/scanner/scanner_test.go
libgo/go/html/const.go
libgo/go/html/parse.go
libgo/go/html/parse_test.go
libgo/go/html/template/error.go
libgo/go/html/template/escape.go
libgo/go/html/template/escape_test.go
libgo/go/image/color/ycbcr.go [new file with mode: 0644]
libgo/go/image/color/ycbcr_test.go [moved from libgo/go/image/ycbcr/ycbcr_test.go with 98% similarity]
libgo/go/image/draw/bench_test.go
libgo/go/image/draw/draw.go
libgo/go/image/draw/draw_test.go
libgo/go/image/jpeg/reader.go
libgo/go/image/jpeg/writer.go
libgo/go/image/jpeg/writer_test.go
libgo/go/image/png/writer_test.go
libgo/go/image/tiff/reader_test.go
libgo/go/image/ycbcr.go [new file with mode: 0644]
libgo/go/image/ycbcr/ycbcr.go [deleted file]
libgo/go/io/ioutil/ioutil_test.go
libgo/go/log/syslog/syslog.go
libgo/go/math/all_test.go
libgo/go/math/big/int_test.go
libgo/go/math/big/nat.go
libgo/go/math/big/nat_test.go
libgo/go/math/sin.go
libgo/go/mime/type_unix.go
libgo/go/net/cgo_stub.go
libgo/go/net/dial.go
libgo/go/net/dial_test.go [new file with mode: 0644]
libgo/go/net/dnsclient_unix.go
libgo/go/net/dnsconfig.go
libgo/go/net/fd.go
libgo/go/net/fd_linux.go
libgo/go/net/fd_netbsd.go [new file with mode: 0644]
libgo/go/net/fd_openbsd.go
libgo/go/net/file.go
libgo/go/net/http/client.go
libgo/go/net/http/jar.go [new file with mode: 0644]
libgo/go/net/http/readrequest_test.go
libgo/go/net/http/request_test.go
libgo/go/net/http/serve_test.go
libgo/go/net/interface_bsd.go
libgo/go/net/interface_linux.go
libgo/go/net/interface_netbsd.go [new file with mode: 0644]
libgo/go/net/interface_test.go
libgo/go/net/ip.go
libgo/go/net/ipraw_test.go
libgo/go/net/iprawsock_posix.go
libgo/go/net/ipsock_posix.go
libgo/go/net/lookup_unix.go
libgo/go/net/net.go
libgo/go/net/newpollserver.go
libgo/go/net/port.go
libgo/go/net/rpc/server_test.go
libgo/go/net/sendfile_stub.go
libgo/go/net/sock.go
libgo/go/net/sock_bsd.go
libgo/go/net/tcpsock_posix.go
libgo/go/net/textproto/reader_test.go
libgo/go/net/udpsock_posix.go
libgo/go/net/unixsock_posix.go
libgo/go/old/regexp/all_test.go
libgo/go/old/template/template_test.go
libgo/go/os/dir_unix.go
libgo/go/os/error_posix.go
libgo/go/os/exec/exec.go
libgo/go/os/exec/exec_test.go
libgo/go/os/exec/lp_unix.go
libgo/go/os/exec_posix.go
libgo/go/os/exec_unix.go
libgo/go/os/file_posix.go
libgo/go/os/file_unix.go
libgo/go/os/os_test.go
libgo/go/os/os_unix_test.go
libgo/go/os/path_unix.go
libgo/go/os/signal/signal.go
libgo/go/os/signal/signal_test.go
libgo/go/os/sys_bsd.go
libgo/go/os/user/lookup_stubs.go
libgo/go/os/user/lookup_unix.go
libgo/go/path/filepath/path.go
libgo/go/path/filepath/path_test.go
libgo/go/path/filepath/path_unix.go
libgo/go/path/path.go
libgo/go/path/path_test.go
libgo/go/regexp/all_test.go
libgo/go/regexp/exec_test.go
libgo/go/sort/sort.go
libgo/go/strconv/atof.go
libgo/go/strconv/atof_test.go
libgo/go/strconv/decimal.go
libgo/go/strconv/extfloat.go [new file with mode: 0644]
libgo/go/strconv/ftoa.go
libgo/go/strings/strings_test.go
libgo/go/syscall/bpf_bsd.go
libgo/go/syscall/env_unix.go
libgo/go/syscall/exec_unix.go
libgo/go/syscall/route_bsd.go
libgo/go/syscall/route_netbsd.go [new file with mode: 0644]
libgo/go/syscall/sockcmsg_unix.go
libgo/go/syscall/socket_linux.go
libgo/go/syscall/syscall_unix.go
libgo/go/testing/benchmark.go
libgo/go/testing/example.go
libgo/go/testing/testing.go
libgo/go/testing/wrapper.go [new file with mode: 0644]
libgo/go/text/template/exec.go
libgo/go/text/template/exec_test.go
libgo/go/time/sys_unix.go
libgo/go/time/time.go
libgo/go/time/time_test.go
libgo/go/time/zoneinfo_unix.go
libgo/go/websocket/server.go
libgo/merge.sh
libgo/runtime/goc2c.c
libgo/runtime/lock_futex.c
libgo/runtime/lock_sema.c
libgo/runtime/mprof.goc
libgo/runtime/runtime.h
libgo/runtime/string.goc

index 6c9de9b..a70a926 100644 (file)
@@ -12,9 +12,9 @@ func main() {
        var t testing.T
        
        // make sure error mentions that
-       // ch is unexported, not just "ch not found".
+       // name is unexported, not just "name not found".
 
-       t.ch = nil      // ERROR "unexported"
+       t.name = nil    // ERROR "unexported"
        
        println(testing.anyLowercaseName("asdf"))       // ERROR "unexported" "undefined: testing.anyLowercaseName"
 }
index bd71072..96fb7f6 100644 (file)
@@ -1,4 +1,4 @@
-82fdc445f2ff
+4a8268927758
 
 The first line of this file holds the Mercurial revision number of the
 last merge done from the master library sources.
index 3c34c4b..348a1ca 100644 (file)
@@ -279,8 +279,7 @@ toolexeclibgoimage_DATA = \
        image/gif.gox \
        image/jpeg.gox \
        image/png.gox \
-       image/tiff.gox \
-       image/ycbcr.gox
+       image/tiff.gox
 
 toolexeclibgoindexdir = $(toolexeclibgodir)/index
 
@@ -586,7 +585,8 @@ go_image_files = \
        go/image/format.go \
        go/image/geom.go \
        go/image/image.go \
-       go/image/names.go
+       go/image/names.go \
+       go/image/ycbcr.go
 
 go_io_files = \
        go/io/multi.go \
@@ -654,10 +654,15 @@ if LIBGO_IS_LINUX
 go_net_fd_os_file = go/net/fd_linux.go
 go_net_newpollserver_file = go/net/newpollserver.go
 else # !LIBGO_IS_LINUX && !LIBGO_IS_RTEMS
+if LIBGO_IS_NETBSD
+go_net_fd_os_file = go/net/fd_netbsd.go
+go_net_newpollserver_file = go/net/newpollserver.go
+else # !LIBGO_IS_NETBSD && !LIBGO_IS_LINUX && !LIBGO_IS_RTEMS
 # By default use select with pipes.  Most systems should have
 # something better.
 go_net_fd_os_file = go/net/fd_select.go
 go_net_newpollserver_file = go/net/newpollserver.go
+endif # !LIBGO_IS_NETBSD
 endif # !LIBGO_IS_LINUX
 endif # !LIBGO_IS_RTEMS
 
@@ -688,8 +693,12 @@ endif
 if LIBGO_IS_LINUX
 go_net_interface_file = go/net/interface_linux.go
 else
+if LIBGO_IS_NETBSD
+go_net_interface_file = go/net/interface_netbsd.go
+else
 go_net_interface_file = go/net/interface_stub.go
 endif
+endif
 
 go_net_files = \
        go/net/cgo_unix.go \
@@ -845,6 +854,7 @@ go_strconv_files = \
        go/strconv/atof.go \
        go/strconv/atoi.go \
        go/strconv/decimal.go \
+       go/strconv/extfloat.go \
        go/strconv/ftoa.go \
        go/strconv/itoa.go \
        go/strconv/quote.go
@@ -880,7 +890,8 @@ go_syslog_c_files = \
 go_testing_files = \
        go/testing/benchmark.go \
        go/testing/example.go \
-       go/testing/testing.go
+       go/testing/testing.go \
+       go/testing/wrapper.go
 
 go_time_files = \
        go/time/format.go \
@@ -1197,7 +1208,9 @@ go_go_build_files = \
 go_go_doc_files = \
        go/go/doc/comment.go \
        go/go/doc/doc.go \
-       go/go/doc/example.go
+       go/go/doc/example.go \
+       go/go/doc/exports.go \
+       go/go/doc/filter.go
 go_go_parser_files = \
        go/go/parser/interface.go \
        go/go/parser/parser.go
@@ -1241,7 +1254,8 @@ go_image_bmp_files = \
        go/image/bmp/reader.go
 
 go_image_color_files = \
-       go/image/color/color.go
+       go/image/color/color.go \
+       go/image/color/ycbcr.go
 
 go_image_draw_files = \
        go/image/draw/draw.go
@@ -1266,9 +1280,6 @@ go_image_tiff_files = \
        go/image/tiff/consts.go \
        go/image/tiff/reader.go
 
-go_image_ycbcr_files = \
-       go/image/ycbcr/ycbcr.go
-
 go_index_suffixarray_files = \
        go/index/suffixarray/qsufsort.go \
        go/index/suffixarray/suffixarray.go
@@ -1318,6 +1329,7 @@ go_net_http_files = \
        go/net/http/filetransport.go \
        go/net/http/fs.go \
        go/net/http/header.go \
+       go/net/http/jar.go \
        go/net/http/lex.go \
        go/net/http/request.go \
        go/net/http/response.go \
@@ -1761,7 +1773,6 @@ libgo_go_objs = \
        image/jpeg.lo \
        image/png.lo \
        image/tiff.lo \
-       image/ycbcr.lo \
        index/suffixarray.lo \
        io/ioutil.lo \
        log/syslog.lo \
@@ -3066,16 +3077,6 @@ image/tiff/check: $(CHECK_DEPS)
        @$(CHECK)
 .PHONY: image/tiff/check
 
-@go_include@ image/ycbcr.lo.dep
-image/ycbcr.lo.dep: $(go_image_ycbcr_files)
-       $(BUILDDEPS)
-image/ycbcr.lo: $(go_image_ycbcr_files)
-       $(BUILDPACKAGE)
-image/ycbcr/check: $(CHECK_DEPS)
-       @$(MKDIR_P) image/ycbcr
-       @$(CHECK)
-.PHONY: image/ycbcr/check
-
 @go_include@ index/suffixarray.lo.dep
 index/suffixarray.lo.dep: $(go_index_suffixarray_files)
        $(BUILDDEPS)
@@ -3728,8 +3729,6 @@ image/png.gox: image/png.lo
        $(BUILDGOX)
 image/tiff.gox: image/tiff.lo
        $(BUILDGOX)
-image/ycbcr.gox: image/ycbcr.lo
-       $(BUILDGOX)
 
 index/suffixarray.gox: index/suffixarray.lo
        $(BUILDGOX)
@@ -3938,11 +3937,11 @@ TEST_PACKAGES = \
        hash/crc32/check \
        hash/crc64/check \
        hash/fnv/check \
+       image/color/check \
        image/draw/check \
        image/jpeg/check \
        image/png/check \
        image/tiff/check \
-       image/ycbcr/check \
        index/suffixarray/check \
        io/ioutil/check \
        log/syslog/check \
index ebc63f0..6bf1847 100644 (file)
@@ -167,20 +167,19 @@ am__DEPENDENCIES_2 = bufio/bufio.lo bytes/bytes.lo bytes/index.lo \
        hash/fnv.lo net/http/cgi.lo net/http/fcgi.lo \
        net/http/httptest.lo net/http/httputil.lo net/http/pprof.lo \
        image/bmp.lo image/color.lo image/draw.lo image/gif.lo \
-       image/jpeg.lo image/png.lo image/tiff.lo image/ycbcr.lo \
-       index/suffixarray.lo io/ioutil.lo log/syslog.lo \
-       log/syslog/syslog_c.lo math/big.lo math/cmplx.lo math/rand.lo \
-       mime/mime.lo mime/multipart.lo net/dict.lo net/http.lo \
-       net/mail.lo net/rpc.lo net/smtp.lo net/textproto.lo net/url.lo \
-       old/netchan.lo old/regexp.lo old/template.lo \
-       $(am__DEPENDENCIES_1) os/user.lo os/signal.lo path/filepath.lo \
-       regexp/syntax.lo net/rpc/jsonrpc.lo runtime/debug.lo \
-       runtime/pprof.lo sync/atomic.lo sync/atomic_c.lo \
-       syscall/syscall.lo syscall/errno.lo syscall/wait.lo \
-       text/scanner.lo text/tabwriter.lo text/template.lo \
-       text/template/parse.lo testing/testing.lo testing/iotest.lo \
-       testing/quick.lo testing/script.lo unicode/utf16.lo \
-       unicode/utf8.lo
+       image/jpeg.lo image/png.lo image/tiff.lo index/suffixarray.lo \
+       io/ioutil.lo log/syslog.lo log/syslog/syslog_c.lo math/big.lo \
+       math/cmplx.lo math/rand.lo mime/mime.lo mime/multipart.lo \
+       net/dict.lo net/http.lo net/mail.lo net/rpc.lo net/smtp.lo \
+       net/textproto.lo net/url.lo old/netchan.lo old/regexp.lo \
+       old/template.lo $(am__DEPENDENCIES_1) os/user.lo os/signal.lo \
+       path/filepath.lo regexp/syntax.lo net/rpc/jsonrpc.lo \
+       runtime/debug.lo runtime/pprof.lo sync/atomic.lo \
+       sync/atomic_c.lo syscall/syscall.lo syscall/errno.lo \
+       syscall/wait.lo text/scanner.lo text/tabwriter.lo \
+       text/template.lo text/template/parse.lo testing/testing.lo \
+       testing/iotest.lo testing/quick.lo testing/script.lo \
+       unicode/utf16.lo unicode/utf8.lo
 libgo_la_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
        $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
        $(am__DEPENDENCIES_1)
@@ -732,8 +731,7 @@ toolexeclibgoimage_DATA = \
        image/gif.gox \
        image/jpeg.gox \
        image/png.gox \
-       image/tiff.gox \
-       image/ycbcr.gox
+       image/tiff.gox
 
 toolexeclibgoindexdir = $(toolexeclibgodir)/index
 toolexeclibgoindex_DATA = \
@@ -972,7 +970,8 @@ go_image_files = \
        go/image/format.go \
        go/image/geom.go \
        go/image/image.go \
-       go/image/names.go
+       go/image/names.go \
+       go/image/ycbcr.go
 
 go_io_files = \
        go/io/multi.go \
@@ -1034,10 +1033,12 @@ go_mime_files = \
 
 # By default use select with pipes.  Most systems should have
 # something better.
-@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_select.go
+@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_select.go
+@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_netbsd.go
 @LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_linux.go
 @LIBGO_IS_RTEMS_TRUE@go_net_fd_os_file = go/net/fd_select.go
-@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go
+@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go
+@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go
 @LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go
 @LIBGO_IS_RTEMS_TRUE@go_net_newpollserver_file = go/net/newpollserver_rtems.go
 @LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_cgo_file = go/net/cgo_bsd.go
@@ -1050,7 +1051,8 @@ go_mime_files = \
 @LIBGO_IS_LINUX_TRUE@go_net_sock_file = go/net/sock_linux.go
 @LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_stub.go
 @LIBGO_IS_LINUX_TRUE@go_net_sendfile_file = go/net/sendfile_linux.go
-@LIBGO_IS_LINUX_FALSE@go_net_interface_file = go/net/interface_stub.go
+@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@go_net_interface_file = go/net/interface_stub.go
+@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@go_net_interface_file = go/net/interface_netbsd.go
 @LIBGO_IS_LINUX_TRUE@go_net_interface_file = go/net/interface_linux.go
 go_net_files = \
        go/net/cgo_unix.go \
@@ -1165,6 +1167,7 @@ go_strconv_files = \
        go/strconv/atof.go \
        go/strconv/atoi.go \
        go/strconv/decimal.go \
+       go/strconv/extfloat.go \
        go/strconv/ftoa.go \
        go/strconv/itoa.go \
        go/strconv/quote.go
@@ -1194,7 +1197,8 @@ go_syslog_c_files = \
 go_testing_files = \
        go/testing/benchmark.go \
        go/testing/example.go \
-       go/testing/testing.go
+       go/testing/testing.go \
+       go/testing/wrapper.go
 
 go_time_files = \
        go/time/format.go \
@@ -1563,7 +1567,9 @@ go_go_build_files = \
 go_go_doc_files = \
        go/go/doc/comment.go \
        go/go/doc/doc.go \
-       go/go/doc/example.go
+       go/go/doc/example.go \
+       go/go/doc/exports.go \
+       go/go/doc/filter.go
 
 go_go_parser_files = \
        go/go/parser/interface.go \
@@ -1614,7 +1620,8 @@ go_image_bmp_files = \
        go/image/bmp/reader.go
 
 go_image_color_files = \
-       go/image/color/color.go
+       go/image/color/color.go \
+       go/image/color/ycbcr.go
 
 go_image_draw_files = \
        go/image/draw/draw.go
@@ -1639,9 +1646,6 @@ go_image_tiff_files = \
        go/image/tiff/consts.go \
        go/image/tiff/reader.go
 
-go_image_ycbcr_files = \
-       go/image/ycbcr/ycbcr.go
-
 go_index_suffixarray_files = \
        go/index/suffixarray/qsufsort.go \
        go/index/suffixarray/suffixarray.go
@@ -1694,6 +1698,7 @@ go_net_http_files = \
        go/net/http/filetransport.go \
        go/net/http/fs.go \
        go/net/http/header.go \
+       go/net/http/jar.go \
        go/net/http/lex.go \
        go/net/http/request.go \
        go/net/http/response.go \
@@ -2043,7 +2048,6 @@ libgo_go_objs = \
        image/jpeg.lo \
        image/png.lo \
        image/tiff.lo \
-       image/ycbcr.lo \
        index/suffixarray.lo \
        io/ioutil.lo \
        log/syslog.lo \
@@ -2299,11 +2303,11 @@ TEST_PACKAGES = \
        hash/crc32/check \
        hash/crc64/check \
        hash/fnv/check \
+       image/color/check \
        image/draw/check \
        image/jpeg/check \
        image/png/check \
        image/tiff/check \
-       image/ycbcr/check \
        index/suffixarray/check \
        io/ioutil/check \
        log/syslog/check \
@@ -5657,16 +5661,6 @@ image/tiff/check: $(CHECK_DEPS)
        @$(CHECK)
 .PHONY: image/tiff/check
 
-@go_include@ image/ycbcr.lo.dep
-image/ycbcr.lo.dep: $(go_image_ycbcr_files)
-       $(BUILDDEPS)
-image/ycbcr.lo: $(go_image_ycbcr_files)
-       $(BUILDPACKAGE)
-image/ycbcr/check: $(CHECK_DEPS)
-       @$(MKDIR_P) image/ycbcr
-       @$(CHECK)
-.PHONY: image/ycbcr/check
-
 @go_include@ index/suffixarray.lo.dep
 index/suffixarray.lo.dep: $(go_index_suffixarray_files)
        $(BUILDDEPS)
@@ -6314,8 +6308,6 @@ image/png.gox: image/png.lo
        $(BUILDGOX)
 image/tiff.gox: image/tiff.lo
        $(BUILDGOX)
-image/ycbcr.gox: image/ycbcr.lo
-       $(BUILDGOX)
 
 index/suffixarray.gox: index/suffixarray.lo
        $(BUILDGOX)
index c1ee7ba..7e8a38d 100755 (executable)
@@ -657,6 +657,8 @@ LIBGO_IS_SOLARIS_FALSE
 LIBGO_IS_SOLARIS_TRUE
 LIBGO_IS_RTEMS_FALSE
 LIBGO_IS_RTEMS_TRUE
+LIBGO_IS_NETBSD_FALSE
+LIBGO_IS_NETBSD_TRUE
 LIBGO_IS_LINUX_FALSE
 LIBGO_IS_LINUX_TRUE
 LIBGO_IS_IRIX_FALSE
@@ -11097,7 +11099,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11100 "configure"
+#line 11102 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11203,7 +11205,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11206 "configure"
+#line 11208 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -13473,6 +13475,7 @@ case ${host} in
   *-*-freebsd*)  is_freebsd=yes; GOOS=freebsd ;;
   *-*-irix6*)    is_irix=yes;    GOOS=irix ;;
   *-*-linux*)    is_linux=yes;   GOOS=linux ;;
+  *-*-netbsd*)  is_netbsd=yes;  GOOS=netbsd ;;
   *-*-rtems*)    is_rtems=yes;   GOOS=rtems ;;
   *-*-solaris2*) is_solaris=yes; GOOS=solaris ;;
 esac
@@ -13508,6 +13511,14 @@ else
   LIBGO_IS_LINUX_FALSE=
 fi
 
+ if test $is_netbsd = yes; then
+  LIBGO_IS_NETBSD_TRUE=
+  LIBGO_IS_NETBSD_FALSE='#'
+else
+  LIBGO_IS_NETBSD_TRUE='#'
+  LIBGO_IS_NETBSD_FALSE=
+fi
+
  if test $is_rtems = yes; then
   LIBGO_IS_RTEMS_TRUE=
   LIBGO_IS_RTEMS_FALSE='#'
@@ -14938,6 +14949,10 @@ if test -z "${LIBGO_IS_LINUX_TRUE}" && test -z "${LIBGO_IS_LINUX_FALSE}"; then
   as_fn_error "conditional \"LIBGO_IS_LINUX\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${LIBGO_IS_NETBSD_TRUE}" && test -z "${LIBGO_IS_NETBSD_FALSE}"; then
+  as_fn_error "conditional \"LIBGO_IS_NETBSD\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${LIBGO_IS_RTEMS_TRUE}" && test -z "${LIBGO_IS_RTEMS_FALSE}"; then
   as_fn_error "conditional \"LIBGO_IS_RTEMS\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
index a56106e..8132655 100644 (file)
@@ -134,6 +134,7 @@ case ${host} in
   *-*-freebsd*)  is_freebsd=yes; GOOS=freebsd ;;
   *-*-irix6*)    is_irix=yes;    GOOS=irix ;;
   *-*-linux*)    is_linux=yes;   GOOS=linux ;;
+  *-*-netbsd*)  is_netbsd=yes;  GOOS=netbsd ;;
   *-*-rtems*)    is_rtems=yes;   GOOS=rtems ;;
   *-*-solaris2*) is_solaris=yes; GOOS=solaris ;;
 esac
@@ -141,6 +142,7 @@ AM_CONDITIONAL(LIBGO_IS_DARWIN, test $is_darwin = yes)
 AM_CONDITIONAL(LIBGO_IS_FREEBSD, test $is_freebsd = yes)
 AM_CONDITIONAL(LIBGO_IS_IRIX, test $is_irix = yes)
 AM_CONDITIONAL(LIBGO_IS_LINUX, test $is_linux = yes)
+AM_CONDITIONAL(LIBGO_IS_NETBSD, test $is_netbsd = yes)
 AM_CONDITIONAL(LIBGO_IS_RTEMS, test $is_rtems = yes)
 AM_CONDITIONAL(LIBGO_IS_SOLARIS, test $is_solaris = yes)
 AC_SUBST(GOOS)
index 9594fe8..0e40268 100644 (file)
@@ -163,10 +163,10 @@ func readTestZip(t *testing.T, zt ZipTest) {
        done := make(chan bool)
        for i := 0; i < 5; i++ {
                for j, ft := range zt.File {
-                       go func() {
+                       go func(j int, ft ZipTestFile) {
                                readTestFile(t, ft, z.File[j])
                                done <- true
-                       }()
+                       }(j, ft)
                        n++
                }
        }
index c53a83c..34a87fa 100644 (file)
@@ -96,12 +96,28 @@ func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
        )
 }
 
+// timeToMsDosTime converts a time.Time to an MS-DOS date and time.
+// The resolution is 2s.
+// See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
+func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
+       t = t.In(time.UTC)
+       fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
+       fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
+       return
+}
+
 // ModTime returns the modification time.
 // The resolution is 2s.
 func (h *FileHeader) ModTime() time.Time {
        return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
 }
 
+// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time.
+// The resolution is 2s.
+func (h *FileHeader) SetModTime(t time.Time) {
+       h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
+}
+
 // traditional names for Unix constants
 const (
        s_IFMT  = 0xf000
index 2075715..8aab2b6 100644 (file)
@@ -11,6 +11,7 @@ import (
        "fmt"
        "io"
        "testing"
+       "time"
 )
 
 type stringReaderAt string
@@ -55,3 +56,13 @@ func TestOver65kFiles(t *testing.T) {
                }
        }
 }
+
+func TestModTime(t *testing.T) {
+       var testTime = time.Date(2009, time.November, 10, 23, 45, 58, 0, time.UTC)
+       fh := new(FileHeader)
+       fh.SetModTime(testTime)
+       outTime := fh.ModTime()
+       if !outTime.Equal(testTime) {
+               t.Errorf("times don't match: got %s, want %s", outTime, testTime)
+       }
+}
index 5235970..adb9330 100644 (file)
@@ -16,7 +16,6 @@ const N = 10000  // make this bigger for a larger (and slower) test
 var data string  // test data for write tests
 var bytes []byte // test data; same as data but as a slice.
 
-
 func init() {
        bytes = make([]byte, N)
        for i := 0; i < N; i++ {
index a2a08c2..2a1d41b 100644 (file)
@@ -289,8 +289,7 @@ func bmIndexByte(b *testing.B, index func([]byte, byte) int, n int) {
        for i := 0; i < b.N; i++ {
                j := index(buf, 'x')
                if j != n-1 {
-                       println("bad index", j)
-                       panic("bad index")
+                       b.Fatal("bad index", j)
                }
        }
        buf[n-1] = '\x00'
@@ -317,7 +316,7 @@ func bmEqual(b *testing.B, equal func([]byte, []byte) bool, n int) {
        for i := 0; i < b.N; i++ {
                eq := equal(buf1, buf2)
                if !eq {
-                       panic("bad equal")
+                       b.Fatal("bad equal")
                }
        }
        buf1[n-1] = '\x00'
@@ -339,8 +338,7 @@ func bmIndex(b *testing.B, index func([]byte, []byte) int, n int) {
        for i := 0; i < b.N; i++ {
                j := index(buf, buf[n-7:])
                if j != n-7 {
-                       println("bad index", j)
-                       panic("bad index")
+                       b.Fatal("bad index", j)
                }
        }
        buf[n-1] = '\x00'
@@ -362,8 +360,7 @@ func bmIndexEasy(b *testing.B, index func([]byte, []byte) int, n int) {
        for i := 0; i < b.N; i++ {
                j := index(buf, buf[n-7:])
                if j != n-7 {
-                       println("bad index", j)
-                       panic("bad index")
+                       b.Fatal("bad index", j)
                }
        }
        buf[n-1] = '\x00'
@@ -385,8 +382,7 @@ func bmCount(b *testing.B, count func([]byte, []byte) int, n int) {
        for i := 0; i < b.N; i++ {
                j := count(buf, buf[n-7:])
                if j != 1 {
-                       println("bad count", j)
-                       panic("bad count")
+                       b.Fatal("bad count", j)
                }
        }
        buf[n-1] = '\x00'
@@ -408,8 +404,7 @@ func bmCountEasy(b *testing.B, count func([]byte, []byte) int, n int) {
        for i := 0; i < b.N; i++ {
                j := count(buf, buf[n-7:])
                if j != 1 {
-                       println("bad count", j)
-                       panic("bad count")
+                       b.Fatal("bad count", j)
                }
        }
        buf[n-1] = '\x00'
diff --git a/libgo/go/bytes/example_test.go b/libgo/go/bytes/example_test.go
new file mode 100644 (file)
index 0000000..02da1ac
--- /dev/null
@@ -0,0 +1,24 @@
+package bytes_test
+
+import (
+       . "bytes"
+       "encoding/base64"
+       "io"
+       "os"
+)
+
+// Hello world!
+func ExampleBuffer() {
+       var b Buffer // A Buffer needs no initialization.
+       b.Write([]byte("Hello "))
+       b.Write([]byte("world!"))
+       b.WriteTo(os.Stdout)
+}
+
+// Gophers rule!
+func ExampleBuffer_reader() {
+       // A Buffer can turn a string or a []byte into an io.Reader.
+       buf := NewBufferString("R29waGVycyBydWxlIQ==")
+       dec := base64.NewDecoder(base64.StdEncoding, buf)
+       io.Copy(os.Stdout, dec)
+}
index aa1d0df..e500c66 100644 (file)
@@ -356,7 +356,7 @@ func BenchmarkEncrypt(b *testing.B) {
        tt := encryptTests[0]
        c, err := NewCipher(tt.key)
        if err != nil {
-               panic("NewCipher")
+               b.Fatal("NewCipher:", err)
        }
        out := make([]byte, len(tt.in))
        b.StartTimer()
index 53672a4..c913494 100644 (file)
@@ -71,3 +71,6 @@ func RegisterHash(h Hash, f func() hash.Hash) {
        }
        hashes[h] = f
 }
+
+// PrivateKey represents a private key using an unspecified algorithm.
+type PrivateKey interface{}
index df39970..74e7d23 100644 (file)
@@ -16,6 +16,7 @@ import (
 
 // PublicKeyType is the armor type for a PGP public key.
 var PublicKeyType = "PGP PUBLIC KEY BLOCK"
+
 // PrivateKeyType is the armor type for a PGP private key.
 var PrivateKeyType = "PGP PRIVATE KEY BLOCK"
 
index d9cddf6..5d4fc81 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin freebsd linux openbsd
+// +build darwin freebsd linux netbsd openbsd
 
 // Unix cryptographically secure pseudorandom number
 // generator.
index f57d932..a461ad9 100644 (file)
@@ -5,8 +5,8 @@
 package tls
 
 import (
+       "crypto"
        "crypto/rand"
-       "crypto/rsa"
        "crypto/x509"
        "io"
        "strings"
@@ -255,7 +255,7 @@ func (c *Config) BuildNameToCertificate() {
 // A Certificate is a chain of one or more certificates, leaf first.
 type Certificate struct {
        Certificate [][]byte
-       PrivateKey  *rsa.PrivateKey
+       PrivateKey  crypto.PrivateKey // supported types: *rsa.PrivateKey
        // OCSPStaple contains an optional OCSP response which will be served
        // to clients that request it.
        OCSPStaple []byte
index e39e59c..7364800 100644 (file)
@@ -234,7 +234,7 @@ func (c *Conn) clientHandshake() error {
                digest := make([]byte, 0, 36)
                digest = finishedHash.serverMD5.Sum(digest)
                digest = finishedHash.serverSHA1.Sum(digest)
-               signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey, crypto.MD5SHA1, digest)
+               signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, digest)
                if err != nil {
                        return c.sendAlert(alertInternalError)
                }
index b531717..c3c1664 100644 (file)
@@ -44,7 +44,7 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe
                ciphertext = ckx.ciphertext[2:]
        }
 
-       err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey, ciphertext, preMasterSecret)
+       err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey.(*rsa.PrivateKey), ciphertext, preMasterSecret)
        if err != nil {
                return nil, err
        }
@@ -147,7 +147,7 @@ Curve:
        copy(serverECDHParams[4:], ecdhePublic)
 
        md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams)
-       sig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey, crypto.MD5SHA1, md5sha1)
+       sig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, md5sha1)
        if err != nil {
                return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
        }
index 1512241..db1b18b 100644 (file)
@@ -8,7 +8,7 @@ package tls
 // Note: We disable -Werror here because the code in this file uses a deprecated API to stay
 // compatible with both Mac OS X 10.6 and 10.7. Using a deprecated function on Darwin generates
 // a warning.
-#cgo CFLAGS: -Wno-error
+#cgo CFLAGS: -Wno-error -Wno-deprecated-declarations
 #cgo LDFLAGS: -framework CoreFoundation -framework Security
 #include <CoreFoundation/CoreFoundation.h>
 #include <Security/Security.h>
index 18dcb02..d00493a 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build plan9
+// +build plan9 darwin/nocgo
 
 package tls
 
index adc7f9b..5a0a876 100644 (file)
@@ -28,6 +28,9 @@ func NewCertPool() *CertPool {
 // given certificate. If no such certificate can be found or the signature
 // doesn't match, it returns nil.
 func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int) {
+       if s == nil {
+               return
+       }
        var candidates []int
 
        if len(cert.AuthorityKeyId) > 0 {
index df54430..2016858 100644 (file)
@@ -19,6 +19,7 @@ type verifyTest struct {
        roots         []string
        currentTime   int64
        dnsName       string
+       nilRoots      bool
 
        errorCallback  func(*testing.T, int, error) bool
        expectedChains [][]string
@@ -48,6 +49,14 @@ var verifyTests = []verifyTest{
        {
                leaf:          googleLeaf,
                intermediates: []string{thawteIntermediate},
+               nilRoots:      true, // verifies that we don't crash
+               currentTime:   1302726541,
+               dnsName:       "www.google.com",
+               errorCallback: expectAuthorityUnknown,
+       },
+       {
+               leaf:          googleLeaf,
+               intermediates: []string{thawteIntermediate},
                roots:         []string{verisignRoot},
                currentTime:   1,
                dnsName:       "www.example.com",
@@ -136,6 +145,9 @@ func TestVerify(t *testing.T) {
                        DNSName:       test.dnsName,
                        CurrentTime:   time.Unix(test.currentTime, 0),
                }
+               if test.nilRoots {
+                       opts.Roots = nil
+               }
 
                for j, root := range test.roots {
                        ok := opts.Roots.AppendCertsFromPEM([]byte(root))
index 65ca315..28c7880 100644 (file)
@@ -981,6 +981,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P
 // pemCRLPrefix is the magic string that indicates that we have a PEM encoded
 // CRL.
 var pemCRLPrefix = []byte("-----BEGIN X509 CRL")
+
 // pemType is the type of a PEM encoded CRL.
 var pemType = "X509 CRL"
 
index 562e7a0..e5c2988 100644 (file)
@@ -13,7 +13,8 @@ import (
 
 func dotest() bool {
        // For now, only works on ELF platforms.
-       return syscall.OS == "linux" && os.Getenv("GOARCH") == "amd64"
+       // TODO: convert to work with new go tool
+       return false && syscall.OS == "linux" && os.Getenv("GOARCH") == "amd64"
 }
 
 func getTable(t *testing.T) *Table {
index fd4fdb0..3e7057e 100644 (file)
@@ -171,11 +171,42 @@ func (br *byteSliceReader) Read(p []byte) (int, error) {
        return n, nil
 }
 
-func BenchmarkRead(b *testing.B) {
+func BenchmarkReadSlice1000Int32s(b *testing.B) {
+       bsr := &byteSliceReader{}
+       slice := make([]int32, 1000)
+       buf := make([]byte, len(slice)*4)
+       b.SetBytes(int64(len(buf)))
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               bsr.remain = buf
+               Read(bsr, BigEndian, slice)
+       }
+}
+
+func BenchmarkReadStruct(b *testing.B) {
+       bsr := &byteSliceReader{}
+       var buf bytes.Buffer
+       Write(&buf, BigEndian, &s)
+       n := TotalSize(reflect.ValueOf(s))
+       b.SetBytes(int64(n))
+       t := s
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               bsr.remain = buf.Bytes()
+               Read(bsr, BigEndian, &t)
+       }
+       b.StopTimer()
+       if !reflect.DeepEqual(s, t) {
+               b.Fatal("no match")
+       }
+}
+
+func BenchmarkReadInts(b *testing.B) {
        var ls Struct
        bsr := &byteSliceReader{}
        var r io.Reader = bsr
-
+       b.SetBytes(2 * (1 + 2 + 4 + 8))
+       b.ResetTimer()
        for i := 0; i < b.N; i++ {
                bsr.remain = big
                Read(r, BigEndian, &ls.Int8)
@@ -196,25 +227,19 @@ func BenchmarkRead(b *testing.B) {
        for i := range want.Array {
                want.Array[i] = 0
        }
+       b.StopTimer()
        if !reflect.DeepEqual(ls, want) {
                panic("no match")
        }
 }
 
-func BenchmarkWrite(b *testing.B) {
+func BenchmarkWriteInts(b *testing.B) {
        buf := new(bytes.Buffer)
        var w io.Writer = buf
-
+       b.SetBytes(2 * (1 + 2 + 4 + 8))
+       b.ResetTimer()
        for i := 0; i < b.N; i++ {
                buf.Reset()
-               Write(w, BigEndian, &s.Int8)
-               Write(w, BigEndian, &s.Int16)
-               Write(w, BigEndian, &s.Int32)
-               Write(w, BigEndian, &s.Int64)
-               Write(w, BigEndian, &s.Uint8)
-               Write(w, BigEndian, &s.Uint16)
-               Write(w, BigEndian, &s.Uint32)
-               Write(w, BigEndian, &s.Uint64)
                Write(w, BigEndian, s.Int8)
                Write(w, BigEndian, s.Int16)
                Write(w, BigEndian, s.Int32)
@@ -224,11 +249,8 @@ func BenchmarkWrite(b *testing.B) {
                Write(w, BigEndian, s.Uint32)
                Write(w, BigEndian, s.Uint64)
        }
-
-       if !bytes.Equal(buf.Bytes()[:30], big[:30]) {
-               panic("first half doesn't match")
-       }
-       if !bytes.Equal(buf.Bytes()[30:], big[:30]) {
-               panic("second half doesn't match")
+       b.StopTimer()
+       if !bytes.Equal(buf.Bytes(), big[:30]) {
+               b.Fatalf("first half doesn't match: %x %x", buf.Bytes(), big[:30])
        }
 }
index b553d6d..dc550f2 100644 (file)
@@ -165,6 +165,7 @@ func TestNonCanonicalZero(t *testing.T) {
 
 func BenchmarkPutUvarint32(b *testing.B) {
        buf := make([]byte, MaxVarintLen32)
+       b.SetBytes(4)
        for i := 0; i < b.N; i++ {
                for j := uint(0); j < MaxVarintLen32; j++ {
                        PutUvarint(buf, 1<<(j*7))
@@ -174,6 +175,7 @@ func BenchmarkPutUvarint32(b *testing.B) {
 
 func BenchmarkPutUvarint64(b *testing.B) {
        buf := make([]byte, MaxVarintLen64)
+       b.SetBytes(8)
        for i := 0; i < b.N; i++ {
                for j := uint(0); j < MaxVarintLen64; j++ {
                        PutUvarint(buf, 1<<(j*7))
index dc0e007..73844b9 100644 (file)
@@ -102,12 +102,15 @@ func TestIntCodec(t *testing.T) {
 
 // The result of encoding a true boolean with field number 7
 var boolResult = []byte{0x07, 0x01}
+
 // The result of encoding a number 17 with field number 7
 var signedResult = []byte{0x07, 2 * 17}
 var unsignedResult = []byte{0x07, 17}
 var floatResult = []byte{0x07, 0xFE, 0x31, 0x40}
+
 // The result of encoding a number 17+19i with field number 7
 var complexResult = []byte{0x07, 0xFE, 0x31, 0x40, 0xFE, 0x33, 0x40}
+
 // The result of encoding "hello" with field number 7
 var bytesResult = []byte{0x07, 0x05, 'h', 'e', 'l', 'l', 'o'}
 
index c7e4823..f05b17c 100644 (file)
@@ -469,7 +469,14 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) {
 // isZero returns whether the value is the zero of its type.
 func isZero(val reflect.Value) bool {
        switch val.Kind() {
-       case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+       case reflect.Array:
+               for i := 0; i < val.Len(); i++ {
+                       if !isZero(val.Index(i)) {
+                               return false
+                       }
+               }
+               return true
+       case reflect.Map, reflect.Slice, reflect.String:
                return val.Len() == 0
        case reflect.Bool:
                return !val.Bool()
@@ -483,6 +490,13 @@ func isZero(val reflect.Value) bool {
                return val.Float() == 0
        case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
                return val.Uint() == 0
+       case reflect.Struct:
+               for i := 0; i < val.NumField(); i++ {
+                       if !isZero(val.Field(i)) {
+                               return false
+                       }
+               }
+               return true
        }
        panic("unknown type in isZero " + val.Type().String())
 }
index eacfd84..b8dfeeb 100644 (file)
@@ -13,6 +13,7 @@ import (
        "io"
        "strings"
        "testing"
+       "time"
 )
 
 // Types that implement the GobEncoder/Decoder interfaces.
@@ -526,3 +527,50 @@ func TestGobEncoderExtraIndirect(t *testing.T) {
                t.Errorf("got = %q, want %q", got, gdb)
        }
 }
+
+// Another bug: this caused a crash with the new Go1 Time type.
+// We throw in a gob-encoding array, to test another case of isZero
+
+type isZeroBug struct {
+       T time.Time
+       S string
+       I int
+       A isZeroBugArray
+}
+
+type isZeroBugArray [2]uint8
+
+// Receiver is value, not pointer, to test isZero of array.
+func (a isZeroBugArray) GobEncode() (b []byte, e error) {
+       b = append(b, a[:]...)
+       return b, nil
+}
+
+func (a *isZeroBugArray) GobDecode(data []byte) error {
+       println("DECODE")
+       if len(data) != len(a) {
+               return io.EOF
+       }
+       a[0] = data[0]
+       a[1] = data[1]
+       return nil
+}
+
+func TestGobEncodeIsZero(t *testing.T) {
+       x := isZeroBug{time.Now(), "hello", -55, isZeroBugArray{1, 2}}
+       b := new(bytes.Buffer)
+       enc := NewEncoder(b)
+       err := enc.Encode(x)
+       if err != nil {
+               t.Fatal("encode:", err)
+       }
+       var y isZeroBug
+       dec := NewDecoder(b)
+       err = dec.Decode(&y)
+       if err != nil {
+               t.Fatal("decode:", err)
+       }
+       if x != y {
+               t.Fatalf("%v != %v", x, y)
+       }
+}
index 47437a6..1017eb7 100644 (file)
@@ -39,7 +39,7 @@ func benchmarkEndToEnd(r io.Reader, w io.Writer, b *testing.B) {
 func BenchmarkEndToEndPipe(b *testing.B) {
        r, w, err := os.Pipe()
        if err != nil {
-               panic("can't get pipe:" + err.Error())
+               b.Fatal("can't get pipe:", err)
        }
        benchmarkEndToEnd(r, w, b)
 }
index 1b20843..71a28be 100644 (file)
@@ -130,6 +130,7 @@ func userType(rt reflect.Type) *userTypeInfo {
        }
        return ut
 }
+
 // A typeId represents a gob Type as an integer that can be passed on the wire.
 // Internally, typeIds are used as keys to a map to recover the underlying type info.
 type typeId int32
index f0c5201..333c1c0 100644 (file)
@@ -84,7 +84,7 @@ func BenchmarkCodeEncoder(b *testing.B) {
        enc := NewEncoder(ioutil.Discard)
        for i := 0; i < b.N; i++ {
                if err := enc.Encode(&codeStruct); err != nil {
-                       panic(err)
+                       b.Fatal("Encode:", err)
                }
        }
        b.SetBytes(int64(len(codeJSON)))
@@ -98,7 +98,7 @@ func BenchmarkCodeMarshal(b *testing.B) {
        }
        for i := 0; i < b.N; i++ {
                if _, err := Marshal(&codeStruct); err != nil {
-                       panic(err)
+                       b.Fatal("Marshal:", err)
                }
        }
        b.SetBytes(int64(len(codeJSON)))
@@ -120,7 +120,7 @@ func BenchmarkCodeDecoder(b *testing.B) {
                buf.WriteByte('\n')
                buf.WriteByte('\n')
                if err := dec.Decode(&r); err != nil {
-                       panic(err)
+                       b.Fatal("Decode:", err)
                }
        }
        b.SetBytes(int64(len(codeJSON)))
@@ -135,7 +135,7 @@ func BenchmarkCodeUnmarshal(b *testing.B) {
        for i := 0; i < b.N; i++ {
                var r codeResponse
                if err := Unmarshal(codeJSON, &r); err != nil {
-                       panic(err)
+                       b.Fatal("Unmmarshal:", err)
                }
        }
        b.SetBytes(int64(len(codeJSON)))
@@ -150,7 +150,7 @@ func BenchmarkCodeUnmarshalReuse(b *testing.B) {
        var r codeResponse
        for i := 0; i < b.N; i++ {
                if err := Unmarshal(codeJSON, &r); err != nil {
-                       panic(err)
+                       b.Fatal("Unmmarshal:", err)
                }
        }
        b.SetBytes(int64(len(codeJSON)))
index 0a70092..8287b33 100644 (file)
@@ -228,7 +228,9 @@ func (d *decodeState) value(v reflect.Value) {
                // Feed in an empty string - the shortest, simplest value -
                // so that it knows we got to the end of the value.
                if d.scan.redo {
-                       panic("redo")
+                       // rewind.
+                       d.scan.redo = false
+                       d.scan.step = stateBeginValue
                }
                d.scan.step(&d.scan, '"')
                d.scan.step(&d.scan, '"')
@@ -317,25 +319,22 @@ func (d *decodeState) array(v reflect.Value) {
        }
        v = pv
 
-       // Decoding into nil interface?  Switch to non-reflect code.
-       iv := v
-       ok := iv.Kind() == reflect.Interface
-       if ok {
-               iv.Set(reflect.ValueOf(d.arrayInterface()))
-               return
-       }
-
        // Check type of target.
-       av := v
-       if av.Kind() != reflect.Array && av.Kind() != reflect.Slice {
+       switch v.Kind() {
+       default:
                d.saveError(&UnmarshalTypeError{"array", v.Type()})
                d.off--
                d.next()
                return
+       case reflect.Interface:
+               // Decoding into nil interface?  Switch to non-reflect code.
+               v.Set(reflect.ValueOf(d.arrayInterface()))
+               return
+       case reflect.Array:
+       case reflect.Slice:
+               break
        }
 
-       sv := v
-
        i := 0
        for {
                // Look ahead for ] - can only happen on first iteration.
@@ -349,23 +348,25 @@ func (d *decodeState) array(v reflect.Value) {
                d.scan.undo(op)
 
                // Get element of array, growing if necessary.
-               if i >= av.Cap() && sv.IsValid() {
-                       newcap := sv.Cap() + sv.Cap()/2
-                       if newcap < 4 {
-                               newcap = 4
+               if v.Kind() == reflect.Slice {
+                       // Grow slice if necessary
+                       if i >= v.Cap() {
+                               newcap := v.Cap() + v.Cap()/2
+                               if newcap < 4 {
+                                       newcap = 4
+                               }
+                               newv := reflect.MakeSlice(v.Type(), v.Len(), newcap)
+                               reflect.Copy(newv, v)
+                               v.Set(newv)
+                       }
+                       if i >= v.Len() {
+                               v.SetLen(i + 1)
                        }
-                       newv := reflect.MakeSlice(sv.Type(), sv.Len(), newcap)
-                       reflect.Copy(newv, sv)
-                       sv.Set(newv)
-               }
-               if i >= av.Len() && sv.IsValid() {
-                       // Must be slice; gave up on array during i >= av.Cap().
-                       sv.SetLen(i + 1)
                }
 
-               // Decode into element.
-               if i < av.Len() {
-                       d.value(av.Index(i))
+               if i < v.Len() {
+                       // Decode into element.
+                       d.value(v.Index(i))
                } else {
                        // Ran out of fixed array: skip.
                        d.value(reflect.Value{})
@@ -382,19 +383,19 @@ func (d *decodeState) array(v reflect.Value) {
                }
        }
 
-       if i < av.Len() {
-               if !sv.IsValid() {
+       if i < v.Len() {
+               if v.Kind() == reflect.Array {
                        // Array.  Zero the rest.
-                       z := reflect.Zero(av.Type().Elem())
-                       for ; i < av.Len(); i++ {
-                               av.Index(i).Set(z)
+                       z := reflect.Zero(v.Type().Elem())
+                       for ; i < v.Len(); i++ {
+                               v.Index(i).Set(z)
                        }
                } else {
-                       sv.SetLen(i)
+                       v.SetLen(i)
                }
        }
-       if i == 0 && av.Kind() == reflect.Slice && sv.IsNil() {
-               sv.Set(reflect.MakeSlice(sv.Type(), 0, 0))
+       if i == 0 && v.Kind() == reflect.Slice {
+               v.Set(reflect.MakeSlice(v.Type(), 0, 0))
        }
 }
 
index bf3953e..05c8a06 100644 (file)
@@ -6,6 +6,7 @@ package json
 
 import (
        "bytes"
+       "fmt"
        "reflect"
        "strings"
        "testing"
@@ -73,6 +74,12 @@ var unmarshalTests = []unmarshalTest{
 
        // syntax errors
        {`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}},
+       {`[1, 2, 3+]`, nil, nil, &SyntaxError{"invalid character '+' after array element", 9}},
+
+       // array tests
+       {`[1, 2, 3]`, new([3]int), [3]int{1, 2, 3}, nil},
+       {`[1, 2, 3]`, new([1]int), [1]int{1}, nil},
+       {`[1, 2, 3]`, new([5]int), [5]int{1, 2, 3, 0, 0}, nil},
 
        // composite tests
        {allValueIndent, new(All), allValue, nil},
@@ -242,6 +249,38 @@ func TestHTMLEscape(t *testing.T) {
        }
 }
 
+// WrongString is a struct that's misusing the ,string modifier.
+type WrongString struct {
+       Message string `json:"result,string"`
+}
+
+type wrongStringTest struct {
+       in, err string
+}
+
+// TODO(bradfitz): as part of Issue 2331, fix these tests' expected
+// error values to be helpful, rather than the confusing messages they
+// are now.
+var wrongStringTests = []wrongStringTest{
+       {`{"result":"x"}`, "JSON decoder out of sync - data changing underfoot?"},
+       {`{"result":"foo"}`, "json: cannot unmarshal bool into Go value of type string"},
+       {`{"result":"123"}`, "json: cannot unmarshal number into Go value of type string"},
+}
+
+// If people misuse the ,string modifier, the error message should be
+// helpful, telling the user that they're doing it wrong.
+func TestErrorMessageFromMisusedString(t *testing.T) {
+       for n, tt := range wrongStringTests {
+               r := strings.NewReader(tt.in)
+               var s WrongString
+               err := NewDecoder(r).Decode(&s)
+               got := fmt.Sprintf("%v", err)
+               if got != tt.err {
+                       t.Errorf("%d. got err = %q, want %q", n, got, tt.err)
+               }
+       }
+}
+
 func noSpace(c rune) rune {
        if isSpace(c) {
                return -1
index ff8e80c..3d2f4fc 100644 (file)
@@ -197,6 +197,7 @@ var hex = "0123456789abcdef"
 // An encodeState encodes JSON into a bytes.Buffer.
 type encodeState struct {
        bytes.Buffer // accumulated output
+       scratch      [64]byte
 }
 
 func (e *encodeState) marshal(v interface{}) (err error) {
@@ -275,14 +276,26 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
                }
 
        case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-               writeString(e, strconv.FormatInt(v.Int(), 10))
-
+               b := strconv.AppendInt(e.scratch[:0], v.Int(), 10)
+               if quoted {
+                       writeString(e, string(b))
+               } else {
+                       e.Write(b)
+               }
        case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
-               writeString(e, strconv.FormatUint(v.Uint(), 10))
-
+               b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10)
+               if quoted {
+                       writeString(e, string(b))
+               } else {
+                       e.Write(b)
+               }
        case reflect.Float32, reflect.Float64:
-               writeString(e, strconv.FormatFloat(v.Float(), 'g', -1, v.Type().Bits()))
-
+               b := strconv.AppendFloat(e.scratch[:0], v.Float(), 'g', -1, v.Type().Bits())
+               if quoted {
+                       writeString(e, string(b))
+               } else {
+                       e.Write(b)
+               }
        case reflect.String:
                if quoted {
                        sb, err := Marshal(v.String())
index 8040765..6a24169 100644 (file)
@@ -394,7 +394,7 @@ func TestUnmarshal(t *testing.T) {
                if err != nil {
                        t.Errorf("#%d: unexpected error: %#v", i, err)
                } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
-                       t.Errorf("#%d: unmarshal(%#s) = %#v, want %#v", i, test.ExpectXML, got, want)
+                       t.Errorf("#%d: unmarshal(%q) = %#v, want %#v", i, test.ExpectXML, got, want)
                }
        }
 }
index 92384b6..d035ec1 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build linux
+
 package inotify
 
 import (
@@ -17,6 +19,9 @@ func TestInotifyEvents(t *testing.T) {
                t.Fatalf("NewWatcher() failed: %s", err)
        }
 
+       t.Logf("NEEDS TO BE CONVERTED TO NEW GO TOOL") // TODO
+       return
+
        // Add a watch for "_test"
        err = watcher.Watch("_test")
        if err != nil {
index 948b911..937982c 100644 (file)
@@ -22,10 +22,10 @@ var drivers = make(map[string]driver.Driver)
 // it panics.
 func Register(name string, driver driver.Driver) {
        if driver == nil {
-               panic("db: Register driver is nil")
+               panic("sql: Register driver is nil")
        }
        if _, dup := drivers[name]; dup {
-               panic("db: Register called twice for driver " + name)
+               panic("sql: Register called twice for driver " + name)
        }
        drivers[name] = driver
 }
@@ -80,7 +80,7 @@ type ScannerInto interface {
 // ErrNoRows is returned by Scan when QueryRow doesn't return a
 // row. In such a case, QueryRow returns a placeholder *Row value that
 // defers this error until a Scan.
-var ErrNoRows = errors.New("db: no rows in result set")
+var ErrNoRows = errors.New("sql: no rows in result set")
 
 // DB is a database handle. It's safe for concurrent use by multiple
 // goroutines.
@@ -102,7 +102,7 @@ type DB struct {
 func Open(driverName, dataSourceName string) (*DB, error) {
        driver, ok := drivers[driverName]
        if !ok {
-               return nil, fmt.Errorf("db: unknown driver %q (forgotten import?)", driverName)
+               return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
        }
        return &DB{driver: driver, dsn: dataSourceName}, nil
 }
@@ -514,7 +514,7 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
        // placeholders, so we won't sanity check input here and instead let the
        // driver deal with errors.
        if want := si.NumInput(); want != -1 && len(args) != want {
-               return nil, fmt.Errorf("db: expected %d arguments, got %d", want, len(args))
+               return nil, fmt.Errorf("sql: expected %d arguments, got %d", want, len(args))
        }
 
        // Convert args to subset types.
@@ -522,10 +522,10 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
                for n, arg := range args {
                        args[n], err = cc.ColumnConverter(n).ConvertValue(arg)
                        if err != nil {
-                               return nil, fmt.Errorf("db: converting Exec argument #%d's type: %v", n, err)
+                               return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
                        }
                        if !driver.IsParameterSubsetType(args[n]) {
-                               return nil, fmt.Errorf("db: driver ColumnConverter error converted %T to unsupported type %T",
+                               return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T",
                                        arg, args[n])
                        }
                }
@@ -533,7 +533,7 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
                for n, arg := range args {
                        args[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
                        if err != nil {
-                               return nil, fmt.Errorf("db: converting Exec argument #%d's type: %v", n, err)
+                               return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
                        }
                }
        }
@@ -555,7 +555,7 @@ func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(), si driver.Stmt, e
        s.mu.Lock()
        if s.closed {
                s.mu.Unlock()
-               err = errors.New("db: statement is closed")
+               err = errors.New("sql: statement is closed")
                return
        }
 
@@ -617,7 +617,7 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
        // placeholders, so we won't sanity check input here and instead let the
        // driver deal with errors.
        if want := si.NumInput(); want != -1 && len(args) != want {
-               return nil, fmt.Errorf("db: statement expects %d inputs; got %d", si.NumInput(), len(args))
+               return nil, fmt.Errorf("sql: statement expects %d inputs; got %d", si.NumInput(), len(args))
        }
        sargs, err := subsetTypeArgs(args)
        if err != nil {
@@ -737,27 +737,40 @@ func (rs *Rows) Err() error {
        return rs.lasterr
 }
 
+// Columns returns the column names.
+// Columns returns an error if the rows are closed, or if the rows
+// are from QueryRow and there was a deferred error.
+func (rs *Rows) Columns() ([]string, error) {
+       if rs.closed {
+               return nil, errors.New("sql: Rows are closed")
+       }
+       if rs.rowsi == nil {
+               return nil, errors.New("sql: no Rows available")
+       }
+       return rs.rowsi.Columns(), nil
+}
+
 // Scan copies the columns in the current row into the values pointed
 // at by dest. If dest contains pointers to []byte, the slices should
 // not be modified and should only be considered valid until the next
 // call to Next or Scan.
 func (rs *Rows) Scan(dest ...interface{}) error {
        if rs.closed {
-               return errors.New("db: Rows closed")
+               return errors.New("sql: Rows closed")
        }
        if rs.lasterr != nil {
                return rs.lasterr
        }
        if rs.lastcols == nil {
-               return errors.New("db: Scan called without calling Next")
+               return errors.New("sql: Scan called without calling Next")
        }
        if len(dest) != len(rs.lastcols) {
-               return fmt.Errorf("db: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest))
+               return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest))
        }
        for i, sv := range rs.lastcols {
                err := convertAssign(dest[i], sv)
                if err != nil {
-                       return fmt.Errorf("db: Scan error on column index %d: %v", i, err)
+                       return fmt.Errorf("sql: Scan error on column index %d: %v", i, err)
                }
        }
        return nil
index f8ccf76..5307a23 100644 (file)
@@ -75,6 +75,23 @@ func TestQuery(t *testing.T) {
        }
 }
 
+func TestRowsColumns(t *testing.T) {
+       db := newTestDB(t, "people")
+       defer closeDB(t, db)
+       rows, err := db.Query("SELECT|people|age,name|")
+       if err != nil {
+               t.Fatalf("Query: %v", err)
+       }
+       cols, err := rows.Columns()
+       if err != nil {
+               t.Fatalf("Columns: %v", err)
+       }
+       want := []string{"age", "name"}
+       if !reflect.DeepEqual(cols, want) {
+               t.Errorf("got %#v; want %#v", cols, want)
+       }
+}
+
 func TestQueryRow(t *testing.T) {
        db := newTestDB(t, "people")
        defer closeDB(t, db)
@@ -187,12 +204,12 @@ func TestExec(t *testing.T) {
                {[]interface{}{7, 9}, ""},
 
                // Invalid conversions:
-               {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "db: converting Exec argument #1's type: sql/driver: value 4294967295 overflows int32"},
-               {[]interface{}{"Brad", "strconv fail"}, "db: converting Exec argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"},
+               {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting Exec argument #1's type: sql/driver: value 4294967295 overflows int32"},
+               {[]interface{}{"Brad", "strconv fail"}, "sql: converting Exec argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"},
 
                // Wrong number of args:
-               {[]interface{}{}, "db: expected 2 arguments, got 0"},
-               {[]interface{}{1, 2, 3}, "db: expected 2 arguments, got 3"},
+               {[]interface{}{}, "sql: expected 2 arguments, got 0"},
+               {[]interface{}{1, 2, 3}, "sql: expected 2 arguments, got 3"},
        }
        for n, et := range execTests {
                _, err := stmt.Exec(et.args...)
index 1a38235..3a7e9fb 100644 (file)
@@ -283,8 +283,8 @@ func (p *publickeyAuth) method() string {
        return "publickey"
 }
 
-// ClientAuthPublickey returns a ClientAuth using public key authentication.
-func ClientAuthPublickey(impl ClientKeyring) ClientAuth {
+// ClientAuthKeyring returns a ClientAuth using public key authentication.
+func ClientAuthKeyring(impl ClientKeyring) ClientAuth {
        return &publickeyAuth{impl}
 }
 
index 2b89e97..c41a93b 100644 (file)
@@ -122,7 +122,7 @@ var (
                PasswordCallback: func(user, pass string) bool {
                        return user == "testuser" && pass == string(clientPassword)
                },
-               PubKeyCallback: func(user, algo string, pubkey []byte) bool {
+               PublicKeyCallback: func(user, algo string, pubkey []byte) bool {
                        key := clientKeychain.keys[0].(*rsa.PrivateKey).PublicKey
                        expected := []byte(serializePublickey(key))
                        algoname := algoName(key)
@@ -179,7 +179,7 @@ func TestClientAuthPublickey(t *testing.T) {
        config := &ClientConfig{
                User: "testuser",
                Auth: []ClientAuth{
-                       ClientAuthPublickey(clientKeychain),
+                       ClientAuthKeyring(clientKeychain),
                },
        }
        c, err := Dial("tcp", newMockAuthServer(t), config)
@@ -210,7 +210,7 @@ func TestClientAuthWrongPassword(t *testing.T) {
                User: "testuser",
                Auth: []ClientAuth{
                        ClientAuthPassword(wrongPw),
-                       ClientAuthPublickey(clientKeychain),
+                       ClientAuthKeyring(clientKeychain),
                },
        }
 
@@ -228,7 +228,7 @@ func TestClientAuthInvalidPublickey(t *testing.T) {
        config := &ClientConfig{
                User: "testuser",
                Auth: []ClientAuth{
-                       ClientAuthPublickey(kc),
+                       ClientAuthKeyring(kc),
                },
        }
 
@@ -246,7 +246,7 @@ func TestClientAuthRSAandDSA(t *testing.T) {
        config := &ClientConfig{
                User: "testuser",
                Auth: []ClientAuth{
-                       ClientAuthPublickey(kc),
+                       ClientAuthKeyring(kc),
                },
        }
        c, err := Dial("tcp", newMockAuthServer(t), config)
index 24e3a63..b4bdba9 100644 (file)
@@ -50,7 +50,7 @@ func TestFuncPublickeyAuth(t *testing.T) {
        config := &ClientConfig{
                User: *sshuser,
                Auth: []ClientAuth{
-                       ClientAuthPublickey(kc),
+                       ClientAuthKeyring(kc),
                },
        }
        conn, err := Dial("tcp", "localhost:22", config)
index 1eee9a4..31011c6 100644 (file)
@@ -36,10 +36,10 @@ type ServerConfig struct {
        // several goroutines.
        PasswordCallback func(user, password string) bool
 
-       // PubKeyCallback, if non-nil, is called when a client attempts public
+       // PublicKeyCallback, if non-nil, is called when a client attempts public
        // key authentication. It must return true iff the given public key is
        // valid for the given user.
-       PubKeyCallback func(user, algo string, pubkey []byte) bool
+       PublicKeyCallback func(user, algo string, pubkey []byte) bool
 
        // Cryptographic-related configuration.
        Crypto CryptoConfig
@@ -359,7 +359,7 @@ func isAcceptableAlgo(algo string) bool {
 
 // testPubKey returns true if the given public key is acceptable for the user.
 func (s *ServerConn) testPubKey(user, algo string, pubKey []byte) bool {
-       if s.config.PubKeyCallback == nil || !isAcceptableAlgo(algo) {
+       if s.config.PublicKeyCallback == nil || !isAcceptableAlgo(algo) {
                return false
        }
 
@@ -369,7 +369,7 @@ func (s *ServerConn) testPubKey(user, algo string, pubKey []byte) bool {
                }
        }
 
-       result := s.config.PubKeyCallback(user, algo, pubKey)
+       result := s.config.PublicKeyCallback(user, algo, pubKey)
        if len(s.cachedPubKeys) < maxCachedPubKeys {
                c := cachedPubKey{
                        user:   user,
@@ -425,7 +425,7 @@ userAuthLoop:
                                break userAuthLoop
                        }
                case "publickey":
-                       if s.config.PubKeyCallback == nil {
+                       if s.config.PublicKeyCallback == nil {
                                break
                        }
                        payload := userAuthReq.Payload
@@ -499,7 +499,7 @@ userAuthLoop:
                if s.config.PasswordCallback != nil {
                        failureMsg.Methods = append(failureMsg.Methods, "password")
                }
-               if s.config.PubKeyCallback != nil {
+               if s.config.PublicKeyCallback != nil {
                        failureMsg.Methods = append(failureMsg.Methods, "publickey")
                }
 
index bf9a88e..807dd87 100644 (file)
@@ -68,10 +68,12 @@ type Session struct {
 
        *clientChan // the channel backing this session
 
-       started        bool // true once Start, Run or Shell is invoked.
-       closeAfterWait []io.Closer
-       copyFuncs      []func() error
-       errch          chan error // one send per copyFunc
+       started   bool // true once Start, Run or Shell is invoked.
+       copyFuncs []func() error
+       errch     chan error // one send per copyFunc
+
+       // true if pipe method is active
+       stdinpipe, stdoutpipe, stderrpipe bool
 }
 
 // RFC 4254 Section 6.4.
@@ -237,11 +239,9 @@ func (s *Session) waitForResponse() error {
 func (s *Session) start() error {
        s.started = true
 
-       type F func(*Session) error
+       type F func(*Session)
        for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} {
-               if err := setupFd(s); err != nil {
-                       return err
-               }
+               setupFd(s)
        }
 
        s.errch = make(chan error, len(s.copyFuncs))
@@ -274,9 +274,6 @@ func (s *Session) Wait() error {
                        copyError = err
                }
        }
-       for _, fd := range s.closeAfterWait {
-               fd.Close()
-       }
        if waitErr != nil {
                return waitErr
        }
@@ -341,7 +338,10 @@ func (s *Session) wait() error {
        return &ExitError{wm}
 }
 
-func (s *Session) stdin() error {
+func (s *Session) stdin() {
+       if s.stdinpipe {
+               return
+       }
        if s.Stdin == nil {
                s.Stdin = new(bytes.Buffer)
        }
@@ -352,10 +352,12 @@ func (s *Session) stdin() error {
                }
                return err
        })
-       return nil
 }
 
-func (s *Session) stdout() error {
+func (s *Session) stdout() {
+       if s.stdoutpipe {
+               return
+       }
        if s.Stdout == nil {
                s.Stdout = ioutil.Discard
        }
@@ -363,10 +365,12 @@ func (s *Session) stdout() error {
                _, err := io.Copy(s.Stdout, s.clientChan.stdout)
                return err
        })
-       return nil
 }
 
-func (s *Session) stderr() error {
+func (s *Session) stderr() {
+       if s.stderrpipe {
+               return
+       }
        if s.Stderr == nil {
                s.Stderr = ioutil.Discard
        }
@@ -374,7 +378,6 @@ func (s *Session) stderr() error {
                _, err := io.Copy(s.Stderr, s.clientChan.stderr)
                return err
        })
-       return nil
 }
 
 // StdinPipe returns a pipe that will be connected to the
@@ -386,10 +389,8 @@ func (s *Session) StdinPipe() (io.WriteCloser, error) {
        if s.started {
                return nil, errors.New("ssh: StdinPipe after process started")
        }
-       pr, pw := io.Pipe()
-       s.Stdin = pr
-       s.closeAfterWait = append(s.closeAfterWait, pr)
-       return pw, nil
+       s.stdinpipe = true
+       return s.clientChan.stdin, nil
 }
 
 // StdoutPipe returns a pipe that will be connected to the
@@ -398,17 +399,15 @@ func (s *Session) StdinPipe() (io.WriteCloser, error) {
 // stdout and stderr streams. If the StdoutPipe reader is
 // not serviced fast enought it may eventually cause the
 // remote command to block.
-func (s *Session) StdoutPipe() (io.ReadCloser, error) {
+func (s *Session) StdoutPipe() (io.Reader, error) {
        if s.Stdout != nil {
                return nil, errors.New("ssh: Stdout already set")
        }
        if s.started {
                return nil, errors.New("ssh: StdoutPipe after process started")
        }
-       pr, pw := io.Pipe()
-       s.Stdout = pw
-       s.closeAfterWait = append(s.closeAfterWait, pw)
-       return pr, nil
+       s.stdoutpipe = true
+       return s.clientChan.stdout, nil
 }
 
 // StderrPipe returns a pipe that will be connected to the
@@ -417,17 +416,15 @@ func (s *Session) StdoutPipe() (io.ReadCloser, error) {
 // stdout and stderr streams. If the StderrPipe reader is
 // not serviced fast enought it may eventually cause the
 // remote command to block.
-func (s *Session) StderrPipe() (io.ReadCloser, error) {
+func (s *Session) StderrPipe() (io.Reader, error) {
        if s.Stderr != nil {
                return nil, errors.New("ssh: Stderr already set")
        }
        if s.started {
                return nil, errors.New("ssh: StderrPipe after process started")
        }
-       pr, pw := io.Pipe()
-       s.Stderr = pw
-       s.closeAfterWait = append(s.closeAfterWait, pw)
-       return pr, nil
+       s.stderrpipe = true
+       return s.clientChan.stderr, nil
 }
 
 // TODO(dfc) add Output and CombinedOutput helpers
index a28ead0..2882620 100644 (file)
@@ -20,7 +20,7 @@ func dial(handler serverType, t *testing.T) *ClientConn {
        serverConfig.PasswordCallback = func(user, pass string) bool {
                return user == "testuser" && pass == string(pw)
        }
-       serverConfig.PubKeyCallback = nil
+       serverConfig.PublicKeyCallback = nil
 
        l, err := Listen("tcp", "127.0.0.1:0", serverConfig)
        if err != nil {
index a85044a..bee41ee 100644 (file)
@@ -10,6 +10,7 @@ import (
        "io"
        "net"
 )
+
 // Dial initiates a connection to the addr from the remote host.
 // addr is resolved using net.ResolveTCPAddr before connection. 
 // This could allow an observer to observe the DNS name of the 
index 18d76cd..809e88c 100644 (file)
@@ -2,13 +2,56 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build linux
+
 package terminal
 
-import "io"
+import (
+       "io"
+       "sync"
+)
+
+// EscapeCodes contains escape sequences that can be written to the terminal in
+// order to achieve different styles of text.
+type EscapeCodes struct {
+       // Foreground colors
+       Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
+
+       // Reset all attributes
+       Reset []byte
+}
+
+var vt100EscapeCodes = EscapeCodes{
+       Black:   []byte{keyEscape, '[', '3', '0', 'm'},
+       Red:     []byte{keyEscape, '[', '3', '1', 'm'},
+       Green:   []byte{keyEscape, '[', '3', '2', 'm'},
+       Yellow:  []byte{keyEscape, '[', '3', '3', 'm'},
+       Blue:    []byte{keyEscape, '[', '3', '4', 'm'},
+       Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
+       Cyan:    []byte{keyEscape, '[', '3', '6', 'm'},
+       White:   []byte{keyEscape, '[', '3', '7', 'm'},
+
+       Reset: []byte{keyEscape, '[', '0', 'm'},
+}
 
 // Terminal contains the state for running a VT100 terminal that is capable of
 // reading lines of input.
 type Terminal struct {
+       // AutoCompleteCallback, if non-null, is called for each keypress
+       // with the full input line and the current position of the cursor.
+       // If it returns a nil newLine, the key press is processed normally.
+       // Otherwise it returns a replacement line and the new cursor position.
+       AutoCompleteCallback func(line []byte, pos, key int) (newLine []byte, newPos int)
+
+       // Escape contains a pointer to the escape codes for this terminal.
+       // It's always a valid pointer, although the escape codes themselves
+       // may be empty if the terminal doesn't support them.
+       Escape *EscapeCodes
+
+       // lock protects the terminal and the state in this object from
+       // concurrent processing of a key press and a Write() call.
+       lock sync.Mutex
+
        c      io.ReadWriter
        prompt string
 
@@ -16,6 +59,8 @@ type Terminal struct {
        line []byte
        // pos is the logical position of the cursor in line
        pos int
+       // echo is true if local echo is enabled
+       echo bool
 
        // cursorX contains the current X value of the cursor where the left
        // edge is 0. cursorY contains the row number where the first row of
@@ -40,10 +85,12 @@ type Terminal struct {
 // "> ").
 func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
        return &Terminal{
+               Escape:     &vt100EscapeCodes,
                c:          c,
                prompt:     prompt,
                termWidth:  80,
                termHeight: 24,
+               echo:       true,
        }
 }
 
@@ -109,18 +156,11 @@ func bytesToKey(b []byte) (int, []byte) {
 
 // queue appends data to the end of t.outBuf
 func (t *Terminal) queue(data []byte) {
-       if len(t.outBuf)+len(data) > cap(t.outBuf) {
-               newOutBuf := make([]byte, len(t.outBuf), 2*(len(t.outBuf)+len(data)))
-               copy(newOutBuf, t.outBuf)
-               t.outBuf = newOutBuf
-       }
-
-       oldLen := len(t.outBuf)
-       t.outBuf = t.outBuf[:len(t.outBuf)+len(data)]
-       copy(t.outBuf[oldLen:], data)
+       t.outBuf = append(t.outBuf, data...)
 }
 
 var eraseUnderCursor = []byte{' ', keyEscape, '[', 'D'}
+var space = []byte{' '}
 
 func isPrintable(key int) bool {
        return key >= 32 && key < 127
@@ -129,6 +169,10 @@ func isPrintable(key int) bool {
 // moveCursorToPos appends data to t.outBuf which will move the cursor to the
 // given, logical position in the text.
 func (t *Terminal) moveCursorToPos(pos int) {
+       if !t.echo {
+               return
+       }
+
        x := len(t.prompt) + pos
        y := x / t.termWidth
        x = x % t.termWidth
@@ -153,6 +197,12 @@ func (t *Terminal) moveCursorToPos(pos int) {
                right = x - t.cursorX
        }
 
+       t.cursorX = x
+       t.cursorY = y
+       t.move(up, down, left, right)
+}
+
+func (t *Terminal) move(up, down, left, right int) {
        movement := make([]byte, 3*(up+down+left+right))
        m := movement
        for i := 0; i < up; i++ {
@@ -180,11 +230,14 @@ func (t *Terminal) moveCursorToPos(pos int) {
                m = m[3:]
        }
 
-       t.cursorX = x
-       t.cursorY = y
        t.queue(movement)
 }
 
+func (t *Terminal) clearLineToRight() {
+       op := []byte{keyEscape, '[', 'K'}
+       t.queue(op)
+}
+
 const maxLineLength = 4096
 
 // handleKey processes the given key and, optionally, returns a line of text
@@ -196,12 +249,15 @@ func (t *Terminal) handleKey(key int) (line string, ok bool) {
                        return
                }
                t.pos--
+               t.moveCursorToPos(t.pos)
 
                copy(t.line[t.pos:], t.line[1+t.pos:])
                t.line = t.line[:len(t.line)-1]
-               t.writeLine(t.line[t.pos:])
-               t.moveCursorToPos(t.pos)
+               if t.echo {
+                       t.writeLine(t.line[t.pos:])
+               }
                t.queue(eraseUnderCursor)
+               t.moveCursorToPos(t.pos)
        case keyAltLeft:
                // move left by a word.
                if t.pos == 0 {
@@ -260,6 +316,25 @@ func (t *Terminal) handleKey(key int) (line string, ok bool) {
                t.cursorY = 0
                t.maxLine = 0
        default:
+               if t.AutoCompleteCallback != nil {
+                       t.lock.Unlock()
+                       newLine, newPos := t.AutoCompleteCallback(t.line, t.pos, key)
+                       t.lock.Lock()
+
+                       if newLine != nil {
+                               if t.echo {
+                                       t.moveCursorToPos(0)
+                                       t.writeLine(newLine)
+                                       for i := len(newLine); i < len(t.line); i++ {
+                                               t.writeLine(space)
+                                       }
+                                       t.moveCursorToPos(newPos)
+                               }
+                               t.line = newLine
+                               t.pos = newPos
+                               return
+                       }
+               }
                if !isPrintable(key) {
                        return
                }
@@ -274,7 +349,9 @@ func (t *Terminal) handleKey(key int) (line string, ok bool) {
                t.line = t.line[:len(t.line)+1]
                copy(t.line[t.pos+1:], t.line[t.pos:])
                t.line[t.pos] = byte(key)
-               t.writeLine(t.line[t.pos:])
+               if t.echo {
+                       t.writeLine(t.line[t.pos:])
+               }
                t.pos++
                t.moveCursorToPos(t.pos)
        }
@@ -283,15 +360,6 @@ func (t *Terminal) handleKey(key int) (line string, ok bool) {
 
 func (t *Terminal) writeLine(line []byte) {
        for len(line) != 0 {
-               if t.cursorX == t.termWidth {
-                       t.queue([]byte("\r\n"))
-                       t.cursorX = 0
-                       t.cursorY++
-                       if t.cursorY > t.maxLine {
-                               t.maxLine = t.cursorY
-                       }
-               }
-
                remainingOnLine := t.termWidth - t.cursorX
                todo := len(line)
                if todo > remainingOnLine {
@@ -300,16 +368,95 @@ func (t *Terminal) writeLine(line []byte) {
                t.queue(line[:todo])
                t.cursorX += todo
                line = line[todo:]
+
+               if t.cursorX == t.termWidth {
+                       t.cursorX = 0
+                       t.cursorY++
+                       if t.cursorY > t.maxLine {
+                               t.maxLine = t.cursorY
+                       }
+               }
        }
 }
 
 func (t *Terminal) Write(buf []byte) (n int, err error) {
-       return t.c.Write(buf)
+       t.lock.Lock()
+       defer t.lock.Unlock()
+
+       if t.cursorX == 0 && t.cursorY == 0 {
+               // This is the easy case: there's nothing on the screen that we
+               // have to move out of the way.
+               return t.c.Write(buf)
+       }
+
+       // We have a prompt and possibly user input on the screen. We
+       // have to clear it first.
+       t.move(0, /* up */ 0, /* down */ t.cursorX, /* left */ 0 /* right */ )
+       t.cursorX = 0
+       t.clearLineToRight()
+
+       for t.cursorY > 0 {
+               t.move(1, /* up */ 0, 0, 0)
+               t.cursorY--
+               t.clearLineToRight()
+       }
+
+       if _, err = t.c.Write(t.outBuf); err != nil {
+               return
+       }
+       t.outBuf = t.outBuf[:0]
+
+       if n, err = t.c.Write(buf); err != nil {
+               return
+       }
+
+       t.queue([]byte(t.prompt))
+       chars := len(t.prompt)
+       if t.echo {
+               t.queue(t.line)
+               chars += len(t.line)
+       }
+       t.cursorX = chars % t.termWidth
+       t.cursorY = chars / t.termWidth
+       t.moveCursorToPos(t.pos)
+
+       if _, err = t.c.Write(t.outBuf); err != nil {
+               return
+       }
+       t.outBuf = t.outBuf[:0]
+       return
+}
+
+// ReadPassword temporarily changes the prompt and reads a password, without
+// echo, from the terminal.
+func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
+       t.lock.Lock()
+       defer t.lock.Unlock()
+
+       oldPrompt := t.prompt
+       t.prompt = prompt
+       t.echo = false
+
+       line, err = t.readLine()
+
+       t.prompt = oldPrompt
+       t.echo = true
+
+       return
 }
 
 // ReadLine returns a line of input from the terminal.
 func (t *Terminal) ReadLine() (line string, err error) {
-       if t.cursorX == 0 {
+       t.lock.Lock()
+       defer t.lock.Unlock()
+
+       return t.readLine()
+}
+
+func (t *Terminal) readLine() (line string, err error) {
+       // t.lock must be held at this point
+
+       if t.cursorX == 0 && t.cursorY == 0 {
                t.writeLine([]byte(t.prompt))
                t.c.Write(t.outBuf)
                t.outBuf = t.outBuf[:0]
@@ -320,7 +467,11 @@ func (t *Terminal) ReadLine() (line string, err error) {
                // containing a partial key sequence
                readBuf := t.inBuf[len(t.remainder):]
                var n int
+
+               t.lock.Unlock()
                n, err = t.c.Read(readBuf)
+               t.lock.Lock()
+
                if err != nil {
                        return
                }
@@ -358,5 +509,8 @@ func (t *Terminal) ReadLine() (line string, err error) {
 }
 
 func (t *Terminal) SetSize(width, height int) {
+       t.lock.Lock()
+       defer t.lock.Unlock()
+
        t.termWidth, t.termHeight = width, height
 }
index a219721..75628f6 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build linux
+
 package terminal
 
 import (
index 0303567..a5bbfca 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build linux
+
 // Package terminal provides support functions for dealing with terminals, as
 // commonly found on UNIX systems.
 //
@@ -9,7 +11,7 @@
 //
 //     oldState, err := terminal.MakeRaw(0)
 //     if err != nil {
-//             panic(err.String())
+//             panic(err)
 //     }
 //     defer terminal.Restore(0, oldState)
 package terminal
@@ -17,6 +19,7 @@ package terminal
 import (
        "io"
        "syscall"
+       "unsafe"
 )
 
 // State contains the state of a terminal.
@@ -57,6 +60,18 @@ func Restore(fd int, state *State) error {
        return err
 }
 
+func ioctl(int, int, unsafe.Pointer) int __asm__("ioctl")
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+       var dimensions [4]uint16
+
+       if ioctl(fd, syscall.TIOCGWINSZ, unsafe.Pointer(&dimensions)) < 0 {
+               return -1, -1, syscall.GetErrno()
+       }
+       return int(dimensions[1]), int(dimensions[0]), nil
+}
+
 // ReadPassword reads a line of input from a terminal without local echo.  This
 // is commonly used for inputting passwords and other sensitive data. The slice
 // returned does not include the \n.
index d47ffd1..a6e3a6a 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build windows
+
 // Package winfsnotify allows the user to receive
 // file system event notifications on Windows.
 package winfsnotify
index b9c43d9..59ac162 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build windows
+
 package winfsnotify
 
 import (
index d58421b..3b79873 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build windows
+
 package main
 
 import (
index 24f3dd4..f876088 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build windows
+
 package main
 
 import (
index b062ca3..5666c6d 100644 (file)
@@ -1,3 +1,4 @@
+// +build windows
 // mksyscall_windows.pl winapi.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
index d7fe296..d34a4f8 100644 (file)
@@ -506,78 +506,42 @@ func BenchmarkSprintfFloat(b *testing.B) {
        }
 }
 
+var mallocBuf bytes.Buffer
+
+var mallocTest = []struct {
+       count int
+       desc  string
+       fn    func()
+}{
+       {0, `Sprintf("")`, func() { Sprintf("") }},
+       {1, `Sprintf("xxx")`, func() { Sprintf("xxx") }},
+       {1, `Sprintf("%x")`, func() { Sprintf("%x", 7) }},
+       {2, `Sprintf("%s")`, func() { Sprintf("%s", "hello") }},
+       {1, `Sprintf("%x %x")`, func() { Sprintf("%x", 7, 112) }},
+       {1, `Sprintf("%g")`, func() { Sprintf("%g", 3.14159) }},
+       {0, `Fprintf(buf, "%x %x %x")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%x %x %x", 7, 8, 9) }},
+       {1, `Fprintf(buf, "%s")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%s", "hello") }},
+}
+
+var _ bytes.Buffer
+
 func TestCountMallocs(t *testing.T) {
        if testing.Short() {
                return
        }
-       const N = 100
-       runtime.UpdateMemStats()
-       mallocs := 0 - runtime.MemStats.Mallocs
-       for i := 0; i < N; i++ {
-               Sprintf("")
-       }
-       runtime.UpdateMemStats()
-       mallocs += runtime.MemStats.Mallocs
-       Printf("mallocs per Sprintf(\"\"): %d\n", mallocs/N)
-       runtime.UpdateMemStats()
-       mallocs = 0 - runtime.MemStats.Mallocs
-       for i := 0; i < N; i++ {
-               Sprintf("xxx")
-       }
-       runtime.UpdateMemStats()
-       mallocs += runtime.MemStats.Mallocs
-       Printf("mallocs per Sprintf(\"xxx\"): %d\n", mallocs/N)
-       runtime.UpdateMemStats()
-       mallocs = 0 - runtime.MemStats.Mallocs
-       for i := 0; i < N; i++ {
-               Sprintf("%x", i)
-       }
-       runtime.UpdateMemStats()
-       mallocs += runtime.MemStats.Mallocs
-       Printf("mallocs per Sprintf(\"%%x\"): %d\n", mallocs/N)
-       runtime.UpdateMemStats()
-       mallocs = 0 - runtime.MemStats.Mallocs
-       for i := 0; i < N; i++ {
-               Sprintf("%s", "hello")
-       }
-       runtime.UpdateMemStats()
-       mallocs += runtime.MemStats.Mallocs
-       Printf("mallocs per Sprintf(\"%%s\"): %d\n", mallocs/N)
-       runtime.UpdateMemStats()
-       mallocs = 0 - runtime.MemStats.Mallocs
-       for i := 0; i < N; i++ {
-               Sprintf("%x %x", i, i)
-       }
-       runtime.UpdateMemStats()
-       mallocs += runtime.MemStats.Mallocs
-       Printf("mallocs per Sprintf(\"%%x %%x\"): %d\n", mallocs/N)
-       runtime.UpdateMemStats()
-       mallocs = 0 - runtime.MemStats.Mallocs
-       for i := 0; i < N; i++ {
-               Sprintf("%g", 3.14159)
-       }
-       runtime.UpdateMemStats()
-       mallocs += runtime.MemStats.Mallocs
-       Printf("mallocs per Sprintf(\"%%g\"): %d\n", mallocs/N)
-       buf := new(bytes.Buffer)
-       runtime.UpdateMemStats()
-       mallocs = 0 - runtime.MemStats.Mallocs
-       for i := 0; i < N; i++ {
-               buf.Reset()
-               Fprintf(buf, "%x %x %x", i, i, i)
-       }
-       runtime.UpdateMemStats()
-       mallocs += runtime.MemStats.Mallocs
-       Printf("mallocs per Fprintf(buf, \"%%x %%x %%x\"): %d\n", mallocs/N)
-       runtime.UpdateMemStats()
-       mallocs = 0 - runtime.MemStats.Mallocs
-       for i := 0; i < N; i++ {
-               buf.Reset()
-               Fprintf(buf, "%s", "hello")
+       for _, mt := range mallocTest {
+               const N = 100
+               runtime.UpdateMemStats()
+               mallocs := 0 - runtime.MemStats.Mallocs
+               for i := 0; i < N; i++ {
+                       mt.fn()
+               }
+               runtime.UpdateMemStats()
+               mallocs += runtime.MemStats.Mallocs
+               if mallocs/N != uint64(mt.count) {
+                       t.Errorf("%s: expected %d mallocs, got %d", mt.desc, mt.count, mallocs/N)
+               }
        }
-       runtime.UpdateMemStats()
-       mallocs += runtime.MemStats.Mallocs
-       Printf("mallocs per Fprintf(buf, \"%%s\"): %d\n", mallocs/N)
 }
 
 type flagPrinter struct{}
index 5f62c06..78d9e99 100644 (file)
@@ -154,12 +154,17 @@ func putint(buf []byte, base, val uint64, digits string) int {
        return i - 1
 }
 
+var (
+       trueBytes  = []byte("true")
+       falseBytes = []byte("false")
+)
+
 // fmt_boolean formats a boolean.
 func (f *fmt) fmt_boolean(v bool) {
        if v {
-               f.padString("true")
+               f.pad(trueBytes)
        } else {
-               f.padString("false")
+               f.pad(falseBytes)
        }
 }
 
@@ -283,31 +288,18 @@ func (f *fmt) fmt_s(s string) {
 }
 
 // fmt_sx formats a string as a hexadecimal encoding of its bytes.
-func (f *fmt) fmt_sx(s string) {
-       t := ""
+func (f *fmt) fmt_sx(s, digits string) {
+       // TODO: Avoid buffer by pre-padding.
+       var b bytes.Buffer
        for i := 0; i < len(s); i++ {
                if i > 0 && f.space {
-                       t += " "
+                       b.WriteByte(' ')
                }
                v := s[i]
-               t += string(ldigits[v>>4])
-               t += string(ldigits[v&0xF])
+               b.WriteByte(digits[v>>4])
+               b.WriteByte(digits[v&0xF])
        }
-       f.padString(t)
-}
-
-// fmt_sX formats a string as an uppercase hexadecimal encoding of its bytes.
-func (f *fmt) fmt_sX(s string) {
-       t := ""
-       for i := 0; i < len(s); i++ {
-               if i > 0 && f.space {
-                       t += " "
-               }
-               v := s[i]
-               t += string(udigits[v>>4])
-               t += string(udigits[v&0xF])
-       }
-       f.padString(t)
+       f.pad(b.Bytes())
 }
 
 // fmt_q formats a string as a double-quoted, escaped Go string constant.
@@ -329,13 +321,13 @@ func (f *fmt) fmt_q(s string) {
 // fmt_qc formats the integer as a single-quoted, escaped Go character constant.
 // If the character is not valid Unicode, it will print '\ufffd'.
 func (f *fmt) fmt_qc(c int64) {
-       var quoted string
+       var quoted []byte
        if f.plus {
-               quoted = strconv.QuoteRuneToASCII(rune(c))
+               quoted = strconv.AppendQuoteRuneToASCII(f.intbuf[0:0], rune(c))
        } else {
-               quoted = strconv.QuoteRune(rune(c))
+               quoted = strconv.AppendQuoteRune(f.intbuf[0:0], rune(c))
        }
-       f.padString(quoted)
+       f.pad(quoted)
 }
 
 // floating-point
@@ -347,57 +339,70 @@ func doPrec(f *fmt, def int) int {
        return def
 }
 
-// Add a plus sign or space to the floating-point string representation if missing and required.
-func (f *fmt) plusSpace(s string) {
-       if s[0] != '-' {
+// formatFloat formats a float64; it is an efficient equivalent to  f.pad(strconv.FormatFloat()...).
+func (f *fmt) formatFloat(v float64, verb byte, prec, n int) {
+       // We leave one byte at the beginning of f.intbuf for a sign if needed,
+       // and make it a space, which we might be able to use.
+       f.intbuf[0] = ' '
+       slice := strconv.AppendFloat(f.intbuf[0:1], v, verb, prec, n)
+       // Add a plus sign or space to the floating-point string representation if missing and required.
+       // The formatted number starts at slice[1].
+       switch slice[1] {
+       case '-', '+':
+               // We're set; drop the leading space.
+               slice = slice[1:]
+       default:
+               // There's no sign, but we might need one.
                if f.plus {
-                       s = "+" + s
+                       slice[0] = '+'
                } else if f.space {
-                       s = " " + s
+                       // space is already there
+               } else {
+                       slice = slice[1:]
                }
        }
-       f.padString(s)
+       f.pad(slice)
 }
 
 // fmt_e64 formats a float64 in the form -1.23e+12.
-func (f *fmt) fmt_e64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'e', doPrec(f, 6), 64)) }
+func (f *fmt) fmt_e64(v float64) { f.formatFloat(v, 'e', doPrec(f, 6), 64) }
 
 // fmt_E64 formats a float64 in the form -1.23E+12.
-func (f *fmt) fmt_E64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'E', doPrec(f, 6), 64)) }
+func (f *fmt) fmt_E64(v float64) { f.formatFloat(v, 'E', doPrec(f, 6), 64) }
 
 // fmt_f64 formats a float64 in the form -1.23.
-func (f *fmt) fmt_f64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'f', doPrec(f, 6), 64)) }
+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.plusSpace(strconv.FormatFloat(v, 'g', doPrec(f, -1), 64)) }
+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.
-func (f *fmt) fmt_G64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'G', doPrec(f, -1), 64)) }
+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).
-func (f *fmt) fmt_fb64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'b', 0, 64)) }
+func (f *fmt) fmt_fb64(v float64) { f.formatFloat(v, 'b', 0, 64) }
 
 // float32
 // cannot defer to float64 versions
 // because it will get rounding wrong in corner cases.
 
 // fmt_e32 formats a float32 in the form -1.23e+12.
-func (f *fmt) fmt_e32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'e', doPrec(f, 6), 32)) }
+func (f *fmt) fmt_e32(v float32) { f.formatFloat(float64(v), 'e', doPrec(f, 6), 32) }
 
 // fmt_E32 formats a float32 in the form -1.23E+12.
-func (f *fmt) fmt_E32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'E', doPrec(f, 6), 32)) }
+func (f *fmt) fmt_E32(v float32) { f.formatFloat(float64(v), 'E', doPrec(f, 6), 32) }
 
 // fmt_f32 formats a float32 in the form -1.23.
-func (f *fmt) fmt_f32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'f', doPrec(f, 6), 32)) }
+func (f *fmt) fmt_f32(v float32) { f.formatFloat(float64(v), 'f', doPrec(f, 6), 32) }
 
 // fmt_g32 formats a float32 in the 'f' or 'e' form according to size.
-func (f *fmt) fmt_g32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'g', doPrec(f, -1), 32)) }
+func (f *fmt) fmt_g32(v float32) { f.formatFloat(float64(v), 'g', doPrec(f, -1), 32) }
 
 // fmt_G32 formats a float32 in the 'f' or 'E' form according to size.
-func (f *fmt) fmt_G32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'G', doPrec(f, -1), 32)) }
+func (f *fmt) fmt_G32(v float32) { f.formatFloat(float64(v), 'G', doPrec(f, -1), 32) }
 
 // fmt_fb32 formats a float32 in the form -123p3 (exponent is power of 2).
-func (f *fmt) fmt_fb32(v float32) { f.padString(strconv.FormatFloat(float64(v), 'b', 0, 32)) }
+func (f *fmt) fmt_fb32(v float32) { f.formatFloat(float64(v), 'b', 0, 32) }
 
 // fmt_c64 formats a complex64 according to the verb.
 func (f *fmt) fmt_c64(v complex64, verb rune) {
index 9f157da..3b7d346 100644 (file)
@@ -503,9 +503,9 @@ func (p *pp) fmtString(v string, verb rune, goSyntax bool) {
        case 's':
                p.fmt.fmt_s(v)
        case 'x':
-               p.fmt.fmt_sx(v)
+               p.fmt.fmt_sx(v, ldigits)
        case 'X':
-               p.fmt.fmt_sX(v)
+               p.fmt.fmt_sx(v, udigits)
        case 'q':
                p.fmt.fmt_q(v)
        default:
@@ -542,9 +542,9 @@ func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, depth int) {
        case 's':
                p.fmt.fmt_s(s)
        case 'x':
-               p.fmt.fmt_sx(s)
+               p.fmt.fmt_sx(s, ldigits)
        case 'X':
-               p.fmt.fmt_sX(s)
+               p.fmt.fmt_sx(s, udigits)
        case 'q':
                p.fmt.fmt_q(s)
        default:
index 92e3669..fbe4779 100644 (file)
@@ -80,7 +80,7 @@ func (s *Scope) String() string {
 type Object struct {
        Kind ObjKind
        Name string      // declared name
-       Decl interface{} // corresponding Field, XxxSpec, FuncDecl, or LabeledStmt; or nil
+       Decl interface{} // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, or AssignStmt; or nil
        Data interface{} // object-specific data; or nil
        Type interface{} // place holder for type information; may be nil
 }
@@ -125,6 +125,12 @@ func (obj *Object) Pos() token.Pos {
                if d.Label.Name == name {
                        return d.Label.Pos()
                }
+       case *AssignStmt:
+               for _, x := range d.Lhs {
+                       if ident, isIdent := x.(*Ident); isIdent && ident.Name == name {
+                               return ident.Pos()
+                       }
+               }
        }
        return token.NoPos
 }
index e22a49a..fd40306 100644 (file)
@@ -46,8 +46,9 @@ var buildPkgs = []struct {
        {
                "go/build/cgotest",
                &DirInfo{
-                       CgoFiles:    []string{"cgotest.go"},
+                       CgoFiles:    ifCgo([]string{"cgotest.go"}),
                        CFiles:      []string{"cgotest.c"},
+                       HFiles:      []string{"cgotest.h"},
                        Imports:     []string{"C", "unsafe"},
                        TestImports: []string{},
                        Package:     "cgotest",
@@ -55,6 +56,13 @@ var buildPkgs = []struct {
        },
 }
 
+func ifCgo(x []string) []string {
+       if DefaultContext.CgoEnabled {
+               return x
+       }
+       return nil
+}
+
 const cmdtestOutput = "3"
 
 func TestBuild(t *testing.T) {
@@ -71,6 +79,10 @@ func TestBuild(t *testing.T) {
                        continue
                }
 
+               if tt.dir == "go/build/cgotest" && len(info.CgoFiles) == 0 {
+                       continue
+               }
+
                s, err := Build(tree, tt.dir, info)
                if err != nil {
                        t.Errorf("Build(%#q): %v", tt.dir, err)
index 2c89224..265261f 100644 (file)
@@ -26,9 +26,9 @@ import (
 
 // A Context specifies the supporting context for a build.
 type Context struct {
-       GOARCH string // target architecture
-       GOOS   string // target operating system
-       // TODO(rsc,adg): GOPATH
+       GOARCH     string // target architecture
+       GOOS       string // target operating system
+       CgoEnabled bool   // whether cgo can be used
 
        // By default, ScanDir uses the operating system's
        // file system calls to read directories and files.
@@ -75,9 +75,36 @@ func (ctxt *Context) readFile(dir, file string) (string, []byte, error) {
 // The DefaultContext is the default Context for builds.
 // It uses the GOARCH and GOOS environment variables
 // if set, or else the compiled code's GOARCH and GOOS.
-var DefaultContext = Context{
-       GOARCH: envOr("GOARCH", runtime.GOARCH),
-       GOOS:   envOr("GOOS", runtime.GOOS),
+var DefaultContext = defaultContext()
+
+var cgoEnabled = map[string]bool{
+       "darwin/386":    true,
+       "darwin/amd64":  true,
+       "linux/386":     true,
+       "linux/amd64":   true,
+       "freebsd/386":   true,
+       "freebsd/amd64": true,
+       "windows/386":   true,
+       "windows/amd64": true,
+}
+
+func defaultContext() Context {
+       var c Context
+
+       c.GOARCH = envOr("GOARCH", runtime.GOARCH)
+       c.GOOS = envOr("GOOS", runtime.GOOS)
+
+       s := os.Getenv("CGO_ENABLED")
+       switch s {
+       case "1":
+               c.CgoEnabled = true
+       case "0":
+               c.CgoEnabled = false
+       default:
+               c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]
+       }
+
+       return c
 }
 
 func envOr(name, def string) string {
@@ -96,8 +123,9 @@ type DirInfo struct {
 
        // Source files
        GoFiles  []string // .go files in dir (excluding CgoFiles)
+       HFiles   []string // .h files in dir
        CFiles   []string // .c files in dir
-       SFiles   []string // .s files in dir
+       SFiles   []string // .s (and, when using cgo, .S files in dir)
        CgoFiles []string // .go files that import "C"
 
        // Cgo directives
@@ -135,6 +163,7 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
                return nil, err
        }
 
+       var Sfiles []string // files with ".S" (capital S)
        var di DirInfo
        imported := make(map[string]bool)
        testImported := make(map[string]bool)
@@ -154,7 +183,7 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
 
                ext := path.Ext(name)
                switch ext {
-               case ".go", ".c", ".s":
+               case ".go", ".c", ".s", ".h", ".S":
                        // tentatively okay
                default:
                        // skip
@@ -175,9 +204,15 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
                case ".c":
                        di.CFiles = append(di.CFiles, name)
                        continue
+               case ".h":
+                       di.HFiles = append(di.HFiles, name)
+                       continue
                case ".s":
                        di.SFiles = append(di.SFiles, name)
                        continue
+               case ".S":
+                       Sfiles = append(Sfiles, name)
+                       continue
                }
 
                pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments)
@@ -256,7 +291,9 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
                        }
                }
                if isCgo {
-                       di.CgoFiles = append(di.CgoFiles, name)
+                       if ctxt.CgoEnabled {
+                               di.CgoFiles = append(di.CgoFiles, name)
+                       }
                } else if isTest {
                        if pkg == string(pf.Name.Name) {
                                di.TestGoFiles = append(di.TestGoFiles, name)
@@ -282,6 +319,15 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
                di.TestImports[i] = p
                i++
        }
+
+       // add the .S files only if we are using cgo
+       // (which means gcc will compile them).
+       // The standard assemblers expect .s files.
+       if len(di.CgoFiles) > 0 {
+               di.SFiles = append(di.SFiles, Sfiles...)
+               sort.Strings(di.SFiles)
+       }
+
        // File name lists are sorted because ReadDir sorts.
        sort.Strings(di.Imports)
        sort.Strings(di.TestImports)
@@ -289,7 +335,6 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
 }
 
 var slashslash = []byte("//")
-var plusBuild = []byte("+build")
 
 // shouldBuild reports whether it is okay to use this file,
 // The rule is that in the file's leading run of // comments
@@ -510,14 +555,22 @@ func splitQuoted(s string) (r []string, err error) {
 //
 //     $GOOS
 //     $GOARCH
-//     $GOOS/$GOARCH
+//     cgo (if cgo is enabled)
+//     nocgo (if cgo is disabled)
+//     a slash-separated list of any of these
 //
 func (ctxt *Context) matchOSArch(name string) bool {
+       if ctxt.CgoEnabled && name == "cgo" {
+               return true
+       }
+       if !ctxt.CgoEnabled && name == "nocgo" {
+               return true
+       }
        if name == ctxt.GOOS || name == ctxt.GOARCH {
                return true
        }
        i := strings.Index(name, "/")
-       return i >= 0 && name[:i] == ctxt.GOOS && name[i+1:] == ctxt.GOARCH
+       return i >= 0 && ctxt.matchOSArch(name[:i]) && ctxt.matchOSArch(name[i+1:])
 }
 
 // goodOSArchFile returns false if the name contains a $GOOS or $GOARCH
index 91d6c43..7a28180 100644 (file)
@@ -57,7 +57,7 @@ func (t *Tree) PkgDir() string {
 func (t *Tree) BinDir() string {
        if t.Goroot {
                if gobin := os.Getenv("GOBIN"); gobin != "" {
-                       return gobin
+                       return filepath.Clean(gobin)
                }
        }
        return filepath.Join(t.Path, "bin")
@@ -85,8 +85,8 @@ func (t *Tree) HasPkg(pkg string) bool {
 }
 
 var (
-       ErrNotFound     = errors.New("go/build: package could not be found locally")
-       ErrTreeNotFound = errors.New("go/build: no valid GOROOT or GOPATH could be found")
+       ErrNotFound     = errors.New("package could not be found locally")
+       ErrTreeNotFound = errors.New("no valid GOROOT or GOPATH could be found")
 )
 
 // FindTree takes an import or filesystem path and returns the
@@ -151,7 +151,7 @@ func init() {
        root := runtime.GOROOT()
        t, err := newTree(root)
        if err != nil {
-               log.Printf("go/build: invalid GOROOT %q: %v", root, err)
+               log.Printf("invalid GOROOT %q: %v", root, err)
        } else {
                t.Goroot = true
                Path = []*Tree{t}
@@ -163,7 +163,7 @@ func init() {
                }
                t, err := newTree(p)
                if err != nil {
-                       log.Printf("go/build: invalid GOPATH %q: %v", p, err)
+                       log.Printf("invalid GOPATH %q: %v", p, err)
                        continue
                }
                Path = append(Path, t)
index 9174864..1bb2241 100644 (file)
@@ -13,17 +13,32 @@ import (
 )
 
 // ----------------------------------------------------------------------------
+// Collection of documentation info
 
-type typeDoc struct {
+// embeddedType describes the type of an anonymous field.
+//
+type embeddedType struct {
+       typ *typeInfo // the corresponding base type
+       ptr bool      // if set, the anonymous field type is a pointer
+}
+
+type typeInfo struct {
        // len(decl.Specs) == 1, and the element type is *ast.TypeSpec
        // if the type declaration hasn't been seen yet, decl is nil
-       decl *ast.GenDecl
-       // values, factory functions, and methods associated with the type
+       decl     *ast.GenDecl
+       embedded []embeddedType
+       forward  *TypeDoc // forward link to processed type documentation
+
+       // declarations associated with the type
        values    []*ast.GenDecl // consts and vars
        factories map[string]*ast.FuncDecl
        methods   map[string]*ast.FuncDecl
 }
 
+func (info *typeInfo) addEmbeddedType(embedded *typeInfo, isPtr bool) {
+       info.embedded = append(info.embedded, embeddedType{embedded, isPtr})
+}
+
 // docReader accumulates documentation for a single package.
 // It modifies the AST: Comments (declaration documentation)
 // that have been collected by the DocReader are set to nil
@@ -32,17 +47,19 @@ type typeDoc struct {
 // printing the corresponding AST node).
 //
 type docReader struct {
-       doc     *ast.CommentGroup // package documentation, if any
-       pkgName string
-       values  []*ast.GenDecl // consts and vars
-       types   map[string]*typeDoc
-       funcs   map[string]*ast.FuncDecl
-       bugs    []*ast.CommentGroup
+       doc      *ast.CommentGroup // package documentation, if any
+       pkgName  string
+       values   []*ast.GenDecl // consts and vars
+       types    map[string]*typeInfo
+       embedded map[string]*typeInfo // embedded types, possibly not exported
+       funcs    map[string]*ast.FuncDecl
+       bugs     []*ast.CommentGroup
 }
 
 func (doc *docReader) init(pkgName string) {
        doc.pkgName = pkgName
-       doc.types = make(map[string]*typeDoc)
+       doc.types = make(map[string]*typeInfo)
+       doc.embedded = make(map[string]*typeInfo)
        doc.funcs = make(map[string]*ast.FuncDecl)
 }
 
@@ -52,56 +69,40 @@ func (doc *docReader) addDoc(comments *ast.CommentGroup) {
                doc.doc = comments
                return
        }
-
        // More than one package comment: Usually there will be only
        // one file with a package comment, but it's better to collect
        // all comments than drop them on the floor.
-       // (This code isn't particularly clever - no amortized doubling is
-       // used - but this situation occurs rarely and is not time-critical.)
-       n1 := len(doc.doc.List)
-       n2 := len(comments.List)
-       list := make([]*ast.Comment, n1+1+n2) // + 1 for separator line
-       copy(list, doc.doc.List)
-       list[n1] = &ast.Comment{token.NoPos, "//"} // separator line
-       copy(list[n1+1:], comments.List)
-       doc.doc = &ast.CommentGroup{list}
-}
-
-func (doc *docReader) addType(decl *ast.GenDecl) {
-       spec := decl.Specs[0].(*ast.TypeSpec)
-       typ := doc.lookupTypeDoc(spec.Name.Name)
-       // typ should always be != nil since declared types
-       // are always named - be conservative and check
-       if typ != nil {
-               // a type should be added at most once, so typ.decl
-               // should be nil - if it isn't, simply overwrite it
-               typ.decl = decl
-       }
+       blankComment := &ast.Comment{token.NoPos, "//"}
+       list := append(doc.doc.List, blankComment)
+       doc.doc.List = append(list, comments.List...)
 }
 
-func (doc *docReader) lookupTypeDoc(name string) *typeDoc {
-       if name == "" {
+func (doc *docReader) lookupTypeInfo(name string) *typeInfo {
+       if name == "" || name == "_" {
                return nil // no type docs for anonymous types
        }
-       if tdoc, found := doc.types[name]; found {
-               return tdoc
+       if info, found := doc.types[name]; found {
+               return info
        }
        // type wasn't found - add one without declaration
-       tdoc := &typeDoc{nil, nil, make(map[string]*ast.FuncDecl), make(map[string]*ast.FuncDecl)}
-       doc.types[name] = tdoc
-       return tdoc
+       info := &typeInfo{
+               factories: make(map[string]*ast.FuncDecl),
+               methods:   make(map[string]*ast.FuncDecl),
+       }
+       doc.types[name] = info
+       return info
 }
 
-func baseTypeName(typ ast.Expr) string {
+func baseTypeName(typ ast.Expr, allTypes bool) string {
        switch t := typ.(type) {
        case *ast.Ident:
                // if the type is not exported, the effect to
                // a client is as if there were no type name
-               if t.IsExported() {
+               if t.IsExported() || allTypes {
                        return t.Name
                }
        case *ast.StarExpr:
-               return baseTypeName(t.X)
+               return baseTypeName(t.X, allTypes)
        }
        return ""
 }
@@ -120,7 +121,7 @@ func (doc *docReader) addValue(decl *ast.GenDecl) {
                        switch {
                        case v.Type != nil:
                                // a type is present; determine its name
-                               name = baseTypeName(v.Type)
+                               name = baseTypeName(v.Type, false)
                        case decl.Tok == token.CONST:
                                // no type is present but we have a constant declaration;
                                // use the previous type name (w/o more type information
@@ -148,7 +149,7 @@ func (doc *docReader) addValue(decl *ast.GenDecl) {
        values := &doc.values
        if domName != "" && domFreq >= int(float64(len(decl.Specs))*threshold) {
                // typed entries are sufficiently frequent
-               typ := doc.lookupTypeDoc(domName)
+               typ := doc.lookupTypeInfo(domName)
                if typ != nil {
                        values = &typ.values // associate with that type
                }
@@ -175,10 +176,13 @@ func setFunc(table map[string]*ast.FuncDecl, f *ast.FuncDecl) {
 }
 
 func (doc *docReader) addFunc(fun *ast.FuncDecl) {
+       // strip function body
+       fun.Body = nil
+
        // determine if it should be associated with a type
        if fun.Recv != nil {
                // method
-               typ := doc.lookupTypeDoc(baseTypeName(fun.Recv.List[0].Type))
+               typ := doc.lookupTypeInfo(baseTypeName(fun.Recv.List[0].Type, false))
                if typ != nil {
                        // exported receiver type
                        setFunc(typ.methods, fun)
@@ -199,8 +203,8 @@ func (doc *docReader) addFunc(fun *ast.FuncDecl) {
                        // exactly one (named or anonymous) result associated
                        // with the first type in result signature (there may
                        // be more than one result)
-                       tname := baseTypeName(res.Type)
-                       typ := doc.lookupTypeDoc(tname)
+                       tname := baseTypeName(res.Type, false)
+                       typ := doc.lookupTypeInfo(tname)
                        if typ != nil {
                                // named and exported result type
                                setFunc(typ.factories, fun)
@@ -224,10 +228,17 @@ func (doc *docReader) addDecl(decl ast.Decl) {
                        case token.TYPE:
                                // types are handled individually
                                for _, spec := range d.Specs {
-                                       // make a (fake) GenDecl node for this TypeSpec
+                                       tspec := spec.(*ast.TypeSpec)
+                                       // add the type to the documentation
+                                       info := doc.lookupTypeInfo(tspec.Name.Name)
+                                       if info == nil {
+                                               continue // no name - ignore the type
+                                       }
+                                       // Make a (fake) GenDecl node for this TypeSpec
                                        // (we need to do this here - as opposed to just
                                        // for printing - so we don't lose the GenDecl
-                                       // documentation)
+                                       // documentation). Since a new GenDecl node is
+                                       // created, there's no need to nil out d.Doc.
                                        //
                                        // TODO(gri): Consider just collecting the TypeSpec
                                        // node (and copy in the GenDecl.doc if there is no
@@ -235,8 +246,32 @@ func (doc *docReader) addDecl(decl ast.Decl) {
                                        // makeTypeDocs below). Simpler data structures, but
                                        // would lose GenDecl documentation if the TypeSpec
                                        // has documentation as well.
-                                       doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos, []ast.Spec{spec}, token.NoPos})
-                                       // A new GenDecl node is created, no need to nil out d.Doc.
+                                       fake := &ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos,
+                                               []ast.Spec{tspec}, token.NoPos}
+                                       // A type should be added at most once, so info.decl
+                                       // should be nil - if it isn't, simply overwrite it.
+                                       info.decl = fake
+                                       // Look for anonymous fields that might contribute methods.
+                                       var fields *ast.FieldList
+                                       switch typ := spec.(*ast.TypeSpec).Type.(type) {
+                                       case *ast.StructType:
+                                               fields = typ.Fields
+                                       case *ast.InterfaceType:
+                                               fields = typ.Methods
+                                       }
+                                       if fields != nil {
+                                               for _, field := range fields.List {
+                                                       if len(field.Names) == 0 {
+                                                               // anonymous field - add corresponding type
+                                                               // to the info and collect it in doc
+                                                               name := baseTypeName(field.Type, true)
+                                                               if embedded := doc.lookupTypeInfo(name); embedded != nil {
+                                                                       _, ptr := field.Type.(*ast.StarExpr)
+                                                                       info.addEmbeddedType(embedded, ptr)
+                                                               }
+                                                       }
+                                               }
+                                       }
                                }
                        }
                }
@@ -285,19 +320,15 @@ func (doc *docReader) addFile(src *ast.File) {
        src.Comments = nil // consumed unassociated comments - remove from ast.File node
 }
 
-func NewFileDoc(file *ast.File) *PackageDoc {
-       var r docReader
-       r.init(file.Name.Name)
-       r.addFile(file)
-       return r.newDoc("", nil)
-}
-
-func NewPackageDoc(pkg *ast.Package, importpath string) *PackageDoc {
+func NewPackageDoc(pkg *ast.Package, importpath string, exportsOnly bool) *PackageDoc {
        var r docReader
        r.init(pkg.Name)
        filenames := make([]string, len(pkg.Files))
        i := 0
        for filename, f := range pkg.Files {
+               if exportsOnly {
+                       r.fileExports(f)
+               }
                r.addFile(f)
                filenames[i] = filename
                i++
@@ -397,6 +428,25 @@ func makeFuncDocs(m map[string]*ast.FuncDecl) []*FuncDoc {
        return d
 }
 
+type methodSet map[string]*FuncDoc
+
+func (mset methodSet) add(m *FuncDoc) {
+       if mset[m.Name] == nil {
+               mset[m.Name] = m
+       }
+}
+
+func (mset methodSet) sortedList() []*FuncDoc {
+       list := make([]*FuncDoc, len(mset))
+       i := 0
+       for _, m := range mset {
+               list[i] = m
+               i++
+       }
+       sort.Sort(sortFuncDoc(list))
+       return list
+}
+
 // TypeDoc is the documentation for a declared type.
 // Consts and Vars are sorted lists of constants and variables of (mostly) that type.
 // Factories is a sorted list of factory functions that return that type.
@@ -407,7 +457,9 @@ type TypeDoc struct {
        Consts    []*ValueDoc
        Vars      []*ValueDoc
        Factories []*FuncDoc
-       Methods   []*FuncDoc
+       methods   []*FuncDoc // top-level methods only
+       embedded  methodSet  // embedded methods only
+       Methods   []*FuncDoc // all methods including embedded ones
        Decl      *ast.GenDecl
        order     int
 }
@@ -429,11 +481,17 @@ func (p sortTypeDoc) Less(i, j int) bool {
 // NOTE(rsc): This would appear not to be correct for type ( )
 // blocks, but the doc extractor above has split them into
 // individual declarations.
-func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {
-       d := make([]*TypeDoc, len(m))
+func (doc *docReader) makeTypeDocs(m map[string]*typeInfo) []*TypeDoc {
+       // TODO(gri) Consider computing the embedded method information
+       //           before calling makeTypeDocs. Then this function can
+       //           be single-phased again. Also, it might simplify some
+       //           of the logic.
+       //
+       // phase 1: associate collected declarations with TypeDocs
+       list := make([]*TypeDoc, len(m))
        i := 0
        for _, old := range m {
-               // all typeDocs should have a declaration associated with
+               // all typeInfos should have a declaration associated with
                // them after processing an entire package - be conservative
                // and check
                if decl := old.decl; decl != nil {
@@ -451,10 +509,16 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {
                        t.Consts = makeValueDocs(old.values, token.CONST)
                        t.Vars = makeValueDocs(old.values, token.VAR)
                        t.Factories = makeFuncDocs(old.factories)
-                       t.Methods = makeFuncDocs(old.methods)
+                       t.methods = makeFuncDocs(old.methods)
+                       // The list of embedded types' methods is computed from the list
+                       // of embedded types, some of which may not have been processed
+                       // yet (i.e., their forward link is nil) - do this in a 2nd phase.
+                       // The final list of methods can only be computed after that -
+                       // do this in a 3rd phase.
                        t.Decl = old.decl
                        t.order = i
-                       d[i] = t
+                       old.forward = t // old has been processed
+                       list[i] = t
                        i++
                } else {
                        // no corresponding type declaration found - move any associated
@@ -477,9 +541,99 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {
                        }
                }
        }
-       d = d[0:i] // some types may have been ignored
-       sort.Sort(sortTypeDoc(d))
-       return d
+       list = list[0:i] // some types may have been ignored
+
+       // phase 2: collect embedded methods for each processed typeInfo
+       for _, old := range m {
+               if t := old.forward; t != nil {
+                       // old has been processed into t; collect embedded
+                       // methods for t from the list of processed embedded
+                       // types in old (and thus for which the methods are known)
+                       typ := t.Type
+                       if _, ok := typ.Type.(*ast.StructType); ok {
+                               // struct
+                               t.embedded = make(methodSet)
+                               collectEmbeddedMethods(t.embedded, old, typ.Name.Name)
+                       } else {
+                               // interface
+                               // TODO(gri) fix this
+                       }
+               }
+       }
+
+       // phase 3: compute final method set for each TypeDoc
+       for _, d := range list {
+               if len(d.embedded) > 0 {
+                       // there are embedded methods - exclude
+                       // the ones with names conflicting with
+                       // non-embedded methods
+                       mset := make(methodSet)
+                       // top-level methods have priority
+                       for _, m := range d.methods {
+                               mset.add(m)
+                       }
+                       // add non-conflicting embedded methods
+                       for _, m := range d.embedded {
+                               mset.add(m)
+                       }
+                       d.Methods = mset.sortedList()
+               } else {
+                       // no embedded methods
+                       d.Methods = d.methods
+               }
+       }
+
+       sort.Sort(sortTypeDoc(list))
+       return list
+}
+
+// collectEmbeddedMethods collects the embedded methods from all
+// processed embedded types found in info in mset. It considers
+// embedded types at the most shallow level first so that more
+// deeply nested embedded methods with conflicting names are
+// excluded.
+//
+func collectEmbeddedMethods(mset methodSet, info *typeInfo, recvTypeName string) {
+       for _, e := range info.embedded {
+               if e.typ.forward != nil { // == e was processed
+                       for _, m := range e.typ.forward.methods {
+                               mset.add(customizeRecv(m, e.ptr, recvTypeName))
+                       }
+                       collectEmbeddedMethods(mset, e.typ, recvTypeName)
+               }
+       }
+}
+
+func customizeRecv(m *FuncDoc, embeddedIsPtr bool, recvTypeName string) *FuncDoc {
+       if m == nil || m.Decl == nil || m.Decl.Recv == nil || len(m.Decl.Recv.List) != 1 {
+               return m // shouldn't happen, but be safe
+       }
+
+       // copy existing receiver field and set new type
+       // TODO(gri) is receiver type computation correct?
+       //           what about deeply nested embeddings?
+       newField := *m.Decl.Recv.List[0]
+       _, origRecvIsPtr := newField.Type.(*ast.StarExpr)
+       var typ ast.Expr = ast.NewIdent(recvTypeName)
+       if embeddedIsPtr || origRecvIsPtr {
+               typ = &ast.StarExpr{token.NoPos, typ}
+       }
+       newField.Type = typ
+
+       // copy existing receiver field list and set new receiver field
+       newFieldList := *m.Decl.Recv
+       newFieldList.List = []*ast.Field{&newField}
+
+       // copy existing function declaration and set new receiver field list
+       newFuncDecl := *m.Decl
+       newFuncDecl.Recv = &newFieldList
+
+       // copy existing function documentation and set new declaration
+       newM := *m
+       newM.Decl = &newFuncDecl
+       newM.Recv = typ
+
+       return &newM
 }
 
 func makeBugDocs(list []*ast.CommentGroup) []string {
@@ -523,104 +677,3 @@ func (doc *docReader) newDoc(importpath string, filenames []string) *PackageDoc
        p.Bugs = makeBugDocs(doc.bugs)
        return p
 }
-
-// ----------------------------------------------------------------------------
-// Filtering by name
-
-type Filter func(string) bool
-
-func matchFields(fields *ast.FieldList, f Filter) bool {
-       if fields != nil {
-               for _, field := range fields.List {
-                       for _, name := range field.Names {
-                               if f(name.Name) {
-                                       return true
-                               }
-                       }
-               }
-       }
-       return false
-}
-
-func matchDecl(d *ast.GenDecl, f Filter) bool {
-       for _, d := range d.Specs {
-               switch v := d.(type) {
-               case *ast.ValueSpec:
-                       for _, name := range v.Names {
-                               if f(name.Name) {
-                                       return true
-                               }
-                       }
-               case *ast.TypeSpec:
-                       if f(v.Name.Name) {
-                               return true
-                       }
-                       switch t := v.Type.(type) {
-                       case *ast.StructType:
-                               if matchFields(t.Fields, f) {
-                                       return true
-                               }
-                       case *ast.InterfaceType:
-                               if matchFields(t.Methods, f) {
-                                       return true
-                               }
-                       }
-               }
-       }
-       return false
-}
-
-func filterValueDocs(a []*ValueDoc, f Filter) []*ValueDoc {
-       w := 0
-       for _, vd := range a {
-               if matchDecl(vd.Decl, f) {
-                       a[w] = vd
-                       w++
-               }
-       }
-       return a[0:w]
-}
-
-func filterFuncDocs(a []*FuncDoc, f Filter) []*FuncDoc {
-       w := 0
-       for _, fd := range a {
-               if f(fd.Name) {
-                       a[w] = fd
-                       w++
-               }
-       }
-       return a[0:w]
-}
-
-func filterTypeDocs(a []*TypeDoc, f Filter) []*TypeDoc {
-       w := 0
-       for _, td := range a {
-               n := 0 // number of matches
-               if matchDecl(td.Decl, f) {
-                       n = 1
-               } else {
-                       // type name doesn't match, but we may have matching consts, vars, factories or methods
-                       td.Consts = filterValueDocs(td.Consts, f)
-                       td.Vars = filterValueDocs(td.Vars, f)
-                       td.Factories = filterFuncDocs(td.Factories, f)
-                       td.Methods = filterFuncDocs(td.Methods, f)
-                       n += len(td.Consts) + len(td.Vars) + len(td.Factories) + len(td.Methods)
-               }
-               if n > 0 {
-                       a[w] = td
-                       w++
-               }
-       }
-       return a[0:w]
-}
-
-// Filter eliminates documentation for names that don't pass through the filter f.
-// TODO: Recognize "Type.Method" as a name.
-//
-func (p *PackageDoc) Filter(f Filter) {
-       p.Consts = filterValueDocs(p.Consts, f)
-       p.Vars = filterValueDocs(p.Vars, f)
-       p.Types = filterTypeDocs(p.Types, f)
-       p.Funcs = filterFuncDocs(p.Funcs, f)
-       p.Doc = "" // don't show top-level package doc
-}
diff --git a/libgo/go/go/doc/exports.go b/libgo/go/go/doc/exports.go
new file mode 100644 (file)
index 0000000..9cd186a
--- /dev/null
@@ -0,0 +1,167 @@
+// 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.
+
+// This file implements export filtering of an AST.
+
+package doc
+
+import "go/ast"
+
+func filterIdentList(list []*ast.Ident) []*ast.Ident {
+       j := 0
+       for _, x := range list {
+               if ast.IsExported(x.Name) {
+                       list[j] = x
+                       j++
+               }
+       }
+       return list[0:j]
+}
+
+func baseName(x ast.Expr) *ast.Ident {
+       switch t := x.(type) {
+       case *ast.Ident:
+               return t
+       case *ast.SelectorExpr:
+               if _, ok := t.X.(*ast.Ident); ok {
+                       return t.Sel
+               }
+       case *ast.StarExpr:
+               return baseName(t.X)
+       }
+       return nil
+}
+
+func (doc *docReader) filterFieldList(fields *ast.FieldList) (removedFields bool) {
+       if fields == nil {
+               return false
+       }
+       list := fields.List
+       j := 0
+       for _, f := range list {
+               keepField := false
+               if len(f.Names) == 0 {
+                       // anonymous field
+                       name := baseName(f.Type)
+                       keepField = name != nil && name.IsExported()
+               } else {
+                       n := len(f.Names)
+                       f.Names = filterIdentList(f.Names)
+                       if len(f.Names) < n {
+                               removedFields = true
+                       }
+                       keepField = len(f.Names) > 0
+               }
+               if keepField {
+                       doc.filterType(f.Type)
+                       list[j] = f
+                       j++
+               }
+       }
+       if j < len(list) {
+               removedFields = true
+       }
+       fields.List = list[0:j]
+       return
+}
+
+func (doc *docReader) filterParamList(fields *ast.FieldList) bool {
+       if fields == nil {
+               return false
+       }
+       var b bool
+       for _, f := range fields.List {
+               if doc.filterType(f.Type) {
+                       b = true
+               }
+       }
+       return b
+}
+
+func (doc *docReader) filterType(typ ast.Expr) bool {
+       switch t := typ.(type) {
+       case *ast.Ident:
+               return ast.IsExported(t.Name)
+       case *ast.ParenExpr:
+               return doc.filterType(t.X)
+       case *ast.ArrayType:
+               return doc.filterType(t.Elt)
+       case *ast.StructType:
+               if doc.filterFieldList(t.Fields) {
+                       t.Incomplete = true
+               }
+               return len(t.Fields.List) > 0
+       case *ast.FuncType:
+               b1 := doc.filterParamList(t.Params)
+               b2 := doc.filterParamList(t.Results)
+               return b1 || b2
+       case *ast.InterfaceType:
+               if doc.filterFieldList(t.Methods) {
+                       t.Incomplete = true
+               }
+               return len(t.Methods.List) > 0
+       case *ast.MapType:
+               b1 := doc.filterType(t.Key)
+               b2 := doc.filterType(t.Value)
+               return b1 || b2
+       case *ast.ChanType:
+               return doc.filterType(t.Value)
+       }
+       return false
+}
+
+func (doc *docReader) filterSpec(spec ast.Spec) bool {
+       switch s := spec.(type) {
+       case *ast.ValueSpec:
+               s.Names = filterIdentList(s.Names)
+               if len(s.Names) > 0 {
+                       doc.filterType(s.Type)
+                       return true
+               }
+       case *ast.TypeSpec:
+               if ast.IsExported(s.Name.Name) {
+                       doc.filterType(s.Type)
+                       return true
+               }
+       }
+       return false
+}
+
+func (doc *docReader) filterSpecList(list []ast.Spec) []ast.Spec {
+       j := 0
+       for _, s := range list {
+               if doc.filterSpec(s) {
+                       list[j] = s
+                       j++
+               }
+       }
+       return list[0:j]
+}
+
+func (doc *docReader) filterDecl(decl ast.Decl) bool {
+       switch d := decl.(type) {
+       case *ast.GenDecl:
+               d.Specs = doc.filterSpecList(d.Specs)
+               return len(d.Specs) > 0
+       case *ast.FuncDecl:
+               return ast.IsExported(d.Name.Name)
+       }
+       return false
+}
+
+// fileExports trims the AST for a Go file in place such that
+// only exported nodes remain. fileExports returns true if
+// there are exported declarations; otherwise it returns false.
+//
+func (doc *docReader) fileExports(src *ast.File) bool {
+       j := 0
+       for _, d := range src.Decls {
+               if doc.filterDecl(d) {
+                       src.Decls[j] = d
+                       j++
+               }
+       }
+       src.Decls = src.Decls[0:j]
+       return j > 0
+}
diff --git a/libgo/go/go/doc/filter.go b/libgo/go/go/doc/filter.go
new file mode 100644 (file)
index 0000000..71c2ebb
--- /dev/null
@@ -0,0 +1,105 @@
+// Copyright 2009 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 doc
+
+import "go/ast"
+
+type Filter func(string) bool
+
+func matchFields(fields *ast.FieldList, f Filter) bool {
+       if fields != nil {
+               for _, field := range fields.List {
+                       for _, name := range field.Names {
+                               if f(name.Name) {
+                                       return true
+                               }
+                       }
+               }
+       }
+       return false
+}
+
+func matchDecl(d *ast.GenDecl, f Filter) bool {
+       for _, d := range d.Specs {
+               switch v := d.(type) {
+               case *ast.ValueSpec:
+                       for _, name := range v.Names {
+                               if f(name.Name) {
+                                       return true
+                               }
+                       }
+               case *ast.TypeSpec:
+                       if f(v.Name.Name) {
+                               return true
+                       }
+                       switch t := v.Type.(type) {
+                       case *ast.StructType:
+                               if matchFields(t.Fields, f) {
+                                       return true
+                               }
+                       case *ast.InterfaceType:
+                               if matchFields(t.Methods, f) {
+                                       return true
+                               }
+                       }
+               }
+       }
+       return false
+}
+
+func filterValueDocs(a []*ValueDoc, f Filter) []*ValueDoc {
+       w := 0
+       for _, vd := range a {
+               if matchDecl(vd.Decl, f) {
+                       a[w] = vd
+                       w++
+               }
+       }
+       return a[0:w]
+}
+
+func filterFuncDocs(a []*FuncDoc, f Filter) []*FuncDoc {
+       w := 0
+       for _, fd := range a {
+               if f(fd.Name) {
+                       a[w] = fd
+                       w++
+               }
+       }
+       return a[0:w]
+}
+
+func filterTypeDocs(a []*TypeDoc, f Filter) []*TypeDoc {
+       w := 0
+       for _, td := range a {
+               n := 0 // number of matches
+               if matchDecl(td.Decl, f) {
+                       n = 1
+               } else {
+                       // type name doesn't match, but we may have matching consts, vars, factories or methods
+                       td.Consts = filterValueDocs(td.Consts, f)
+                       td.Vars = filterValueDocs(td.Vars, f)
+                       td.Factories = filterFuncDocs(td.Factories, f)
+                       td.Methods = filterFuncDocs(td.Methods, f)
+                       n += len(td.Consts) + len(td.Vars) + len(td.Factories) + len(td.Methods)
+               }
+               if n > 0 {
+                       a[w] = td
+                       w++
+               }
+       }
+       return a[0:w]
+}
+
+// Filter eliminates documentation for names that don't pass through the filter f.
+// TODO: Recognize "Type.Method" as a name.
+//
+func (p *PackageDoc) Filter(f Filter) {
+       p.Consts = filterValueDocs(p.Consts, f)
+       p.Vars = filterValueDocs(p.Vars, f)
+       p.Types = filterTypeDocs(p.Types, f)
+       p.Funcs = filterFuncDocs(p.Funcs, f)
+       p.Doc = "" // don't show top-level package doc
+}
index f0a8055..9fbed2d 100644 (file)
@@ -144,28 +144,31 @@ func (p *parser) declare(decl, data interface{}, scope *ast.Scope, kind ast.ObjK
        }
 }
 
-func (p *parser) shortVarDecl(idents []*ast.Ident) {
+func (p *parser) shortVarDecl(decl *ast.AssignStmt, list []ast.Expr) {
        // Go spec: A short variable declaration may redeclare variables
        // provided they were originally declared in the same block with
        // the same type, and at least one of the non-blank variables is new.
        n := 0 // number of new variables
-       for _, ident := range idents {
-               assert(ident.Obj == nil, "identifier already declared or resolved")
-               obj := ast.NewObj(ast.Var, ident.Name)
-               // short var declarations cannot have redeclaration errors
-               // and are not global => no need to remember the respective
-               // declaration
-               ident.Obj = obj
-               if ident.Name != "_" {
-                       if alt := p.topScope.Insert(obj); alt != nil {
-                               ident.Obj = alt // redeclaration
-                       } else {
-                               n++ // new declaration
+       for _, x := range list {
+               if ident, isIdent := x.(*ast.Ident); isIdent {
+                       assert(ident.Obj == nil, "identifier already declared or resolved")
+                       obj := ast.NewObj(ast.Var, ident.Name)
+                       // remember corresponding assignment for other tools
+                       obj.Decl = decl
+                       ident.Obj = obj
+                       if ident.Name != "_" {
+                               if alt := p.topScope.Insert(obj); alt != nil {
+                                       ident.Obj = alt // redeclaration
+                               } else {
+                                       n++ // new declaration
+                               }
                        }
+               } else {
+                       p.errorExpected(x.Pos(), "identifier")
                }
        }
        if n == 0 && p.mode&DeclarationErrors != 0 {
-               p.error(idents[0].Pos(), "no new variables on left side of :=")
+               p.error(list[0].Pos(), "no new variables on left side of :=")
        }
 }
 
@@ -522,7 +525,7 @@ func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident {
        for i, x := range list {
                ident, isIdent := x.(*ast.Ident)
                if !isIdent {
-                       pos := x.(ast.Expr).Pos()
+                       pos := x.Pos()
                        p.errorExpected(pos, "identifier")
                        ident = &ast.Ident{pos, "_", nil}
                }
@@ -1400,10 +1403,11 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) {
                } else {
                        y = p.parseRhsList()
                }
+               as := &ast.AssignStmt{x, pos, tok, y}
                if tok == token.DEFINE {
-                       p.shortVarDecl(p.makeIdentList(x))
+                       p.shortVarDecl(as, x)
                }
-               return &ast.AssignStmt{x, pos, tok, y}, isRange
+               return as, isRange
        }
 
        if len(x) > 1 {
@@ -1715,34 +1719,28 @@ func (p *parser) parseCommClause() *ast.CommClause {
                        comm = &ast.SendStmt{lhs[0], arrow, rhs}
                } else {
                        // RecvStmt
-                       pos := p.pos
-                       tok := p.tok
-                       var rhs ast.Expr
-                       if tok == token.ASSIGN || tok == token.DEFINE {
+                       if tok := p.tok; tok == token.ASSIGN || tok == token.DEFINE {
                                // RecvStmt with assignment
                                if len(lhs) > 2 {
                                        p.errorExpected(lhs[0].Pos(), "1 or 2 expressions")
                                        // continue with first two expressions
                                        lhs = lhs[0:2]
                                }
+                               pos := p.pos
                                p.next()
-                               rhs = p.parseRhs()
-                               if tok == token.DEFINE && lhs != nil {
-                                       p.shortVarDecl(p.makeIdentList(lhs))
+                               rhs := p.parseRhs()
+                               as := &ast.AssignStmt{lhs, pos, tok, []ast.Expr{rhs}}
+                               if tok == token.DEFINE {
+                                       p.shortVarDecl(as, lhs)
                                }
+                               comm = as
                        } else {
-                               // rhs must be single receive operation
+                               // lhs must be single receive operation
                                if len(lhs) > 1 {
                                        p.errorExpected(lhs[0].Pos(), "1 expression")
                                        // continue with first expression
                                }
-                               rhs = lhs[0]
-                               lhs = nil // there is no lhs
-                       }
-                       if lhs != nil {
-                               comm = &ast.AssignStmt{lhs, pos, tok, []ast.Expr{rhs}}
-                       } else {
-                               comm = &ast.ExprStmt{rhs}
+                               comm = &ast.ExprStmt{lhs[0]}
                        }
                }
        } else {
index b2a48c2..6817cc4 100644 (file)
@@ -39,7 +39,10 @@ import (
 //            future (not yet interspersed) comments in this function.
 //
 func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (printedBreak bool) {
-       n := p.nlines(line-p.pos.Line, min)
+       n := nlimit(line - p.pos.Line)
+       if n < min {
+               n = min
+       }
        if n > 0 {
                p.print(ws)
                if newSection {
@@ -361,9 +364,10 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
        lbrace := fields.Opening
        list := fields.List
        rbrace := fields.Closing
+       hasComments := isIncomplete || p.commentBefore(p.fset.Position(rbrace))
        srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.fset.Position(lbrace).Line == p.fset.Position(rbrace).Line
 
-       if !isIncomplete && !p.commentBefore(p.fset.Position(rbrace)) && srcIsOneLine {
+       if !hasComments && srcIsOneLine {
                // possibly a one-line struct/interface
                if len(list) == 0 {
                        // no blank between keyword and {} in this case
@@ -388,9 +392,13 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
                        return
                }
        }
+       // hasComments || !srcIsOneLine
+
+       p.print(blank, lbrace, token.LBRACE, indent)
+       if hasComments || len(list) > 0 {
+               p.print(formfeed)
+       }
 
-       // at least one entry or incomplete
-       p.print(blank, lbrace, token.LBRACE, indent, formfeed)
        if isStruct {
 
                sep := vtab
@@ -1509,9 +1517,14 @@ func (p *printer) file(src *ast.File) {
                        prev := tok
                        tok = declToken(d)
                        // if the declaration token changed (e.g., from CONST to TYPE)
+                       // or the next declaration has documentation associated with it,
                        // print an empty line between top-level declarations
+                       // (because p.linebreak is called with the position of d, which
+                       // is past any documentation, the minimum requirement is satisfied
+                       // even w/o the extra getDoc(d) nil-check - leave it in case the
+                       // linebreak logic improves - there's already a TODO).
                        min := 1
-                       if prev != tok {
+                       if prev != tok || getDoc(d) != nil {
                                min = 2
                        }
                        p.linebreak(p.fset.Position(d.Pos()).Line, min, ignore, false)
index 8538236..a78cfc6 100644 (file)
@@ -18,8 +18,11 @@ import (
        "text/tabwriter"
 )
 
-const debug = false // enable for debugging
-const infinity = 1 << 30
+const (
+       maxNewlines = 2     // max. number of newlines between source text
+       debug       = false // enable for debugging
+       infinity    = 1 << 30
+)
 
 type whiteSpace byte
 
@@ -89,21 +92,7 @@ func (p *printer) internalError(msg ...interface{}) {
        }
 }
 
-// nlines returns the adjusted number of linebreaks given the desired number
-// of breaks n such that min <= result <= max.
-//
-func (p *printer) nlines(n, min int) int {
-       const max = 2 // max. number of newlines
-       switch {
-       case n < min:
-               return min
-       case n > max:
-               return max
-       }
-       return n
-}
-
-// writeByte writes a single byte to p.output and updates p.pos.
+// writeByte writes ch to p.output and updates p.pos.
 func (p *printer) writeByte(ch byte) {
        p.output.WriteByte(ch)
        p.pos.Offset++
@@ -128,13 +117,11 @@ func (p *printer) writeByte(ch byte) {
        }
 }
 
-// writeNewlines writes up to n newlines to p.output and updates p.pos.
-// The actual number of newlines written is limited by nlines.
-// nl must be one of '\n' or '\f'.
-//
-func (p *printer) writeNewlines(n int, nl byte) {
-       for n = p.nlines(n, 0); n > 0; n-- {
-               p.writeByte(nl)
+// writeByteN writes ch n times to p.output and updates p.pos.
+func (p *printer) writeByteN(ch byte, n int) {
+       for n > 0 {
+               p.writeByte(ch)
+               n--
        }
 }
 
@@ -223,8 +210,8 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
        }
 
        if pos.IsValid() && pos.Filename != p.last.Filename {
-               // comment in a different file - separate with newlines (writeNewlines will limit the number)
-               p.writeNewlines(10, '\f')
+               // comment in a different file - separate with newlines
+               p.writeByteN('\f', maxNewlines)
                return
        }
 
@@ -270,6 +257,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
        } else {
                // comment on a different line:
                // separate with at least one line break
+               droppedLinebreak := false
                if prev == nil {
                        // first comment of a comment group
                        j := 0
@@ -295,6 +283,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
                                case newline, formfeed:
                                        // TODO(gri): may want to keep formfeed info in some cases
                                        p.wsbuf[i] = ignore
+                                       droppedLinebreak = true
                                }
                                j = i
                                break
@@ -302,25 +291,41 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
                        p.writeWhitespace(j)
                }
 
-               // turn off indent if we're about to print a line directive.
-               indent := p.indent
-               if strings.HasPrefix(comment.Text, linePrefix) {
-                       p.indent = 0
+               // determine number of linebreaks before the comment
+               n := 0
+               if pos.IsValid() && p.last.IsValid() {
+                       n = pos.Line - p.last.Line
+                       if n < 0 { // should never happen
+                               n = 0
+                       }
+               }
+
+               // at the package scope level only (p.indent == 0),
+               // add an extra newline if we dropped one before:
+               // this preserves a blank line before documentation
+               // comments at the package scope level (issue 2570)
+               if p.indent == 0 && droppedLinebreak {
+                       n++
                }
 
-               // use formfeeds to break columns before a comment;
-               // this is analogous to using formfeeds to separate
-               // individual lines of /*-style comments - but make
-               // sure there is at least one line break if the previous
-               // comment was a line comment
-               n := pos.Line - p.last.Line // if !pos.IsValid(), pos.Line == 0, and n will be 0
-               if n <= 0 && prev != nil && prev.Text[1] == '/' {
+               // make sure there is at least one line break
+               // if the previous comment was a line comment
+               if n == 0 && prev != nil && prev.Text[1] == '/' {
                        n = 1
                }
+
                if n > 0 {
-                       p.writeNewlines(n, '\f')
+                       // turn off indent if we're about to print a line directive
+                       indent := p.indent
+                       if strings.HasPrefix(comment.Text, linePrefix) {
+                               p.indent = 0
+                       }
+                       // use formfeeds to break columns before a comment;
+                       // this is analogous to using formfeeds to separate
+                       // individual lines of /*-style comments
+                       p.writeByteN('\f', nlimit(n))
+                       p.indent = indent // restore indent
                }
-               p.indent = indent
        }
 }
 
@@ -550,10 +555,11 @@ func (p *printer) writeComment(comment *ast.Comment) {
 // writeCommentSuffix writes a line break after a comment if indicated
 // and processes any leftover indentation information. If a line break
 // is needed, the kind of break (newline vs formfeed) depends on the
-// pending whitespace. writeCommentSuffix returns true if a pending
-// formfeed was dropped from the whitespace buffer.
+// pending whitespace. The writeCommentSuffix result indicates if a
+// newline was written or if a formfeed was dropped from the whitespace
+// buffer.
 //
-func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
+func (p *printer) writeCommentSuffix(needsLinebreak bool) (wroteNewline, droppedFF bool) {
        for i, ch := range p.wsbuf {
                switch ch {
                case blank, vtab:
@@ -566,6 +572,7 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
                        // but remember if we dropped any formfeeds
                        if needsLinebreak {
                                needsLinebreak = false
+                               wroteNewline = true
                        } else {
                                if ch == formfeed {
                                        droppedFF = true
@@ -579,6 +586,7 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
        // make sure we have a line break
        if needsLinebreak {
                p.writeByte('\n')
+               wroteNewline = true
        }
 
        return
@@ -587,10 +595,10 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
 // intersperseComments consumes all comments that appear before the next token
 // tok and prints it together with the buffered whitespace (i.e., the whitespace
 // that needs to be written before the next token). A heuristic is used to mix
-// the comments and whitespace. intersperseComments returns true if a pending
-// formfeed was dropped from the whitespace buffer.
+// the comments and whitespace. The intersperseComments result indicates if a
+// newline was written or if a formfeed was dropped from the whitespace buffer.
 //
-func (p *printer) intersperseComments(next token.Position, tok token.Token) (droppedFF bool) {
+func (p *printer) intersperseComments(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) {
        var last *ast.Comment
        for ; p.commentBefore(next); p.cindex++ {
                for _, c := range p.comments[p.cindex].List {
@@ -618,7 +626,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (dro
        // no comment was written - we should never reach here since
        // intersperseComments should not be called in that case
        p.internalError("intersperseComments called without pending comments")
-       return false
+       return
 }
 
 // whiteWhitespace writes the first n whitespace entries.
@@ -671,6 +679,14 @@ func (p *printer) writeWhitespace(n int) {
 // ----------------------------------------------------------------------------
 // Printing interface
 
+// nlines limits n to maxNewlines.
+func nlimit(n int) int {
+       if n > maxNewlines {
+               n = maxNewlines
+       }
+       return n
+}
+
 func mayCombine(prev token.Token, next byte) (b bool) {
        switch prev {
        case token.INT:
@@ -765,17 +781,22 @@ func (p *printer) print(args ...interface{}) {
                p.pos = next
 
                if data != "" {
-                       nl := byte('\n')
-                       if p.flush(next, tok) {
-                               nl = '\f' // dropped formfeed before
-                       }
+                       wroteNewline, droppedFF := p.flush(next, tok)
 
                        // intersperse extra newlines if present in the source
                        // (don't do this in flush as it will cause extra newlines
-                       // at the end of a file) - use formfeeds if we dropped one
-                       // before
-                       if n := next.Line - p.pos.Line; n > 0 {
-                               p.writeNewlines(n, nl)
+                       // at the end of a file)
+                       n := nlimit(next.Line - p.pos.Line)
+                       // don't exceed maxNewlines if we already wrote one
+                       if wroteNewline && n == maxNewlines {
+                               n = maxNewlines - 1
+                       }
+                       if n > 0 {
+                               ch := byte('\n')
+                               if droppedFF {
+                                       ch = '\f' // use formfeed since we dropped one before
+                               }
+                               p.writeByteN(ch, n)
                        }
 
                        p.writeItem(next, data, isLit)
@@ -790,16 +811,15 @@ func (p *printer) commentBefore(next token.Position) bool {
        return p.cindex < len(p.comments) && p.fset.Position(p.comments[p.cindex].List[0].Pos()).Offset < next.Offset
 }
 
-// Flush prints any pending comments and whitespace occurring
-// textually before the position of the next token tok. Flush
-// returns true if a pending formfeed character was dropped
-// from the whitespace buffer as a result of interspersing
-// comments.
+// Flush prints any pending comments and whitespace occurring textually
+// before the position of the next token tok. The Flush result indicates
+// if a newline was written or if a formfeed was dropped from the whitespace
+// buffer.
 //
-func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) {
+func (p *printer) flush(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) {
        if p.commentBefore(next) {
                // if there are comments before the next item, intersperse them
-               droppedFF = p.intersperseComments(next, tok)
+               wroteNewline, droppedFF = p.intersperseComments(next, tok)
        } else {
                // otherwise, write any leftover whitespace
                p.writeWhitespace(len(p.wsbuf))
@@ -810,7 +830,8 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) {
 // getNode returns the ast.CommentGroup associated with n, if any.
 func getDoc(n ast.Node) *ast.CommentGroup {
        switch n := n.(type) {
-       // *ast.Fields cannot be printed separately - ignore for now
+       case *ast.Field:
+               return n.Doc
        case *ast.ImportSpec:
                return n.Doc
        case *ast.ValueSpec:
index 7b33225..d2ad9e3 100644 (file)
@@ -106,7 +106,7 @@ type S3 struct {
 var x int      // x
 var ()
 
-// This comment SHOULD be associated with the next declaration.
+// This comment SHOULD be associated with f0.
 func f0() {
        const pi = 3.14 // pi
        var s1 struct{} /* an empty struct */   /* foo */
@@ -115,8 +115,9 @@ func f0() {
        var s2 struct{} = struct{}{}
        x := pi
 }
+
 //
-// NO SPACE HERE
+// This comment should be associated with f1, with one blank line before the comment.
 //
 func f1() {
        f0()
index 2a9a86b..222e0a7 100644 (file)
@@ -107,7 +107,7 @@ var x int  // x
 var ()
 
 
-// This comment SHOULD be associated with the next declaration.
+// This comment SHOULD be associated with f0.
 func f0() {
        const pi = 3.14  // pi
        var s1 struct {}  /* an empty struct */ /* foo */
@@ -117,7 +117,7 @@ func f0() {
        x := pi
 }
 //
-// NO SPACE HERE
+// This comment should be associated with f1, with one blank line before the comment.
 //
 func f1() {
        f0()
@@ -130,7 +130,7 @@ func f1() {
 
 
 func _() {
-       // this comment should be properly indented
+// this comment should be properly indented
 }
 
 
index bfa2568..239ba89 100644 (file)
@@ -115,6 +115,18 @@ import _ "io"
 
 var _ int
 
+// at least one empty line between declarations of the same kind
+// if there is associated documentation (was issue 2570)
+type T1 struct{}
+
+// T2 comment
+type T2 struct {
+}      // should be a two-line struct
+
+// T3 comment
+type T2 struct {
+}      // should be a two-line struct
+
 // printing of constant literals
 const (
        _       = "foobar"
@@ -286,6 +298,15 @@ type _ struct {
        }
 }
 
+// no blank lines in empty structs and interfaces, but leave 1- or 2-line layout alone
+type _ struct{}
+type _ struct {
+}
+
+type _ interface{}
+type _ interface {
+}
+
 // no tabs for single or ungrouped decls
 func _() {
        const xxxxxx = 0
index 1d69c57..68f9030 100644 (file)
@@ -115,6 +115,20 @@ import (
 import _ "io"
 var _ int
 
+// at least one empty line between declarations of the same kind
+// if there is associated documentation (was issue 2570)
+type T1 struct{}
+// T2 comment
+type T2 struct {
+} // should be a two-line struct
+
+
+// T3 comment
+type T2 struct {
+
+
+} // should be a two-line struct
+
 
 // printing of constant literals
 const (
@@ -293,6 +307,18 @@ type _ struct {
 }
 
 
+// no blank lines in empty structs and interfaces, but leave 1- or 2-line layout alone
+type _ struct{            }
+type _ struct {
+
+}
+
+type _ interface{            }
+type _ interface {
+
+}
+
+
 // no tabs for single or ungrouped decls
 func _() {
        const xxxxxx = 0
index a6d8510..90e1743 100644 (file)
@@ -271,7 +271,6 @@ func _() {
        // Known bug: The first use call may have more than one empty line before
        //            (see go/printer/nodes.go, func linebreak).
 
-
        use(x)
 
        if x < x {
@@ -386,7 +385,6 @@ L:  // A comment on the same line as the label, followed by a single empty line.
        // Known bug: There may be more than one empty line before MoreCode()
        //            (see go/printer/nodes.go, func linebreak).
 
-
        MoreCode()
 }
 
index cef9c48..7fb0104 100644 (file)
@@ -426,13 +426,16 @@ func (S *Scanner) scanString() {
        S.next()
 }
 
-func (S *Scanner) scanRawString() {
+func (S *Scanner) scanRawString() (hasCR bool) {
        // '`' opening already consumed
        offs := S.offset - 1
 
        for S.ch != '`' {
                ch := S.ch
                S.next()
+               if ch == '\r' {
+                       hasCR = true
+               }
                if ch < 0 {
                        S.error(offs, "string not terminated")
                        break
@@ -440,6 +443,7 @@ func (S *Scanner) scanRawString() {
        }
 
        S.next()
+       return
 }
 
 func (S *Scanner) skipWhitespace() {
@@ -490,6 +494,18 @@ func (S *Scanner) switch4(tok0, tok1 token.Token, ch2 rune, tok2, tok3 token.Tok
        return tok0
 }
 
+func stripCR(b []byte) []byte {
+       c := make([]byte, len(b))
+       i := 0
+       for _, ch := range b {
+               if ch != '\r' {
+                       c[i] = ch
+                       i++
+               }
+       }
+       return c[:i]
+}
+
 // Scan scans the next token and returns the token position,
 // the token, and the literal string corresponding to the
 // token. The source end is indicated by token.EOF.
@@ -518,6 +534,7 @@ scanAgain:
        insertSemi := false
        offs := S.offset
        tok := token.ILLEGAL
+       hasCR := false
 
        // determine token value
        switch ch := S.ch; {
@@ -556,7 +573,7 @@ scanAgain:
                case '`':
                        insertSemi = true
                        tok = token.STRING
-                       S.scanRawString()
+                       hasCR = S.scanRawString()
                case ':':
                        tok = S.switch2(token.COLON, token.DEFINE)
                case '.':
@@ -663,5 +680,9 @@ scanAgain:
        // TODO(gri): The scanner API should change such that the literal string
        //            is only valid if an actual literal was scanned. This will
        //            permit a more efficient implementation.
-       return S.file.Pos(offs), tok, string(S.src[offs:S.offset])
+       lit := S.src[offs:S.offset]
+       if hasCR {
+               lit = stripCR(lit)
+       }
+       return S.file.Pos(offs), tok, string(lit)
 }
index 7ed927a..dc8ab2a 100644 (file)
@@ -83,6 +83,8 @@ var tokens = [...]elt{
                "`",
                literal,
        },
+       {token.STRING, "`\r`", literal},
+       {token.STRING, "`foo\r\nbar`", literal},
 
        // Operators and delimiters
        {token.ADD, "+", operator},
@@ -239,8 +241,16 @@ func TestScan(t *testing.T) {
                if tok != e.tok {
                        t.Errorf("bad token for %q: got %s, expected %s", lit, tok, e.tok)
                }
-               if e.tok.IsLiteral() && lit != e.lit {
-                       t.Errorf("bad literal for %q: got %q, expected %q", lit, lit, e.lit)
+               if e.tok.IsLiteral() {
+                       // no CRs in raw string literals
+                       elit := e.lit
+                       if elit[0] == '`' {
+                               elit = string(stripCR([]byte(elit)))
+                               epos.Offset += len(e.lit) - len(lit) // correct position
+                       }
+                       if lit != elit {
+                               t.Errorf("bad literal for %q: got %q, expected %q", lit, lit, elit)
+                       }
                }
                if tokenclass(tok) != e.class {
                        t.Errorf("bad class for %q: got %d, expected %d", lit, tokenclass(tok), e.class)
index 832e9db..d7cc8bb 100644 (file)
@@ -7,7 +7,7 @@ package html
 // Section 12.2.3.2 of the HTML5 specification says "The following elements
 // have varying levels of special parsing rules".
 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements
-var isSpecialElement = map[string]bool{
+var isSpecialElementMap = map[string]bool{
        "address":    true,
        "applet":     true,
        "area":       true,
@@ -88,3 +88,13 @@ var isSpecialElement = map[string]bool{
        "wbr":        true,
        "xmp":        true,
 }
+
+func isSpecialElement(element *Node) bool {
+       switch element.Namespace {
+       case "", "html":
+               return isSpecialElementMap[element.Data]
+       case "svg":
+               return element.Data == "foreignObject"
+       }
+       return false
+}
index 4cb2469..6962e64 100644 (file)
@@ -319,10 +319,7 @@ func (p *parser) resetInsertionMode() {
                case "html":
                        p.im = beforeHeadIM
                default:
-                       if p.top().Namespace == "" {
-                               continue
-                       }
-                       p.im = inForeignContentIM
+                       continue
                }
                return
        }
@@ -705,7 +702,7 @@ func inBodyIM(p *parser) bool {
                                case "address", "div", "p":
                                        continue
                                default:
-                                       if !isSpecialElement[node.Data] {
+                                       if !isSpecialElement(node) {
                                                continue
                                        }
                                }
@@ -723,7 +720,7 @@ func inBodyIM(p *parser) bool {
                                case "address", "div", "p":
                                        continue
                                default:
-                                       if !isSpecialElement[node.Data] {
+                                       if !isSpecialElement(node) {
                                                continue
                                        }
                                }
@@ -814,7 +811,6 @@ func inBodyIM(p *parser) bool {
                        // TODO: adjust foreign attributes.
                        p.addElement(p.tok.Data, p.tok.Attr)
                        p.top().Namespace = namespace
-                       p.im = inForeignContentIM
                        return true
                case "caption", "col", "colgroup", "frame", "head", "tbody", "td", "tfoot", "th", "thead", "tr":
                        // Ignore the token.
@@ -895,7 +891,7 @@ func (p *parser) inBodyEndTagFormatting(tag string) {
                // Steps 5-6. Find the furthest block.
                var furthestBlock *Node
                for _, e := range p.oe[feIndex:] {
-                       if isSpecialElement[e.Data] {
+                       if isSpecialElement(e) {
                                furthestBlock = e
                                break
                        }
@@ -988,7 +984,7 @@ func (p *parser) inBodyEndTagOther(tag string) {
                        p.oe = p.oe[:i]
                        break
                }
-               if isSpecialElement[p.oe[i].Data] {
+               if isSpecialElement(p.oe[i]) {
                        break
                }
        }
@@ -1206,6 +1202,13 @@ func inTableBodyIM(p *parser) bool {
                        add = true
                        data = "tr"
                        consumed = false
+               case "caption", "col", "colgroup", "tbody", "tfoot", "thead":
+                       if !p.popUntil(tableScopeStopTags, "tbody", "thead", "tfoot") {
+                               // Ignore the token.
+                               return true
+                       }
+                       p.im = inTableIM
+                       return false
                default:
                        // TODO.
                }
@@ -1569,6 +1572,19 @@ func afterAfterFramesetIM(p *parser) bool {
                        Type: CommentNode,
                        Data: p.tok.Data,
                })
+       case TextToken:
+               // Ignore all text but whitespace.
+               s := strings.Map(func(c rune) rune {
+                       switch c {
+                       case ' ', '\t', '\n', '\f', '\r':
+                               return c
+                       }
+                       return -1
+               }, p.tok.Data)
+               if s != "" {
+                       p.reconstructActiveFormattingElements()
+                       p.addText(s)
+               }
        case StartTagToken:
                switch p.tok.Data {
                case "html":
@@ -1583,8 +1599,19 @@ func afterAfterFramesetIM(p *parser) bool {
 }
 
 // Section 12.2.5.5.
-func inForeignContentIM(p *parser) bool {
+func parseForeignContent(p *parser) bool {
        switch p.tok.Type {
+       case TextToken:
+               // TODO: HTML integration points.
+               if p.top().Namespace == "" {
+                       inBodyIM(p)
+                       p.resetInsertionMode()
+                       return true
+               }
+               if p.framesetOK {
+                       p.framesetOK = strings.TrimLeft(p.tok.Data, whitespace) == ""
+               }
+               p.addText(p.tok.Data)
        case CommentToken:
                p.addChild(&Node{
                        Type: CommentNode,
@@ -1592,7 +1619,14 @@ func inForeignContentIM(p *parser) bool {
                })
        case StartTagToken:
                if breakout[p.tok.Data] {
-                       // TODO.
+                       for i := len(p.oe) - 1; i >= 0; i-- {
+                               // TODO: HTML, MathML integration points.
+                               if p.oe[i].Namespace == "" {
+                                       p.oe = p.oe[:i+1]
+                                       break
+                               }
+                       }
+                       return false
                }
                switch p.top().Namespace {
                case "mathml":
@@ -1606,13 +1640,36 @@ func inForeignContentIM(p *parser) bool {
                // TODO: adjust foreign attributes.
                p.addElement(p.tok.Data, p.tok.Attr)
        case EndTagToken:
-               // TODO.
+               for i := len(p.oe) - 1; i >= 0; i-- {
+                       if p.oe[i].Namespace == "" {
+                               return p.im(p)
+                       }
+                       if strings.EqualFold(p.oe[i].Data, p.tok.Data) {
+                               p.oe = p.oe[:i]
+                               break
+                       }
+               }
+               return true
        default:
                // Ignore the token.
        }
        return true
 }
 
+// Section 12.2.5.
+func (p *parser) inForeignContent() bool {
+       if len(p.oe) == 0 {
+               return false
+       }
+       n := p.oe[len(p.oe)-1]
+       if n.Namespace == "" {
+               return false
+       }
+       // TODO: MathML, HTML integration points.
+       // TODO: MathML's annotation-xml combining with SVG's svg.
+       return true
+}
+
 func (p *parser) parse() error {
        // Iterate until EOF. Any other error will cause an early return.
        consumed := true
@@ -1625,7 +1682,11 @@ func (p *parser) parse() error {
                                return err
                        }
                }
-               consumed = p.im(p)
+               if p.inForeignContent() {
+                       consumed = parseForeignContent(p)
+               } else {
+                       consumed = p.im(p)
+               }
        }
        // Loop until the final token (the ErrorToken signifying EOF) is consumed.
        for {
index e887631..015b583 100644 (file)
@@ -172,7 +172,8 @@ func TestParser(t *testing.T) {
                {"tests3.dat", -1},
                {"tests4.dat", -1},
                {"tests5.dat", -1},
-               {"tests6.dat", 36},
+               {"tests6.dat", 47},
+               {"tests10.dat", 16},
        }
        for _, tf := range testFiles {
                f, err := os.Open("testdata/webkit/" + tf.filename)
index 9622d7e..dcac748 100644 (file)
@@ -183,11 +183,11 @@ const (
 
 func (e *Error) Error() string {
        if e.Line != 0 {
-               return fmt.Sprintf("exp/template/html:%s:%d: %s", e.Name, e.Line, e.Description)
+               return fmt.Sprintf("html/template:%s:%d: %s", e.Name, e.Line, e.Description)
        } else if e.Name != "" {
-               return fmt.Sprintf("exp/template/html:%s: %s", e.Name, e.Description)
+               return fmt.Sprintf("html/template:%s: %s", e.Name, e.Description)
        }
-       return "exp/template/html: " + e.Description
+       return "html/template: " + e.Description
 }
 
 // errorf creates an error given a format string f and args.
index 2f6be3b..c6f723a 100644 (file)
@@ -486,9 +486,17 @@ func (e *escaper) escapeTree(c context, name string, line int) (context, string)
        }
        t := e.template(name)
        if t == nil {
+               // Two cases: The template exists but is empty, or has never been mentioned at
+               // all. Distinguish the cases in the error messages.
+               if e.tmpl.set[name] != nil {
+                       return context{
+                               state: stateError,
+                               err:   errorf(ErrNoSuchTemplate, line, "%q is an incomplete or empty template", name),
+                       }, dname
+               }
                return context{
                        state: stateError,
-                       err:   errorf(ErrNoSuchTemplate, line, "no such template %s", name),
+                       err:   errorf(ErrNoSuchTemplate, line, "no such template %q", name),
                }, dname
        }
        if dname != name {
index 2d15c71..a57f982 100644 (file)
@@ -928,7 +928,7 @@ func TestErrors(t *testing.T) {
                },
                {
                        `{{template "foo"}}`,
-                       "z:1: no such template foo",
+                       "z:1: no such template \"foo\"",
                },
                {
                        `<div{{template "y"}}>` +
@@ -944,23 +944,23 @@ func TestErrors(t *testing.T) {
                },
                {
                        `<input type=button value=onclick=>`,
-                       `exp/template/html:z: "=" in unquoted attr: "onclick="`,
+                       `html/template:z: "=" in unquoted attr: "onclick="`,
                },
                {
                        `<input type=button value= onclick=>`,
-                       `exp/template/html:z: "=" in unquoted attr: "onclick="`,
+                       `html/template:z: "=" in unquoted attr: "onclick="`,
                },
                {
                        `<input type=button value= 1+1=2>`,
-                       `exp/template/html:z: "=" in unquoted attr: "1+1=2"`,
+                       `html/template:z: "=" in unquoted attr: "1+1=2"`,
                },
                {
                        "<a class=`foo>",
-                       "exp/template/html:z: \"`\" in unquoted attr: \"`foo\"",
+                       "html/template:z: \"`\" in unquoted attr: \"`foo\"",
                },
                {
                        `<a style=font:'Arial'>`,
-                       `exp/template/html:z: "'" in unquoted attr: "font:'Arial'"`,
+                       `html/template:z: "'" in unquoted attr: "font:'Arial'"`,
                },
                {
                        `<a=foo>`,
diff --git a/libgo/go/image/color/ycbcr.go b/libgo/go/image/color/ycbcr.go
new file mode 100644 (file)
index 0000000..c79816d
--- /dev/null
@@ -0,0 +1,99 @@
+// 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 color
+
+// RGBToYCbCr converts an RGB triple to a Y'CbCr triple. All components lie
+// within the range [0, 255].
+func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
+       // The JFIF specification says:
+       //      Y' =  0.2990*R + 0.5870*G + 0.1140*B
+       //      Cb = -0.1687*R - 0.3313*G + 0.5000*B + 128
+       //      Cr =  0.5000*R - 0.4187*G - 0.0813*B + 128
+       // http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'.
+       r1 := int(r)
+       g1 := int(g)
+       b1 := int(b)
+       yy := (19595*r1 + 38470*g1 + 7471*b1 + 1<<15) >> 16
+       cb := (-11056*r1 - 21712*g1 + 32768*b1 + 257<<15) >> 16
+       cr := (32768*r1 - 27440*g1 - 5328*b1 + 257<<15) >> 16
+       if yy < 0 {
+               yy = 0
+       } else if yy > 255 {
+               yy = 255
+       }
+       if cb < 0 {
+               cb = 0
+       } else if cb > 255 {
+               cb = 255
+       }
+       if cr < 0 {
+               cr = 0
+       } else if cr > 255 {
+               cr = 255
+       }
+       return uint8(yy), uint8(cb), uint8(cr)
+}
+
+// YCbCrToRGB converts a Y'CbCr triple to an RGB triple. All components lie
+// within the range [0, 255].
+func YCbCrToRGB(y, cb, cr uint8) (uint8, uint8, uint8) {
+       // The JFIF specification says:
+       //      R = Y' + 1.40200*(Cr-128)
+       //      G = Y' - 0.34414*(Cb-128) - 0.71414*(Cr-128)
+       //      B = Y' + 1.77200*(Cb-128)
+       // http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'.
+       yy1 := int(y)<<16 + 1<<15
+       cb1 := int(cb) - 128
+       cr1 := int(cr) - 128
+       r := (yy1 + 91881*cr1) >> 16
+       g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
+       b := (yy1 + 116130*cb1) >> 16
+       if r < 0 {
+               r = 0
+       } else if r > 255 {
+               r = 255
+       }
+       if g < 0 {
+               g = 0
+       } else if g > 255 {
+               g = 255
+       }
+       if b < 0 {
+               b = 0
+       } else if b > 255 {
+               b = 255
+       }
+       return uint8(r), uint8(g), uint8(b)
+}
+
+// YCbCr represents a fully opaque 24-bit Y'CbCr color, having 8 bits each for
+// one luma and two chroma components.
+//
+// JPEG, VP8, the MPEG family and other codecs use this color model. Such
+// codecs often use the terms YUV and Y'CbCr interchangeably, but strictly
+// speaking, the term YUV applies only to analog video signals, and Y' (luma)
+// is Y (luminance) after applying gamma correction.
+//
+// Conversion between RGB and Y'CbCr is lossy and there are multiple, slightly
+// different formulae for converting between the two. This package follows
+// the JFIF specification at http://www.w3.org/Graphics/JPEG/jfif3.pdf.
+type YCbCr struct {
+       Y, Cb, Cr uint8
+}
+
+func (c YCbCr) RGBA() (uint32, uint32, uint32, uint32) {
+       r, g, b := YCbCrToRGB(c.Y, c.Cb, c.Cr)
+       return uint32(r) * 0x101, uint32(g) * 0x101, uint32(b) * 0x101, 0xffff
+}
+
+// YCbCrModel is the Model for Y'CbCr colors.
+var YCbCrModel Model = ModelFunc(func(c Color) Color {
+       if _, ok := c.(YCbCr); ok {
+               return c
+       }
+       r, g, b, _ := c.RGBA()
+       y, u, v := RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8))
+       return YCbCr{y, u, v}
+})
similarity index 98%
rename from libgo/go/image/ycbcr/ycbcr_test.go
rename to libgo/go/image/color/ycbcr_test.go
index 2e60a6f..92a0e6f 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package ycbcr
+package color
 
 import (
        "testing"
index 2be9118..4cd2095 100644 (file)
@@ -7,7 +7,6 @@ package draw
 import (
        "image"
        "image/color"
-       "image/ycbcr"
        "testing"
 )
 
@@ -51,7 +50,7 @@ func bench(b *testing.B, dcm, scm, mcm color.Model, op Op) {
                }
                dst = dst1
        default:
-               panic("unreachable")
+               b.Fatal("unknown destination color model", dcm)
        }
 
        var src image.Image
@@ -97,7 +96,7 @@ func bench(b *testing.B, dcm, scm, mcm color.Model, op Op) {
                        }
                }
                src = src1
-       case ycbcr.YCbCrColorModel:
+       case color.YCbCrModel:
                yy := make([]uint8, srcw*srch)
                cb := make([]uint8, srcw*srch)
                cr := make([]uint8, srcw*srch)
@@ -106,17 +105,17 @@ func bench(b *testing.B, dcm, scm, mcm color.Model, op Op) {
                        cb[i] = uint8(5 * i % 0x100)
                        cr[i] = uint8(7 * i % 0x100)
                }
-               src = &ycbcr.YCbCr{
+               src = &image.YCbCr{
                        Y:              yy,
                        Cb:             cb,
                        Cr:             cr,
                        YStride:        srcw,
                        CStride:        srcw,
-                       SubsampleRatio: ycbcr.SubsampleRatio444,
+                       SubsampleRatio: image.YCbCrSubsampleRatio444,
                        Rect:           image.Rect(0, 0, srcw, srch),
                }
        default:
-               panic("unreachable")
+               b.Fatal("unknown source color model", scm)
        }
 
        var mask image.Image
@@ -137,7 +136,7 @@ func bench(b *testing.B, dcm, scm, mcm color.Model, op Op) {
                }
                mask = mask1
        default:
-               panic("unreachable")
+               b.Fatal("unknown mask color model", mcm)
        }
 
        b.StartTimer()
@@ -177,7 +176,7 @@ func BenchmarkNRGBASrc(b *testing.B) {
 }
 
 func BenchmarkYCbCr(b *testing.B) {
-       bench(b, color.RGBAModel, ycbcr.YCbCrColorModel, nil, Over)
+       bench(b, color.RGBAModel, color.YCbCrModel, nil, Over)
 }
 
 func BenchmarkGlyphOver(b *testing.B) {
index af02639..228ed6e 100644 (file)
@@ -11,7 +11,6 @@ package draw
 import (
        "image"
        "image/color"
-       "image/ycbcr"
 )
 
 // m is the maximum color value returned by image.Color.RGBA.
@@ -81,7 +80,7 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
                                case *image.NRGBA:
                                        drawNRGBAOver(dst0, r, src0, sp)
                                        return
-                               case *ycbcr.YCbCr:
+                               case *image.YCbCr:
                                        drawYCbCr(dst0, r, src0, sp)
                                        return
                                }
@@ -104,7 +103,7 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
                                case *image.NRGBA:
                                        drawNRGBASrc(dst0, r, src0, sp)
                                        return
-                               case *ycbcr.YCbCr:
+                               case *image.YCbCr:
                                        drawYCbCr(dst0, r, src0, sp)
                                        return
                                }
@@ -346,8 +345,8 @@ func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image
        }
 }
 
-func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *ycbcr.YCbCr, sp image.Point) {
-       // A YCbCr image is always fully opaque, and so if the mask is implicitly nil
+func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) {
+       // An image.YCbCr is always fully opaque, and so if the mask is implicitly nil
        // (i.e. fully opaque) then the op is effectively always Src.
        var (
                yy, cb, cr uint8
@@ -357,7 +356,7 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *ycbcr.YCbCr, sp image.Po
        y0 := r.Min.Y - dst.Rect.Min.Y
        y1 := r.Max.Y - dst.Rect.Min.Y
        switch src.SubsampleRatio {
-       case ycbcr.SubsampleRatio422:
+       case image.YCbCrSubsampleRatio422:
                for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
                        dpix := dst.Pix[y*dst.Stride:]
                        for x, sx := x0, sp.X; x != x1; x, sx = x+4, sx+1 {
@@ -365,14 +364,14 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *ycbcr.YCbCr, sp image.Po
                                yy = src.Y[sy*src.YStride+sx]
                                cb = src.Cb[sy*src.CStride+i]
                                cr = src.Cr[sy*src.CStride+i]
-                               rr, gg, bb := ycbcr.YCbCrToRGB(yy, cb, cr)
+                               rr, gg, bb := color.YCbCrToRGB(yy, cb, cr)
                                dpix[x+0] = rr
                                dpix[x+1] = gg
                                dpix[x+2] = bb
                                dpix[x+3] = 255
                        }
                }
-       case ycbcr.SubsampleRatio420:
+       case image.YCbCrSubsampleRatio420:
                for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
                        dpix := dst.Pix[y*dst.Stride:]
                        for x, sx := x0, sp.X; x != x1; x, sx = x+4, sx+1 {
@@ -380,7 +379,7 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *ycbcr.YCbCr, sp image.Po
                                yy = src.Y[sy*src.YStride+sx]
                                cb = src.Cb[j*src.CStride+i]
                                cr = src.Cr[j*src.CStride+i]
-                               rr, gg, bb := ycbcr.YCbCrToRGB(yy, cb, cr)
+                               rr, gg, bb := color.YCbCrToRGB(yy, cb, cr)
                                dpix[x+0] = rr
                                dpix[x+1] = gg
                                dpix[x+2] = bb
@@ -395,7 +394,7 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *ycbcr.YCbCr, sp image.Po
                                yy = src.Y[sy*src.YStride+sx]
                                cb = src.Cb[sy*src.CStride+sx]
                                cr = src.Cr[sy*src.CStride+sx]
-                               rr, gg, bb := ycbcr.YCbCrToRGB(yy, cb, cr)
+                               rr, gg, bb := color.YCbCrToRGB(yy, cb, cr)
                                dpix[x+0] = rr
                                dpix[x+1] = gg
                                dpix[x+2] = bb
index 663ab67..56a4502 100644 (file)
@@ -7,7 +7,6 @@ package draw
 import (
        "image"
        "image/color"
-       "image/ycbcr"
        "testing"
 )
 
@@ -56,13 +55,13 @@ func vgradGreenNRGBA(alpha int) image.Image {
 }
 
 func vgradCr() image.Image {
-       m := &ycbcr.YCbCr{
+       m := &image.YCbCr{
                Y:              make([]byte, 16*16),
                Cb:             make([]byte, 16*16),
                Cr:             make([]byte, 16*16),
                YStride:        16,
                CStride:        16,
-               SubsampleRatio: ycbcr.SubsampleRatio444,
+               SubsampleRatio: image.YCbCrSubsampleRatio444,
                Rect:           image.Rect(0, 0, 16, 16),
        }
        for y := 0; y < 16; y++ {
index c1fc2d5..ed1a962 100644 (file)
@@ -11,7 +11,6 @@ import (
        "bufio"
        "image"
        "image/color"
-       "image/ycbcr"
        "io"
 )
 
@@ -97,7 +96,7 @@ type decoder struct {
        r             Reader
        width, height int
        img1          *image.Gray
-       img3          *ycbcr.YCbCr
+       img3          *image.YCbCr
        ri            int // Restart Interval.
        nComp         int
        comp          [nColorComponent]component
@@ -203,20 +202,20 @@ func (d *decoder) makeImg(h0, v0, mxx, myy int) {
                d.img1 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.Gray)
                return
        }
-       var subsampleRatio ycbcr.SubsampleRatio
+       var subsampleRatio image.YCbCrSubsampleRatio
        n := h0 * v0
        switch n {
        case 1:
-               subsampleRatio = ycbcr.SubsampleRatio444
+               subsampleRatio = image.YCbCrSubsampleRatio444
        case 2:
-               subsampleRatio = ycbcr.SubsampleRatio422
+               subsampleRatio = image.YCbCrSubsampleRatio422
        case 4:
-               subsampleRatio = ycbcr.SubsampleRatio420
+               subsampleRatio = image.YCbCrSubsampleRatio420
        default:
                panic("unreachable")
        }
        b := make([]byte, mxx*myy*(1*8*8*n+2*8*8))
-       d.img3 = &ycbcr.YCbCr{
+       d.img3 = &image.YCbCr{
                Y:              b[mxx*myy*(0*8*8*n+0*8*8) : mxx*myy*(1*8*8*n+0*8*8)],
                Cb:             b[mxx*myy*(1*8*8*n+0*8*8) : mxx*myy*(1*8*8*n+1*8*8)],
                Cr:             b[mxx*myy*(1*8*8*n+1*8*8) : mxx*myy*(1*8*8*n+2*8*8)],
@@ -466,7 +465,7 @@ func DecodeConfig(r io.Reader) (image.Config, error) {
        case nGrayComponent:
                return image.Config{color.GrayModel, d.width, d.height}, nil
        case nColorComponent:
-               return image.Config{ycbcr.YCbCrColorModel, d.width, d.height}, nil
+               return image.Config{color.YCbCrModel, d.width, d.height}, nil
        }
        return image.Config{}, FormatError("missing SOF marker")
 }
index fab0bd0..71fe37c 100644 (file)
@@ -8,7 +8,7 @@ import (
        "bufio"
        "errors"
        "image"
-       "image/ycbcr"
+       "image/color"
        "io"
 )
 
@@ -379,7 +379,7 @@ func toYCbCr(m image.Image, p image.Point, yBlock, cbBlock, crBlock *block) {
        for j := 0; j < 8; j++ {
                for i := 0; i < 8; i++ {
                        r, g, b, _ := m.At(min(p.X+i, xmax), min(p.Y+j, ymax)).RGBA()
-                       yy, cb, cr := ycbcr.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8))
+                       yy, cb, cr := color.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8))
                        yBlock[8*j+i] = int(yy)
                        cbBlock[8*j+i] = int(cb)
                        crBlock[8*j+i] = int(cr)
@@ -404,7 +404,7 @@ func rgbaToYCbCr(m *image.RGBA, p image.Point, yBlock, cbBlock, crBlock *block)
                                sx = xmax
                        }
                        pix := m.Pix[offset+sx*4:]
-                       yy, cb, cr := ycbcr.RGBToYCbCr(pix[0], pix[1], pix[2])
+                       yy, cb, cr := color.RGBToYCbCr(pix[0], pix[1], pix[2])
                        yBlock[8*j+i] = int(yy)
                        cbBlock[8*j+i] = int(cb)
                        crBlock[8*j+i] = int(cr)
index 28e8732..e4b56d2 100644 (file)
@@ -105,7 +105,7 @@ func BenchmarkEncodeRGBOpaque(b *testing.B) {
                }
        }
        if !img.Opaque() {
-               panic("expected image to be opaque")
+               b.Fatal("expected image to be opaque")
        }
        b.SetBytes(640 * 480 * 4)
        b.StartTimer()
index 1757e14..228eccc 100644 (file)
@@ -125,7 +125,7 @@ func BenchmarkEncodeRGBOpaque(b *testing.B) {
                }
        }
        if !img.Opaque() {
-               panic("expected image to be opaque")
+               b.Fatal("expected image to be opaque")
        }
        b.SetBytes(640 * 480 * 4)
        b.StartTimer()
@@ -138,7 +138,7 @@ func BenchmarkEncodeRGBA(b *testing.B) {
        b.StopTimer()
        img := image.NewRGBA(image.Rect(0, 0, 640, 480))
        if img.Opaque() {
-               panic("expected image to not be opaque")
+               b.Fatal("expected image to not be opaque")
        }
        b.SetBytes(640 * 480 * 4)
        b.StartTimer()
index 1a3d23b..ee5dafd 100644 (file)
@@ -113,7 +113,7 @@ func BenchmarkDecode(b *testing.B) {
        for i := 0; i < b.N; i++ {
                _, err := Decode(r)
                if err != nil {
-                       panic(err)
+                       b.Fatal("Decode:", err)
                }
        }
 }
diff --git a/libgo/go/image/ycbcr.go b/libgo/go/image/ycbcr.go
new file mode 100644 (file)
index 0000000..81f3c9f
--- /dev/null
@@ -0,0 +1,87 @@
+// 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 image
+
+import (
+       "image/color"
+)
+
+// YCbCrSubsampleRatio is the chroma subsample ratio used in a YCbCr image.
+type YCbCrSubsampleRatio int
+
+const (
+       YCbCrSubsampleRatio444 YCbCrSubsampleRatio = iota
+       YCbCrSubsampleRatio422
+       YCbCrSubsampleRatio420
+)
+
+// YCbCr is an in-memory image of Y'CbCr colors. There is one Y sample per
+// pixel, but each Cb and Cr sample can span one or more pixels.
+// YStride is the Y slice index delta between vertically adjacent pixels.
+// CStride is the Cb and Cr slice index delta between vertically adjacent pixels
+// that map to separate chroma samples.
+// It is not an absolute requirement, but YStride and len(Y) are typically
+// multiples of 8, and:
+//     For 4:4:4, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/1.
+//     For 4:2:2, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/2.
+//     For 4:2:0, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/4.
+type YCbCr struct {
+       Y              []uint8
+       Cb             []uint8
+       Cr             []uint8
+       YStride        int
+       CStride        int
+       SubsampleRatio YCbCrSubsampleRatio
+       Rect           Rectangle
+}
+
+func (p *YCbCr) ColorModel() color.Model {
+       return color.YCbCrModel
+}
+
+func (p *YCbCr) Bounds() Rectangle {
+       return p.Rect
+}
+
+func (p *YCbCr) At(x, y int) color.Color {
+       if !(Point{x, y}.In(p.Rect)) {
+               return color.YCbCr{}
+       }
+       switch p.SubsampleRatio {
+       case YCbCrSubsampleRatio422:
+               i := x / 2
+               return color.YCbCr{
+                       p.Y[y*p.YStride+x],
+                       p.Cb[y*p.CStride+i],
+                       p.Cr[y*p.CStride+i],
+               }
+       case YCbCrSubsampleRatio420:
+               i, j := x/2, y/2
+               return color.YCbCr{
+                       p.Y[y*p.YStride+x],
+                       p.Cb[j*p.CStride+i],
+                       p.Cr[j*p.CStride+i],
+               }
+       }
+       // Default to 4:4:4 subsampling.
+       return color.YCbCr{
+               p.Y[y*p.YStride+x],
+               p.Cb[y*p.CStride+x],
+               p.Cr[y*p.CStride+x],
+       }
+}
+
+// SubImage returns an image representing the portion of the image p visible
+// through r. The returned value shares pixels with the original image.
+func (p *YCbCr) SubImage(r Rectangle) Image {
+       q := new(YCbCr)
+       *q = *p
+       q.Rect = q.Rect.Intersect(r)
+       return q
+}
+
+func (p *YCbCr) Opaque() bool {
+       return true
+}
diff --git a/libgo/go/image/ycbcr/ycbcr.go b/libgo/go/image/ycbcr/ycbcr.go
deleted file mode 100644 (file)
index