diff options
author | bstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4> | 2011-12-14 08:52:21 +0000 |
---|---|---|
committer | bstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4> | 2011-12-14 08:52:21 +0000 |
commit | d0b175bffc2f2cb91d1fb529b7c36d3e984d9594 (patch) | |
tree | eb438cb5c7cabed8d102b2c0c1bdd1b0aebb59eb /libgo | |
parent | 5e3123db0a9b4c8def9fee64446b130ce81ace45 (diff) | |
download | gcc-d0b175bffc2f2cb91d1fb529b7c36d3e984d9594.tar.gz |
2011-12-14 Basile Starynkevitch <basile@starynkevitch.net>
MELT branch merged with trunk rev 182322 using svnmerge
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/melt-branch@182325 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo')
398 files changed, 12850 insertions, 8470 deletions
diff --git a/libgo/MERGE b/libgo/MERGE index 5e896c008bf..9847f4715a5 100644 --- a/libgo/MERGE +++ b/libgo/MERGE @@ -1,4 +1,4 @@ -2f4482b89a6b +0beb796b4ef8 The first line of this file holds the Mercurial revision number of the last merge done from the master library sources. diff --git a/libgo/Makefile.am b/libgo/Makefile.am index cd264e32668..67ae8710903 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -233,7 +233,6 @@ toolexeclibgoexpdir = $(toolexeclibgodir)/exp toolexeclibgoexp_DATA = \ exp/ebnf.gox \ - exp/gui.gox \ $(exp_inotify_gox) \ exp/norm.gox \ exp/spdy.gox \ @@ -242,11 +241,6 @@ toolexeclibgoexp_DATA = \ exp/terminal.gox \ exp/types.gox -toolexeclibgoexpguidir = $(toolexeclibgoexpdir)/gui - -toolexeclibgoexpgui_DATA = \ - exp/gui/x11.gox - toolexeclibgoexpsqldir = $(toolexeclibgoexpdir)/sql toolexeclibgoexpsql_DATA = \ @@ -447,6 +441,7 @@ runtime_files = \ runtime/go-map-len.c \ runtime/go-map-range.c \ runtime/go-nanotime.c \ + runtime/go-now.c \ runtime/go-new-map.c \ runtime/go-new.c \ runtime/go-panic.c \ @@ -576,6 +571,7 @@ go_hash_files = \ go_html_files = \ go/html/const.go \ go/html/doc.go \ + go/html/doctype.go \ go/html/entity.go \ go/html/escape.go \ go/html/node.go \ @@ -648,7 +644,8 @@ go_math_files = \ go_mime_files = \ go/mime/grammar.go \ go/mime/mediatype.go \ - go/mime/type.go + go/mime/type.go \ + go/mime/type_unix.go if LIBGO_IS_RTEMS go_net_fd_os_file = go/net/fd_select.go @@ -770,7 +767,6 @@ go_os_files = \ $(go_os_dir_file) \ go/os/dir.go \ go/os/env.go \ - go/os/env_unix.go \ go/os/error.go \ go/os/error_posix.go \ go/os/exec.go \ @@ -888,7 +884,7 @@ go_time_files = \ go/time/sys_unix.go \ go/time/tick.go \ go/time/time.go \ - go/time/zoneinfo_posix.go \ + go/time/zoneinfo.go \ go/time/zoneinfo_unix.go go_unicode_files = \ @@ -1038,6 +1034,7 @@ go_crypto_twofish_files = \ go_crypto_x509_files = \ go/crypto/x509/cert_pool.go \ go/crypto/x509/pkcs1.go \ + go/crypto/x509/pkcs8.go \ go/crypto/x509/verify.go \ go/crypto/x509/x509.go go_crypto_xtea_files = \ @@ -1135,8 +1132,6 @@ go_encoding_xml_files = \ go_exp_ebnf_files = \ go/exp/ebnf/ebnf.go \ go/exp/ebnf/parser.go -go_exp_gui_files = \ - go/exp/gui/gui.go go_exp_inotify_files = \ go/exp/inotify/inotify_linux.go go_exp_norm_files = \ @@ -1156,6 +1151,7 @@ go_exp_sql_files = \ go/exp/sql/sql.go go_exp_ssh_files = \ go/exp/ssh/channel.go \ + go/exp/ssh/cipher.go \ go/exp/ssh/client.go \ go/exp/ssh/client_auth.go \ go/exp/ssh/common.go \ @@ -1164,10 +1160,11 @@ go_exp_ssh_files = \ go/exp/ssh/server.go \ go/exp/ssh/server_shell.go \ go/exp/ssh/session.go \ + go/exp/ssh/tcpip.go \ go/exp/ssh/transport.go go_exp_terminal_files = \ - go/exp/terminal/shell.go \ - go/exp/terminal/terminal.go + go/exp/terminal/terminal.go \ + go/exp/terminal/util.go go_exp_types_files = \ go/exp/types/check.go \ go/exp/types/const.go \ @@ -1176,10 +1173,6 @@ go_exp_types_files = \ go/exp/types/types.go \ go/exp/types/universe.go -go_exp_gui_x11_files = \ - go/exp/gui/x11/auth.go \ - go/exp/gui/x11/conn.go - go_exp_sql_driver_files = \ go/exp/sql/driver/driver.go \ go/exp/sql/driver/types.go @@ -1413,13 +1406,11 @@ go_text_template_files = \ go/text/template/exec.go \ go/text/template/funcs.go \ go/text/template/helper.go \ - go/text/template/parse.go \ - go/text/template/set.go + go/text/template/template.go go_text_template_parse_files = \ go/text/template/parse/lex.go \ go/text/template/parse/node.go \ - go/text/template/parse/parse.go \ - go/text/template/parse/set.go + go/text/template/parse/parse.go go_sync_atomic_files = \ go/sync/atomic/doc.go @@ -1459,11 +1450,22 @@ syscall_exec_file = go/syscall/exec_unix.go endif # Define Wait4. +if LIBGO_IS_RTEMS +syscall_wait_file = +else if HAVE_WAIT4 syscall_wait_file = go/syscall/libcall_wait4.go else syscall_wait_file = go/syscall/libcall_waitpid.go endif +endif + +# Support for pulling apart wait status. +if LIBGO_IS_RTEMS +syscall_wait_c_file = +else +syscall_wait_c_file = go/syscall/wait.c +endif # Define Sleep. if LIBGO_IS_RTEMS @@ -1546,6 +1548,7 @@ syscall_netlink_file = endif go_base_syscall_files = \ + go/syscall/env_unix.go \ go/syscall/libcall_support.go \ go/syscall/libcall_posix.go \ go/syscall/socket.go \ @@ -1572,7 +1575,7 @@ go_syscall_files = \ syscall_arch.go go_syscall_c_files = \ go/syscall/errno.c \ - go/syscall/wait.c + $(syscall_wait_c_file) libcalls.go: s-libcalls; @true s-libcalls: Makefile go/syscall/mksyscall.awk $(go_base_syscall_files) @@ -1722,14 +1725,12 @@ libgo_go_objs = \ encoding/pem.lo \ encoding/xml.lo \ exp/ebnf.lo \ - exp/gui.lo \ exp/norm.lo \ exp/spdy.lo \ exp/sql.lo \ exp/ssh.lo \ exp/terminal.lo \ exp/types.lo \ - exp/gui/x11.lo \ exp/sql/driver.lo \ html/template.lo \ go/ast.lo \ @@ -2781,16 +2782,6 @@ exp/ebnf/check: $(CHECK_DEPS) @$(CHECK) .PHONY: exp/ebnf/check -@go_include@ exp/gui.lo.dep -exp/gui.lo.dep: $(go_exp_gui_files) - $(BUILDDEPS) -exp/gui.lo: $(go_exp_gui_files) - $(BUILDPACKAGE) -exp/gui/check: $(CHECK_DEPS) - @$(MKDIR_P) exp/gui - @$(CHECK) -.PHONY: exp/gui/check - @go_include@ exp/norm.lo.dep exp/norm.lo.dep: $(go_exp_norm_files) $(BUILDDEPS) @@ -2851,16 +2842,6 @@ exp/types/check: $(CHECK_DEPS) @$(CHECK) .PHONY: exp/types/check -@go_include@ exp/gui/x11.lo.dep -exp/gui/x11.lo.dep: $(go_exp_gui_x11_files) - $(BUILDDEPS) -exp/gui/x11.lo: $(go_exp_gui_x11_files) - $(BUILDPACKAGE) -exp/gui/x11/check: $(CHECK_DEPS) - @$(MKDIR_P) exp/gui/x11 - @$(CHECK) -.PHONY: exp/gui/x11/check - @go_include@ exp/inotify.lo.dep exp/inotify.lo.dep: $(go_exp_inotify_files) $(BUILDDEPS) @@ -3683,8 +3664,6 @@ encoding/xml.gox: encoding/xml.lo exp/ebnf.gox: exp/ebnf.lo $(BUILDGOX) -exp/gui.gox: exp/gui.lo - $(BUILDGOX) exp/inotify.gox: exp/inotify.lo $(BUILDGOX) exp/norm.gox: exp/norm.lo @@ -3700,9 +3679,6 @@ exp/terminal.gox: exp/terminal.lo exp/types.gox: exp/types.lo $(BUILDGOX) -exp/gui/x11.gox: exp/gui/x11.lo - $(BUILDGOX) - exp/sql/driver.gox: exp/sql/driver.lo $(BUILDGOX) @@ -3947,6 +3923,7 @@ TEST_PACKAGES = \ html/template/check \ go/ast/check \ $(go_build_check_omitted_since_it_calls_6g) \ + go/doc/check \ go/parser/check \ go/printer/check \ go/scanner/check \ diff --git a/libgo/Makefile.in b/libgo/Makefile.in index 11b86509e6f..2e1ea76eb2c 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -102,7 +102,6 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" \ "$(DESTDIR)$(toolexeclibgodebugdir)" \ "$(DESTDIR)$(toolexeclibgoencodingdir)" \ "$(DESTDIR)$(toolexeclibgoexpdir)" \ - "$(DESTDIR)$(toolexeclibgoexpguidir)" \ "$(DESTDIR)$(toolexeclibgoexpsqldir)" \ "$(DESTDIR)$(toolexeclibgogodir)" \ "$(DESTDIR)$(toolexeclibgohashdir)" \ @@ -161,15 +160,15 @@ am__DEPENDENCIES_2 = bufio/bufio.lo bytes/bytes.lo bytes/index.lo \ encoding/base64.lo encoding/binary.lo encoding/csv.lo \ encoding/git85.lo encoding/gob.lo encoding/hex.lo \ encoding/json.lo encoding/pem.lo encoding/xml.lo exp/ebnf.lo \ - exp/gui.lo exp/norm.lo exp/spdy.lo exp/sql.lo exp/ssh.lo \ - exp/terminal.lo exp/types.lo exp/gui/x11.lo exp/sql/driver.lo \ - html/template.lo go/ast.lo go/build.lo go/doc.lo go/parser.lo \ - go/printer.lo go/scanner.lo go/token.lo hash/adler32.lo \ - hash/crc32.lo hash/crc64.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 \ + exp/norm.lo exp/spdy.lo exp/sql.lo exp/ssh.lo exp/terminal.lo \ + exp/types.lo exp/sql/driver.lo html/template.lo go/ast.lo \ + go/build.lo go/doc.lo go/parser.lo go/printer.lo go/scanner.lo \ + go/token.lo hash/adler32.lo hash/crc32.lo hash/crc64.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 \ @@ -200,12 +199,12 @@ am__libgo_la_SOURCES_DIST = runtime/go-append.c runtime/go-assert.c \ runtime/go-interface-val-compare.c runtime/go-make-slice.c \ runtime/go-map-delete.c runtime/go-map-index.c \ runtime/go-map-len.c runtime/go-map-range.c \ - runtime/go-nanotime.c runtime/go-new-map.c runtime/go-new.c \ - runtime/go-panic.c runtime/go-print.c runtime/go-recover.c \ - runtime/go-reflect.c runtime/go-reflect-call.c \ - runtime/go-reflect-map.c runtime/go-rune.c \ - runtime/go-runtime-error.c runtime/go-setenv.c \ - runtime/go-signal.c runtime/go-strcmp.c \ + runtime/go-nanotime.c runtime/go-now.c runtime/go-new-map.c \ + runtime/go-new.c runtime/go-panic.c runtime/go-print.c \ + runtime/go-recover.c runtime/go-reflect.c \ + runtime/go-reflect-call.c runtime/go-reflect-map.c \ + runtime/go-rune.c runtime/go-runtime-error.c \ + runtime/go-setenv.c runtime/go-signal.c runtime/go-strcmp.c \ runtime/go-string-to-byte-array.c \ runtime/go-string-to-int-array.c runtime/go-strplus.c \ runtime/go-strslice.c runtime/go-trampoline.c \ @@ -238,20 +237,20 @@ am__objects_4 = go-append.lo go-assert.lo go-assert-interface.lo \ go-interface-compare.lo go-interface-eface-compare.lo \ go-interface-val-compare.lo go-make-slice.lo go-map-delete.lo \ go-map-index.lo go-map-len.lo go-map-range.lo go-nanotime.lo \ - go-new-map.lo go-new.lo go-panic.lo go-print.lo go-recover.lo \ - go-reflect.lo go-reflect-call.lo go-reflect-map.lo go-rune.lo \ - go-runtime-error.lo go-setenv.lo go-signal.lo go-strcmp.lo \ - go-string-to-byte-array.lo go-string-to-int-array.lo \ - go-strplus.lo go-strslice.lo go-trampoline.lo go-type-eface.lo \ - go-type-error.lo go-type-identity.lo go-type-interface.lo \ - go-type-string.lo go-typedesc-equal.lo go-typestring.lo \ - go-unreflect.lo go-unsafe-new.lo go-unsafe-newarray.lo \ - go-unsafe-pointer.lo go-unwind.lo chan.lo cpuprof.lo \ - $(am__objects_1) mcache.lo mcentral.lo $(am__objects_2) \ - mfinal.lo mfixalloc.lo mgc0.lo mheap.lo msize.lo proc.lo \ - runtime.lo thread.lo yield.lo $(am__objects_3) iface.lo \ - malloc.lo map.lo mprof.lo reflect.lo runtime1.lo sema.lo \ - sigqueue.lo string.lo time.lo + go-now.lo go-new-map.lo go-new.lo go-panic.lo go-print.lo \ + go-recover.lo go-reflect.lo go-reflect-call.lo \ + go-reflect-map.lo go-rune.lo go-runtime-error.lo go-setenv.lo \ + go-signal.lo go-strcmp.lo go-string-to-byte-array.lo \ + go-string-to-int-array.lo go-strplus.lo go-strslice.lo \ + go-trampoline.lo go-type-eface.lo go-type-error.lo \ + go-type-identity.lo go-type-interface.lo go-type-string.lo \ + go-typedesc-equal.lo go-typestring.lo go-unreflect.lo \ + go-unsafe-new.lo go-unsafe-newarray.lo go-unsafe-pointer.lo \ + go-unwind.lo chan.lo cpuprof.lo $(am__objects_1) mcache.lo \ + mcentral.lo $(am__objects_2) mfinal.lo mfixalloc.lo mgc0.lo \ + mheap.lo msize.lo proc.lo runtime.lo thread.lo yield.lo \ + $(am__objects_3) iface.lo malloc.lo map.lo mprof.lo reflect.lo \ + runtime1.lo sema.lo sigqueue.lo string.lo time.lo am_libgo_la_OBJECTS = $(am__objects_4) libgo_la_OBJECTS = $(am_libgo_la_OBJECTS) libgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ @@ -290,18 +289,18 @@ DATA = $(toolexeclibgo_DATA) $(toolexeclibgoarchive_DATA) \ $(toolexeclibgocrypto_DATA) $(toolexeclibgocryptoopenpgp_DATA) \ $(toolexeclibgocryptox509_DATA) $(toolexeclibgodebug_DATA) \ $(toolexeclibgoencoding_DATA) $(toolexeclibgoexp_DATA) \ - $(toolexeclibgoexpgui_DATA) $(toolexeclibgoexpsql_DATA) \ - $(toolexeclibgogo_DATA) $(toolexeclibgohash_DATA) \ - $(toolexeclibgohtml_DATA) $(toolexeclibgoimage_DATA) \ - $(toolexeclibgoindex_DATA) $(toolexeclibgoio_DATA) \ - $(toolexeclibgolog_DATA) $(toolexeclibgomath_DATA) \ - $(toolexeclibgomime_DATA) $(toolexeclibgonet_DATA) \ - $(toolexeclibgonethttp_DATA) $(toolexeclibgonetrpc_DATA) \ - $(toolexeclibgoold_DATA) $(toolexeclibgoos_DATA) \ - $(toolexeclibgopath_DATA) $(toolexeclibgoregexp_DATA) \ - $(toolexeclibgoruntime_DATA) $(toolexeclibgosync_DATA) \ - $(toolexeclibgotesting_DATA) $(toolexeclibgotext_DATA) \ - $(toolexeclibgotexttemplate_DATA) $(toolexeclibgounicode_DATA) + $(toolexeclibgoexpsql_DATA) $(toolexeclibgogo_DATA) \ + $(toolexeclibgohash_DATA) $(toolexeclibgohtml_DATA) \ + $(toolexeclibgoimage_DATA) $(toolexeclibgoindex_DATA) \ + $(toolexeclibgoio_DATA) $(toolexeclibgolog_DATA) \ + $(toolexeclibgomath_DATA) $(toolexeclibgomime_DATA) \ + $(toolexeclibgonet_DATA) $(toolexeclibgonethttp_DATA) \ + $(toolexeclibgonetrpc_DATA) $(toolexeclibgoold_DATA) \ + $(toolexeclibgoos_DATA) $(toolexeclibgopath_DATA) \ + $(toolexeclibgoregexp_DATA) $(toolexeclibgoruntime_DATA) \ + $(toolexeclibgosync_DATA) $(toolexeclibgotesting_DATA) \ + $(toolexeclibgotext_DATA) $(toolexeclibgotexttemplate_DATA) \ + $(toolexeclibgounicode_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ @@ -690,7 +689,6 @@ toolexeclibgoencoding_DATA = \ toolexeclibgoexpdir = $(toolexeclibgodir)/exp toolexeclibgoexp_DATA = \ exp/ebnf.gox \ - exp/gui.gox \ $(exp_inotify_gox) \ exp/norm.gox \ exp/spdy.gox \ @@ -699,10 +697,6 @@ toolexeclibgoexp_DATA = \ exp/terminal.gox \ exp/types.gox -toolexeclibgoexpguidir = $(toolexeclibgoexpdir)/gui -toolexeclibgoexpgui_DATA = \ - exp/gui/x11.gox - toolexeclibgoexpsqldir = $(toolexeclibgoexpdir)/sql toolexeclibgoexpsql_DATA = \ exp/sql/driver.gox @@ -868,6 +862,7 @@ runtime_files = \ runtime/go-map-len.c \ runtime/go-map-range.c \ runtime/go-nanotime.c \ + runtime/go-now.c \ runtime/go-new-map.c \ runtime/go-new.c \ runtime/go-panic.c \ @@ -960,6 +955,7 @@ go_hash_files = \ go_html_files = \ go/html/const.go \ go/html/doc.go \ + go/html/doctype.go \ go/html/entity.go \ go/html/escape.go \ go/html/node.go \ @@ -1032,7 +1028,8 @@ go_math_files = \ go_mime_files = \ go/mime/grammar.go \ go/mime/mediatype.go \ - go/mime/type.go + go/mime/type.go \ + go/mime/type_unix.go # By default use select with pipes. Most systems should have # something better. @@ -1103,7 +1100,6 @@ go_os_files = \ $(go_os_dir_file) \ go/os/dir.go \ go/os/env.go \ - go/os/env_unix.go \ go/os/error.go \ go/os/error_posix.go \ go/os/exec.go \ @@ -1204,7 +1200,7 @@ go_time_files = \ go/time/sys_unix.go \ go/time/tick.go \ go/time/time.go \ - go/time/zoneinfo_posix.go \ + go/time/zoneinfo.go \ go/time/zoneinfo_unix.go go_unicode_files = \ @@ -1377,6 +1373,7 @@ go_crypto_twofish_files = \ go_crypto_x509_files = \ go/crypto/x509/cert_pool.go \ go/crypto/x509/pkcs1.go \ + go/crypto/x509/pkcs8.go \ go/crypto/x509/verify.go \ go/crypto/x509/x509.go @@ -1495,9 +1492,6 @@ go_exp_ebnf_files = \ go/exp/ebnf/ebnf.go \ go/exp/ebnf/parser.go -go_exp_gui_files = \ - go/exp/gui/gui.go - go_exp_inotify_files = \ go/exp/inotify/inotify_linux.go @@ -1521,6 +1515,7 @@ go_exp_sql_files = \ go_exp_ssh_files = \ go/exp/ssh/channel.go \ + go/exp/ssh/cipher.go \ go/exp/ssh/client.go \ go/exp/ssh/client_auth.go \ go/exp/ssh/common.go \ @@ -1529,11 +1524,12 @@ go_exp_ssh_files = \ go/exp/ssh/server.go \ go/exp/ssh/server_shell.go \ go/exp/ssh/session.go \ + go/exp/ssh/tcpip.go \ go/exp/ssh/transport.go go_exp_terminal_files = \ - go/exp/terminal/shell.go \ - go/exp/terminal/terminal.go + go/exp/terminal/terminal.go \ + go/exp/terminal/util.go go_exp_types_files = \ go/exp/types/check.go \ @@ -1543,10 +1539,6 @@ go_exp_types_files = \ go/exp/types/types.go \ go/exp/types/universe.go -go_exp_gui_x11_files = \ - go/exp/gui/x11/auth.go \ - go/exp/gui/x11/conn.go - go_exp_sql_driver_files = \ go/exp/sql/driver/driver.go \ go/exp/sql/driver/types.go @@ -1803,14 +1795,12 @@ go_text_template_files = \ go/text/template/exec.go \ go/text/template/funcs.go \ go/text/template/helper.go \ - go/text/template/parse.go \ - go/text/template/set.go + go/text/template/template.go go_text_template_parse_files = \ go/text/template/parse/lex.go \ go/text/template/parse/node.go \ - go/text/template/parse/parse.go \ - go/text/template/parse/set.go + go/text/template/parse/parse.go go_sync_atomic_files = \ go/sync/atomic/doc.go @@ -1847,10 +1837,15 @@ go_unicode_utf8_files = \ # Define ForkExec and Exec. @LIBGO_IS_RTEMS_TRUE@syscall_exec_file = go/syscall/exec_stubs.go -@HAVE_WAIT4_FALSE@syscall_wait_file = go/syscall/libcall_waitpid.go +@HAVE_WAIT4_FALSE@@LIBGO_IS_RTEMS_FALSE@syscall_wait_file = go/syscall/libcall_waitpid.go +@HAVE_WAIT4_TRUE@@LIBGO_IS_RTEMS_FALSE@syscall_wait_file = go/syscall/libcall_wait4.go # Define Wait4. -@HAVE_WAIT4_TRUE@syscall_wait_file = go/syscall/libcall_wait4.go +@LIBGO_IS_RTEMS_TRUE@syscall_wait_file = +@LIBGO_IS_RTEMS_FALSE@syscall_wait_c_file = go/syscall/wait.c + +# Support for pulling apart wait status. +@LIBGO_IS_RTEMS_TRUE@syscall_wait_c_file = @LIBGO_IS_RTEMS_FALSE@syscall_sleep_file = go/syscall/sleep_select.go # Define Sleep. @@ -1890,6 +1885,7 @@ go_unicode_utf8_files = \ # Support for netlink sockets and messages. @LIBGO_IS_LINUX_TRUE@syscall_netlink_file = go/syscall/netlink_linux.go go_base_syscall_files = \ + go/syscall/env_unix.go \ go/syscall/libcall_support.go \ go/syscall/libcall_posix.go \ go/syscall/socket.go \ @@ -1917,7 +1913,7 @@ go_syscall_files = \ go_syscall_c_files = \ go/syscall/errno.c \ - go/syscall/wait.c + $(syscall_wait_c_file) @LIBGO_IS_LINUX_FALSE@os_lib_inotify_lo = @@ -2013,14 +2009,12 @@ libgo_go_objs = \ encoding/pem.lo \ encoding/xml.lo \ exp/ebnf.lo \ - exp/gui.lo \ exp/norm.lo \ exp/spdy.lo \ exp/sql.lo \ exp/ssh.lo \ exp/terminal.lo \ exp/types.lo \ - exp/gui/x11.lo \ exp/sql/driver.lo \ html/template.lo \ go/ast.lo \ @@ -2292,6 +2286,7 @@ TEST_PACKAGES = \ html/template/check \ go/ast/check \ $(go_build_check_omitted_since_it_calls_6g) \ + go/doc/check \ go/parser/check \ go/printer/check \ go/scanner/check \ @@ -2508,6 +2503,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-nanotime.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-new-map.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-new.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-now.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-panic.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-print.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-recover.Plo@am__quote@ @@ -2796,6 +2792,13 @@ go-nanotime.lo: runtime/go-nanotime.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-nanotime.lo `test -f 'runtime/go-nanotime.c' || echo '$(srcdir)/'`runtime/go-nanotime.c +go-now.lo: runtime/go-now.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-now.lo -MD -MP -MF $(DEPDIR)/go-now.Tpo -c -o go-now.lo `test -f 'runtime/go-now.c' || echo '$(srcdir)/'`runtime/go-now.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-now.Tpo $(DEPDIR)/go-now.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-now.c' object='go-now.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-now.lo `test -f 'runtime/go-now.c' || echo '$(srcdir)/'`runtime/go-now.c + go-new-map.lo: runtime/go-new-map.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-new-map.lo -MD -MP -MF $(DEPDIR)/go-new-map.Tpo -c -o go-new-map.lo `test -f 'runtime/go-new-map.c' || echo '$(srcdir)/'`runtime/go-new-map.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-new-map.Tpo $(DEPDIR)/go-new-map.Plo @@ -3371,26 +3374,6 @@ uninstall-toolexeclibgoexpDATA: test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(toolexeclibgoexpdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(toolexeclibgoexpdir)" && rm -f $$files -install-toolexeclibgoexpguiDATA: $(toolexeclibgoexpgui_DATA) - @$(NORMAL_INSTALL) - test -z "$(toolexeclibgoexpguidir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgoexpguidir)" - @list='$(toolexeclibgoexpgui_DATA)'; test -n "$(toolexeclibgoexpguidir)" || list=; \ - for p in $$list; do \ - if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ - echo "$$d$$p"; \ - done | $(am__base_list) | \ - while read files; do \ - echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(toolexeclibgoexpguidir)'"; \ - $(INSTALL_DATA) $$files "$(DESTDIR)$(toolexeclibgoexpguidir)" || exit $$?; \ - done - -uninstall-toolexeclibgoexpguiDATA: - @$(NORMAL_UNINSTALL) - @list='$(toolexeclibgoexpgui_DATA)'; test -n "$(toolexeclibgoexpguidir)" || list=; \ - files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ - test -n "$$files" || exit 0; \ - echo " ( cd '$(DESTDIR)$(toolexeclibgoexpguidir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(toolexeclibgoexpguidir)" && rm -f $$files install-toolexeclibgoexpsqlDATA: $(toolexeclibgoexpsql_DATA) @$(NORMAL_INSTALL) test -z "$(toolexeclibgoexpsqldir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgoexpsqldir)" @@ -4168,7 +4151,7 @@ all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) all-multi $(DATA) \ config.h installdirs: installdirs-recursive installdirs-am: - for dir in "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibgodir)" "$(DESTDIR)$(toolexeclibgoarchivedir)" "$(DESTDIR)$(toolexeclibgocompressdir)" "$(DESTDIR)$(toolexeclibgocontainerdir)" "$(DESTDIR)$(toolexeclibgocryptodir)" "$(DESTDIR)$(toolexeclibgocryptoopenpgpdir)" "$(DESTDIR)$(toolexeclibgocryptox509dir)" "$(DESTDIR)$(toolexeclibgodebugdir)" "$(DESTDIR)$(toolexeclibgoencodingdir)" "$(DESTDIR)$(toolexeclibgoexpdir)" "$(DESTDIR)$(toolexeclibgoexpguidir)" "$(DESTDIR)$(toolexeclibgoexpsqldir)" "$(DESTDIR)$(toolexeclibgogodir)" "$(DESTDIR)$(toolexeclibgohashdir)" "$(DESTDIR)$(toolexeclibgohtmldir)" "$(DESTDIR)$(toolexeclibgoimagedir)" "$(DESTDIR)$(toolexeclibgoindexdir)" "$(DESTDIR)$(toolexeclibgoiodir)" "$(DESTDIR)$(toolexeclibgologdir)" "$(DESTDIR)$(toolexeclibgomathdir)" "$(DESTDIR)$(toolexeclibgomimedir)" "$(DESTDIR)$(toolexeclibgonetdir)" "$(DESTDIR)$(toolexeclibgonethttpdir)" "$(DESTDIR)$(toolexeclibgonetrpcdir)" "$(DESTDIR)$(toolexeclibgoolddir)" "$(DESTDIR)$(toolexeclibgoosdir)" "$(DESTDIR)$(toolexeclibgopathdir)" "$(DESTDIR)$(toolexeclibgoregexpdir)" "$(DESTDIR)$(toolexeclibgoruntimedir)" "$(DESTDIR)$(toolexeclibgosyncdir)" "$(DESTDIR)$(toolexeclibgotestingdir)" "$(DESTDIR)$(toolexeclibgotextdir)" "$(DESTDIR)$(toolexeclibgotexttemplatedir)" "$(DESTDIR)$(toolexeclibgounicodedir)"; do \ + for dir in "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibgodir)" "$(DESTDIR)$(toolexeclibgoarchivedir)" "$(DESTDIR)$(toolexeclibgocompressdir)" "$(DESTDIR)$(toolexeclibgocontainerdir)" "$(DESTDIR)$(toolexeclibgocryptodir)" "$(DESTDIR)$(toolexeclibgocryptoopenpgpdir)" "$(DESTDIR)$(toolexeclibgocryptox509dir)" "$(DESTDIR)$(toolexeclibgodebugdir)" "$(DESTDIR)$(toolexeclibgoencodingdir)" "$(DESTDIR)$(toolexeclibgoexpdir)" "$(DESTDIR)$(toolexeclibgoexpsqldir)" "$(DESTDIR)$(toolexeclibgogodir)" "$(DESTDIR)$(toolexeclibgohashdir)" "$(DESTDIR)$(toolexeclibgohtmldir)" "$(DESTDIR)$(toolexeclibgoimagedir)" "$(DESTDIR)$(toolexeclibgoindexdir)" "$(DESTDIR)$(toolexeclibgoiodir)" "$(DESTDIR)$(toolexeclibgologdir)" "$(DESTDIR)$(toolexeclibgomathdir)" "$(DESTDIR)$(toolexeclibgomimedir)" "$(DESTDIR)$(toolexeclibgonetdir)" "$(DESTDIR)$(toolexeclibgonethttpdir)" "$(DESTDIR)$(toolexeclibgonetrpcdir)" "$(DESTDIR)$(toolexeclibgoolddir)" "$(DESTDIR)$(toolexeclibgoosdir)" "$(DESTDIR)$(toolexeclibgopathdir)" "$(DESTDIR)$(toolexeclibgoregexpdir)" "$(DESTDIR)$(toolexeclibgoruntimedir)" "$(DESTDIR)$(toolexeclibgosyncdir)" "$(DESTDIR)$(toolexeclibgotestingdir)" "$(DESTDIR)$(toolexeclibgotextdir)" "$(DESTDIR)$(toolexeclibgotexttemplatedir)" "$(DESTDIR)$(toolexeclibgounicodedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive @@ -4238,7 +4221,6 @@ install-exec-am: install-multi install-toolexeclibLIBRARIES \ install-toolexeclibgocryptox509DATA \ install-toolexeclibgodebugDATA \ install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \ - install-toolexeclibgoexpguiDATA \ install-toolexeclibgoexpsqlDATA install-toolexeclibgogoDATA \ install-toolexeclibgohashDATA install-toolexeclibgohtmlDATA \ install-toolexeclibgoimageDATA install-toolexeclibgoindexDATA \ @@ -4304,7 +4286,6 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \ uninstall-toolexeclibgodebugDATA \ uninstall-toolexeclibgoencodingDATA \ uninstall-toolexeclibgoexpDATA \ - uninstall-toolexeclibgoexpguiDATA \ uninstall-toolexeclibgoexpsqlDATA \ uninstall-toolexeclibgogoDATA uninstall-toolexeclibgohashDATA \ uninstall-toolexeclibgohtmlDATA \ @@ -4352,7 +4333,6 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \ install-toolexeclibgocryptox509DATA \ install-toolexeclibgodebugDATA \ install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \ - install-toolexeclibgoexpguiDATA \ install-toolexeclibgoexpsqlDATA install-toolexeclibgogoDATA \ install-toolexeclibgohashDATA install-toolexeclibgohtmlDATA \ install-toolexeclibgoimageDATA install-toolexeclibgoindexDATA \ @@ -4382,7 +4362,6 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \ uninstall-toolexeclibgodebugDATA \ uninstall-toolexeclibgoencodingDATA \ uninstall-toolexeclibgoexpDATA \ - uninstall-toolexeclibgoexpguiDATA \ uninstall-toolexeclibgoexpsqlDATA \ uninstall-toolexeclibgogoDATA uninstall-toolexeclibgohashDATA \ uninstall-toolexeclibgohtmlDATA \ @@ -5380,16 +5359,6 @@ exp/ebnf/check: $(CHECK_DEPS) @$(CHECK) .PHONY: exp/ebnf/check -@go_include@ exp/gui.lo.dep -exp/gui.lo.dep: $(go_exp_gui_files) - $(BUILDDEPS) -exp/gui.lo: $(go_exp_gui_files) - $(BUILDPACKAGE) -exp/gui/check: $(CHECK_DEPS) - @$(MKDIR_P) exp/gui - @$(CHECK) -.PHONY: exp/gui/check - @go_include@ exp/norm.lo.dep exp/norm.lo.dep: $(go_exp_norm_files) $(BUILDDEPS) @@ -5450,16 +5419,6 @@ exp/types/check: $(CHECK_DEPS) @$(CHECK) .PHONY: exp/types/check -@go_include@ exp/gui/x11.lo.dep -exp/gui/x11.lo.dep: $(go_exp_gui_x11_files) - $(BUILDDEPS) -exp/gui/x11.lo: $(go_exp_gui_x11_files) - $(BUILDPACKAGE) -exp/gui/x11/check: $(CHECK_DEPS) - @$(MKDIR_P) exp/gui/x11 - @$(CHECK) -.PHONY: exp/gui/x11/check - @go_include@ exp/inotify.lo.dep exp/inotify.lo.dep: $(go_exp_inotify_files) $(BUILDDEPS) @@ -6277,8 +6236,6 @@ encoding/xml.gox: encoding/xml.lo exp/ebnf.gox: exp/ebnf.lo $(BUILDGOX) -exp/gui.gox: exp/gui.lo - $(BUILDGOX) exp/inotify.gox: exp/inotify.lo $(BUILDGOX) exp/norm.gox: exp/norm.lo @@ -6294,9 +6251,6 @@ exp/terminal.gox: exp/terminal.lo exp/types.gox: exp/types.lo $(BUILDGOX) -exp/gui/x11.gox: exp/gui/x11.lo - $(BUILDGOX) - exp/sql/driver.gox: exp/sql/driver.lo $(BUILDGOX) diff --git a/libgo/go/archive/tar/common.go b/libgo/go/archive/tar/common.go index 67355086a63..fc7a40923cd 100644 --- a/libgo/go/archive/tar/common.go +++ b/libgo/go/archive/tar/common.go @@ -11,41 +11,42 @@ // http://www.gnu.org/software/tar/manual/html_node/Standard.html package tar +import "time" + const ( blockSize = 512 // Types - TypeReg = '0' // regular file. - TypeRegA = '\x00' // regular file. - TypeLink = '1' // hard link. - TypeSymlink = '2' // symbolic link. - TypeChar = '3' // character device node. - TypeBlock = '4' // block device node. - TypeDir = '5' // directory. - TypeFifo = '6' // fifo node. - TypeCont = '7' // reserved. - TypeXHeader = 'x' // extended header. - TypeXGlobalHeader = 'g' // global extended header. + TypeReg = '0' // regular file + TypeRegA = '\x00' // regular file + TypeLink = '1' // hard link + TypeSymlink = '2' // symbolic link + TypeChar = '3' // character device node + TypeBlock = '4' // block device node + TypeDir = '5' // directory + TypeFifo = '6' // fifo node + TypeCont = '7' // reserved + TypeXHeader = 'x' // extended header + TypeXGlobalHeader = 'g' // global extended header ) // A Header represents a single header in a tar archive. // Some fields may not be populated. type Header struct { - Name string // name of header file entry. - Mode int64 // permission and mode bits. - Uid int // user id of owner. - Gid int // group id of owner. - Size int64 // length in bytes. - Mtime int64 // modified time; seconds since epoch. - Typeflag byte // type of header entry. - Linkname string // target name of link. - Uname string // user name of owner. - Gname string // group name of owner. - Devmajor int64 // major number of character or block device. - Devminor int64 // minor number of character or block device. - Atime int64 // access time; seconds since epoch. - Ctime int64 // status change time; seconds since epoch. - + Name string // name of header file entry + Mode int64 // permission and mode bits + Uid int // user id of owner + Gid int // group id of owner + Size int64 // length in bytes + ModTime time.Time // modified time + Typeflag byte // type of header entry + Linkname string // target name of link + Uname string // user name of owner + Gname string // group name of owner + Devmajor int64 // major number of character or block device + Devminor int64 // minor number of character or block device + AccessTime time.Time // access time + ChangeTime time.Time // status change time } var zeroBlock = make([]byte, blockSize) diff --git a/libgo/go/archive/tar/reader.go b/libgo/go/archive/tar/reader.go index facba2cc7a3..76955e2ec03 100644 --- a/libgo/go/archive/tar/reader.go +++ b/libgo/go/archive/tar/reader.go @@ -14,6 +14,7 @@ import ( "io/ioutil" "os" "strconv" + "time" ) var ( @@ -141,7 +142,7 @@ func (tr *Reader) readHeader() *Header { hdr.Uid = int(tr.octal(s.next(8))) hdr.Gid = int(tr.octal(s.next(8))) hdr.Size = tr.octal(s.next(12)) - hdr.Mtime = tr.octal(s.next(12)) + hdr.ModTime = time.Unix(tr.octal(s.next(12)), 0) s.next(8) // chksum hdr.Typeflag = s.next(1)[0] hdr.Linkname = cString(s.next(100)) @@ -178,8 +179,8 @@ func (tr *Reader) readHeader() *Header { prefix = cString(s.next(155)) case "star": prefix = cString(s.next(131)) - hdr.Atime = tr.octal(s.next(12)) - hdr.Ctime = tr.octal(s.next(12)) + hdr.AccessTime = time.Unix(tr.octal(s.next(12)), 0) + hdr.ChangeTime = time.Unix(tr.octal(s.next(12)), 0) } if len(prefix) > 0 { hdr.Name = prefix + "/" + hdr.Name diff --git a/libgo/go/archive/tar/reader_test.go b/libgo/go/archive/tar/reader_test.go index 00eea6b62d7..5ca4212ae7b 100644 --- a/libgo/go/archive/tar/reader_test.go +++ b/libgo/go/archive/tar/reader_test.go @@ -12,6 +12,7 @@ import ( "os" "reflect" "testing" + "time" ) type untarTest struct { @@ -29,7 +30,7 @@ var gnuTarTest = &untarTest{ Uid: 73025, Gid: 5000, Size: 5, - Mtime: 1244428340, + ModTime: time.Unix(1244428340, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", @@ -40,7 +41,7 @@ var gnuTarTest = &untarTest{ Uid: 73025, Gid: 5000, Size: 11, - Mtime: 1244436044, + ModTime: time.Unix(1244436044, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", @@ -58,30 +59,30 @@ var untarTests = []*untarTest{ file: "testdata/star.tar", headers: []*Header{ &Header{ - Name: "small.txt", - Mode: 0640, - Uid: 73025, - Gid: 5000, - Size: 5, - Mtime: 1244592783, - Typeflag: '0', - Uname: "dsymonds", - Gname: "eng", - Atime: 1244592783, - Ctime: 1244592783, + Name: "small.txt", + Mode: 0640, + Uid: 73025, + Gid: 5000, + Size: 5, + ModTime: time.Unix(1244592783, 0), + Typeflag: '0', + Uname: "dsymonds", + Gname: "eng", + AccessTime: time.Unix(1244592783, 0), + ChangeTime: time.Unix(1244592783, 0), }, &Header{ - Name: "small2.txt", - Mode: 0640, - Uid: 73025, - Gid: 5000, - Size: 11, - Mtime: 1244592783, - Typeflag: '0', - Uname: "dsymonds", - Gname: "eng", - Atime: 1244592783, - Ctime: 1244592783, + Name: "small2.txt", + Mode: 0640, + Uid: 73025, + Gid: 5000, + Size: 11, + ModTime: time.Unix(1244592783, 0), + Typeflag: '0', + Uname: "dsymonds", + Gname: "eng", + AccessTime: time.Unix(1244592783, 0), + ChangeTime: time.Unix(1244592783, 0), }, }, }, @@ -94,7 +95,7 @@ var untarTests = []*untarTest{ Uid: 73025, Gid: 5000, Size: 5, - Mtime: 1244593104, + ModTime: time.Unix(1244593104, 0), Typeflag: '\x00', }, &Header{ @@ -103,7 +104,7 @@ var untarTests = []*untarTest{ Uid: 73025, Gid: 5000, Size: 11, - Mtime: 1244593104, + ModTime: time.Unix(1244593104, 0), Typeflag: '\x00', }, }, @@ -221,7 +222,7 @@ func TestIncrementalRead(t *testing.T) { h.Write(rdbuf[0:nr]) } // verify checksum - have := fmt.Sprintf("%x", h.Sum()) + have := fmt.Sprintf("%x", h.Sum(nil)) want := cksums[nread] if want != have { t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want) diff --git a/libgo/go/archive/tar/writer.go b/libgo/go/archive/tar/writer.go index 222df90782c..b9310b3f189 100644 --- a/libgo/go/archive/tar/writer.go +++ b/libgo/go/archive/tar/writer.go @@ -127,19 +127,19 @@ func (tw *Writer) WriteHeader(hdr *Header) error { // TODO(dsymonds): handle names longer than 100 chars copy(s.next(100), []byte(hdr.Name)) - tw.octal(s.next(8), hdr.Mode) // 100:108 - tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116 - tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124 - tw.numeric(s.next(12), hdr.Size) // 124:136 - tw.numeric(s.next(12), hdr.Mtime) // 136:148 - s.next(8) // chksum (148:156) - s.next(1)[0] = hdr.Typeflag // 156:157 - tw.cString(s.next(100), hdr.Linkname) // linkname (157:257) - copy(s.next(8), []byte("ustar\x0000")) // 257:265 - tw.cString(s.next(32), hdr.Uname) // 265:297 - tw.cString(s.next(32), hdr.Gname) // 297:329 - tw.numeric(s.next(8), hdr.Devmajor) // 329:337 - tw.numeric(s.next(8), hdr.Devminor) // 337:345 + tw.octal(s.next(8), hdr.Mode) // 100:108 + tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116 + tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124 + tw.numeric(s.next(12), hdr.Size) // 124:136 + tw.numeric(s.next(12), hdr.ModTime.Unix()) // 136:148 + s.next(8) // chksum (148:156) + s.next(1)[0] = hdr.Typeflag // 156:157 + tw.cString(s.next(100), hdr.Linkname) // linkname (157:257) + copy(s.next(8), []byte("ustar\x0000")) // 257:265 + tw.cString(s.next(32), hdr.Uname) // 265:297 + tw.cString(s.next(32), hdr.Gname) // 297:329 + tw.numeric(s.next(8), hdr.Devmajor) // 329:337 + tw.numeric(s.next(8), hdr.Devminor) // 337:345 // Use the GNU magic instead of POSIX magic if we used any GNU extensions. if tw.usedBinary { diff --git a/libgo/go/archive/tar/writer_test.go b/libgo/go/archive/tar/writer_test.go index 6cc93868820..8d7ed32d32e 100644 --- a/libgo/go/archive/tar/writer_test.go +++ b/libgo/go/archive/tar/writer_test.go @@ -11,6 +11,7 @@ import ( "io/ioutil" "testing" "testing/iotest" + "time" ) type writerTestEntry struct { @@ -38,7 +39,7 @@ var writerTests = []*writerTest{ Uid: 73025, Gid: 5000, Size: 5, - Mtime: 1246508266, + ModTime: time.Unix(1246508266, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", @@ -52,7 +53,7 @@ var writerTests = []*writerTest{ Uid: 73025, Gid: 5000, Size: 11, - Mtime: 1245217492, + ModTime: time.Unix(1245217492, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", @@ -66,7 +67,7 @@ var writerTests = []*writerTest{ Uid: 1000, Gid: 1000, Size: 0, - Mtime: 1314603082, + ModTime: time.Unix(1314603082, 0), Typeflag: '2', Linkname: "small.txt", Uname: "strings", @@ -89,7 +90,7 @@ var writerTests = []*writerTest{ Uid: 73025, Gid: 5000, Size: 16 << 30, - Mtime: 1254699560, + ModTime: time.Unix(1254699560, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", diff --git a/libgo/go/archive/zip/reader.go b/libgo/go/archive/zip/reader.go index cfbe5498a15..4365009a308 100644 --- a/libgo/go/archive/zip/reader.go +++ b/libgo/go/archive/zip/reader.go @@ -56,7 +56,7 @@ func OpenReader(name string) (*ReadCloser, error) { return nil, err } r := new(ReadCloser) - if err := r.init(f, fi.Size); err != nil { + if err := r.init(f, fi.Size()); err != nil { f.Close() return nil, err } diff --git a/libgo/go/archive/zip/reader_test.go b/libgo/go/archive/zip/reader_test.go index ca0b04e2bba..8c0ecaa4386 100644 --- a/libgo/go/archive/zip/reader_test.go +++ b/libgo/go/archive/zip/reader_test.go @@ -164,8 +164,8 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) { t.Error(err) return } - if got, want := f.Mtime_ns()/1e9, mtime.Seconds(); got != want { - t.Errorf("%s: mtime=%s (%d); want %s (%d)", f.Name, time.SecondsToUTC(got), got, mtime, want) + if ft := f.ModTime(); !ft.Equal(mtime) { + t.Errorf("%s: mtime=%s, want %s", f.Name, ft, mtime) } testFileMode(t, f, ft.Mode) diff --git a/libgo/go/archive/zip/struct.go b/libgo/go/archive/zip/struct.go index b862b5a6acb..43c04bb27b2 100644 --- a/libgo/go/archive/zip/struct.go +++ b/libgo/go/archive/zip/struct.go @@ -11,8 +11,10 @@ This package does not support ZIP64 or disk spanning. */ package zip -import "errors" -import "time" +import ( + "errors" + "time" +) // Compression methods. const ( @@ -74,24 +76,26 @@ func recoverError(errp *error) { // The resolution is 2s. // See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx func msDosTimeToTime(dosDate, dosTime uint16) time.Time { - return time.Time{ + return time.Date( // date bits 0-4: day of month; 5-8: month; 9-15: years since 1980 - Year: int64(dosDate>>9 + 1980), - Month: int(dosDate >> 5 & 0xf), - Day: int(dosDate & 0x1f), + int(dosDate>>9+1980), + time.Month(dosDate>>5&0xf), + int(dosDate&0x1f), // time bits 0-4: second/2; 5-10: minute; 11-15: hour - Hour: int(dosTime >> 11), - Minute: int(dosTime >> 5 & 0x3f), - Second: int(dosTime & 0x1f * 2), - } + int(dosTime>>11), + int(dosTime>>5&0x3f), + int(dosTime&0x1f*2), + 0, // nanoseconds + + time.UTC, + ) } -// Mtime_ns returns the modified time in ns since epoch. +// ModTime returns the modification time. // The resolution is 2s. -func (h *FileHeader) Mtime_ns() int64 { - t := msDosTimeToTime(h.ModifiedDate, h.ModifiedTime) - return t.Seconds() * 1e9 +func (h *FileHeader) ModTime() time.Time { + return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime) } // Mode returns the permission and mode bits for the FileHeader. diff --git a/libgo/go/bufio/bufio_test.go b/libgo/go/bufio/bufio_test.go index 1f893951c15..54029cd40fd 100644 --- a/libgo/go/bufio/bufio_test.go +++ b/libgo/go/bufio/bufio_test.go @@ -10,7 +10,6 @@ import ( "fmt" "io" "io/ioutil" - "os" "strings" "testing" "testing/iotest" @@ -425,9 +424,9 @@ var errorWriterTests = []errorWriterTest{ {0, 1, nil, io.ErrShortWrite}, {1, 2, nil, io.ErrShortWrite}, {1, 1, nil, nil}, - {0, 1, os.EPIPE, os.EPIPE}, - {1, 2, os.EPIPE, os.EPIPE}, - {1, 1, os.EPIPE, os.EPIPE}, + {0, 1, io.ErrClosedPipe, io.ErrClosedPipe}, + {1, 2, io.ErrClosedPipe, io.ErrClosedPipe}, + {1, 1, io.ErrClosedPipe, io.ErrClosedPipe}, } func TestWriteErrors(t *testing.T) { diff --git a/libgo/go/builtin/builtin.go b/libgo/go/builtin/builtin.go index 5a7aaf364db..e81616ca418 100644 --- a/libgo/go/builtin/builtin.go +++ b/libgo/go/builtin/builtin.go @@ -91,6 +91,11 @@ type rune rune // invocation. type Type int +// Type1 is here for the purposes of documentation only. It is a stand-in +// for any Go type, but represents the same type for any given function +// invocation. +type Type1 int + // IntegerType is here for the purposes of documentation only. It is a stand-in // for any integer type: int, uint, int8 etc. type IntegerType int @@ -119,6 +124,11 @@ func append(slice []Type, elems ...Type) []Type // len(src) and len(dst). func copy(dst, src []Type) int +// The delete built-in function deletes the element with the specified key +// (m[key]) from the map. If there is no such element, delete is a no-op. +// If m is nil, delete panics. +func delete(m map[Type]Type1, key Type) + // The len built-in function returns the length of v, according to its type: // Array: the number of elements in v. // Pointer to array: the number of elements in *v (even if v is nil). @@ -171,7 +181,7 @@ func complex(r, i FloatType) ComplexType // The return value will be floating point type corresponding to the type of c. func real(c ComplexType) FloatType -// The imaginary built-in function returns the imaginary part of the complex +// The imag built-in function returns the imaginary part of the complex // number c. The return value will be floating point type corresponding to // the type of c. func imag(c ComplexType) FloatType diff --git a/libgo/go/bytes/bytes_test.go b/libgo/go/bytes/bytes_test.go index 9256b184274..829ef05319c 100644 --- a/libgo/go/bytes/bytes_test.go +++ b/libgo/go/bytes/bytes_test.go @@ -662,48 +662,49 @@ func TestRunes(t *testing.T) { } type TrimTest struct { - f func([]byte, string) []byte + f string in, cutset, out string } var trimTests = []TrimTest{ - {Trim, "abba", "a", "bb"}, - {Trim, "abba", "ab", ""}, - {TrimLeft, "abba", "ab", ""}, - {TrimRight, "abba", "ab", ""}, - {TrimLeft, "abba", "a", "bba"}, - {TrimRight, "abba", "a", "abb"}, - {Trim, "<tag>", "<>", "tag"}, - {Trim, "* listitem", " *", "listitem"}, - {Trim, `"quote"`, `"`, "quote"}, - {Trim, "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, + {"Trim", "abba", "a", "bb"}, + {"Trim", "abba", "ab", ""}, + {"TrimLeft", "abba", "ab", ""}, + {"TrimRight", "abba", "ab", ""}, + {"TrimLeft", "abba", "a", "bba"}, + {"TrimRight", "abba", "a", "abb"}, + {"Trim", "<tag>", "<>", "tag"}, + {"Trim", "* listitem", " *", "listitem"}, + {"Trim", `"quote"`, `"`, "quote"}, + {"Trim", "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, //empty string tests - {Trim, "abba", "", "abba"}, - {Trim, "", "123", ""}, - {Trim, "", "", ""}, - {TrimLeft, "abba", "", "abba"}, - {TrimLeft, "", "123", ""}, - {TrimLeft, "", "", ""}, - {TrimRight, "abba", "", "abba"}, - {TrimRight, "", "123", ""}, - {TrimRight, "", "", ""}, - {TrimRight, "☺\xc0", "☺", "☺\xc0"}, + {"Trim", "abba", "", "abba"}, + {"Trim", "", "123", ""}, + {"Trim", "", "", ""}, + {"TrimLeft", "abba", "", "abba"}, + {"TrimLeft", "", "123", ""}, + {"TrimLeft", "", "", ""}, + {"TrimRight", "abba", "", "abba"}, + {"TrimRight", "", "123", ""}, + {"TrimRight", "", "", ""}, + {"TrimRight", "☺\xc0", "☺", "☺\xc0"}, } func TestTrim(t *testing.T) { for _, tc := range trimTests { - actual := string(tc.f([]byte(tc.in), tc.cutset)) - var name string - switch tc.f { - case Trim: - name = "Trim" - case TrimLeft: - name = "TrimLeft" - case TrimRight: - name = "TrimRight" + name := tc.f + var f func([]byte, string) []byte + switch name { + case "Trim": + f = Trim + case "TrimLeft": + f = TrimLeft + case "TrimRight": + f = TrimRight default: - t.Error("Undefined trim function") + t.Errorf("Undefined trim function %s", name) } + actual := string(f([]byte(tc.in), tc.cutset)) if actual != tc.out { t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.cutset, actual, tc.out) } diff --git a/libgo/go/compress/gzip/gunzip.go b/libgo/go/compress/gzip/gunzip.go index a23e515e0e0..7c78b9e366d 100644 --- a/libgo/go/compress/gzip/gunzip.go +++ b/libgo/go/compress/gzip/gunzip.go @@ -13,6 +13,7 @@ import ( "hash" "hash/crc32" "io" + "time" ) // BUG(nigeltao): Comments and Names don't properly map UTF-8 character codes outside of @@ -42,11 +43,11 @@ var ChecksumError = errors.New("gzip checksum error") // The gzip file stores a header giving metadata about the compressed file. // That header is exposed as the fields of the Compressor and Decompressor structs. type Header struct { - Comment string // comment - Extra []byte // "extra data" - Mtime uint32 // modification time (seconds since January 1, 1970) - Name string // file name - OS byte // operating system type + Comment string // comment + Extra []byte // "extra data" + ModTime time.Time // modification time + Name string // file name + OS byte // operating system type } // An Decompressor is an io.Reader that can be read to retrieve @@ -130,7 +131,7 @@ func (z *Decompressor) readHeader(save bool) error { } z.flg = z.buf[3] if save { - z.Mtime = get4(z.buf[4:8]) + z.ModTime = time.Unix(int64(get4(z.buf[4:8])), 0) // z.buf[8] is xfl, ignored z.OS = z.buf[9] } diff --git a/libgo/go/compress/gzip/gzip.go b/libgo/go/compress/gzip/gzip.go index 94b0f1f85e2..07b91b66823 100644 --- a/libgo/go/compress/gzip/gzip.go +++ b/libgo/go/compress/gzip/gzip.go @@ -122,7 +122,7 @@ func (z *Compressor) Write(p []byte) (int, error) { if z.Comment != "" { z.buf[3] |= 0x10 } - put4(z.buf[4:8], z.Mtime) + put4(z.buf[4:8], uint32(z.ModTime.Unix())) if z.level == BestCompression { z.buf[8] = 2 } else if z.level == BestSpeed { diff --git a/libgo/go/compress/gzip/gzip_test.go b/libgo/go/compress/gzip/gzip_test.go index 121e627e6b2..815825be999 100644 --- a/libgo/go/compress/gzip/gzip_test.go +++ b/libgo/go/compress/gzip/gzip_test.go @@ -8,6 +8,7 @@ import ( "io" "io/ioutil" "testing" + "time" ) // pipe creates two ends of a pipe that gzip and gunzip, and runs dfunc at the @@ -53,7 +54,7 @@ func TestWriter(t *testing.T) { func(compressor *Compressor) { compressor.Comment = "comment" compressor.Extra = []byte("extra") - compressor.Mtime = 1e8 + compressor.ModTime = time.Unix(1e8, 0) compressor.Name = "name" _, err := compressor.Write([]byte("payload")) if err != nil { @@ -74,8 +75,8 @@ func TestWriter(t *testing.T) { if string(decompressor.Extra) != "extra" { t.Fatalf("extra is %q, want %q", decompressor.Extra, "extra") } - if decompressor.Mtime != 1e8 { - t.Fatalf("mtime is %d, want %d", decompressor.Mtime, uint32(1e8)) + if decompressor.ModTime.Unix() != 1e8 { + t.Fatalf("mtime is %d, want %d", decompressor.ModTime.Unix(), uint32(1e8)) } if decompressor.Name != "name" { t.Fatalf("name is %q, want %q", decompressor.Name, "name") diff --git a/libgo/go/compress/lzw/reader.go b/libgo/go/compress/lzw/reader.go index c787a9568b3..0ed742c8977 100644 --- a/libgo/go/compress/lzw/reader.go +++ b/libgo/go/compress/lzw/reader.go @@ -19,7 +19,6 @@ import ( "errors" "fmt" "io" - "os" ) // Order specifies the bit ordering in an LZW data stream. @@ -212,8 +211,10 @@ func (d *decoder) flush() { d.o = 0 } +var errClosed = errors.New("compress/lzw: reader/writer is closed") + func (d *decoder) Close() error { - d.err = os.EINVAL // in case any Reads come along + d.err = errClosed // in case any Reads come along return nil } diff --git a/libgo/go/compress/lzw/writer.go b/libgo/go/compress/lzw/writer.go index 3f380fadce2..488ba6428db 100644 --- a/libgo/go/compress/lzw/writer.go +++ b/libgo/go/compress/lzw/writer.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "io" - "os" ) // A writer is a buffered, flushable writer. @@ -49,8 +48,9 @@ const ( type encoder struct { // w is the writer that compressed bytes are written to. w writer - // write, bits, nBits and width are the state for converting a code stream - // into a byte stream. + // order, write, bits, nBits and width are the state for + // converting a code stream into a byte stream. + order Order write func(*encoder, uint32) error bits uint32 nBits uint @@ -64,7 +64,7 @@ type encoder struct { // call. It is equal to invalidCode if there was no such call. savedCode uint32 // err is the first error encountered during writing. Closing the encoder - // will make any future Write calls return os.EINVAL. + // will make any future Write calls return errClosed err error // table is the hash table from 20-bit keys to 12-bit values. Each table // entry contains key<<12|val and collisions resolve by linear probing. @@ -191,13 +191,13 @@ loop: // flush e's underlying writer. func (e *encoder) Close() error { if e.err != nil { - if e.err == os.EINVAL { + if e.err == errClosed { return nil } return e.err } - // Make any future calls to Write return os.EINVAL. - e.err = os.EINVAL + // Make any future calls to Write return errClosed. + e.err = errClosed // Write the savedCode if valid. if e.savedCode != invalidCode { if err := e.write(e, e.savedCode); err != nil { @@ -214,7 +214,7 @@ func (e *encoder) Close() error { } // Write the final bits. if e.nBits > 0 { - if e.write == (*encoder).writeMSB { + if e.order == MSB { e.bits >>= 24 } if err := e.w.WriteByte(uint8(e.bits)); err != nil { @@ -250,6 +250,7 @@ func NewWriter(w io.Writer, order Order, litWidth int) io.WriteCloser { lw := uint(litWidth) return &encoder{ w: bw, + order: order, write: write, width: 1 + lw, litWidth: lw, diff --git a/libgo/go/compress/lzw/writer_test.go b/libgo/go/compress/lzw/writer_test.go index 154cdf8090e..d249a09b295 100644 --- a/libgo/go/compress/lzw/writer_test.go +++ b/libgo/go/compress/lzw/writer_test.go @@ -50,10 +50,6 @@ func testFile(t *testing.T, fn string, order Order, litWidth int) { return } _, err1 := lzww.Write(b[:n]) - if err1 == os.EPIPE { - // Fail, but do not report the error, as some other (presumably reportable) error broke the pipe. - return - } if err1 != nil { t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err1) return diff --git a/libgo/go/compress/zlib/writer_test.go b/libgo/go/compress/zlib/writer_test.go index 32f05ab6856..a71894da320 100644 --- a/libgo/go/compress/zlib/writer_test.go +++ b/libgo/go/compress/zlib/writer_test.go @@ -59,10 +59,6 @@ func testLevelDict(t *testing.T, fn string, b0 []byte, level int, d string) { } defer zlibw.Close() _, err = zlibw.Write(b0) - if err == os.EPIPE { - // Fail, but do not report the error, as some other (presumably reported) error broke the pipe. - return - } if err != nil { t.Errorf("%s (level=%d, dict=%q): %v", fn, level, d, err) return diff --git a/libgo/go/crypto/aes/cipher.go b/libgo/go/crypto/aes/cipher.go index 5ad75eccb50..28752e73613 100644 --- a/libgo/go/crypto/aes/cipher.go +++ b/libgo/go/crypto/aes/cipher.go @@ -41,7 +41,7 @@ func NewCipher(key []byte) (*Cipher, error) { } // BlockSize returns the AES block size, 16 bytes. -// It is necessary to satisfy the Cipher interface in the +// It is necessary to satisfy the Block interface in the // package "crypto/cipher". func (c *Cipher) BlockSize() int { return BlockSize } diff --git a/libgo/go/crypto/bcrypt/bcrypt.go b/libgo/go/crypto/bcrypt/bcrypt.go index 97401356228..362b2eb53cb 100644 --- a/libgo/go/crypto/bcrypt/bcrypt.go +++ b/libgo/go/crypto/bcrypt/bcrypt.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package bcrypt implements Provos and Mazières's bcrypt adapative hashing +// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing // algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf package bcrypt diff --git a/libgo/go/crypto/blowfish/cipher.go b/libgo/go/crypto/blowfish/cipher.go index a5d56d2ebae..94e10f0e267 100644 --- a/libgo/go/crypto/blowfish/cipher.go +++ b/libgo/go/crypto/blowfish/cipher.go @@ -54,7 +54,7 @@ func NewSaltedCipher(key, salt []byte) (*Cipher, error) { } // BlockSize returns the Blowfish block size, 8 bytes. -// It is necessary to satisfy the Cipher interface in the +// It is necessary to satisfy the Block interface in the // package "crypto/cipher". func (c *Cipher) BlockSize() int { return BlockSize } diff --git a/libgo/go/crypto/ecdsa/ecdsa_test.go b/libgo/go/crypto/ecdsa/ecdsa_test.go index 22360b5708c..45433e10203 100644 --- a/libgo/go/crypto/ecdsa/ecdsa_test.go +++ b/libgo/go/crypto/ecdsa/ecdsa_test.go @@ -214,7 +214,7 @@ func TestVectors(t *testing.T) { msg, _ := hex.DecodeString(test.msg) sha.Reset() sha.Write(msg) - hashed := sha.Sum() + hashed := sha.Sum(nil) r := fromHex(test.r) s := fromHex(test.s) if Verify(&pub, hashed, r, s) != test.ok { diff --git a/libgo/go/crypto/hmac/hmac.go b/libgo/go/crypto/hmac/hmac.go index 6a17bbd44fa..deaceafb260 100644 --- a/libgo/go/crypto/hmac/hmac.go +++ b/libgo/go/crypto/hmac/hmac.go @@ -48,15 +48,15 @@ func (h *hmac) tmpPad(xor byte) { } } -func (h *hmac) Sum() []byte { - sum := h.inner.Sum() +func (h *hmac) Sum(in []byte) []byte { + sum := h.inner.Sum(nil) h.tmpPad(0x5c) for i, b := range sum { h.tmp[padSize+i] = b } h.outer.Reset() h.outer.Write(h.tmp) - return h.outer.Sum() + return h.outer.Sum(in) } func (h *hmac) Write(p []byte) (n int, err error) { @@ -81,7 +81,7 @@ func New(h func() hash.Hash, key []byte) hash.Hash { if len(key) > padSize { // If key is too big, hash it. hm.outer.Write(key) - key = hm.outer.Sum() + key = hm.outer.Sum(nil) } hm.key = make([]byte, len(key)) copy(hm.key, key) diff --git a/libgo/go/crypto/hmac/hmac_test.go b/libgo/go/crypto/hmac/hmac_test.go index 03431c92f75..eac254b9d19 100644 --- a/libgo/go/crypto/hmac/hmac_test.go +++ b/libgo/go/crypto/hmac/hmac_test.go @@ -192,7 +192,7 @@ func TestHMAC(t *testing.T) { // Repetitive Sum() calls should return the same value for k := 0; k < 2; k++ { - sum := fmt.Sprintf("%x", h.Sum()) + sum := fmt.Sprintf("%x", h.Sum(nil)) if sum != tt.out { t.Errorf("test %d.%d.%d: have %s want %s\n", i, j, k, sum, tt.out) } diff --git a/libgo/go/crypto/md4/md4.go b/libgo/go/crypto/md4/md4.go index f21cc51a21a..e51e8bee50c 100644 --- a/libgo/go/crypto/md4/md4.go +++ b/libgo/go/crypto/md4/md4.go @@ -77,7 +77,7 @@ func (d *digest) Write(p []byte) (nn int, err error) { return } -func (d0 *digest) Sum() []byte { +func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0, so that caller can keep writing and summing. d := new(digest) *d = *d0 @@ -103,14 +103,11 @@ func (d0 *digest) Sum() []byte { panic("d.nx != 0") } - p := make([]byte, 16) - j := 0 for _, s := range d.s { - p[j+0] = byte(s >> 0) - p[j+1] = byte(s >> 8) - p[j+2] = byte(s >> 16) - p[j+3] = byte(s >> 24) - j += 4 + in = append(in, byte(s>>0)) + in = append(in, byte(s>>8)) + in = append(in, byte(s>>16)) + in = append(in, byte(s>>24)) } - return p + return in } diff --git a/libgo/go/crypto/md4/md4_test.go b/libgo/go/crypto/md4/md4_test.go index 721bd4cbcc8..b56edd7875d 100644 --- a/libgo/go/crypto/md4/md4_test.go +++ b/libgo/go/crypto/md4/md4_test.go @@ -58,10 +58,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("md4[%d](%s) = %s want %s", j, g.in, s, g.out) } diff --git a/libgo/go/crypto/md5/md5.go b/libgo/go/crypto/md5/md5.go index 20f3a1b6f75..182cfb85370 100644 --- a/libgo/go/crypto/md5/md5.go +++ b/libgo/go/crypto/md5/md5.go @@ -77,7 +77,7 @@ func (d *digest) Write(p []byte) (nn int, err error) { return } -func (d0 *digest) Sum() []byte { +func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. d := new(digest) *d = *d0 @@ -103,14 +103,11 @@ func (d0 *digest) Sum() []byte { panic("d.nx != 0") } - p := make([]byte, 16) - j := 0 for _, s := range d.s { - p[j+0] = byte(s >> 0) - p[j+1] = byte(s >> 8) - p[j+2] = byte(s >> 16) - p[j+3] = byte(s >> 24) - j += 4 + in = append(in, byte(s>>0)) + in = append(in, byte(s>>8)) + in = append(in, byte(s>>16)) + in = append(in, byte(s>>24)) } - return p + return in } diff --git a/libgo/go/crypto/md5/md5_test.go b/libgo/go/crypto/md5/md5_test.go index 857002b7013..b15e4668c32 100644 --- a/libgo/go/crypto/md5/md5_test.go +++ b/libgo/go/crypto/md5/md5_test.go @@ -58,10 +58,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("md5[%d](%s) = %s want %s", j, g.in, s, g.out) } diff --git a/libgo/go/crypto/ocsp/ocsp.go b/libgo/go/crypto/ocsp/ocsp.go index a04b5bd7135..b9dfdf94e31 100644 --- a/libgo/go/crypto/ocsp/ocsp.go +++ b/libgo/go/crypto/ocsp/ocsp.go @@ -61,7 +61,7 @@ type responseData struct { Version int `asn1:"optional,default:1,explicit,tag:0"` RequestorName pkix.RDNSequence `asn1:"optional,explicit,tag:1"` KeyHash []byte `asn1:"optional,explicit,tag:2"` - ProducedAt *time.Time + ProducedAt time.Time Responses []singleResponse } @@ -70,12 +70,12 @@ type singleResponse struct { Good asn1.Flag `asn1:"explicit,tag:0,optional"` Revoked revokedInfo `asn1:"explicit,tag:1,optional"` Unknown asn1.Flag `asn1:"explicit,tag:2,optional"` - ThisUpdate *time.Time - NextUpdate *time.Time `asn1:"explicit,tag:0,optional"` + ThisUpdate time.Time + NextUpdate time.Time `asn1:"explicit,tag:0,optional"` } type revokedInfo struct { - RevocationTime *time.Time + RevocationTime time.Time Reason int `asn1:"explicit,tag:0,optional"` } @@ -97,7 +97,7 @@ type Response struct { // Status is one of {Good, Revoked, Unknown, ServerFailed} Status int SerialNumber []byte - ProducedAt, ThisUpdate, NextUpdate, RevokedAt *time.Time + ProducedAt, ThisUpdate, NextUpdate, RevokedAt time.Time RevocationReason int Certificate *x509.Certificate } @@ -161,7 +161,7 @@ func ParseResponse(bytes []byte) (*Response, error) { pub := ret.Certificate.PublicKey.(*rsa.PublicKey) h.Write(basicResp.TBSResponseData.Raw) - digest := h.Sum() + digest := h.Sum(nil) signature := basicResp.Signature.RightAlign() if rsa.VerifyPKCS1v15(pub, hashType, digest, signature) != nil { diff --git a/libgo/go/crypto/ocsp/ocsp_test.go b/libgo/go/crypto/ocsp/ocsp_test.go index 7be37211c10..bacca558b48 100644 --- a/libgo/go/crypto/ocsp/ocsp_test.go +++ b/libgo/go/crypto/ocsp/ocsp_test.go @@ -15,7 +15,13 @@ func TestOCSPDecode(t *testing.T) { t.Error(err) } - expected := Response{Status: 0, SerialNumber: []byte{0x1, 0xd0, 0xfa}, RevocationReason: 0, ThisUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 15, Minute: 1, Second: 5, ZoneOffset: 0, Zone: "UTC"}, NextUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 18, Minute: 35, Second: 17, ZoneOffset: 0, Zone: "UTC"}} + expected := Response{ + Status: 0, + SerialNumber: []byte{0x1, 0xd0, 0xfa}, + RevocationReason: 0, + ThisUpdate: time.Date(2010, 7, 7, 15, 1, 5, 0, time.UTC), + NextUpdate: time.Date(2010, 7, 7, 18, 35, 17, 0, time.UTC), + } if !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) { t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate) diff --git a/libgo/go/crypto/openpgp/canonical_text.go b/libgo/go/crypto/openpgp/canonical_text.go index fe4557aafc1..98cee5e75ae 100644 --- a/libgo/go/crypto/openpgp/canonical_text.go +++ b/libgo/go/crypto/openpgp/canonical_text.go @@ -41,8 +41,8 @@ func (cth *canonicalTextHash) Write(buf []byte) (int, error) { return len(buf), nil } -func (cth *canonicalTextHash) Sum() []byte { - return cth.h.Sum() +func (cth *canonicalTextHash) Sum(in []byte) []byte { + return cth.h.Sum(in) } func (cth *canonicalTextHash) Reset() { diff --git a/libgo/go/crypto/openpgp/canonical_text_test.go b/libgo/go/crypto/openpgp/canonical_text_test.go index ae54f8c83ee..841475f80c0 100644 --- a/libgo/go/crypto/openpgp/canonical_text_test.go +++ b/libgo/go/crypto/openpgp/canonical_text_test.go @@ -17,8 +17,8 @@ func (r recordingHash) Write(b []byte) (n int, err error) { return r.buf.Write(b) } -func (r recordingHash) Sum() []byte { - return r.buf.Bytes() +func (r recordingHash) Sum(in []byte) []byte { + return append(in, r.buf.Bytes()...) } func (r recordingHash) Reset() { @@ -33,7 +33,7 @@ func testCanonicalText(t *testing.T, input, expected string) { r := recordingHash{bytes.NewBuffer(nil)} c := NewCanonicalTextHash(r) c.Write([]byte(input)) - result := c.Sum() + result := c.Sum(nil) if expected != string(result) { t.Errorf("input: %x got: %x want: %x", input, result, expected) } diff --git a/libgo/go/crypto/openpgp/keys.go b/libgo/go/crypto/openpgp/keys.go index b705d226e1f..df39970c0b6 100644 --- a/libgo/go/crypto/openpgp/keys.go +++ b/libgo/go/crypto/openpgp/keys.go @@ -381,7 +381,7 @@ const defaultRSAKeyBits = 2048 // NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a // single identity composed of the given full name, comment and email, any of // which may be empty but must not contain any of "()<>\x00". -func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email string) (*Entity, error) { +func NewEntity(rand io.Reader, currentTime time.Time, name, comment, email string) (*Entity, error) { uid := packet.NewUserId(name, comment, email) if uid == nil { return nil, error_.InvalidArgumentError("user id field contained invalid characters") @@ -395,11 +395,9 @@ func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email strin return nil, err } - t := uint32(currentTimeSecs) - e := &Entity{ - PrimaryKey: packet.NewRSAPublicKey(t, &signingPriv.PublicKey, false /* not a subkey */ ), - PrivateKey: packet.NewRSAPrivateKey(t, signingPriv, false /* not a subkey */ ), + PrimaryKey: packet.NewRSAPublicKey(currentTime, &signingPriv.PublicKey, false /* not a subkey */ ), + PrivateKey: packet.NewRSAPrivateKey(currentTime, signingPriv, false /* not a subkey */ ), Identities: make(map[string]*Identity), } isPrimaryId := true @@ -407,7 +405,7 @@ func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email strin Name: uid.Name, UserId: uid, SelfSignature: &packet.Signature{ - CreationTime: t, + CreationTime: currentTime, SigType: packet.SigTypePositiveCert, PubKeyAlgo: packet.PubKeyAlgoRSA, Hash: crypto.SHA256, @@ -421,10 +419,10 @@ func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email strin e.Subkeys = make([]Subkey, 1) e.Subkeys[0] = Subkey{ - PublicKey: packet.NewRSAPublicKey(t, &encryptingPriv.PublicKey, true /* is a subkey */ ), - PrivateKey: packet.NewRSAPrivateKey(t, encryptingPriv, true /* is a subkey */ ), + PublicKey: packet.NewRSAPublicKey(currentTime, &encryptingPriv.PublicKey, true /* is a subkey */ ), + PrivateKey: packet.NewRSAPrivateKey(currentTime, encryptingPriv, true /* is a subkey */ ), Sig: &packet.Signature{ - CreationTime: t, + CreationTime: currentTime, SigType: packet.SigTypeSubkeyBinding, PubKeyAlgo: packet.PubKeyAlgoRSA, Hash: crypto.SHA256, @@ -533,7 +531,7 @@ func (e *Entity) SignIdentity(identity string, signer *Entity) error { SigType: packet.SigTypeGenericCert, PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, Hash: crypto.SHA256, - CreationTime: uint32(time.Seconds()), + CreationTime: time.Now(), IssuerKeyId: &signer.PrivateKey.KeyId, } if err := sig.SignKey(e.PrimaryKey, signer.PrivateKey); err != nil { diff --git a/libgo/go/crypto/openpgp/packet/private_key.go b/libgo/go/crypto/openpgp/packet/private_key.go index c0ff82b4135..d67e9688617 100644 --- a/libgo/go/crypto/openpgp/packet/private_key.go +++ b/libgo/go/crypto/openpgp/packet/private_key.go @@ -17,6 +17,7 @@ import ( "io/ioutil" "math/big" "strconv" + "time" ) // PrivateKey represents a possibly encrypted private key. See RFC 4880, @@ -32,9 +33,9 @@ type PrivateKey struct { iv []byte } -func NewRSAPrivateKey(currentTimeSecs uint32, priv *rsa.PrivateKey, isSubkey bool) *PrivateKey { +func NewRSAPrivateKey(currentTime time.Time, priv *rsa.PrivateKey, isSubkey bool) *PrivateKey { pk := new(PrivateKey) - pk.PublicKey = *NewRSAPublicKey(currentTimeSecs, &priv.PublicKey, isSubkey) + pk.PublicKey = *NewRSAPublicKey(currentTime, &priv.PublicKey, isSubkey) pk.PrivateKey = priv return pk } @@ -99,13 +100,9 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) { } func mod64kHash(d []byte) uint16 { - h := uint16(0) - for i := 0; i < len(d); i += 2 { - v := uint16(d[i]) << 8 - if i+1 < len(d) { - v += uint16(d[i+1]) - } - h += v + var h uint16 + for _, b := range d { + h += uint16(b) } return h } @@ -195,7 +192,7 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) error { } h := sha1.New() h.Write(data[:len(data)-sha1.Size]) - sum := h.Sum() + sum := h.Sum(nil) if !bytes.Equal(sum, data[len(data)-sha1.Size:]) { return error_.StructuralError("private key checksum failure") } diff --git a/libgo/go/crypto/openpgp/packet/private_key_test.go b/libgo/go/crypto/openpgp/packet/private_key_test.go index 60eebaa6b09..35d8951a86b 100644 --- a/libgo/go/crypto/openpgp/packet/private_key_test.go +++ b/libgo/go/crypto/openpgp/packet/private_key_test.go @@ -6,19 +6,20 @@ package packet import ( "testing" + "time" ) var privateKeyTests = []struct { privateKeyHex string - creationTime uint32 + creationTime time.Time }{ { privKeyRSAHex, - 0x4cc349a8, + time.Unix(0x4cc349a8, 0), }, { privKeyElGamalHex, - 0x4df9ee1a, + time.Unix(0x4df9ee1a, 0), }, } @@ -43,7 +44,7 @@ func TestPrivateKeyRead(t *testing.T) { continue } - if privKey.CreationTime != test.creationTime || privKey.Encrypted { + if !privKey.CreationTime.Equal(test.creationTime) || privKey.Encrypted { t.Errorf("#%d: bad result, got: %#v", i, privKey) } } diff --git a/libgo/go/crypto/openpgp/packet/public_key.go b/libgo/go/crypto/openpgp/packet/public_key.go index 7d71dc49a7b..9aa30e0c15f 100644 --- a/libgo/go/crypto/openpgp/packet/public_key.go +++ b/libgo/go/crypto/openpgp/packet/public_key.go @@ -16,11 +16,12 @@ import ( "io" "math/big" "strconv" + "time" ) // PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2. type PublicKey struct { - CreationTime uint32 // seconds since the epoch + CreationTime time.Time PubKeyAlgo PublicKeyAlgorithm PublicKey interface{} // Either a *rsa.PublicKey or *dsa.PublicKey Fingerprint [20]byte @@ -38,9 +39,9 @@ func fromBig(n *big.Int) parsedMPI { } // NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey. -func NewRSAPublicKey(creationTimeSecs uint32, pub *rsa.PublicKey, isSubkey bool) *PublicKey { +func NewRSAPublicKey(creationTime time.Time, pub *rsa.PublicKey, isSubkey bool) *PublicKey { pk := &PublicKey{ - CreationTime: creationTimeSecs, + CreationTime: creationTime, PubKeyAlgo: PubKeyAlgoRSA, PublicKey: pub, IsSubkey: isSubkey, @@ -62,7 +63,7 @@ func (pk *PublicKey) parse(r io.Reader) (err error) { if buf[0] != 4 { return error_.UnsupportedError("public key version") } - pk.CreationTime = uint32(buf[1])<<24 | uint32(buf[2])<<16 | uint32(buf[3])<<8 | uint32(buf[4]) + pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0) pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5]) switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: @@ -87,7 +88,7 @@ func (pk *PublicKey) setFingerPrintAndKeyId() { fingerPrint := sha1.New() pk.SerializeSignaturePrefix(fingerPrint) pk.serializeWithoutHeaders(fingerPrint) - copy(pk.Fingerprint[:], fingerPrint.Sum()) + copy(pk.Fingerprint[:], fingerPrint.Sum(nil)) pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20]) } @@ -234,10 +235,11 @@ func (pk *PublicKey) Serialize(w io.Writer) (err error) { func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) { var buf [6]byte buf[0] = 4 - buf[1] = byte(pk.CreationTime >> 24) - buf[2] = byte(pk.CreationTime >> 16) - buf[3] = byte(pk.CreationTime >> 8) - buf[4] = byte(pk.CreationTime) + t := uint32(pk.CreationTime.Unix()) + buf[1] = byte(t >> 24) + buf[2] = byte(t >> 16) + buf[3] = byte(t >> 8) + buf[4] = byte(t) buf[5] = byte(pk.PubKeyAlgo) _, err = w.Write(buf[:]) @@ -269,7 +271,7 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro } signed.Write(sig.HashSuffix) - hashBytes := signed.Sum() + hashBytes := signed.Sum(nil) if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { return error_.SignatureError("hash tag doesn't match") diff --git a/libgo/go/crypto/openpgp/packet/public_key_test.go b/libgo/go/crypto/openpgp/packet/public_key_test.go index 6e8bfbce66e..72f459f47bf 100644 --- a/libgo/go/crypto/openpgp/packet/public_key_test.go +++ b/libgo/go/crypto/openpgp/packet/public_key_test.go @@ -8,19 +8,20 @@ import ( "bytes" "encoding/hex" "testing" + "time" ) var pubKeyTests = []struct { hexData string hexFingerprint string - creationTime uint32 + creationTime time.Time pubKeyAlgo PublicKeyAlgorithm keyId uint64 keyIdString string keyIdShort string }{ - {rsaPkDataHex, rsaFingerprintHex, 0x4d3c5c10, PubKeyAlgoRSA, 0xa34d7e18c20c31bb, "A34D7E18C20C31BB", "C20C31BB"}, - {dsaPkDataHex, dsaFingerprintHex, 0x4d432f89, PubKeyAlgoDSA, 0x8e8fbe54062f19ed, "8E8FBE54062F19ED", "062F19ED"}, + {rsaPkDataHex, rsaFingerprintHex, time.Unix(0x4d3c5c10, 0), PubKeyAlgoRSA, 0xa34d7e18c20c31bb, "A34D7E18C20C31BB", "C20C31BB"}, + {dsaPkDataHex, dsaFingerprintHex, time.Unix(0x4d432f89, 0), PubKeyAlgoDSA, 0x8e8fbe54062f19ed, "8E8FBE54062F19ED", "062F19ED"}, } func TestPublicKeyRead(t *testing.T) { @@ -38,8 +39,8 @@ func TestPublicKeyRead(t *testing.T) { if pk.PubKeyAlgo != test.pubKeyAlgo { t.Errorf("#%d: bad public key algorithm got:%x want:%x", i, pk.PubKeyAlgo, test.pubKeyAlgo) } - if pk.CreationTime != test.creationTime { - t.Errorf("#%d: bad creation time got:%x want:%x", i, pk.CreationTime, test.creationTime) + if !pk.CreationTime.Equal(test.creationTime) { + t.Errorf("#%d: bad creation time got:%v want:%v", i, pk.CreationTime, test.creationTime) } expectedFingerprint, _ := hex.DecodeString(test.hexFingerprint) if !bytes.Equal(expectedFingerprint, pk.Fingerprint[:]) { diff --git a/libgo/go/crypto/openpgp/packet/signature.go b/libgo/go/crypto/openpgp/packet/signature.go index 4ebb906cad7..1cdc1ee0f0c 100644 --- a/libgo/go/crypto/openpgp/packet/signature.go +++ b/libgo/go/crypto/openpgp/packet/signature.go @@ -15,6 +15,7 @@ import ( "hash" "io" "strconv" + "time" ) // Signature represents a signature. See RFC 4880, section 5.2. @@ -28,7 +29,7 @@ type Signature struct { // HashTag contains the first two bytes of the hash for fast rejection // of bad signed data. HashTag [2]byte - CreationTime uint32 // Unix epoch time + CreationTime time.Time RSASignature parsedMPI DSASigR, DSASigS parsedMPI @@ -151,7 +152,7 @@ func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) } } - if sig.CreationTime == 0 { + if sig.CreationTime.IsZero() { err = error_.StructuralError("no creation time in signature") } @@ -223,7 +224,12 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r err = error_.StructuralError("signature creation time not four bytes") return } - sig.CreationTime = binary.BigEndian.Uint32(subpacket) + t := binary.BigEndian.Uint32(subpacket) + if t == 0 { + sig.CreationTime = time.Time{} + } else { + sig.CreationTime = time.Unix(int64(t), 0) + } case signatureExpirationSubpacket: // Signature expiration time, section 5.2.3.10 if !isHashed { @@ -417,7 +423,7 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) { } h.Write(sig.HashSuffix) - digest = h.Sum() + digest = h.Sum(nil) copy(sig.HashTag[:], digest) return } @@ -541,10 +547,7 @@ type outputSubpacket struct { func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) { creationTime := make([]byte, 4) - creationTime[0] = byte(sig.CreationTime >> 24) - creationTime[1] = byte(sig.CreationTime >> 16) - creationTime[2] = byte(sig.CreationTime >> 8) - creationTime[3] = byte(sig.CreationTime) + binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix())) subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime}) if sig.IssuerKeyId != nil { diff --git a/libgo/go/crypto/openpgp/packet/symmetrically_encrypted.go b/libgo/go/crypto/openpgp/packet/symmetrically_encrypted.go index 8225db6d2f6..dff776e3eb2 100644 --- a/libgo/go/crypto/openpgp/packet/symmetrically_encrypted.go +++ b/libgo/go/crypto/openpgp/packet/symmetrically_encrypted.go @@ -201,7 +201,7 @@ func (ser *seMDCReader) Close() error { } ser.h.Write(ser.trailer[:2]) - final := ser.h.Sum() + final := ser.h.Sum(nil) if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 { return error_.SignatureError("hash mismatch") } @@ -227,7 +227,7 @@ func (w *seMDCWriter) Close() (err error) { buf[0] = mdcPacketTagByte buf[1] = sha1.Size w.h.Write(buf[:2]) - digest := w.h.Sum() + digest := w.h.Sum(nil) copy(buf[2:], digest) _, err = w.w.Write(buf[:]) diff --git a/libgo/go/crypto/openpgp/s2k/s2k.go b/libgo/go/crypto/openpgp/s2k/s2k.go index 2a753db16bd..83673e17335 100644 --- a/libgo/go/crypto/openpgp/s2k/s2k.go +++ b/libgo/go/crypto/openpgp/s2k/s2k.go @@ -34,7 +34,7 @@ func Salted(out []byte, h hash.Hash, in []byte, salt []byte) { } h.Write(salt) h.Write(in) - n := copy(out[done:], h.Sum()) + n := copy(out[done:], h.Sum(nil)) done += n } } @@ -68,7 +68,7 @@ func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) { written += len(combined) } } - n := copy(out[done:], h.Sum()) + n := copy(out[done:], h.Sum(nil)) done += n } } diff --git a/libgo/go/crypto/openpgp/write.go b/libgo/go/crypto/openpgp/write.go index 6f3450c9cdb..60dae01e64b 100644 --- a/libgo/go/crypto/openpgp/write.go +++ b/libgo/go/crypto/openpgp/write.go @@ -68,7 +68,7 @@ func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.S sig.SigType = sigType sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo sig.Hash = crypto.SHA256 - sig.CreationTime = uint32(time.Seconds()) + sig.CreationTime = time.Now() sig.IssuerKeyId = &signer.PrivateKey.KeyId h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) @@ -95,8 +95,8 @@ type FileHints struct { // file should not be written to disk. It may be equal to "_CONSOLE" to // suggest the data should not be written to disk. FileName string - // EpochSeconds contains the modification time of the file, or 0 if not applicable. - EpochSeconds uint32 + // ModTime contains the modification time of the file, or the zero time if not applicable. + ModTime time.Time } // SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. @@ -115,7 +115,11 @@ func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHi if err != nil { return } - return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds) + var epochSeconds uint32 + if !hints.ModTime.IsZero() { + epochSeconds = uint32(hints.ModTime.Unix()) + } + return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds) } // intersectPreferences mutates and returns a prefix of a that contains only @@ -243,7 +247,11 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint w = noOpCloser{encryptedData} } - literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds) + var epochSeconds uint32 + if !hints.ModTime.IsZero() { + epochSeconds = uint32(hints.ModTime.Unix()) + } + literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds) if err != nil { return nil, err } @@ -275,7 +283,7 @@ func (s signatureWriter) Close() error { SigType: packet.SigTypeBinary, PubKeyAlgo: s.signer.PubKeyAlgo, Hash: s.hashType, - CreationTime: uint32(time.Seconds()), + CreationTime: time.Now(), IssuerKeyId: &s.signer.KeyId, } diff --git a/libgo/go/crypto/openpgp/write_test.go b/libgo/go/crypto/openpgp/write_test.go index 3cadf4cc95a..02fa5b75bff 100644 --- a/libgo/go/crypto/openpgp/write_test.go +++ b/libgo/go/crypto/openpgp/write_test.go @@ -54,7 +54,7 @@ func TestNewEntity(t *testing.T) { return } - e, err := NewEntity(rand.Reader, time.Seconds(), "Test User", "test", "test@example.com") + e, err := NewEntity(rand.Reader, time.Now(), "Test User", "test", "test@example.com") if err != nil { t.Errorf("failed to create entity: %s", err) return diff --git a/libgo/go/crypto/rand/rand_unix.go b/libgo/go/crypto/rand/rand_unix.go index 09442ad2830..d9cddf6d2ad 100644 --- a/libgo/go/crypto/rand/rand_unix.go +++ b/libgo/go/crypto/rand/rand_unix.go @@ -100,7 +100,7 @@ func (r *reader) Read(b []byte) (n int, err error) { // t = encrypt(time) // dst = encrypt(t^seed) // seed = encrypt(t^dst) - ns := time.Nanoseconds() + ns := time.Now().UnixNano() r.time[0] = byte(ns >> 56) r.time[1] = byte(ns >> 48) r.time[2] = byte(ns >> 40) diff --git a/libgo/go/crypto/rand/rand_windows.go b/libgo/go/crypto/rand/rand_windows.go index 590571d23f6..2b2bd4bba6b 100644 --- a/libgo/go/crypto/rand/rand_windows.go +++ b/libgo/go/crypto/rand/rand_windows.go @@ -28,16 +28,16 @@ func (r *rngReader) Read(b []byte) (n int, err error) { if r.prov == 0 { const provType = syscall.PROV_RSA_FULL const flags = syscall.CRYPT_VERIFYCONTEXT | syscall.CRYPT_SILENT - errno := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags) - if errno != 0 { + err := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags) + if err != nil { r.mu.Unlock() - return 0, os.NewSyscallError("CryptAcquireContext", errno) + return 0, os.NewSyscallError("CryptAcquireContext", err) } } r.mu.Unlock() - errno := syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) - if errno != 0 { - return 0, os.NewSyscallError("CryptGenRandom", errno) + err = syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) + if err != nil { + return 0, os.NewSyscallError("CryptGenRandom", err) } return len(b), nil } diff --git a/libgo/go/crypto/rand/util.go b/libgo/go/crypto/rand/util.go index b44ae9897ba..fc5fe6c65e9 100644 --- a/libgo/go/crypto/rand/util.go +++ b/libgo/go/crypto/rand/util.go @@ -5,16 +5,16 @@ package rand import ( + "errors" "io" "math/big" - "os" ) // Prime returns a number, p, of the given size, such that p is prime // with high probability. func Prime(rand io.Reader, bits int) (p *big.Int, err error) { if bits < 1 { - err = os.EINVAL + err = errors.New("crypto/rand: prime size must be positive") } b := uint(bits % 8) diff --git a/libgo/go/crypto/ripemd160/ripemd160.go b/libgo/go/crypto/ripemd160/ripemd160.go index 6ccfe875f55..c128ee445a5 100644 --- a/libgo/go/crypto/ripemd160/ripemd160.go +++ b/libgo/go/crypto/ripemd160/ripemd160.go @@ -81,7 +81,7 @@ func (d *digest) Write(p []byte) (nn int, err error) { return } -func (d0 *digest) Sum() []byte { +func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. d := new(digest) *d = *d0 @@ -107,11 +107,11 @@ func (d0 *digest) Sum() []byte { panic("d.nx != 0") } - p := make([]byte, 20) - j := 0 for _, s := range d.s { - p[j], p[j+1], p[j+2], p[j+3] = byte(s), byte(s>>8), byte(s>>16), byte(s>>24) - j += 4 + in = append(in, byte(s)) + in = append(in, byte(s>>8)) + in = append(in, byte(s>>16)) + in = append(in, byte(s>>24)) } - return p + return in } diff --git a/libgo/go/crypto/ripemd160/ripemd160_test.go b/libgo/go/crypto/ripemd160/ripemd160_test.go index f4135f5cf65..5df1b2593d2 100644 --- a/libgo/go/crypto/ripemd160/ripemd160_test.go +++ b/libgo/go/crypto/ripemd160/ripemd160_test.go @@ -38,10 +38,10 @@ func TestVectors(t *testing.T) { io.WriteString(md, tv.in) } else { io.WriteString(md, tv.in[0:len(tv.in)/2]) - md.Sum() + md.Sum(nil) io.WriteString(md, tv.in[len(tv.in)/2:]) } - s := fmt.Sprintf("%x", md.Sum()) + s := fmt.Sprintf("%x", md.Sum(nil)) if s != tv.out { t.Fatalf("RIPEMD-160[%d](%s) = %s, expected %s", j, tv.in, s, tv.out) } @@ -56,7 +56,7 @@ func TestMillionA(t *testing.T) { io.WriteString(md, "aaaaaaaaaa") } out := "52783243c1697bdbe16d37f97f68f08325dc1528" - s := fmt.Sprintf("%x", md.Sum()) + s := fmt.Sprintf("%x", md.Sum(nil)) if s != out { t.Fatalf("RIPEMD-160 (1 million 'a') = %s, expected %s", s, out) } diff --git a/libgo/go/crypto/rsa/pkcs1v15_test.go b/libgo/go/crypto/rsa/pkcs1v15_test.go index 66188ac10ed..58d5fda1976 100644 --- a/libgo/go/crypto/rsa/pkcs1v15_test.go +++ b/libgo/go/crypto/rsa/pkcs1v15_test.go @@ -168,7 +168,7 @@ func TestSignPKCS1v15(t *testing.T) { for i, test := range signPKCS1v15Tests { h := sha1.New() h.Write([]byte(test.in)) - digest := h.Sum() + digest := h.Sum(nil) s, err := SignPKCS1v15(nil, rsaPrivateKey, crypto.SHA1, digest) if err != nil { @@ -186,7 +186,7 @@ func TestVerifyPKCS1v15(t *testing.T) { for i, test := range signPKCS1v15Tests { h := sha1.New() h.Write([]byte(test.in)) - digest := h.Sum() + digest := h.Sum(nil) sig, _ := hex.DecodeString(test.out) diff --git a/libgo/go/crypto/rsa/rsa.go b/libgo/go/crypto/rsa/rsa.go index 27ccf61c4fc..f74525c103a 100644 --- a/libgo/go/crypto/rsa/rsa.go +++ b/libgo/go/crypto/rsa/rsa.go @@ -194,7 +194,7 @@ func mgf1XOR(out []byte, hash hash.Hash, seed []byte) { for done < len(out) { hash.Write(seed) hash.Write(counter[0:4]) - digest := hash.Sum() + digest := hash.Sum(nil) hash.Reset() for i := 0; i < len(digest) && done < len(out); i++ { @@ -231,7 +231,7 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l } hash.Write(label) - lHash := hash.Sum() + lHash := hash.Sum(nil) hash.Reset() em := make([]byte, k) @@ -428,7 +428,7 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext } hash.Write(label) - lHash := hash.Sum() + lHash := hash.Sum(nil) hash.Reset() // Converting the plaintext number to bytes will strip any diff --git a/libgo/go/crypto/sha1/sha1.go b/libgo/go/crypto/sha1/sha1.go index 4cdf5b2e989..f41cdb5b027 100644 --- a/libgo/go/crypto/sha1/sha1.go +++ b/libgo/go/crypto/sha1/sha1.go @@ -79,7 +79,7 @@ func (d *digest) Write(p []byte) (nn int, err error) { return } -func (d0 *digest) Sum() []byte { +func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. d := new(digest) *d = *d0 @@ -105,14 +105,11 @@ func (d0 *digest) Sum() []byte { panic("d.nx != 0") } - p := make([]byte, 20) - j := 0 for _, s := range d.h { - p[j+0] = byte(s >> 24) - p[j+1] = byte(s >> 16) - p[j+2] = byte(s >> 8) - p[j+3] = byte(s >> 0) - j += 4 + in = append(in, byte(s>>24)) + in = append(in, byte(s>>16)) + in = append(in, byte(s>>8)) + in = append(in, byte(s)) } - return p + return in } diff --git a/libgo/go/crypto/sha1/sha1_test.go b/libgo/go/crypto/sha1/sha1_test.go index 2712fe35eaf..c23df6c41e9 100644 --- a/libgo/go/crypto/sha1/sha1_test.go +++ b/libgo/go/crypto/sha1/sha1_test.go @@ -60,10 +60,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha1[%d](%s) = %s want %s", j, g.in, s, g.out) } diff --git a/libgo/go/crypto/sha256/sha256.go b/libgo/go/crypto/sha256/sha256.go index 14b8cfc7eca..34861f6cf49 100644 --- a/libgo/go/crypto/sha256/sha256.go +++ b/libgo/go/crypto/sha256/sha256.go @@ -123,7 +123,7 @@ func (d *digest) Write(p []byte) (nn int, err error) { return } -func (d0 *digest) Sum() []byte { +func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. d := new(digest) *d = *d0 @@ -149,17 +149,15 @@ func (d0 *digest) Sum() []byte { panic("d.nx != 0") } - p := make([]byte, 32) - j := 0 - for _, s := range d.h { - p[j+0] = byte(s >> 24) - p[j+1] = byte(s >> 16) - p[j+2] = byte(s >> 8) - p[j+3] = byte(s >> 0) - j += 4 - } + h := d.h[:] if d.is224 { - return p[0:28] + h = d.h[:7] + } + for _, s := range h { + in = append(in, byte(s>>24)) + in = append(in, byte(s>>16)) + in = append(in, byte(s>>8)) + in = append(in, byte(s)) } - return p + return in } diff --git a/libgo/go/crypto/sha256/sha256_test.go b/libgo/go/crypto/sha256/sha256_test.go index 42a3fa7a010..a6efb375456 100644 --- a/libgo/go/crypto/sha256/sha256_test.go +++ b/libgo/go/crypto/sha256/sha256_test.go @@ -94,10 +94,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha256[%d](%s) = %s want %s", j, g.in, s, g.out) } @@ -112,10 +112,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha224[%d](%s) = %s want %s", j, g.in, s, g.out) } diff --git a/libgo/go/crypto/sha512/sha512.go b/libgo/go/crypto/sha512/sha512.go index 1bd27982bb7..3cf65cbe7c8 100644 --- a/libgo/go/crypto/sha512/sha512.go +++ b/libgo/go/crypto/sha512/sha512.go @@ -123,7 +123,7 @@ func (d *digest) Write(p []byte) (nn int, err error) { return } -func (d0 *digest) Sum() []byte { +func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. d := new(digest) *d = *d0 @@ -149,21 +149,19 @@ func (d0 *digest) Sum() []byte { panic("d.nx != 0") } - p := make([]byte, 64) - j := 0 - for _, s := range d.h { - p[j+0] = byte(s >> 56) - p[j+1] = byte(s >> 48) - p[j+2] = byte(s >> 40) - p[j+3] = byte(s >> 32) - p[j+4] = byte(s >> 24) - p[j+5] = byte(s >> 16) - p[j+6] = byte(s >> 8) - p[j+7] = byte(s >> 0) - j += 8 - } + h := d.h[:] if d.is384 { - return p[0:48] + h = d.h[:6] + } + for _, s := range h { + in = append(in, byte(s>>56)) + in = append(in, byte(s>>48)) + in = append(in, byte(s>>40)) + in = append(in, byte(s>>32)) + in = append(in, byte(s>>24)) + in = append(in, byte(s>>16)) + in = append(in, byte(s>>8)) + in = append(in, byte(s)) } - return p + return in } diff --git a/libgo/go/crypto/sha512/sha512_test.go b/libgo/go/crypto/sha512/sha512_test.go index dd116dc17b3..a70f7c54e39 100644 --- a/libgo/go/crypto/sha512/sha512_test.go +++ b/libgo/go/crypto/sha512/sha512_test.go @@ -94,10 +94,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha512[%d](%s) = %s want %s", j, g.in, s, g.out) } @@ -112,10 +112,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha384[%d](%s) = %s want %s", j, g.in, s, g.out) } diff --git a/libgo/go/crypto/tls/cipher_suites.go b/libgo/go/crypto/tls/cipher_suites.go index 1134f362583..c0e8656f79b 100644 --- a/libgo/go/crypto/tls/cipher_suites.go +++ b/libgo/go/crypto/tls/cipher_suites.go @@ -37,6 +37,7 @@ type keyAgreement interface { // A cipherSuite is a specific combination of key agreement, cipher and MAC // function. All cipher suites currently assume RSA key agreement. type cipherSuite struct { + id uint16 // the lengths, in bytes, of the key material needed for each component. keyLen int macLen int @@ -50,13 +51,13 @@ type cipherSuite struct { mac func(version uint16, macKey []byte) macFunction } -var cipherSuites = map[uint16]*cipherSuite{ - TLS_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, rsaKA, false, cipherRC4, macSHA1}, - TLS_RSA_WITH_3DES_EDE_CBC_SHA: &cipherSuite{24, 20, 8, rsaKA, false, cipher3DES, macSHA1}, - TLS_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, rsaKA, false, cipherAES, macSHA1}, - TLS_ECDHE_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1}, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: &cipherSuite{24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1}, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1}, +var cipherSuites = []*cipherSuite{ + &cipherSuite{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, false, cipherRC4, macSHA1}, + &cipherSuite{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, false, cipher3DES, macSHA1}, + &cipherSuite{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, false, cipherAES, macSHA1}, + &cipherSuite{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1}, + &cipherSuite{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1}, + &cipherSuite{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1}, } func cipherRC4(key, iv []byte, isRead bool) interface{} { @@ -126,13 +127,13 @@ func (s ssl30MAC) MAC(seq, record []byte) []byte { s.h.Write(record[:1]) s.h.Write(record[3:5]) s.h.Write(record[recordHeaderLen:]) - digest := s.h.Sum() + digest := s.h.Sum(nil) s.h.Reset() s.h.Write(s.key) s.h.Write(ssl30Pad2[:padLength]) s.h.Write(digest) - return s.h.Sum() + return s.h.Sum(nil) } // tls10MAC implements the TLS 1.0 MAC function. RFC 2246, section 6.2.3. @@ -148,7 +149,7 @@ func (s tls10MAC) MAC(seq, record []byte) []byte { s.h.Reset() s.h.Write(seq) s.h.Write(record) - return s.h.Sum() + return s.h.Sum(nil) } func rsaKA() keyAgreement { @@ -159,15 +160,20 @@ func ecdheRSAKA() keyAgreement { return new(ecdheRSAKeyAgreement) } -// mutualCipherSuite returns a cipherSuite and its id given a list of supported +// mutualCipherSuite returns a cipherSuite given a list of supported // ciphersuites and the id requested by the peer. -func mutualCipherSuite(have []uint16, want uint16) (suite *cipherSuite, id uint16) { +func mutualCipherSuite(have []uint16, want uint16) *cipherSuite { for _, id := range have { if id == want { - return cipherSuites[id], id + for _, suite := range cipherSuites { + if suite.id == want { + return suite + } + } + return nil } } - return + return nil } // A list of the possible cipher suite ids. Taken from diff --git a/libgo/go/crypto/tls/common.go b/libgo/go/crypto/tls/common.go index ea520859b82..f57d932a98f 100644 --- a/libgo/go/crypto/tls/common.go +++ b/libgo/go/crypto/tls/common.go @@ -121,7 +121,7 @@ type Config struct { // Time returns the current time as the number of seconds since the epoch. // If Time is nil, TLS uses the system time.Seconds. - Time func() int64 + Time func() time.Time // Certificates contains one or more certificate chains // to present to the other side of the connection. @@ -175,10 +175,10 @@ func (c *Config) rand() io.Reader { return r } -func (c *Config) time() int64 { +func (c *Config) time() time.Time { t := c.Time if t == nil { - t = time.Seconds + t = time.Now } return t() } @@ -315,9 +315,7 @@ var ( func initDefaultCipherSuites() { varDefaultCipherSuites = make([]uint16, len(cipherSuites)) - i := 0 - for id := range cipherSuites { - varDefaultCipherSuites[i] = id - i++ + for i, suite := range cipherSuites { + varDefaultCipherSuites[i] = suite.id } } diff --git a/libgo/go/crypto/tls/conn.go b/libgo/go/crypto/tls/conn.go index f4178e30c58..b8fa2737f67 100644 --- a/libgo/go/crypto/tls/conn.go +++ b/libgo/go/crypto/tls/conn.go @@ -93,7 +93,8 @@ func (c *Conn) SetTimeout(nsec int64) error { } // SetReadTimeout sets the time (in nanoseconds) that -// Read will wait for data before returning os.EAGAIN. +// Read will wait for data before returning a net.Error +// with Timeout() == true. // Setting nsec == 0 (the default) disables the deadline. func (c *Conn) SetReadTimeout(nsec int64) error { return c.conn.SetReadTimeout(nsec) @@ -737,7 +738,7 @@ func (c *Conn) Write(b []byte) (n int, err error) { return c.writeRecord(recordTypeApplicationData, b) } -// Read can be made to time out and return err == os.EAGAIN +// Read can be made to time out and return a net.Error with Timeout() == true // after a fixed time limit; see SetTimeout and SetReadTimeout. func (c *Conn) Read(b []byte) (n int, err error) { if err = c.Handshake(); err != nil { diff --git a/libgo/go/crypto/tls/handshake_client.go b/libgo/go/crypto/tls/handshake_client.go index aed991ccd1b..b4337f2aac6 100644 --- a/libgo/go/crypto/tls/handshake_client.go +++ b/libgo/go/crypto/tls/handshake_client.go @@ -32,7 +32,7 @@ func (c *Conn) clientHandshake() error { nextProtoNeg: len(c.config.NextProtos) > 0, } - t := uint32(c.config.time()) + t := uint32(c.config.time().Unix()) hello.random[0] = byte(t >> 24) hello.random[1] = byte(t >> 16) hello.random[2] = byte(t >> 8) @@ -72,7 +72,7 @@ func (c *Conn) clientHandshake() error { return errors.New("server advertised unrequested NPN") } - suite, suiteId := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite) + suite := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite) if suite == nil { return c.sendAlert(alertHandshakeFailure) } @@ -232,8 +232,8 @@ func (c *Conn) clientHandshake() error { if cert != nil { certVerify := new(certificateVerifyMsg) var digest [36]byte - copy(digest[0:16], finishedHash.serverMD5.Sum()) - copy(digest[16:36], finishedHash.serverSHA1.Sum()) + copy(digest[0:16], finishedHash.serverMD5.Sum(nil)) + copy(digest[16:36], finishedHash.serverSHA1.Sum(nil)) signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey, crypto.MD5SHA1, digest[0:]) if err != nil { return c.sendAlert(alertInternalError) @@ -292,7 +292,7 @@ func (c *Conn) clientHandshake() error { } c.handshakeComplete = true - c.cipherSuite = suiteId + c.cipherSuite = suite.id return nil } diff --git a/libgo/go/crypto/tls/handshake_messages.go b/libgo/go/crypto/tls/handshake_messages.go index f11232d8ee5..5438e749ce8 100644 --- a/libgo/go/crypto/tls/handshake_messages.go +++ b/libgo/go/crypto/tls/handshake_messages.go @@ -4,6 +4,8 @@ package tls +import "bytes" + type clientHelloMsg struct { raw []byte vers uint16 @@ -18,6 +20,25 @@ type clientHelloMsg struct { supportedPoints []uint8 } +func (m *clientHelloMsg) equal(i interface{}) bool { + m1, ok := i.(*clientHelloMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.vers == m1.vers && + bytes.Equal(m.random, m1.random) && + bytes.Equal(m.sessionId, m1.sessionId) && + eqUint16s(m.cipherSuites, m1.cipherSuites) && + bytes.Equal(m.compressionMethods, m1.compressionMethods) && + m.nextProtoNeg == m1.nextProtoNeg && + m.serverName == m1.serverName && + m.ocspStapling == m1.ocspStapling && + eqUint16s(m.supportedCurves, m1.supportedCurves) && + bytes.Equal(m.supportedPoints, m1.supportedPoints) +} + func (m *clientHelloMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -309,6 +330,23 @@ type serverHelloMsg struct { ocspStapling bool } +func (m *serverHelloMsg) equal(i interface{}) bool { + m1, ok := i.(*serverHelloMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.vers == m1.vers && + bytes.Equal(m.random, m1.random) && + bytes.Equal(m.sessionId, m1.sessionId) && + m.cipherSuite == m1.cipherSuite && + m.compressionMethod == m1.compressionMethod && + m.nextProtoNeg == m1.nextProtoNeg && + eqStrings(m.nextProtos, m1.nextProtos) && + m.ocspStapling == m1.ocspStapling +} + func (m *serverHelloMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -463,6 +501,16 @@ type certificateMsg struct { certificates [][]byte } +func (m *certificateMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + eqByteSlices(m.certificates, m1.certificates) +} + func (m *certificateMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -540,6 +588,16 @@ type serverKeyExchangeMsg struct { key []byte } +func (m *serverKeyExchangeMsg) equal(i interface{}) bool { + m1, ok := i.(*serverKeyExchangeMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.key, m1.key) +} + func (m *serverKeyExchangeMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -571,6 +629,17 @@ type certificateStatusMsg struct { response []byte } +func (m *certificateStatusMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateStatusMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.statusType == m1.statusType && + bytes.Equal(m.response, m1.response) +} + func (m *certificateStatusMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -622,6 +691,11 @@ func (m *certificateStatusMsg) unmarshal(data []byte) bool { type serverHelloDoneMsg struct{} +func (m *serverHelloDoneMsg) equal(i interface{}) bool { + _, ok := i.(*serverHelloDoneMsg) + return ok +} + func (m *serverHelloDoneMsg) marshal() []byte { x := make([]byte, 4) x[0] = typeServerHelloDone @@ -637,6 +711,16 @@ type clientKeyExchangeMsg struct { ciphertext []byte } +func (m *clientKeyExchangeMsg) equal(i interface{}) bool { + m1, ok := i.(*clientKeyExchangeMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.ciphertext, m1.ciphertext) +} + func (m *clientKeyExchangeMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -671,6 +755,16 @@ type finishedMsg struct { verifyData []byte } +func (m *finishedMsg) equal(i interface{}) bool { + m1, ok := i.(*finishedMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.verifyData, m1.verifyData) +} + func (m *finishedMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -698,6 +792,16 @@ type nextProtoMsg struct { proto string } +func (m *nextProtoMsg) equal(i interface{}) bool { + m1, ok := i.(*nextProtoMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.proto == m1.proto +} + func (m *nextProtoMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -759,6 +863,17 @@ type certificateRequestMsg struct { certificateAuthorities [][]byte } +func (m *certificateRequestMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateRequestMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.certificateTypes, m1.certificateTypes) && + eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) +} + func (m *certificateRequestMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -859,6 +974,16 @@ type certificateVerifyMsg struct { signature []byte } +func (m *certificateVerifyMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateVerifyMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.signature, m1.signature) +} + func (m *certificateVerifyMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -902,3 +1027,39 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool { return true } + +func eqUint16s(x, y []uint16) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if y[i] != v { + return false + } + } + return true +} + +func eqStrings(x, y []string) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if y[i] != v { + return false + } + } + return true +} + +func eqByteSlices(x, y [][]byte) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if !bytes.Equal(v, y[i]) { + return false + } + } + return true +} diff --git a/libgo/go/crypto/tls/handshake_messages_test.go b/libgo/go/crypto/tls/handshake_messages_test.go index 87e8f7e428d..e62a9d581b3 100644 --- a/libgo/go/crypto/tls/handshake_messages_test.go +++ b/libgo/go/crypto/tls/handshake_messages_test.go @@ -27,10 +27,12 @@ var tests = []interface{}{ type testMessage interface { marshal() []byte unmarshal([]byte) bool + equal(interface{}) bool } func TestMarshalUnmarshal(t *testing.T) { rand := rand.New(rand.NewSource(0)) + for i, iface := range tests { ty := reflect.ValueOf(iface).Type() @@ -54,7 +56,7 @@ func TestMarshalUnmarshal(t *testing.T) { } m2.marshal() // to fill any marshal cache in the message - if !reflect.DeepEqual(m1, m2) { + if !m1.equal(m2) { t.Errorf("#%d got:%#v want:%#v %x", i, m2, m1, marshaled) break } diff --git a/libgo/go/crypto/tls/handshake_server.go b/libgo/go/crypto/tls/handshake_server.go index d5af084eda5..bbb23c0c9f6 100644 --- a/libgo/go/crypto/tls/handshake_server.go +++ b/libgo/go/crypto/tls/handshake_server.go @@ -56,18 +56,25 @@ Curves: ellipticOk := supportedCurve && supportedPointFormat var suite *cipherSuite - var suiteId uint16 FindCipherSuite: for _, id := range clientHello.cipherSuites { for _, supported := range config.cipherSuites() { if id == supported { - suite = cipherSuites[id] + suite = nil + for _, s := range cipherSuites { + if s.id == id { + suite = s + break + } + } + if suite == nil { + continue + } // Don't select a ciphersuite which we can't // support for this client. if suite.elliptic && !ellipticOk { continue } - suiteId = id break FindCipherSuite } } @@ -87,8 +94,8 @@ FindCipherSuite: } hello.vers = vers - hello.cipherSuite = suiteId - t := uint32(config.time()) + hello.cipherSuite = suite.id + t := uint32(config.time().Unix()) hello.random = make([]byte, 32) hello.random[0] = byte(t >> 24) hello.random[1] = byte(t >> 16) @@ -228,8 +235,8 @@ FindCipherSuite: } digest := make([]byte, 36) - copy(digest[0:16], finishedHash.serverMD5.Sum()) - copy(digest[16:36], finishedHash.serverSHA1.Sum()) + copy(digest[0:16], finishedHash.serverMD5.Sum(nil)) + copy(digest[16:36], finishedHash.serverSHA1.Sum(nil)) err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature) if err != nil { c.sendAlert(alertBadCertificate) @@ -296,7 +303,7 @@ FindCipherSuite: c.writeRecord(recordTypeHandshake, finished.marshal()) c.handshakeComplete = true - c.cipherSuite = suiteId + c.cipherSuite = suite.id return nil } diff --git a/libgo/go/crypto/tls/handshake_server_test.go b/libgo/go/crypto/tls/handshake_server_test.go index bc3797947f5..e00c32c5508 100644 --- a/libgo/go/crypto/tls/handshake_server_test.go +++ b/libgo/go/crypto/tls/handshake_server_test.go @@ -15,6 +15,7 @@ import ( "strconv" "strings" "testing" + "time" ) type zeroSource struct{} @@ -31,7 +32,7 @@ var testConfig *Config func init() { testConfig = new(Config) - testConfig.Time = func() int64 { return 0 } + testConfig.Time = func() time.Time { return time.Unix(0, 0) } testConfig.Rand = zeroSource{} testConfig.Certificates = make([]Certificate, 1) testConfig.Certificates[0].Certificate = [][]byte{testCertificate} diff --git a/libgo/go/crypto/tls/key_agreement.go b/libgo/go/crypto/tls/key_agreement.go index 08fb852d66a..b531717d840 100644 --- a/libgo/go/crypto/tls/key_agreement.go +++ b/libgo/go/crypto/tls/key_agreement.go @@ -90,13 +90,13 @@ func md5SHA1Hash(slices ...[]byte) []byte { for _, slice := range slices { hmd5.Write(slice) } - copy(md5sha1, hmd5.Sum()) + copy(md5sha1, hmd5.Sum(nil)) hsha1 := sha1.New() for _, slice := range slices { hsha1.Write(slice) } - copy(md5sha1[md5.Size:], hsha1.Sum()) + copy(md5sha1[md5.Size:], hsha1.Sum(nil)) return md5sha1 } diff --git a/libgo/go/crypto/tls/prf.go b/libgo/go/crypto/tls/prf.go index d758f21aa8e..637ef03e2d7 100644 --- a/libgo/go/crypto/tls/prf.go +++ b/libgo/go/crypto/tls/prf.go @@ -22,14 +22,14 @@ func splitPreMasterSecret(secret []byte) (s1, s2 []byte) { func pHash(result, secret, seed []byte, hash func() hash.Hash) { h := hmac.New(hash, secret) h.Write(seed) - a := h.Sum() + a := h.Sum(nil) j := 0 for j < len(result) { h.Reset() h.Write(a) h.Write(seed) - b := h.Sum() + b := h.Sum(nil) todo := len(b) if j+todo > len(result) { todo = len(result) - j @@ -39,7 +39,7 @@ func pHash(result, secret, seed []byte, hash func() hash.Hash) { h.Reset() h.Write(a) - a = h.Sum() + a = h.Sum(nil) } } @@ -84,13 +84,13 @@ func pRF30(result, secret, label, seed []byte) { hashSHA1.Write(b[:i+1]) hashSHA1.Write(secret) hashSHA1.Write(seed) - digest := hashSHA1.Sum() + digest := hashSHA1.Sum(nil) hashMD5.Reset() hashMD5.Write(secret) hashMD5.Write(digest) - done += copy(result[done:], hashMD5.Sum()) + done += copy(result[done:], hashMD5.Sum(nil)) i++ } } @@ -182,24 +182,24 @@ func finishedSum30(md5, sha1 hash.Hash, masterSecret []byte, magic [4]byte) []by md5.Write(magic[:]) md5.Write(masterSecret) md5.Write(ssl30Pad1[:]) - md5Digest := md5.Sum() + md5Digest := md5.Sum(nil) md5.Reset() md5.Write(masterSecret) md5.Write(ssl30Pad2[:]) md5.Write(md5Digest) - md5Digest = md5.Sum() + md5Digest = md5.Sum(nil) sha1.Write(magic[:]) sha1.Write(masterSecret) sha1.Write(ssl30Pad1[:40]) - sha1Digest := sha1.Sum() + sha1Digest := sha1.Sum(nil) sha1.Reset() sha1.Write(masterSecret) sha1.Write(ssl30Pad2[:40]) sha1.Write(sha1Digest) - sha1Digest = sha1.Sum() + sha1Digest = sha1.Sum(nil) ret := make([]byte, len(md5Digest)+len(sha1Digest)) copy(ret, md5Digest) @@ -217,8 +217,8 @@ func (h finishedHash) clientSum(masterSecret []byte) []byte { return finishedSum30(h.clientMD5, h.clientSHA1, masterSecret, ssl3ClientFinishedMagic) } - md5 := h.clientMD5.Sum() - sha1 := h.clientSHA1.Sum() + md5 := h.clientMD5.Sum(nil) + sha1 := h.clientSHA1.Sum(nil) return finishedSum10(md5, sha1, clientFinishedLabel, masterSecret) } @@ -229,7 +229,7 @@ func (h finishedHash) serverSum(masterSecret []byte) []byte { return finishedSum30(h.serverMD5, h.serverSHA1, masterSecret, ssl3ServerFinishedMagic) } - md5 := h.serverMD5.Sum() - sha1 := h.serverSHA1.Sum() + md5 := h.serverMD5.Sum(nil) + sha1 := h.serverSHA1.Sum(nil) return finishedSum10(md5, sha1, serverFinishedLabel, masterSecret) } diff --git a/libgo/go/crypto/tls/root_unix.go b/libgo/go/crypto/tls/root_unix.go index 095beec104a..1b9aeb03b5b 100644 --- a/libgo/go/crypto/tls/root_unix.go +++ b/libgo/go/crypto/tls/root_unix.go @@ -14,6 +14,7 @@ var certFiles = []string{ "/etc/ssl/certs/ca-certificates.crt", // Linux etc "/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL "/etc/ssl/ca-bundle.pem", // OpenSUSE + "/etc/ssl/cert.pem", // OpenBSD } func initDefaultRoots() { diff --git a/libgo/go/crypto/tls/root_windows.go b/libgo/go/crypto/tls/root_windows.go index b8e27a9a5d2..319309ae6e7 100644 --- a/libgo/go/crypto/tls/root_windows.go +++ b/libgo/go/crypto/tls/root_windows.go @@ -6,39 +6,32 @@ package tls import ( "crypto/x509" - "reflect" "syscall" "unsafe" ) func loadStore(roots *x509.CertPool, name string) { - store, errno := syscall.CertOpenSystemStore(syscall.InvalidHandle, syscall.StringToUTF16Ptr(name)) - if errno != 0 { + store, err := syscall.CertOpenSystemStore(syscall.InvalidHandle, syscall.StringToUTF16Ptr(name)) + if err != nil { return } + defer syscall.CertCloseStore(store, 0) var cert *syscall.CertContext for { - cert = syscall.CertEnumCertificatesInStore(store, cert) - if cert == nil { - break + cert, err = syscall.CertEnumCertificatesInStore(store, cert) + if err != nil { + return } - var asn1Slice []byte - hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&asn1Slice)) - hdrp.Data = cert.EncodedCert - hdrp.Len = int(cert.Length) - hdrp.Cap = int(cert.Length) - - buf := make([]byte, len(asn1Slice)) - copy(buf, asn1Slice) - - if cert, err := x509.ParseCertificate(buf); err == nil { - roots.AddCert(cert) + buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:] + // ParseCertificate requires its own copy of certificate data to keep. + buf2 := make([]byte, cert.Length) + copy(buf2, buf) + if c, err := x509.ParseCertificate(buf2); err == nil { + roots.AddCert(c) } } - - syscall.CertCloseStore(store, 0) } func initDefaultRoots() { diff --git a/libgo/go/crypto/tls/tls.go b/libgo/go/crypto/tls/tls.go index 3ca62407ff0..79ab5023129 100644 --- a/libgo/go/crypto/tls/tls.go +++ b/libgo/go/crypto/tls/tls.go @@ -157,10 +157,21 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error) return } - key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes) - if err != nil { - err = errors.New("crypto/tls: failed to parse key: " + err.Error()) - return + // OpenSSL 0.9.8 generates PKCS#1 private keys by default, while + // OpenSSL 1.0.0 generates PKCS#8 keys. We try both. + var key *rsa.PrivateKey + if key, err = x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes); err != nil { + var privKey interface{} + if privKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes); err != nil { + err = errors.New("crypto/tls: failed to parse key: " + err.Error()) + return + } + + var ok bool + if key, ok = privKey.(*rsa.PrivateKey); !ok { + err = errors.New("crypto/tls: found non-RSA private key in PKCS#8 wrapping") + return + } } cert.PrivateKey = key diff --git a/libgo/go/crypto/x509/cert_pool.go b/libgo/go/crypto/x509/cert_pool.go index b9196ed46ed..adc7f9bc6d7 100644 --- a/libgo/go/crypto/x509/cert_pool.go +++ b/libgo/go/crypto/x509/cert_pool.go @@ -8,7 +8,7 @@ import ( "encoding/pem" ) -// Roots is a set of certificates. +// CertPool is a set of certificates. type CertPool struct { bySubjectKeyId map[string][]int byName map[string][]int @@ -70,11 +70,11 @@ func (s *CertPool) AddCert(cert *Certificate) { s.byName[name] = append(s.byName[name], n) } -// AppendCertsFromPEM attempts to parse a series of PEM encoded root -// certificates. It appends any certificates found to s and returns true if any -// certificates were successfully parsed. +// AppendCertsFromPEM attempts to parse a series of PEM encoded certificates. +// It appends any certificates found to s and returns true if any certificates +// were successfully parsed. // -// On many Linux systems, /etc/ssl/cert.pem will contains the system wide set +// On many Linux systems, /etc/ssl/cert.pem will contain the system wide set // of root CAs in a format suitable for this function. func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) { for len(pemCerts) > 0 { diff --git a/libgo/go/crypto/x509/pkcs8.go b/libgo/go/crypto/x509/pkcs8.go new file mode 100644 index 00000000000..4d8e0518e02 --- /dev/null +++ b/libgo/go/crypto/x509/pkcs8.go @@ -0,0 +1,42 @@ +// 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 x509 + +import ( + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" +) + +// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See +// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn. +type pkcs8 struct { + Version int + Algo pkix.AlgorithmIdentifier + PrivateKey []byte + // optional attributes omitted. +} + +// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See +// http://www.rsa.com/rsalabs/node.asp?id=2130 +func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) { + var privKey pkcs8 + if _, err := asn1.Unmarshal(der, &privKey); err != nil { + return nil, err + } + switch { + case privKey.Algo.Algorithm.Equal(oidRSA): + key, err = ParsePKCS1PrivateKey(privKey.PrivateKey) + if err != nil { + return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error()) + } + return key, nil + default: + return nil, fmt.Errorf("crypto/x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm) + } + + panic("unreachable") +} diff --git a/libgo/go/crypto/x509/pkcs8_test.go b/libgo/go/crypto/x509/pkcs8_test.go new file mode 100644 index 00000000000..372005f908c --- /dev/null +++ b/libgo/go/crypto/x509/pkcs8_test.go @@ -0,0 +1,20 @@ +// 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 x509 + +import ( + "encoding/hex" + "testing" +) + +var pkcs8PrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031` + +func TestPKCS8(t *testing.T) { + derBytes, _ := hex.DecodeString(pkcs8PrivateKeyHex) + _, err := ParsePKCS8PrivateKey(derBytes) + if err != nil { + t.Errorf("failed to decode PKCS8 key: %s", err) + } +} diff --git a/libgo/go/crypto/x509/pkix/pkix.go b/libgo/go/crypto/x509/pkix/pkix.go index b35274c9ae1..8eced55f932 100644 --- a/libgo/go/crypto/x509/pkix/pkix.go +++ b/libgo/go/crypto/x509/pkix/pkix.go @@ -142,10 +142,9 @@ type CertificateList struct { SignatureValue asn1.BitString } -// HasExpired returns true iff currentTimeSeconds is past the expiry time of -// certList. -func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool { - return certList.TBSCertList.NextUpdate.Seconds() <= currentTimeSeconds +// HasExpired returns true iff now is past the expiry time of certList. +func (certList *CertificateList) HasExpired(now time.Time) bool { + return now.After(certList.TBSCertList.NextUpdate) } // TBSCertificateList represents the ASN.1 structure of the same name. See RFC @@ -155,8 +154,8 @@ type TBSCertificateList struct { Version int `asn1:"optional,default:2"` Signature AlgorithmIdentifier Issuer RDNSequence - ThisUpdate *time.Time - NextUpdate *time.Time + ThisUpdate time.Time + NextUpdate time.Time RevokedCertificates []RevokedCertificate `asn1:"optional"` Extensions []Extension `asn1:"tag:0,optional,explicit"` } @@ -165,6 +164,6 @@ type TBSCertificateList struct { // 5280, section 5.1. type RevokedCertificate struct { SerialNumber *big.Int - RevocationTime *time.Time + RevocationTime time.Time Extensions []Extension `asn1:"optional"` } diff --git a/libgo/go/crypto/x509/verify.go b/libgo/go/crypto/x509/verify.go index 3021d20a67f..50a3b66e555 100644 --- a/libgo/go/crypto/x509/verify.go +++ b/libgo/go/crypto/x509/verify.go @@ -76,7 +76,7 @@ type VerifyOptions struct { DNSName string Intermediates *CertPool Roots *CertPool - CurrentTime int64 // if 0, the current system time is used. + CurrentTime time.Time // if zero, the current time is used } const ( @@ -87,8 +87,11 @@ const ( // isValid performs validity checks on the c. func (c *Certificate) isValid(certType int, opts *VerifyOptions) error { - if opts.CurrentTime < c.NotBefore.Seconds() || - opts.CurrentTime > c.NotAfter.Seconds() { + now := opts.CurrentTime + if now.IsZero() { + now = time.Now() + } + if now.Before(c.NotBefore) || now.After(c.NotAfter) { return CertificateInvalidError{c, Expired} } @@ -136,9 +139,6 @@ func (c *Certificate) isValid(certType int, opts *VerifyOptions) error { // // WARNING: this doesn't do any revocation checking. func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) { - if opts.CurrentTime == 0 { - opts.CurrentTime = time.Seconds() - } err = c.isValid(leafCertificate, &opts) if err != nil { return diff --git a/libgo/go/crypto/x509/verify_test.go b/libgo/go/crypto/x509/verify_test.go index 2194d15bc88..df5443023ff 100644 --- a/libgo/go/crypto/x509/verify_test.go +++ b/libgo/go/crypto/x509/verify_test.go @@ -10,6 +10,7 @@ import ( "errors" "strings" "testing" + "time" ) type verifyTest struct { @@ -133,7 +134,7 @@ func TestVerify(t *testing.T) { Roots: NewCertPool(), Intermediates: NewCertPool(), DNSName: test.dnsName, - CurrentTime: test.currentTime, + CurrentTime: time.Unix(test.currentTime, 0), } for j, root := range test.roots { diff --git a/libgo/go/crypto/x509/x509.go b/libgo/go/crypto/x509/x509.go index 9ff7db9a0f9..7e6b5c96f53 100644 --- a/libgo/go/crypto/x509/x509.go +++ b/libgo/go/crypto/x509/x509.go @@ -107,7 +107,7 @@ type dsaSignature struct { } type validity struct { - NotBefore, NotAfter *time.Time + NotBefore, NotAfter time.Time } type publicKeyInfo struct { @@ -303,7 +303,7 @@ type Certificate struct { SerialNumber *big.Int Issuer pkix.Name Subject pkix.Name - NotBefore, NotAfter *time.Time // Validity bounds. + NotBefore, NotAfter time.Time // Validity bounds. KeyUsage KeyUsage ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages. @@ -398,7 +398,7 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature } h.Write(signed) - digest := h.Sum() + digest := h.Sum(nil) switch pub := c.PublicKey.(type) { case *rsa.PublicKey: @@ -899,11 +899,10 @@ var ( oidRSA = []int{1, 2, 840, 113549, 1, 1, 1} ) -// CreateSelfSignedCertificate creates a new certificate based on -// a template. The following members of template are used: SerialNumber, -// Subject, NotBefore, NotAfter, KeyUsage, BasicConstraintsValid, IsCA, -// MaxPathLen, SubjectKeyId, DNSNames, PermittedDNSDomainsCritical, -// PermittedDNSDomains. +// CreateCertificate creates a new certificate based on a template. The +// following members of template are used: SerialNumber, Subject, NotBefore, +// NotAfter, KeyUsage, BasicConstraintsValid, IsCA, MaxPathLen, SubjectKeyId, +// DNSNames, PermittedDNSDomainsCritical, PermittedDNSDomains. // // The certificate is signed by parent. If parent is equal to template then the // certificate is self-signed. The parameter pub is the public key of the @@ -958,7 +957,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P h := sha1.New() h.Write(tbsCertContents) - digest := h.Sum() + digest := h.Sum(nil) signature, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, digest) if err != nil { @@ -1006,7 +1005,7 @@ func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err error) { // CreateCRL returns a DER encoded CRL, signed by this Certificate, that // contains the given list of revoked certificates. -func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCerts []pkix.RevokedCertificate, now, expiry *time.Time) (crlBytes []byte, err error) { +func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) { tbsCertList := pkix.TBSCertificateList{ Version: 2, Signature: pkix.AlgorithmIdentifier{ @@ -1025,7 +1024,7 @@ func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCer h := sha1.New() h.Write(tbsCertListContents) - digest := h.Sum() + digest := h.Sum(nil) signature, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, digest) if err != nil { diff --git a/libgo/go/crypto/x509/x509_test.go b/libgo/go/crypto/x509/x509_test.go index c42471507be..f0327b0124d 100644 --- a/libgo/go/crypto/x509/x509_test.go +++ b/libgo/go/crypto/x509/x509_test.go @@ -250,8 +250,8 @@ func TestCreateSelfSignedCertificate(t *testing.T) { CommonName: commonName, Organization: []string{"Acme Co"}, }, - NotBefore: time.SecondsToUTC(1000), - NotAfter: time.SecondsToUTC(100000), + NotBefore: time.Unix(1000, 0), + NotAfter: time.Unix(100000, 0), SubjectKeyId: []byte{1, 2, 3, 4}, KeyUsage: KeyUsageCertSign, @@ -396,8 +396,8 @@ func TestCRLCreation(t *testing.T) { block, _ = pem.Decode([]byte(pemCertificate)) cert, _ := ParseCertificate(block.Bytes) - now := time.SecondsToUTC(1000) - expiry := time.SecondsToUTC(10000) + now := time.Unix(1000, 0) + expiry := time.Unix(10000, 0) revokedCerts := []pkix.RevokedCertificate{ { @@ -443,7 +443,7 @@ func TestParseDERCRL(t *testing.T) { t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) } - if certList.HasExpired(1302517272) { + if certList.HasExpired(time.Unix(1302517272, 0)) { t.Errorf("CRL has expired (but shouldn't have)") } @@ -463,7 +463,7 @@ func TestParsePEMCRL(t *testing.T) { t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) } - if certList.HasExpired(1302517272) { + if certList.HasExpired(time.Unix(1302517272, 0)) { t.Errorf("CRL has expired (but shouldn't have)") } diff --git a/libgo/go/crypto/xtea/cipher.go b/libgo/go/crypto/xtea/cipher.go index 64d933c2b67..3ed05814a3b 100644 --- a/libgo/go/crypto/xtea/cipher.go +++ b/libgo/go/crypto/xtea/cipher.go @@ -44,7 +44,7 @@ func NewCipher(key []byte) (*Cipher, error) { } // BlockSize returns the XTEA block size, 8 bytes. -// It is necessary to satisfy the Cipher interface in the +// It is necessary to satisfy the Block interface in the // package "crypto/cipher". func (c *Cipher) BlockSize() int { return BlockSize } diff --git a/libgo/go/encoding/asn1/asn1.go b/libgo/go/encoding/asn1/asn1.go index a0066654f8d..22a0dde0da4 100644 --- a/libgo/go/encoding/asn1/asn1.go +++ b/libgo/go/encoding/asn1/asn1.go @@ -247,7 +247,7 @@ func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) // UTCTime -func parseUTCTime(bytes []byte) (ret *time.Time, err error) { +func parseUTCTime(bytes []byte) (ret time.Time, err error) { s := string(bytes) ret, err = time.Parse("0601021504Z0700", s) if err == nil { @@ -259,7 +259,7 @@ func parseUTCTime(bytes []byte) (ret *time.Time, err error) { // parseGeneralizedTime parses the GeneralizedTime from the given byte slice // and returns the resulting time. -func parseGeneralizedTime(bytes []byte) (ret *time.Time, err error) { +func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) { return time.Parse("20060102150405Z0700", string(bytes)) } @@ -450,7 +450,7 @@ var ( objectIdentifierType = reflect.TypeOf(ObjectIdentifier{}) enumeratedType = reflect.TypeOf(Enumerated(0)) flagType = reflect.TypeOf(Flag(false)) - timeType = reflect.TypeOf(&time.Time{}) + timeType = reflect.TypeOf(time.Time{}) rawValueType = reflect.TypeOf(RawValue{}) rawContentsType = reflect.TypeOf(RawContent(nil)) bigIntType = reflect.TypeOf(new(big.Int)) @@ -647,7 +647,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam err = err1 return case timeType: - var time *time.Time + var time time.Time var err1 error if universalTag == tagUTCTime { time, err1 = parseUTCTime(innerBytes) @@ -799,7 +799,7 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { // // An ASN.1 ENUMERATED can be written to an Enumerated. // -// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a *time.Time. +// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a time.Time. // // An ASN.1 PrintableString or IA5String can be written to a string. // diff --git a/libgo/go/encoding/asn1/asn1_test.go b/libgo/go/encoding/asn1/asn1_test.go index 1c529bdb30c..2e6fccf7b80 100644 --- a/libgo/go/encoding/asn1/asn1_test.go +++ b/libgo/go/encoding/asn1/asn1_test.go @@ -202,43 +202,51 @@ func TestObjectIdentifier(t *testing.T) { type timeTest struct { in string ok bool - out *time.Time + out time.Time } var utcTestData = []timeTest{ - {"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, -7 * 60 * 60, ""}}, - {"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 7*60*60 + 30*60, ""}}, - {"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, "UTC"}}, - {"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, "UTC"}}, - {"a10506234540Z", false, nil}, - {"91a506234540Z", false, nil}, - {"9105a6234540Z", false, nil}, - {"910506a34540Z", false, nil}, - {"910506334a40Z", false, nil}, - {"91050633444aZ", false, nil}, - {"910506334461Z", false, nil}, - {"910506334400Za", false, nil}, + {"910506164540-0700", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", -7*60*60))}, + {"910506164540+0730", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", 7*60*60+30*60))}, + {"910506234540Z", true, time.Date(1991, 05, 06, 23, 45, 40, 0, time.UTC)}, + {"9105062345Z", true, time.Date(1991, 05, 06, 23, 45, 0, 0, time.UTC)}, + {"a10506234540Z", false, time.Time{}}, + {"91a506234540Z", false, time.Time{}}, + {"9105a6234540Z", false, time.Time{}}, + {"910506a34540Z", false, time.Time{}}, + {"910506334a40Z", false, time.Time{}}, + {"91050633444aZ", false, time.Time{}}, + {"910506334461Z", false, time.Time{}}, + {"910506334400Za", false, time.Time{}}, } func TestUTCTime(t *testing.T) { for i, test := range utcTestData { ret, err := parseUTCTime([]byte(test.in)) - if (err == nil) != test.ok { - t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) - } - if err == nil { - if !reflect.DeepEqual(test.out, ret) { - t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) + if err != nil { + if test.ok { + t.Errorf("#%d: parseUTCTime(%q) = error %v", i, err) } + continue + } + if !test.ok { + t.Errorf("#%d: parseUTCTime(%q) succeeded, should have failed", i) + continue + } + const format = "Jan _2 15:04:05 -0700 2006" // ignore zone name, just offset + have := ret.Format(format) + want := test.out.Format(format) + if have != want { + t.Errorf("#%d: parseUTCTime(%q) = %s, want %s", test.in, have, want) } } } var generalizedTimeTestData = []timeTest{ - {"20100102030405Z", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, "UTC"}}, - {"20100102030405", false, nil}, - {"20100102030405+0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 6*60*60 + 7*60, ""}}, - {"20100102030405-0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, -6*60*60 - 7*60, ""}}, + {"20100102030405Z", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.UTC)}, + {"20100102030405", false, time.Time{}}, + {"20100102030405+0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", 6*60*60+7*60))}, + {"20100102030405-0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", -6*60*60-7*60))}, } func TestGeneralizedTime(t *testing.T) { @@ -407,7 +415,7 @@ type AttributeTypeAndValue struct { } type Validity struct { - NotBefore, NotAfter *time.Time + NotBefore, NotAfter time.Time } type PublicKeyInfo struct { @@ -475,7 +483,10 @@ var derEncodedSelfSignedCert = Certificate{ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}}, RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}}, }, - Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, ZoneOffset: 0, Zone: "UTC"}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, ZoneOffset: 0, Zone: "UTC"}}, + Validity: Validity{ + NotBefore: time.Date(2009, 10, 8, 00, 25, 53, 0, time.UTC), + NotAfter: time.Date(2010, 10, 8, 00, 25, 53, 0, time.UTC), + }, Subject: RDNSequence{ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}}, diff --git a/libgo/go/encoding/asn1/marshal.go b/libgo/go/encoding/asn1/marshal.go index 89c50a70ef4..c181e43f979 100644 --- a/libgo/go/encoding/asn1/marshal.go +++ b/libgo/go/encoding/asn1/marshal.go @@ -288,52 +288,58 @@ func marshalTwoDigits(out *forkableWriter, v int) (err error) { return out.WriteByte(byte('0' + v%10)) } -func marshalUTCTime(out *forkableWriter, t *time.Time) (err error) { +func marshalUTCTime(out *forkableWriter, t time.Time) (err error) { + utc := t.UTC() + year, month, day := utc.Date() + switch { - case 1950 <= t.Year && t.Year < 2000: - err = marshalTwoDigits(out, int(t.Year-1900)) - case 2000 <= t.Year && t.Year < 2050: - err = marshalTwoDigits(out, int(t.Year-2000)) + case 1950 <= year && year < 2000: + err = marshalTwoDigits(out, int(year-1900)) + case 2000 <= year && year < 2050: + err = marshalTwoDigits(out, int(year-2000)) default: return StructuralError{"Cannot represent time as UTCTime"} } - if err != nil { return } - err = marshalTwoDigits(out, t.Month) + err = marshalTwoDigits(out, int(month)) if err != nil { return } - err = marshalTwoDigits(out, t.Day) + err = marshalTwoDigits(out, day) if err != nil { return } - err = marshalTwoDigits(out, t.Hour) + hour, min, sec := utc.Clock() + + err = marshalTwoDigits(out, hour) if err != nil { return } - err = marshalTwoDigits(out, t.Minute) + err = marshalTwoDigits(out, min) if err != nil { return } - err = marshalTwoDigits(out, t.Second) + err = marshalTwoDigits(out, sec) if err != nil { return } + _, offset := t.Zone() + switch { - case t.ZoneOffset/60 == 0: + case offset/60 == 0: err = out.WriteByte('Z') return - case t.ZoneOffset > 0: + case offset > 0: err = out.WriteByte('+') - case t.ZoneOffset < 0: + case offset < 0: err = out.WriteByte('-') } @@ -341,7 +347,7 @@ func marshalUTCTime(out *forkableWriter, t *time.Time) (err error) { return } - offsetMinutes := t.ZoneOffset / 60 + offsetMinutes := offset / 60 if offsetMinutes < 0 { offsetMinutes = -offsetMinutes } @@ -366,7 +372,7 @@ func stripTagAndLength(in []byte) []byte { func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err error) { switch value.Type() { case timeType: - return marshalUTCTime(out, value.Interface().(*time.Time)) + return marshalUTCTime(out, value.Interface().(time.Time)) case bitStringType: return marshalBitString(out, value.Interface().(BitString)) case objectIdentifierType: diff --git a/libgo/go/encoding/asn1/marshal_test.go b/libgo/go/encoding/asn1/marshal_test.go index 03df5f1e1d5..d05b5d8d4e9 100644 --- a/libgo/go/encoding/asn1/marshal_test.go +++ b/libgo/go/encoding/asn1/marshal_test.go @@ -51,10 +51,7 @@ type optionalRawValueTest struct { type testSET []int -func setPST(t *time.Time) *time.Time { - t.ZoneOffset = -28800 - return t -} +var PST = time.FixedZone("PST", -8*60*60) type marshalTest struct { in interface{} @@ -73,9 +70,9 @@ var marshalTests = []marshalTest{ {[]byte{1, 2, 3}, "0403010203"}, {implicitTagTest{64}, "3003850140"}, {explicitTagTest{64}, "3005a503020140"}, - {time.SecondsToUTC(0), "170d3730303130313030303030305a"}, - {time.SecondsToUTC(1258325776), "170d3039313131353232353631365a"}, - {setPST(time.SecondsToUTC(1258325776)), "17113039313131353232353631362d30383030"}, + {time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"}, + {time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"}, + {time.Unix(1258325776, 0).In(PST), "17113039313131353232353631362d30383030"}, {BitString{[]byte{0x80}, 1}, "03020780"}, {BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"}, {ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"}, @@ -123,7 +120,8 @@ func TestMarshal(t *testing.T) { } out, _ := hex.DecodeString(test.out) if bytes.Compare(out, data) != 0 { - t.Errorf("#%d got: %x want %x", i, data, out) + t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out) + } } } diff --git a/libgo/go/encoding/json/bench_test.go b/libgo/go/encoding/json/bench_test.go new file mode 100644 index 00000000000..f0c52011a1d --- /dev/null +++ b/libgo/go/encoding/json/bench_test.go @@ -0,0 +1,157 @@ +// 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. + +// Large data benchmark. +// The JSON data is a summary of agl's changes in the +// go, webkit, and chromium open source projects. +// We benchmark converting between the JSON form +// and in-memory data structures. + +package json + +import ( + "bytes" + "compress/gzip" + "io/ioutil" + "os" + "testing" +) + +type codeResponse struct { + Tree *codeNode `json:"tree"` + Username string `json:"username"` +} + +type codeNode struct { + Name string `json:"name"` + Kids []*codeNode `json:"kids"` + CLWeight float64 `json:"cl_weight"` + Touches int `json:"touches"` + MinT int64 `json:"min_t"` + MaxT int64 `json:"max_t"` + MeanT int64 `json:"mean_t"` +} + +var codeJSON []byte +var codeStruct codeResponse + +func codeInit() { + f, err := os.Open("testdata/code.json.gz") + if err != nil { + panic(err) + } + defer f.Close() + gz, err := gzip.NewReader(f) + if err != nil { + panic(err) + } + data, err := ioutil.ReadAll(gz) + if err != nil { + panic(err) + } + + codeJSON = data + + if err := Unmarshal(codeJSON, &codeStruct); err != nil { + panic("unmarshal code.json: " + err.Error()) + } + + if data, err = Marshal(&codeStruct); err != nil { + panic("marshal code.json: " + err.Error()) + } + + if !bytes.Equal(data, codeJSON) { + println("different lengths", len(data), len(codeJSON)) + for i := 0; i < len(data) && i < len(codeJSON); i++ { + if data[i] != codeJSON[i] { + println("re-marshal: changed at byte", i) + println("orig: ", string(codeJSON[i-10:i+10])) + println("new: ", string(data[i-10:i+10])) + break + } + } + panic("re-marshal code.json: different result") + } +} + +func BenchmarkCodeEncoder(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + enc := NewEncoder(ioutil.Discard) + for i := 0; i < b.N; i++ { + if err := enc.Encode(&codeStruct); err != nil { + panic(err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeMarshal(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + for i := 0; i < b.N; i++ { + if _, err := Marshal(&codeStruct); err != nil { + panic(err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeDecoder(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + var buf bytes.Buffer + dec := NewDecoder(&buf) + var r codeResponse + for i := 0; i < b.N; i++ { + buf.Write(codeJSON) + // hide EOF + buf.WriteByte('\n') + buf.WriteByte('\n') + buf.WriteByte('\n') + if err := dec.Decode(&r); err != nil { + panic(err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeUnmarshal(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + for i := 0; i < b.N; i++ { + var r codeResponse + if err := Unmarshal(codeJSON, &r); err != nil { + panic(err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeUnmarshalReuse(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + var r codeResponse + for i := 0; i < b.N; i++ { + if err := Unmarshal(codeJSON, &r); err != nil { + panic(err) + } + } + b.SetBytes(int64(len(codeJSON))) +} diff --git a/libgo/go/encoding/json/decode.go b/libgo/go/encoding/json/decode.go index 41295d2d241..2ea06c50c27 100644 --- a/libgo/go/encoding/json/decode.go +++ b/libgo/go/encoding/json/decode.go @@ -227,7 +227,7 @@ func (d *decodeState) value(v reflect.Value) { // d.scan thinks we're still at the beginning of the item. // Feed in an empty string - the shortest, simplest value - // so that it knows we got to the end of the value. - if d.scan.step == stateRedo { + if d.scan.redo { panic("redo") } d.scan.step(&d.scan, '"') @@ -381,6 +381,7 @@ func (d *decodeState) array(v reflect.Value) { d.error(errPhase) } } + if i < av.Len() { if !sv.IsValid() { // Array. Zero the rest. @@ -392,6 +393,9 @@ func (d *decodeState) array(v reflect.Value) { sv.SetLen(i) } } + if i == 0 && av.Kind() == reflect.Slice && sv.IsNil() { + sv.Set(reflect.MakeSlice(sv.Type(), 0, 0)) + } } // object consumes an object from d.data[d.off-1:], decoding into the value v. diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go index 35964c5d9c2..14284f50e47 100644 --- a/libgo/go/encoding/json/encode.go +++ b/libgo/go/encoding/json/encode.go @@ -16,6 +16,7 @@ import ( "runtime" "sort" "strconv" + "sync" "unicode" "unicode/utf8" ) @@ -295,28 +296,10 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { case reflect.Struct: e.WriteByte('{') - t := v.Type() - n := v.NumField() first := true - for i := 0; i < n; i++ { - f := t.Field(i) - if f.PkgPath != "" { - continue - } - tag, omitEmpty, quoted := f.Name, false, false - if tv := f.Tag.Get("json"); tv != "" { - if tv == "-" { - continue - } - name, opts := parseTag(tv) - if isValidTag(name) { - tag = name - } - omitEmpty = opts.Contains("omitempty") - quoted = opts.Contains("string") - } - fieldValue := v.Field(i) - if omitEmpty && isEmptyValue(fieldValue) { + for _, ef := range encodeFields(v.Type()) { + fieldValue := v.Field(ef.i) + if ef.omitEmpty && isEmptyValue(fieldValue) { continue } if first { @@ -324,9 +307,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { } else { e.WriteByte(',') } - e.string(tag) + e.string(ef.tag) e.WriteByte(':') - e.reflectValueQuoted(fieldValue, quoted) + e.reflectValueQuoted(fieldValue, ef.quoted) } e.WriteByte('}') @@ -470,3 +453,63 @@ func (e *encodeState) string(s string) (int, error) { e.WriteByte('"') return e.Len() - len0, nil } + +// encodeField contains information about how to encode a field of a +// struct. +type encodeField struct { + i int // field index in struct + tag string + quoted bool + omitEmpty bool +} + +var ( + typeCacheLock sync.RWMutex + encodeFieldsCache = make(map[reflect.Type][]encodeField) +) + +// encodeFields returns a slice of encodeField for a given +// struct type. +func encodeFields(t reflect.Type) []encodeField { + typeCacheLock.RLock() + fs, ok := encodeFieldsCache[t] + typeCacheLock.RUnlock() + if ok { + return fs + } + + typeCacheLock.Lock() + defer typeCacheLock.Unlock() + fs, ok = encodeFieldsCache[t] + if ok { + return fs + } + + v := reflect.Zero(t) + n := v.NumField() + for i := 0; i < n; i++ { + f := t.Field(i) + if f.PkgPath != "" { + continue + } + var ef encodeField + ef.i = i + ef.tag = f.Name + + tv := f.Tag.Get("json") + if tv != "" { + if tv == "-" { + continue + } + name, opts := parseTag(tv) + if isValidTag(name) { + ef.tag = name + } + ef.omitEmpty = opts.Contains("omitempty") + ef.quoted = opts.Contains("string") + } + fs = append(fs, ef) + } + encodeFieldsCache[t] = fs + return fs +} diff --git a/libgo/go/encoding/json/scanner.go b/libgo/go/encoding/json/scanner.go index 179690464b9..2661f410e01 100644 --- a/libgo/go/encoding/json/scanner.go +++ b/libgo/go/encoding/json/scanner.go @@ -80,6 +80,9 @@ type scanner struct { // on a 64-bit Mac Mini, and it's nicer to read. step func(*scanner, int) int + // Reached end of top-level value. + endTop bool + // Stack of what we're in the middle of - array values, object keys, object values. parseState []int @@ -87,6 +90,7 @@ type scanner struct { err error // 1-byte redo (see undo method) + redo bool redoCode int redoState func(*scanner, int) int @@ -135,6 +139,8 @@ func (s *scanner) reset() { s.step = stateBeginValue s.parseState = s.parseState[0:0] s.err = nil + s.redo = false + s.endTop = false } // eof tells the scanner that the end of input has been reached. @@ -143,11 +149,11 @@ func (s *scanner) eof() int { if s.err != nil { return scanError } - if s.step == stateEndTop { + if s.endTop { return scanEnd } s.step(s, ' ') - if s.step == stateEndTop { + if s.endTop { return scanEnd } if s.err == nil { @@ -166,8 +172,10 @@ func (s *scanner) pushParseState(p int) { func (s *scanner) popParseState() { n := len(s.parseState) - 1 s.parseState = s.parseState[0:n] + s.redo = false if n == 0 { s.step = stateEndTop + s.endTop = true } else { s.step = stateEndValue } @@ -269,6 +277,7 @@ func stateEndValue(s *scanner, c int) int { if n == 0 { // Completed top-level before the current byte. s.step = stateEndTop + s.endTop = true return stateEndTop(s, c) } if c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n') { @@ -606,16 +615,18 @@ func quoteChar(c int) string { // undo causes the scanner to return scanCode from the next state transition. // This gives callers a simple 1-byte undo mechanism. func (s *scanner) undo(scanCode int) { - if s.step == stateRedo { - panic("invalid use of scanner") + if s.redo { + panic("json: invalid use of scanner") } s.redoCode = scanCode s.redoState = s.step s.step = stateRedo + s.redo = true } // stateRedo helps implement the scanner's 1-byte undo. func stateRedo(s *scanner, c int) int { + s.redo = false s.step = s.redoState return s.redoCode } diff --git a/libgo/go/encoding/json/scanner_test.go b/libgo/go/encoding/json/scanner_test.go index a0a5995af8f..14d850865a6 100644 --- a/libgo/go/encoding/json/scanner_test.go +++ b/libgo/go/encoding/json/scanner_test.go @@ -186,11 +186,12 @@ func TestNextValueBig(t *testing.T) { } } +var benchScan scanner + func BenchmarkSkipValue(b *testing.B) { initBig() - var scan scanner for i := 0; i < b.N; i++ { - nextValue(jsonBig, &scan) + nextValue(jsonBig, &benchScan) } b.SetBytes(int64(len(jsonBig))) } diff --git a/libgo/go/encoding/xml/xml.go b/libgo/go/encoding/xml/xml.go index 216d8889b23..d67a299f5bb 100644 --- a/libgo/go/encoding/xml/xml.go +++ b/libgo/go/encoding/xml/xml.go @@ -61,7 +61,7 @@ type StartElement struct { func (e StartElement) Copy() StartElement { attrs := make([]Attr, len(e.Attr)) - copy(e.Attr, attrs) + copy(attrs, e.Attr) e.Attr = attrs return e } diff --git a/libgo/go/encoding/xml/xml_test.go b/libgo/go/encoding/xml/xml_test.go index 6c874fadb7a..25ffc917dcb 100644 --- a/libgo/go/encoding/xml/xml_test.go +++ b/libgo/go/encoding/xml/xml_test.go @@ -7,7 +7,6 @@ package xml import ( "bytes" "io" - "os" "reflect" "strings" "testing" @@ -30,71 +29,69 @@ const testInput = ` </body><!-- missing final newline -->` var rawTokens = []Token{ - CharData([]byte("\n")), + CharData("\n"), ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)}, - CharData([]byte("\n")), - Directive([]byte(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + CharData("\n"), + Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`), - ), - CharData([]byte("\n")), + CharData("\n"), StartElement{Name{"", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}}, - CharData([]byte("\n ")), + CharData("\n "), StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}}, - CharData([]byte("World <>'\" 白鵬翔")), + CharData("World <>'\" 白鵬翔"), EndElement{Name{"", "hello"}}, - CharData([]byte("\n ")), - StartElement{Name{"", "goodbye"}, nil}, + CharData("\n "), + StartElement{Name{"", "goodbye"}, []Attr{}}, EndElement{Name{"", "goodbye"}}, - CharData([]byte("\n ")), + CharData("\n "), StartElement{Name{"", "outer"}, []Attr{{Name{"foo", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}}, - CharData([]byte("\n ")), - StartElement{Name{"", "inner"}, nil}, + CharData("\n "), + StartElement{Name{"", "inner"}, []Attr{}}, EndElement{Name{"", "inner"}}, - CharData([]byte("\n ")), + CharData("\n "), EndElement{Name{"", "outer"}}, - CharData([]byte("\n ")), - StartElement{Name{"tag", "name"}, nil}, - CharData([]byte("\n ")), - CharData([]byte("Some text here.")), - CharData([]byte("\n ")), + CharData("\n "), + StartElement{Name{"tag", "name"}, []Attr{}}, + CharData("\n "), + CharData("Some text here."), + CharData("\n "), EndElement{Name{"tag", "name"}}, - CharData([]byte("\n")), + CharData("\n"), EndElement{Name{"", "body"}}, - Comment([]byte(" missing final newline ")), + Comment(" missing final newline "), } var cookedTokens = []Token{ - CharData([]byte("\n")), + CharData("\n"), ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)}, - CharData([]byte("\n")), - Directive([]byte(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + CharData("\n"), + Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`), - ), - CharData([]byte("\n")), + CharData("\n"), StartElement{Name{"ns2", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}}, - CharData([]byte("\n ")), + CharData("\n "), StartElement{Name{"ns2", "hello"}, []Attr{{Name{"", "lang"}, "en"}}}, - CharData([]byte("World <>'\" 白鵬翔")), + CharData("World <>'\" 白鵬翔"), EndElement{Name{"ns2", "hello"}}, - CharData([]byte("\n ")), - StartElement{Name{"ns2", "goodbye"}, nil}, + CharData("\n "), + StartElement{Name{"ns2", "goodbye"}, []Attr{}}, EndElement{Name{"ns2", "goodbye"}}, - CharData([]byte("\n ")), + CharData("\n "), StartElement{Name{"ns2", "outer"}, []Attr{{Name{"ns1", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}}, - CharData([]byte("\n ")), - StartElement{Name{"ns2", "inner"}, nil}, + CharData("\n "), + StartElement{Name{"ns2", "inner"}, []Attr{}}, EndElement{Name{"ns2", "inner"}}, - CharData([]byte("\n ")), + CharData("\n "), EndElement{Name{"ns2", "outer"}}, - CharData([]byte("\n ")), - StartElement{Name{"ns3", "name"}, nil}, - CharData([]byte("\n ")), - CharData([]byte("Some text here.")), - CharData([]byte("\n ")), + CharData("\n "), + StartElement{Name{"ns3", "name"}, []Attr{}}, + CharData("\n "), + CharData("Some text here."), + CharData("\n "), EndElement{Name{"ns3", "name"}}, - CharData([]byte("\n")), + CharData("\n"), EndElement{Name{"ns2", "body"}}, - Comment([]byte(" missing final newline ")), + Comment(" missing final newline "), } const testInputAltEncoding = ` @@ -102,11 +99,11 @@ const testInputAltEncoding = ` <TAG>VALUE</TAG>` var rawTokensAltEncoding = []Token{ - CharData([]byte("\n")), + CharData("\n"), ProcInst{"xml", []byte(`version="1.0" encoding="x-testing-uppercase"`)}, - CharData([]byte("\n")), - StartElement{Name{"", "tag"}, nil}, - CharData([]byte("value")), + CharData("\n"), + StartElement{Name{"", "tag"}, []Attr{}}, + CharData("value"), EndElement{Name{"", "tag"}}, } @@ -205,7 +202,7 @@ func (d *downCaser) ReadByte() (c byte, err error) { func (d *downCaser) Read(p []byte) (int, error) { d.t.Fatalf("unexpected Read call on downCaser reader") - return 0, os.EINVAL + panic("unreachable") } func TestRawTokenAltEncoding(t *testing.T) { @@ -271,21 +268,21 @@ var nestedDirectivesInput = ` ` var nestedDirectivesTokens = []Token{ - CharData([]byte("\n")), - Directive([]byte(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`)), - CharData([]byte("\n")), - Directive([]byte(`DOCTYPE [<!ENTITY xlt ">">]`)), - CharData([]byte("\n")), - Directive([]byte(`DOCTYPE [<!ENTITY xlt "<">]`)), - CharData([]byte("\n")), - Directive([]byte(`DOCTYPE [<!ENTITY xlt '>'>]`)), - CharData([]byte("\n")), - Directive([]byte(`DOCTYPE [<!ENTITY xlt '<'>]`)), - CharData([]byte("\n")), - Directive([]byte(`DOCTYPE [<!ENTITY xlt '">'>]`)), - CharData([]byte("\n")), - Directive([]byte(`DOCTYPE [<!ENTITY xlt "'<">]`)), - CharData([]byte("\n")), + CharData("\n"), + Directive(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`), + CharData("\n"), + Directive(`DOCTYPE [<!ENTITY xlt ">">]`), + CharData("\n"), + Directive(`DOCTYPE [<!ENTITY xlt "<">]`), + CharData("\n"), + Directive(`DOCTYPE [<!ENTITY xlt '>'>]`), + CharData("\n"), + Directive(`DOCTYPE [<!ENTITY xlt '<'>]`), + CharData("\n"), + Directive(`DOCTYPE [<!ENTITY xlt '">'>]`), + CharData("\n"), + Directive(`DOCTYPE [<!ENTITY xlt "'<">]`), + CharData("\n"), } func TestNestedDirectives(t *testing.T) { @@ -489,10 +486,13 @@ func TestCopyTokenStartElement(t *testing.T) { elt := StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}} var tok1 Token = elt tok2 := CopyToken(tok1) + if tok1.(StartElement).Attr[0].Value != "en" { + t.Error("CopyToken overwrote Attr[0]") + } if !reflect.DeepEqual(tok1, tok2) { t.Error("CopyToken(StartElement) != StartElement") } - elt.Attr[0] = Attr{Name{"", "lang"}, "de"} + tok1.(StartElement).Attr[0] = Attr{Name{"", "lang"}, "de"} if reflect.DeepEqual(tok1, tok2) { t.Error("CopyToken(CharData) uses same buffer.") } diff --git a/libgo/go/exp/gotype/gotype.go b/libgo/go/exp/gotype/gotype.go index bc4a112c98f..a2a9361866d 100644 --- a/libgo/go/exp/gotype/gotype.go +++ b/libgo/go/exp/gotype/gotype.go @@ -150,15 +150,15 @@ func processFiles(filenames []string, allFiles bool) { switch info, err := os.Stat(filename); { case err != nil: report(err) - case info.IsRegular(): - if allFiles || isGoFilename(info.Name) { - filenames[i] = filename - i++ - } - case info.IsDirectory(): + case info.IsDir(): if allFiles || *recursive { processDirectory(filename) } + default: + if allFiles || isGoFilename(info.Name()) { + filenames[i] = filename + i++ + } } } fset := token.NewFileSet() diff --git a/libgo/go/exp/gui/gui.go b/libgo/go/exp/gui/gui.go deleted file mode 100644 index a69f83a1f50..00000000000 --- a/libgo/go/exp/gui/gui.go +++ /dev/null @@ -1,57 +0,0 @@ -// 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 gui defines a basic graphical user interface programming model. -package gui - -import ( - "image" - "image/draw" -) - -// A Window represents a single graphics window. -type Window interface { - // Screen returns an editable Image for the window. - Screen() draw.Image - // FlushImage flushes changes made to Screen() back to screen. - FlushImage() - // EventChan returns a channel carrying UI events such as key presses, - // mouse movements and window resizes. - EventChan() <-chan interface{} - // Close closes the window. - Close() error -} - -// A KeyEvent is sent for a key press or release. -type KeyEvent struct { - // The value k represents key k being pressed. - // The value -k represents key k being released. - // The specific set of key values is not specified, - // but ordinary characters represent themselves. - Key int -} - -// A MouseEvent is sent for a button press or release or for a mouse movement. -type MouseEvent struct { - // Buttons is a bit mask of buttons: 1<<0 is left, 1<<1 middle, 1<<2 right. - // It represents button state and not necessarily the state delta: bit 0 - // being on means that the left mouse button is down, but does not imply - // that the same button was up in the previous MouseEvent. - Buttons int - // Loc is the location of the cursor. - Loc image.Point - // Nsec is the event's timestamp. - Nsec int64 -} - -// A ConfigEvent is sent each time the window's color model or size changes. -// The client should respond by calling Window.Screen to obtain a new image. -type ConfigEvent struct { - Config image.Config -} - -// An ErrEvent is sent when an error occurs. -type ErrEvent struct { - Err error -} diff --git a/libgo/go/exp/gui/x11/auth.go b/libgo/go/exp/gui/x11/auth.go deleted file mode 100644 index 24e941cb36b..00000000000 --- a/libgo/go/exp/gui/x11/auth.go +++ /dev/null @@ -1,96 +0,0 @@ -// 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 x11 - -import ( - "bufio" - "errors" - "io" - "os" -) - -// readU16BE reads a big-endian uint16 from r, using b as a scratch buffer. -func readU16BE(r io.Reader, b []byte) (uint16, error) { - _, err := io.ReadFull(r, b[0:2]) - if err != nil { - return 0, err - } - return uint16(b[0])<<8 + uint16(b[1]), nil -} - -// readStr reads a length-prefixed string from r, using b as a scratch buffer. -func readStr(r io.Reader, b []byte) (string, error) { - n, err := readU16BE(r, b) - if err != nil { - return "", err - } - if int(n) > len(b) { - return "", errors.New("Xauthority entry too long for buffer") - } - _, err = io.ReadFull(r, b[0:n]) - if err != nil { - return "", err - } - return string(b[0:n]), nil -} - -// readAuth reads the X authority file and returns the name/data pair for the display. -// displayStr is the "12" out of a $DISPLAY like ":12.0". -func readAuth(displayStr string) (name, data string, err error) { - // b is a scratch buffer to use and should be at least 256 bytes long - // (i.e. it should be able to hold a hostname). - var b [256]byte - // As per /usr/include/X11/Xauth.h. - const familyLocal = 256 - - fn := os.Getenv("XAUTHORITY") - if fn == "" { - home := os.Getenv("HOME") - if home == "" { - err = errors.New("Xauthority not found: $XAUTHORITY, $HOME not set") - return - } - fn = home + "/.Xauthority" - } - r, err := os.Open(fn) - if err != nil { - return - } - defer r.Close() - br := bufio.NewReader(r) - - hostname, err := os.Hostname() - if err != nil { - return - } - for { - var family uint16 - var addr, disp, name0, data0 string - family, err = readU16BE(br, b[0:2]) - if err != nil { - return - } - addr, err = readStr(br, b[0:]) - if err != nil { - return - } - disp, err = readStr(br, b[0:]) - if err != nil { - return - } - name0, err = readStr(br, b[0:]) - if err != nil { - return - } - data0, err = readStr(br, b[0:]) - if err != nil { - return - } - if family == familyLocal && addr == hostname && disp == displayStr { - return name0, data0, nil - } - } - panic("unreachable") -} diff --git a/libgo/go/exp/gui/x11/conn.go b/libgo/go/exp/gui/x11/conn.go deleted file mode 100644 index 15afc657ecb..00000000000 --- a/libgo/go/exp/gui/x11/conn.go +++ /dev/null @@ -1,631 +0,0 @@ -// 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 x11 implements an X11 backend for the exp/gui package. -// -// The X protocol specification is at ftp://ftp.x.org/pub/X11R7.0/doc/PDF/proto.pdf. -// A summary of the wire format can be found in XCB's xproto.xml. -package x11 - -import ( - "bufio" - "errors" - "exp/gui" - "image" - "image/draw" - "io" - "log" - "net" - "os" - "strconv" - "strings" - "time" -) - -type resID uint32 // X resource IDs. - -// TODO(nigeltao): Handle window resizes. -const ( - windowHeight = 600 - windowWidth = 800 -) - -const ( - keymapLo = 8 - keymapHi = 255 -) - -type conn struct { - c io.Closer - r *bufio.Reader - w *bufio.Writer - - gc, window, root, visual resID - - img *image.RGBA - eventc chan interface{} - mouseState gui.MouseEvent - - buf [256]byte // General purpose scratch buffer. - - flush chan bool - flushBuf0 [24]byte - flushBuf1 [4 * 1024]byte -} - -// writeSocket runs in its own goroutine, serving both FlushImage calls -// directly from the exp/gui client and indirectly from X expose events. -// It paints c.img to the X server via PutImage requests. -func (c *conn) writeSocket() { - defer c.c.Close() - for _ = range c.flush { - b := c.img.Bounds() - if b.Empty() { - continue - } - // Each X request has a 16-bit length (in terms of 4-byte units). To avoid going over - // this limit, we send PutImage for each row of the image, rather than trying to paint - // the entire image in one X request. This approach could easily be optimized (or the - // X protocol may have an escape sequence to delimit very large requests). - // TODO(nigeltao): See what XCB's xcb_put_image does in this situation. - units := 6 + b.Dx() - if units > 0xffff || b.Dy() > 0xffff { - log.Print("x11: window is too large for PutImage") - return - } - - c.flushBuf0[0] = 0x48 // PutImage opcode. - c.flushBuf0[1] = 0x02 // XCB_IMAGE_FORMAT_Z_PIXMAP. - c.flushBuf0[2] = uint8(units) - c.flushBuf0[3] = uint8(units >> 8) - setU32LE(c.flushBuf0[4:8], uint32(c.window)) - setU32LE(c.flushBuf0[8:12], uint32(c.gc)) - setU32LE(c.flushBuf0[12:16], 1<<16|uint32(b.Dx())) - c.flushBuf0[21] = 0x18 // depth = 24 bits. - - for y := b.Min.Y; y < b.Max.Y; y++ { - setU32LE(c.flushBuf0[16:20], uint32(y<<16)) - if _, err := c.w.Write(c.flushBuf0[:24]); err != nil { - if err != io.EOF { - log.Println("x11:", err) - } - return - } - p := c.img.Pix[(y-b.Min.Y)*c.img.Stride:] - for x, dx := 0, 4*b.Dx(); x < dx; { - nx := dx - x - if nx > len(c.flushBuf1) { - nx = len(c.flushBuf1) &^ 3 - } - for i := 0; i < nx; i += 4 { - // X11's order is BGRX, not RGBA. - c.flushBuf1[i+0] = p[x+i+2] - c.flushBuf1[i+1] = p[x+i+1] - c.flushBuf1[i+2] = p[x+i+0] - } - x += nx - if _, err := c.w.Write(c.flushBuf1[:nx]); err != nil { - if err != io.EOF { - log.Println("x11:", err) - } - return - } - } - } - if err := c.w.Flush(); err != nil { - if err != io.EOF { - log.Println("x11:", err) - } - return - } - } -} - -func (c *conn) Screen() draw.Image { return c.img } - -func (c *conn) FlushImage() { - select { - case c.flush <- false: - // Flush notification sent. - default: - // Could not send. - // Flush notification must be pending already. - } -} - -func (c *conn) Close() error { - // Shut down the writeSocket goroutine. This will close the socket to the - // X11 server, which will cause c.eventc to close. - close(c.flush) - for _ = range c.eventc { - // Drain the channel to allow the readSocket goroutine to shut down. - } - return nil -} - -func (c *conn) EventChan() <-chan interface{} { return c.eventc } - -// readSocket runs in its own goroutine, reading X events and sending gui -// events on c's EventChan. -func (c *conn) readSocket() { - var ( - keymap [256][]int - keysymsPerKeycode int - ) - defer close(c.eventc) - for { - // X events are always 32 bytes long. - if _, err := io.ReadFull(c.r, c.buf[:32]); err != nil { - if err != io.EOF { - c.eventc <- gui.ErrEvent{err} - } - return - } - switch c.buf[0] { - case 0x01: // Reply from a request (e.g. GetKeyboardMapping). - cookie := int(c.buf[3])<<8 | int(c.buf[2]) - if cookie != 1 { - // We issued only one request (GetKeyboardMapping) with a cookie of 1, - // so we shouldn't get any other reply from the X server. - c.eventc <- gui.ErrEvent{errors.New("x11: unexpected cookie")} - return - } - keysymsPerKeycode = int(c.buf[1]) - b := make([]int, 256*keysymsPerKeycode) - for i := range keymap { - keymap[i] = b[i*keysymsPerKeycode : (i+1)*keysymsPerKeycode] - } - for i := keymapLo; i <= keymapHi; i++ { - m := keymap[i] - for j := range m { - u, err := readU32LE(c.r, c.buf[:4]) - if err != nil { - if err != io.EOF { - c.eventc <- gui.ErrEvent{err} - } - return - } - m[j] = int(u) - } - } - case 0x02, 0x03: // Key press, key release. - // X Keyboard Encoding is documented at http://tronche.com/gui/x/xlib/input/keyboard-encoding.html - // TODO(nigeltao): Do we need to implement the "MODE SWITCH / group modifier" feature - // or is that some no-longer-used X construct? - if keysymsPerKeycode < 2 { - // Either we haven't yet received the GetKeyboardMapping reply or - // the X server has sent one that's too short. - continue - } - keycode := int(c.buf[1]) - shift := int(c.buf[28]) & 0x01 - keysym := keymap[keycode][shift] - if keysym == 0 { - keysym = keymap[keycode][0] - } - // TODO(nigeltao): Should we send KeyEvents for Shift/Ctrl/Alt? Should Shift-A send - // the same int down the channel as the sent on just the A key? - // TODO(nigeltao): How should IME events (e.g. key presses that should generate CJK text) work? Or - // is that outside the scope of the gui.Window interface? - if c.buf[0] == 0x03 { - keysym = -keysym - } - c.eventc <- gui.KeyEvent{keysym} - case 0x04, 0x05: // Button press, button release. - mask := 1 << (c.buf[1] - 1) - if c.buf[0] == 0x04 { - c.mouseState.Buttons |= mask - } else { - c.mouseState.Buttons &^= mask - } - c.mouseState.Nsec = time.Nanoseconds() - c.eventc <- c.mouseState - case 0x06: // Motion notify. - c.mouseState.Loc.X = int(int16(c.buf[25])<<8 | int16(c.buf[24])) - c.mouseState.Loc.Y = int(int16(c.buf[27])<<8 | int16(c.buf[26])) - c.mouseState.Nsec = time.Nanoseconds() - c.eventc <- c.mouseState - case 0x0c: // Expose. - // A single user action could trigger multiple expose events (e.g. if moving another - // window with XShape'd rounded corners over our window). In that case, the X server will - // send a uint16 count (in bytes 16-17) of the number of additional expose events coming. - // We could parse each event for the (x, y, width, height) and maintain a minimal dirty - // rectangle, but for now, the simplest approach is to paint the entire window, when - // receiving the final event in the series. - if c.buf[17] == 0 && c.buf[16] == 0 { - // TODO(nigeltao): Should we ignore the very first expose event? A freshly mapped window - // will trigger expose, but until the first c.FlushImage call, there's probably nothing to - // paint but black. For an 800x600 window, at 4 bytes per pixel, each repaint writes about - // 2MB over the socket. - c.FlushImage() - } - // TODO(nigeltao): Should we listen to DestroyNotify (0x11) and ResizeRequest (0x19) events? - // What about EnterNotify (0x07) and LeaveNotify (0x08)? - } - } -} - -// connect connects to the X server given by the full X11 display name (e.g. -// ":12.0") and returns the connection as well as the portion of the full name -// that is the display number (e.g. "12"). -// Examples: -// connect(":1") // calls net.Dial("unix", "", "/tmp/.X11-unix/X1"), displayStr="1" -// connect("/tmp/launch-123/:0") // calls net.Dial("unix", "", "/tmp/launch-123/:0"), displayStr="0" -// connect("hostname:2.1") // calls net.Dial("tcp", "", "hostname:6002"), displayStr="2" -// connect("tcp/hostname:1.0") // calls net.Dial("tcp", "", "hostname:6001"), displayStr="1" -func connect(display string) (conn net.Conn, displayStr string, err error) { - colonIdx := strings.LastIndex(display, ":") - if colonIdx < 0 { - return nil, "", errors.New("bad display: " + display) - } - // Parse the section before the colon. - var protocol, host, socket string - if display[0] == '/' { - socket = display[:colonIdx] - } else { - if i := strings.LastIndex(display, "/"); i < 0 { - // The default protocol is TCP. - protocol = "tcp" - host = display[:colonIdx] - } else { - protocol = display[:i] - host = display[i+1 : colonIdx] - } - } - // Parse the section after the colon. - after := display[colonIdx+1:] - if after == "" { - return nil, "", errors.New("bad display: " + display) - } - if i := strings.LastIndex(after, "."); i < 0 { - displayStr = after - } else { - displayStr = after[:i] - } - displayInt, err := strconv.Atoi(displayStr) - if err != nil || displayInt < 0 { - return nil, "", errors.New("bad display: " + display) - } - // Make the connection. - if socket != "" { - conn, err = net.Dial("unix", socket+":"+displayStr) - } else if host != "" { - conn, err = net.Dial(protocol, host+":"+strconv.Itoa(6000+displayInt)) - } else { - conn, err = net.Dial("unix", "/tmp/.X11-unix/X"+displayStr) - } - if err != nil { - return nil, "", errors.New("cannot connect to " + display + ": " + err.Error()) - } - return -} - -// authenticate authenticates ourselves with the X server. -// displayStr is the "12" out of ":12.0". -func authenticate(w *bufio.Writer, displayStr string) error { - key, value, err := readAuth(displayStr) - if err != nil { - return err - } - // Assume that the authentication protocol is "MIT-MAGIC-COOKIE-1". - if len(key) != 18 || len(value) != 16 { - return errors.New("unsupported Xauth") - } - // 0x006c means little-endian. 0x000b, 0x0000 means X major version 11, minor version 0. - // 0x0012 and 0x0010 means the auth key and value have lengths 18 and 16. - // The final 0x0000 is padding, so that the string length is a multiple of 4. - _, err = io.WriteString(w, "\x6c\x00\x0b\x00\x00\x00\x12\x00\x10\x00\x00\x00") - if err != nil { - return err - } - _, err = io.WriteString(w, key) - if err != nil { - return err - } - // Again, the 0x0000 is padding. - _, err = io.WriteString(w, "\x00\x00") - if err != nil { - return err - } - _, err = io.WriteString(w, value) - if err != nil { - return err - } - err = w.Flush() - if err != nil { - return err - } - return nil -} - -// readU8 reads a uint8 from r, using b as a scratch buffer. -func readU8(r io.Reader, b []byte) (uint8, error) { - _, err := io.ReadFull(r, b[:1]) - if err != nil { - return 0, err - } - return uint8(b[0]), nil -} - -// readU16LE reads a little-endian uint16 from r, using b as a scratch buffer. -func readU16LE(r io.Reader, b []byte) (uint16, error) { - _, err := io.ReadFull(r, b[:2]) - if err != nil { - return 0, err - } - return uint16(b[0]) | uint16(b[1])<<8, nil -} - -// readU32LE reads a little-endian uint32 from r, using b as a scratch buffer. -func readU32LE(r io.Reader, b []byte) (uint32, error) { - _, err := io.ReadFull(r, b[:4]) - if err != nil { - return 0, err - } - return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, nil -} - -// setU32LE sets b[:4] to be the little-endian representation of u. -func setU32LE(b []byte, u uint32) { - b[0] = byte((u >> 0) & 0xff) - b[1] = byte((u >> 8) & 0xff) - b[2] = byte((u >> 16) & 0xff) - b[3] = byte((u >> 24) & 0xff) -} - -// checkPixmapFormats checks that we have an agreeable X pixmap Format. -func checkPixmapFormats(r io.Reader, b []byte, n int) (agree bool, err error) { - for i := 0; i < n; i++ { - _, err = io.ReadFull(r, b[:8]) - if err != nil { - return - } - // Byte 0 is depth, byte 1 is bits-per-pixel, byte 2 is scanline-pad, the rest (5) is padding. - if b[0] == 24 && b[1] == 32 { - agree = true - } - } - return -} - -// checkDepths checks that we have an agreeable X Depth (i.e. one that has an agreeable X VisualType). -func checkDepths(r io.Reader, b []byte, n int, visual uint32) (agree bool, err error) { - for i := 0; i < n; i++ { - var depth, visualsLen uint16 - depth, err = readU16LE(r, b) - if err != nil { - return - } - depth &= 0xff - visualsLen, err = readU16LE(r, b) - if err != nil { - return - } - // Ignore 4 bytes of padding. - _, err = io.ReadFull(r, b[:4]) - if err != nil { - return - } - for j := 0; j < int(visualsLen); j++ { - // Read 24 bytes: visual(4), class(1), bits per rgb value(1), colormap entries(2), - // red mask(4), green mask(4), blue mask(4), padding(4). - v, _ := readU32LE(r, b) - _, _ = readU32LE(r, b) - rm, _ := readU32LE(r, b) - gm, _ := readU32LE(r, b) - bm, _ := readU32LE(r, b) - _, err = readU32LE(r, b) - if err != nil { - return - } - if v == visual && rm == 0xff0000 && gm == 0xff00 && bm == 0xff && depth == 24 { - agree = true - } - } - } - return -} - -// checkScreens checks that we have an agreeable X Screen. -func checkScreens(r io.Reader, b []byte, n int) (root, visual uint32, err error) { - for i := 0; i < n; i++ { - var root0, visual0, x uint32 - root0, err = readU32LE(r, b) - if err != nil { - return - } - // Ignore the next 7x4 bytes, which is: colormap, whitepixel, blackpixel, current input masks, - // width and height (pixels), width and height (mm), min and max installed maps. - _, err = io.ReadFull(r, b[:28]) - if err != nil { - return - } - visual0, err = readU32LE(r, b) - if err != nil { - return - } - // Next 4 bytes: backing stores, save unders, root depth, allowed depths length. - x, err = readU32LE(r, b) - if err != nil { - return - } - nDepths := int(x >> 24) - var agree bool - agree, err = checkDepths(r, b, nDepths, visual0) - if err != nil { - return - } - if agree && root == 0 { - root = root0 - visual = visual0 - } - } - return -} - -// handshake performs the protocol handshake with the X server, and ensures -// that the server provides a compatible Screen, Depth, etc. -func (c *conn) handshake() error { - _, err := io.ReadFull(c.r, c.buf[:8]) - if err != nil { - return err - } - // Byte 0 should be 1 (success), bytes 2:6 should be 0xb0000000 (major/minor version 11.0). - if c.buf[0] != 1 || c.buf[2] != 11 || c.buf[3] != 0 || c.buf[4] != 0 || c.buf[5] != 0 { - return errors.New("unsupported X version") - } - // Ignore the release number. - _, err = io.ReadFull(c.r, c.buf[:4]) - if err != nil { - return err - } - // Read the resource ID base. - resourceIdBase, err := readU32LE(c.r, c.buf[:4]) - if err != nil { - return err - } - // Read the resource ID mask. - resourceIdMask, err := readU32LE(c.r, c.buf[:4]) - if err != nil { - return err - } - if resourceIdMask < 256 { - return errors.New("X resource ID mask is too small") - } - // Ignore the motion buffer size. - _, err = io.ReadFull(c.r, c.buf[:4]) - if err != nil { - return err - } - // Read the vendor length and round it up to a multiple of 4, - // for X11 protocol alignment reasons. - vendorLen, err := readU16LE(c.r, c.buf[:2]) - if err != nil { - return err - } - vendorLen = (vendorLen + 3) &^ 3 - // Read the maximum request length. - maxReqLen, err := readU16LE(c.r, c.buf[:2]) - if err != nil { - return err - } - if maxReqLen != 0xffff { - return errors.New("unsupported X maximum request length") - } - // Read the roots length. - rootsLen, err := readU8(c.r, c.buf[:1]) - if err != nil { - return err - } - // Read the pixmap formats length. - pixmapFormatsLen, err := readU8(c.r, c.buf[:1]) - if err != nil { - return err - } - // Ignore some things that we don't care about (totaling 10 + vendorLen bytes): - // imageByteOrder(1), bitmapFormatBitOrder(1), bitmapFormatScanlineUnit(1) bitmapFormatScanlinePad(1), - // minKeycode(1), maxKeycode(1), padding(4), vendor (vendorLen). - if 10+int(vendorLen) > cap(c.buf) { - return errors.New("unsupported X vendor") - } - _, err = io.ReadFull(c.r, c.buf[:10+int(vendorLen)]) - if err != nil { - return err - } - // Check that we have an agreeable pixmap format. - agree, err := checkPixmapFormats(c.r, c.buf[:8], int(pixmapFormatsLen)) - if err != nil { - return err - } - if !agree { - return errors.New("unsupported X pixmap formats") - } - // Check that we have an agreeable screen. - root, visual, err := checkScreens(c.r, c.buf[:24], int(rootsLen)) - if err != nil { - return err - } - if root == 0 || visual == 0 { - return errors.New("unsupported X screen") - } - c.gc = resID(resourceIdBase) - c.window = resID(resourceIdBase + 1) - c.root = resID(root) - c.visual = resID(visual) - return nil -} - -// NewWindow calls NewWindowDisplay with $DISPLAY. -func NewWindow() (gui.Window, error) { - display := os.Getenv("DISPLAY") - if len(display) == 0 { - return nil, errors.New("$DISPLAY not set") - } - return NewWindowDisplay(display) -} - -// NewWindowDisplay returns a new gui.Window, backed by a newly created and -// mapped X11 window. The X server to connect to is specified by the display -// string, such as ":1". -func NewWindowDisplay(display string) (gui.Window, error) { - socket, displayStr, err := connect(display) - if err != nil { - return nil, err - } - c := new(conn) - c.c = socket - c.r = bufio.NewReader(socket) - c.w = bufio.NewWriter(socket) - err = authenticate(c.w, displayStr) - if err != nil { - return nil, err - } - err = c.handshake() - if err != nil { - return nil, err - } - - // Now that we're connected, show a window, via three X protocol messages. - // First, issue a GetKeyboardMapping request. This is the first request, and - // will be associated with a cookie of 1. - setU32LE(c.buf[0:4], 0x00020065) // 0x65 is the GetKeyboardMapping opcode, and the message is 2 x 4 bytes long. - setU32LE(c.buf[4:8], uint32((keymapHi-keymapLo+1)<<8|keymapLo)) - // Second, create a graphics context (GC). - setU32LE(c.buf[8:12], 0x00060037) // 0x37 is the CreateGC opcode, and the message is 6 x 4 bytes long. - setU32LE(c.buf[12:16], uint32(c.gc)) - setU32LE(c.buf[16:20], uint32(c.root)) - setU32LE(c.buf[20:24], 0x00010004) // Bit 2 is XCB_GC_FOREGROUND, bit 16 is XCB_GC_GRAPHICS_EXPOSURES. - setU32LE(c.buf[24:28], 0x00000000) // The Foreground is black. - setU32LE(c.buf[28:32], 0x00000000) // GraphicsExposures' value is unused. - // Third, create the window. - setU32LE(c.buf[32:36], 0x000a0001) // 0x01 is the CreateWindow opcode, and the message is 10 x 4 bytes long. - setU32LE(c.buf[36:40], uint32(c.window)) - setU32LE(c.buf[40:44], uint32(c.root)) - setU32LE(c.buf[44:48], 0x00000000) // Initial (x, y) is (0, 0). - setU32LE(c.buf[48:52], windowHeight<<16|windowWidth) - setU32LE(c.buf[52:56], 0x00010000) // Border width is 0, XCB_WINDOW_CLASS_INPUT_OUTPUT is 1. - setU32LE(c.buf[56:60], uint32(c.visual)) - setU32LE(c.buf[60:64], 0x00000802) // Bit 1 is XCB_CW_BACK_PIXEL, bit 11 is XCB_CW_EVENT_MASK. - setU32LE(c.buf[64:68], 0x00000000) // The Back-Pixel is black. - setU32LE(c.buf[68:72], 0x0000804f) // Key/button press and release, pointer motion, and expose event masks. - // Fourth, map the window. - setU32LE(c.buf[72:76], 0x00020008) // 0x08 is the MapWindow opcode, and the message is 2 x 4 bytes long. - setU32LE(c.buf[76:80], uint32(c.window)) - // Write the bytes. - _, err = c.w.Write(c.buf[:80]) - if err != nil { - return nil, err - } - err = c.w.Flush() - if err != nil { - return nil, err - } - - c.img = image.NewRGBA(image.Rect(0, 0, windowWidth, windowHeight)) - c.eventc = make(chan interface{}, 16) - c.flush = make(chan bool, 1) - go c.readSocket() - go c.writeSocket() - return c, nil -} diff --git a/libgo/go/exp/inotify/inotify_linux.go b/libgo/go/exp/inotify/inotify_linux.go index d6b7e8514e6..f12436618f2 100644 --- a/libgo/go/exp/inotify/inotify_linux.go +++ b/libgo/go/exp/inotify/inotify_linux.go @@ -105,9 +105,9 @@ func (w *Watcher) AddWatch(path string, flags uint32) error { watchEntry.flags |= flags flags |= syscall.IN_MASK_ADD } - wd, errno := syscall.InotifyAddWatch(w.fd, path, flags) - if wd == -1 { - return &os.PathError{"inotify_add_watch", path, os.Errno(errno)} + wd, err := syscall.InotifyAddWatch(w.fd, path, flags) + if err != nil { + return &os.PathError{"inotify_add_watch", path, err} } if !found { @@ -139,14 +139,10 @@ func (w *Watcher) RemoveWatch(path string) error { // readEvents reads from the inotify file descriptor, converts the // received events into Event objects and sends them via the Event channel func (w *Watcher) readEvents() { - var ( - buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events - n int // Number of bytes read with read() - errno int // Syscall errno - ) + var buf [syscall.SizeofInotifyEvent * 4096]byte for { - n, errno = syscall.Read(w.fd, buf[0:]) + n, err := syscall.Read(w.fd, buf[0:]) // See if there is a message on the "done" channel var done bool select { @@ -156,16 +152,16 @@ func (w *Watcher) readEvents() { // If EOF or a "done" message is received if n == 0 || done { - errno := syscall.Close(w.fd) - if errno == -1 { - w.Error <- os.NewSyscallError("close", errno) + err := syscall.Close(w.fd) + if err != nil { + w.Error <- os.NewSyscallError("close", err) } close(w.Event) close(w.Error) return } if n < 0 { - w.Error <- os.NewSyscallError("read", errno) + w.Error <- os.NewSyscallError("read", err) continue } if n < syscall.SizeofInotifyEvent { diff --git a/libgo/go/exp/sql/convert.go b/libgo/go/exp/sql/convert.go index e46cebe9a3d..48e281203be 100644 --- a/libgo/go/exp/sql/convert.go +++ b/libgo/go/exp/sql/convert.go @@ -14,6 +14,21 @@ import ( "strconv" ) +// subsetTypeArgs takes a slice of arguments from callers of the sql +// package and converts them into a slice of the driver package's +// "subset types". +func subsetTypeArgs(args []interface{}) ([]interface{}, error) { + out := make([]interface{}, len(args)) + for n, arg := range args { + var err error + out[n], err = driver.DefaultParameterConverter.ConvertValue(arg) + if err != nil { + return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n+1, err) + } + } + return out, nil +} + // convertAssign copies to dest the value in src, converting it if possible. // An error is returned if the copy would result in loss of information. // dest should be a pointer type. diff --git a/libgo/go/exp/sql/driver/driver.go b/libgo/go/exp/sql/driver/driver.go index 6a51c342415..f0bcca29106 100644 --- a/libgo/go/exp/sql/driver/driver.go +++ b/libgo/go/exp/sql/driver/driver.go @@ -7,7 +7,7 @@ // // Code simply using databases should use package sql. // -// Drivers only need to be aware of a subset of Go's types. The db package +// Drivers only need to be aware of a subset of Go's types. The sql package // will convert all types into one of the following: // // int64 @@ -36,19 +36,22 @@ type Driver interface { Open(name string) (Conn, error) } -// Execer is an optional interface that may be implemented by a Driver -// or a Conn. -// -// If a Driver does not implement Execer, the sql package's DB.Exec -// method first obtains a free connection from its free pool or from -// the driver's Open method. Execer should only be implemented by -// drivers that can provide a more efficient implementation. +// ErrSkip may be returned by some optional interfaces' methods to +// indicate at runtime that the fast path is unavailable and the sql +// package should continue as if the optional interface was not +// implemented. ErrSkip is only supported where explicitly +// documented. +var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented") + +// Execer is an optional interface that may be implemented by a Conn. // // If a Conn does not implement Execer, the db package's DB.Exec will // first prepare a query, execute the statement, and then close the // statement. // // All arguments are of a subset type as defined in the package docs. +// +// Exec may return ErrSkip. type Execer interface { Exec(query string, args []interface{}) (Result, error) } @@ -91,9 +94,35 @@ type Result interface { // used by multiple goroutines concurrently. type Stmt interface { // Close closes the statement. + // + // Closing a statement should not interrupt any outstanding + // query created from that statement. That is, the following + // order of operations is valid: + // + // * create a driver statement + // * call Query on statement, returning Rows + // * close the statement + // * read from Rows + // + // If closing a statement invalidates currently-running + // queries, the final step above will incorrectly fail. + // + // TODO(bradfitz): possibly remove the restriction above, if + // enough driver authors object and find it complicates their + // code too much. The sql package could be smarter about + // refcounting the statement and closing it at the appropriate + // time. Close() error // NumInput returns the number of placeholder parameters. + // + // If NumInput returns >= 0, the sql package will sanity check + // argument counts from callers and return errors to the caller + // before the statement's Exec or Query methods are called. + // + // NumInput may also return -1, if the driver doesn't know + // its number of placeholders. In that case, the sql package + // will not sanity check Exec or Query argument counts. NumInput() int // Exec executes a query that doesn't return rows, such @@ -135,6 +164,8 @@ type Rows interface { // The dest slice may be populated with only with values // of subset types defined above, but excluding string. // All string values must be converted to []byte. + // + // Next should return io.EOF when there are no more rows. Next(dest []interface{}) error } diff --git a/libgo/go/exp/sql/fakedb_test.go b/libgo/go/exp/sql/fakedb_test.go index c8a19974d64..2474a86f644 100644 --- a/libgo/go/exp/sql/fakedb_test.go +++ b/libgo/go/exp/sql/fakedb_test.go @@ -90,6 +90,8 @@ type fakeStmt struct { cmd string table string + closed bool + colName []string // used by CREATE, INSERT, SELECT (selected columns) colType []string // used by CREATE colValue []interface{} // used by INSERT (mix of strings and "?" for bound params) @@ -195,6 +197,29 @@ func (c *fakeConn) Close() error { return nil } +func checkSubsetTypes(args []interface{}) error { + for n, arg := range args { + switch arg.(type) { + case int64, float64, bool, nil, []byte, string: + default: + return fmt.Errorf("fakedb_test: invalid argument #%d: %v, type %T", n+1, arg, arg) + } + } + return nil +} + +func (c *fakeConn) Exec(query string, args []interface{}) (driver.Result, error) { + // This is an optional interface, but it's implemented here + // just to check that all the args of of the proper types. + // ErrSkip is returned so the caller acts as if we didn't + // implement this at all. + err := checkSubsetTypes(args) + if err != nil { + return nil, err + } + return nil, driver.ErrSkip +} + func errf(msg string, args ...interface{}) error { return errors.New("fakedb: " + fmt.Sprintf(msg, args...)) } @@ -209,6 +234,9 @@ func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, e stmt.table = parts[0] stmt.colName = strings.Split(parts[1], ",") for n, colspec := range strings.Split(parts[2], ",") { + if colspec == "" { + continue + } nameVal := strings.Split(colspec, "=") if len(nameVal) != 2 { return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n) @@ -319,10 +347,21 @@ func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter { } func (s *fakeStmt) Close() error { + s.closed = true return nil } +var errClosed = errors.New("fakedb: statement has been closed") + func (s *fakeStmt) Exec(args []interface{}) (driver.Result, error) { + if s.closed { + return nil, errClosed + } + err := checkSubsetTypes(args) + if err != nil { + return nil, err + } + db := s.c.db switch s.cmd { case "WIPE": @@ -377,6 +416,14 @@ func (s *fakeStmt) execInsert(args []interface{}) (driver.Result, error) { } func (s *fakeStmt) Query(args []interface{}) (driver.Rows, error) { + if s.closed { + return nil, errClosed + } + err := checkSubsetTypes(args) + if err != nil { + return nil, err + } + db := s.c.db if len(args) != s.placeholders { panic("error in pkg db; should only get here if size is correct") diff --git a/libgo/go/exp/sql/sql.go b/libgo/go/exp/sql/sql.go index 291af7f67dc..f17d12eaa13 100644 --- a/libgo/go/exp/sql/sql.go +++ b/libgo/go/exp/sql/sql.go @@ -88,8 +88,9 @@ type DB struct { driver driver.Driver dsn string - mu sync.Mutex + mu sync.Mutex // protects freeConn and closed freeConn []driver.Conn + closed bool } // Open opens a database specified by its database driver name and a @@ -106,6 +107,22 @@ func Open(driverName, dataSourceName string) (*DB, error) { return &DB{driver: driver, dsn: dataSourceName}, nil } +// Close closes the database, releasing any open resources. +func (db *DB) Close() error { + db.mu.Lock() + defer db.mu.Unlock() + var err error + for _, c := range db.freeConn { + err1 := c.Close() + if err1 != nil { + err = err1 + } + } + db.freeConn = nil + db.closed = true + return err +} + func (db *DB) maxIdleConns() int { const defaultMaxIdleConns = 2 // TODO(bradfitz): ask driver, if supported, for its default preference @@ -116,6 +133,9 @@ func (db *DB) maxIdleConns() int { // conn returns a newly-opened or cached driver.Conn func (db *DB) conn() (driver.Conn, error) { db.mu.Lock() + if db.closed { + return nil, errors.New("sql: database is closed") + } if n := len(db.freeConn); n > 0 { conn := db.freeConn[n-1] db.freeConn = db.freeConn[:n-1] @@ -140,11 +160,13 @@ func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) { } func (db *DB) putConn(c driver.Conn) { - if n := len(db.freeConn); n < db.maxIdleConns() { + db.mu.Lock() + defer db.mu.Unlock() + if n := len(db.freeConn); !db.closed && n < db.maxIdleConns() { db.freeConn = append(db.freeConn, c) return } - db.closeConn(c) + db.closeConn(c) // TODO(bradfitz): release lock before calling this? } func (db *DB) closeConn(c driver.Conn) { @@ -180,17 +202,11 @@ func (db *DB) Prepare(query string) (*Stmt, error) { // Exec executes a query without returning any rows. func (db *DB) Exec(query string, args ...interface{}) (Result, error) { - // Optional fast path, if the driver implements driver.Execer. - if execer, ok := db.driver.(driver.Execer); ok { - resi, err := execer.Exec(query, args) - if err != nil { - return nil, err - } - return result{resi}, nil + sargs, err := subsetTypeArgs(args) + if err != nil { + return nil, err } - // If the driver does not implement driver.Execer, we need - // a connection. ci, err := db.conn() if err != nil { return nil, err @@ -198,11 +214,13 @@ func (db *DB) Exec(query string, args ...interface{}) (Result, error) { defer db.putConn(ci) if execer, ok := ci.(driver.Execer); ok { - resi, err := execer.Exec(query, args) - if err != nil { - return nil, err + resi, err := execer.Exec(query, sargs) + if err != driver.ErrSkip { + if err != nil { + return nil, err + } + return result{resi}, nil } - return result{resi}, nil } sti, err := ci.Prepare(query) @@ -210,7 +228,8 @@ func (db *DB) Exec(query string, args ...interface{}) (Result, error) { return nil, err } defer sti.Close() - resi, err := sti.Exec(args) + + resi, err := sti.Exec(sargs) if err != nil { return nil, err } @@ -325,25 +344,26 @@ func (tx *Tx) Rollback() error { return tx.txi.Rollback() } -// Prepare creates a prepared statement. +// Prepare creates a prepared statement for use within a transaction. // -// The statement is only valid within the scope of this transaction. +// The returned statement operates within the transaction and can no longer +// be used once the transaction has been committed or rolled back. +// +// To use an existing prepared statement on this transaction, see Tx.Stmt. func (tx *Tx) Prepare(query string) (*Stmt, error) { - // TODO(bradfitz): the restriction that the returned statement - // is only valid for this Transaction is lame and negates a - // lot of the benefit of prepared statements. We could be - // more efficient here and either provide a method to take an - // existing Stmt (created on perhaps a different Conn), and - // re-create it on this Conn if necessary. Or, better: keep a - // map in DB of query string to Stmts, and have Stmt.Execute - // do the right thing and re-prepare if the Conn in use - // doesn't have that prepared statement. But we'll want to - // avoid caching the statement in the case where we only call - // conn.Prepare implicitly (such as in db.Exec or tx.Exec), - // but the caller package can't be holding a reference to the - // returned statement. Perhaps just looking at the reference - // count (by noting Stmt.Close) would be enough. We might also - // want a finalizer on Stmt to drop the reference count. + // TODO(bradfitz): We could be more efficient here and either + // provide a method to take an existing Stmt (created on + // perhaps a different Conn), and re-create it on this Conn if + // necessary. Or, better: keep a map in DB of query string to + // Stmts, and have Stmt.Execute do the right thing and + // re-prepare if the Conn in use doesn't have that prepared + // statement. But we'll want to avoid caching the statement + // in the case where we only call conn.Prepare implicitly + // (such as in db.Exec or tx.Exec), but the caller package + // can't be holding a reference to the returned statement. + // Perhaps just looking at the reference count (by noting + // Stmt.Close) would be enough. We might also want a finalizer + // on Stmt to drop the reference count. ci, err := tx.grabConn() if err != nil { return nil, err @@ -364,6 +384,39 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) { return stmt, nil } +// Stmt returns a transaction-specific prepared statement from +// an existing statement. +// +// Example: +// updateMoney, err := db.Prepare("UPDATE balance SET money=money+? WHERE id=?") +// ... +// tx, err := db.Begin() +// ... +// res, err := tx.Stmt(updateMoney).Exec(123.45, 98293203) +func (tx *Tx) Stmt(stmt *Stmt) *Stmt { + // TODO(bradfitz): optimize this. Currently this re-prepares + // each time. This is fine for now to illustrate the API but + // we should really cache already-prepared statements + // per-Conn. See also the big comment in Tx.Prepare. + + if tx.db != stmt.db { + return &Stmt{stickyErr: errors.New("sql: Tx.Stmt: statement from different database used")} + } + ci, err := tx.grabConn() + if err != nil { + return &Stmt{stickyErr: err} + } + defer tx.releaseConn() + si, err := ci.Prepare(stmt.query) + return &Stmt{ + db: tx.db, + tx: tx, + txsi: si, + query: stmt.query, + stickyErr: err, + } +} + // Exec executes a query that doesn't return rows. // For example: an INSERT and UPDATE. func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) { @@ -386,7 +439,13 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) { return nil, err } defer sti.Close() - resi, err := sti.Exec(args) + + sargs, err := subsetTypeArgs(args) + if err != nil { + return nil, err + } + + resi, err := sti.Exec(sargs) if err != nil { return nil, err } @@ -423,8 +482,9 @@ type connStmt struct { // Stmt is a prepared statement. Stmt is safe for concurrent use by multiple goroutines. type Stmt struct { // Immutable: - db *DB // where we came from - query string // that created the Sttm + db *DB // where we came from + query string // that created the Stmt + stickyErr error // if non-nil, this error is returned for all operations // If in a transaction, else both nil: tx *Tx @@ -449,7 +509,10 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) { } defer releaseConn() - if want := si.NumInput(); len(args) != want { + // -1 means the driver doesn't know how to count the number of + // 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)) } @@ -485,6 +548,9 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) { // statement, a function to call to release the connection, and a // statement bound to that connection. func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(), si driver.Stmt, err error) { + if s.stickyErr != nil { + return nil, nil, nil, s.stickyErr + } s.mu.Lock() if s.closed { s.mu.Unlock() @@ -545,10 +611,18 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) { if err != nil { return nil, err } - if len(args) != si.NumInput() { + + // -1 means the driver doesn't know how to count the number of + // 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)) } - rowsi, err := si.Query(args) + sargs, err := subsetTypeArgs(args) + if err != nil { + return nil, err + } + rowsi, err := si.Query(sargs) if err != nil { s.db.putConn(ci) return nil, err @@ -585,6 +659,9 @@ func (s *Stmt) QueryRow(args ...interface{}) *Row { // Close closes the statement. func (s *Stmt) Close() error { + if s.stickyErr != nil { + return s.stickyErr + } s.mu.Lock() defer s.mu.Unlock() if s.closed { diff --git a/libgo/go/exp/sql/sql_test.go b/libgo/go/exp/sql/sql_test.go index eb1bb58966e..4f8318d26ef 100644 --- a/libgo/go/exp/sql/sql_test.go +++ b/libgo/go/exp/sql/sql_test.go @@ -5,6 +5,7 @@ package sql import ( + "reflect" "strings" "testing" ) @@ -22,7 +23,6 @@ func newTestDB(t *testing.T, name string) *DB { exec(t, db, "INSERT|people|name=Alice,age=?", 1) exec(t, db, "INSERT|people|name=Bob,age=?", 2) exec(t, db, "INSERT|people|name=Chris,age=?", 3) - } return db } @@ -34,8 +34,50 @@ func exec(t *testing.T, db *DB, query string, args ...interface{}) { } } +func closeDB(t *testing.T, db *DB) { + err := db.Close() + if err != nil { + t.Fatalf("error closing DB: %v", err) + } +} + func TestQuery(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) + } + type row struct { + age int + name string + } + got := []row{} + for rows.Next() { + var r row + err = rows.Scan(&r.age, &r.name) + if err != nil { + t.Fatalf("Scan: %v", err) + } + got = append(got, r) + } + err = rows.Err() + if err != nil { + t.Fatalf("Err: %v", err) + } + want := []row{ + {age: 1, name: "Alice"}, + {age: 2, name: "Bob"}, + {age: 3, name: "Chris"}, + } + if !reflect.DeepEqual(got, want) { + t.Logf(" got: %#v\nwant: %#v", got, want) + } +} + +func TestQueryRow(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) var name string var age int @@ -67,8 +109,27 @@ func TestQuery(t *testing.T) { } } +func TestStatementErrorAfterClose(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + stmt, err := db.Prepare("SELECT|people|age|name=?") + if err != nil { + t.Fatalf("Prepare: %v", err) + } + err = stmt.Close() + if err != nil { + t.Fatalf("Close: %v", err) + } + var name string + err = stmt.QueryRow("foo").Scan(&name) + if err == nil { + t.Errorf("expected error from QueryRow.Scan after Stmt.Close") + } +} + func TestStatementQueryRow(t *testing.T) { db := newTestDB(t, "people") + defer closeDB(t, db) stmt, err := db.Prepare("SELECT|people|age|name=?") if err != nil { t.Fatalf("Prepare: %v", err) @@ -94,6 +155,7 @@ func TestStatementQueryRow(t *testing.T) { // just a test of fakedb itself func TestBogusPreboundParameters(t *testing.T) { db := newTestDB(t, "foo") + defer closeDB(t, db) exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") _, err := db.Prepare("INSERT|t1|name=?,age=bogusconversion") if err == nil { @@ -104,8 +166,9 @@ func TestBogusPreboundParameters(t *testing.T) { } } -func TestDb(t *testing.T) { +func TestExec(t *testing.T) { db := newTestDB(t, "foo") + defer closeDB(t, db) exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") stmt, err := db.Prepare("INSERT|t1|name=?,age=?") if err != nil { @@ -143,3 +206,25 @@ func TestDb(t *testing.T) { } } } + +func TestTxStmt(t *testing.T) { + db := newTestDB(t, "") + defer closeDB(t, db) + exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") + stmt, err := db.Prepare("INSERT|t1|name=?,age=?") + if err != nil { + t.Fatalf("Stmt, err = %v, %v", stmt, err) + } + tx, err := db.Begin() + if err != nil { + t.Fatalf("Begin = %v", err) + } + _, err = tx.Stmt(stmt).Exec("Bobby", 7) + if err != nil { + t.Fatalf("Exec = %v", err) + } + err = tx.Commit() + if err != nil { + t.Fatalf("Commit = %v", err) + } +} diff --git a/libgo/go/exp/ssh/channel.go b/libgo/go/exp/ssh/channel.go index 6ff8203ce27..9d75f37de74 100644 --- a/libgo/go/exp/ssh/channel.go +++ b/libgo/go/exp/ssh/channel.go @@ -244,13 +244,13 @@ func (c *channel) Write(data []byte) (n int, err error) { packet := make([]byte, 1+4+4+len(todo)) packet[0] = msgChannelData - packet[1] = byte(c.theirId) >> 24 - packet[2] = byte(c.theirId) >> 16 - packet[3] = byte(c.theirId) >> 8 + packet[1] = byte(c.theirId >> 24) + packet[2] = byte(c.theirId >> 16) + packet[3] = byte(c.theirId >> 8) packet[4] = byte(c.theirId) - packet[5] = byte(len(todo)) >> 24 - packet[6] = byte(len(todo)) >> 16 - packet[7] = byte(len(todo)) >> 8 + packet[5] = byte(len(todo) >> 24) + packet[6] = byte(len(todo) >> 16) + packet[7] = byte(len(todo) >> 8) packet[8] = byte(len(todo)) copy(packet[9:], todo) diff --git a/libgo/go/exp/ssh/cipher.go b/libgo/go/exp/ssh/cipher.go new file mode 100644 index 00000000000..de4926d7b81 --- /dev/null +++ b/libgo/go/exp/ssh/cipher.go @@ -0,0 +1,88 @@ +// 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 ssh + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rc4" +) + +// streamDump is used to dump the initial keystream for stream ciphers. It is a +// a write-only buffer, and not intended for reading so do not require a mutex. +var streamDump [512]byte + +// noneCipher implements cipher.Stream and provides no encryption. It is used +// by the transport before the first key-exchange. +type noneCipher struct{} + +func (c noneCipher) XORKeyStream(dst, src []byte) { + copy(dst, src) +} + +func newAESCTR(key, iv []byte) (cipher.Stream, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return cipher.NewCTR(c, iv), nil +} + +func newRC4(key, iv []byte) (cipher.Stream, error) { + return rc4.NewCipher(key) +} + +type cipherMode struct { + keySize int + ivSize int + skip int + createFn func(key, iv []byte) (cipher.Stream, error) +} + +func (c *cipherMode) createCipher(key, iv []byte) (cipher.Stream, error) { + if len(key) < c.keySize { + panic("ssh: key length too small for cipher") + } + if len(iv) < c.ivSize { + panic("ssh: iv too small for cipher") + } + + stream, err := c.createFn(key[:c.keySize], iv[:c.ivSize]) + if err != nil { + return nil, err + } + + for remainingToDump := c.skip; remainingToDump > 0; { + dumpThisTime := remainingToDump + if dumpThisTime > len(streamDump) { + dumpThisTime = len(streamDump) + } + stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime]) + remainingToDump -= dumpThisTime + } + + return stream, nil +} + +// Specifies a default set of ciphers and a preference order. This is based on +// OpenSSH's default client preference order, minus algorithms that are not +// implemented. +var DefaultCipherOrder = []string{ + "aes128-ctr", "aes192-ctr", "aes256-ctr", + "arcfour256", "arcfour128", +} + +var cipherModes = map[string]*cipherMode{ + // Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms + // are defined in the order specified in the RFC. + "aes128-ctr": &cipherMode{16, aes.BlockSize, 0, newAESCTR}, + "aes192-ctr": &cipherMode{24, aes.BlockSize, 0, newAESCTR}, + "aes256-ctr": &cipherMode{32, aes.BlockSize, 0, newAESCTR}, + + // Ciphers from RFC4345, which introduces security-improved arcfour ciphers. + // They are defined in the order specified in the RFC. + "arcfour128": &cipherMode{16, 0, 1536, newRC4}, + "arcfour256": &cipherMode{32, 0, 1536, newRC4}, +} diff --git a/libgo/go/exp/ssh/cipher_test.go b/libgo/go/exp/ssh/cipher_test.go new file mode 100644 index 00000000000..ea27bd8a803 --- /dev/null +++ b/libgo/go/exp/ssh/cipher_test.go @@ -0,0 +1,62 @@ +// 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 ssh + +import ( + "bytes" + "testing" +) + +// TestCipherReversal tests that each cipher factory produces ciphers that can +// encrypt and decrypt some data successfully. +func TestCipherReversal(t *testing.T) { + testData := []byte("abcdefghijklmnopqrstuvwxyz012345") + testKey := []byte("AbCdEfGhIjKlMnOpQrStUvWxYz012345") + testIv := []byte("sdflkjhsadflkjhasdflkjhsadfklhsa") + + cryptBuffer := make([]byte, 32) + + for name, cipherMode := range cipherModes { + encrypter, err := cipherMode.createCipher(testKey, testIv) + if err != nil { + t.Errorf("failed to create encrypter for %q: %s", name, err) + continue + } + decrypter, err := cipherMode.createCipher(testKey, testIv) + if err != nil { + t.Errorf("failed to create decrypter for %q: %s", name, err) + continue + } + + copy(cryptBuffer, testData) + + encrypter.XORKeyStream(cryptBuffer, cryptBuffer) + if name == "none" { + if !bytes.Equal(cryptBuffer, testData) { + t.Errorf("encryption made change with 'none' cipher") + continue + } + } else { + if bytes.Equal(cryptBuffer, testData) { + t.Errorf("encryption made no change with %q", name) + continue + } + } + + decrypter.XORKeyStream(cryptBuffer, cryptBuffer) + if !bytes.Equal(cryptBuffer, testData) { + t.Errorf("decrypted bytes not equal to input with %q", name) + continue + } + } +} + +func TestDefaultCiphersExist(t *testing.T) { + for _, cipherAlgo := range DefaultCipherOrder { + if _, ok := cipherModes[cipherAlgo]; !ok { + t.Errorf("default cipher %q is unknown", cipherAlgo) + } + } +} diff --git a/libgo/go/exp/ssh/client.go b/libgo/go/exp/ssh/client.go index da45688eee0..429dee975bc 100644 --- a/libgo/go/exp/ssh/client.go +++ b/libgo/go/exp/ssh/client.go @@ -35,10 +35,6 @@ func Client(c net.Conn, config *ClientConfig) (*ClientConn, error) { conn.Close() return nil, err } - if err := conn.authenticate(); err != nil { - conn.Close() - return nil, err - } go conn.mainLoop() return conn, nil } @@ -64,8 +60,8 @@ func (c *ClientConn) handshake() error { clientKexInit := kexInitMsg{ KexAlgos: supportedKexAlgos, ServerHostKeyAlgos: supportedHostKeyAlgos, - CiphersClientServer: supportedCiphers, - CiphersServerClient: supportedCiphers, + CiphersClientServer: c.config.Crypto.ciphers(), + CiphersServerClient: c.config.Crypto.ciphers(), MACsClientServer: supportedMACs, MACsServerClient: supportedMACs, CompressionClientServer: supportedCompressions, @@ -128,7 +124,10 @@ func (c *ClientConn) handshake() error { if packet[0] != msgNewKeys { return UnexpectedMessageError{msgNewKeys, packet[0]} } - return c.transport.reader.setupKeys(serverKeys, K, H, H, hashFunc) + if err := c.transport.reader.setupKeys(serverKeys, K, H, H, hashFunc); err != nil { + return err + } + return c.authenticate(H) } // kexDH performs Diffie-Hellman key agreement on a ClientConn. The @@ -173,39 +172,12 @@ func (c *ClientConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha marshalInt(K, kInt) h.Write(K) - H := h.Sum() + H := h.Sum(nil) return H, K, nil } -// openChan opens a new client channel. The most common session type is "session". -// The full set of valid session types are listed in RFC 4250 4.9.1. -func (c *ClientConn) openChan(typ string) (*clientChan, error) { - ch := c.newChan(c.transport) - if err := c.writePacket(marshal(msgChannelOpen, channelOpenMsg{ - ChanType: typ, - PeersId: ch.id, - PeersWindow: 1 << 14, - MaxPacketSize: 1 << 15, // RFC 4253 6.1 - })); err != nil { - c.chanlist.remove(ch.id) - return nil, err - } - // wait for response - switch msg := (<-ch.msg).(type) { - case *channelOpenConfirmMsg: - ch.peersId = msg.MyId - case *channelOpenFailureMsg: - c.chanlist.remove(ch.id) - return nil, errors.New(msg.Message) - default: - c.chanlist.remove(ch.id) - return nil, errors.New("Unexpected packet") - } - return ch, nil -} - -// mainloop reads incoming messages and routes channel messages +// mainLoop reads incoming messages and routes channel messages // to their respective ClientChans. func (c *ClientConn) mainLoop() { // TODO(dfc) signal the underlying close to all channels @@ -271,7 +243,7 @@ func (c *ClientConn) mainLoop() { case *windowAdjustMsg: c.getChan(msg.PeersId).win <- int(msg.AdditionalBytes) default: - fmt.Printf("mainLoop: unhandled %#v\n", msg) + fmt.Printf("mainLoop: unhandled message %T: %v\n", msg, msg) } } } @@ -301,6 +273,9 @@ type ClientConfig struct { // A slice of ClientAuth methods. Only the first instance // of a particular RFC 4252 method will be used during authentication. Auth []ClientAuth + + // Cryptographic-related configuration. + Crypto CryptoConfig } func (c *ClientConfig) rand() io.Reader { @@ -335,27 +310,16 @@ func newClientChan(t *transport, id uint32) *clientChan { // Close closes the channel. This does not close the underlying connection. func (c *clientChan) Close() error { return c.writePacket(marshal(msgChannelClose, channelCloseMsg{ - PeersId: c.id, + PeersId: c.peersId, })) } -func (c *clientChan) sendChanReq(req channelRequestMsg) error { - if err := c.writePacket(marshal(msgChannelRequest, req)); err != nil { - return err - } - msg := <-c.msg - if _, ok := msg.(*channelRequestSuccessMsg); ok { - return nil - } - return fmt.Errorf("failed to complete request: %s, %#v", req.Request, msg) -} - // Thread safe channel list. type chanlist struct { // protects concurrent access to chans sync.Mutex // chans are indexed by the local id of the channel, clientChan.id. - // The PeersId value of messages received by ClientConn.mainloop is + // The PeersId value of messages received by ClientConn.mainLoop is // used to locate the right local clientChan in this slice. chans []*clientChan } @@ -392,7 +356,7 @@ func (c *chanlist) remove(id uint32) { // A chanWriter represents the stdin of a remote process. type chanWriter struct { win chan int // receives window adjustments - id uint32 // this channel's id + peersId uint32 // the peer's id rwin int // current rwin size packetWriter // for sending channelDataMsg } @@ -411,8 +375,8 @@ func (w *chanWriter) Write(data []byte) (n int, err error) { n = len(data) packet := make([]byte, 0, 9+n) packet = append(packet, msgChannelData, - byte(w.id)>>24, byte(w.id)>>16, byte(w.id)>>8, byte(w.id), - byte(n)>>24, byte(n)>>16, byte(n)>>8, byte(n)) + byte(w.peersId>>24), byte(w.peersId>>16), byte(w.peersId>>8), byte(w.peersId), + byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) err = w.writePacket(append(packet, data...)) w.rwin -= n return @@ -421,7 +385,7 @@ func (w *chanWriter) Write(data []byte) (n int, err error) { } func (w *chanWriter) Close() error { - return w.writePacket(marshal(msgChannelEOF, channelEOFMsg{w.id})) + return w.writePacket(marshal(msgChannelEOF, channelEOFMsg{w.peersId})) } // A chanReader represents stdout or stderr of a remote process. @@ -430,8 +394,8 @@ type chanReader struct { // If writes to this channel block, they will block mainLoop, making // it unable to receive new messages from the remote side. data chan []byte // receives data from remote - id uint32 - packetWriter // for sending windowAdjustMsg + peersId uint32 // the peer's id + packetWriter // for sending windowAdjustMsg buf []byte } @@ -443,7 +407,7 @@ func (r *chanReader) Read(data []byte) (int, error) { n := copy(data, r.buf) r.buf = r.buf[n:] msg := windowAdjustMsg{ - PeersId: r.id, + PeersId: r.peersId, AdditionalBytes: uint32(n), } return n, r.writePacket(marshal(msgChannelWindowAdjust, msg)) @@ -455,7 +419,3 @@ func (r *chanReader) Read(data []byte) (int, error) { } panic("unreachable") } - -func (r *chanReader) Close() error { - return r.writePacket(marshal(msgChannelEOF, channelEOFMsg{r.id})) -} diff --git a/libgo/go/exp/ssh/client_auth.go b/libgo/go/exp/ssh/client_auth.go index 0089d0c769e..25f9e216225 100644 --- a/libgo/go/exp/ssh/client_auth.go +++ b/libgo/go/exp/ssh/client_auth.go @@ -6,10 +6,11 @@ package ssh import ( "errors" + "io" ) // authenticate authenticates with the remote server. See RFC 4252. -func (c *ClientConn) authenticate() error { +func (c *ClientConn) authenticate(session []byte) error { // initiate user auth session if err := c.writePacket(marshal(msgServiceRequest, serviceRequestMsg{serviceUserAuth})); err != nil { return err @@ -26,7 +27,7 @@ func (c *ClientConn) authenticate() error { // then any untried methods suggested by the server. tried, remain := make(map[string]bool), make(map[string]bool) for auth := ClientAuth(new(noneAuth)); auth != nil; { - ok, methods, err := auth.auth(c.config.User, c.transport) + ok, methods, err := auth.auth(session, c.config.User, c.transport, c.config.rand()) if err != nil { return err } @@ -60,7 +61,7 @@ type ClientAuth interface { // Returns true if authentication is successful. // If authentication is not successful, a []string of alternative // method names is returned. - auth(user string, t *transport) (bool, []string, error) + auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) // method returns the RFC 4252 method name. method() string @@ -69,7 +70,7 @@ type ClientAuth interface { // "none" authentication, RFC 4252 section 5.2. type noneAuth int -func (n *noneAuth) auth(user string, t *transport) (bool, []string, error) { +func (n *noneAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) { if err := t.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{ User: user, Service: serviceSSH, @@ -102,7 +103,7 @@ type passwordAuth struct { ClientPassword } -func (p *passwordAuth) auth(user string, t *transport) (bool, []string, error) { +func (p *passwordAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) { type passwordAuthMsg struct { User string Service string @@ -155,3 +156,140 @@ type ClientPassword interface { func ClientAuthPassword(impl ClientPassword) ClientAuth { return &passwordAuth{impl} } + +// ClientKeyring implements access to a client key ring. +type ClientKeyring interface { + // Key returns the i'th rsa.Publickey or dsa.Publickey, or nil if + // no key exists at i. + Key(i int) (key interface{}, err error) + + // Sign returns a signature of the given data using the i'th key + // and the supplied random source. + Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) +} + +// "publickey" authentication, RFC 4252 Section 7. +type publickeyAuth struct { + ClientKeyring +} + +func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) { + type publickeyAuthMsg struct { + User string + Service string + Method string + // HasSig indicates to the reciver packet that the auth request is signed and + // should be used for authentication of the request. + HasSig bool + Algoname string + Pubkey string + // Sig is defined as []byte so marshal will exclude it during the query phase + Sig []byte `ssh:"rest"` + } + + // Authentication is performed in two stages. The first stage sends an + // enquiry to test if each key is acceptable to the remote. The second + // stage attempts to authenticate with the valid keys obtained in the + // first stage. + + var index int + // a map of public keys to their index in the keyring + validKeys := make(map[int]interface{}) + for { + key, err := p.Key(index) + if err != nil { + return false, nil, err + } + if key == nil { + // no more keys in the keyring + break + } + pubkey := serializePublickey(key) + algoname := algoName(key) + msg := publickeyAuthMsg{ + User: user, + Service: serviceSSH, + Method: p.method(), + HasSig: false, + Algoname: algoname, + Pubkey: string(pubkey), + } + if err := t.writePacket(marshal(msgUserAuthRequest, msg)); err != nil { + return false, nil, err + } + packet, err := t.readPacket() + if err != nil { + return false, nil, err + } + switch packet[0] { + case msgUserAuthPubKeyOk: + msg := decode(packet).(*userAuthPubKeyOkMsg) + if msg.Algo != algoname || msg.PubKey != string(pubkey) { + continue + } + validKeys[index] = key + case msgUserAuthFailure: + default: + return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]} + } + index++ + } + + // methods that may continue if this auth is not successful. + var methods []string + for i, key := range validKeys { + pubkey := serializePublickey(key) + algoname := algoName(key) + sign, err := p.Sign(i, rand, buildDataSignedForAuth(session, userAuthRequestMsg{ + User: user, + Service: serviceSSH, + Method: p.method(), + }, []byte(algoname), pubkey)) + if err != nil { + return false, nil, err + } + // manually wrap the serialized signature in a string + s := serializeSignature(algoname, sign) + sig := make([]byte, stringLength(s)) + marshalString(sig, s) + msg := publickeyAuthMsg{ + User: user, + Service: serviceSSH, + Method: p.method(), + HasSig: true, + Algoname: algoname, + Pubkey: string(pubkey), + Sig: sig, + } + p := marshal(msgUserAuthRequest, msg) + if err := t.writePacket(p); err != nil { + return false, nil, err + } + packet, err := t.readPacket() + if err != nil { + return false, nil, err + } + switch packet[0] { + case msgUserAuthSuccess: + return true, nil, nil + case msgUserAuthFailure: + msg := decode(packet).(*userAuthFailureMsg) + methods = msg.Methods + continue + case msgDisconnect: + return false, nil, io.EOF + default: + return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]} + } + } + return false, methods, nil +} + +func (p *publickeyAuth) method() string { + return "publickey" +} + +// ClientAuthPublickey returns a ClientAuth using public key authentication. +func ClientAuthPublickey(impl ClientKeyring) ClientAuth { + return &publickeyAuth{impl} +} diff --git a/libgo/go/exp/ssh/client_auth_test.go b/libgo/go/exp/ssh/client_auth_test.go new file mode 100644 index 00000000000..4ef9213a9cd --- /dev/null +++ b/libgo/go/exp/ssh/client_auth_test.go @@ -0,0 +1,248 @@ +// 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 ssh + +import ( + "bytes" + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "io" + "io/ioutil" + "testing" +) + +const _pem = `-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA19lGVsTqIT5iiNYRgnoY1CwkbETW5cq+Rzk5v/kTlf31XpSU +70HVWkbTERECjaYdXM2gGcbb+sxpq6GtXf1M3kVomycqhxwhPv4Cr6Xp4WT/jkFx +9z+FFzpeodGJWjOH6L2H5uX1Cvr9EDdQp9t9/J32/qBFntY8GwoUI/y/1MSTmMiF +tupdMODN064vd3gyMKTwrlQ8tZM6aYuyOPsutLlUY7M5x5FwMDYvnPDSeyT/Iw0z +s3B+NCyqeeMd2T7YzQFnRATj0M7rM5LoSs7DVqVriOEABssFyLj31PboaoLhOKgc +qoM9khkNzr7FHVvi+DhYM2jD0DwvqZLN6NmnLwIDAQABAoIBAQCGVj+kuSFOV1lT ++IclQYA6bM6uY5mroqcSBNegVxCNhWU03BxlW//BE9tA/+kq53vWylMeN9mpGZea +riEMIh25KFGWXqXlOOioH8bkMsqA8S7sBmc7jljyv+0toQ9vCCtJ+sueNPhxQQxH +D2YvUjfzBQ04I9+wn30BByDJ1QA/FoPsunxIOUCcRBE/7jxuLYcpR+JvEF68yYIh +atXRld4W4in7T65YDR8jK1Uj9XAcNeDYNpT/M6oFLx1aPIlkG86aCWRO19S1jLPT +b1ZAKHHxPMCVkSYW0RqvIgLXQOR62D0Zne6/2wtzJkk5UCjkSQ2z7ZzJpMkWgDgN +ifCULFPBAoGBAPoMZ5q1w+zB+knXUD33n1J+niN6TZHJulpf2w5zsW+m2K6Zn62M +MXndXlVAHtk6p02q9kxHdgov34Uo8VpuNjbS1+abGFTI8NZgFo+bsDxJdItemwC4 +KJ7L1iz39hRN/ZylMRLz5uTYRGddCkeIHhiG2h7zohH/MaYzUacXEEy3AoGBANz8 +e/msleB+iXC0cXKwds26N4hyMdAFE5qAqJXvV3S2W8JZnmU+sS7vPAWMYPlERPk1 +D8Q2eXqdPIkAWBhrx4RxD7rNc5qFNcQWEhCIxC9fccluH1y5g2M+4jpMX2CT8Uv+ +3z+NoJ5uDTXZTnLCfoZzgZ4nCZVZ+6iU5U1+YXFJAoGBANLPpIV920n/nJmmquMj +orI1R/QXR9Cy56cMC65agezlGOfTYxk5Cfl5Ve+/2IJCfgzwJyjWUsFx7RviEeGw +64o7JoUom1HX+5xxdHPsyZ96OoTJ5RqtKKoApnhRMamau0fWydH1yeOEJd+TRHhc +XStGfhz8QNa1dVFvENczja1vAoGABGWhsd4VPVpHMc7lUvrf4kgKQtTC2PjA4xoc +QJ96hf/642sVE76jl+N6tkGMzGjnVm4P2j+bOy1VvwQavKGoXqJBRd5Apppv727g +/SM7hBXKFc/zH80xKBBgP/i1DR7kdjakCoeu4ngeGywvu2jTS6mQsqzkK+yWbUxJ +I7mYBsECgYB/KNXlTEpXtz/kwWCHFSYA8U74l7zZbVD8ul0e56JDK+lLcJ0tJffk +gqnBycHj6AhEycjda75cs+0zybZvN4x65KZHOGW/O/7OAWEcZP5TPb3zf9ned3Hl +NsZoFj52ponUM6+99A2CmezFCN16c4mbA//luWF+k3VVqR6BpkrhKw== +-----END RSA PRIVATE KEY-----` + +// reused internally by tests +var serverConfig = new(ServerConfig) + +func init() { + if err := serverConfig.SetRSAPrivateKey([]byte(_pem)); err != nil { + panic("unable to set private key: " + err.Error()) + } +} + +// keychain implements the ClientPublickey interface +type keychain struct { + keys []*rsa.PrivateKey +} + +func (k *keychain) Key(i int) (interface{}, error) { + if i < 0 || i >= len(k.keys) { + return nil, nil + } + return k.keys[i].PublicKey, nil +} + +func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) { + hashFunc := crypto.SHA1 + h := hashFunc.New() + h.Write(data) + digest := h.Sum(nil) + return rsa.SignPKCS1v15(rand, k.keys[i], hashFunc, digest) +} + +func (k *keychain) loadPEM(file string) error { + buf, err := ioutil.ReadFile(file) + if err != nil { + return err + } + block, _ := pem.Decode(buf) + if block == nil { + return errors.New("ssh: no key found") + } + r, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return err + } + k.keys = append(k.keys, r) + return nil +} + +var pkey *rsa.PrivateKey + +func init() { + var err error + pkey, err = rsa.GenerateKey(rand.Reader, 512) + if err != nil { + panic("unable to generate public key") + } +} + +func TestClientAuthPublickey(t *testing.T) { + k := new(keychain) + k.keys = append(k.keys, pkey) + + serverConfig.PubKeyCallback = func(user, algo string, pubkey []byte) bool { + expected := []byte(serializePublickey(k.keys[0].PublicKey)) + algoname := algoName(k.keys[0].PublicKey) + return user == "testuser" && algo == algoname && bytes.Equal(pubkey, expected) + } + serverConfig.PasswordCallback = nil + + l, err := Listen("tcp", "127.0.0.1:0", serverConfig) + if err != nil { + t.Fatalf("unable to listen: %s", err) + } + defer l.Close() + + done := make(chan bool, 1) + go func() { + c, err := l.Accept() + if err != nil { + t.Fatal(err) + } + defer c.Close() + if err := c.Handshake(); err != nil { + t.Error(err) + } + done <- true + }() + + config := &ClientConfig{ + User: "testuser", + Auth: []ClientAuth{ + ClientAuthPublickey(k), + }, + } + + c, err := Dial("tcp", l.Addr().String(), config) + if err != nil { + t.Fatalf("unable to dial remote side: %s", err) + } + defer c.Close() + <-done +} + +// password implements the ClientPassword interface +type password string + +func (p password) Password(user string) (string, error) { + return string(p), nil +} + +func TestClientAuthPassword(t *testing.T) { + pw := password("tiger") + + serverConfig.PasswordCallback = func(user, pass string) bool { + return user == "testuser" && pass == string(pw) + } + serverConfig.PubKeyCallback = nil + + l, err := Listen("tcp", "127.0.0.1:0", serverConfig) + if err != nil { + t.Fatalf("unable to listen: %s", err) + } + defer l.Close() + + done := make(chan bool) + go func() { + c, err := l.Accept() + if err != nil { + t.Fatal(err) + } + if err := c.Handshake(); err != nil { + t.Error(err) + } + defer c.Close() + done <- true + }() + + config := &ClientConfig{ + User: "testuser", + Auth: []ClientAuth{ + ClientAuthPassword(pw), + }, + } + + c, err := Dial("tcp", l.Addr().String(), config) + if err != nil { + t.Fatalf("unable to dial remote side: %s", err) + } + defer c.Close() + <-done +} + +func TestClientAuthPasswordAndPublickey(t *testing.T) { + pw := password("tiger") + + serverConfig.PasswordCallback = func(user, pass string) bool { + return user == "testuser" && pass == string(pw) + } + + k := new(keychain) + k.keys = append(k.keys, pkey) + + serverConfig.PubKeyCallback = func(user, algo string, pubkey []byte) bool { + expected := []byte(serializePublickey(k.keys[0].PublicKey)) + algoname := algoName(k.keys[0].PublicKey) + return user == "testuser" && algo == algoname && bytes.Equal(pubkey, expected) + } + + l, err := Listen("tcp", "127.0.0.1:0", serverConfig) + if err != nil { + t.Fatalf("unable to listen: %s", err) + } + defer l.Close() + + done := make(chan bool) + go func() { + c, err := l.Accept() + if err != nil { + t.Fatal(err) + } + if err := c.Handshake(); err != nil { + t.Error(err) + } + defer c.Close() + done <- true + }() + + wrongPw := password("wrong") + config := &ClientConfig{ + User: "testuser", + Auth: []ClientAuth{ + ClientAuthPassword(wrongPw), + ClientAuthPublickey(k), + }, + } + + c, err := Dial("tcp", l.Addr().String(), config) + if err != nil { + t.Fatalf("unable to dial remote side: %s", err) + } + defer c.Close() + <-done +} diff --git a/libgo/go/exp/ssh/client_func_test.go b/libgo/go/exp/ssh/client_func_test.go new file mode 100644 index 00000000000..137456095a0 --- /dev/null +++ b/libgo/go/exp/ssh/client_func_test.go @@ -0,0 +1,61 @@ +// 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 ssh + +// ClientConn functional tests. +// These tests require a running ssh server listening on port 22 +// on the local host. Functional tests will be skipped unless +// -ssh.user and -ssh.pass must be passed to gotest. + +import ( + "flag" + "testing" +) + +var ( + sshuser = flag.String("ssh.user", "", "ssh username") + sshpass = flag.String("ssh.pass", "", "ssh password") + sshprivkey = flag.String("ssh.privkey", "", "ssh privkey file") +) + +func TestFuncPasswordAuth(t *testing.T) { + if *sshuser == "" { + t.Log("ssh.user not defined, skipping test") + return + } + config := &ClientConfig{ + User: *sshuser, + Auth: []ClientAuth{ + ClientAuthPassword(password(*sshpass)), + }, + } + conn, err := Dial("tcp", "localhost:22", config) + if err != nil { + t.Fatalf("Unable to connect: %s", err) + } + defer conn.Close() +} + +func TestFuncPublickeyAuth(t *testing.T) { + if *sshuser == "" { + t.Log("ssh.user not defined, skipping test") + return + } + kc := new(keychain) + if err := kc.loadPEM(*sshprivkey); err != nil { + t.Fatalf("unable to load private key: %s", err) + } + config := &ClientConfig{ + User: *sshuser, + Auth: []ClientAuth{ + ClientAuthPublickey(kc), + }, + } + conn, err := Dial("tcp", "localhost:22", config) + if err != nil { + t.Fatalf("unable to connect: %s", err) + } + defer conn.Close() +} diff --git a/libgo/go/exp/ssh/common.go b/libgo/go/exp/ssh/common.go index 273820b6428..6844fb89b79 100644 --- a/libgo/go/exp/ssh/common.go +++ b/libgo/go/exp/ssh/common.go @@ -5,6 +5,8 @@ package ssh import ( + "crypto/dsa" + "crypto/rsa" "math/big" "strconv" "sync" @@ -14,7 +16,6 @@ import ( const ( kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" hostAlgoRSA = "ssh-rsa" - cipherAES128CTR = "aes128-ctr" macSHA196 = "hmac-sha1-96" compressionNone = "none" serviceUserAuth = "ssh-userauth" @@ -23,7 +24,6 @@ const ( var supportedKexAlgos = []string{kexAlgoDH14SHA1} var supportedHostKeyAlgos = []string{hostAlgoRSA} -var supportedCiphers = []string{cipherAES128CTR} var supportedMACs = []string{macSHA196} var supportedCompressions = []string{compressionNone} @@ -127,3 +127,113 @@ func findAgreedAlgorithms(transport *transport, clientKexInit, serverKexInit *ke ok = true return } + +// Cryptographic configuration common to both ServerConfig and ClientConfig. +type CryptoConfig struct { + // The allowed cipher algorithms. If unspecified then DefaultCipherOrder is + // used. + Ciphers []string +} + +func (c *CryptoConfig) ciphers() []string { + if c.Ciphers == nil { + return DefaultCipherOrder + } + return c.Ciphers +} + +// serialize a signed slice according to RFC 4254 6.6. +func serializeSignature(algoname string, sig []byte) []byte { + length := stringLength([]byte(algoname)) + length += stringLength(sig) + + ret := make([]byte, length) + r := marshalString(ret, []byte(algoname)) + r = marshalString(r, sig) + + return ret +} + +// serialize an rsa.PublicKey or dsa.PublicKey according to RFC 4253 6.6. +func serializePublickey(key interface{}) []byte { + algoname := algoName(key) + switch key := key.(type) { + case rsa.PublicKey: + e := new(big.Int).SetInt64(int64(key.E)) + length := stringLength([]byte(algoname)) + length += intLength(e) + length += intLength(key.N) + ret := make([]byte, length) + r := marshalString(ret, []byte(algoname)) + r = marshalInt(r, e) + marshalInt(r, key.N) + return ret + case dsa.PublicKey: + length := stringLength([]byte(algoname)) + length += intLength(key.P) + length += intLength(key.Q) + length += intLength(key.G) + length += intLength(key.Y) + ret := make([]byte, length) + r := marshalString(ret, []byte(algoname)) + r = marshalInt(r, key.P) + r = marshalInt(r, key.Q) + r = marshalInt(r, key.G) + marshalInt(r, key.Y) + return ret + } + panic("unexpected key type") +} + +func algoName(key interface{}) string { + switch key.(type) { + case rsa.PublicKey: + return "ssh-rsa" + case dsa.PublicKey: + return "ssh-dss" + } + panic("unexpected key type") +} + +// buildDataSignedForAuth returns the data that is signed in order to prove +// posession of a private key. See RFC 4252, section 7. +func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { + user := []byte(req.User) + service := []byte(req.Service) + method := []byte(req.Method) + + length := stringLength(sessionId) + length += 1 + length += stringLength(user) + length += stringLength(service) + length += stringLength(method) + length += 1 + length += stringLength(algo) + length += stringLength(pubKey) + + ret := make([]byte, length) + r := marshalString(ret, sessionId) + r[0] = msgUserAuthRequest + r = r[1:] + r = marshalString(r, user) + r = marshalString(r, service) + r = marshalString(r, method) + r[0] = 1 + r = r[1:] + r = marshalString(r, algo) + r = marshalString(r, pubKey) + return ret +} + +// safeString sanitises s according to RFC 4251, section 9.2. +// All control characters except tab, carriage return and newline are +// replaced by 0x20. +func safeString(s string) string { + out := []byte(s) + for i, c := range out { + if c < 0x20 && c != 0xd && c != 0xa && c != 0x9 { + out[i] = 0x20 + } + } + return string(out) +} diff --git a/libgo/go/exp/ssh/common_test.go b/libgo/go/exp/ssh/common_test.go new file mode 100644 index 00000000000..2f4448a1bd4 --- /dev/null +++ b/libgo/go/exp/ssh/common_test.go @@ -0,0 +1,26 @@ +// 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 ssh + +import ( + "testing" +) + +var strings = map[string]string{ + "\x20\x0d\x0a": "\x20\x0d\x0a", + "flibble": "flibble", + "new\x20line": "new\x20line", + "123456\x07789": "123456 789", + "\t\t\x10\r\n": "\t\t \r\n", +} + +func TestSafeString(t *testing.T) { + for s, expected := range strings { + actual := safeString(s) + if expected != actual { + t.Errorf("expected: %v, actual: %v", []byte(expected), []byte(actual)) + } + } +} diff --git a/libgo/go/exp/ssh/doc.go b/libgo/go/exp/ssh/doc.go index 248b2fec4f8..480f877191a 100644 --- a/libgo/go/exp/ssh/doc.go +++ b/libgo/go/exp/ssh/doc.go @@ -92,9 +92,9 @@ Each ClientConn can support multiple interactive sessions, represented by a Sess session, err := client.NewSession() Once a Session is created, you can execute a single command on the remote side -using the Exec method. +using the Run method. - if err := session.Exec("/usr/bin/whoami"); err != nil { + if err := session.Run("/usr/bin/whoami"); err != nil { panic("Failed to exec: " + err.String()) } reader := bufio.NewReader(session.Stdin) diff --git a/libgo/go/exp/ssh/messages.go b/libgo/go/exp/ssh/messages.go index e24b6398b56..cebb5609db3 100644 --- a/libgo/go/exp/ssh/messages.go +++ b/libgo/go/exp/ssh/messages.go @@ -392,7 +392,10 @@ func parseString(in []byte) (out, rest []byte, ok bool) { return } -var comma = []byte{','} +var ( + comma = []byte{','} + emptyNameList = []string{} +) func parseNameList(in []byte) (out []string, rest []byte, ok bool) { contents, rest, ok := parseString(in) @@ -400,6 +403,7 @@ func parseNameList(in []byte) (out []string, rest []byte, ok bool) { return } if len(contents) == 0 { + out = emptyNameList return } parts := bytes.Split(contents, comma) @@ -444,8 +448,6 @@ func parseUint32(in []byte) (out uint32, rest []byte, ok bool) { return } -const maxPacketSize = 36000 - func nameListLength(namelist []string) int { length := 4 /* uint32 length prefix */ for i, name := range namelist { diff --git a/libgo/go/exp/ssh/server.go b/libgo/go/exp/ssh/server.go index 62035d52b7d..1eee9a4a977 100644 --- a/libgo/go/exp/ssh/server.go +++ b/libgo/go/exp/ssh/server.go @@ -40,6 +40,9 @@ type ServerConfig struct { // 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 + + // Cryptographic-related configuration. + Crypto CryptoConfig } func (c *ServerConfig) rand() io.Reader { @@ -204,11 +207,11 @@ func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha marshalInt(K, kInt) h.Write(K) - H = h.Sum() + H = h.Sum(nil) h.Reset() h.Write(H) - hh := h.Sum() + hh := h.Sum(nil) var sig []byte switch hostKeyAlgo { @@ -221,7 +224,7 @@ func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha return nil, nil, errors.New("internal error") } - serializedSig := serializeRSASignature(sig) + serializedSig := serializeSignature(hostAlgoRSA, sig) kexDHReply := kexDHReplyMsg{ HostKey: serializedHostKey, @@ -234,50 +237,9 @@ func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha return } -func serializeRSASignature(sig []byte) []byte { - length := stringLength([]byte(hostAlgoRSA)) - length += stringLength(sig) - - ret := make([]byte, length) - r := marshalString(ret, []byte(hostAlgoRSA)) - r = marshalString(r, sig) - - return ret -} - // serverVersion is the fixed identification string that Server will use. var serverVersion = []byte("SSH-2.0-Go\r\n") -// buildDataSignedForAuth returns the data that is signed in order to prove -// posession of a private key. See RFC 4252, section 7. -func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { - user := []byte(req.User) - service := []byte(req.Service) - method := []byte(req.Method) - - length := stringLength(sessionId) - length += 1 - length += stringLength(user) - length += stringLength(service) - length += stringLength(method) - length += 1 - length += stringLength(algo) - length += stringLength(pubKey) - - ret := make([]byte, length) - r := marshalString(ret, sessionId) - r[0] = msgUserAuthRequest - r = r[1:] - r = marshalString(r, user) - r = marshalString(r, service) - r = marshalString(r, method) - r[0] = 1 - r = r[1:] - r = marshalString(r, algo) - r = marshalString(r, pubKey) - return ret -} - // Handshake performs an SSH transport and client authentication on the given ServerConn. func (s *ServerConn) Handshake() error { var magics handshakeMagics @@ -298,8 +260,8 @@ func (s *ServerConn) Handshake() error { serverKexInit := kexInitMsg{ KexAlgos: supportedKexAlgos, ServerHostKeyAlgos: supportedHostKeyAlgos, - CiphersClientServer: supportedCiphers, - CiphersServerClient: supportedCiphers, + CiphersClientServer: s.config.Crypto.ciphers(), + CiphersServerClient: s.config.Crypto.ciphers(), MACsClientServer: supportedMACs, MACsServerClient: supportedMACs, CompressionClientServer: supportedCompressions, @@ -364,7 +326,9 @@ func (s *ServerConn) Handshake() error { if packet[0] != msgNewKeys { return UnexpectedMessageError{msgNewKeys, packet[0]} } - s.transport.reader.setupKeys(clientKeys, K, H, H, hashFunc) + if err = s.transport.reader.setupKeys(clientKeys, K, H, H, hashFunc); err != nil { + return err + } if packet, err = s.readPacket(); err != nil { return err } @@ -514,7 +478,7 @@ userAuthLoop: hashFunc := crypto.SHA1 h := hashFunc.New() h.Write(signedData) - digest := h.Sum() + digest := h.Sum(nil) rsaKey, ok := parseRSA(pubKey) if !ok { return ParseError{msgUserAuthRequest} diff --git a/libgo/go/exp/ssh/session.go b/libgo/go/exp/ssh/session.go index 77154f2c3c3..5f98a8d58c6 100644 --- a/libgo/go/exp/ssh/session.go +++ b/libgo/go/exp/ssh/session.go @@ -8,125 +8,409 @@ package ssh // "RFC 4254, section 6". import ( - "encoding/binary" + "bytes" "errors" + "fmt" "io" + "io/ioutil" +) + +type Signal string + +// POSIX signals as listed in RFC 4254 Section 6.10. +const ( + SIGABRT Signal = "ABRT" + SIGALRM Signal = "ALRM" + SIGFPE Signal = "FPE" + SIGHUP Signal = "HUP" + SIGILL Signal = "ILL" + SIGINT Signal = "INT" + SIGKILL Signal = "KILL" + SIGPIPE Signal = "PIPE" + SIGQUIT Signal = "QUIT" + SIGSEGV Signal = "SEGV" + SIGTERM Signal = "TERM" + SIGUSR1 Signal = "USR1" + SIGUSR2 Signal = "USR2" ) // A Session represents a connection to a remote command or shell. type Session struct { - // Writes to Stdin are made available to the remote command's standard input. - // Closing Stdin causes the command to observe an EOF on its standard input. - Stdin io.WriteCloser - - // Reads from Stdout and Stderr consume from the remote command's standard - // output and error streams, respectively. - // There is a fixed amount of buffering that is shared for the two streams. - // Failing to read from either may eventually cause the command to block. - // Closing Stdout unblocks such writes and causes them to return errors. - Stdout io.ReadCloser - Stderr io.Reader + // Stdin specifies the remote process's standard input. + // If Stdin is nil, the remote process reads from an empty + // bytes.Buffer. + Stdin io.Reader + + // Stdout and Stderr specify the remote process's standard + // output and error. + // + // If either is nil, Run connects the corresponding file + // descriptor to an instance of ioutil.Discard. There is a + // fixed amount of buffering that is shared for the two streams. + // If either blocks it may eventually cause the remote + // command to block. + Stdout io.Writer + Stderr io.Writer *clientChan // the channel backing this session - started bool // started is set to true once a Shell or Exec is invoked. + started bool // true once Start, Run or Shell is invoked. + closeAfterWait []io.Closer + copyFuncs []func() error + errch chan error // one send per copyFunc +} + +// RFC 4254 Section 6.4. +type setenvRequest struct { + PeersId uint32 + Request string + WantReply bool + Name string + Value string } // Setenv sets an environment variable that will be applied to any -// command executed by Shell or Exec. +// command executed by Shell or Run. func (s *Session) Setenv(name, value string) error { - n, v := []byte(name), []byte(value) - nlen, vlen := stringLength(n), stringLength(v) - payload := make([]byte, nlen+vlen) - marshalString(payload[:nlen], n) - marshalString(payload[nlen:], v) - - return s.sendChanReq(channelRequestMsg{ - PeersId: s.id, - Request: "env", - WantReply: true, - RequestSpecificData: payload, - }) + req := setenvRequest{ + PeersId: s.peersId, + Request: "env", + WantReply: true, + Name: name, + Value: value, + } + if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { + return err + } + return s.waitForResponse() } -// An empty mode list (a string of 1 character, opcode 0), see RFC 4254 Section 8. -var emptyModeList = []byte{0, 0, 0, 1, 0} +// An empty mode list, see RFC 4254 Section 8. +var emptyModelist = "\x00" + +// RFC 4254 Section 6.2. +type ptyRequestMsg struct { + PeersId uint32 + Request string + WantReply bool + Term string + Columns uint32 + Rows uint32 + Width uint32 + Height uint32 + Modelist string +} // RequestPty requests the association of a pty with the session on the remote host. func (s *Session) RequestPty(term string, h, w int) error { - buf := make([]byte, 4+len(term)+16+len(emptyModeList)) - b := marshalString(buf, []byte(term)) - binary.BigEndian.PutUint32(b, uint32(h)) - binary.BigEndian.PutUint32(b[4:], uint32(w)) - binary.BigEndian.PutUint32(b[8:], uint32(h*8)) - binary.BigEndian.PutUint32(b[12:], uint32(w*8)) - copy(b[16:], emptyModeList) - - return s.sendChanReq(channelRequestMsg{ - PeersId: s.id, - Request: "pty-req", - WantReply: true, - RequestSpecificData: buf, - }) + req := ptyRequestMsg{ + PeersId: s.peersId, + Request: "pty-req", + WantReply: true, + Term: term, + Columns: uint32(w), + Rows: uint32(h), + Width: uint32(w * 8), + Height: uint32(h * 8), + Modelist: emptyModelist, + } + if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { + return err + } + return s.waitForResponse() } -// Exec runs cmd on the remote host. Typically, the remote -// server passes cmd to the shell for interpretation. -// A Session only accepts one call to Exec or Shell. -func (s *Session) Exec(cmd string) error { +// RFC 4254 Section 6.9. +type signalMsg struct { + PeersId uint32 + Request string + WantReply bool + Signal string +} + +// Signal sends the given signal to the remote process. +// sig is one of the SIG* constants. +func (s *Session) Signal(sig Signal) error { + req := signalMsg{ + PeersId: s.peersId, + Request: "signal", + WantReply: false, + Signal: string(sig), + } + return s.writePacket(marshal(msgChannelRequest, req)) +} + +// RFC 4254 Section 6.5. +type execMsg struct { + PeersId uint32 + Request string + WantReply bool + Command string +} + +// Start runs cmd on the remote host. Typically, the remote +// server passes cmd to the shell for interpretation. +// A Session only accepts one call to Run, Start or Shell. +func (s *Session) Start(cmd string) error { if s.started { - return errors.New("session already started") + return errors.New("ssh: session already started") } - cmdLen := stringLength([]byte(cmd)) - payload := make([]byte, cmdLen) - marshalString(payload, []byte(cmd)) - s.started = true + req := execMsg{ + PeersId: s.peersId, + Request: "exec", + WantReply: true, + Command: cmd, + } + if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { + return err + } + if err := s.waitForResponse(); err != nil { + return fmt.Errorf("ssh: could not execute command %s: %v", cmd, err) + } + return s.start() +} - return s.sendChanReq(channelRequestMsg{ - PeersId: s.id, - Request: "exec", - WantReply: true, - RequestSpecificData: payload, - }) +// Run runs cmd on the remote host and waits for it to terminate. +// Typically, the remote server passes cmd to the shell for +// interpretation. A Session only accepts one call to Run, +// Start or Shell. +func (s *Session) Run(cmd string) error { + err := s.Start(cmd) + if err != nil { + return err + } + return s.Wait() } -// Shell starts a login shell on the remote host. A Session only -// accepts one call to Exec or Shell. +// Shell starts a login shell on the remote host. A Session only +// accepts one call to Run, Start or Shell. func (s *Session) Shell() error { if s.started { - return errors.New("session already started") + return errors.New("ssh: session already started") } - s.started = true - - return s.sendChanReq(channelRequestMsg{ - PeersId: s.id, + req := channelRequestMsg{ + PeersId: s.peersId, Request: "shell", WantReply: true, + } + if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { + return err + } + if err := s.waitForResponse(); err != nil { + return fmt.Errorf("ssh: cound not execute shell: %v", err) + } + return s.start() +} + +func (s *Session) waitForResponse() error { + msg := <-s.msg + switch msg.(type) { + case *channelRequestSuccessMsg: + return nil + case *channelRequestFailureMsg: + return errors.New("request failed") + } + return fmt.Errorf("unknown packet %T received: %v", msg, msg) +} + +func (s *Session) start() error { + s.started = true + + type F func(*Session) error + for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} { + if err := setupFd(s); err != nil { + return err + } + } + + s.errch = make(chan error, len(s.copyFuncs)) + for _, fn := range s.copyFuncs { + go func(fn func() error) { + s.errch <- fn() + }(fn) + } + return nil +} + +// Wait waits for the remote command to exit. +func (s *Session) Wait() error { + if !s.started { + return errors.New("ssh: session not started") + } + waitErr := s.wait() + + var copyError error + for _ = range s.copyFuncs { + if err := <-s.errch; err != nil && copyError == nil { + copyError = err + } + } + for _, fd := range s.closeAfterWait { + fd.Close() + } + if waitErr != nil { + return waitErr + } + return copyError +} + +func (s *Session) wait() error { + for { + switch msg := (<-s.msg).(type) { + case *channelRequestMsg: + // TODO(dfc) improve this behavior to match os.Waitmsg + switch msg.Request { + case "exit-status": + d := msg.RequestSpecificData + status := int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3]) + if status > 0 { + return fmt.Errorf("remote process exited with %d", status) + } + return nil + case "exit-signal": + // TODO(dfc) make a more readable error message + return fmt.Errorf("%v", msg.RequestSpecificData) + default: + return fmt.Errorf("wait: unexpected channel request: %v", msg) + } + default: + return fmt.Errorf("wait: unexpected packet %T received: %v", msg, msg) + } + } + panic("unreachable") +} + +func (s *Session) stdin() error { + if s.Stdin == nil { + s.Stdin = new(bytes.Buffer) + } + s.copyFuncs = append(s.copyFuncs, func() error { + w := &chanWriter{ + packetWriter: s, + peersId: s.peersId, + win: s.win, + } + _, err := io.Copy(w, s.Stdin) + if err1 := w.Close(); err == nil { + err = err1 + } + return err }) + return nil } +func (s *Session) stdout() error { + if s.Stdout == nil { + s.Stdout = ioutil.Discard + } + s.copyFuncs = append(s.copyFuncs, func() error { + r := &chanReader{ + packetWriter: s, + peersId: s.peersId, + data: s.data, + } + _, err := io.Copy(s.Stdout, r) + return err + }) + return nil +} + +func (s *Session) stderr() error { + if s.Stderr == nil { + s.Stderr = ioutil.Discard + } + s.copyFuncs = append(s.copyFuncs, func() error { + r := &chanReader{ + packetWriter: s, + peersId: s.peersId, + data: s.dataExt, + } + _, err := io.Copy(s.Stderr, r) + return err + }) + return nil +} + +// StdinPipe returns a pipe that will be connected to the +// remote command's standard input when the command starts. +func (s *Session) StdinPipe() (io.WriteCloser, error) { + if s.Stdin != nil { + return nil, errors.New("ssh: Stdin already set") + } + 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 +} + +// StdoutPipe returns a pipe that will be connected to the +// remote command's standard output when the command starts. +// There is a fixed amount of buffering that is shared between +// 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) { + 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 +} + +// StderrPipe returns a pipe that will be connected to the +// remote command's standard error when the command starts. +// There is a fixed amount of buffering that is shared between +// 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) { + 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 +} + +// TODO(dfc) add Output and CombinedOutput helpers + // NewSession returns a new interactive session on the remote host. func (c *ClientConn) NewSession() (*Session, error) { - ch, err := c.openChan("session") - if err != nil { + ch := c.newChan(c.transport) + if err := c.writePacket(marshal(msgChannelOpen, channelOpenMsg{ + ChanType: "session", + PeersId: ch.id, + PeersWindow: 1 << 14, + MaxPacketSize: 1 << 15, // RFC 4253 6.1 + })); err != nil { + c.chanlist.remove(ch.id) return nil, err } - return &Session{ - Stdin: &chanWriter{ - packetWriter: ch, - id: ch.id, - win: ch.win, - }, - Stdout: &chanReader{ - packetWriter: ch, - id: ch.id, - data: ch.data, - }, - Stderr: &chanReader{ - packetWriter: ch, - id: ch.id, - data: ch.dataExt, - }, - clientChan: ch, - }, nil + // wait for response + msg := <-ch.msg + switch msg := msg.(type) { + case *channelOpenConfirmMsg: + ch.peersId = msg.MyId + ch.win <- int(msg.MyWindow) + return &Session{ + clientChan: ch, + }, nil + case *channelOpenFailureMsg: + c.chanlist.remove(ch.id) + return nil, fmt.Errorf("ssh: channel open failed: %s", msg.Message) + } + c.chanlist.remove(ch.id) + return nil, fmt.Errorf("ssh: unexpected message %T: %v", msg, msg) } diff --git a/libgo/go/exp/ssh/session_test.go b/libgo/go/exp/ssh/session_test.go new file mode 100644 index 00000000000..4be7746d17e --- /dev/null +++ b/libgo/go/exp/ssh/session_test.go @@ -0,0 +1,149 @@ +// 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 ssh + +// Session tests. + +import ( + "bytes" + "io" + "testing" +) + +// dial constructs a new test server and returns a *ClientConn. +func dial(t *testing.T) *ClientConn { + pw := password("tiger") + serverConfig.PasswordCallback = func(user, pass string) bool { + return user == "testuser" && pass == string(pw) + } + serverConfig.PubKeyCallback = nil + + l, err := Listen("tcp", "127.0.0.1:0", serverConfig) + if err != nil { + t.Fatalf("unable to listen: %s", err) + } + go func() { + defer l.Close() + conn, err := l.Accept() + if err != nil { + t.Errorf("Unable to accept: %v", err) + return + } + defer conn.Close() + if err := conn.Handshake(); err != nil { + t.Errorf("Unable to handshake: %v", err) + return + } + for { + ch, err := conn.Accept() + if err == io.EOF { + return + } + if err != nil { + t.Errorf("Unable to accept incoming channel request: %v", err) + return + } + if ch.ChannelType() != "session" { + ch.Reject(UnknownChannelType, "unknown channel type") + continue + } + ch.Accept() + go func() { + defer ch.Close() + // this string is returned to stdout + shell := NewServerShell(ch, "golang") + shell.ReadLine() + type exitMsg struct { + PeersId uint32 + Request string + WantReply bool + Status uint32 + } + // TODO(dfc) casting to the concrete type should not be + // necessary to send a packet. + msg := exitMsg{ + PeersId: ch.(*channel).theirId, + Request: "exit-status", + WantReply: false, + Status: 0, + } + ch.(*channel).serverConn.writePacket(marshal(msgChannelRequest, msg)) + }() + } + t.Log("done") + }() + + config := &ClientConfig{ + User: "testuser", + Auth: []ClientAuth{ + ClientAuthPassword(pw), + }, + } + + c, err := Dial("tcp", l.Addr().String(), config) + if err != nil { + t.Fatalf("unable to dial remote side: %s", err) + } + return c +} + +// Test a simple string is returned to session.Stdout. +func TestSessionShell(t *testing.T) { + conn := dial(t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %s", err) + } + defer session.Close() + stdout := new(bytes.Buffer) + session.Stdout = stdout + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %s", err) + } + if err := session.Wait(); err != nil { + t.Fatalf("Remote command did not exit cleanly: %s", err) + } + actual := stdout.String() + if actual != "golang" { + t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) + } +} + +// TODO(dfc) add support for Std{in,err}Pipe when the Server supports it. + +// Test a simple string is returned via StdoutPipe. +func TestSessionStdoutPipe(t *testing.T) { + conn := dial(t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %s", err) + } + defer session.Close() + stdout, err := session.StdoutPipe() + if err != nil { + t.Fatalf("Unable to request StdoutPipe(): %v", err) + } + var buf bytes.Buffer + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %s", err) + } + done := make(chan bool, 1) + go func() { + if _, err := io.Copy(&buf, stdout); err != nil { + t.Errorf("Copy of stdout failed: %v", err) + } + done <- true + }() + if err := session.Wait(); err != nil { + t.Fatalf("Remote command did not exit cleanly: %s", err) + } + <-done + actual := buf.String() + if actual != "golang" { + t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) + } +} diff --git a/libgo/go/exp/ssh/tcpip.go b/libgo/go/exp/ssh/tcpip.go new file mode 100644 index 00000000000..f3bbac5d19e --- /dev/null +++ b/libgo/go/exp/ssh/tcpip.go @@ -0,0 +1,146 @@ +// 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 ssh + +import ( + "errors" + "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 +// remote host. Consider using ssh.DialTCP to avoid this. +func (c *ClientConn) Dial(n, addr string) (net.Conn, error) { + raddr, err := net.ResolveTCPAddr(n, addr) + if err != nil { + return nil, err + } + return c.DialTCP(n, nil, raddr) +} + +// DialTCP connects to the remote address raddr on the network net, +// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used +// as the local address for the connection. +func (c *ClientConn) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) { + if laddr == nil { + laddr = &net.TCPAddr{ + IP: net.IPv4zero, + Port: 0, + } + } + ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port) + if err != nil { + return nil, err + } + return &tcpchanconn{ + tcpchan: ch, + laddr: laddr, + raddr: raddr, + }, nil +} + +// dial opens a direct-tcpip connection to the remote server. laddr and raddr are passed as +// strings and are expected to be resolveable at the remote end. +func (c *ClientConn) dial(laddr string, lport int, raddr string, rport int) (*tcpchan, error) { + // RFC 4254 7.2 + type channelOpenDirectMsg struct { + ChanType string + PeersId uint32 + PeersWindow uint32 + MaxPacketSize uint32 + raddr string + rport uint32 + laddr string + lport uint32 + } + ch := c.newChan(c.transport) + if err := c.writePacket(marshal(msgChannelOpen, channelOpenDirectMsg{ + ChanType: "direct-tcpip", + PeersId: ch.id, + PeersWindow: 1 << 14, + MaxPacketSize: 1 << 15, // RFC 4253 6.1 + raddr: raddr, + rport: uint32(rport), + laddr: laddr, + lport: uint32(lport), + })); err != nil { + c.chanlist.remove(ch.id) + return nil, err + } + // wait for response + switch msg := (<-ch.msg).(type) { + case *channelOpenConfirmMsg: + ch.peersId = msg.MyId + ch.win <- int(msg.MyWindow) + case *channelOpenFailureMsg: + c.chanlist.remove(ch.id) + return nil, errors.New("ssh: error opening remote TCP connection: " + msg.Message) + default: + c.chanlist.remove(ch.id) + return nil, errors.New("ssh: unexpected packet") + } + return &tcpchan{ + clientChan: ch, + Reader: &chanReader{ + packetWriter: ch, + peersId: ch.peersId, + data: ch.data, + }, + Writer: &chanWriter{ + packetWriter: ch, + peersId: ch.peersId, + win: ch.win, + }, + }, nil +} + +type tcpchan struct { + *clientChan // the backing channel + io.Reader + io.Writer +} + +// tcpchanconn fulfills the net.Conn interface without +// the tcpchan having to hold laddr or raddr directly. +type tcpchanconn struct { + *tcpchan + laddr, raddr net.Addr +} + +// LocalAddr returns the local network address. +func (t *tcpchanconn) LocalAddr() net.Addr { + return t.laddr +} + +// RemoteAddr returns the remote network address. +func (t *tcpchanconn) RemoteAddr() net.Addr { + return t.raddr +} + +// SetTimeout sets the read and write deadlines associated +// with the connection. +func (t *tcpchanconn) SetTimeout(nsec int64) error { + if err := t.SetReadTimeout(nsec); err != nil { + return err + } + return t.SetWriteTimeout(nsec) +} + +// SetReadTimeout sets the time (in nanoseconds) that +// Read will wait for data before returning an error with Timeout() == true. +// Setting nsec == 0 (the default) disables the deadline. +func (t *tcpchanconn) SetReadTimeout(nsec int64) error { + return errors.New("ssh: tcpchan: timeout not supported") +} + +// SetWriteTimeout sets the time (in nanoseconds) that +// Write will wait to send its data before returning an error with Timeout() == true. +// Setting nsec == 0 (the default) disables the deadline. +// Even if write times out, it may return n > 0, indicating that +// some of the data was successfully written. +func (t *tcpchanconn) SetWriteTimeout(nsec int64) error { + return errors.New("ssh: tcpchan: timeout not supported") +} diff --git a/libgo/go/exp/ssh/tcpip_func_test.go b/libgo/go/exp/ssh/tcpip_func_test.go new file mode 100644 index 00000000000..261297241e9 --- /dev/null +++ b/libgo/go/exp/ssh/tcpip_func_test.go @@ -0,0 +1,59 @@ +// 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 ssh + +// direct-tcpip functional tests + +import ( + "net" + "net/http" + "testing" +) + +func TestTCPIPHTTP(t *testing.T) { + if *sshuser == "" { + t.Log("ssh.user not defined, skipping test") + return + } + // google.com will generate at least one redirect, possibly three + // depending on your location. + doTest(t, "http://google.com") +} + +func TestTCPIPHTTPS(t *testing.T) { + if *sshuser == "" { + t.Log("ssh.user not defined, skipping test") + return + } + doTest(t, "https://encrypted.google.com/") +} + +func doTest(t *testing.T, url string) { + config := &ClientConfig{ + User: *sshuser, + Auth: []ClientAuth{ + ClientAuthPassword(password(*sshpass)), + }, + } + conn, err := Dial("tcp", "localhost:22", config) + if err != nil { + t.Fatalf("Unable to connect: %s", err) + } + defer conn.Close() + tr := &http.Transport{ + Dial: func(n, addr string) (net.Conn, error) { + return conn.Dial(n, addr) + }, + } + client := &http.Client{ + Transport: tr, + } + resp, err := client.Get(url) + if err != nil { + t.Fatalf("unable to proxy: %s", err) + } + // got a body without error + t.Log(resp) +} diff --git a/libgo/go/exp/ssh/transport.go b/libgo/go/exp/ssh/transport.go index 579a9d82de9..bcd073e7ce6 100644 --- a/libgo/go/exp/ssh/transport.go +++ b/libgo/go/exp/ssh/transport.go @@ -7,7 +7,6 @@ package ssh import ( "bufio" "crypto" - "crypto/aes" "crypto/cipher" "crypto/hmac" "crypto/subtle" @@ -19,7 +18,10 @@ import ( ) const ( - paddingMultiple = 16 // TODO(dfc) does this need to be configurable? + packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher. + minPacketSize = 16 + maxPacketSize = 36000 + minPaddingSize = 4 // TODO(huin) should this be configurable? ) // filteredConn reduces the set of methods exposed when embeddeding @@ -61,8 +63,7 @@ type reader struct { type writer struct { *sync.Mutex // protects writer.Writer from concurrent writes *bufio.Writer - paddingMultiple int - rand io.Reader + rand io.Reader common } @@ -82,14 +83,11 @@ type common struct { func (r *reader) readOnePacket() ([]byte, error) { var lengthBytes = make([]byte, 5) var macSize uint32 - if _, err := io.ReadFull(r, lengthBytes); err != nil { return nil, err } - if r.cipher != nil { - r.cipher.XORKeyStream(lengthBytes, lengthBytes) - } + r.cipher.XORKeyStream(lengthBytes, lengthBytes) if r.mac != nil { r.mac.Reset() @@ -125,7 +123,7 @@ func (r *reader) readOnePacket() ([]byte, error) { if r.mac != nil { r.mac.Write(packet[:length-1]) - if subtle.ConstantTimeCompare(r.mac.Sum(), mac) != 1 { + if subtle.ConstantTimeCompare(r.mac.Sum(nil), mac) != 1 { return nil, errors.New("ssh: MAC failure") } } @@ -153,9 +151,9 @@ func (w *writer) writePacket(packet []byte) error { w.Mutex.Lock() defer w.Mutex.Unlock() - paddingLength := paddingMultiple - (5+len(packet))%paddingMultiple + paddingLength := packetSizeMultiple - (5+len(packet))%packetSizeMultiple if paddingLength < 4 { - paddingLength += paddingMultiple + paddingLength += packetSizeMultiple } length := len(packet) + 1 + paddingLength @@ -188,11 +186,9 @@ func (w *writer) writePacket(packet []byte) error { // TODO(dfc) lengthBytes, packet and padding should be // subslices of a single buffer - if w.cipher != nil { - w.cipher.XORKeyStream(lengthBytes, lengthBytes) - w.cipher.XORKeyStream(packet, packet) - w.cipher.XORKeyStream(padding, padding) - } + w.cipher.XORKeyStream(lengthBytes, lengthBytes) + w.cipher.XORKeyStream(packet, packet) + w.cipher.XORKeyStream(padding, padding) if _, err := w.Write(lengthBytes); err != nil { return err @@ -205,7 +201,7 @@ func (w *writer) writePacket(packet []byte) error { } if w.mac != nil { - if _, err := w.Write(w.mac.Sum()); err != nil { + if _, err := w.Write(w.mac.Sum(nil)); err != nil { return err } } @@ -227,11 +223,17 @@ func newTransport(conn net.Conn, rand io.Reader) *transport { return &transport{ reader: reader{ Reader: bufio.NewReader(conn), + common: common{ + cipher: noneCipher{}, + }, }, writer: writer{ Writer: bufio.NewWriter(conn), rand: rand, Mutex: new(sync.Mutex), + common: common{ + cipher: noneCipher{}, + }, }, filteredConn: conn, } @@ -249,29 +251,32 @@ var ( clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} ) -// setupKeys sets the cipher and MAC keys from K, H and sessionId, as +// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as // described in RFC 4253, section 6.4. direction should either be serverKeys // (to setup server->client keys) or clientKeys (for client->server keys). func (c *common) setupKeys(d direction, K, H, sessionId []byte, hashFunc crypto.Hash) error { - h := hashFunc.New() + cipherMode := cipherModes[c.cipherAlgo] - blockSize := 16 - keySize := 16 macKeySize := 20 - iv := make([]byte, blockSize) - key := make([]byte, keySize) + iv := make([]byte, cipherMode.ivSize) + key := make([]byte, cipherMode.keySize) macKey := make([]byte, macKeySize) + + h := hashFunc.New() generateKeyMaterial(iv, d.ivTag, K, H, sessionId, h) generateKeyMaterial(key, d.keyTag, K, H, sessionId, h) generateKeyMaterial(macKey, d.macKeyTag, K, H, sessionId, h) c.mac = truncatingMAC{12, hmac.NewSHA1(macKey)} - aes, err := aes.NewCipher(key) + + cipher, err := cipherMode.createCipher(key, iv) if err != nil { return err } - c.cipher = cipher.NewCTR(aes, iv) + + c.cipher = cipher + return nil } @@ -292,7 +297,7 @@ func generateKeyMaterial(out, tag []byte, K, H, sessionId []byte, h hash.Hash) { h.Write(digestsSoFar) } - digest := h.Sum() + digest := h.Sum(nil) n := copy(out, digest) out = out[n:] if len(out) > 0 { @@ -312,9 +317,9 @@ func (t truncatingMAC) Write(data []byte) (int, error) { return t.hmac.Write(data) } -func (t truncatingMAC) Sum() []byte { - digest := t.hmac.Sum() - return digest[:t.length] +func (t truncatingMAC) Sum(in []byte) []byte { + out := t.hmac.Sum(in) + return out[:len(in)+t.length] } func (t truncatingMAC) Reset() { diff --git a/libgo/go/exp/terminal/shell.go b/libgo/go/exp/terminal/shell.go deleted file mode 100644 index 5c5916755d6..00000000000 --- a/libgo/go/exp/terminal/shell.go +++ /dev/null @@ -1,356 +0,0 @@ -// 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 terminal - -import "io" - -// Shell contains the state for running a VT100 terminal that is capable of -// reading lines of input. -type Shell struct { - c io.ReadWriter - prompt string - - // line is the current line being entered. - line []byte - // pos is the logical position of the cursor in line - pos int - - // 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 - // the current line is 0. - cursorX, cursorY int - // maxLine is the greatest value of cursorY so far. - maxLine int - - termWidth, termHeight int - - // outBuf contains the terminal data to be sent. - outBuf []byte - // remainder contains the remainder of any partial key sequences after - // a read. It aliases into inBuf. - remainder []byte - inBuf [256]byte -} - -// NewShell runs a VT100 terminal on the given ReadWriter. If the ReadWriter is -// a local terminal, that terminal must first have been put into raw mode. -// prompt is a string that is written at the start of each input line (i.e. -// "> "). -func NewShell(c io.ReadWriter, prompt string) *Shell { - return &Shell{ - c: c, - prompt: prompt, - termWidth: 80, - termHeight: 24, - } -} - -const ( - keyCtrlD = 4 - keyEnter = '\r' - keyEscape = 27 - keyBackspace = 127 - keyUnknown = 256 + iota - keyUp - keyDown - keyLeft - keyRight - keyAltLeft - keyAltRight -) - -// bytesToKey tries to parse a key sequence from b. If successful, it returns -// the key and the remainder of the input. Otherwise it returns -1. -func bytesToKey(b []byte) (int, []byte) { - if len(b) == 0 { - return -1, nil - } - - if b[0] != keyEscape { - return int(b[0]), b[1:] - } - - if len(b) >= 3 && b[0] == keyEscape && b[1] == '[' { - switch b[2] { - case 'A': - return keyUp, b[3:] - case 'B': - return keyDown, b[3:] - case 'C': - return keyRight, b[3:] - case 'D': - return keyLeft, b[3:] - } - } - - if len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { - switch b[5] { - case 'C': - return keyAltRight, b[6:] - case 'D': - return keyAltLeft, b[6:] - } - } - - // If we get here then we have a key that we don't recognise, or a - // partial sequence. It's not clear how one should find the end of a - // sequence without knowing them all, but it seems that [a-zA-Z] only - // appears at the end of a sequence. - for i, c := range b[0:] { - if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' { - return keyUnknown, b[i+1:] - } - } - - return -1, b -} - -// queue appends data to the end of ss.outBuf -func (ss *Shell) queue(data []byte) { - if len(ss.outBuf)+len(data) > cap(ss.outBuf) { - newOutBuf := make([]byte, len(ss.outBuf), 2*(len(ss.outBuf)+len(data))) - copy(newOutBuf, ss.outBuf) - ss.outBuf = newOutBuf - } - - oldLen := len(ss.outBuf) - ss.outBuf = ss.outBuf[:len(ss.outBuf)+len(data)] - copy(ss.outBuf[oldLen:], data) -} - -var eraseUnderCursor = []byte{' ', keyEscape, '[', 'D'} - -func isPrintable(key int) bool { - return key >= 32 && key < 127 -} - -// moveCursorToPos appends data to ss.outBuf which will move the cursor to the -// given, logical position in the text. -func (ss *Shell) moveCursorToPos(pos int) { - x := len(ss.prompt) + pos - y := x / ss.termWidth - x = x % ss.termWidth - - up := 0 - if y < ss.cursorY { - up = ss.cursorY - y - } - - down := 0 - if y > ss.cursorY { - down = y - ss.cursorY - } - - left := 0 - if x < ss.cursorX { - left = ss.cursorX - x - } - - right := 0 - if x > ss.cursorX { - right = x - ss.cursorX - } - - movement := make([]byte, 3*(up+down+left+right)) - m := movement - for i := 0; i < up; i++ { - m[0] = keyEscape - m[1] = '[' - m[2] = 'A' - m = m[3:] - } - for i := 0; i < down; i++ { - m[0] = keyEscape - m[1] = '[' - m[2] = 'B' - m = m[3:] - } - for i := 0; i < left; i++ { - m[0] = keyEscape - m[1] = '[' - m[2] = 'D' - m = m[3:] - } - for i := 0; i < right; i++ { - m[0] = keyEscape - m[1] = '[' - m[2] = 'C' - m = m[3:] - } - - ss.cursorX = x - ss.cursorY = y - ss.queue(movement) -} - -const maxLineLength = 4096 - -// handleKey processes the given key and, optionally, returns a line of text -// that the user has entered. -func (ss *Shell) handleKey(key int) (line string, ok bool) { - switch key { - case keyBackspace: - if ss.pos == 0 { - return - } - ss.pos-- - - copy(ss.line[ss.pos:], ss.line[1+ss.pos:]) - ss.line = ss.line[:len(ss.line)-1] - ss.writeLine(ss.line[ss.pos:]) - ss.moveCursorToPos(ss.pos) - ss.queue(eraseUnderCursor) - case keyAltLeft: - // move left by a word. - if ss.pos == 0 { - return - } - ss.pos-- - for ss.pos > 0 { - if ss.line[ss.pos] != ' ' { - break - } - ss.pos-- - } - for ss.pos > 0 { - if ss.line[ss.pos] == ' ' { - ss.pos++ - break - } - ss.pos-- - } - ss.moveCursorToPos(ss.pos) - case keyAltRight: - // move right by a word. - for ss.pos < len(ss.line) { - if ss.line[ss.pos] == ' ' { - break - } - ss.pos++ - } - for ss.pos < len(ss.line) { - if ss.line[ss.pos] != ' ' { - break - } - ss.pos++ - } - ss.moveCursorToPos(ss.pos) - case keyLeft: - if ss.pos == 0 { - return - } - ss.pos-- - ss.moveCursorToPos(ss.pos) - case keyRight: - if ss.pos == len(ss.line) { - return - } - ss.pos++ - ss.moveCursorToPos(ss.pos) - case keyEnter: - ss.moveCursorToPos(len(ss.line)) - ss.queue([]byte("\r\n")) - line = string(ss.line) - ok = true - ss.line = ss.line[:0] - ss.pos = 0 - ss.cursorX = 0 - ss.cursorY = 0 - ss.maxLine = 0 - default: - if !isPrintable(key) { - return - } - if len(ss.line) == maxLineLength { - return - } - if len(ss.line) == cap(ss.line) { - newLine := make([]byte, len(ss.line), 2*(1+len(ss.line))) - copy(newLine, ss.line) - ss.line = newLine - } - ss.line = ss.line[:len(ss.line)+1] - copy(ss.line[ss.pos+1:], ss.line[ss.pos:]) - ss.line[ss.pos] = byte(key) - ss.writeLine(ss.line[ss.pos:]) - ss.pos++ - ss.moveCursorToPos(ss.pos) - } - return -} - -func (ss *Shell) writeLine(line []byte) { - for len(line) != 0 { - if ss.cursorX == ss.termWidth { - ss.queue([]byte("\r\n")) - ss.cursorX = 0 - ss.cursorY++ - if ss.cursorY > ss.maxLine { - ss.maxLine = ss.cursorY - } - } - - remainingOnLine := ss.termWidth - ss.cursorX - todo := len(line) - if todo > remainingOnLine { - todo = remainingOnLine - } - ss.queue(line[:todo]) - ss.cursorX += todo - line = line[todo:] - } -} - -func (ss *Shell) Write(buf []byte) (n int, err error) { - return ss.c.Write(buf) -} - -// ReadLine returns a line of input from the terminal. -func (ss *Shell) ReadLine() (line string, err error) { - ss.writeLine([]byte(ss.prompt)) - ss.c.Write(ss.outBuf) - ss.outBuf = ss.outBuf[:0] - - for { - // ss.remainder is a slice at the beginning of ss.inBuf - // containing a partial key sequence - readBuf := ss.inBuf[len(ss.remainder):] - var n int - n, err = ss.c.Read(readBuf) - if err != nil { - return - } - - if err == nil { - ss.remainder = ss.inBuf[:n+len(ss.remainder)] - rest := ss.remainder - lineOk := false - for !lineOk { - var key int - key, rest = bytesToKey(rest) - if key < 0 { - break - } - if key == keyCtrlD { - return "", io.EOF - } - line, lineOk = ss.handleKey(key) - } - if len(rest) > 0 { - n := copy(ss.inBuf[:], rest) - ss.remainder = ss.inBuf[:n] - } else { - ss.remainder = nil - } - ss.c.Write(ss.outBuf) - ss.outBuf = ss.outBuf[:0] - if lineOk { - return - } - continue - } - } - panic("unreachable") -} diff --git a/libgo/go/exp/terminal/terminal.go b/libgo/go/exp/terminal/terminal.go index 5732543ffc2..18d76cd6b90 100644 --- a/libgo/go/exp/terminal/terminal.go +++ b/libgo/go/exp/terminal/terminal.go @@ -2,102 +2,361 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package terminal provides support functions for dealing with terminals, as -// commonly found on UNIX systems. -// -// Putting a terminal into raw mode is the most common requirement: -// -// oldState, err := terminal.MakeRaw(0) -// if err != nil { -// panic(err.String()) -// } -// defer terminal.Restore(0, oldState) package terminal -import ( - "io" - "os" - "syscall" -) +import "io" + +// Terminal contains the state for running a VT100 terminal that is capable of +// reading lines of input. +type Terminal struct { + c io.ReadWriter + prompt string + + // line is the current line being entered. + line []byte + // pos is the logical position of the cursor in line + pos int + + // 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 + // the current line is 0. + cursorX, cursorY int + // maxLine is the greatest value of cursorY so far. + maxLine int + + termWidth, termHeight int -// State contains the state of a terminal. -type State struct { - termios syscall.Termios + // outBuf contains the terminal data to be sent. + outBuf []byte + // remainder contains the remainder of any partial key sequences after + // a read. It aliases into inBuf. + remainder []byte + inBuf [256]byte } -// IsTerminal returns true if the given file descriptor is a terminal. -func IsTerminal(fd int) bool { - var termios syscall.Termios - e := syscall.Tcgetattr(fd, &termios) - return e == 0 +// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is +// a local terminal, that terminal must first have been put into raw mode. +// prompt is a string that is written at the start of each input line (i.e. +// "> "). +func NewTerminal(c io.ReadWriter, prompt string) *Terminal { + return &Terminal{ + c: c, + prompt: prompt, + termWidth: 80, + termHeight: 24, + } } -// MakeRaw put the terminal connected to the given file descriptor into raw -// mode and returns the previous state of the terminal so that it can be -// restored. -func MakeRaw(fd int) (*State, error) { - var oldState State - if e := syscall.Tcgetattr(fd, &oldState.termios); e != 0 { - return nil, os.Errno(e) +const ( + keyCtrlD = 4 + keyEnter = '\r' + keyEscape = 27 + keyBackspace = 127 + keyUnknown = 256 + iota + keyUp + keyDown + keyLeft + keyRight + keyAltLeft + keyAltRight +) + +// bytesToKey tries to parse a key sequence from b. If successful, it returns +// the key and the remainder of the input. Otherwise it returns -1. +func bytesToKey(b []byte) (int, []byte) { + if len(b) == 0 { + return -1, nil + } + + if b[0] != keyEscape { + return int(b[0]), b[1:] + } + + if len(b) >= 3 && b[0] == keyEscape && b[1] == '[' { + switch b[2] { + case 'A': + return keyUp, b[3:] + case 'B': + return keyDown, b[3:] + case 'C': + return keyRight, b[3:] + case 'D': + return keyLeft, b[3:] + } + } + + if len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { + switch b[5] { + case 'C': + return keyAltRight, b[6:] + case 'D': + return keyAltLeft, b[6:] + } + } + + // If we get here then we have a key that we don't recognise, or a + // partial sequence. It's not clear how one should find the end of a + // sequence without knowing them all, but it seems that [a-zA-Z] only + // appears at the end of a sequence. + for i, c := range b[0:] { + if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' { + return keyUnknown, b[i+1:] + } } - newState := oldState.termios - newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF - newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG - if e := syscall.Tcsetattr(fd, syscall.TCSANOW, &newState); e != 0 { - return nil, os.Errno(e) + return -1, b +} + +// 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 } - return &oldState, nil + oldLen := len(t.outBuf) + t.outBuf = t.outBuf[:len(t.outBuf)+len(data)] + copy(t.outBuf[oldLen:], data) } -// Restore restores the terminal connected to the given file descriptor to a -// previous state. -func Restore(fd int, state *State) error { - e := syscall.Tcsetattr(fd, syscall.TCSANOW, &state.termios) - return os.Errno(e) +var eraseUnderCursor = []byte{' ', keyEscape, '[', 'D'} + +func isPrintable(key int) bool { + return key >= 32 && key < 127 } -// 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. -func ReadPassword(fd int) ([]byte, error) { - var oldState syscall.Termios - if e := syscall.Tcgetattr(fd, &oldState); e != 0 { - return nil, os.Errno(e) +// 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) { + x := len(t.prompt) + pos + y := x / t.termWidth + x = x % t.termWidth + + up := 0 + if y < t.cursorY { + up = t.cursorY - y } - newState := oldState - newState.Lflag &^= syscall.ECHO - if e := syscall.Tcsetattr(fd, syscall.TCSANOW, &newState); e != 0 { - return nil, os.Errno(e) + down := 0 + if y > t.cursorY { + down = y - t.cursorY } - defer func() { - syscall.Tcsetattr(fd, syscall.TCSANOW, &oldState) - }() + left := 0 + if x < t.cursorX { + left = t.cursorX - x + } - var buf [16]byte - var ret []byte - for { - n, errno := syscall.Read(fd, buf[:]) - if errno != 0 { - return nil, os.Errno(errno) + right := 0 + if x > t.cursorX { + right = x - t.cursorX + } + + movement := make([]byte, 3*(up+down+left+right)) + m := movement + for i := 0; i < up; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'A' + m = m[3:] + } + for i := 0; i < down; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'B' + m = m[3:] + } + for i := 0; i < left; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'D' + m = m[3:] + } + for i := 0; i < right; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'C' + m = m[3:] + } + + t.cursorX = x + t.cursorY = y + t.queue(movement) +} + +const maxLineLength = 4096 + +// handleKey processes the given key and, optionally, returns a line of text +// that the user has entered. +func (t *Terminal) handleKey(key int) (line string, ok bool) { + switch key { + case keyBackspace: + if t.pos == 0 { + return + } + 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) + t.queue(eraseUnderCursor) + case keyAltLeft: + // move left by a word. + if t.pos == 0 { + return + } + t.pos-- + for t.pos > 0 { + if t.line[t.pos] != ' ' { + break + } + t.pos-- + } + for t.pos > 0 { + if t.line[t.pos] == ' ' { + t.pos++ + break + } + t.pos-- + } + t.moveCursorToPos(t.pos) + case keyAltRight: + // move right by a word. + for t.pos < len(t.line) { + if t.line[t.pos] == ' ' { + break + } + t.pos++ } - if n == 0 { - if len(ret) == 0 { - return nil, io.EOF + for t.pos < len(t.line) { + if t.line[t.pos] != ' ' { + break + } + t.pos++ + } + t.moveCursorToPos(t.pos) + case keyLeft: + if t.pos == 0 { + return + } + t.pos-- + t.moveCursorToPos(t.pos) + case keyRight: + if t.pos == len(t.line) { + return + } + t.pos++ + t.moveCursorToPos(t.pos) + case keyEnter: + t.moveCursorToPos(len(t.line)) + t.queue([]byte("\r\n")) + line = string(t.line) + ok = true + t.line = t.line[:0] + t.pos = 0 + t.cursorX = 0 + t.cursorY = 0 + t.maxLine = 0 + default: + if !isPrintable(key) { + return + } + if len(t.line) == maxLineLength { + return + } + if len(t.line) == cap(t.line) { + newLine := make([]byte, len(t.line), 2*(1+len(t.line))) + copy(newLine, t.line) + t.line = newLine + } + 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:]) + t.pos++ + t.moveCursorToPos(t.pos) + } + return +} + +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 } - break } - if buf[n-1] == '\n' { - n-- + + remainingOnLine := t.termWidth - t.cursorX + todo := len(line) + if todo > remainingOnLine { + todo = remainingOnLine } - ret = append(ret, buf[:n]...) - if n < len(buf) { - break + t.queue(line[:todo]) + t.cursorX += todo + line = line[todo:] + } +} + +func (t *Terminal) Write(buf []byte) (n int, err error) { + return t.c.Write(buf) +} + +// ReadLine returns a line of input from the terminal. +func (t *Terminal) ReadLine() (line string, err error) { + if t.cursorX == 0 { + t.writeLine([]byte(t.prompt)) + t.c.Write(t.outBuf) + t.outBuf = t.outBuf[:0] + } + + for { + // t.remainder is a slice at the beginning of t.inBuf + // containing a partial key sequence + readBuf := t.inBuf[len(t.remainder):] + var n int + n, err = t.c.Read(readBuf) + if err != nil { + return + } + + if err == nil { + t.remainder = t.inBuf[:n+len(t.remainder)] + rest := t.remainder + lineOk := false + for !lineOk { + var key int + key, rest = bytesToKey(rest) + if key < 0 { + break + } + if key == keyCtrlD { + return "", io.EOF + } + line, lineOk = t.handleKey(key) + } + if len(rest) > 0 { + n := copy(t.inBuf[:], rest) + t.remainder = t.inBuf[:n] + } else { + t.remainder = nil + } + t.c.Write(t.outBuf) + t.outBuf = t.outBuf[:0] + if lineOk { + return + } + continue } } + panic("unreachable") +} - return ret, nil +func (t *Terminal) SetSize(width, height int) { + t.termWidth, t.termHeight = width, height } diff --git a/libgo/go/exp/terminal/shell_test.go b/libgo/go/exp/terminal/terminal_test.go index 8a76a85d5dc..a2197210e2a 100644 --- a/libgo/go/exp/terminal/shell_test.go +++ b/libgo/go/exp/terminal/terminal_test.go @@ -41,7 +41,7 @@ func (c *MockTerminal) Write(data []byte) (n int, err error) { func TestClose(t *testing.T) { c := &MockTerminal{} - ss := NewShell(c, "> ") + ss := NewTerminal(c, "> ") line, err := ss.ReadLine() if line != "" { t.Errorf("Expected empty line but got: %s", line) @@ -95,7 +95,7 @@ func TestKeyPresses(t *testing.T) { toSend: []byte(test.in), bytesPerRead: j, } - ss := NewShell(c, "> ") + ss := NewTerminal(c, "> ") line, err := ss.ReadLine() if line != test.line { t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line) diff --git a/libgo/go/exp/terminal/util.go b/libgo/go/exp/terminal/util.go new file mode 100644 index 00000000000..03035673869 --- /dev/null +++ b/libgo/go/exp/terminal/util.go @@ -0,0 +1,102 @@ +// 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 terminal provides support functions for dealing with terminals, as +// commonly found on UNIX systems. +// +// Putting a terminal into raw mode is the most common requirement: +// +// oldState, err := terminal.MakeRaw(0) +// if err != nil { +// panic(err.String()) +// } +// defer terminal.Restore(0, oldState) +package terminal + +import ( + "io" + "syscall" +) + +// State contains the state of a terminal. +type State struct { + termios syscall.Termios +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd int) bool { + var termios syscall.Termios + err := syscall.Tcgetattr(fd, &termios) + return err == nil +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd int) (*State, error) { + var oldState State + if err := syscall.Tcgetattr(fd, &oldState.termios); err != nil { + return nil, err + } + + newState := oldState.termios + newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF + newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG + if err := syscall.Tcsetattr(fd, syscall.TCSANOW, &newState); err != nil { + return nil, err + } + + return &oldState, nil +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd int, state *State) error { + err := syscall.Tcsetattr(fd, syscall.TCSANOW, &state.termios) + return err +} + +// 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. +func ReadPassword(fd int) ([]byte, error) { + var oldState syscall.Termios + if err := syscall.Tcgetattr(fd, &oldState); err != nil { + return nil, err + } + + newState := oldState + newState.Lflag &^= syscall.ECHO + if err := syscall.Tcsetattr(fd, syscall.TCSANOW, &newState); err != nil { + return nil, err + } + + defer func() { + syscall.Tcsetattr(fd, syscall.TCSANOW, &oldState) + }() + + var buf [16]byte + var ret []byte + for { + n, err := syscall.Read(fd, buf[:]) + if err != nil { + return nil, err + } + if n == 0 { + if len(ret) == 0 { + return nil, io.EOF + } + break + } + if buf[n-1] == '\n' { + n-- + } + ret = append(ret, buf[:n]...) + if n < len(buf) { + break + } + } + + return ret, nil +} diff --git a/libgo/go/exp/types/check_test.go b/libgo/go/exp/types/check_test.go index 4a30acf2315..35535ea406f 100644 --- a/libgo/go/exp/types/check_test.go +++ b/libgo/go/exp/types/check_test.go @@ -202,7 +202,7 @@ func TestCheck(t *testing.T) { // For easy debugging w/o changing the testing code, // if there is a local test file, only test that file. const testfile = "test.go" - if fi, err := os.Stat(testfile); err == nil && fi.IsRegular() { + if fi, err := os.Stat(testfile); err == nil && !fi.IsDir() { fmt.Printf("WARNING: Testing only %s (remove it to run all tests)\n", testfile) check(t, testfile, []string{testfile}) return diff --git a/libgo/go/exp/types/gcimporter.go b/libgo/go/exp/types/gcimporter.go index 4167caf3f0e..16a8667ff66 100644 --- a/libgo/go/exp/types/gcimporter.go +++ b/libgo/go/exp/types/gcimporter.go @@ -59,7 +59,7 @@ func findPkg(path string) (filename, id string) { // try extensions for _, ext := range pkgExts { filename = noext + ext - if f, err := os.Stat(filename); err == nil && f.IsRegular() { + if f, err := os.Stat(filename); err == nil && !f.IsDir() { return } } diff --git a/libgo/go/exp/types/gcimporter_test.go b/libgo/go/exp/types/gcimporter_test.go index 3f66d226153..7475d352209 100644 --- a/libgo/go/exp/types/gcimporter_test.go +++ b/libgo/go/exp/types/gcimporter_test.go @@ -58,32 +58,32 @@ func testPath(t *testing.T, path string) bool { return true } -const maxTime = 3e9 // maximum allotted testing time in ns +const maxTime = 3 * time.Second -func testDir(t *testing.T, dir string, endTime int64) (nimports int) { +func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) { dirname := filepath.Join(pkgRoot, dir) list, err := ioutil.ReadDir(dirname) if err != nil { t.Errorf("testDir(%s): %s", dirname, err) } for _, f := range list { - if time.Nanoseconds() >= endTime { + if time.Now().After(endTime) { t.Log("testing time used up") return } switch { - case f.IsRegular(): + case !f.IsDir(): // try extensions for _, ext := range pkgExts { - if strings.HasSuffix(f.Name, ext) { - name := f.Name[0 : len(f.Name)-len(ext)] // remove extension + if strings.HasSuffix(f.Name(), ext) { + name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension if testPath(t, filepath.Join(dir, name)) { nimports++ } } } - case f.IsDirectory(): - nimports += testDir(t, filepath.Join(dir, f.Name), endTime) + case f.IsDir(): + nimports += testDir(t, filepath.Join(dir, f.Name()), endTime) } } return @@ -96,6 +96,6 @@ func TestGcImport(t *testing.T) { if testPath(t, "./testdata/exports") { nimports++ } - nimports += testDir(t, "", time.Nanoseconds()+maxTime) // installed packages + nimports += testDir(t, "", time.Now().Add(maxTime)) // installed packages t.Logf("tested %d imports", nimports) } diff --git a/libgo/go/fmt/fmt_test.go b/libgo/go/fmt/fmt_test.go index db83f85f957..00aac798cb0 100644 --- a/libgo/go/fmt/fmt_test.go +++ b/libgo/go/fmt/fmt_test.go @@ -47,8 +47,10 @@ func TestFmtInterface(t *testing.T) { const b32 uint32 = 1<<32 - 1 const b64 uint64 = 1<<64 - 1 -var array = []int{1, 2, 3, 4, 5} -var iarray = []interface{}{1, "hello", 2.5, nil} +var array = [5]int{1, 2, 3, 4, 5} +var iarray = [4]interface{}{1, "hello", 2.5, nil} +var slice = array[:] +var islice = iarray[:] type A struct { i int @@ -327,6 +329,12 @@ var fmttests = []struct { {"%v", &array, "&[1 2 3 4 5]"}, {"%v", &iarray, "&[1 hello 2.5 <nil>]"}, + // slices + {"%v", slice, "[1 2 3 4 5]"}, + {"%v", islice, "[1 hello 2.5 <nil>]"}, + {"%v", &slice, "&[1 2 3 4 5]"}, + {"%v", &islice, "&[1 hello 2.5 <nil>]"}, + // complexes with %v {"%v", 1 + 2i, "(1+2i)"}, {"%v", complex64(1 + 2i), "(1+2i)"}, @@ -357,6 +365,14 @@ var fmttests = []struct { {"%#v", map[string]B{"a": {1, 2}}, `map[string] fmt_test.B{"a":fmt_test.B{I:1, j:2}}`}, {"%#v", []string{"a", "b"}, `[]string{"a", "b"}`}, {"%#v", SI{}, `fmt_test.SI{I:interface {}(nil)}`}, + {"%#v", []int(nil), `[]int(nil)`}, + {"%#v", []int{}, `[]int{}`}, + {"%#v", array, `[5]int{1, 2, 3, 4, 5}`}, + {"%#v", &array, `&[5]int{1, 2, 3, 4, 5}`}, + {"%#v", iarray, `[4]interface {}{1, "hello", 2.5, interface {}(nil)}`}, + {"%#v", &iarray, `&[4]interface {}{1, "hello", 2.5, interface {}(nil)}`}, + {"%#v", map[int]byte(nil), `map[int] uint8(nil)`}, + {"%#v", map[int]byte{}, `map[int] uint8{}`}, // slices with other formats {"%#x", []int{1, 2, 15}, `[0x1 0x2 0xf]`}, diff --git a/libgo/go/fmt/print.go b/libgo/go/fmt/print.go index bfa88d18704..e5ca1172405 100644 --- a/libgo/go/fmt/print.go +++ b/libgo/go/fmt/print.go @@ -795,6 +795,10 @@ BigSwitch: case reflect.Map: if goSyntax { p.buf.WriteString(f.Type().String()) + if f.IsNil() { + p.buf.WriteString("(nil)") + break + } p.buf.WriteByte('{') } else { p.buf.Write(mapBytes) @@ -873,6 +877,10 @@ BigSwitch: } if goSyntax { p.buf.WriteString(value.Type().String()) + if f.Kind() == reflect.Slice && f.IsNil() { + p.buf.WriteString("(nil)") + break + } p.buf.WriteByte('{') } else { p.buf.WriteByte('[') diff --git a/libgo/go/fmt/scan_test.go b/libgo/go/fmt/scan_test.go index d3c39be6071..0689bf3b6e4 100644 --- a/libgo/go/fmt/scan_test.go +++ b/libgo/go/fmt/scan_test.go @@ -324,7 +324,7 @@ var x, y Xs var z IntString var multiTests = []ScanfMultiTest{ - {"", "", nil, nil, ""}, + {"", "", []interface{}{}, []interface{}{}, ""}, {"%d", "23", args(&i), args(23), ""}, {"%2s%3s", "22333", args(&s, &t), args("22", "333"), ""}, {"%2d%3d", "44555", args(&i, &j), args(44, 555), ""}, @@ -378,7 +378,7 @@ func testScan(name string, t *testing.T, scan func(r io.Reader, a ...interface{} } val := v.Interface() if !reflect.DeepEqual(val, test.out) { - t.Errorf("%s scanning %q: expected %v got %v, type %T", name, test.text, test.out, val, val) + t.Errorf("%s scanning %q: expected %#v got %#v, type %T", name, test.text, test.out, val, val) } } } @@ -417,7 +417,7 @@ func TestScanf(t *testing.T) { } val := v.Interface() if !reflect.DeepEqual(val, test.out) { - t.Errorf("scanning (%q, %q): expected %v got %v, type %T", test.format, test.text, test.out, val, val) + t.Errorf("scanning (%q, %q): expected %#v got %#v, type %T", test.format, test.text, test.out, val, val) } } } @@ -520,7 +520,7 @@ func testScanfMulti(name string, t *testing.T) { } result := resultVal.Interface() if !reflect.DeepEqual(result, test.out) { - t.Errorf("scanning (%q, %q): expected %v got %v", test.format, test.text, test.out, result) + t.Errorf("scanning (%q, %q): expected %#v got %#v", test.format, test.text, test.out, result) } } } diff --git a/libgo/go/go/ast/ast.go b/libgo/go/go/ast/ast.go index a0aa5ff1204..1485f351c07 100644 --- a/libgo/go/go/ast/ast.go +++ b/libgo/go/go/ast/ast.go @@ -412,29 +412,29 @@ func (x *ChanType) End() token.Pos { return x.Value.End() } // exprNode() ensures that only expression/type nodes can be // assigned to an ExprNode. // -func (x *BadExpr) exprNode() {} -func (x *Ident) exprNode() {} -func (x *Ellipsis) exprNode() {} -func (x *BasicLit) exprNode() {} -func (x *FuncLit) exprNode() {} -func (x *CompositeLit) exprNode() {} -func (x *ParenExpr) exprNode() {} -func (x *SelectorExpr) exprNode() {} -func (x *IndexExpr) exprNode() {} -func (x *SliceExpr) exprNode() {} -func (x *TypeAssertExpr) exprNode() {} -func (x *CallExpr) exprNode() {} -func (x *StarExpr) exprNode() {} -func (x *UnaryExpr) exprNode() {} -func (x *BinaryExpr) exprNode() {} -func (x *KeyValueExpr) exprNode() {} - -func (x *ArrayType) exprNode() {} -func (x *StructType) exprNode() {} -func (x *FuncType) exprNode() {} -func (x *InterfaceType) exprNode() {} -func (x *MapType) exprNode() {} -func (x *ChanType) exprNode() {} +func (*BadExpr) exprNode() {} +func (*Ident) exprNode() {} +func (*Ellipsis) exprNode() {} +func (*BasicLit) exprNode() {} +func (*FuncLit) exprNode() {} +func (*CompositeLit) exprNode() {} +func (*ParenExpr) exprNode() {} +func (*SelectorExpr) exprNode() {} +func (*IndexExpr) exprNode() {} +func (*SliceExpr) exprNode() {} +func (*TypeAssertExpr) exprNode() {} +func (*CallExpr) exprNode() {} +func (*StarExpr) exprNode() {} +func (*UnaryExpr) exprNode() {} +func (*BinaryExpr) exprNode() {} +func (*KeyValueExpr) exprNode() {} + +func (*ArrayType) exprNode() {} +func (*StructType) exprNode() {} +func (*FuncType) exprNode() {} +func (*InterfaceType) exprNode() {} +func (*MapType) exprNode() {} +func (*ChanType) exprNode() {} // ---------------------------------------------------------------------------- // Convenience functions for Idents @@ -711,27 +711,27 @@ func (s *RangeStmt) End() token.Pos { return s.Body.End() } // stmtNode() ensures that only statement nodes can be // assigned to a StmtNode. // -func (s *BadStmt) stmtNode() {} -func (s *DeclStmt) stmtNode() {} -func (s *EmptyStmt) stmtNode() {} -func (s *LabeledStmt) stmtNode() {} -func (s *ExprStmt) stmtNode() {} -func (s *SendStmt) stmtNode() {} -func (s *IncDecStmt) stmtNode() {} -func (s *AssignStmt) stmtNode() {} -func (s *GoStmt) stmtNode() {} -func (s *DeferStmt) stmtNode() {} -func (s *ReturnStmt) stmtNode() {} -func (s *BranchStmt) stmtNode() {} -func (s *BlockStmt) stmtNode() {} -func (s *IfStmt) stmtNode() {} -func (s *CaseClause) stmtNode() {} -func (s *SwitchStmt) stmtNode() {} -func (s *TypeSwitchStmt) stmtNode() {} -func (s *CommClause) stmtNode() {} -func (s *SelectStmt) stmtNode() {} -func (s *ForStmt) stmtNode() {} -func (s *RangeStmt) stmtNode() {} +func (*BadStmt) stmtNode() {} +func (*DeclStmt) stmtNode() {} +func (*EmptyStmt) stmtNode() {} +func (*LabeledStmt) stmtNode() {} +func (*ExprStmt) stmtNode() {} +func (*SendStmt) stmtNode() {} +func (*IncDecStmt) stmtNode() {} +func (*AssignStmt) stmtNode() {} +func (*GoStmt) stmtNode() {} +func (*DeferStmt) stmtNode() {} +func (*ReturnStmt) stmtNode() {} +func (*BranchStmt) stmtNode() {} +func (*BlockStmt) stmtNode() {} +func (*IfStmt) stmtNode() {} +func (*CaseClause) stmtNode() {} +func (*SwitchStmt) stmtNode() {} +func (*TypeSwitchStmt) stmtNode() {} +func (*CommClause) stmtNode() {} +func (*SelectStmt) stmtNode() {} +func (*ForStmt) stmtNode() {} +func (*RangeStmt) stmtNode() {} // ---------------------------------------------------------------------------- // Declarations @@ -807,9 +807,9 @@ func (s *TypeSpec) End() token.Pos { return s.Type.End() } // specNode() ensures that only spec nodes can be // assigned to a Spec. // -func (s *ImportSpec) specNode() {} -func (s *ValueSpec) specNode() {} -func (s *TypeSpec) specNode() {} +func (*ImportSpec) specNode() {} +func (*ValueSpec) specNode() {} +func (*TypeSpec) specNode() {} // A declaration is represented by one of the following declaration nodes. // @@ -875,9 +875,9 @@ func (d *FuncDecl) End() token.Pos { // declNode() ensures that only declaration nodes can be // assigned to a DeclNode. // -func (d *BadDecl) declNode() {} -func (d *GenDecl) declNode() {} -func (d *FuncDecl) declNode() {} +func (*BadDecl) declNode() {} +func (*GenDecl) declNode() {} +func (*FuncDecl) declNode() {} // ---------------------------------------------------------------------------- // Files and packages diff --git a/libgo/go/go/ast/filter.go b/libgo/go/go/ast/filter.go index d7d4b4b6b6d..bec235e2f98 100644 --- a/libgo/go/go/ast/filter.go +++ b/libgo/go/go/ast/filter.go @@ -24,7 +24,7 @@ func exportFilter(name string) bool { // it returns false otherwise. // func FileExports(src *File) bool { - return FilterFile(src, exportFilter) + return filterFile(src, exportFilter, true) } // PackageExports trims the AST for a Go package in place such that @@ -35,7 +35,7 @@ func FileExports(src *File) bool { // it returns false otherwise. // func PackageExports(pkg *Package) bool { - return FilterPackage(pkg, exportFilter) + return filterPackage(pkg, exportFilter, true) } // ---------------------------------------------------------------------------- @@ -72,7 +72,7 @@ func fieldName(x Expr) *Ident { return nil } -func filterFieldList(fields *FieldList, filter Filter) (removedFields bool) { +func filterFieldList(fields *FieldList, filter Filter, export bool) (removedFields bool) { if fields == nil { return false } @@ -93,8 +93,8 @@ func filterFieldList(fields *FieldList, filter Filter) (removedFields bool) { keepField = len(f.Names) > 0 } if keepField { - if filter == exportFilter { - filterType(f.Type, filter) + if export { + filterType(f.Type, filter, export) } list[j] = f j++ @@ -107,84 +107,84 @@ func filterFieldList(fields *FieldList, filter Filter) (removedFields bool) { return } -func filterParamList(fields *FieldList, filter Filter) bool { +func filterParamList(fields *FieldList, filter Filter, export bool) bool { if fields == nil { return false } var b bool for _, f := range fields.List { - if filterType(f.Type, filter) { + if filterType(f.Type, filter, export) { b = true } } return b } -func filterType(typ Expr, f Filter) bool { +func filterType(typ Expr, f Filter, export bool) bool { switch t := typ.(type) { case *Ident: return f(t.Name) case *ParenExpr: - return filterType(t.X, f) + return filterType(t.X, f, export) case *ArrayType: - return filterType(t.Elt, f) + return filterType(t.Elt, f, export) case *StructType: - if filterFieldList(t.Fields, f) { + if filterFieldList(t.Fields, f, export) { t.Incomplete = true } return len(t.Fields.List) > 0 case *FuncType: - b1 := filterParamList(t.Params, f) - b2 := filterParamList(t.Results, f) + b1 := filterParamList(t.Params, f, export) + b2 := filterParamList(t.Results, f, export) return b1 || b2 case *InterfaceType: - if filterFieldList(t.Methods, f) { + if filterFieldList(t.Methods, f, export) { t.Incomplete = true } return len(t.Methods.List) > 0 case *MapType: - b1 := filterType(t.Key, f) - b2 := filterType(t.Value, f) + b1 := filterType(t.Key, f, export) + b2 := filterType(t.Value, f, export) return b1 || b2 case *ChanType: - return filterType(t.Value, f) + return filterType(t.Value, f, export) } return false } -func filterSpec(spec Spec, f Filter) bool { +func filterSpec(spec Spec, f Filter, export bool) bool { switch s := spec.(type) { case *ValueSpec: s.Names = filterIdentList(s.Names, f) if len(s.Names) > 0 { - if f == exportFilter { - filterType(s.Type, f) + if export { + filterType(s.Type, f, export) } return true } case *TypeSpec: if f(s.Name.Name) { - if f == exportFilter { - filterType(s.Type, f) + if export { + filterType(s.Type, f, export) } return true } - if f != exportFilter { + if !export { // For general filtering (not just exports), // filter type even if name is not filtered // out. // If the type contains filtered elements, // keep the declaration. - return filterType(s.Type, f) + return filterType(s.Type, f, export) } } return false } -func filterSpecList(list []Spec, f Filter) []Spec { +func filterSpecList(list []Spec, f Filter, export bool) []Spec { j := 0 for _, s := range list { - if filterSpec(s, f) { + if filterSpec(s, f, export) { list[j] = s j++ } @@ -200,9 +200,13 @@ func filterSpecList(list []Spec, f Filter) []Spec { // filtering; it returns false otherwise. // func FilterDecl(decl Decl, f Filter) bool { + return filterDecl(decl, f, false) +} + +func filterDecl(decl Decl, f Filter, export bool) bool { switch d := decl.(type) { case *GenDecl: - d.Specs = filterSpecList(d.Specs, f) + d.Specs = filterSpecList(d.Specs, f, export) return len(d.Specs) > 0 case *FuncDecl: return f(d.Name.Name) @@ -221,9 +225,13 @@ func FilterDecl(decl Decl, f Filter) bool { // left after filtering; it returns false otherwise. // func FilterFile(src *File, f Filter) bool { + return filterFile(src, f, false) +} + +func filterFile(src *File, f Filter, export bool) bool { j := 0 for _, d := range src.Decls { - if FilterDecl(d, f) { + if filterDecl(d, f, export) { src.Decls[j] = d j++ } @@ -244,9 +252,13 @@ func FilterFile(src *File, f Filter) bool { // left after filtering; it returns false otherwise. // func FilterPackage(pkg *Package, f Filter) bool { + return filterPackage(pkg, f, false) +} + +func filterPackage(pkg *Package, f Filter, export bool) bool { hasDecls := false for _, src := range pkg.Files { - if FilterFile(src, f) { + if filterFile(src, f, export) { hasDecls = true } } diff --git a/libgo/go/go/ast/resolve.go b/libgo/go/go/ast/resolve.go index b24688d2ea3..c7c8e7c101e 100644 --- a/libgo/go/go/ast/resolve.go +++ b/libgo/go/go/ast/resolve.go @@ -113,7 +113,7 @@ func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer, importErrors = true continue } - path, _ := strconv.Unquote(string(spec.Path.Value)) + path, _ := strconv.Unquote(spec.Path.Value) pkg, err := importer(imports, path) if err != nil { p.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err) diff --git a/libgo/go/go/build/build.go b/libgo/go/go/build/build.go index e3de8d0fa7f..5301ab53e51 100644 --- a/libgo/go/go/build/build.go +++ b/libgo/go/go/build/build.go @@ -15,6 +15,7 @@ import ( "regexp" "runtime" "strings" + "time" ) // Build produces a build Script for the given package. @@ -150,7 +151,7 @@ func (s *Script) Run() error { // Stale returns true if the build's inputs are newer than its outputs. func (s *Script) Stale() bool { - var latest int64 + var latest time.Time // get latest mtime of outputs for _, file := range s.Output { fi, err := os.Stat(file) @@ -158,13 +159,13 @@ func (s *Script) Stale() bool { // any error reading output files means stale return true } - if m := fi.Mtime_ns; m > latest { - latest = m + if mtime := fi.ModTime(); mtime.After(latest) { + latest = mtime } } for _, file := range s.Input { fi, err := os.Stat(file) - if err != nil || fi.Mtime_ns > latest { + if err != nil || fi.ModTime().After(latest) { // any error reading input files means stale // (attempt to rebuild to figure out why) return true diff --git a/libgo/go/go/build/build_test.go b/libgo/go/go/build/build_test.go index db8bc6c8a59..e22a49aa3d6 100644 --- a/libgo/go/go/build/build_test.go +++ b/libgo/go/go/build/build_test.go @@ -37,18 +37,20 @@ var buildPkgs = []struct { { "go/build/cmdtest", &DirInfo{ - GoFiles: []string{"main.go"}, - Package: "main", - Imports: []string{"go/build/pkgtest"}, + GoFiles: []string{"main.go"}, + Package: "main", + Imports: []string{"go/build/pkgtest"}, + TestImports: []string{}, }, }, { "go/build/cgotest", &DirInfo{ - CgoFiles: []string{"cgotest.go"}, - CFiles: []string{"cgotest.c"}, - Imports: []string{"C", "unsafe"}, - Package: "cgotest", + CgoFiles: []string{"cgotest.go"}, + CFiles: []string{"cgotest.c"}, + Imports: []string{"C", "unsafe"}, + TestImports: []string{}, + Package: "cgotest", }, }, } diff --git a/libgo/go/go/build/dir.go b/libgo/go/go/build/dir.go index 0d175c75deb..12dc99942a7 100644 --- a/libgo/go/go/build/dir.go +++ b/libgo/go/go/build/dir.go @@ -38,16 +38,16 @@ type Context struct { // format of the strings dir and file: they can be // slash-separated, backslash-separated, even URLs. - // ReadDir returns a slice of *os.FileInfo, sorted by Name, + // ReadDir returns a slice of os.FileInfo, sorted by Name, // describing the content of the named directory. // The dir argument is the argument to ScanDir. // If ReadDir is nil, ScanDir uses io.ReadDir. - ReadDir func(dir string) (fi []*os.FileInfo, err error) + ReadDir func(dir string) (fi []os.FileInfo, err error) // ReadFile returns the content of the file named file // in the directory named dir. The dir argument is the // argument to ScanDir, and the file argument is the - // Name field from an *os.FileInfo returned by ReadDir. + // Name field from an os.FileInfo returned by ReadDir. // The returned path is the full name of the file, to be // used in error messages. // @@ -56,7 +56,7 @@ type Context struct { ReadFile func(dir, file string) (path string, content []byte, err error) } -func (ctxt *Context) readDir(dir string) ([]*os.FileInfo, error) { +func (ctxt *Context) readDir(dir string) ([]os.FileInfo, error) { if f := ctxt.ReadDir; f != nil { return f(dir) } @@ -140,18 +140,19 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) { testImported := make(map[string]bool) fset := token.NewFileSet() for _, d := range dirs { - if !d.IsRegular() { + if d.IsDir() { continue } - if strings.HasPrefix(d.Name, "_") || - strings.HasPrefix(d.Name, ".") { + name := d.Name() + if strings.HasPrefix(name, "_") || + strings.HasPrefix(name, ".") { continue } - if !ctxt.goodOSArchFile(d.Name) { + if !ctxt.goodOSArchFile(name) { continue } - ext := path.Ext(d.Name) + ext := path.Ext(name) switch ext { case ".go", ".c", ".s": // tentatively okay @@ -161,7 +162,7 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) { } // Look for +build comments to accept or reject the file. - filename, data, err := ctxt.readFile(dir, d.Name) + filename, data, err := ctxt.readFile(dir, name) if err != nil { return nil, err } @@ -172,10 +173,10 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) { // Going to save the file. For non-Go files, can stop here. switch ext { case ".c": - di.CFiles = append(di.CFiles, d.Name) + di.CFiles = append(di.CFiles, name) continue case ".s": - di.SFiles = append(di.SFiles, d.Name) + di.SFiles = append(di.SFiles, name) continue } @@ -192,7 +193,7 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) { continue } - isTest := strings.HasSuffix(d.Name, "_test.go") + isTest := strings.HasSuffix(name, "_test.go") if isTest && strings.HasSuffix(pkg, "_test") { pkg = pkg[:len(pkg)-len("_test")] } @@ -255,15 +256,15 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) { } } if isCgo { - di.CgoFiles = append(di.CgoFiles, d.Name) + di.CgoFiles = append(di.CgoFiles, name) } else if isTest { if pkg == string(pf.Name.Name) { - di.TestGoFiles = append(di.TestGoFiles, d.Name) + di.TestGoFiles = append(di.TestGoFiles, name) } else { - di.XTestGoFiles = append(di.XTestGoFiles, d.Name) + di.XTestGoFiles = append(di.XTestGoFiles, name) } } else { - di.GoFiles = append(di.GoFiles, d.Name) + di.GoFiles = append(di.GoFiles, name) } } if di.Package == "" { diff --git a/libgo/go/go/build/path.go b/libgo/go/go/build/path.go index 7ccb12993b3..91d6c430a9d 100644 --- a/libgo/go/go/build/path.go +++ b/libgo/go/go/build/path.go @@ -70,7 +70,7 @@ func (t *Tree) HasSrc(pkg string) bool { if err != nil { return false } - return fi.IsDirectory() + return fi.IsDir() } // HasPkg returns whether the given package's @@ -80,7 +80,7 @@ func (t *Tree) HasPkg(pkg string) bool { if err != nil { return false } - return fi.IsRegular() + return !fi.IsDir() // TODO(adg): check object version is consistent } diff --git a/libgo/go/go/doc/comment.go b/libgo/go/go/doc/comment.go index 19216f85b96..d7bb384ed03 100644 --- a/libgo/go/go/doc/comment.go +++ b/libgo/go/go/doc/comment.go @@ -7,11 +7,14 @@ package doc import ( + "bytes" "go/ast" "io" "regexp" "strings" "text/template" // for HTMLEscape + "unicode" + "unicode/utf8" ) func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' } @@ -168,6 +171,8 @@ var ( html_endp = []byte("</p>\n") html_pre = []byte("<pre>") html_endpre = []byte("</pre>\n") + html_h = []byte("<h3>") + html_endh = []byte("</h3>\n") ) // Emphasize and escape a line of text for HTML. URLs are converted into links; @@ -268,6 +273,51 @@ func unindent(block [][]byte) { } } +// heading returns the (possibly trimmed) line if it passes as a valid section +// heading; otherwise it returns nil. +func heading(line []byte) []byte { + line = bytes.TrimSpace(line) + if len(line) == 0 { + return nil + } + + // a heading must start with an uppercase letter + r, _ := utf8.DecodeRune(line) + if !unicode.IsLetter(r) || !unicode.IsUpper(r) { + return nil + } + + // it must end in a letter, digit or ':' + r, _ = utf8.DecodeLastRune(line) + if !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != ':' { + return nil + } + + // strip trailing ':', if any + if r == ':' { + line = line[0 : len(line)-1] + } + + // exclude lines with illegal characters + if bytes.IndexAny(line, ",.;:!?+*/=()[]{}_^°&§~%#@<\">\\") >= 0 { + return nil + } + + // allow "'" for possessive "'s" only + for b := line; ; { + i := bytes.IndexRune(b, '\'') + if i < 0 { + break + } + if i+1 >= len(b) || b[i+1] != 's' || (i+2 < len(b) && b[i+2] != ' ') { + return nil // not followed by "s " + } + b = b[i+2:] + } + + return line +} + // Convert comment text to formatted HTML. // The comment was prepared by DocReader, // so it is known not to have leading, trailing blank lines @@ -276,6 +326,7 @@ func unindent(block [][]byte) { // // Turn each run of multiple \n into </p><p>. // Turn each run of indented lines into a <pre> block without indent. +// Enclose headings with header tags. // // URLs in the comment text are converted into links; if the URL also appears // in the words map, the link is taken from the map (if the corresponding map @@ -286,6 +337,8 @@ func unindent(block [][]byte) { // into a link. func ToHTML(w io.Writer, s []byte, words map[string]string) { inpara := false + lastWasBlank := false + lastWasHeading := false close := func() { if inpara { @@ -308,6 +361,7 @@ func ToHTML(w io.Writer, s []byte, words map[string]string) { // close paragraph close() i++ + lastWasBlank = true continue } if indentLen(line) > 0 { @@ -334,10 +388,30 @@ func ToHTML(w io.Writer, s []byte, words map[string]string) { emphasize(w, line, nil, false) // no nice text formatting } w.Write(html_endpre) + lastWasHeading = false continue } + + if lastWasBlank && !lastWasHeading && i+2 < len(lines) && + isBlank(lines[i+1]) && !isBlank(lines[i+2]) && indentLen(lines[i+2]) == 0 { + // current line is non-blank, sourounded by blank lines + // and the next non-blank line is not indented: this + // might be a heading. + if head := heading(line); head != nil { + close() + w.Write(html_h) + template.HTMLEscape(w, head) + w.Write(html_endh) + i += 2 + lastWasHeading = true + continue + } + } + // open paragraph open() + lastWasBlank = false + lastWasHeading = false emphasize(w, lines[i], words, true) // nice text formatting i++ } diff --git a/libgo/go/go/doc/comment_test.go b/libgo/go/go/doc/comment_test.go new file mode 100644 index 00000000000..870660ad628 --- /dev/null +++ b/libgo/go/go/doc/comment_test.go @@ -0,0 +1,39 @@ +// 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 doc + +import ( + "testing" +) + +var headingTests = []struct { + line string + ok bool +}{ + {"Section", true}, + {"A typical usage", true}, + {"ΔΛΞ is Greek", true}, + {"Foo 42", true}, + {"", false}, + {"section", false}, + {"A typical usage:", true}, + {"δ is Greek", false}, + {"Foo §", false}, + {"Fermat's Last Sentence", true}, + {"Fermat's", true}, + {"'sX", false}, + {"Ted 'Too' Bar", false}, + {"Use n+m", false}, + {"Scanning:", true}, + {"N:M", false}, +} + +func TestIsHeading(t *testing.T) { + for _, tt := range headingTests { + if h := heading([]byte(tt.line)); (h != nil) != tt.ok { + t.Errorf("isHeading(%q) = %v, want %v", tt.line, h, tt.ok) + } + } +} diff --git a/libgo/go/go/doc/headscan.go b/libgo/go/go/doc/headscan.go new file mode 100644 index 00000000000..83f24627c95 --- /dev/null +++ b/libgo/go/go/doc/headscan.go @@ -0,0 +1,111 @@ +// 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. + +/* + The headscan command extracts comment headings from package files; + it is used to detect false positives which may require an adjustment + to the comment formatting heuristics in comment.go. + + Usage: headscan [-root root_directory] + + By default, the $GOROOT/src directory is scanned. +*/ +package main + +import ( + "bytes" + "flag" + "fmt" + "go/doc" + "go/parser" + "go/token" + "os" + "path/filepath" + "runtime" + "strings" +) + +var ( + root = flag.String("root", filepath.Join(runtime.GOROOT(), "src"), "root of filesystem tree to scan") + verbose = flag.Bool("v", false, "verbose mode") +) + +const ( + html_h = "<h3>" + html_endh = "</h3>\n" +) + +func isGoFile(fi os.FileInfo) bool { + return strings.HasSuffix(fi.Name(), ".go") && + !strings.HasSuffix(fi.Name(), "_test.go") +} + +func appendHeadings(list []string, comment string) []string { + var buf bytes.Buffer + doc.ToHTML(&buf, []byte(comment), nil) + for s := buf.String(); ; { + i := strings.Index(s, html_h) + if i < 0 { + break + } + i += len(html_h) + j := strings.Index(s, html_endh) + if j < 0 { + list = append(list, s[i:]) // incorrect HTML + break + } + list = append(list, s[i:j]) + s = s[j+len(html_endh):] + } + return list +} + +func main() { + flag.Parse() + fset := token.NewFileSet() + nheadings := 0 + err := filepath.Walk(*root, func(path string, fi os.FileInfo, err error) error { + if !fi.IsDir() { + return nil + } + pkgs, err := parser.ParseDir(fset, path, isGoFile, parser.ParseComments) + if err != nil { + if *verbose { + fmt.Fprintln(os.Stderr, err) + } + return nil + } + for _, pkg := range pkgs { + d := doc.NewPackageDoc(pkg, path) + list := appendHeadings(nil, d.Doc) + for _, d := range d.Consts { + list = appendHeadings(list, d.Doc) + } + for _, d := range d.Types { + list = appendHeadings(list, d.Doc) + } + for _, d := range d.Vars { + list = appendHeadings(list, d.Doc) + } + for _, d := range d.Funcs { + list = appendHeadings(list, d.Doc) + } + if len(list) > 0 { + // directories may contain multiple packages; + // print path and package name + fmt.Printf("%s (package %s)\n", path, pkg.Name) + for _, h := range list { + fmt.Printf("\t%s\n", h) + } + nheadings += len(list) + } + } + return nil + }) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + fmt.Println(nheadings, "headings found") +} diff --git a/libgo/go/go/parser/interface.go b/libgo/go/go/parser/interface.go index d3bab31c5a3..be11f461c3b 100644 --- a/libgo/go/go/parser/interface.go +++ b/libgo/go/go/parser/interface.go @@ -188,7 +188,7 @@ func ParseFiles(fset *token.FileSet, filenames []string, mode uint) (pkgs map[st // returned. If a parse error occurred, a non-nil but incomplete map and the // error are returned. // -func ParseDir(fset *token.FileSet, path string, filter func(*os.FileInfo) bool, mode uint) (map[string]*ast.Package, error) { +func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, mode uint) (map[string]*ast.Package, error) { fd, err := os.Open(path) if err != nil { return nil, err @@ -202,10 +202,9 @@ func ParseDir(fset *token.FileSet, path string, filter func(*os.FileInfo) bool, filenames := make([]string, len(list)) n := 0 - for i := 0; i < len(list); i++ { - d := &list[i] + for _, d := range list { if filter == nil || filter(d) { - filenames[n] = filepath.Join(path, d.Name) + filenames[n] = filepath.Join(path, d.Name()) n++ } } diff --git a/libgo/go/go/parser/parser_test.go b/libgo/go/go/parser/parser_test.go index dee90fbcf4c..f602db8896d 100644 --- a/libgo/go/go/parser/parser_test.go +++ b/libgo/go/go/parser/parser_test.go @@ -113,7 +113,7 @@ func nameFilter(filename string) bool { return true } -func dirFilter(f *os.FileInfo) bool { return nameFilter(f.Name) } +func dirFilter(f os.FileInfo) bool { return nameFilter(f.Name()) } func TestParse4(t *testing.T) { path := "." diff --git a/libgo/go/go/printer/nodes.go b/libgo/go/go/printer/nodes.go index 248e43d4e72..53f36092fda 100644 --- a/libgo/go/go/printer/nodes.go +++ b/libgo/go/go/printer/nodes.go @@ -1377,7 +1377,7 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) { // in RawFormat cfg := Config{Mode: RawFormat} var buf bytes.Buffer - if _, err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil { + if err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil { return } if buf.Len() <= maxSize { diff --git a/libgo/go/go/printer/performance_test.go b/libgo/go/go/printer/performance_test.go index 84fb2808eba..dbd942292b5 100644 --- a/libgo/go/go/printer/performance_test.go +++ b/libgo/go/go/printer/performance_test.go @@ -20,7 +20,7 @@ import ( var testfile *ast.File func testprint(out io.Writer, file *ast.File) { - if _, err := (&Config{TabIndent | UseSpaces, 8}).Fprint(out, fset, file); err != nil { + if err := (&Config{TabIndent | UseSpaces, 8}).Fprint(out, fset, file); err != nil { log.Fatalf("print error: %s", err) } } diff --git a/libgo/go/go/printer/printer.go b/libgo/go/go/printer/printer.go index aba7d93a64b..f8c22f1419d 100644 --- a/libgo/go/go/printer/printer.go +++ b/libgo/go/go/printer/printer.go @@ -13,13 +13,15 @@ import ( "io" "os" "path/filepath" + "strconv" + "strings" "text/tabwriter" ) const debug = false // enable for debugging +const infinity = 1 << 30 - -type whiteSpace int +type whiteSpace byte const ( ignore = whiteSpace(0) @@ -31,18 +33,6 @@ const ( unindent = whiteSpace('<') ) -var ( - esc = []byte{tabwriter.Escape} - htab = []byte{'\t'} - htabs = []byte("\t\t\t\t\t\t\t\t") - newlines = []byte("\n\n\n\n\n\n\n\n") // more than the max determined by nlines - formfeeds = []byte("\f\f\f\f\f\f\f\f") // more than the max determined by nlines -) - -// Special positions -var noPos token.Position // use noPos when a position is needed but not known -var infinity = 1 << 30 - // Use ignoreMultiLine if the multiLine information is not important. var ignoreMultiLine = new(bool) @@ -50,31 +40,20 @@ var ignoreMultiLine = new(bool) type pmode int const ( - inLiteral pmode = 1 << iota - noExtraLinebreak + noExtraLinebreak pmode = 1 << iota ) -// local error wrapper so we can distinguish errors we want to return -// as errors from genuine panics (which we don't want to return as errors) -type osError struct { - err error -} - type printer struct { // Configuration (does not change after initialization) - output io.Writer Config fset *token.FileSet // Current state - written int // number of bytes written - indent int // current indentation - mode pmode // current printer mode - lastTok token.Token // the last token printed (token.ILLEGAL if it's whitespace) - - // Reused buffers - wsbuf []whiteSpace // delayed white space - litbuf bytes.Buffer // for creation of escaped literals and comments + output bytes.Buffer // raw printer result + indent int // current indentation + mode pmode // current printer mode + lastTok token.Token // the last token printed (token.ILLEGAL if it's whitespace) + wsbuf []whiteSpace // delayed white space // The (possibly estimated) position in the generated output; // in AST space (i.e., pos is set whenever a token position is @@ -95,8 +74,7 @@ type printer struct { nodeSizes map[ast.Node]int } -func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) { - p.output = output +func (p *printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) { p.Config = *cfg p.fset = fset p.wsbuf = make([]whiteSpace, 0, 16) // whitespace sequences are short @@ -111,19 +89,6 @@ func (p *printer) internalError(msg ...interface{}) { } } -// escape escapes string s by bracketing it with tabwriter.Escape. -// Escaped strings pass through tabwriter unchanged. (Note that -// valid Go programs cannot contain tabwriter.Escape bytes since -// they do not appear in legal UTF-8 sequences). -// -func (p *printer) escape(s string) string { - p.litbuf.Reset() - p.litbuf.WriteByte(tabwriter.Escape) - p.litbuf.WriteString(s) - p.litbuf.WriteByte(tabwriter.Escape) - return p.litbuf.String() -} - // nlines returns the adjusted number of linebreaks given the desired number // of breaks n such that min <= result <= max. // @@ -138,82 +103,79 @@ func (p *printer) nlines(n, min int) int { return n } -// write0 writes raw (uninterpreted) data to p.output and handles errors. -// write0 does not indent after newlines, and does not HTML-escape or update p.pos. -// -func (p *printer) write0(data []byte) { - if len(data) > 0 { - n, err := p.output.Write(data) - p.written += n - if err != nil { - panic(osError{err}) +// writeByte writes a single byte to p.output and updates p.pos. +func (p *printer) writeByte(ch byte) { + p.output.WriteByte(ch) + p.pos.Offset++ + p.pos.Column++ + + if ch == '\n' || ch == '\f' { + // write indentation + // use "hard" htabs - indentation columns + // must not be discarded by the tabwriter + const htabs = "\t\t\t\t\t\t\t\t" + j := p.indent + for j > len(htabs) { + p.output.WriteString(htabs) + j -= len(htabs) } + p.output.WriteString(htabs[0:j]) + + // update p.pos + p.pos.Line++ + p.pos.Offset += p.indent + p.pos.Column = 1 + p.indent } } -// write interprets data and writes it to p.output. It inserts indentation -// after a line break unless in a tabwriter escape sequence. -// It updates p.pos as a side-effect. +// 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) write(data []byte) { - i0 := 0 - for i, b := range data { - switch b { - case '\n', '\f': - // write segment ending in b - p.write0(data[i0 : i+1]) - - // update p.pos - p.pos.Offset += i + 1 - i0 - p.pos.Line++ - p.pos.Column = 1 - - if p.mode&inLiteral == 0 { - // write indentation - // use "hard" htabs - indentation columns - // must not be discarded by the tabwriter - j := p.indent - for ; j > len(htabs); j -= len(htabs) { - p.write0(htabs) - } - p.write0(htabs[0:j]) - - // update p.pos - p.pos.Offset += p.indent - p.pos.Column += p.indent - } - - // next segment start - i0 = i + 1 - - case tabwriter.Escape: - p.mode ^= inLiteral +func (p *printer) writeNewlines(n int, nl byte) { + for n = p.nlines(n, 0); n > 0; n-- { + p.writeByte(nl) + } +} - // ignore escape chars introduced by printer - they are - // invisible and must not affect p.pos (was issue #1089) - p.pos.Offset-- - p.pos.Column-- - } +// writeString writes the string s to p.output and updates p.pos. +// If isLit is set, s is escaped w/ tabwriter.Escape characters +// to protect s from being interpreted by the tabwriter. +// +// Note: writeString is only used to write Go tokens, literals, and +// comments, all of which must be written literally. Thus, it is correct +// to always set isLit = true. However, setting it explicitly only when +// needed (i.e., when we don't know that s contains no tabs or line breaks) +// avoids processing extra escape characters and reduces run time of the +// printer benchmark by up to 10%. +// +func (p *printer) writeString(s string, isLit bool) { + if isLit { + // Protect s such that is passes through the tabwriter + // unchanged. Note that valid Go programs cannot contain + // tabwriter.Escape bytes since they do not appear in legal + // UTF-8 sequences. + p.output.WriteByte(tabwriter.Escape) } - // write remaining segment - p.write0(data[i0:]) + p.output.WriteString(s) // update p.pos - d := len(data) - i0 - p.pos.Offset += d - p.pos.Column += d -} - -func (p *printer) writeNewlines(n int, useFF bool) { - if n > 0 { - n = p.nlines(n, 0) - if useFF { - p.write(formfeeds[0:n]) - } else { - p.write(newlines[0:n]) + nlines := 0 + column := p.pos.Column + len(s) + for i := 0; i < len(s); i++ { + if s[i] == '\n' { + nlines++ + column = len(s) - i } } + p.pos.Offset += len(s) + p.pos.Line += nlines + p.pos.Column = column + + if isLit { + p.output.WriteByte(tabwriter.Escape) + } } // writeItem writes data at position pos. data is the text corresponding to @@ -222,7 +184,7 @@ func (p *printer) writeNewlines(n int, useFF bool) { // source text. writeItem updates p.last to the position immediately following // the data. // -func (p *printer) writeItem(pos token.Position, data string) { +func (p *printer) writeItem(pos token.Position, data string, isLit bool) { if pos.IsValid() { // continue with previous position if we don't have a valid pos if p.last.IsValid() && p.last.Filename != pos.Filename { @@ -238,12 +200,14 @@ func (p *printer) writeItem(pos token.Position, data string) { if debug { // do not update p.pos - use write0 _, filename := filepath.Split(pos.Filename) - p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, pos.Column))) + fmt.Fprintf(&p.output, "[%s:%d:%d]", filename, pos.Line, pos.Column) } - p.write([]byte(data)) + p.writeString(data, isLit) p.last = p.pos } +const linePrefix = "//line " + // writeCommentPrefix writes the whitespace before a comment. // If there is any pending whitespace, it consumes as much of // it as is likely to help position the comment nicely. @@ -252,15 +216,15 @@ func (p *printer) writeItem(pos token.Position, data string) { // a group of comments (or nil), and isKeyword indicates if the // next item is a keyword. // -func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment, isKeyword bool) { - if p.written == 0 { +func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *ast.Comment, isKeyword bool) { + if p.output.Len() == 0 { // the comment is the first item to be printed - don't write any whitespace return } if pos.IsValid() && pos.Filename != p.last.Filename { // comment in a different file - separate with newlines (writeNewlines will limit the number) - p.writeNewlines(10, true) + p.writeNewlines(10, '\f') return } @@ -293,14 +257,14 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment } // make sure there is at least one separator if !hasSep { + sep := byte('\t') if pos.Line == next.Line { // next item is on the same line as the comment // (which must be a /*-style comment): separate // with a blank instead of a tab - p.write([]byte{' '}) - } else { - p.write(htab) + sep = ' ' } + p.writeByte(sep) } } else { @@ -337,6 +301,13 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment } 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 + } + // use formfeeds to break columns before a comment; // this is analogous to using formfeeds to separate // individual lines of /*-style comments - but make @@ -346,29 +317,31 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment if n <= 0 && prev != nil && prev.Text[1] == '/' { n = 1 } - p.writeNewlines(n, true) + if n > 0 { + p.writeNewlines(n, '\f') + } + p.indent = indent } } -// TODO(gri): It should be possible to convert the code below from using -// []byte to string and in the process eliminate some conversions. - // Split comment text into lines -func split(text []byte) [][]byte { +// (using strings.Split(text, "\n") is significantly slower for +// this specific purpose, as measured with: gotest -bench=Print) +func split(text string) []string { // count lines (comment text never ends in a newline) n := 1 - for _, c := range text { - if c == '\n' { + for i := 0; i < len(text); i++ { + if text[i] == '\n' { n++ } } // split - lines := make([][]byte, n) + lines := make([]string, n) n = 0 i := 0 - for j, c := range text { - if c == '\n' { + for j := 0; j < len(text); j++ { + if text[j] == '\n' { lines[n] = text[i:j] // exclude newline i = j + 1 // discard newline n++ @@ -379,16 +352,18 @@ func split(text []byte) [][]byte { return lines } -func isBlank(s []byte) bool { - for _, b := range s { - if b > ' ' { +// Returns true if s contains only white space +// (only tabs and blanks can appear in the printer's context). +func isBlank(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] > ' ' { return false } } return true } -func commonPrefix(a, b []byte) []byte { +func commonPrefix(a, b string) string { i := 0 for i < len(a) && i < len(b) && a[i] == b[i] && (a[i] <= ' ' || a[i] == '*') { i++ @@ -396,7 +371,7 @@ func commonPrefix(a, b []byte) []byte { return a[0:i] } -func stripCommonPrefix(lines [][]byte) { +func stripCommonPrefix(lines []string) { if len(lines) < 2 { return // at most one line - nothing to do } @@ -420,19 +395,21 @@ func stripCommonPrefix(lines [][]byte) { // Note that the first and last line are never empty (they // contain the opening /* and closing */ respectively) and // thus they can be ignored by the blank line check. - var prefix []byte + var prefix string if len(lines) > 2 { + first := true for i, line := range lines[1 : len(lines)-1] { switch { case isBlank(line): - lines[1+i] = nil // range starts at line 1 - case prefix == nil: + lines[1+i] = "" // range starts at line 1 + case first: prefix = commonPrefix(line, line) + first = false default: prefix = commonPrefix(prefix, line) } } - } else { // len(lines) == 2 + } else { // len(lines) == 2, lines cannot be blank (contain /* and */) line := lines[1] prefix = commonPrefix(line, line) } @@ -441,7 +418,7 @@ func stripCommonPrefix(lines [][]byte) { * Check for vertical "line of stars" and correct prefix accordingly. */ lineOfStars := false - if i := bytes.Index(prefix, []byte{'*'}); i >= 0 { + if i := strings.Index(prefix, "*"); i >= 0 { // Line of stars present. if i > 0 && prefix[i-1] == ' ' { i-- // remove trailing blank from prefix so stars remain aligned @@ -489,7 +466,7 @@ func stripCommonPrefix(lines [][]byte) { } // Shorten the computed common prefix by the length of // suffix, if it is found as suffix of the prefix. - if bytes.HasSuffix(prefix, suffix) { + if strings.HasSuffix(prefix, string(suffix)) { prefix = prefix[0 : len(prefix)-len(suffix)] } } @@ -499,19 +476,18 @@ func stripCommonPrefix(lines [][]byte) { // with the opening /*, otherwise align the text with the other // lines. last := lines[len(lines)-1] - closing := []byte("*/") - i := bytes.Index(last, closing) + closing := "*/" + i := strings.Index(last, closing) // i >= 0 (closing is always present) if isBlank(last[0:i]) { // last line only contains closing */ - var sep []byte if lineOfStars { - // insert an aligning blank - sep = []byte{' '} + closing = " */" // add blank to align final star } - lines[len(lines)-1] = bytes.Join([][]byte{prefix, closing}, sep) + lines[len(lines)-1] = prefix + closing } else { // last line contains more comment text - assume - // it is aligned like the other lines + // it is aligned like the other lines and include + // in prefix computation prefix = commonPrefix(prefix, last) } @@ -526,28 +502,47 @@ func stripCommonPrefix(lines [][]byte) { func (p *printer) writeComment(comment *ast.Comment) { text := comment.Text + if strings.HasPrefix(text, linePrefix) { + pos := strings.TrimSpace(text[len(linePrefix):]) + i := strings.LastIndex(pos, ":") + if i >= 0 { + // The line directive we are about to print changed + // the Filename and Line number used by go/token + // as it was reading the input originally. + // In order to match the original input, we have to + // update our own idea of the file and line number + // accordingly, after printing the directive. + file := pos[:i] + line, _ := strconv.Atoi(pos[i+1:]) + defer func() { + p.pos.Filename = file + p.pos.Line = line + p.pos.Column = 1 + }() + } + } + // shortcut common case of //-style comments if text[1] == '/' { - p.writeItem(p.fset.Position(comment.Pos()), p.escape(text)) + p.writeItem(p.fset.Position(comment.Pos()), text, true) return } // for /*-style comments, print line by line and let the // write function take care of the proper indentation - lines := split([]byte(text)) + lines := split(text) stripCommonPrefix(lines) // write comment lines, separated by formfeed, // without a line break after the last line - linebreak := formfeeds[0:1] pos := p.fset.Position(comment.Pos()) for i, line := range lines { if i > 0 { - p.write(linebreak) + p.writeByte('\f') pos = p.pos } if len(line) > 0 { - p.writeItem(pos, p.escape(string(line))) + p.writeItem(pos, line, true) } } } @@ -583,7 +578,7 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) { // make sure we have a line break if needsLinebreak { - p.write([]byte{'\n'}) + p.writeByte('\n') } return @@ -599,7 +594,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (dro var last *ast.Comment for ; p.commentBefore(next); p.cindex++ { for _, c := range p.comments[p.cindex].List { - p.writeCommentPrefix(p.fset.Position(c.Pos()), next, last, tok.IsKeyword()) + p.writeCommentPrefix(p.fset.Position(c.Pos()), next, last, c, tok.IsKeyword()) p.writeComment(c) last = c } @@ -609,7 +604,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (dro if last.Text[1] == '*' && p.fset.Position(last.Pos()).Line == next.Line { // the last comment is a /*-style comment and the next item // follows on the same line: separate with an extra blank - p.write([]byte{' '}) + p.writeByte(' ') } // ensure that there is a line break after a //-style comment, // before a closing '}' unless explicitly disabled, or at eof @@ -629,7 +624,6 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (dro // whiteWhitespace writes the first n whitespace entries. func (p *printer) writeWhitespace(n int) { // write entries - var data [1]byte for i := 0; i < n; i++ { switch ch := p.wsbuf[i]; ch { case ignore: @@ -661,8 +655,7 @@ func (p *printer) writeWhitespace(n int) { } fallthrough default: - data[0] = byte(ch) - p.write(data[0:]) + p.writeByte(byte(ch)) } } @@ -678,7 +671,6 @@ func (p *printer) writeWhitespace(n int) { // ---------------------------------------------------------------------------- // Printing interface - func mayCombine(prev token.Token, next byte) (b bool) { switch prev { case token.INT: @@ -711,7 +703,8 @@ func mayCombine(prev token.Token, next byte) (b bool) { func (p *printer) print(args ...interface{}) { for _, f := range args { next := p.pos // estimated position of next item - var data string + data := "" + isLit := false var tok token.Token switch x := f.(type) { @@ -739,7 +732,8 @@ func (p *printer) print(args ...interface{}) { data = x.Name tok = token.IDENT case *ast.BasicLit: - data = p.escape(x.Value) + data = x.Value + isLit = true tok = x.Kind case token.Token: s := x.String() @@ -771,15 +765,20 @@ func (p *printer) print(args ...interface{}) { p.pos = next if data != "" { - droppedFF := p.flush(next, tok) + nl := byte('\n') + if p.flush(next, tok) { + nl = '\f' // dropped formfeed before + } // 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 - p.writeNewlines(next.Line-p.pos.Line, droppedFF) + if n := next.Line - p.pos.Line; n > 0 { + p.writeNewlines(n, nl) + } - p.writeItem(next, data) + p.writeItem(next, data, isLit) } } } @@ -808,6 +807,35 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) { return } +func (p *printer) printNode(node interface{}) error { + switch n := node.(type) { + case ast.Expr: + p.useNodeComments = true + p.expr(n, ignoreMultiLine) + case ast.Stmt: + p.useNodeComments = true + // A labeled statement will un-indent to position the + // label. Set indent to 1 so we don't get indent "underflow". + if _, labeledStmt := n.(*ast.LabeledStmt); labeledStmt { + p.indent = 1 + } + p.stmt(n, false, ignoreMultiLine) + case ast.Decl: + p.useNodeComments = true + p.decl(n, ignoreMultiLine) + case ast.Spec: + p.useNodeComments = true + p.spec(n, 1, false, ignoreMultiLine) + case *ast.File: + p.comments = n.Comments + p.useNodeComments = n.Comments == nil + p.file(n) + default: + return fmt.Errorf("go/printer: unsupported node type %T", n) + } + return nil +} + // ---------------------------------------------------------------------------- // Trimmer @@ -837,6 +865,8 @@ const ( // However, this would mess up any formatting done by // the tabwriter. +var aNewline = []byte("\n") + func (p *trimmer) Write(data []byte) (n int, err error) { // invariants: // p.state == inSpace: @@ -855,8 +885,8 @@ func (p *trimmer) Write(data []byte) (n int, err error) { case '\t', ' ': p.space.WriteByte(b) // WriteByte returns no errors case '\n', '\f': - p.space.Reset() // discard trailing space - _, err = p.output.Write(newlines[0:1]) // write newline + p.space.Reset() // discard trailing space + _, err = p.output.Write(aNewline) case tabwriter.Escape: _, err = p.output.Write(p.space.Bytes()) p.state = inEscape @@ -883,7 +913,7 @@ func (p *trimmer) Write(data []byte) (n int, err error) { _, err = p.output.Write(data[m:n]) p.state = inSpace p.space.Reset() - _, err = p.output.Write(newlines[0:1]) // write newline + _, err = p.output.Write(aNewline) case tabwriter.Escape: _, err = p.output.Write(data[m:n]) p.state = inEscape @@ -925,15 +955,22 @@ type Config struct { } // fprint implements Fprint and takes a nodesSizes map for setting up the printer state. -func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{}, nodeSizes map[ast.Node]int) (written int, err error) { +func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{}, nodeSizes map[ast.Node]int) (err error) { + // print node + var p printer + p.init(cfg, fset, nodeSizes) + if err = p.printNode(node); err != nil { + return + } + p.flush(token.Position{Offset: infinity, Line: infinity}, token.EOF) + // redirect output through a trimmer to eliminate trailing whitespace // (Input to a tabwriter must be untrimmed since trailing tabs provide // formatting information. The tabwriter could provide trimming // functionality but no tabwriter is used when RawFormat is set.) output = &trimmer{output: output} - // setup tabwriter if needed and redirect output - var tw *tabwriter.Writer + // redirect output through a tabwriter if necessary if cfg.Mode&RawFormat == 0 { minwidth := cfg.Tabwidth @@ -948,63 +985,28 @@ func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{ twmode |= tabwriter.TabIndent } - tw = tabwriter.NewWriter(output, minwidth, cfg.Tabwidth, 1, padchar, twmode) - output = tw + output = tabwriter.NewWriter(output, minwidth, cfg.Tabwidth, 1, padchar, twmode) } - // setup printer - var p printer - p.init(output, cfg, fset, nodeSizes) - defer func() { - written = p.written - if e := recover(); e != nil { - err = e.(osError).err // re-panics if it's not a local osError - } - }() - - // print node - switch n := node.(type) { - case ast.Expr: - p.useNodeComments = true - p.expr(n, ignoreMultiLine) - case ast.Stmt: - p.useNodeComments = true - // A labeled statement will un-indent to position the - // label. Set indent to 1 so we don't get indent "underflow". - if _, labeledStmt := n.(*ast.LabeledStmt); labeledStmt { - p.indent = 1 - } - p.stmt(n, false, ignoreMultiLine) - case ast.Decl: - p.useNodeComments = true - p.decl(n, ignoreMultiLine) - case ast.Spec: - p.useNodeComments = true - p.spec(n, 1, false, ignoreMultiLine) - case *ast.File: - p.comments = n.Comments - p.useNodeComments = n.Comments == nil - p.file(n) - default: - panic(osError{fmt.Errorf("printer.Fprint: unsupported node type %T", n)}) + // write printer result via tabwriter/trimmer to output + if _, err = output.Write(p.output.Bytes()); err != nil { + return } - p.flush(token.Position{Offset: infinity, Line: infinity}, token.EOF) // flush tabwriter, if any - if tw != nil { - tw.Flush() // ignore errors + if tw, _ := (output).(*tabwriter.Writer); tw != nil { + err = tw.Flush() } return } -// Fprint "pretty-prints" an AST node to output and returns the number -// of bytes written and an error (if any) for a given configuration cfg. +// Fprint "pretty-prints" an AST node to output for a given configuration cfg. // Position information is interpreted relative to the file set fset. // The node type must be *ast.File, or assignment-compatible to ast.Expr, // ast.Decl, ast.Spec, or ast.Stmt. // -func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) (int, error) { +func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) error { return cfg.fprint(output, fset, node, make(map[ast.Node]int)) } @@ -1012,6 +1014,5 @@ func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{ // It calls Config.Fprint with default settings. // func Fprint(output io.Writer, fset *token.FileSet, node interface{}) error { - _, err := (&Config{Tabwidth: 8}).Fprint(output, fset, node) // don't care about number of bytes written - return err + return (&Config{Tabwidth: 8}).Fprint(output, fset, node) } diff --git a/libgo/go/go/printer/printer_test.go b/libgo/go/go/printer/printer_test.go index a644aa383ab..924d4dfdb29 100644 --- a/libgo/go/go/printer/printer_test.go +++ b/libgo/go/go/printer/printer_test.go @@ -62,7 +62,7 @@ func runcheck(t *testing.T, source, golden string, mode checkMode) { // format source var buf bytes.Buffer - if _, err := cfg.Fprint(&buf, fset, prog); err != nil { + if err := cfg.Fprint(&buf, fset, prog); err != nil { t.Error(err) } res := buf.Bytes() diff --git a/libgo/go/hash/adler32/adler32.go b/libgo/go/hash/adler32/adler32.go index 10bed2f05e4..8103a89d439 100644 --- a/libgo/go/hash/adler32/adler32.go +++ b/libgo/go/hash/adler32/adler32.go @@ -71,14 +71,13 @@ func (d *digest) Write(p []byte) (nn int, err error) { func (d *digest) Sum32() uint32 { return finish(d.a, d.b) } -func (d *digest) Sum() []byte { - p := make([]byte, 4) +func (d *digest) Sum(in []byte) []byte { s := d.Sum32() - p[0] = byte(s >> 24) - p[1] = byte(s >> 16) - p[2] = byte(s >> 8) - p[3] = byte(s) - return p + in = append(in, byte(s>>24)) + in = append(in, byte(s>>16)) + in = append(in, byte(s>>8)) + in = append(in, byte(s)) + return in } // Checksum returns the Adler-32 checksum of data. diff --git a/libgo/go/hash/crc32/crc32.go b/libgo/go/hash/crc32/crc32.go index 5980ec0dc98..557fab8a52e 100644 --- a/libgo/go/hash/crc32/crc32.go +++ b/libgo/go/hash/crc32/crc32.go @@ -119,14 +119,13 @@ func (d *digest) Write(p []byte) (n int, err error) { func (d *digest) Sum32() uint32 { return d.crc } -func (d *digest) Sum() []byte { - p := make([]byte, 4) +func (d *digest) Sum(in []byte) []byte { s := d.Sum32() - p[0] = byte(s >> 24) - p[1] = byte(s >> 16) - p[2] = byte(s >> 8) - p[3] = byte(s) - return p + in = append(in, byte(s>>24)) + in = append(in, byte(s>>16)) + in = append(in, byte(s>>8)) + in = append(in, byte(s)) + return in } // Checksum returns the CRC-32 checksum of data diff --git a/libgo/go/hash/crc64/crc64.go b/libgo/go/hash/crc64/crc64.go index 42e53c3a5bd..e5c1db4b3d2 100644 --- a/libgo/go/hash/crc64/crc64.go +++ b/libgo/go/hash/crc64/crc64.go @@ -75,18 +75,17 @@ func (d *digest) Write(p []byte) (n int, err error) { func (d *digest) Sum64() uint64 { return d.crc } -func (d *digest) Sum() []byte { - p := make([]byte, 8) +func (d *digest) Sum(in []byte) []byte { s := d.Sum64() - p[0] = byte(s >> 56) - p[1] = byte(s >> 48) - p[2] = byte(s >> 40) - p[3] = byte(s >> 32) - p[4] = byte(s >> 24) - p[5] = byte(s >> 16) - p[6] = byte(s >> 8) - p[7] = byte(s) - return p + in = append(in, byte(s>>56)) + in = append(in, byte(s>>48)) + in = append(in, byte(s>>40)) + in = append(in, byte(s>>32)) + in = append(in, byte(s>>24)) + in = append(in, byte(s>>16)) + in = append(in, byte(s>>8)) + in = append(in, byte(s)) + return in } // Checksum returns the CRC-64 checksum of data diff --git a/libgo/go/hash/fnv/fnv.go b/libgo/go/hash/fnv/fnv.go index ce3ed0d0f40..2c8a25118e2 100644 --- a/libgo/go/hash/fnv/fnv.go +++ b/libgo/go/hash/fnv/fnv.go @@ -8,7 +8,6 @@ package fnv import ( - "encoding/binary" "hash" ) @@ -105,26 +104,46 @@ func (s *sum32a) Size() int { return 4 } func (s *sum64) Size() int { return 8 } func (s *sum64a) Size() int { return 8 } -func (s *sum32) Sum() []byte { - a := make([]byte, 4) - binary.BigEndian.PutUint32(a, uint32(*s)) - return a +func (s *sum32) Sum(in []byte) []byte { + v := uint32(*s) + in = append(in, byte(v>>24)) + in = append(in, byte(v>>16)) + in = append(in, byte(v>>8)) + in = append(in, byte(v)) + return in } -func (s *sum32a) Sum() []byte { - a := make([]byte, 4) - binary.BigEndian.PutUint32(a, uint32(*s)) - return a +func (s *sum32a) Sum(in []byte) []byte { + v := uint32(*s) + in = append(in, byte(v>>24)) + in = append(in, byte(v>>16)) + in = append(in, byte(v>>8)) + in = append(in, byte(v)) + return in } -func (s *sum64) Sum() []byte { - a := make([]byte, 8) - binary.BigEndian.PutUint64(a, uint64(*s)) - return a +func (s *sum64) Sum(in []byte) []byte { + v := uint64(*s) + in = append(in, byte(v>>56)) + in = append(in, byte(v>>48)) + in = append(in, byte(v>>40)) + in = append(in, byte(v>>32)) + in = append(in, byte(v>>24)) + in = append(in, byte(v>>16)) + in = append(in, byte(v>>8)) + in = append(in, byte(v)) + return in } -func (s *sum64a) Sum() []byte { - a := make([]byte, 8) - binary.BigEndian.PutUint64(a, uint64(*s)) - return a +func (s *sum64a) Sum(in []byte) []byte { + v := uint64(*s) + in = append(in, byte(v>>56)) + in = append(in, byte(v>>48)) + in = append(in, byte(v>>40)) + in = append(in, byte(v>>32)) + in = append(in, byte(v>>24)) + in = append(in, byte(v>>16)) + in = append(in, byte(v>>8)) + in = append(in, byte(v)) + return in } diff --git a/libgo/go/hash/fnv/fnv_test.go b/libgo/go/hash/fnv/fnv_test.go index 429230c80b4..17454deda90 100644 --- a/libgo/go/hash/fnv/fnv_test.go +++ b/libgo/go/hash/fnv/fnv_test.go @@ -72,7 +72,7 @@ func testGolden(t *testing.T, hash hash.Hash, gold []golden) { if done != len(g.text) { t.Fatalf("wrote only %d out of %d bytes", done, len(g.text)) } - if actual := hash.Sum(); !bytes.Equal(g.sum, actual) { + if actual := hash.Sum(nil); !bytes.Equal(g.sum, actual) { t.Errorf("hash(%q) = 0x%x want 0x%x", g.text, actual, g.sum) } } @@ -97,26 +97,26 @@ func TestIntegrity64a(t *testing.T) { func testIntegrity(t *testing.T, h hash.Hash) { data := []byte{'1', '2', 3, 4, 5} h.Write(data) - sum := h.Sum() + sum := h.Sum(nil) if size := h.Size(); size != len(sum) { t.Fatalf("Size()=%d but len(Sum())=%d", size, len(sum)) } - if a := h.Sum(); !bytes.Equal(sum, a) { + if a := h.Sum(nil); !bytes.Equal(sum, a) { t.Fatalf("first Sum()=0x%x, second Sum()=0x%x", sum, a) } h.Reset() h.Write(data) - if a := h.Sum(); !bytes.Equal(sum, a) { + if a := h.Sum(nil); !bytes.Equal(sum, a) { t.Fatalf("Sum()=0x%x, but after Reset() Sum()=0x%x", sum, a) } h.Reset() h.Write(data[:2]) h.Write(data[2:]) - if a := h.Sum(); !bytes.Equal(sum, a) { + if a := h.Sum(nil); !bytes.Equal(sum, a) { t.Fatalf("Sum()=0x%x, but with partial writes, Sum()=0x%x", sum, a) } @@ -162,6 +162,6 @@ func benchmark(b *testing.B, h hash.Hash) { for todo := b.N; todo != 0; todo-- { h.Reset() h.Write(data) - h.Sum() + h.Sum(nil) } } diff --git a/libgo/go/hash/hash.go b/libgo/go/hash/hash.go index 3536c0b6a64..0d7765dc505 100644 --- a/libgo/go/hash/hash.go +++ b/libgo/go/hash/hash.go @@ -13,9 +13,9 @@ type Hash interface { // It never returns an error. io.Writer - // Sum returns the current hash, without changing the - // underlying hash state. - Sum() []byte + // Sum appends the current hash in the same manner as append(), without + // changing the underlying hash state. + Sum(in []byte) []byte // Reset resets the hash to one with zero bytes written. Reset() diff --git a/libgo/go/html/doc.go b/libgo/go/html/doc.go index 1bea690c2c8..56b194ffb90 100644 --- a/libgo/go/html/doc.go +++ b/libgo/go/html/doc.go @@ -37,7 +37,7 @@ lower-cased, and attributes are collected into a []Attribute. For example: for { if z.Next() == html.ErrorToken { // Returning io.EOF indicates success. - return z.Error() + return z.Err() } emitToken(z.Token()) } @@ -51,7 +51,7 @@ call to Next. For example, to extract an HTML page's anchor text: tt := z.Next() switch tt { case ErrorToken: - return z.Error() + return z.Err() case TextToken: if depth > 0 { // emitBytes should copy the []byte it receives, diff --git a/libgo/go/html/doctype.go b/libgo/go/html/doctype.go new file mode 100644 index 00000000000..f692061a551 --- /dev/null +++ b/libgo/go/html/doctype.go @@ -0,0 +1,156 @@ +// 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 html + +import ( + "strings" +) + +// parseDoctype parses the data from a DoctypeToken into a name, +// public identifier, and system identifier. It returns a Node whose Type +// is DoctypeNode, whose Data is the name, and which has attributes +// named "system" and "public" for the two identifiers if they were present. +// quirks is whether the document should be parsed in "quirks mode". +func parseDoctype(s string) (n *Node, quirks bool) { + n = &Node{Type: DoctypeNode} + + // Find the name. + space := strings.IndexAny(s, whitespace) + if space == -1 { + space = len(s) + } + n.Data = s[:space] + // The comparison to "html" is case-sensitive. + if n.Data != "html" { + quirks = true + } + n.Data = strings.ToLower(n.Data) + s = strings.TrimLeft(s[space:], whitespace) + + if len(s) < 6 { + // It can't start with "PUBLIC" or "SYSTEM". + // Ignore the rest of the string. + return n, quirks || s != "" + } + + key := strings.ToLower(s[:6]) + s = s[6:] + for key == "public" || key == "system" { + s = strings.TrimLeft(s, whitespace) + if s == "" { + break + } + quote := s[0] + if quote != '"' && quote != '\'' { + break + } + s = s[1:] + q := strings.IndexRune(s, rune(quote)) + var id string + if q == -1 { + id = s + s = "" + } else { + id = s[:q] + s = s[q+1:] + } + n.Attr = append(n.Attr, Attribute{Key: key, Val: id}) + if key == "public" { + key = "system" + } else { + key = "" + } + } + + if key != "" || s != "" { + quirks = true + } else if len(n.Attr) > 0 { + if n.Attr[0].Key == "public" { + public := strings.ToLower(n.Attr[0].Val) + switch public { + case "-//w3o//dtd w3 html strict 3.0//en//", "-/w3d/dtd html 4.0 transitional/en", "html": + quirks = true + default: + for _, q := range quirkyIDs { + if strings.HasPrefix(public, q) { + quirks = true + break + } + } + } + // The following two public IDs only cause quirks mode if there is no system ID. + if len(n.Attr) == 1 && (strings.HasPrefix(public, "-//w3c//dtd html 4.01 frameset//") || + strings.HasPrefix(public, "-//w3c//dtd html 4.01 transitional//")) { + quirks = true + } + } + if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" && + strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" { + quirks = true + } + } + + return n, quirks +} + +// quirkyIDs is a list of public doctype identifiers that cause a document +// to be interpreted in quirks mode. The identifiers should be in lower case. +var quirkyIDs = []string{ + "+//silmaril//dtd html pro v0r11 19970101//", + "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", + "-//as//dtd html 3.0 aswedit + extensions//", + "-//ietf//dtd html 2.0 level 1//", + "-//ietf//dtd html 2.0 level 2//", + "-//ietf//dtd html 2.0 strict level 1//", + "-//ietf//dtd html 2.0 strict level 2//", + "-//ietf//dtd html 2.0 strict//", + "-//ietf//dtd html 2.0//", + "-//ietf//dtd html 2.1e//", + "-//ietf//dtd html 3.0//", + "-//ietf//dtd html 3.2 final//", + "-//ietf//dtd html 3.2//", + "-//ietf//dtd html 3//", + "-//ietf//dtd html level 0//", + "-//ietf//dtd html level 1//", + "-//ietf//dtd html level 2//", + "-//ietf//dtd html level 3//", + "-//ietf//dtd html strict level 0//", + "-//ietf//dtd html strict level 1//", + "-//ietf//dtd html strict level 2//", + "-//ietf//dtd html strict level 3//", + "-//ietf//dtd html strict//", + "-//ietf//dtd html//", + "-//metrius//dtd metrius presentational//", + "-//microsoft//dtd internet explorer 2.0 html strict//", + "-//microsoft//dtd internet explorer 2.0 html//", + "-//microsoft//dtd internet explorer 2.0 tables//", + "-//microsoft//dtd internet explorer 3.0 html strict//", + "-//microsoft//dtd internet explorer 3.0 html//", + "-//microsoft//dtd internet explorer 3.0 tables//", + "-//netscape comm. corp.//dtd html//", + "-//netscape comm. corp.//dtd strict html//", + "-//o'reilly and associates//dtd html 2.0//", + "-//o'reilly and associates//dtd html extended 1.0//", + "-//o'reilly and associates//dtd html extended relaxed 1.0//", + "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", + "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", + "-//spyglass//dtd html 2.0 extended//", + "-//sq//dtd html 2.0 hotmetal + extensions//", + "-//sun microsystems corp.//dtd hotjava html//", + "-//sun microsystems corp.//dtd hotjava strict html//", + "-//w3c//dtd html 3 1995-03-24//", + "-//w3c//dtd html 3.2 draft//", + "-//w3c//dtd html 3.2 final//", + "-//w3c//dtd html 3.2//", + "-//w3c//dtd html 3.2s draft//", + "-//w3c//dtd html 4.0 frameset//", + "-//w3c//dtd html 4.0 transitional//", + "-//w3c//dtd html experimental 19960712//", + "-//w3c//dtd html experimental 970421//", + "-//w3c//dtd w3 html//", + "-//w3o//dtd w3 html 3.0//", + "-//webtechs//dtd mozilla html 2.0//", + "-//webtechs//dtd mozilla html//", +} diff --git a/libgo/go/html/parse.go b/libgo/go/html/parse.go index f47d4ea147c..97fbc514d82 100644 --- a/libgo/go/html/parse.go +++ b/libgo/go/html/parse.go @@ -29,12 +29,19 @@ type parser struct { head, form *Node // Other parsing state flags (section 11.2.3.5). scripting, framesetOK bool + // im is the current insertion mode. + im insertionMode // originalIM is the insertion mode to go back to after completing a text // or inTableText insertion mode. originalIM insertionMode // fosterParenting is whether new elements should be inserted according to // the foster parenting rules (section 11.2.5.3). fosterParenting bool + // quirks is whether the parser is operating in "quirks mode." + quirks bool + // context is the context element when parsing an HTML fragment + // (section 11.4). + context *Node } func (p *parser) top() *Node { @@ -265,133 +272,126 @@ func (p *parser) acknowledgeSelfClosingTag() { // An insertion mode (section 11.2.3.1) is the state transition function from // a particular state in the HTML5 parser's state machine. It updates the -// parser's fields depending on parser.token (where ErrorToken means EOF). In -// addition to returning the next insertionMode state, it also returns whether -// the token was consumed. -type insertionMode func(*parser) (insertionMode, bool) - -// useTheRulesFor runs the delegate insertionMode over p, returning the actual -// insertionMode unless the delegate caused a state transition. -// Section 11.2.3.1, "using the rules for". -func useTheRulesFor(p *parser, actual, delegate insertionMode) (insertionMode, bool) { - im, consumed := delegate(p) - if p.originalIM == delegate { - p.originalIM = actual - } - if im != delegate { - return im, consumed - } - return actual, consumed -} +// parser's fields depending on parser.tok (where ErrorToken means EOF). +// It returns whether the token was consumed. +type insertionMode func(*parser) bool // setOriginalIM sets the insertion mode to return to after completing a text or // inTableText insertion mode. // Section 11.2.3.1, "using the rules for". -func (p *parser) setOriginalIM(im insertionMode) { +func (p *parser) setOriginalIM() { if p.originalIM != nil { panic("html: bad parser state: originalIM was set twice") } - p.originalIM = im + p.originalIM = p.im } // Section 11.2.3.1, "reset the insertion mode". -func (p *parser) resetInsertionMode() insertionMode { +func (p *parser) resetInsertionMode() { for i := len(p.oe) - 1; i >= 0; i-- { n := p.oe[i] - if i == 0 { - // TODO: set n to the context element, for HTML fragment parsing. + if i == 0 && p.context != nil { + n = p.context } + switch n.Data { case "select": - return inSelectIM + p.im = inSelectIM case "td", "th": - return inCellIM + p.im = inCellIM case "tr": - return inRowIM + p.im = inRowIM case "tbody", "thead", "tfoot": - return inTableBodyIM + p.im = inTableBodyIM case "caption": - // TODO: return inCaptionIM + p.im = inCaptionIM case "colgroup": - // TODO: return inColumnGroupIM + p.im = inColumnGroupIM case "table": - return inTableIM + p.im = inTableIM case "head": - return inBodyIM + p.im = inBodyIM case "body": - return inBodyIM + p.im = inBodyIM case "frameset": - // TODO: return inFramesetIM + p.im = inFramesetIM case "html": - return beforeHeadIM + p.im = beforeHeadIM + default: + continue } + return } - return inBodyIM + p.im = inBodyIM } +const whitespace = " \t\r\n\f" + // Section 11.2.5.4.1. -func initialIM(p *parser) (insertionMode, bool) { +func initialIM(p *parser) bool { switch p.tok.Type { + case TextToken: + p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) + if len(p.tok.Data) == 0 { + // It was all whitespace, so ignore it. + return true + } case CommentToken: p.doc.Add(&Node{ Type: CommentNode, Data: p.tok.Data, }) - return initialIM, true + return true case DoctypeToken: - p.doc.Add(&Node{ - Type: DoctypeNode, - Data: p.tok.Data, - }) - return beforeHTMLIM, true + n, quirks := parseDoctype(p.tok.Data) + p.doc.Add(n) + p.quirks = quirks + p.im = beforeHTMLIM + return true } - // TODO: set "quirks mode"? It's defined in the DOM spec instead of HTML5 proper, - // and so switching on "quirks mode" might belong in a different package. - return beforeHTMLIM, false + p.quirks = true + p.im = beforeHTMLIM + return false } // Section 11.2.5.4.2. -func beforeHTMLIM(p *parser) (insertionMode, bool) { - var ( - add bool - attr []Attribute - implied bool - ) +func beforeHTMLIM(p *parser) bool { switch p.tok.Type { - case ErrorToken: - implied = true case TextToken: - // TODO: distinguish whitespace text from others. - implied = true + p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) + if len(p.tok.Data) == 0 { + // It was all whitespace, so ignore it. + return true + } case StartTagToken: if p.tok.Data == "html" { - add = true - attr = p.tok.Attr - } else { - implied = true + p.addElement(p.tok.Data, p.tok.Attr) + p.im = beforeHeadIM + return true } case EndTagToken: switch p.tok.Data { case "head", "body", "html", "br": - implied = true + // Drop down to creating an implied <html> tag. default: // Ignore the token. + return true } case CommentToken: p.doc.Add(&Node{ Type: CommentNode, Data: p.tok.Data, }) - return beforeHTMLIM, true - } - if add || implied { - p.addElement("html", attr) + return true } - return beforeHeadIM, !implied + // Create an implied <html> tag. + p.addElement("html", nil) + p.im = beforeHeadIM + return false } // Section 11.2.5.4.3. -func beforeHeadIM(p *parser) (insertionMode, bool) { +func beforeHeadIM(p *parser) bool { var ( add bool attr []Attribute @@ -401,7 +401,11 @@ func beforeHeadIM(p *parser) (insertionMode, bool) { case ErrorToken: implied = true case TextToken: - // TODO: distinguish whitespace text from others. + p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) + if len(p.tok.Data) == 0 { + // It was all whitespace, so ignore it. + return true + } implied = true case StartTagToken: switch p.tok.Data { @@ -409,7 +413,7 @@ func beforeHeadIM(p *parser) (insertionMode, bool) { add = true attr = p.tok.Attr case "html": - return useTheRulesFor(p, beforeHeadIM, inBodyIM) + return inBodyIM(p) default: implied = true } @@ -425,19 +429,18 @@ func beforeHeadIM(p *parser) (insertionMode, bool) { Type: CommentNode, Data: p.tok.Data, }) - return beforeHeadIM, true + return true } if add || implied { p.addElement("head", attr) p.head = p.top() } - return inHeadIM, !implied + p.im = inHeadIM + return !implied } -const whitespace = " \t\r\n\f" - // Section 11.2.5.4.4. -func inHeadIM(p *parser) (insertionMode, bool) { +func inHeadIM(p *parser) bool { var ( pop bool implied bool @@ -451,48 +454,60 @@ func inHeadIM(p *parser) (insertionMode, bool) { // Add the initial whitespace to the current node. p.addText(p.tok.Data[:len(p.tok.Data)-len(s)]) if s == "" { - return inHeadIM, true + return true } p.tok.Data = s } implied = true case StartTagToken: switch p.tok.Data { + case "html": + return inBodyIM(p) case "base", "basefont", "bgsound", "command", "link", "meta": p.addElement(p.tok.Data, p.tok.Attr) p.oe.pop() p.acknowledgeSelfClosingTag() case "script", "title", "noscript", "noframes", "style": p.addElement(p.tok.Data, p.tok.Attr) - p.setOriginalIM(inHeadIM) - return textIM, true + p.setOriginalIM() + p.im = textIM + return true + case "head": + // Ignore the token. + return true default: implied = true } case EndTagToken: - if p.tok.Data == "head" { + switch p.tok.Data { + case "head": pop = true + case "body", "html", "br": + implied = true + default: + // Ignore the token. + return true } - // TODO. case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) - return inHeadIM, true + return true } if pop || implied { n := p.oe.pop() if n.Data != "head" { panic("html: bad parser state: <head> element not found, in the in-head insertion mode") } - return afterHeadIM, !implied + p.im = afterHeadIM + return !implied } - return inHeadIM, true + return true } // Section 11.2.5.4.6. -func afterHeadIM(p *parser) (insertionMode, bool) { +func afterHeadIM(p *parser) bool { var ( add bool attr []Attribute @@ -512,11 +527,13 @@ func afterHeadIM(p *parser) (insertionMode, bool) { attr = p.tok.Attr framesetOK = false case "frameset": - // TODO. + p.addElement(p.tok.Data, p.tok.Attr) + p.im = inFramesetIM + return true case "base", "basefont", "bgsound", "link", "meta", "noframes", "script", "style", "title": p.oe = append(p.oe, p.head) defer p.oe.pop() - return useTheRulesFor(p, afterHeadIM, inHeadIM) + return inHeadIM(p) case "head": // TODO. default: @@ -524,19 +541,27 @@ func afterHeadIM(p *parser) (insertionMode, bool) { framesetOK = true } case EndTagToken: - // TODO. + switch p.tok.Data { + case "body", "html", "br": + implied = true + framesetOK = true + default: + // Ignore the token. + return true + } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) - return afterHeadIM, true + return true } if add || implied { p.addElement("body", attr) p.framesetOK = framesetOK } - return inBodyIM, !implied + p.im = inBodyIM + return !implied } // copyAttributes copies attributes of src not found on dst to dst. @@ -557,14 +582,33 @@ func copyAttributes(dst *Node, src Token) { } // Section 11.2.5.4.7. -func inBodyIM(p *parser) (insertionMode, bool) { +func inBodyIM(p *parser) bool { switch p.tok.Type { case TextToken: + switch n := p.oe.top(); n.Data { + case "pre", "listing", "textarea": + if len(n.Child) == 0 { + // Ignore a newline at the start of a <pre> block. + d := p.tok.Data + if d != "" && d[0] == '\r' { + d = d[1:] + } + if d != "" && d[0] == '\n' { + d = d[1:] + } + if d == "" { + return true + } + p.tok.Data = d + } + } p.reconstructActiveFormattingElements() p.addText(p.tok.Data) p.framesetOK = false case StartTagToken: switch p.tok.Data { + case "html": + copyAttributes(p.oe[0], p.tok) case "address", "article", "aside", "blockquote", "center", "details", "dir", "div", "dl", "fieldset", "figcaption", "figure", "footer", "header", "hgroup", "menu", "nav", "ol", "p", "section", "summary", "ul": p.popUntil(buttonScopeStopTags, "p") p.addElement(p.tok.Data, p.tok.Attr) @@ -589,6 +633,13 @@ func inBodyIM(p *parser) (insertionMode, bool) { case "b", "big", "code", "em", "font", "i", "s", "small", "strike", "strong", "tt", "u": p.reconstructActiveFormattingElements() p.addFormattingElement(p.tok.Data, p.tok.Attr) + case "nobr": + p.reconstructActiveFormattingElements() + if p.elementInScope(defaultScopeStopTags, "nobr") { + p.inBodyEndTagFormatting("nobr") + p.reconstructActiveFormattingElements() + } + p.addFormattingElement(p.tok.Data, p.tok.Attr) case "applet", "marquee", "object": p.reconstructActiveFormattingElements() p.addElement(p.tok.Data, p.tok.Attr) @@ -601,10 +652,13 @@ func inBodyIM(p *parser) (insertionMode, bool) { p.acknowledgeSelfClosingTag() p.framesetOK = false case "table": - p.popUntil(buttonScopeStopTags, "p") // TODO: skip this step in quirks mode. + if !p.quirks { + p.popUntil(buttonScopeStopTags, "p") + } p.addElement(p.tok.Data, p.tok.Attr) p.framesetOK = false - return inTableIM, true + p.im = inTableIM + return true case "hr": p.popUntil(buttonScopeStopTags, "p") p.addElement(p.tok.Data, p.tok.Attr) @@ -616,7 +670,14 @@ func inBodyIM(p *parser) (insertionMode, bool) { p.addElement(p.tok.Data, p.tok.Attr) p.framesetOK = false // TODO: detect <select> inside a table. - return inSelectIM, true + p.im = inSelectIM + return true + case "form": + if p.form == nil { + p.popUntil(buttonScopeStopTags, "p") + p.addElement(p.tok.Data, p.tok.Attr) + p.form = p.top() + } case "li": p.framesetOK = false for i := len(p.oe) - 1; i >= 0; i-- { @@ -634,7 +695,28 @@ func inBodyIM(p *parser) (insertionMode, bool) { break } p.popUntil(buttonScopeStopTags, "p") - p.addElement("li", p.tok.Attr) + p.addElement(p.tok.Data, p.tok.Attr) + case "dd", "dt": + p.framesetOK = false + for i := len(p.oe) - 1; i >= 0; i-- { + node := p.oe[i] + switch node.Data { + case "dd", "dt": + p.oe = p.oe[:i] + case "address", "div", "p": + continue + default: + if !isSpecialElement[node.Data] { + continue + } + } + break + } + p.popUntil(buttonScopeStopTags, "p") + p.addElement(p.tok.Data, p.tok.Attr) + case "plaintext": + p.popUntil(buttonScopeStopTags, "p") + p.addElement(p.tok.Data, p.tok.Attr) case "optgroup", "option": if p.top().Data == "option" { p.oe.pop() @@ -650,10 +732,55 @@ func inBodyIM(p *parser) (insertionMode, bool) { } } case "base", "basefont", "bgsound", "command", "link", "meta", "noframes", "script", "style", "title": - return useTheRulesFor(p, inBodyIM, inHeadIM) + return inHeadIM(p) case "image": p.tok.Data = "img" - return inBodyIM, false + return false + case "isindex": + if p.form != nil { + // Ignore the token. + return true + } + action := "" + prompt := "This is a searchable index. Enter search keywords: " + attr := []Attribute{{Key: "name", Val: "isindex"}} + for _, a := range p.tok.Attr { + switch a.Key { + case "action": + action = a.Val + case "name": + // Ignore the attribute. + case "prompt": + prompt = a.Val + default: + attr = append(attr, a) + } + } + p.acknowledgeSelfClosingTag() + p.popUntil(buttonScopeStopTags, "p") + p.addElement("form", nil) + p.form = p.top() + if action != "" { + p.form.Attr = []Attribute{{Key: "action", Val: action}} + } + p.addElement("hr", nil) + p.oe.pop() + p.addElement("label", nil) + p.addText(prompt) + p.addElement("input", attr) + p.oe.pop() + p.oe.pop() + p.addElement("hr", nil) + p.oe.pop() + p.oe.pop() + p.form = nil + case "xmp": + p.popUntil(buttonScopeStopTags, "p") + p.reconstructActiveFormattingElements() + p.framesetOK = false + p.addElement(p.tok.Data, p.tok.Attr) + case "caption", "col", "colgroup", "frame", "head", "tbody", "td", "tfoot", "th", "thead", "tr": + // Ignore the token. default: // TODO. p.addElement(p.tok.Data, p.tok.Attr) @@ -662,7 +789,8 @@ func inBodyIM(p *parser) (insertionMode, bool) { switch p.tok.Data { case "body": // TODO: autoclose the stack of open elements. - return afterBodyIM, true + p.im = afterBodyIM + return true case "p": if !p.elementInScope(buttonScopeStopTags, "p") { p.addElement("p", nil) @@ -676,6 +804,9 @@ func inBodyIM(p *parser) (insertionMode, bool) { if p.popUntil(defaultScopeStopTags, p.tok.Data) { p.clearActiveFormattingElements() } + case "br": + p.tok.Type = StartTagToken + return false default: p.inBodyEndTagOther(p.tok.Data) } @@ -686,7 +817,7 @@ func inBodyIM(p *parser) (insertionMode, bool) { }) } - return inBodyIM, true + return true } func (p *parser) inBodyEndTagFormatting(tag string) { @@ -827,45 +958,64 @@ func (p *parser) inBodyEndTagOther(tag string) { } // Section 11.2.5.4.8. -func textIM(p *parser) (insertionMode, bool) { +func textIM(p *parser) bool { switch p.tok.Type { case ErrorToken: p.oe.pop() case TextToken: p.addText(p.tok.Data) - return textIM, true + return true case EndTagToken: p.oe.pop() } - o := p.originalIM + p.im = p.originalIM p.originalIM = nil - return o, p.tok.Type == EndTagToken + return p.tok.Type == EndTagToken } // Section 11.2.5.4.9. -func inTableIM(p *parser) (insertionMode, bool) { +func inTableIM(p *parser) bool { switch p.tok.Type { case ErrorToken: // Stop parsing. - return nil, true + return true case TextToken: // TODO. case StartTagToken: switch p.tok.Data { + case "caption": + p.clearStackToContext(tableScopeStopTags) + p.afe = append(p.afe, &scopeMarker) + p.addElement(p.tok.Data, p.tok.Attr) + p.im = inCaptionIM + return true case "tbody", "tfoot", "thead": p.clearStackToContext(tableScopeStopTags) p.addElement(p.tok.Data, p.tok.Attr) - return inTableBodyIM, true + p.im = inTableBodyIM + return true case "td", "th", "tr": p.clearStackToContext(tableScopeStopTags) p.addElement("tbody", nil) - return inTableBodyIM, false + p.im = inTableBodyIM + return false case "table": if p.popUntil(tableScopeStopTags, "table") { - return p.resetInsertionMode(), false + p.resetInsertionMode() + return false } // Ignore the token. - return inTableIM, true + return true + case "colgroup": + p.clearStackToContext(tableScopeStopTags) + p.addElement(p.tok.Data, p.tok.Attr) + p.im = inColumnGroupIM + return true + case "col": + p.clearStackToContext(tableScopeStopTags) + p.addElement("colgroup", p.tok.Attr) + p.im = inColumnGroupIM + return false default: // TODO. } @@ -873,20 +1023,21 @@ func inTableIM(p *parser) (insertionMode, bool) { switch p.tok.Data { case "table": if p.popUntil(tableScopeStopTags, "table") { - return p.resetInsertionMode(), true + p.resetInsertionMode() + return true } // Ignore the token. - return inTableIM, true + return true case "body", "caption", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr": // Ignore the token. - return inTableIM, true + return true } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) - return inTableIM, true + return true } switch p.top().Data { @@ -895,7 +1046,7 @@ func inTableIM(p *parser) (insertionMode, bool) { defer func() { p.fosterParenting = false }() } - return useTheRulesFor(p, inTableIM, inBodyIM) + return inBodyIM(p) } // clearStackToContext pops elements off the stack of open elements @@ -911,8 +1062,90 @@ func (p *parser) clearStackToContext(stopTags []string) { } } +// Section 11.2.5.4.11. +func inCaptionIM(p *parser) bool { + switch p.tok.Type { + case StartTagToken: + switch p.tok.Data { + case "caption", "col", "colgroup", "tbody", "td", "tfoot", "thead", "tr": + if p.popUntil(tableScopeStopTags, "caption") { + p.clearActiveFormattingElements() + p.im = inTableIM + return false + } else { + // Ignore the token. + return true + } + } + case EndTagToken: + switch p.tok.Data { + case "caption": + if p.popUntil(tableScopeStopTags, "caption") { + p.clearActiveFormattingElements() + p.im = inTableIM + } + return true + case "table": + if p.popUntil(tableScopeStopTags, "caption") { + p.clearActiveFormattingElements() + p.im = inTableIM + return false + } else { + // Ignore the token. + return true + } + case "body", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr": + // Ignore the token. + return true + } + } + return inBodyIM(p) +} + +// Section 11.2.5.4.12. +func inColumnGroupIM(p *parser) bool { + switch p.tok.Type { + case CommentToken: + p.addChild(&Node{ + Type: CommentNode, + Data: p.tok.Data, + }) + return true + case DoctypeToken: + // Ignore the token. + return true + case StartTagToken: + switch p.tok.Data { + case "html": + return inBodyIM(p) + case "col": + p.addElement(p.tok.Data, p.tok.Attr) + p.oe.pop() + p.acknowledgeSelfClosingTag() + return true + } + case EndTagToken: + switch p.tok.Data { + case "colgroup": + if p.oe.top().Data != "html" { + p.oe.pop() + } + p.im = inTableIM + return true + case "col": + // Ignore the token. + return true + } + } + if p.oe.top().Data != "html" { + p.oe.pop() + } + p.im = inTableIM + return false +} + // Section 11.2.5.4.13. -func inTableBodyIM(p *parser) (insertionMode, bool) { +func inTableBodyIM(p *parser) bool { var ( add bool data string @@ -942,31 +1175,33 @@ func inTableBodyIM(p *parser) (insertionMode, bool) { switch p.tok.Data { case "table": if p.popUntil(tableScopeStopTags, "tbody", "thead", "tfoot") { - return inTableIM, false + p.im = inTableIM + return false } // Ignore the token. - return inTableBodyIM, true + return true case "body", "caption", "col", "colgroup", "html", "td", "th", "tr": // Ignore the token. - return inTableBodyIM, true + return true } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) - return inTableBodyIM, true + return true } if add { // TODO: clear the stack back to a table body context. p.addElement(data, attr) - return inRowIM, consumed + p.im = inRowIM + return consumed } - return useTheRulesFor(p, inTableBodyIM, inTableIM) + return inTableIM(p) } // Section 11.2.5.4.14. -func inRowIM(p *parser) (insertionMode, bool) { +func inRowIM(p *parser) bool { switch p.tok.Type { case ErrorToken: // TODO. @@ -978,13 +1213,15 @@ func inRowIM(p *parser) (insertionMode, bool) { p.clearStackToContext(tableRowContextStopTags) p.addElement(p.tok.Data, p.tok.Attr) p.afe = append(p.afe, &scopeMarker) - return inCellIM, true + p.im = inCellIM + return true case "caption", "col", "colgroup", "tbody", "tfoot", "thead", "tr": if p.popUntil(tableScopeStopTags, "tr") { - return inTableBodyIM, false + p.im = inTableBodyIM + return false } // Ignore the token. - return inRowIM, true + return true default: // TODO. } @@ -992,21 +1229,23 @@ func inRowIM(p *parser) (insertionMode, bool) { switch p.tok.Data { case "tr": if p.popUntil(tableScopeStopTags, "tr") { - return inTableBodyIM, true + p.im = inTableBodyIM + return true } // Ignore the token. - return inRowIM, true + return true case "table": if p.popUntil(tableScopeStopTags, "tr") { - return inTableBodyIM, false + p.im = inTableBodyIM + return false } // Ignore the token. - return inRowIM, true + return true case "tbody", "tfoot", "thead": // TODO. case "body", "caption", "col", "colgroup", "html", "td", "th": // Ignore the token. - return inRowIM, true + return true default: // TODO. } @@ -1015,13 +1254,13 @@ func inRowIM(p *parser) (insertionMode, bool) { Type: CommentNode, Data: p.tok.Data, }) - return inRowIM, true + return true } - return useTheRulesFor(p, inRowIM, inTableIM) + return inTableIM(p) } // Section 11.2.5.4.15. -func inCellIM(p *parser) (insertionMode, bool) { +func inCellIM(p *parser) bool { var ( closeTheCellAndReprocess bool ) @@ -1037,10 +1276,11 @@ func inCellIM(p *parser) (insertionMode, bool) { case "td", "th": if !p.popUntil(tableScopeStopTags, p.tok.Data) { // Ignore the token. - return inCellIM, true + return true } p.clearActiveFormattingElements() - return inRowIM, true + p.im = inRowIM + return true case "body", "caption", "col", "colgroup", "html": // TODO. case "table", "tbody", "tfoot", "thead", "tr": @@ -1052,19 +1292,20 @@ func inCellIM(p *parser) (insertionMode, bool) { Type: CommentNode, Data: p.tok.Data, }) - return inCellIM, true + return true } if closeTheCellAndReprocess { if p.popUntil(tableScopeStopTags, "td") || p.popUntil(tableScopeStopTags, "th") { p.clearActiveFormattingElements() - return inRowIM, false + p.im = inRowIM + return false } } - return useTheRulesFor(p, inCellIM, inBodyIM) + return inBodyIM(p) } // Section 11.2.5.4.16. -func inSelectIM(p *parser) (insertionMode, bool) { +func inSelectIM(p *parser) bool { endSelect := false switch p.tok.Type { case ErrorToken: @@ -1081,7 +1322,13 @@ func inSelectIM(p *parser) (insertionMode, bool) { } p.addElement(p.tok.Data, p.tok.Attr) case "optgroup": - // TODO. + if p.top().Data == "option" { + p.oe.pop() + } + if p.top().Data == "optgroup" { + p.oe.pop() + } + p.addElement(p.tok.Data, p.tok.Attr) case "select": endSelect = true case "input", "keygen", "textarea": @@ -1094,9 +1341,17 @@ func inSelectIM(p *parser) (insertionMode, bool) { case EndTagToken: switch p.tok.Data { case "option": - // TODO. + if p.top().Data == "option" { + p.oe.pop() + } case "optgroup": - // TODO. + i := len(p.oe) - 1 + if p.oe[i].Data == "option" { + i-- + } + if p.oe[i].Data == "optgroup" { + p.oe = p.oe[:i] + } case "select": endSelect = true default: @@ -1113,34 +1368,33 @@ func inSelectIM(p *parser) (insertionMode, bool) { switch p.oe[i].Data { case "select": p.oe = p.oe[:i] - return p.resetInsertionMode(), true + p.resetInsertionMode() + return true case "option", "optgroup": continue default: // Ignore the token. - return inSelectIM, true + return true } } } - return inSelectIM, true + return true } // Section 11.2.5.4.18. -func afterBodyIM(p *parser) (insertionMode, bool) { +func afterBodyIM(p *parser) bool { switch p.tok.Type { case ErrorToken: - // TODO. - case TextToken: - // TODO. + // Stop parsing. + return true case StartTagToken: - // TODO. + if p.tok.Data == "html" { + return inBodyIM(p) + } case EndTagToken: - switch p.tok.Data { - case "html": - // TODO: autoclose the stack of open elements. - return afterAfterBodyIM, true - default: - // TODO. + if p.tok.Data == "html" { + p.im = afterAfterBodyIM + return true } case CommentToken: // The comment is attached to the <html> element. @@ -1151,63 +1405,212 @@ func afterBodyIM(p *parser) (insertionMode, bool) { Type: CommentNode, Data: p.tok.Data, }) - return afterBodyIM, true + return true + } + p.im = inBodyIM + return false +} + +// Section 11.2.5.4.19. +func inFramesetIM(p *parser) bool { + switch p.tok.Type { + case CommentToken: + p.addChild(&Node{ + Type: CommentNode, + Data: p.tok.Data, + }) + case StartTagToken: + switch p.tok.Data { + case "html": + return inBodyIM(p) + case "frameset": + p.addElement(p.tok.Data, p.tok.Attr) + case "frame": + p.addElement(p.tok.Data, p.tok.Attr) + p.oe.pop() + p.acknowledgeSelfClosingTag() + case "noframes": + return inHeadIM(p) + } + case EndTagToken: + switch p.tok.Data { + case "frameset": + if p.oe.top().Data != "html" { + p.oe.pop() + if p.oe.top().Data != "frameset" { + p.im = afterFramesetIM + return true + } + } + } + default: + // Ignore the token. + } + return true +} + +// Section 11.2.5.4.20. +func afterFramesetIM(p *parser) bool { + switch p.tok.Type { + case CommentToken: + p.addChild(&Node{ + Type: CommentNode, + Data: p.tok.Data, + }) + case StartTagToken: + switch p.tok.Data { + case "html": + return inBodyIM(p) + case "noframes": + return inHeadIM(p) + } + case EndTagToken: + switch p.tok.Data { + case "html": + p.im = afterAfterFramesetIM + return true + } + default: + // Ignore the token. } - // TODO: should this be "return inBodyIM, true"? - return afterBodyIM, true + return true } // Section 11.2.5.4.21. -func afterAfterBodyIM(p *parser) (insertionMode, bool) { +func afterAfterBodyIM(p *parser) bool { switch p.tok.Type { case ErrorToken: // Stop parsing. - return nil, true + return true case TextToken: // TODO. case StartTagToken: if p.tok.Data == "html" { - return useTheRulesFor(p, afterAfterBodyIM, inBodyIM) + return inBodyIM(p) } case CommentToken: p.doc.Add(&Node{ Type: CommentNode, Data: p.tok.Data, }) - return afterAfterBodyIM, true + return true } - return inBodyIM, false + p.im = inBodyIM + return false } -// Parse returns the parse tree for the HTML from the given Reader. -// The input is assumed to be UTF-8 encoded. -func Parse(r io.Reader) (*Node, error) { - p := &parser{ - tokenizer: NewTokenizer(r), - doc: &Node{ - Type: DocumentNode, - }, - scripting: true, - framesetOK: true, +// Section 11.2.5.4.22. +func afterAfterFramesetIM(p *parser) bool { + switch p.tok.Type { + case CommentToken: + p.addChild(&Node{ + Type: CommentNode, + Data: p.tok.Data, + }) + case StartTagToken: + switch p.tok.Data { + case "html": + return inBodyIM(p) + case "noframes": + return inHeadIM(p) + } + default: + // Ignore the token. } + return true +} + +func (p *parser) parse() error { // Iterate until EOF. Any other error will cause an early return. - im, consumed := initialIM, true + consumed := true for { if consumed { if err := p.read(); err != nil { if err == io.EOF { break } - return nil, err + return err } } - im, consumed = im(p) + consumed = p.im(p) } // Loop until the final token (the ErrorToken signifying EOF) is consumed. for { - if im, consumed = im(p); consumed { + if consumed = p.im(p); consumed { break } } + return nil +} + +// Parse returns the parse tree for the HTML from the given Reader. +// The input is assumed to be UTF-8 encoded. +func Parse(r io.Reader) (*Node, error) { + p := &parser{ + tokenizer: NewTokenizer(r), + doc: &Node{ + Type: DocumentNode, + }, + scripting: true, + framesetOK: true, + im: initialIM, + } + err := p.parse() + if err != nil { + return nil, err + } return p.doc, nil } + +// ParseFragment parses a fragment of HTML and returns the nodes that were +// found. If the fragment is the InnerHTML for an existing element, pass that +// element in context. +func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { + p := &parser{ + tokenizer: NewTokenizer(r), + doc: &Node{ + Type: DocumentNode, + }, + scripting: true, + context: context, + } + + if context != nil { + switch context.Data { + case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "title", "textarea", "xmp": + p.tokenizer.rawTag = context.Data + } + } + + root := &Node{ + Type: ElementNode, + Data: "html", + } + p.doc.Add(root) + p.oe = nodeStack{root} + p.resetInsertionMode() + + for n := context; n != nil; n = n.Parent { + if n.Type == ElementNode && n.Data == "form" { + p.form = n + break + } + } + + err := p.parse() + if err != nil { + return nil, err + } + + parent := p.doc + if context != nil { + parent = root + } + + result := parent.Child + parent.Child = nil + for _, n := range result { + n.Parent = nil + } + return result, nil +} diff --git a/libgo/go/html/parse_test.go b/libgo/go/html/parse_test.go index 27979225b33..e0c19cff6da 100644 --- a/libgo/go/html/parse_test.go +++ b/libgo/go/html/parse_test.go @@ -10,65 +10,77 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" "strings" "testing" ) -func pipeErr(err error) io.Reader { - pr, pw := io.Pipe() - pw.CloseWithError(err) - return pr -} - -func readDat(filename string, c chan io.Reader) { - defer close(c) - f, err := os.Open("testdata/webkit/" + filename) +// readParseTest reads a single test case from r. +func readParseTest(r *bufio.Reader) (text, want, context string, err error) { + line, err := r.ReadSlice('\n') if err != nil { - c <- pipeErr(err) - return + return "", "", "", err } - defer f.Close() + var b []byte - // Loop through the lines of the file. Each line beginning with "#" denotes - // a new section, which is returned as a separate io.Reader. - r := bufio.NewReader(f) - var pw *io.PipeWriter + // Read the HTML. + if string(line) != "#data\n" { + return "", "", "", fmt.Errorf(`got %q want "#data\n"`, line) + } for { - line, err := r.ReadSlice('\n') + line, err = r.ReadSlice('\n') if err != nil { - if pw != nil { - pw.CloseWithError(err) - pw = nil - } else { - c <- pipeErr(err) - } - return + return "", "", "", err } - if len(line) == 0 { - continue + if line[0] == '#' { + break + } + b = append(b, line...) + } + text = strings.TrimRight(string(b), "\n") + b = b[:0] + + // Skip the error list. + if string(line) != "#errors\n" { + return "", "", "", fmt.Errorf(`got %q want "#errors\n"`, line) + } + for { + line, err = r.ReadSlice('\n') + if err != nil { + return "", "", "", err } if line[0] == '#' { - if pw != nil { - pw.Close() - } - var pr *io.PipeReader - pr, pw = io.Pipe() - c <- pr - continue + break + } + } + + if string(line) == "#document-fragment\n" { + line, err = r.ReadSlice('\n') + if err != nil { + return "", "", "", err } - if line[0] != '|' { - // Strip the trailing '\n'. - line = line[:len(line)-1] + context = strings.TrimSpace(string(line)) + line, err = r.ReadSlice('\n') + if err != nil { + return "", "", "", err } - if pw != nil { - if _, err := pw.Write(line); err != nil { - pw.CloseWithError(err) - pw = nil - } + } + + // Read the dump of what the parse tree should be. + if string(line) != "#document\n" { + return "", "", "", fmt.Errorf(`got %q want "#document\n"`, line) + } + for { + line, err = r.ReadSlice('\n') + if err != nil && err != io.EOF { + return "", "", "", err + } + if len(line) == 0 || len(line) == 1 && line[0] == '\n' { + break } + b = append(b, line...) } + return text, string(b), context, nil } func dumpIndent(w io.Writer, level int) { @@ -93,11 +105,27 @@ func dumpLevel(w io.Writer, n *Node, level int) error { fmt.Fprintf(w, `%s="%s"`, a.Key, a.Val) } case TextNode: - fmt.Fprintf(w, "%q", n.Data) + fmt.Fprintf(w, `"%s"`, n.Data) case CommentNode: fmt.Fprintf(w, "<!-- %s -->", n.Data) case DoctypeNode: - fmt.Fprintf(w, "<!DOCTYPE %s>", n.Data) + fmt.Fprintf(w, "<!DOCTYPE %s", n.Data) + if n.Attr != nil { + var p, s string + for _, a := range n.Attr { + switch a.Key { + case "public": + p = a.Val + case "system": + s = a.Val + } + } + if p != "" || s != "" { + fmt.Fprintf(w, ` "%s"`, p) + fmt.Fprintf(w, ` "%s"`, s) + } + } + io.WriteString(w, ">") case scopeMarkerNode: return errors.New("unexpected scopeMarkerNode") default: @@ -133,46 +161,62 @@ func TestParser(t *testing.T) { n int }{ // TODO(nigeltao): Process all the test cases from all the .dat files. - {"tests1.dat", 92}, - {"tests2.dat", 0}, - {"tests3.dat", 0}, + {"doctype01.dat", -1}, + {"tests1.dat", -1}, + {"tests2.dat", -1}, + {"tests3.dat", -1}, + {"tests4.dat", -1}, + {"tests5.dat", -1}, } for _, tf := range testFiles { - rc := make(chan io.Reader) - go readDat(tf.filename, rc) + f, err := os.Open("testdata/webkit/" + tf.filename) + if err != nil { + t.Fatal(err) + } + defer f.Close() + r := bufio.NewReader(f) for i := 0; i != tf.n; i++ { - // Parse the #data section. - dataReader := <-rc - if dataReader == nil { + text, want, context, err := readParseTest(r) + if err == io.EOF && tf.n == -1 { break } - b, err := ioutil.ReadAll(dataReader) if err != nil { t.Fatal(err) } - text := string(b) - doc, err := Parse(strings.NewReader(text)) - if err != nil { - t.Fatal(err) + + var doc *Node + if context == "" { + doc, err = Parse(strings.NewReader(text)) + if err != nil { + t.Fatal(err) + } + } else { + contextNode := &Node{ + Type: ElementNode, + Data: context, + } + nodes, err := ParseFragment(strings.NewReader(text), contextNode) + if err != nil { + t.Fatal(err) + } + doc = &Node{ + Type: DocumentNode, + } + for _, n := range nodes { + doc.Add(n) + } } + got, err := dump(doc) if err != nil { t.Fatal(err) } - // Skip the #error section. - if _, err := io.Copy(ioutil.Discard, <-rc); err != nil { - t.Fatal(err) - } // Compare the parsed tree to the #document section. - b, err = ioutil.ReadAll(<-rc) - if err != nil { - t.Fatal(err) - } - if want := string(b); got != want { + if got != want { t.Errorf("%s test #%d %q, got vs want:\n----\n%s----\n%s----", tf.filename, i, text, got, want) continue } - if renderTestBlacklist[text] { + if renderTestBlacklist[text] || context != "" { continue } // Check that rendering and re-parsing results in an identical tree. @@ -193,12 +237,6 @@ func TestParser(t *testing.T) { continue } } - // Drain any untested cases for the test file. - for r := range rc { - if _, err := ioutil.ReadAll(r); err != nil { - t.Fatal(err) - } - } } } @@ -213,4 +251,8 @@ var renderTestBlacklist = map[string]bool{ // More cases of <a> being reparented: `<a href="blah">aba<table><a href="foo">br<tr><td></td></tr>x</table>aoe`: true, `<a><table><a></table><p><a><div><a>`: true, + `<a><table><td><a><table></table><a></tr><a></table><a>`: true, + // A <plaintext> element is reparented, putting it before a table. + // A <plaintext> element can't have anything after it in HTML. + `<table><plaintext><td>`: true, } diff --git a/libgo/go/html/render.go b/libgo/go/html/render.go index c815f35f1e1..7e1a4669657 100644 --- a/libgo/go/html/render.go +++ b/libgo/go/html/render.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "io" + "strings" ) type writer interface { @@ -52,7 +53,19 @@ func Render(w io.Writer, n *Node) error { return buf.Flush() } +// plaintextAbort is returned from render1 when a <plaintext> element +// has been rendered. No more end tags should be rendered after that. +var plaintextAbort = errors.New("html: internal error (plaintext abort)") + func render(w writer, n *Node) error { + err := render1(w, n) + if err == plaintextAbort { + err = nil + } + return err +} + +func render1(w writer, n *Node) error { // Render non-element nodes; these are the easy cases. switch n.Type { case ErrorNode: @@ -61,7 +74,7 @@ func render(w writer, n *Node) error { return escape(w, n.Data) case DocumentNode: for _, c := range n.Child { - if err := render(w, c); err != nil { + if err := render1(w, c); err != nil { return err } } @@ -86,6 +99,40 @@ func render(w writer, n *Node) error { if _, err := w.WriteString(n.Data); err != nil { return err } + if n.Attr != nil { + var p, s string + for _, a := range n.Attr { + switch a.Key { + case "public": + p = a.Val + case "system": + s = a.Val + } + } + if p != "" { + if _, err := w.WriteString(" PUBLIC "); err != nil { + return err + } + if err := writeQuoted(w, p); err != nil { + return err + } + if s != "" { + if err := w.WriteByte(' '); err != nil { + return err + } + if err := writeQuoted(w, s); err != nil { + return err + } + } + } else if s != "" { + if _, err := w.WriteString(" SYSTEM "); err != nil { + return err + } + if err := writeQuoted(w, s); err != nil { + return err + } + } + } return w.WriteByte('>') default: return errors.New("html: unknown node type") @@ -126,9 +173,19 @@ func render(w writer, n *Node) error { return err } + // Add initial newline where there is danger of a newline beging ignored. + if len(n.Child) > 0 && n.Child[0].Type == TextNode && strings.HasPrefix(n.Child[0].Data, "\n") { + switch n.Data { + case "pre", "listing", "textarea": + if err := w.WriteByte('\n'); err != nil { + return err + } + } + } + // Render any child nodes. switch n.Data { - case "noembed", "noframes", "noscript", "script", "style": + case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp": for _, c := range n.Child { if c.Type != TextNode { return fmt.Errorf("html: raw text element <%s> has non-text child node", n.Data) @@ -137,18 +194,23 @@ func render(w writer, n *Node) error { return err } } + if n.Data == "plaintext" { + // Don't render anything else. <plaintext> must be the + // last element in the file, with no closing tag. + return plaintextAbort + } case "textarea", "title": for _, c := range n.Child { if c.Type != TextNode { return fmt.Errorf("html: RCDATA element <%s> has non-text child node", n.Data) } - if err := render(w, c); err != nil { + if err := render1(w, c); err != nil { return err } } default: for _, c := range n.Child { - if err := render(w, c); err != nil { + if err := render1(w, c); err != nil { return err } } @@ -164,6 +226,27 @@ func render(w writer, n *Node) error { return w.WriteByte('>') } +// writeQuoted writes s to w surrounded by quotes. Normally it will use double +// quotes, but if s contains a double quote, it will use single quotes. +// It is used for writing the identifiers in a doctype declaration. +// In valid HTML, they can't contain both types of quotes. +func writeQuoted(w writer, s string) error { + var q byte = '"' + if strings.Contains(s, `"`) { + q = '\'' + } + if err := w.WriteByte(q); err != nil { + return err + } + if _, err := w.WriteString(s); err != nil { + return err + } + if err := w.WriteByte(q); err != nil { + return err + } + return nil +} + // Section 13.1.2, "Elements", gives this list of void elements. Void elements // are those that can't have any contents. var voidElements = map[string]bool{ diff --git a/libgo/go/html/template/clone_test.go b/libgo/go/html/template/clone_test.go index ed1698acd8b..39788173b99 100644 --- a/libgo/go/html/template/clone_test.go +++ b/libgo/go/html/template/clone_test.go @@ -7,8 +7,6 @@ package template import ( "bytes" "testing" - "text/template" - "text/template/parse" ) func TestClone(t *testing.T) { @@ -48,15 +46,20 @@ func TestClone(t *testing.T) { } for _, test := range tests { - s := template.Must(template.New("s").Parse(test.input)) - d := template.New("d") - d.Tree = &parse.Tree{Name: d.Name(), Root: cloneList(s.Root)} + s, err := New("s").Parse(test.input) + if err != nil { + t.Errorf("input=%q: unexpected parse error %v", test.input, err) + } + + d, _ := New("d").Parse(test.input) + // Hack: just replace the root of the tree. + d.text.Root = cloneList(s.text.Root) - if want, got := s.Root.String(), d.Root.String(); want != got { + if want, got := s.text.Root.String(), d.text.Root.String(); want != got { t.Errorf("want %q, got %q", want, got) } - err := escape(d) + err = escapeTemplates(d, "d") if err != nil { t.Errorf("%q: failed to escape: %s", test.input, err) continue @@ -73,18 +76,17 @@ func TestClone(t *testing.T) { data := []string{"foo", "<bar>", "baz"} - // Make sure escaping d did not affect s. var b bytes.Buffer - s.Execute(&b, data) - if got := b.String(); got != test.want { - t.Errorf("%q: want %q, got %q", test.input, test.want, got) - continue + d.Execute(&b, data) + if got := b.String(); got != test.wantClone { + t.Errorf("input=%q: want %q, got %q", test.input, test.wantClone, got) } + // Make sure escaping d did not affect s. b.Reset() - d.Execute(&b, data) - if got := b.String(); got != test.wantClone { - t.Errorf("%q: want %q, got %q", test.input, test.wantClone, got) + s.text.Execute(&b, data) + if got := b.String(); got != test.want { + t.Errorf("input=%q: want %q, got %q", test.input, test.want, got) } } } diff --git a/libgo/go/html/template/content.go b/libgo/go/html/template/content.go index d720d4ba689..4de7ccde912 100644 --- a/libgo/go/html/template/content.go +++ b/libgo/go/html/template/content.go @@ -6,15 +6,16 @@ package template import ( "fmt" + "reflect" ) // Strings of content from a trusted source. type ( // CSS encapsulates known safe content that matches any of: - // (1) The CSS3 stylesheet production, such as `p { color: purple }`. - // (2) The CSS3 rule production, such as `a[href=~"https:"].foo#bar`. - // (3) CSS3 declaration productions, such as `color: red; margin: 2px`. - // (4) The CSS3 value production, such as `rgba(0, 0, 255, 127)`. + // 1. The CSS3 stylesheet production, such as `p { color: purple }`. + // 2. The CSS3 rule production, such as `a[href=~"https:"].foo#bar`. + // 3. CSS3 declaration productions, such as `color: red; margin: 2px`. + // 4. The CSS3 value production, such as `rgba(0, 0, 255, 127)`. // See http://www.w3.org/TR/css3-syntax/#style CSS string @@ -40,8 +41,8 @@ type ( // JSStr encapsulates a sequence of characters meant to be embedded // between quotes in a JavaScript expression. // The string must match a series of StringCharacters: - // StringCharacter :: SourceCharacter but not `\` or LineTerminator - // | EscapeSequence + // StringCharacter :: SourceCharacter but not `\` or LineTerminator + // | EscapeSequence // Note that LineContinuations are not allowed. // JSStr("foo\\nbar") is fine, but JSStr("foo\\\nbar") is not. JSStr string @@ -70,10 +71,25 @@ const ( contentTypeUnsafe ) +// indirect returns the value, after dereferencing as many times +// as necessary to reach the base type (or nil). +func indirect(a interface{}) interface{} { + if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr { + // Avoid creating a reflect.Value if it's not a pointer. + return a + } + v := reflect.ValueOf(a) + for v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + return v.Interface() +} + // stringify converts its arguments to a string and the type of the content. +// All pointers are dereferenced, as in the text/template package. func stringify(args ...interface{}) (string, contentType) { if len(args) == 1 { - switch s := args[0].(type) { + switch s := indirect(args[0]).(type) { case string: return s, contentTypePlain case CSS: @@ -90,5 +106,8 @@ func stringify(args ...interface{}) (string, contentType) { return string(s), contentTypeURL } } + for i, arg := range args { + args[i] = indirect(arg) + } return fmt.Sprint(args...), contentTypePlain } diff --git a/libgo/go/html/template/doc.go b/libgo/go/html/template/doc.go index 0324c9c0ee3..fc0e3826442 100644 --- a/libgo/go/html/template/doc.go +++ b/libgo/go/html/template/doc.go @@ -13,9 +13,9 @@ Introduction This package wraps package template so you can use the standard template API to parse and execute templates. - set, err := new(template.Set).Parse(...) - // Error checking elided - err = set.Execute(out, "Foo", data) + set, err := new(template.Set).Parse(...) + // Error checking elided + err = set.Execute(out, "Foo", data) If successful, set will now be injection-safe. Otherwise, err is an error defined in the docs for ErrorCode. @@ -29,25 +29,25 @@ trusted, while Execute's data parameter is not. More details are provided below. Example - import "template" - ... - t, err := (&template.Set{}).Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) - err = t.Execute(out, "T", "<script>alert('you have been pwned')</script>") + import "text/template" + ... + t, err := (&template.Set{}).Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) + err = t.Execute(out, "T", "<script>alert('you have been pwned')</script>") produces - Hello, <script>alert('you have been pwned')</script>! + Hello, <script>alert('you have been pwned')</script>! but with contextual autoescaping, - import "html/template" - ... - t, err := (&template.Set{}).Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) - err = t.Execute(out, "T", "<script>alert('you have been pwned')</script>") + import "html/template" + ... + t, err := (&template.Set{}).Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) + err = t.Execute(out, "T", "<script>alert('you have been pwned')</script>") produces safe, escaped HTML output - Hello, <script>alert('you have been pwned')</script>! + Hello, <script>alert('you have been pwned')</script>! Contexts @@ -80,36 +80,36 @@ Contexts Assuming {{.}} is `O'Reilly: How are <i>you</i>?`, the table below shows how {{.}} appears when used in the context to the left. -Context {{.}} After -{{.}} O'Reilly: How are <i>you</i>? -<a title='{{.}}'> O'Reilly: How are you? -<a href="/{{.}}"> O'Reilly: How are %3ci%3eyou%3c/i%3e? -<a href="?q={{.}}"> O'Reilly%3a%20How%20are%3ci%3e...%3f -<a onx='f("{{.}}")'> O\x27Reilly: How are \x3ci\x3eyou...? -<a onx='f({{.}})'> "O\x27Reilly: How are \x3ci\x3eyou...?" -<a onx='pattern = /{{.}}/;'> O\x27Reilly: How are \x3ci\x3eyou...\x3f + Context {{.}} After + {{.}} O'Reilly: How are <i>you</i>? + <a title='{{.}}'> O'Reilly: How are you? + <a href="/{{.}}"> O'Reilly: How are %3ci%3eyou%3c/i%3e? + <a href="?q={{.}}"> O'Reilly%3a%20How%20are%3ci%3e...%3f + <a onx='f("{{.}}")'> O\x27Reilly: How are \x3ci\x3eyou...? + <a onx='f({{.}})'> "O\x27Reilly: How are \x3ci\x3eyou...?" + <a onx='pattern = /{{.}}/;'> O\x27Reilly: How are \x3ci\x3eyou...\x3f If used in an unsafe context, then the value might be filtered out: -Context {{.}} After -<a href="{{.}}"> #ZgotmplZ + Context {{.}} After + <a href="{{.}}"> #ZgotmplZ since "O'Reilly:" is not an allowed protocol like "http:". If {{.}} is the innocuous word, `left`, then it can appear more widely, -Context {{.}} After -{{.}} left -<a title='{{.}}'> left -<a href='{{.}}'> left -<a href='/{{.}}'> left -<a href='?dir={{.}}'> left -<a style="border-{{.}}: 4px"> left -<a style="align: {{.}}"> left -<a style="background: '{{.}}'> left -<a style="background: url('{{.}}')> left -<style>p.{{.}} {color:red}</style> left + Context {{.}} After + {{.}} left + <a title='{{.}}'> left + <a href='{{.}}'> left + <a href='/{{.}}'> left + <a href='?dir={{.}}'> left + <a style="border-{{.}}: 4px"> left + <a style="align: {{.}}"> left + <a style="background: '{{.}}'> left + <a style="background: url('{{.}}')> left + <style>p.{{.}} {color:red}</style> left Non-string values can be used in JavaScript contexts. If {{.}} is diff --git a/libgo/go/html/template/escape.go b/libgo/go/html/template/escape.go index 8ac07eae24c..4a7a9354c93 100644 --- a/libgo/go/html/template/escape.go +++ b/libgo/go/html/template/escape.go @@ -12,24 +12,15 @@ import ( "text/template/parse" ) -// escape rewrites each action in the template to guarantee that the output is -// properly escaped. -func escape(t *template.Template) error { - var s template.Set - s.Add(t) - return escapeSet(&s, t.Name()) - // TODO: if s contains cloned dependencies due to self-recursion - // cross-context, error out. -} - -// escapeSet rewrites the template set to guarantee that the output of any of -// the named templates is properly escaped. -// Names should include the names of all templates that might be Executed but -// need not include helper templates. -// If no error is returned, then the named templates have been modified. -// Otherwise the named templates have been rendered unusable. -func escapeSet(s *template.Set, names ...string) error { - e := newEscaper(s) +// escapeTemplates rewrites the named templates, which must be +// associated with t, to guarantee that the output of any of the named +// templates is properly escaped. Names should include the names of +// all templates that might be Executed but need not include helper +// templates. If no error is returned, then the named templates have +// been modified. Otherwise the named templates have been rendered +// unusable. +func escapeTemplates(tmpl *Template, names ...string) error { + e := newEscaper(tmpl) for _, name := range names { c, _ := e.escapeTree(context{}, name, 0) var err error @@ -41,12 +32,13 @@ func escapeSet(s *template.Set, names ...string) error { if err != nil { // Prevent execution of unsafe templates. for _, name := range names { - if t := s.Template(name); t != nil { - t.Tree = nil + if t := tmpl.set[name]; t != nil { + t.text.Tree = nil } } return err } + tmpl.escaped = true } e.commit() return nil @@ -83,8 +75,7 @@ var equivEscapers = map[string]string{ // escaper collects type inferences about templates and changes needed to make // templates injection safe. type escaper struct { - // set is the template set being escaped. - set *template.Set + tmpl *Template // output[templateName] is the output context for a templateName that // has been mangled to include its input context. output map[string]context @@ -102,9 +93,9 @@ type escaper struct { } // newEscaper creates a blank escaper for the given set. -func newEscaper(s *template.Set) *escaper { +func newEscaper(t *Template) *escaper { return &escaper{ - s, + t, map[string]context{}, map[string]*template.Template{}, map[string]bool{}, @@ -442,7 +433,7 @@ func (e *escaper) escapeList(c context, n *parse.ListNode) context { // It returns the best guess at an output context, and the result of the filter // which is the same as whether e was updated. func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter func(*escaper, context) bool) (context, bool) { - e1 := newEscaper(e.set) + e1 := newEscaper(e.tmpl) // Make type inferences available to f. for k, v := range e.output { e1.output[k] = v @@ -501,7 +492,7 @@ func (e *escaper) escapeTree(c context, name string, line int) (context, string) }, dname } if dname != name { - // Use any template derived during an earlier call to escapeSet + // Use any template derived during an earlier call to escapeTemplate // with different top level templates, or clone if necessary. dt := e.template(dname) if dt == nil { @@ -529,7 +520,7 @@ func (e *escaper) computeOutCtx(c context, t *template.Template) context { if !ok && c1.state != stateError { return context{ state: stateError, - // TODO: Find the first node with a line in t.Tree.Root + // TODO: Find the first node with a line in t.text.Tree.Root err: errorf(ErrOutputContext, 0, "cannot compute output context for template %s", t.Name()), } } @@ -729,7 +720,9 @@ func (e *escaper) commit() { e.template(name).Funcs(funcMap) } for _, t := range e.derived { - e.set.Add(t) + if _, err := e.tmpl.text.AddParseTree(t.Name(), t.Tree); err != nil { + panic("error adding derived template") + } } for n, s := range e.actionNodeEdits { ensurePipelineContains(n.Pipe, s) @@ -744,7 +737,7 @@ func (e *escaper) commit() { // template returns the named template given a mangled template name. func (e *escaper) template(name string) *template.Template { - t := e.set.Template(name) + t := e.tmpl.text.Lookup(name) if t == nil { t = e.derived[name] } diff --git a/libgo/go/html/template/escape_test.go b/libgo/go/html/template/escape_test.go index d8bfa321121..b4daca7d6bd 100644 --- a/libgo/go/html/template/escape_test.go +++ b/libgo/go/html/template/escape_test.go @@ -28,7 +28,7 @@ func (x *goodMarshaler) MarshalJSON() ([]byte, error) { } func TestEscape(t *testing.T) { - var data = struct { + data := struct { F, T bool C, G, H string A, E []string @@ -50,6 +50,7 @@ func TestEscape(t *testing.T) { Z: nil, W: HTML(`¡<b class="foo">Hello</b>, <textarea>O'World</textarea>!`), } + pdata := &data tests := []struct { name string @@ -668,6 +669,15 @@ func TestEscape(t *testing.T) { t.Errorf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g) continue } + b.Reset() + if err := tmpl.Execute(b, pdata); err != nil { + t.Errorf("%s: template execution failed for pointer: %s", test.name, err) + continue + } + if w, g := test.output, b.String(); w != g { + t.Errorf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g) + continue + } } } @@ -796,13 +806,15 @@ func TestEscapeSet(t *testing.T) { for name, body := range test.inputs { source += fmt.Sprintf("{{define %q}}%s{{end}} ", name, body) } - s := &Set{} - s.Funcs(fns) - s.Parse(source) + tmpl, err := New("root").Funcs(fns).Parse(source) + if err != nil { + t.Errorf("error parsing %q: %v", source, err) + continue + } var b bytes.Buffer - if err := s.Execute(&b, "main", data); err != nil { - t.Errorf("%q executing %v", err.Error(), s.Template("main")) + if err := tmpl.ExecuteTemplate(&b, "main", data); err != nil { + t.Errorf("%q executing %v", err.Error(), tmpl.Lookup("main")) continue } if got := b.String(); test.want != got { @@ -919,13 +931,13 @@ func TestErrors(t *testing.T) { "z:1: no such template foo", }, { - `{{define "z"}}<div{{template "y"}}>{{end}}` + + `<div{{template "y"}}>` + // Illegal starting in stateTag but not in stateText. `{{define "y"}} foo<b{{end}}`, `"<" in attribute name: " foo<b"`, }, { - `{{define "z"}}<script>reverseList = [{{template "t"}}]</script>{{end}}` + + `<script>reverseList = [{{template "t"}}]</script>` + // Missing " after recursive call. `{{define "t"}}{{if .Tail}}{{template "t" .Tail}}{{end}}{{.Head}}",{{end}}`, `: cannot compute output context for template t$htmltemplate_stateJS_elementScript`, @@ -957,21 +969,13 @@ func TestErrors(t *testing.T) { } for _, test := range tests { - var err error buf := new(bytes.Buffer) - if strings.HasPrefix(test.input, "{{define") { - var s *Set - s, err = (&Set{}).Parse(test.input) - if err == nil { - err = s.Execute(buf, "z", nil) - } - } else { - var t *Template - t, err = New("z").Parse(test.input) - if err == nil { - err = t.Execute(buf, nil) - } + tmpl, err := New("z").Parse(test.input) + if err != nil { + t.Errorf("input=%q: unexpected parse error %s\n", test.input, err) + continue } + err = tmpl.Execute(buf, nil) var got string if err != nil { got = err.Error() @@ -1559,11 +1563,11 @@ func TestEscapeErrorsNotIgnorable(t *testing.T) { func TestEscapeSetErrorsNotIgnorable(t *testing.T) { var b bytes.Buffer - s, err := (&Set{}).Parse(`{{define "t"}}<a{{end}}`) + tmpl, err := New("root").Parse(`{{define "t"}}<a{{end}}`) if err != nil { t.Errorf("failed to parse set: %q", err) } - err = s.Execute(&b, "t", nil) + err = tmpl.ExecuteTemplate(&b, "t", nil) if err == nil { t.Errorf("Expected error") } else if b.Len() != 0 { @@ -1605,6 +1609,29 @@ func TestRedundantFuncs(t *testing.T) { } } +func TestIndirectPrint(t *testing.T) { + a := 3 + ap := &a + b := "hello" + bp := &b + bpp := &bp + tmpl := Must(New("t").Parse(`{{.}}`)) + var buf bytes.Buffer + err := tmpl.Execute(&buf, ap) + if err != nil { + t.Errorf("Unexpected error: %s", err) + } else if buf.String() != "3" { + t.Errorf(`Expected "3"; got %q`, buf.String()) + } + buf.Reset() + err = tmpl.Execute(&buf, bpp) + if err != nil { + t.Errorf("Unexpected error: %s", err) + } else if buf.String() != "hello" { + t.Errorf(`Expected "hello"; got %q`, buf.String()) + } +} + func BenchmarkEscapedExecute(b *testing.B) { tmpl := Must(New("t").Parse(`<a onclick="alert('{{.}}')">{{.}}</a>`)) var buf bytes.Buffer diff --git a/libgo/go/html/template/js.go b/libgo/go/html/template/js.go index 68c53e5ca3b..0e632df4220 100644 --- a/libgo/go/html/template/js.go +++ b/libgo/go/html/template/js.go @@ -8,6 +8,7 @@ import ( "bytes" "encoding/json" "fmt" + "reflect" "strings" "unicode/utf8" ) @@ -117,12 +118,24 @@ var regexpPrecederKeywords = map[string]bool{ "void": true, } +var jsonMarshalType = reflect.TypeOf((*json.Marshaler)(nil)).Elem() + +// indirectToJSONMarshaler returns the value, after dereferencing as many times +// as necessary to reach the base type (or nil) or an implementation of json.Marshal. +func indirectToJSONMarshaler(a interface{}) interface{} { + v := reflect.ValueOf(a) + for !v.Type().Implements(jsonMarshalType) && v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + return v.Interface() +} + // jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has -// nether side-effects nor free variables outside (NaN, Infinity). +// neither side-effects nor free variables outside (NaN, Infinity). func jsValEscaper(args ...interface{}) string { var a interface{} if len(args) == 1 { - a = args[0] + a = indirectToJSONMarshaler(args[0]) switch t := a.(type) { case JS: return string(t) @@ -135,6 +148,9 @@ func jsValEscaper(args ...interface{}) string { a = t.String() } } else { + for i, arg := range args { + args[i] = indirectToJSONMarshaler(arg) + } a = fmt.Sprint(args...) } // TODO: detect cycles before calling Marshal which loops infinitely on diff --git a/libgo/go/html/template/template.go b/libgo/go/html/template/template.go index 47334299384..f05ca190f73 100644 --- a/libgo/go/html/template/template.go +++ b/libgo/go/html/template/template.go @@ -7,233 +7,257 @@ package template import ( "fmt" "io" + "io/ioutil" "path/filepath" + "sync" "text/template" + "text/template/parse" ) -// Set is a specialized template.Set that produces a safe HTML document -// fragment. -type Set struct { - escaped map[string]bool - template.Set -} - // Template is a specialized template.Template that produces a safe HTML // document fragment. type Template struct { escaped bool - *template.Template + // We could embed the text/template field, but it's safer not to because + // we need to keep our version of the name space and the underlying + // template's in sync. + text *template.Template + *nameSpace // common to all associated templates } -// Execute applies the named template to the specified data object, writing -// the output to wr. -func (s *Set) Execute(wr io.Writer, name string, data interface{}) error { - if !s.escaped[name] { - if err := escapeSet(&s.Set, name); err != nil { - return err - } - if s.escaped == nil { - s.escaped = make(map[string]bool) +// nameSpace is the data structure shared by all templates in an association. +type nameSpace struct { + mu sync.Mutex + set map[string]*Template +} + +// Execute applies a parsed template to the specified data object, +// writing the output to wr. +func (t *Template) Execute(wr io.Writer, data interface{}) (err error) { + t.nameSpace.mu.Lock() + if !t.escaped { + if err = escapeTemplates(t, t.Name()); err != nil { + t.escaped = true } - s.escaped[name] = true } - return s.Set.Execute(wr, name, data) + t.nameSpace.mu.Unlock() + if err != nil { + return + } + return t.text.Execute(wr, data) } -// Parse parses a string into a set of named templates. Parse may be called -// multiple times for a given set, adding the templates defined in the string -// to the set. If a template is redefined, the element in the set is -// overwritten with the new definition. -func (set *Set) Parse(src string) (*Set, error) { - set.escaped = nil - s, err := set.Set.Parse(src) - if err != nil { - return nil, err +// ExecuteTemplate applies the template associated with t that has the given name +// to the specified data object and writes the output to wr. +func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) (err error) { + t.nameSpace.mu.Lock() + tmpl := t.set[name] + if tmpl == nil { + t.nameSpace.mu.Unlock() + return fmt.Errorf("template: no template %q associated with template %q", name, t.Name()) } - if s != &(set.Set) { - panic("allocated new set") + if !tmpl.escaped { + err = escapeTemplates(tmpl, name) + } + t.nameSpace.mu.Unlock() + if err != nil { + return } - return set, nil + return tmpl.text.ExecuteTemplate(wr, name, data) } -// Parse parses the template definition string to construct an internal -// representation of the template for execution. -func (tmpl *Template) Parse(src string) (*Template, error) { - tmpl.escaped = false - t, err := tmpl.Template.Parse(src) +// Parse parses a string into a template. Nested template definitions +// will be associated with the top-level template t. Parse may be +// called multiple times to parse definitions of templates to associate +// with t. It is an error if a resulting template is non-empty (contains +// content other than template definitions) and would replace a +// non-empty template with the same name. (In multiple calls to Parse +// with the same receiver template, only one call can contain text +// other than space, comments, and template definitions.) +func (t *Template) Parse(src string) (*Template, error) { + t.nameSpace.mu.Lock() + t.escaped = false + t.nameSpace.mu.Unlock() + ret, err := t.text.Parse(src) if err != nil { return nil, err } - tmpl.Template = t - return tmpl, nil -} - -// Execute applies a parsed template to the specified data object, -// writing the output to wr. -func (t *Template) Execute(wr io.Writer, data interface{}) error { - if !t.escaped { - if err := escape(t.Template); err != nil { - return err + // In general, all the named templates might have changed underfoot. + // Regardless, some new ones may have been defined. + // The template.Template set has been updated; update ours. + t.nameSpace.mu.Lock() + defer t.nameSpace.mu.Unlock() + for _, v := range ret.Templates() { + name := v.Name() + tmpl := t.set[name] + if tmpl == nil { + tmpl = t.new(name) } - t.escaped = true + tmpl.escaped = false + tmpl.text = v } - return t.Template.Execute(wr, data) + return t, nil +} + +// AddParseTree is unimplemented. +func (t *Template) AddParseTree(name string, tree *parse.Tree) error { + return fmt.Errorf("html/template: AddParseTree unimplemented") +} + +// Clone is unimplemented. +func (t *Template) Clone(name string) error { + return fmt.Errorf("html/template: Add unimplemented") } // New allocates a new HTML template with the given name. func New(name string) *Template { - return &Template{false, template.New(name)} + tmpl := &Template{ + false, + template.New(name), + &nameSpace{ + set: make(map[string]*Template), + }, + } + tmpl.set[name] = tmpl + return tmpl } -// Must panics if err is non-nil in the same way as template.Must. -func Must(t *Template, err error) *Template { - t.Template = template.Must(t.Template, err) - return t +// New allocates a new HTML template associated with the given one +// and with the same delimiters. The association, which is transitive, +// allows one template to invoke another with a {{template}} action. +func (t *Template) New(name string) *Template { + t.nameSpace.mu.Lock() + defer t.nameSpace.mu.Unlock() + return t.new(name) } -// ParseFile creates a new Template and parses the template definition from -// the named file. The template name is the base name of the file. -func ParseFile(filename string) (*Template, error) { - t, err := template.ParseFile(filename) - if err != nil { - return nil, err +// new is the implementation of New, without the lock. +func (t *Template) new(name string) *Template { + tmpl := &Template{ + false, + t.text.New(name), + t.nameSpace, } - return &Template{false, t}, nil + tmpl.set[name] = tmpl + return tmpl } -// ParseFile reads the template definition from a file and parses it to -// construct an internal representation of the template for execution. -// The returned template will be nil if an error occurs. -func (tmpl *Template) ParseFile(filename string) (*Template, error) { - t, err := tmpl.Template.ParseFile(filename) - if err != nil { - return nil, err - } - tmpl.Template = t - return tmpl, nil +// Name returns the name of the template. +func (t *Template) Name() string { + return t.text.Name() } -// SetMust panics if the error is non-nil just like template.SetMust. -func SetMust(s *Set, err error) *Set { - if err != nil { - template.SetMust(&(s.Set), err) - } - return s +// Funcs adds the elements of the argument map to the template's function map. +// It panics if a value in the map is not a function with appropriate return +// type. However, it is legal to overwrite elements of the map. The return +// value is the template, so calls can be chained. +func (t *Template) Funcs(funcMap template.FuncMap) *Template { + t.text.Funcs(funcMap) + return t } -// ParseFiles parses the named files into a set of named templates. -// Each file must be parseable by itself. -// If an error occurs, parsing stops and the returned set is nil. -func (set *Set) ParseFiles(filenames ...string) (*Set, error) { - s, err := set.Set.ParseFiles(filenames...) - if err != nil { - return nil, err - } - if s != &(set.Set) { - panic("allocated new set") - } - return set, nil +// Delims sets the action delimiters to the specified strings, to be used in +// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template +// definitions will inherit the settings. An empty delimiter stands for the +// corresponding default: {{ or }}. +// The return value is the template, so calls can be chained. +func (t *Template) Delims(left, right string) *Template { + t.text.Delims(left, right) + return t } -// ParseSetFiles creates a new Set and parses the set definition from the -// named files. Each file must be individually parseable. -func ParseSetFiles(filenames ...string) (*Set, error) { - set := new(Set) - s, err := set.Set.ParseFiles(filenames...) - if err != nil { - return nil, err - } - if s != &(set.Set) { - panic("allocated new set") - } - return set, nil +// Lookup returns the template with the given name that is associated with t, +// or nil if there is no such template. +func (t *Template) Lookup(name string) *Template { + t.nameSpace.mu.Lock() + defer t.nameSpace.mu.Unlock() + return t.set[name] } -// ParseGlob parses the set definition from the files identified by the -// pattern. The pattern is processed by filepath.Glob and must match at -// least one file. -// If an error occurs, parsing stops and the returned set is nil. -func (s *Set) ParseGlob(pattern string) (*Set, error) { - filenames, err := filepath.Glob(pattern) - if err != nil { - return nil, err - } - if len(filenames) == 0 { - return nil, fmt.Errorf("pattern matches no files: %#q", pattern) - } - return s.ParseFiles(filenames...) +// Must panics if err is non-nil in the same way as template.Must. +func Must(t *Template, err error) *Template { + t.text = template.Must(t.text, err) + return t } -// ParseSetGlob creates a new Set and parses the set definition from the -// files identified by the pattern. The pattern is processed by filepath.Glob -// and must match at least one file. -func ParseSetGlob(pattern string) (*Set, error) { - set, err := new(Set).ParseGlob(pattern) - if err != nil { - return nil, err - } - return set, nil +// ParseFiles creates a new Template and parses the template definitions from +// the named files. The returned template's name will have the (base) name and +// (parsed) contents of the first file. There must be at least one file. +// If an error occurs, parsing stops and the returned *Template is nil. +func ParseFiles(filenames ...string) (*Template, error) { + return parseFiles(nil, filenames...) } -// Functions and methods to parse stand-alone template files into a set. +// ParseFiles parses the named files and associates the resulting templates with +// t. If an error occurs, parsing stops and the returned template is nil; +// otherwise it is t. There must be at least one file. +func (t *Template) ParseFiles(filenames ...string) (*Template, error) { + return parseFiles(t, filenames...) +} -// ParseTemplateFiles parses the named template files and adds them to the set -// in the same way as template.ParseTemplateFiles but ensures that templates -// with upper-case names are contextually-autoescaped. -func (set *Set) ParseTemplateFiles(filenames ...string) (*Set, error) { - s, err := set.Set.ParseTemplateFiles(filenames...) - if err != nil { - return nil, err +// parseFiles is the helper for the method and function. If the argument +// template is nil, it is created from the first file. +func parseFiles(t *Template, filenames ...string) (*Template, error) { + if len(filenames) == 0 { + // Not really a problem, but be consistent. + return nil, fmt.Errorf("template: no files named in call to ParseFiles") } - if s != &(set.Set) { - panic("new set allocated") + for _, filename := range filenames { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + s := string(b) + name := filepath.Base(filename) + // First template becomes return value if not already defined, + // and we use that one for subsequent New calls to associate + // all the templates together. Also, if this file has the same name + // as t, this file becomes the contents of t, so + // t, err := New(name).Funcs(xxx).ParseFiles(name) + // works. Otherwise we create a new template associated with t. + var tmpl *Template + if t == nil { + t = New(name) + } + if name == t.Name() { + tmpl = t + } else { + tmpl = t.New(name) + } + _, err = tmpl.Parse(s) + if err != nil { + return nil, err + } } - return set, nil -} - -// ParseTemplateGlob parses the template files matched by the -// patern and adds them to the set. Each template will be named -// the base name of its file. -// Unlike with ParseGlob, each file should be a stand-alone template -// definition suitable for Template.Parse (not Set.Parse); that is, the -// file does not contain {{define}} clauses. ParseTemplateGlob is -// therefore equivalent to calling the ParseFile function to create -// individual templates, which are then added to the set. -// Each file must be parseable by itself. -// If an error occurs, parsing stops and the returned set is nil. -func (s *Set) ParseTemplateGlob(pattern string) (*Set, error) { + return t, nil +} + +// ParseGlob creates a new Template and parses the template definitions from the +// files identified by the pattern, which must match at least one file. The +// returned template will have the (base) name and (parsed) contents of the +// first file matched by the pattern. ParseGlob is equivalent to calling +// ParseFiles with the list of files matched by the pattern. +func ParseGlob(pattern string) (*Template, error) { + return parseGlob(nil, pattern) +} + +// ParseGlob parses the template definitions in the files identified by the +// pattern and associates the resulting templates with t. The pattern is +// processed by filepath.Glob and must match at least one file. ParseGlob is +// equivalent to calling t.ParseFiles with the list of files matched by the +// pattern. +func (t *Template) ParseGlob(pattern string) (*Template, error) { + return parseGlob(t, pattern) +} + +// parseGlob is the implementation of the function and method ParseGlob. +func parseGlob(t *Template, pattern string) (*Template, error) { filenames, err := filepath.Glob(pattern) if err != nil { return nil, err } - return s.ParseTemplateFiles(filenames...) -} - -// ParseTemplateFiles creates a set by parsing the named files, -// each of which defines a single template. Each template will be -// named the base name of its file. -// Unlike with ParseFiles, each file should be a stand-alone template -// definition suitable for Template.Parse (not Set.Parse); that is, the -// file does not contain {{define}} clauses. ParseTemplateFiles is -// therefore equivalent to calling the ParseFile function to create -// individual templates, which are then added to the set. -// Each file must be parseable by itself. Parsing stops if an error is -// encountered. -func ParseTemplateFiles(filenames ...string) (*Set, error) { - return new(Set).ParseTemplateFiles(filenames...) -} - -// ParseTemplateGlob creates a set by parsing the files matched -// by the pattern, each of which defines a single template. The pattern -// is processed by filepath.Glob and must match at least one file. Each -// template will be named the base name of its file. -// Unlike with ParseGlob, each file should be a stand-alone template -// definition suitable for Template.Parse (not Set.Parse); that is, the -// file does not contain {{define}} clauses. ParseTemplateGlob is -// therefore equivalent to calling the ParseFile function to create -// individual templates, which are then added to the set. -// Each file must be parseable by itself. Parsing stops if an error is -// encountered. -func ParseTemplateGlob(pattern string) (*Set, error) { - return new(Set).ParseTemplateGlob(pattern) + if len(filenames) == 0 { + return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern) + } + return parseFiles(t, filenames...) } diff --git a/libgo/go/html/token.go b/libgo/go/html/token.go index 2c138227b10..69af96840c2 100644 --- a/libgo/go/html/token.go +++ b/libgo/go/html/token.go @@ -289,7 +289,11 @@ func (z *Tokenizer) readComment() { for dashCount := 2; ; { c := z.readByte() if z.err != nil { - z.data.end = z.raw.end + // Ignore up to two dashes at EOF. + if dashCount > 2 { + dashCount = 2 + } + z.data.end = z.raw.end - dashCount return } switch c { @@ -375,6 +379,28 @@ func (z *Tokenizer) readMarkupDeclaration() TokenType { return DoctypeToken } +// startTagIn returns whether the start tag in z.buf[z.data.start:z.data.end] +// case-insensitively matches any element of ss. +func (z *Tokenizer) startTagIn(ss ...string) bool { +loop: + for _, s := range ss { + if z.data.end-z.data.start != len(s) { + continue loop + } + for i := 0; i < len(s); i++ { + c := z.buf[z.data.start+i] + if 'A' <= c && c <= 'Z' { + c += 'a' - 'A' + } + if c != s[i] { + continue loop + } + } + return true + } + return false +} + // readStartTag reads the next start tag token. The opening "<a" has already // been consumed, where 'a' means anything in [A-Za-z]. func (z *Tokenizer) readStartTag() TokenType { @@ -401,17 +427,27 @@ func (z *Tokenizer) readStartTag() TokenType { break } } - // Any "<noembed>", "<noframes>", "<noscript>", "<script>", "<style>", - // "<textarea>" or "<title>" tag flags the tokenizer's next token as raw. - // The tag name lengths of these special cases ranges in [5, 8]. - if x := z.data.end - z.data.start; 5 <= x && x <= 8 { - switch z.buf[z.data.start] { - case 'n', 's', 't', 'N', 'S', 'T': - switch s := strings.ToLower(string(z.buf[z.data.start:z.data.end])); s { - case "noembed", "noframes", "noscript", "script", "style", "textarea", "title": - z.rawTag = s - } - } + // Several tags flag the tokenizer's next token as raw. + c, raw := z.buf[z.data.start], false + if 'A' <= c && c <= 'Z' { + c += 'a' - 'A' + } + switch c { + case 'i': + raw = z.startTagIn("iframe") + case 'n': + raw = z.startTagIn("noembed", "noframes", "noscript") + case 'p': + raw = z.startTagIn("plaintext") + case 's': + raw = z.startTagIn("script", "style") + case 't': + raw = z.startTagIn("textarea", "title") + case 'x': + raw = z.startTagIn("xmp") + } + if raw { + z.rawTag = strings.ToLower(string(z.buf[z.data.start:z.data.end])) } // Look for a self-closing token like "<br/>". if z.err == nil && z.buf[z.raw.end-2] == '/' { @@ -551,9 +587,19 @@ func (z *Tokenizer) Next() TokenType { z.data.start = z.raw.end z.data.end = z.raw.end if z.rawTag != "" { - z.readRawOrRCDATA() - z.tt = TextToken - return z.tt + if z.rawTag == "plaintext" { + // Read everything up to EOF. + for z.err == nil { + z.readByte() + } + z.textIsRaw = true + } else { + z.readRawOrRCDATA() + } + if z.data.end > z.data.start { + z.tt = TextToken + return z.tt + } } z.textIsRaw = false diff --git a/libgo/go/html/token_test.go b/libgo/go/html/token_test.go index 61d4e67c06d..672d60c4209 100644 --- a/libgo/go/html/token_test.go +++ b/libgo/go/html/token_test.go @@ -325,6 +325,26 @@ var tokenTests = []tokenTest{ }, { "comment9", + "a<!--z-", + "a$<!--z-->", + }, + { + "comment10", + "a<!--z--", + "a$<!--z-->", + }, + { + "comment11", + "a<!--z---", + "a$<!--z--->", + }, + { + "comment12", + "a<!--z----", + "a$<!--z---->", + }, + { + "comment13", "a<!--x--!>z", "a$<!--x-->$z", }, diff --git a/libgo/go/image/tiff/buffer.go b/libgo/go/image/tiff/buffer.go index ce350738ed8..27533c6047d 100644 --- a/libgo/go/image/tiff/buffer.go +++ b/libgo/go/image/tiff/buffer.go @@ -4,10 +4,7 @@ package tiff -import ( - "io" - "os" -) +import "io" // buffer buffers an io.Reader to satisfy io.ReaderAt. type buffer struct { @@ -19,7 +16,7 @@ func (b *buffer) ReadAt(p []byte, off int64) (int, error) { o := int(off) end := o + len(p) if int64(end) != off+int64(len(p)) { - return 0, os.EINVAL + return 0, io.ErrUnexpectedEOF } m := len(b.buf) diff --git a/libgo/go/io/ioutil/ioutil.go b/libgo/go/io/ioutil/ioutil.go index f6c8cd8a873..be7fa5f2bc8 100644 --- a/libgo/go/io/ioutil/ioutil.go +++ b/libgo/go/io/ioutil/ioutil.go @@ -36,8 +36,8 @@ func ReadFile(filename string) ([]byte, error) { // read, so let's try it but be prepared for the answer to be wrong. fi, err := f.Stat() var n int64 - if err == nil && fi.Size < 2e9 { // Don't preallocate a huge buffer, just in case. - n = fi.Size + if size := fi.Size(); err == nil && size < 2e9 { // Don't preallocate a huge buffer, just in case. + n = size } // As initial capacity for readAll, use n + a little extra in case Size is zero, // and to avoid another allocation after Read has filled the buffer. The readAll @@ -63,16 +63,16 @@ func WriteFile(filename string, data []byte, perm uint32) error { return err } -// A fileInfoList implements sort.Interface. -type fileInfoList []*os.FileInfo +// byName implements sort.Interface. +type byName []os.FileInfo -func (f fileInfoList) Len() int { return len(f) } -func (f fileInfoList) Less(i, j int) bool { return f[i].Name < f[j].Name } -func (f fileInfoList) Swap(i, j int) { f[i], f[j] = f[j], f[i] } +func (f byName) Len() int { return len(f) } +func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() } +func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } // ReadDir reads the directory named by dirname and returns // a list of sorted directory entries. -func ReadDir(dirname string) ([]*os.FileInfo, error) { +func ReadDir(dirname string) ([]os.FileInfo, error) { f, err := os.Open(dirname) if err != nil { return nil, err @@ -82,12 +82,8 @@ func ReadDir(dirname string) ([]*os.FileInfo, error) { if err != nil { return nil, err } - fi := make(fileInfoList, len(list)) - for i := range list { - fi[i] = &list[i] - } - sort.Sort(fi) - return fi, nil + sort.Sort(byName(list)) + return list, nil } type nopCloser struct { diff --git a/libgo/go/io/ioutil/ioutil_test.go b/libgo/go/io/ioutil/ioutil_test.go index 55e4b2c2bc8..89d6815ad50 100644 --- a/libgo/go/io/ioutil/ioutil_test.go +++ b/libgo/go/io/ioutil/ioutil_test.go @@ -15,8 +15,8 @@ func checkSize(t *testing.T, path string, size int64) { if err != nil { t.Fatalf("Stat %q (looking for size %d): %s", path, size, err) } - if dir.Size != size { - t.Errorf("Stat %q: size %d want %d", path, dir.Size, size) + if dir.Size() != size { + t.Errorf("Stat %q: size %d want %d", path, dir.Size(), size) } } @@ -76,9 +76,9 @@ func TestReadDir(t *testing.T) { foundTestDir := false for _, dir := range list { switch { - case dir.IsRegular() && dir.Name == "ioutil_test.go": + case !dir.IsDir() && dir.Name() == "ioutil_test.go": foundTest = true - case dir.IsDirectory() && dir.Name == "_test": + case dir.IsDir() && dir.Name() == "_test": foundTestDir = true } } diff --git a/libgo/go/io/ioutil/tempfile.go b/libgo/go/io/ioutil/tempfile.go index 658ea78bb7c..645eed6abb8 100644 --- a/libgo/go/io/ioutil/tempfile.go +++ b/libgo/go/io/ioutil/tempfile.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "strconv" + "time" ) // Random number state, accessed without lock; racy but harmless. @@ -17,8 +18,7 @@ import ( var rand uint32 func reseed() uint32 { - sec, nsec, _ := os.Time() - return uint32(sec*1e9 + nsec + int64(os.Getpid())) + return uint32(time.Now().UnixNano() + int64(os.Getpid())) } func nextSuffix() string { diff --git a/libgo/go/io/multi_test.go b/libgo/go/io/multi_test.go index 0de5cc312d0..eb717f7bc21 100644 --- a/libgo/go/io/multi_test.go +++ b/libgo/go/io/multi_test.go @@ -77,7 +77,7 @@ func TestMultiWriter(t *testing.T) { t.Errorf("unexpected error: %v", err) } - sha1hex := fmt.Sprintf("%x", sha1.Sum()) + sha1hex := fmt.Sprintf("%x", sha1.Sum(nil)) if sha1hex != "01cb303fa8c30a64123067c5aa6284ba7ec2d31b" { t.Error("incorrect sha1 value") } diff --git a/libgo/go/log/log.go b/libgo/go/log/log.go index b5368af5319..a5d88fd9b34 100644 --- a/libgo/go/log/log.go +++ b/libgo/go/log/log.go @@ -83,27 +83,28 @@ func itoa(buf *bytes.Buffer, i int, wid int) { } } -func (l *Logger) formatHeader(buf *bytes.Buffer, ns int64, file string, line int) { +func (l *Logger) formatHeader(buf *bytes.Buffer, t time.Time, file string, line int) { buf.WriteString(l.prefix) if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 { - t := time.SecondsToLocalTime(ns / 1e9) if l.flag&Ldate != 0 { - itoa(buf, int(t.Year), 4) + year, month, day := t.Date() + itoa(buf, year, 4) buf.WriteByte('/') - itoa(buf, int(t.Month), 2) + itoa(buf, int(month), 2) buf.WriteByte('/') - itoa(buf, int(t.Day), 2) + itoa(buf, day, 2) buf.WriteByte(' ') } if l.flag&(Ltime|Lmicroseconds) != 0 { - itoa(buf, int(t.Hour), 2) + hour, min, sec := t.Clock() + itoa(buf, hour, 2) buf.WriteByte(':') - itoa(buf, int(t.Minute), 2) + itoa(buf, min, 2) buf.WriteByte(':') - itoa(buf, int(t.Second), 2) + itoa(buf, sec, 2) if l.flag&Lmicroseconds != 0 { buf.WriteByte('.') - itoa(buf, int(ns%1e9)/1e3, 6) + itoa(buf, t.Nanosecond()/1e3, 6) } buf.WriteByte(' ') } @@ -133,7 +134,7 @@ func (l *Logger) formatHeader(buf *bytes.Buffer, ns int64, file string, line int // provided for generality, although at the moment on all pre-defined // paths it will be 2. func (l *Logger) Output(calldepth int, s string) error { - now := time.Nanoseconds() // get this early. + now := time.Now() // get this early. var file string var line int l.mu.Lock() diff --git a/libgo/go/log/syslog/syslog.go b/libgo/go/log/syslog/syslog.go index 26a2f736b17..546bc296a5f 100644 --- a/libgo/go/log/syslog/syslog.go +++ b/libgo/go/log/syslog/syslog.go @@ -8,6 +8,7 @@ package syslog import ( + "errors" "fmt" "log" "net" @@ -75,7 +76,7 @@ func Dial(network, raddr string, priority Priority, prefix string) (w *Writer, e // Write sends a log message to the syslog daemon. func (w *Writer) Write(b []byte) (int, error) { if w.priority > LOG_DEBUG || w.priority < LOG_EMERG { - return 0, os.EINVAL + return 0, errors.New("log/syslog: invalid priority") } return w.conn.writeBytes(w.priority, w.prefix, b) } diff --git a/libgo/go/math/abs.go b/libgo/go/math/abs.go index eb3e4c72b32..4c6297c6f33 100644 --- a/libgo/go/math/abs.go +++ b/libgo/go/math/abs.go @@ -7,8 +7,7 @@ package math // Abs returns the absolute value of x. // // Special cases are: -// Abs(+Inf) = +Inf -// Abs(-Inf) = +Inf +// Abs(±Inf) = +Inf // Abs(NaN) = NaN func Abs(x float64) float64 { switch { diff --git a/libgo/go/math/asinh.go b/libgo/go/math/asinh.go index c1cad563c76..d6979463d65 100644 --- a/libgo/go/math/asinh.go +++ b/libgo/go/math/asinh.go @@ -33,8 +33,7 @@ package math // Asinh(x) calculates the inverse hyperbolic sine of x. // // Special cases are: -// Asinh(+Inf) = +Inf -// Asinh(-Inf) = -Inf +// Asinh(±Inf) = ±Inf // Asinh(NaN) = NaN func Asinh(x float64) float64 { const ( diff --git a/libgo/go/math/big/calibrate_test.go b/libgo/go/math/big/calibrate_test.go index 1cd93b1052b..0950eeedbd2 100644 --- a/libgo/go/math/big/calibrate_test.go +++ b/libgo/go/math/big/calibrate_test.go @@ -22,14 +22,14 @@ import ( var calibrate = flag.Bool("calibrate", false, "run calibration test") // measure returns the time to run f -func measure(f func()) int64 { +func measure(f func()) time.Duration { const N = 100 - start := time.Nanoseconds() + start := time.Now() for i := N; i > 0; i-- { f() } - stop := time.Nanoseconds() - return (stop - start) / N + stop := time.Now() + return stop.Sub(start) / N } func computeThresholds() { @@ -46,7 +46,7 @@ func computeThresholds() { th1 := -1 th2 := -1 - var deltaOld int64 + var deltaOld time.Duration for count := -1; count != 0; count-- { // determine Tk, the work load execution time using Karatsuba multiplication karatsubaThreshold = n // enable karatsuba diff --git a/libgo/go/math/big/int.go b/libgo/go/math/big/int.go index 533a97f7495..35e2e294183 100644 --- a/libgo/go/math/big/int.go +++ b/libgo/go/math/big/int.go @@ -176,7 +176,7 @@ func (z *Int) Quo(x, y *Int) *Int { // If y == 0, a division-by-zero run-time panic occurs. // Rem implements truncated modulus (like Go); see QuoRem for more details. func (z *Int) Rem(x, y *Int) *Int { - _, z.abs = nat{}.div(z.abs, x.abs, y.abs) + _, z.abs = nat(nil).div(z.abs, x.abs, y.abs) z.neg = len(z.abs) > 0 && x.neg // 0 has no sign return z } @@ -678,14 +678,14 @@ func (z *Int) Bit(i int) uint { panic("negative bit index") } if z.neg { - t := nat{}.sub(z.abs, natOne) + t := nat(nil).sub(z.abs, natOne) return t.bit(uint(i)) ^ 1 } return z.abs.bit(uint(i)) } -// SetBit sets the i'th bit of z to bit and returns z. +// SetBit sets z to x, with x's i'th bit set to b (0 or 1). // That is, if bit is 1 SetBit sets z = x | (1 << i); // if bit is 0 it sets z = x &^ (1 << i). If bit is not 0 or 1, // SetBit will panic. @@ -710,8 +710,8 @@ func (z *Int) And(x, y *Int) *Int { if x.neg == y.neg { if x.neg { // (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) z.abs = z.abs.add(z.abs.or(x1, y1), natOne) z.neg = true // z cannot be zero if x and y are negative return z @@ -729,7 +729,7 @@ func (z *Int) And(x, y *Int) *Int { } // x & (-y) == x & ^(y-1) == x &^ (y-1) - y1 := nat{}.sub(y.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) z.abs = z.abs.andNot(x.abs, y1) z.neg = false return z @@ -740,8 +740,8 @@ func (z *Int) AndNot(x, y *Int) *Int { if x.neg == y.neg { if x.neg { // (-x) &^ (-y) == ^(x-1) &^ ^(y-1) == ^(x-1) & (y-1) == (y-1) &^ (x-1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) z.abs = z.abs.andNot(y1, x1) z.neg = false return z @@ -755,14 +755,14 @@ func (z *Int) AndNot(x, y *Int) *Int { if x.neg { // (-x) &^ y == ^(x-1) &^ y == ^(x-1) & ^y == ^((x-1) | y) == -(((x-1) | y) + 1) - x1 := nat{}.sub(x.abs, natOne) + x1 := nat(nil).sub(x.abs, natOne) z.abs = z.abs.add(z.abs.or(x1, y.abs), natOne) z.neg = true // z cannot be zero if x is negative and y is positive return z } // x &^ (-y) == x &^ ^(y-1) == x & (y-1) - y1 := nat{}.add(y.abs, natOne) + y1 := nat(nil).add(y.abs, natOne) z.abs = z.abs.and(x.abs, y1) z.neg = false return z @@ -773,8 +773,8 @@ func (z *Int) Or(x, y *Int) *Int { if x.neg == y.neg { if x.neg { // (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) z.abs = z.abs.add(z.abs.and(x1, y1), natOne) z.neg = true // z cannot be zero if x and y are negative return z @@ -792,7 +792,7 @@ func (z *Int) Or(x, y *Int) *Int { } // x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1) - y1 := nat{}.sub(y.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) z.abs = z.abs.add(z.abs.andNot(y1, x.abs), natOne) z.neg = true // z cannot be zero if one of x or y is negative return z @@ -803,8 +803,8 @@ func (z *Int) Xor(x, y *Int) *Int { if x.neg == y.neg { if x.neg { // (-x) ^ (-y) == ^(x-1) ^ ^(y-1) == (x-1) ^ (y-1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) z.abs = z.abs.xor(x1, y1) z.neg = false return z @@ -822,7 +822,7 @@ func (z *Int) Xor(x, y *Int) *Int { } // x ^ (-y) == x ^ ^(y-1) == ^(x ^ (y-1)) == -((x ^ (y-1)) + 1) - y1 := nat{}.sub(y.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) z.abs = z.abs.add(z.abs.xor(x.abs, y1), natOne) z.neg = true // z cannot be zero if only one of x or y is negative return z diff --git a/libgo/go/math/big/int_test.go b/libgo/go/math/big/int_test.go index 163c662b0bb..aa7c1949549 100644 --- a/libgo/go/math/big/int_test.go +++ b/libgo/go/math/big/int_test.go @@ -1242,10 +1242,14 @@ func TestBitSet(t *testing.T) { x.SetString(test.x, 0) b := x.Bit(test.i) if b != test.b { - - t.Errorf("#%d want %v got %v", i, test.b, b) + t.Errorf("#%d got %v want %v", i, b, test.b) } } + z := NewInt(1) + z.SetBit(NewInt(0), 2, 1) + if z.Cmp(NewInt(4)) != 0 { + t.Errorf("destination leaked into result; got %s want 4", z) + } } func BenchmarkBitset(b *testing.B) { diff --git a/libgo/go/math/big/nat.go b/libgo/go/math/big/nat.go index 3fa41e7565f..680445dc9a7 100644 --- a/libgo/go/math/big/nat.go +++ b/libgo/go/math/big/nat.go @@ -21,7 +21,9 @@ package big import ( "errors" "io" + "math" "math/rand" + "sync" ) // An unsigned integer x of the form @@ -447,10 +449,10 @@ func (z nat) mulRange(a, b uint64) nat { case a == b: return z.setUint64(a) case a+1 == b: - return z.mul(nat{}.setUint64(a), nat{}.setUint64(b)) + return z.mul(nat(nil).setUint64(a), nat(nil).setUint64(b)) } m := (a + b) / 2 - return z.mul(nat{}.mulRange(a, m), nat{}.mulRange(m+1, b)) + return z.mul(nat(nil).mulRange(a, m), nat(nil).mulRange(m+1, b)) } // q = (x-r)/y, with 0 <= r < y @@ -719,17 +721,17 @@ func (x nat) string(charset string) string { // special cases switch { - case b < 2 || b > 256: + case b < 2 || MaxBase < b: panic("illegal base") case len(x) == 0: return string(charset[0]) } // allocate buffer for conversion - i := x.bitLen()/log2(b) + 1 // +1: round up + i := int(float64(x.bitLen())/math.Log2(float64(b))) + 1 // off by one at most s := make([]byte, i) - // special case: power of two bases can avoid divisions completely + // convert power of two and non power of two bases separately if b == b&-b { // shift is base-b digit size in bits shift := uint(trailingZeroBits(b)) // shift > 0 because b >= 2 @@ -771,65 +773,209 @@ func (x nat) string(charset string) string { w >>= shift nbits -= shift } + } else { + // determine "big base" as in 10^19 for 19 decimal digits in a 64 bit Word + bb := Word(1) // big base is b**ndigits + ndigits := 0 // number of base b digits + for max := Word(_M / b); bb <= max; bb *= b { + ndigits++ // maximize ndigits where bb = b**ndigits, bb <= _M + } - return string(s[i:]) - } + // construct table of successive squares of bb*leafSize to use in subdivisions + table := divisors(len(x), b, ndigits, bb) - // general case: extract groups of digits by multiprecision division + // preserve x, create local copy for use in divisions + q := nat(nil).set(x) - // maximize ndigits where b**ndigits < 2^_W; bb (big base) is b**ndigits - bb := Word(1) - ndigits := 0 - for max := Word(_M / b); bb <= max; bb *= b { - ndigits++ + // convert q to string s in base b with index of MSD indicated by return value + i = q.convertWords(0, i, s, charset, b, ndigits, bb, table) } - // preserve x, create local copy for use in repeated divisions - q := nat{}.set(x) - var r Word + return string(s[i:]) +} + +// Convert words of q to base b digits in s directly using iterated nat/Word divison to extract +// low-order Words and indirectly by recursive subdivision and nat/nat division by tabulated +// divisors. +// +// The direct method processes n Words by n divW() calls, each of which visits every Word in the +// incrementally shortened q for a total of n + (n-1) + (n-2) ... + 2 + 1, or n(n+1)/2 divW()'s. +// Indirect conversion divides q by its approximate square root, yielding two parts, each half +// the size of q. Using the direct method on both halves means 2 * (n/2)(n/2 + 1)/2 divW()'s plus +// the expensive long div(). Asymptotically, the ratio is favorable at 1/2 the divW()'s, and is +// made better by splitting the subblocks recursively. Best is to split blocks until one more +// split would take longer (because of the nat/nat div()) than the twice as many divW()'s of the +// direct approach. This threshold is represented by leafSize. Benchmarking of leafSize in the +// range 2..64 shows that values of 8 and 16 work well, with a 4x speedup at medium lengths and +// ~30x for 20000 digits. Use nat_test.go's BenchmarkLeafSize tests to optimize leafSize for +// specfic hardware. +// +// lo and hi index character array s. conversion starts with the LSD at hi and moves down toward +// the MSD, which will be at s[0] or s[1]. lo == 0 signals span includes the most significant word. +// +func (q nat) convertWords(lo, hi int, s []byte, charset string, b Word, ndigits int, bb Word, table []divisor) int { + // indirect conversion: split larger blocks to reduce quadratic expense of iterated nat/W division + if leafSize > 0 && len(q) > leafSize && table != nil { + var r nat + index := len(table) - 1 + for len(q) > leafSize { + // find divisor close to sqrt(q) if possible, but in any case < q + maxLength := q.bitLen() // ~= log2 q, or at of least largest possible q of this bit length + minLength := maxLength >> 1 // ~= log2 sqrt(q) + for index > 0 && table[index-1].nbits > minLength { + index-- // desired + } + if table[index].nbits >= maxLength && table[index].bbb.cmp(q) >= 0 { + index-- + if index < 0 { + panic("internal inconsistency") + } + } - // convert - if b == 10 { // hard-coding for 10 here speeds this up by 1.25x + // split q into the two digit number (q'*bbb + r) to form independent subblocks + q, r = q.div(r, q, table[index].bbb) + + // convert subblocks and collect results in s[lo:partition] and s[partition:hi] + partition := hi - table[index].ndigits + r.convertWords(partition, hi, s, charset, b, ndigits, bb, table[0:index]) + hi = partition // i.e., q.convertWords(lo, partition, s, charset, b, ndigits, bb, table[0:index+1]) + } + } // having split any large blocks now process the remaining small block + + // direct conversion: process smaller blocks monolithically to avoid overhead of nat/nat division + var r Word + if b == 10 { // hard-coding for 10 here speeds this up by 1.25x (allows mod as mul vs div) for len(q) > 0 { // extract least significant, base bb "digit" - q, r = q.divW(q, bb) // N.B. >82% of time is here. Optimize divW - if len(q) == 0 { + q, r = q.divW(q, bb) + if lo == 0 && len(q) == 0 { // skip leading zeros in most-significant group of digits for j := 0; j < ndigits && r != 0; j++ { - i-- - s[i] = charset[r%10] - r /= 10 + hi-- + t := r / 10 + s[hi] = charset[r-(t<<3+t<<1)] // 8*t + 2*t = 10*t; r - 10*int(r/10) = r mod 10 + r = t } } else { - for j := 0; j < ndigits; j++ { - i-- - s[i] = charset[r%10] - r /= 10 + for j := 0; j < ndigits && hi > lo; j++ { + hi-- + t := r / 10 + s[hi] = charset[r-(t<<3+t<<1)] // 8*t + 2*t = 10*t; r - 10*int(r/10) = r mod 10 + r = t } } } } else { for len(q) > 0 { // extract least significant group of digits - q, r = q.divW(q, bb) // N.B. >82% of time is here. Optimize divW - if len(q) == 0 { + q, r = q.divW(q, bb) + if lo == 0 && len(q) == 0 { // skip leading zeros in most-significant group of digits for j := 0; j < ndigits && r != 0; j++ { - i-- - s[i] = charset[r%b] - r /= b + hi-- + s[hi] = charset[r%b] + r = r / b } } else { - for j := 0; j < ndigits; j++ { - i-- - s[i] = charset[r%b] - r /= b + for j := 0; j < ndigits && hi > lo; j++ { + hi-- + s[hi] = charset[r%b] + r = r / b } } } } - return string(s[i:]) + // prepend high-order zeroes when q has been normalized to a short number of Words. + // however, do not prepend zeroes when converting the most dignificant digits. + if lo != 0 { // if not MSD + zero := charset[0] + for hi > lo { // while need more leading zeroes + hi-- + s[hi] = zero + } + } + + // return index of most significant output digit in s[] (stored in lowest index) + return hi +} + +// Split blocks greater than leafSize Words (or set to 0 to disable indirect conversion) +// Benchmark and configure leafSize using: gotest -test.bench="Leaf" +// 8 and 16 effective on 3.0 GHz Xeon "Clovertown" CPU (128 byte cache lines) +// 8 and 16 effective on 2.66 GHz Core 2 Duo "Penryn" CPU +var leafSize int = 8 // number of Word-size binary values treat as a monolithic block + +type divisor struct { + bbb nat // divisor + nbits int // bit length of divisor (discounting leading zeroes) ~= log2(bbb) + ndigits int // digit length of divisor in terms of output base digits +} + +const maxCache = 64 // maximum number of divisors in a single table +var cacheBase10 [maxCache]divisor // cached divisors for base 10 +var cacheLock sync.Mutex // defense against concurrent table extensions + +// construct table of powers of bb*leafSize to use in subdivisions +func divisors(m int, b Word, ndigits int, bb Word) []divisor { + // only build table when indirect conversion is enabled and x is large + if leafSize == 0 || m <= leafSize { + return nil + } + + // determine k where (bb**leafSize)**(2**k) >= sqrt(x) + k := 1 + for words := leafSize; words < m>>1 && k < maxCache; words <<= 1 { + k++ + } + + // create new table of divisors or extend and reuse existing table as appropriate + var cached bool + var table []divisor + switch b { + case 10: + table = cacheBase10[0:k] // reuse old table for this conversion + cached = true + default: + table = make([]divisor, k) // new table for this conversion + } + + // extend table + if table[k-1].ndigits == 0 { + if cached { + cacheLock.Lock() // begin critical section + } + + var i int + var larger nat + for i < k && table[i].ndigits != 0 { // skip existing entries + i++ + } + for ; i < k; i++ { // add new entries + if i == 0 { + table[i].bbb = nat(nil).expWW(bb, Word(leafSize)) + table[i].ndigits = ndigits * leafSize + } else { + table[i].bbb = nat(nil).mul(table[i-1].bbb, table[i-1].bbb) + table[i].ndigits = 2 * table[i-1].ndigits + } + + // optimization: exploit aggregated extra bits in macro blocks + larger = nat(nil).set(table[i].bbb) + for mulAddVWW(larger, larger, b, 0) == 0 { + table[i].bbb = table[i].bbb.set(larger) + table[i].ndigits++ + } + + table[i].nbits = table[i].bbb.bitLen() + } + + if cached { + cacheLock.Unlock() // end critical section + } + } + + return table } const deBruijn32 = 0x077CB531 @@ -919,9 +1065,11 @@ func (z nat) setBit(x nat, i uint, b uint) nat { return z.norm() case 1: if j >= n { - n = j + 1 + z = z.make(j + 1) + z[n:].clear() + } else { + z = z.make(n) } - z = z.make(n) copy(z, x) z[j] |= m // no need to normalize @@ -1140,7 +1288,12 @@ func (z nat) expNN(x, y, m nat) nat { } } - return z + return z.norm() +} + +// calculate x**y for Word arguments y and y +func (z nat) expWW(x, y Word) nat { + return z.expNN(nat(nil).setWord(x), nat(nil).setWord(y), nil) } // probablyPrime performs reps Miller-Rabin tests to check whether n is prime. @@ -1191,11 +1344,11 @@ func (n nat) probablyPrime(reps int) bool { return false } - nm1 := nat{}.sub(n, natOne) + nm1 := nat(nil).sub(n, natOne) // 1<<k * q = nm1; q, k := nm1.powersOfTwoDecompose() - nm3 := nat{}.sub(nm1, natTwo) + nm3 := nat(nil).sub(nm1, natTwo) rand := rand.New(rand.NewSource(int64(n[0]))) var x, y, quotient nat diff --git a/libgo/go/math/big/nat_test.go b/libgo/go/math/big/nat_test.go index 041a6c4a255..e3c6552d9fb 100644 --- a/libgo/go/math/big/nat_test.go +++ b/libgo/go/math/big/nat_test.go @@ -16,9 +16,9 @@ var cmpTests = []struct { r int }{ {nil, nil, 0}, - {nil, nat{}, 0}, - {nat{}, nil, 0}, - {nat{}, nat{}, 0}, + {nil, nat(nil), 0}, + {nat(nil), nil, 0}, + {nat(nil), nat(nil), 0}, {nat{0}, nat{0}, 0}, {nat{0}, nat{1}, -1}, {nat{1}, nat{0}, 1}, @@ -67,7 +67,7 @@ var prodNN = []argNN{ func TestSet(t *testing.T) { for _, a := range sumNN { - z := nat{}.set(a.z) + z := nat(nil).set(a.z) if z.cmp(a.z) != 0 { t.Errorf("got z = %v; want %v", z, a.z) } @@ -129,7 +129,7 @@ var mulRangesN = []struct { func TestMulRangeN(t *testing.T) { for i, r := range mulRangesN { - prod := nat{}.mulRange(r.a, r.b).decimalString() + prod := nat(nil).mulRange(r.a, r.b).decimalString() if prod != r.prod { t.Errorf("#%d: got %s; want %s", i, prod, r.prod) } @@ -175,7 +175,7 @@ func toString(x nat, charset string) string { s := make([]byte, i) // don't destroy x - q := nat{}.set(x) + q := nat(nil).set(x) // convert for len(q) > 0 { @@ -212,7 +212,7 @@ func TestString(t *testing.T) { t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s) } - x, b, err := nat{}.scan(strings.NewReader(a.s), len(a.c)) + x, b, err := nat(nil).scan(strings.NewReader(a.s), len(a.c)) if x.cmp(a.x) != 0 { t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x) } @@ -271,7 +271,7 @@ var natScanTests = []struct { func TestScanBase(t *testing.T) { for _, a := range natScanTests { r := strings.NewReader(a.s) - x, b, err := nat{}.scan(r, a.base) + x, b, err := nat(nil).scan(r, a.base) if err == nil && !a.ok { t.Errorf("scan%+v\n\texpected error", a) } @@ -370,86 +370,34 @@ func BenchmarkScanPi(b *testing.B) { } } -const ( - // 314**271 - // base 2: 2249 digits - // base 8: 751 digits - // base 10: 678 digits - // base 16: 563 digits - shortBase = 314 - shortExponent = 271 - - // 3141**2178 - // base 2: 31577 digits - // base 8: 10527 digits - // base 10: 9507 digits - // base 16: 7895 digits - mediumBase = 3141 - mediumExponent = 2718 - - // 3141**2178 - // base 2: 406078 digits - // base 8: 135360 digits - // base 10: 122243 digits - // base 16: 101521 digits - longBase = 31415 - longExponent = 27182 -) - -func BenchmarkScanShort2(b *testing.B) { - ScanHelper(b, 2, shortBase, shortExponent) -} - -func BenchmarkScanShort8(b *testing.B) { - ScanHelper(b, 8, shortBase, shortExponent) -} - -func BenchmarkScanSort10(b *testing.B) { - ScanHelper(b, 10, shortBase, shortExponent) -} - -func BenchmarkScanShort16(b *testing.B) { - ScanHelper(b, 16, shortBase, shortExponent) -} - -func BenchmarkScanMedium2(b *testing.B) { - ScanHelper(b, 2, mediumBase, mediumExponent) -} - -func BenchmarkScanMedium8(b *testing.B) { - ScanHelper(b, 8, mediumBase, mediumExponent) -} +func BenchmarkScan10Base2(b *testing.B) { ScanHelper(b, 2, 10, 10) } +func BenchmarkScan100Base2(b *testing.B) { ScanHelper(b, 2, 10, 100) } +func BenchmarkScan1000Base2(b *testing.B) { ScanHelper(b, 2, 10, 1000) } +func BenchmarkScan10000Base2(b *testing.B) { ScanHelper(b, 2, 10, 10000) } +func BenchmarkScan100000Base2(b *testing.B) { ScanHelper(b, 2, 10, 100000) } -func BenchmarkScanMedium10(b *testing.B) { - ScanHelper(b, 10, mediumBase, mediumExponent) -} - -func BenchmarkScanMedium16(b *testing.B) { - ScanHelper(b, 16, mediumBase, mediumExponent) -} +func BenchmarkScan10Base8(b *testing.B) { ScanHelper(b, 8, 10, 10) } +func BenchmarkScan100Base8(b *testing.B) { ScanHelper(b, 8, 10, 100) } +func BenchmarkScan1000Base8(b *testing.B) { ScanHelper(b, 8, 10, 1000) } +func BenchmarkScan10000Base8(b *testing.B) { ScanHelper(b, 8, 10, 10000) } +func BenchmarkScan100000Base8(b *testing.B) { ScanHelper(b, 8, 10, 100000) } -func BenchmarkScanLong2(b *testing.B) { - ScanHelper(b, 2, longBase, longExponent) -} +func BenchmarkScan10Base10(b *testing.B) { ScanHelper(b, 10, 10, 10) } +func BenchmarkScan100Base10(b *testing.B) { ScanHelper(b, 10, 10, 100) } +func BenchmarkScan1000Base10(b *testing.B) { ScanHelper(b, 10, 10, 1000) } +func BenchmarkScan10000Base10(b *testing.B) { ScanHelper(b, 10, 10, 10000) } +func BenchmarkScan100000Base10(b *testing.B) { ScanHelper(b, 10, 10, 100000) } -func BenchmarkScanLong8(b *testing.B) { - ScanHelper(b, 8, longBase, longExponent) -} - -func BenchmarkScanLong10(b *testing.B) { - ScanHelper(b, 10, longBase, longExponent) -} +func BenchmarkScan10Base16(b *testing.B) { ScanHelper(b, 16, 10, 10) } +func BenchmarkScan100Base16(b *testing.B) { ScanHelper(b, 16, 10, 100) } +func BenchmarkScan1000Base16(b *testing.B) { ScanHelper(b, 16, 10, 1000) } +func BenchmarkScan10000Base16(b *testing.B) { ScanHelper(b, 16, 10, 10000) } +func BenchmarkScan100000Base16(b *testing.B) { ScanHelper(b, 16, 10, 100000) } -func BenchmarkScanLong16(b *testing.B) { - ScanHelper(b, 16, longBase, longExponent) -} - -func ScanHelper(b *testing.B, base int, xv, yv Word) { +func ScanHelper(b *testing.B, base int, x, y Word) { b.StopTimer() - var x, y, z nat - x = x.setWord(xv) - y = y.setWord(yv) - z = z.expNN(x, y, nil) + var z nat + z = z.expWW(x, y) var s string s = z.string(lowercaseDigits[0:base]) @@ -459,68 +407,112 @@ func ScanHelper(b *testing.B, base int, xv, yv Word) { b.StartTimer() for i := 0; i < b.N; i++ { - x.scan(strings.NewReader(s), base) + z.scan(strings.NewReader(s), base) } } -func BenchmarkStringShort2(b *testing.B) { - StringHelper(b, 2, shortBase, shortExponent) -} +func BenchmarkString10Base2(b *testing.B) { StringHelper(b, 2, 10, 10) } +func BenchmarkString100Base2(b *testing.B) { StringHelper(b, 2, 10, 100) } +func BenchmarkString1000Base2(b *testing.B) { StringHelper(b, 2, 10, 1000) } +func BenchmarkString10000Base2(b *testing.B) { StringHelper(b, 2, 10, 10000) } +func BenchmarkString100000Base2(b *testing.B) { StringHelper(b, 2, 10, 100000) } -func BenchmarkStringShort8(b *testing.B) { - StringHelper(b, 8, shortBase, shortExponent) -} +func BenchmarkString10Base8(b *testing.B) { StringHelper(b, 8, 10, 10) } +func BenchmarkString100Base8(b *testing.B) { StringHelper(b, 8, 10, 100) } +func BenchmarkString1000Base8(b *testing.B) { StringHelper(b, 8, 10, 1000) } +func BenchmarkString10000Base8(b *testing.B) { StringHelper(b, 8, 10, 10000) } +func BenchmarkString100000Base8(b *testing.B) { StringHelper(b, 8, 10, 100000) } -func BenchmarkStringShort10(b *testing.B) { - StringHelper(b, 10, shortBase, shortExponent) -} +func BenchmarkString10Base10(b *testing.B) { StringHelper(b, 10, 10, 10) } +func BenchmarkString100Base10(b *testing.B) { StringHelper(b, 10, 10, 100) } +func BenchmarkString1000Base10(b *testing.B) { StringHelper(b, 10, 10, 1000) } +func BenchmarkString10000Base10(b *testing.B) { StringHelper(b, 10, 10, 10000) } +func BenchmarkString100000Base10(b *testing.B) { StringHelper(b, 10, 10, 100000) } -func BenchmarkStringShort16(b *testing.B) { - StringHelper(b, 16, shortBase, shortExponent) -} +func BenchmarkString10Base16(b *testing.B) { StringHelper(b, 16, 10, 10) } +func BenchmarkString100Base16(b *testing.B) { StringHelper(b, 16, 10, 100) } +func BenchmarkString1000Base16(b *testing.B) { StringHelper(b, 16, 10, 1000) } +func BenchmarkString10000Base16(b *testing.B) { StringHelper(b, 16, 10, 10000) } +func BenchmarkString100000Base16(b *testing.B) { StringHelper(b, 16, 10, 100000) } -func BenchmarkStringMedium2(b *testing.B) { - StringHelper(b, 2, mediumBase, mediumExponent) -} - -func BenchmarkStringMedium8(b *testing.B) { - StringHelper(b, 8, mediumBase, mediumExponent) -} +func StringHelper(b *testing.B, base int, x, y Word) { + b.StopTimer() + var z nat + z = z.expWW(x, y) + z.string(lowercaseDigits[0:base]) // warm divisor cache + b.StartTimer() -func BenchmarkStringMedium10(b *testing.B) { - StringHelper(b, 10, mediumBase, mediumExponent) + for i := 0; i < b.N; i++ { + _ = z.string(lowercaseDigits[0:base]) + } } -func BenchmarkStringMedium16(b *testing.B) { - StringHelper(b, 16, mediumBase, mediumExponent) -} +func BenchmarkLeafSize0(b *testing.B) { LeafSizeHelper(b, 10, 0) } // test without splitting +func BenchmarkLeafSize1(b *testing.B) { LeafSizeHelper(b, 10, 1) } +func BenchmarkLeafSize2(b *testing.B) { LeafSizeHelper(b, 10, 2) } +func BenchmarkLeafSize3(b *testing.B) { LeafSizeHelper(b, 10, 3) } +func BenchmarkLeafSize4(b *testing.B) { LeafSizeHelper(b, 10, 4) } +func BenchmarkLeafSize5(b *testing.B) { LeafSizeHelper(b, 10, 5) } +func BenchmarkLeafSize6(b *testing.B) { LeafSizeHelper(b, 10, 6) } +func BenchmarkLeafSize7(b *testing.B) { LeafSizeHelper(b, 10, 7) } +func BenchmarkLeafSize8(b *testing.B) { LeafSizeHelper(b, 10, 8) } +func BenchmarkLeafSize9(b *testing.B) { LeafSizeHelper(b, 10, 9) } +func BenchmarkLeafSize10(b *testing.B) { LeafSizeHelper(b, 10, 10) } +func BenchmarkLeafSize11(b *testing.B) { LeafSizeHelper(b, 10, 11) } +func BenchmarkLeafSize12(b *testing.B) { LeafSizeHelper(b, 10, 12) } +func BenchmarkLeafSize13(b *testing.B) { LeafSizeHelper(b, 10, 13) } +func BenchmarkLeafSize14(b *testing.B) { LeafSizeHelper(b, 10, 14) } +func BenchmarkLeafSize15(b *testing.B) { LeafSizeHelper(b, 10, 15) } +func BenchmarkLeafSize16(b *testing.B) { LeafSizeHelper(b, 10, 16) } +func BenchmarkLeafSize32(b *testing.B) { LeafSizeHelper(b, 10, 32) } // try some large lengths +func BenchmarkLeafSize64(b *testing.B) { LeafSizeHelper(b, 10, 64) } + +func LeafSizeHelper(b *testing.B, base Word, size int) { + b.StopTimer() + originalLeafSize := leafSize + resetTable(cacheBase10[:]) + leafSize = size + b.StartTimer() -func BenchmarkStringLong2(b *testing.B) { - StringHelper(b, 2, longBase, longExponent) -} + for d := 1; d <= 10000; d *= 10 { + b.StopTimer() + var z nat + z = z.expWW(base, Word(d)) // build target number + _ = z.string(lowercaseDigits[0:base]) // warm divisor cache + b.StartTimer() -func BenchmarkStringLong8(b *testing.B) { - StringHelper(b, 8, longBase, longExponent) -} + for i := 0; i < b.N; i++ { + _ = z.string(lowercaseDigits[0:base]) + } + } -func BenchmarkStringLong10(b *testing.B) { - StringHelper(b, 10, longBase, longExponent) + b.StopTimer() + resetTable(cacheBase10[:]) + leafSize = originalLeafSize + b.StartTimer() } -func BenchmarkStringLong16(b *testing.B) { - StringHelper(b, 16, longBase, longExponent) +func resetTable(table []divisor) { + if table != nil && table[0].bbb != nil { + for i := 0; i < len(table); i++ { + table[i].bbb = nil + table[i].nbits = 0 + table[i].ndigits = 0 + } + } } -func StringHelper(b *testing.B, base int, xv, yv Word) { - b.StopTimer() - var x, y, z nat - x = x.setWord(xv) - y = y.setWord(yv) - z = z.expNN(x, y, nil) - b.StartTimer() - - for i := 0; i < b.N; i++ { - z.string(lowercaseDigits[0:base]) +func TestStringPowers(t *testing.T) { + var b, p Word + for b = 2; b <= 16; b++ { + for p = 0; p <= 512; p++ { + x := nat(nil).expWW(b, p) + xs := x.string(lowercaseDigits[0:b]) + xs2 := toString(x, lowercaseDigits[0:b]) + if xs != xs2 { + t.Errorf("failed at %d ** %d in base %d: %s != %s", b, p, b, xs, xs2) + } + } } } @@ -651,17 +643,17 @@ var expNNTests = []struct { func TestExpNN(t *testing.T) { for i, test := range expNNTests { - x, _, _ := nat{}.scan(strings.NewReader(test.x), 0) - y, _, _ := nat{}.scan(strings.NewReader(test.y), 0) - out, _, _ := nat{}.scan(strings.NewReader(test.out), 0) + x, _, _ := nat(nil).scan(strings.NewReader(test.x), 0) + y, _, _ := nat(nil).scan(strings.NewReader(test.y), 0) + out, _, _ := nat(nil).scan(strings.NewReader(test.out), 0) var m nat if len(test.m) > 0 { - m, _, _ = nat{}.scan(strings.NewReader(test.m), 0) + m, _, _ = nat(nil).scan(strings.NewReader(test.m), 0) } - z := nat{}.expNN(x, y, m) + z := nat(nil).expNN(x, y, m) if z.cmp(out) != 0 { t.Errorf("#%d got %v want %v", i, z, out) } diff --git a/libgo/go/math/big/rat.go b/libgo/go/math/big/rat.go index 3a0add32363..adf412485f6 100644 --- a/libgo/go/math/big/rat.go +++ b/libgo/go/math/big/rat.go @@ -33,7 +33,7 @@ func (z *Rat) SetFrac(a, b *Int) *Rat { panic("division by zero") } if &z.a == b || alias(z.a.abs, babs) { - babs = nat{}.set(babs) // make a copy + babs = nat(nil).set(babs) // make a copy } z.a.abs = z.a.abs.set(a.abs) z.b = z.b.set(babs) @@ -315,7 +315,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) { if _, ok := z.a.SetString(s, 10); !ok { return nil, false } - powTen := nat{}.expNN(natTen, exp.abs, nil) + powTen := nat(nil).expNN(natTen, exp.abs, nil) if exp.neg { z.b = powTen z.norm() @@ -357,23 +357,23 @@ func (z *Rat) FloatString(prec int) string { } // z.b != 0 - q, r := nat{}.div(nat{}, z.a.abs, z.b) + q, r := nat(nil).div(nat(nil), z.a.abs, z.b) p := natOne if prec > 0 { - p = nat{}.expNN(natTen, nat{}.setUint64(uint64(prec)), nil) + p = nat(nil).expNN(natTen, nat(nil).setUint64(uint64(prec)), nil) } r = r.mul(r, p) - r, r2 := r.div(nat{}, r, z.b) + r, r2 := r.div(nat(nil), r, z.b) // see if we need to round up r2 = r2.add(r2, r2) if z.b.cmp(r2) <= 0 { r = r.add(r, natOne) if r.cmp(p) >= 0 { - q = nat{}.add(q, natOne) - r = nat{}.sub(r, p) + q = nat(nil).add(q, natOne) + r = nat(nil).sub(r, p) } } diff --git a/libgo/go/math/cbrt.go b/libgo/go/math/cbrt.go index d2b7e910b84..09edc0eae88 100644 --- a/libgo/go/math/cbrt.go +++ b/libgo/go/math/cbrt.go @@ -45,22 +45,21 @@ func Cbrt(x float64) float64 { x = -x sign = true } - // Reduce argument - f, e := Frexp(x) + // Reduce argument and estimate cube root + f, e := Frexp(x) // 0.5 <= f < 1.0 m := e % 3 if m > 0 { m -= 3 e -= m // e is multiple of 3 } - f = Ldexp(f, m) // 0.125 <= f < 1.0 - - // Estimate cube root switch m { case 0: // 0.5 <= f < 1.0 f = A1*f + A2 - A3/(A4+f) - case -1: // 0.25 <= f < 0.5 + case -1: + f *= 0.5 // 0.25 <= f < 0.5 f = B1*f + B2 - B3/(B4+f) - default: // 0.125 <= f < 0.25 + default: // m == -2 + f *= 0.25 // 0.125 <= f < 0.25 f = C1*f + C2 - C3/(C4+f) } y := Ldexp(f, e/3) // e/3 = exponent of cube root diff --git a/libgo/go/math/floor.go b/libgo/go/math/floor.go index babbf645f5c..8de4d7e2ce6 100644 --- a/libgo/go/math/floor.go +++ b/libgo/go/math/floor.go @@ -7,8 +7,7 @@ package math // Floor returns the greatest integer value less than or equal to x. // // Special cases are: -// Floor(+Inf) = +Inf -// Floor(-Inf) = -Inf +// Floor(±Inf) = ±Inf // Floor(NaN) = NaN func Floor(x float64) float64 { // TODO(rsc): Remove manual inlining of IsNaN, IsInf @@ -30,16 +29,14 @@ func Floor(x float64) float64 { // Ceil returns the least integer value greater than or equal to x. // // Special cases are: -// Ceil(+Inf) = +Inf -// Ceil(-Inf) = -Inf +// Ceil(±Inf) = ±Inf // Ceil(NaN) = NaN func Ceil(x float64) float64 { return -Floor(-x) } // Trunc returns the integer value of x. // // Special cases are: -// Trunc(+Inf) = +Inf -// Trunc(-Inf) = -Inf +// Trunc(±Inf) = ±Inf // Trunc(NaN) = NaN func Trunc(x float64) float64 { // TODO(rsc): Remove manual inlining of IsNaN, IsInf diff --git a/libgo/go/math/gamma.go b/libgo/go/math/gamma.go index e117158fee2..7365d8e7754 100644 --- a/libgo/go/math/gamma.go +++ b/libgo/go/math/gamma.go @@ -63,7 +63,7 @@ package math // Stephen L. Moshier // moshier@na-net.ornl.gov -var _P = [...]float64{ +var _gamP = [...]float64{ 1.60119522476751861407e-04, 1.19135147006586384913e-03, 1.04213797561761569935e-02, @@ -72,7 +72,7 @@ var _P = [...]float64{ 4.94214826801497100753e-01, 9.99999999999999996796e-01, } -var _Q = [...]float64{ +var _gamQ = [...]float64{ -2.31581873324120129819e-05, 5.39605580493303397842e-04, -4.45641913851797240494e-03, @@ -82,7 +82,7 @@ var _Q = [...]float64{ 7.14304917030273074085e-02, 1.00000000000000000320e+00, } -var _S = [...]float64{ +var _gamS = [...]float64{ 7.87311395793093628397e-04, -2.29549961613378126380e-04, -2.68132617805781232825e-03, @@ -98,7 +98,7 @@ func stirling(x float64) float64 { MaxStirling = 143.01608 ) w := 1 / x - w = 1 + w*((((_S[0]*w+_S[1])*w+_S[2])*w+_S[3])*w+_S[4]) + w = 1 + w*((((_gamS[0]*w+_gamS[1])*w+_gamS[2])*w+_gamS[3])*w+_gamS[4]) y := Exp(x) if x > MaxStirling { // avoid Pow() overflow v := Pow(x, 0.5*x-0.25) @@ -113,8 +113,7 @@ func stirling(x float64) float64 { // Gamma(x) returns the Gamma function of x. // // Special cases are: -// Gamma(Inf) = Inf -// Gamma(-Inf) = -Inf +// Gamma(±Inf) = ±Inf // Gamma(NaN) = NaN // Large values overflow to +Inf. // Negative integer values equal ±Inf. @@ -176,8 +175,8 @@ func Gamma(x float64) float64 { } x = x - 2 - p = (((((x*_P[0]+_P[1])*x+_P[2])*x+_P[3])*x+_P[4])*x+_P[5])*x + _P[6] - q = ((((((x*_Q[0]+_Q[1])*x+_Q[2])*x+_Q[3])*x+_Q[4])*x+_Q[5])*x+_Q[6])*x + _Q[7] + p = (((((x*_gamP[0]+_gamP[1])*x+_gamP[2])*x+_gamP[3])*x+_gamP[4])*x+_gamP[5])*x + _gamP[6] + q = ((((((x*_gamQ[0]+_gamQ[1])*x+_gamQ[2])*x+_gamQ[3])*x+_gamQ[4])*x+_gamQ[5])*x+_gamQ[6])*x + _gamQ[7] return z * p / q small: diff --git a/libgo/go/math/lgamma.go b/libgo/go/math/lgamma.go index 8f6d7b99fc5..e2bad69dc03 100644 --- a/libgo/go/math/lgamma.go +++ b/libgo/go/math/lgamma.go @@ -88,6 +88,81 @@ package math // // +var _lgamA = [...]float64{ + 7.72156649015328655494e-02, // 0x3FB3C467E37DB0C8 + 3.22467033424113591611e-01, // 0x3FD4A34CC4A60FAD + 6.73523010531292681824e-02, // 0x3FB13E001A5562A7 + 2.05808084325167332806e-02, // 0x3F951322AC92547B + 7.38555086081402883957e-03, // 0x3F7E404FB68FEFE8 + 2.89051383673415629091e-03, // 0x3F67ADD8CCB7926B + 1.19270763183362067845e-03, // 0x3F538A94116F3F5D + 5.10069792153511336608e-04, // 0x3F40B6C689B99C00 + 2.20862790713908385557e-04, // 0x3F2CF2ECED10E54D + 1.08011567247583939954e-04, // 0x3F1C5088987DFB07 + 2.52144565451257326939e-05, // 0x3EFA7074428CFA52 + 4.48640949618915160150e-05, // 0x3F07858E90A45837 +} +var _lgamR = [...]float64{ + 1.0, // placeholder + 1.39200533467621045958e+00, // 0x3FF645A762C4AB74 + 7.21935547567138069525e-01, // 0x3FE71A1893D3DCDC + 1.71933865632803078993e-01, // 0x3FC601EDCCFBDF27 + 1.86459191715652901344e-02, // 0x3F9317EA742ED475 + 7.77942496381893596434e-04, // 0x3F497DDACA41A95B + 7.32668430744625636189e-06, // 0x3EDEBAF7A5B38140 +} +var _lgamS = [...]float64{ + -7.72156649015328655494e-02, // 0xBFB3C467E37DB0C8 + 2.14982415960608852501e-01, // 0x3FCB848B36E20878 + 3.25778796408930981787e-01, // 0x3FD4D98F4F139F59 + 1.46350472652464452805e-01, // 0x3FC2BB9CBEE5F2F7 + 2.66422703033638609560e-02, // 0x3F9B481C7E939961 + 1.84028451407337715652e-03, // 0x3F5E26B67368F239 + 3.19475326584100867617e-05, // 0x3F00BFECDD17E945 +} +var _lgamT = [...]float64{ + 4.83836122723810047042e-01, // 0x3FDEF72BC8EE38A2 + -1.47587722994593911752e-01, // 0xBFC2E4278DC6C509 + 6.46249402391333854778e-02, // 0x3FB08B4294D5419B + -3.27885410759859649565e-02, // 0xBFA0C9A8DF35B713 + 1.79706750811820387126e-02, // 0x3F9266E7970AF9EC + -1.03142241298341437450e-02, // 0xBF851F9FBA91EC6A + 6.10053870246291332635e-03, // 0x3F78FCE0E370E344 + -3.68452016781138256760e-03, // 0xBF6E2EFFB3E914D7 + 2.25964780900612472250e-03, // 0x3F6282D32E15C915 + -1.40346469989232843813e-03, // 0xBF56FE8EBF2D1AF1 + 8.81081882437654011382e-04, // 0x3F4CDF0CEF61A8E9 + -5.38595305356740546715e-04, // 0xBF41A6109C73E0EC + 3.15632070903625950361e-04, // 0x3F34AF6D6C0EBBF7 + -3.12754168375120860518e-04, // 0xBF347F24ECC38C38 + 3.35529192635519073543e-04, // 0x3F35FD3EE8C2D3F4 +} +var _lgamU = [...]float64{ + -7.72156649015328655494e-02, // 0xBFB3C467E37DB0C8 + 6.32827064025093366517e-01, // 0x3FE4401E8B005DFF + 1.45492250137234768737e+00, // 0x3FF7475CD119BD6F + 9.77717527963372745603e-01, // 0x3FEF497644EA8450 + 2.28963728064692451092e-01, // 0x3FCD4EAEF6010924 + 1.33810918536787660377e-02, // 0x3F8B678BBF2BAB09 +} +var _lgamV = [...]float64{ + 1.0, + 2.45597793713041134822e+00, // 0x4003A5D7C2BD619C + 2.12848976379893395361e+00, // 0x40010725A42B18F5 + 7.69285150456672783825e-01, // 0x3FE89DFBE45050AF + 1.04222645593369134254e-01, // 0x3FBAAE55D6537C88 + 3.21709242282423911810e-03, // 0x3F6A5ABB57D0CF61 +} +var _lgamW = [...]float64{ + 4.18938533204672725052e-01, // 0x3FDACFE390C97D69 + 8.33333333333329678849e-02, // 0x3FB555555555553B + -2.77777777728775536470e-03, // 0xBF66C16C16B02E5C + 7.93650558643019558500e-04, // 0x3F4A019F98CF38B6 + -5.95187557450339963135e-04, // 0xBF4380CB8C0FE741 + 8.36339918996282139126e-04, // 0x3F4B67BA4CDAD5D1 + -1.63092934096575273989e-03, // 0xBF5AB89D0B9E43E4 +} + // Lgamma returns the natural logarithm and sign (-1 or +1) of Gamma(x). // // Special cases are: @@ -103,68 +178,10 @@ func Lgamma(x float64) (lgamma float64, sign int) { Two53 = 1 << 53 // 0x4340000000000000 ~9.0072e+15 Two58 = 1 << 58 // 0x4390000000000000 ~2.8823e+17 Tiny = 1.0 / (1 << 70) // 0x3b90000000000000 ~8.47033e-22 - A0 = 7.72156649015328655494e-02 // 0x3FB3C467E37DB0C8 - A1 = 3.22467033424113591611e-01 // 0x3FD4A34CC4A60FAD - A2 = 6.73523010531292681824e-02 // 0x3FB13E001A5562A7 - A3 = 2.05808084325167332806e-02 // 0x3F951322AC92547B - A4 = 7.38555086081402883957e-03 // 0x3F7E404FB68FEFE8 - A5 = 2.89051383673415629091e-03 // 0x3F67ADD8CCB7926B - A6 = 1.19270763183362067845e-03 // 0x3F538A94116F3F5D - A7 = 5.10069792153511336608e-04 // 0x3F40B6C689B99C00 - A8 = 2.20862790713908385557e-04 // 0x3F2CF2ECED10E54D - A9 = 1.08011567247583939954e-04 // 0x3F1C5088987DFB07 - A10 = 2.52144565451257326939e-05 // 0x3EFA7074428CFA52 - A11 = 4.48640949618915160150e-05 // 0x3F07858E90A45837 Tc = 1.46163214496836224576e+00 // 0x3FF762D86356BE3F Tf = -1.21486290535849611461e-01 // 0xBFBF19B9BCC38A42 // Tt = -(tail of Tf) - Tt = -3.63867699703950536541e-18 // 0xBC50C7CAA48A971F - T0 = 4.83836122723810047042e-01 // 0x3FDEF72BC8EE38A2 - T1 = -1.47587722994593911752e-01 // 0xBFC2E4278DC6C509 - T2 = 6.46249402391333854778e-02 // 0x3FB08B4294D5419B - T3 = -3.27885410759859649565e-02 // 0xBFA0C9A8DF35B713 - T4 = 1.79706750811820387126e-02 // 0x3F9266E7970AF9EC - T5 = -1.03142241298341437450e-02 // 0xBF851F9FBA91EC6A - T6 = 6.10053870246291332635e-03 // 0x3F78FCE0E370E344 - T7 = -3.68452016781138256760e-03 // 0xBF6E2EFFB3E914D7 - T8 = 2.25964780900612472250e-03 // 0x3F6282D32E15C915 - T9 = -1.40346469989232843813e-03 // 0xBF56FE8EBF2D1AF1 - T10 = 8.81081882437654011382e-04 // 0x3F4CDF0CEF61A8E9 - T11 = -5.38595305356740546715e-04 // 0xBF41A6109C73E0EC - T12 = 3.15632070903625950361e-04 // 0x3F34AF6D6C0EBBF7 - T13 = -3.12754168375120860518e-04 // 0xBF347F24ECC38C38 - T14 = 3.35529192635519073543e-04 // 0x3F35FD3EE8C2D3F4 - U0 = -7.72156649015328655494e-02 // 0xBFB3C467E37DB0C8 - U1 = 6.32827064025093366517e-01 // 0x3FE4401E8B005DFF - U2 = 1.45492250137234768737e+00 // 0x3FF7475CD119BD6F - U3 = 9.77717527963372745603e-01 // 0x3FEF497644EA8450 - U4 = 2.28963728064692451092e-01 // 0x3FCD4EAEF6010924 - U5 = 1.33810918536787660377e-02 // 0x3F8B678BBF2BAB09 - V1 = 2.45597793713041134822e+00 // 0x4003A5D7C2BD619C - V2 = 2.12848976379893395361e+00 // 0x40010725A42B18F5 - V3 = 7.69285150456672783825e-01 // 0x3FE89DFBE45050AF - V4 = 1.04222645593369134254e-01 // 0x3FBAAE55D6537C88 - V5 = 3.21709242282423911810e-03 // 0x3F6A5ABB57D0CF61 - S0 = -7.72156649015328655494e-02 // 0xBFB3C467E37DB0C8 - S1 = 2.14982415960608852501e-01 // 0x3FCB848B36E20878 - S2 = 3.25778796408930981787e-01 // 0x3FD4D98F4F139F59 - S3 = 1.46350472652464452805e-01 // 0x3FC2BB9CBEE5F2F7 - S4 = 2.66422703033638609560e-02 // 0x3F9B481C7E939961 - S5 = 1.84028451407337715652e-03 // 0x3F5E26B67368F239 - S6 = 3.19475326584100867617e-05 // 0x3F00BFECDD17E945 - R1 = 1.39200533467621045958e+00 // 0x3FF645A762C4AB74 - R2 = 7.21935547567138069525e-01 // 0x3FE71A1893D3DCDC - R3 = 1.71933865632803078993e-01 // 0x3FC601EDCCFBDF27 - R4 = 1.86459191715652901344e-02 // 0x3F9317EA742ED475 - R5 = 7.77942496381893596434e-04 // 0x3F497DDACA41A95B - R6 = 7.32668430744625636189e-06 // 0x3EDEBAF7A5B38140 - W0 = 4.18938533204672725052e-01 // 0x3FDACFE390C97D69 - W1 = 8.33333333333329678849e-02 // 0x3FB555555555553B - W2 = -2.77777777728775536470e-03 // 0xBF66C16C16B02E5C - W3 = 7.93650558643019558500e-04 // 0x3F4A019F98CF38B6 - W4 = -5.95187557450339963135e-04 // 0xBF4380CB8C0FE741 - W5 = 8.36339918996282139126e-04 // 0x3F4B67BA4CDAD5D1 - W6 = -1.63092934096575273989e-03 // 0xBF5AB89D0B9E43E4 + Tt = -3.63867699703950536541e-18 // 0xBC50C7CAA48A971F ) // TODO(rsc): Remove manual inlining of IsNaN, IsInf // when compiler does it for us @@ -249,28 +266,28 @@ func Lgamma(x float64) (lgamma float64, sign int) { switch i { case 0: z := y * y - p1 := A0 + z*(A2+z*(A4+z*(A6+z*(A8+z*A10)))) - p2 := z * (A1 + z*(A3+z*(A5+z*(A7+z*(A9+z*A11))))) + p1 := _lgamA[0] + z*(_lgamA[2]+z*(_lgamA[4]+z*(_lgamA[6]+z*(_lgamA[8]+z*_lgamA[10])))) + p2 := z * (_lgamA[1] + z*(+_lgamA[3]+z*(_lgamA[5]+z*(_lgamA[7]+z*(_lgamA[9]+z*_lgamA[11]))))) p := y*p1 + p2 lgamma += (p - 0.5*y) case 1: z := y * y w := z * y - p1 := T0 + w*(T3+w*(T6+w*(T9+w*T12))) // parallel comp - p2 := T1 + w*(T4+w*(T7+w*(T10+w*T13))) - p3 := T2 + w*(T5+w*(T8+w*(T11+w*T14))) + p1 := _lgamT[0] + w*(_lgamT[3]+w*(_lgamT[6]+w*(_lgamT[9]+w*_lgamT[12]))) // parallel comp + p2 := _lgamT[1] + w*(_lgamT[4]+w*(_lgamT[7]+w*(_lgamT[10]+w*_lgamT[13]))) + p3 := _lgamT[2] + w*(_lgamT[5]+w*(_lgamT[8]+w*(_lgamT[11]+w*_lgamT[14]))) p := z*p1 - (Tt - w*(p2+y*p3)) lgamma += (Tf + p) case 2: - p1 := y * (U0 + y*(U1+y*(U2+y*(U3+y*(U4+y*U5))))) - p2 := 1 + y*(V1+y*(V2+y*(V3+y*(V4+y*V5)))) + p1 := y * (_lgamU[0] + y*(_lgamU[1]+y*(_lgamU[2]+y*(_lgamU[3]+y*(_lgamU[4]+y*_lgamU[5]))))) + p2 := 1 + y*(_lgamV[1]+y*(_lgamV[2]+y*(_lgamV[3]+y*(_lgamV[4]+y*_lgamV[5])))) lgamma += (-0.5*y + p1/p2) } case x < 8: // 2 <= x < 8 i := int(x) y := x - float64(i) - p := y * (S0 + y*(S1+y*(S2+y*(S3+y*(S4+y*(S5+y*S6)))))) - q := 1 + y*(R1+y*(R2+y*(R3+y*(R4+y*(R5+y*R6))))) + p := y * (_lgamS[0] + y*(_lgamS[1]+y*(_lgamS[2]+y*(_lgamS[3]+y*(_lgamS[4]+y*(_lgamS[5]+y*_lgamS[6])))))) + q := 1 + y*(_lgamR[1]+y*(_lgamR[2]+y*(_lgamR[3]+y*(_lgamR[4]+y*(_lgamR[5]+y*_lgamR[6]))))) lgamma = 0.5*y + p/q z := 1.0 // Lgamma(1+s) = Log(s) + Lgamma(s) switch i { @@ -294,7 +311,7 @@ func Lgamma(x float64) (lgamma float64, sign int) { t := Log(x) z := 1 / x y := z * z - w := W0 + z*(W1+y*(W2+y*(W3+y*(W4+y*(W5+y*W6))))) + w := _lgamW[0] + z*(_lgamW[1]+y*(_lgamW[2]+y*(_lgamW[3]+y*(_lgamW[4]+y*(_lgamW[5]+y*_lgamW[6]))))) lgamma = (x-0.5)*(t-1) + w default: // 2**58 <= x <= Inf lgamma = x * (Log(x) - 1) diff --git a/libgo/go/math/log1p.go b/libgo/go/math/log1p.go index c25d73b6641..e8914a1d053 100644 --- a/libgo/go/math/log1p.go +++ b/libgo/go/math/log1p.go @@ -44,7 +44,7 @@ package math // 2 4 6 8 10 12 14 // R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s +Lp6*s +Lp7*s // (the values of Lp1 to Lp7 are listed in the program) -// a-0.2929nd +// and // | 2 14 | -58.45 // | Lp1*s +...+Lp7*s - R(z) | <= 2 // | | @@ -88,6 +88,7 @@ package math // // Special cases are: // Log1p(+Inf) = +Inf +// Log1p(±0) = ±0 // Log1p(-1) = -Inf // Log1p(x < -1) = NaN // Log1p(NaN) = NaN diff --git a/libgo/go/math/modf.go b/libgo/go/math/modf.go index 315174b7014..34889e0c0af 100644 --- a/libgo/go/math/modf.go +++ b/libgo/go/math/modf.go @@ -8,8 +8,7 @@ package math // that sum to f. Both values have the same sign as f. // // Special cases are: -// Modf(+Inf) = +Inf, NaN -// Modf(-Inf) = -Inf, NaN +// Modf(±Inf) = ±Inf, NaN // Modf(NaN) = NaN, NaN func Modf(f float64) (int float64, frac float64) { if f < 1 { diff --git a/libgo/go/math/sincos.go b/libgo/go/math/sincos.go index 4c1576bead1..f5412fd726f 100644 --- a/libgo/go/math/sincos.go +++ b/libgo/go/math/sincos.go @@ -4,10 +4,66 @@ package math +// Coefficients _sin[] and _cos[] are found in pkg/math/sin.go. + // Sincos(x) returns Sin(x), Cos(x). // // Special conditions are: -// Sincos(+Inf) = NaN, NaN -// Sincos(-Inf) = NaN, NaN +// Sincos(±0) = ±0, 1 +// Sincos(±Inf) = NaN, NaN // Sincos(NaN) = NaN, NaN -func Sincos(x float64) (sin, cos float64) { return Sin(x), Cos(x) } +func Sincos(x float64) (sin, cos float64) { + const ( + PI4A = 7.85398125648498535156E-1 // 0x3fe921fb40000000, Pi/4 split into three parts + PI4B = 3.77489470793079817668E-8 // 0x3e64442d00000000, + PI4C = 2.69515142907905952645E-15 // 0x3ce8469898cc5170, + M4PI = 1.273239544735162542821171882678754627704620361328125 // 4/pi + ) + // TODO(rsc): Remove manual inlining of IsNaN, IsInf + // when compiler does it for us + // special cases + switch { + case x == 0: + return x, 1 // return ±0.0, 1.0 + case x != x || x < -MaxFloat64 || x > MaxFloat64: // IsNaN(x) || IsInf(x, 0): + return NaN(), NaN() + } + + // make argument positive + sinSign, cosSign := false, false + if x < 0 { + x = -x + sinSign = true + } + + j := int64(x * M4PI) // integer part of x/(Pi/4), as integer for tests on the phase angle + y := float64(j) // integer part of x/(Pi/4), as float + + if j&1 == 1 { // map zeros to origin + j += 1 + y += 1 + } + j &= 7 // octant modulo 2Pi radians (360 degrees) + if j > 3 { // reflect in x axis + j -= 4 + sinSign, cosSign = !sinSign, !cosSign + } + if j > 1 { + cosSign = !cosSign + } + + z := ((x - y*PI4A) - y*PI4B) - y*PI4C // Extended precision modular arithmetic + zz := z * z + cos = 1.0 - 0.5*zz + zz*zz*((((((_cos[0]*zz)+_cos[1])*zz+_cos[2])*zz+_cos[3])*zz+_cos[4])*zz+_cos[5]) + sin = z + z*zz*((((((_sin[0]*zz)+_sin[1])*zz+_sin[2])*zz+_sin[3])*zz+_sin[4])*zz+_sin[5]) + if j == 1 || j == 2 { + sin, cos = cos, sin + } + if cosSign { + cos = -cos + } + if sinSign { + sin = -sin + } + return +} diff --git a/libgo/go/mime/multipart/formdata.go b/libgo/go/mime/multipart/formdata.go index d9982e5b9c8..ec643c1476f 100644 --- a/libgo/go/mime/multipart/formdata.go +++ b/libgo/go/mime/multipart/formdata.go @@ -160,7 +160,7 @@ type sliceReaderAt []byte func (r sliceReaderAt) ReadAt(b []byte, off int64) (int, error) { if int(off) >= len(r) || off < 0 { - return 0, os.EINVAL + return 0, io.ErrUnexpectedEOF } n := copy(b, r[int(off):]) return n, nil diff --git a/libgo/go/mime/type.go b/libgo/go/mime/type.go index ce72bb5f7f9..e3d968fb81a 100644 --- a/libgo/go/mime/type.go +++ b/libgo/go/mime/type.go @@ -6,19 +6,11 @@ package mime import ( - "bufio" "fmt" - "os" "strings" "sync" ) -var typeFiles = []string{ - "/etc/mime.types", - "/etc/apache2/mime.types", - "/etc/apache/mime.types", -} - var mimeTypes = map[string]string{ ".css": "text/css; charset=utf-8", ".gif": "image/gif", @@ -33,46 +25,13 @@ var mimeTypes = map[string]string{ var mimeLock sync.RWMutex -func loadMimeFile(filename string) { - f, err := os.Open(filename) - if err != nil { - return - } - - reader := bufio.NewReader(f) - for { - line, err := reader.ReadString('\n') - if err != nil { - f.Close() - return - } - fields := strings.Fields(line) - if len(fields) <= 1 || fields[0][0] == '#' { - continue - } - mimeType := fields[0] - for _, ext := range fields[1:] { - if ext[0] == '#' { - break - } - setExtensionType("."+ext, mimeType) - } - } -} - -func initMime() { - for _, filename := range typeFiles { - loadMimeFile(filename) - } -} - var once sync.Once // TypeByExtension returns the MIME type associated with the file extension ext. // The extension ext should begin with a leading dot, as in ".html". // When ext has no associated type, TypeByExtension returns "". // -// The built-in table is small but is is augmented by the local +// The built-in table is small but on unix it is augmented by the local // system's mime.types file(s) if available under one or more of these // names: // @@ -80,6 +39,8 @@ var once sync.Once // /etc/apache2/mime.types // /etc/apache/mime.types // +// Windows system mime types are extracted from registry. +// // Text types have the charset parameter set to "utf-8" by default. func TypeByExtension(ext string) string { once.Do(initMime) diff --git a/libgo/go/mime/type_test.go b/libgo/go/mime/type_test.go index 976f8534309..07e1cd5daec 100644 --- a/libgo/go/mime/type_test.go +++ b/libgo/go/mime/type_test.go @@ -6,15 +6,9 @@ package mime import "testing" -var typeTests = map[string]string{ - ".t1": "application/test", - ".t2": "text/test; charset=utf-8", - ".png": "image/png", -} +var typeTests = initMimeForTests() func TestTypeByExtension(t *testing.T) { - typeFiles = []string{"test.types"} - for ext, want := range typeTests { val := TypeByExtension(ext) if val != want { diff --git a/libgo/go/mime/type_unix.go b/libgo/go/mime/type_unix.go new file mode 100644 index 00000000000..45127ba29df --- /dev/null +++ b/libgo/go/mime/type_unix.go @@ -0,0 +1,59 @@ +// Copyright 2010 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 mime + +import ( + "bufio" + "os" + "strings" +) + +var typeFiles = []string{ + "/etc/mime.types", + "/etc/apache2/mime.types", + "/etc/apache/mime.types", +} + +func loadMimeFile(filename string) { + f, err := os.Open(filename) + if err != nil { + return + } + + reader := bufio.NewReader(f) + for { + line, err := reader.ReadString('\n') + if err != nil { + f.Close() + return + } + fields := strings.Fields(line) + if len(fields) <= 1 || fields[0][0] == '#' { + continue + } + mimeType := fields[0] + for _, ext := range fields[1:] { + if ext[0] == '#' { + break + } + setExtensionType("."+ext, mimeType) + } + } +} + +func initMime() { + for _, filename := range typeFiles { + loadMimeFile(filename) + } +} + +func initMimeForTests() map[string]string { + typeFiles = []string{"test.types"} + return map[string]string{ + ".t1": "application/test", + ".t2": "text/test; charset=utf-8", + ".png": "image/png", + } +} diff --git a/libgo/go/mime/type_windows.go b/libgo/go/mime/type_windows.go new file mode 100644 index 00000000000..7cf2d3984b1 --- /dev/null +++ b/libgo/go/mime/type_windows.go @@ -0,0 +1,61 @@ +// Copyright 2010 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 mime + +import ( + "syscall" + "unsafe" +) + +func initMime() { + var root syscall.Handle + if syscall.RegOpenKeyEx(syscall.HKEY_CLASSES_ROOT, syscall.StringToUTF16Ptr(`\`), + 0, syscall.KEY_READ, &root) != 0 { + return + } + defer syscall.RegCloseKey(root) + var count uint32 + if syscall.RegQueryInfoKey(root, nil, nil, nil, &count, nil, nil, nil, nil, nil, nil, nil) != 0 { + return + } + var buf [1 << 10]uint16 + for i := uint32(0); i < count; i++ { + n := uint32(len(buf)) + if syscall.RegEnumKeyEx(root, i, &buf[0], &n, nil, nil, nil, nil) != 0 { + continue + } + ext := syscall.UTF16ToString(buf[:]) + if len(ext) < 2 || ext[0] != '.' { // looking for extensions only + continue + } + var h syscall.Handle + if syscall.RegOpenKeyEx( + syscall.HKEY_CLASSES_ROOT, syscall.StringToUTF16Ptr(`\`+ext), + 0, syscall.KEY_READ, &h) != 0 { + continue + } + var typ uint32 + n = uint32(len(buf) * 2) // api expects array of bytes, not uint16 + if syscall.RegQueryValueEx( + h, syscall.StringToUTF16Ptr("Content Type"), + nil, &typ, (*byte)(unsafe.Pointer(&buf[0])), &n) != 0 { + syscall.RegCloseKey(h) + continue + } + syscall.RegCloseKey(h) + if typ != syscall.REG_SZ { // null terminated strings only + continue + } + mimeType := syscall.UTF16ToString(buf[:]) + setExtensionType(ext, mimeType) + } +} + +func initMimeForTests() map[string]string { + return map[string]string{ + ".bmp": "image/bmp", + ".png": "image/png", + } +} diff --git a/libgo/go/net/cgo_unix.go b/libgo/go/net/cgo_unix.go index 680d3e70b11..1a0f4063c5a 100644 --- a/libgo/go/net/cgo_unix.go +++ b/libgo/go/net/cgo_unix.go @@ -109,7 +109,7 @@ func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, complet if gerrno == syscall.EAI_NONAME { str = noSuchHost } else if gerrno == syscall.EAI_SYSTEM { - str = syscall.Errstr(syscall.GetErrno()) + str = syscall.GetErrno().Error() } else { str = bytePtrToString(libc_gai_strerror(gerrno)) } diff --git a/libgo/go/net/dnsclient_unix.go b/libgo/go/net/dnsclient_unix.go index bab5f2a9b6e..79a958e3cd0 100644 --- a/libgo/go/net/dnsclient_unix.go +++ b/libgo/go/net/dnsclient_unix.go @@ -29,7 +29,7 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error return nil, &DNSError{Err: "name too long", Name: name} } out := new(dnsMsg) - out.id = uint16(rand.Int()) ^ uint16(time.Nanoseconds()) + out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano()) out.question = []dnsQuestion{ {name, qtype, dnsClassINET}, } diff --git a/libgo/go/net/fd.go b/libgo/go/net/fd.go index 025075de48f..5318c51c9a2 100644 --- a/libgo/go/net/fd.go +++ b/libgo/go/net/fd.go @@ -171,7 +171,7 @@ func (s *pollServer) WakeFD(fd *netFD, mode int) { } func (s *pollServer) Now() int64 { - return time.Nanoseconds() + return time.Now().UnixNano() } func (s *pollServer) CheckDeadlines() { @@ -278,8 +278,8 @@ func startServer() { func newFD(fd, family, proto int, net string) (f *netFD, err error) { onceStartServer.Do(startServer) - if e := syscall.SetNonblock(fd, true); e != 0 { - return nil, os.Errno(e) + if e := syscall.SetNonblock(fd, true); e != nil { + return nil, e } f = &netFD{ sysfd: fd, @@ -306,19 +306,19 @@ func (fd *netFD) setAddr(laddr, raddr Addr) { } func (fd *netFD) connect(ra syscall.Sockaddr) (err error) { - e := syscall.Connect(fd.sysfd, ra) - if e == syscall.EINPROGRESS { - var errno int + err = syscall.Connect(fd.sysfd, ra) + if err == syscall.EINPROGRESS { pollserver.WaitWrite(fd) - e, errno = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR) - if errno != 0 { - return os.NewSyscallError("getsockopt", errno) + var e int + e, err = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR) + if err != nil { + return os.NewSyscallError("getsockopt", err) + } + if e != 0 { + err = syscall.Errno(e) } } - if e != 0 { - return os.Errno(e) - } - return nil + return err } // Add a reference to this fd. @@ -362,9 +362,9 @@ func (fd *netFD) shutdown(how int) error { if fd == nil || fd.sysfile == nil { return os.EINVAL } - errno := syscall.Shutdown(fd.sysfd, how) - if errno != 0 { - return &OpError{"shutdown", fd.net, fd.laddr, os.Errno(errno)} + err := syscall.Shutdown(fd.sysfd, how) + if err != nil { + return &OpError{"shutdown", fd.net, fd.laddr, err} } return nil } @@ -377,6 +377,14 @@ func (fd *netFD) CloseWrite() error { return fd.shutdown(syscall.SHUT_WR) } +type timeoutError struct{} + +func (e *timeoutError) Error() string { return "i/o timeout" } +func (e *timeoutError) Timeout() bool { return true } +func (e *timeoutError) Temporary() bool { return true } + +var errTimeout error = &timeoutError{} + func (fd *netFD) Read(p []byte) (n int, err error) { if fd == nil { return 0, os.EINVAL @@ -393,24 +401,24 @@ func (fd *netFD) Read(p []byte) (n int, err error) { } else { fd.rdeadline = 0 } - var oserr error for { - var errno int - n, errno = syscall.Read(fd.sysfile.Fd(), p) - if errno == syscall.EAGAIN && fd.rdeadline >= 0 { - pollserver.WaitRead(fd) - continue + n, err = syscall.Read(fd.sysfile.Fd(), p) + if err == syscall.EAGAIN { + if fd.rdeadline >= 0 { + pollserver.WaitRead(fd) + continue + } + err = errTimeout } - if errno != 0 { + if err != nil { n = 0 - oserr = os.Errno(errno) - } else if n == 0 && errno == 0 && fd.proto != syscall.SOCK_DGRAM { + } else if n == 0 && err == nil && fd.proto != syscall.SOCK_DGRAM { err = io.EOF } break } - if oserr != nil { - err = &OpError{"read", fd.net, fd.raddr, oserr} + if err != nil && err != io.EOF { + err = &OpError{"read", fd.net, fd.raddr, err} } return } @@ -428,22 +436,22 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { } else { fd.rdeadline = 0 } - var oserr error for { - var errno int - n, sa, errno = syscall.Recvfrom(fd.sysfd, p, 0) - if errno == syscall.EAGAIN && fd.rdeadline >= 0 { - pollserver.WaitRead(fd) - continue + n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0) + if err == syscall.EAGAIN { + if fd.rdeadline >= 0 { + pollserver.WaitRead(fd) + continue + } + err = errTimeout } - if errno != 0 { + if err != nil { n = 0 - oserr = os.Errno(errno) } break } - if oserr != nil { - err = &OpError{"read", fd.net, fd.laddr, oserr} + if err != nil { + err = &OpError{"read", fd.net, fd.laddr, err} } return } @@ -461,24 +469,22 @@ func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S } else { fd.rdeadline = 0 } - var oserr error for { - var errno int - n, oobn, flags, sa, errno = syscall.Recvmsg(fd.sysfd, p, oob, 0) - if errno == syscall.EAGAIN && fd.rdeadline >= 0 { - pollserver.WaitRead(fd) - continue - } - if errno != 0 { - oserr = os.Errno(errno) + n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0) + if err == syscall.EAGAIN { + if fd.rdeadline >= 0 { + pollserver.WaitRead(fd) + continue + } + err = errTimeout } - if n == 0 { - oserr = io.EOF + if err == nil && n == 0 { + err = io.EOF } break } - if oserr != nil { - err = &OpError{"read", fd.net, fd.laddr, oserr} + if err != nil && err != io.EOF { + err = &OpError{"read", fd.net, fd.laddr, err} return } return @@ -501,32 +507,34 @@ func (fd *netFD) Write(p []byte) (n int, err error) { fd.wdeadline = 0 } nn := 0 - var oserr error for { - n, errno := syscall.Write(fd.sysfile.Fd(), p[nn:]) + var n int + n, err = syscall.Write(fd.sysfile.Fd(), p[nn:]) if n > 0 { nn += n } if nn == len(p) { break } - if errno == syscall.EAGAIN && fd.wdeadline >= 0 { - pollserver.WaitWrite(fd) - continue + if err == syscall.EAGAIN { + if fd.wdeadline >= 0 { + pollserver.WaitWrite(fd) + continue + } + err = errTimeout } - if errno != 0 { + if err != nil { n = 0 - oserr = os.Errno(errno) break } if n == 0 { - oserr = io.ErrUnexpectedEOF + err = io.ErrUnexpectedEOF break } } - if oserr != nil { - err = &OpError{"write", fd.net, fd.raddr, oserr} + if err != nil { + err = &OpError{"write", fd.net, fd.raddr, err} } return nn, err } @@ -544,22 +552,21 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) { } else { fd.wdeadline = 0 } - var oserr error for { - errno := syscall.Sendto(fd.sysfd, p, 0, sa) - if errno == syscall.EAGAIN && fd.wdeadline >= 0 { - pollserver.WaitWrite(fd) - continue - } - if errno != 0 { - oserr = os.Errno(errno) + err = syscall.Sendto(fd.sysfd, p, 0, sa) + if err == syscall.EAGAIN { + if fd.wdeadline >= 0 { + pollserver.WaitWrite(fd) + continue + } + err = errTimeout } break } - if oserr == nil { + if err == nil { n = len(p) } else { - err = &OpError{"write", fd.net, fd.raddr, oserr} + err = &OpError{"write", fd.net, fd.raddr, err} } return } @@ -577,24 +584,22 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob } else { fd.wdeadline = 0 } - var oserr error for { - var errno int - errno = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0) - if errno == syscall.EAGAIN && fd.wdeadline >= 0 { - pollserver.WaitWrite(fd) - continue - } - if errno != 0 { - oserr = os.Errno(errno) + err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0) + if err == syscall.EAGAIN { + if fd.wdeadline >= 0 { + pollserver.WaitWrite(fd) + continue + } + err = errTimeout } break } - if oserr == nil { + if err == nil { n = len(p) oobn = len(oob) } else { - err = &OpError{"write", fd.net, fd.raddr, oserr} + err = &OpError{"write", fd.net, fd.raddr, err} } return } @@ -615,25 +620,26 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err // See ../syscall/exec.go for description of ForkLock. // It is okay to hold the lock across syscall.Accept // because we have put fd.sysfd into non-blocking mode. - syscall.ForkLock.RLock() - var s, e int + var s int var rsa syscall.Sockaddr for { if fd.closing { - syscall.ForkLock.RUnlock() return nil, os.EINVAL } - s, rsa, e = syscall.Accept(fd.sysfd) - if e != syscall.EAGAIN || fd.rdeadline < 0 { - break - } - syscall.ForkLock.RUnlock() - pollserver.WaitRead(fd) syscall.ForkLock.RLock() - } - if e != 0 { - syscall.ForkLock.RUnlock() - return nil, &OpError{"accept", fd.net, fd.laddr, os.Errno(e)} + s, rsa, err = syscall.Accept(fd.sysfd) + if err != nil { + syscall.ForkLock.RUnlock() + if err == syscall.EAGAIN { + if fd.rdeadline >= 0 { + pollserver.WaitRead(fd) + continue + } + err = errTimeout + } + return nil, &OpError{"accept", fd.net, fd.laddr, err} + } + break } syscall.CloseOnExec(s) syscall.ForkLock.RUnlock() @@ -648,19 +654,19 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err } func (fd *netFD) dup() (f *os.File, err error) { - ns, e := syscall.Dup(fd.sysfd) - if e != 0 { - return nil, &OpError{"dup", fd.net, fd.laddr, os.Errno(e)} + ns, err := syscall.Dup(fd.sysfd) + if err != nil { + return nil, &OpError{"dup", fd.net, fd.laddr, err} } // We want blocking mode for the new fd, hence the double negative. - if e = syscall.SetNonblock(ns, false); e != 0 { - return nil, &OpError{"setnonblock", fd.net, fd.laddr, os.Errno(e)} + if err = syscall.SetNonblock(ns, false); err != nil { + return nil, &OpError{"setnonblock", fd.net, fd.laddr, err} } return os.NewFile(ns, fd.sysfile.Name()), nil } -func closesocket(s int) (errno int) { +func closesocket(s int) error { return syscall.Close(s) } diff --git a/libgo/go/net/fd_linux.go b/libgo/go/net/fd_linux.go index cce74cd6760..8e07833882e 100644 --- a/libgo/go/net/fd_linux.go +++ b/libgo/go/net/fd_linux.go @@ -35,12 +35,12 @@ type pollster struct { func newpollster() (p *pollster, err error) { p = new(pollster) - var e int + var e error // The arg to epoll_create is a hint to the kernel // about the number of FDs we will care about. // We don't know, and since 2.6.8 the kernel ignores it anyhow. - if p.epfd, e = syscall.EpollCreate(16); e != 0 { + if p.epfd, e = syscall.EpollCreate(16); e != nil { return nil, os.NewSyscallError("epoll_create", e) } p.events = make(map[int]uint32) @@ -68,7 +68,7 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) { } else { op = syscall.EPOLL_CTL_ADD } - if e := syscall.EpollCtl(p.epfd, op, fd, &p.ctlEvent); e != 0 { + if e := syscall.EpollCtl(p.epfd, op, fd, &p.ctlEvent); e != nil { return false, os.NewSyscallError("epoll_ctl", e) } p.events[fd] = p.ctlEvent.Events @@ -97,13 +97,13 @@ func (p *pollster) StopWaiting(fd int, bits uint) { if int32(events)&^syscall.EPOLLONESHOT != 0 { p.ctlEvent.Fd = int32(fd) p.ctlEvent.Events = events - if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &p.ctlEvent); e != 0 { - print("Epoll modify fd=", fd, ": ", os.Errno(e).Error(), "\n") + if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &p.ctlEvent); e != nil { + print("Epoll modify fd=", fd, ": ", e.Error(), "\n") } p.events[fd] = events } else { - if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_DEL, fd, nil); e != 0 { - print("Epoll delete fd=", fd, ": ", os.Errno(e).Error(), "\n") + if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_DEL, fd, nil); e != nil { + print("Epoll delete fd=", fd, ": ", e.Error(), "\n") } delete(p.events, fd) } @@ -141,7 +141,7 @@ func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err erro n, e := syscall.EpollWait(p.epfd, p.waitEventBuf[0:], msec) s.Lock() - if e != 0 { + if e != nil { if e == syscall.EAGAIN || e == syscall.EINTR { continue } diff --git a/libgo/go/net/fd_openbsd.go b/libgo/go/net/fd_openbsd.go index f61008a2fe1..e52ac356b9f 100644 --- a/libgo/go/net/fd_openbsd.go +++ b/libgo/go/net/fd_openbsd.go @@ -23,9 +23,8 @@ type pollster struct { func newpollster() (p *pollster, err error) { p = new(pollster) - var e int - if p.kq, e = syscall.Kqueue(); e != 0 { - return nil, os.NewSyscallError("kqueue", e) + if p.kq, err = syscall.Kqueue(); err != nil { + return nil, os.NewSyscallError("kqueue", err) } p.events = p.eventbuf[0:0] return p, nil @@ -50,14 +49,14 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) { syscall.SetKevent(ev, fd, kmode, flags) n, e := syscall.Kevent(p.kq, p.kbuf[:], nil, nil) - if e != 0 { + if e != nil { return false, os.NewSyscallError("kevent", e) } if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode { return false, os.NewSyscallError("kqueue phase error", e) } if ev.Data != 0 { - return false, os.Errno(int(ev.Data)) + return false, syscall.Errno(int(ev.Data)) } return false, nil } @@ -91,7 +90,7 @@ func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err erro nn, e := syscall.Kevent(p.kq, nil, p.eventbuf[:], t) s.Lock() - if e != 0 { + if e != nil { if e == syscall.EINTR { continue } diff --git a/libgo/go/net/fd_select.go b/libgo/go/net/fd_select.go index b9544e96c75..39c8f2777b2 100644 --- a/libgo/go/net/fd_select.go +++ b/libgo/go/net/fd_select.go @@ -85,7 +85,8 @@ func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err erro timeout = &tv } - var n, e int + var n int + var e error var tmpReadFds, tmpWriteFds syscall.FdSet for { // Temporary syscall.FdSet's into which the values are copied @@ -101,7 +102,7 @@ func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err erro break } } - if e != 0 { + if e != nil { return -1, 0, os.NewSyscallError("select", e) } if n == 0 { diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go index ce228e91edb..264b918c57d 100644 --- a/libgo/go/net/fd_windows.go +++ b/libgo/go/net/fd_windows.go @@ -26,11 +26,11 @@ func init() { var d syscall.WSAData e := syscall.WSAStartup(uint32(0x202), &d) if e != 0 { - initErr = os.NewSyscallError("WSAStartup", e) + initErr = os.NewSyscallError("WSAStartup", syscall.Errno(e)) } } -func closesocket(s syscall.Handle) (errno int) { +func closesocket(s syscall.Handle) (err error) { return syscall.Closesocket(s) } @@ -38,13 +38,13 @@ func closesocket(s syscall.Handle) (errno int) { type anOpIface interface { Op() *anOp Name() string - Submit() (errno int) + Submit() (err error) } // IO completion result parameters. type ioResult struct { qty uint32 - err int + err error } // anOp implements functionality common to all io operations. @@ -54,7 +54,7 @@ type anOp struct { o syscall.Overlapped resultc chan ioResult - errnoc chan int + errnoc chan error fd *netFD } @@ -71,7 +71,7 @@ func (o *anOp) Init(fd *netFD, mode int) { } o.resultc = fd.resultc[i] if fd.errnoc[i] == nil { - fd.errnoc[i] = make(chan int) + fd.errnoc[i] = make(chan error) } o.errnoc = fd.errnoc[i] } @@ -111,14 +111,14 @@ func (s *resultSrv) Run() { for { r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE) switch { - case r.err == 0: + case r.err == nil: // Dequeued successfully completed io packet. - case r.err == syscall.WAIT_TIMEOUT && o == nil: + case r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil: // Wait has timed out (should not happen now, but might be used in the future). panic("GetQueuedCompletionStatus timed out") case o == nil: // Failed to dequeue anything -> report the error. - panic("GetQueuedCompletionStatus failed " + syscall.Errstr(r.err)) + panic("GetQueuedCompletionStatus failed " + r.err.Error()) default: // Dequeued failed io packet. } @@ -153,7 +153,7 @@ func (s *ioSrv) ProcessRemoteIO() { // inline, or, if timeouts are employed, passes the request onto // a special goroutine and waits for completion or cancels request. func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) { - var e int + var e error o := oi.Op() if deadline_delta > 0 { // Send request to a special dedicated thread, @@ -164,19 +164,20 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) { e = oi.Submit() } switch e { - case 0: + case nil: // IO completed immediately, but we need to get our completion message anyway. case syscall.ERROR_IO_PENDING: // IO started, and we have to wait for its completion. default: - return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, os.Errno(e)} + return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, e} } // Wait for our request to complete. + // TODO(rsc): This should stop the timer. var r ioResult if deadline_delta > 0 { select { case r = <-o.resultc: - case <-time.After(deadline_delta): + case <-time.After(time.Duration(deadline_delta) * time.Nanosecond): s.canchan <- oi <-o.errnoc r = <-o.resultc @@ -187,8 +188,8 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) { } else { r = <-o.resultc } - if r.err != 0 { - err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, os.Errno(r.err)} + if r.err != nil { + err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, r.err} } return int(r.qty), err } @@ -200,10 +201,10 @@ var onceStartServer sync.Once func startServer() { resultsrv = new(resultSrv) - var errno int - resultsrv.iocp, errno = syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 1) - if errno != 0 { - panic("CreateIoCompletionPort failed " + syscall.Errstr(errno)) + var err error + resultsrv.iocp, err = syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 1) + if err != nil { + panic("CreateIoCompletionPort: " + err.Error()) } go resultsrv.Run() @@ -228,7 +229,7 @@ type netFD struct { laddr Addr raddr Addr resultc [2]chan ioResult // read/write completion results - errnoc [2]chan int // read/write submit or cancel operation errors + errnoc [2]chan error // read/write submit or cancel operation errors // owned by client rdeadline_delta int64 @@ -256,8 +257,8 @@ func newFD(fd syscall.Handle, family, proto int, net string) (f *netFD, err erro } onceStartServer.Do(startServer) // Associate our socket with resultsrv.iocp. - if _, e := syscall.CreateIoCompletionPort(syscall.Handle(fd), resultsrv.iocp, 0, 0); e != 0 { - return nil, os.Errno(e) + if _, e := syscall.CreateIoCompletionPort(syscall.Handle(fd), resultsrv.iocp, 0, 0); e != nil { + return nil, e } return allocFD(fd, family, proto, net), nil } @@ -268,11 +269,7 @@ func (fd *netFD) setAddr(laddr, raddr Addr) { } func (fd *netFD) connect(ra syscall.Sockaddr) (err error) { - e := syscall.Connect(fd.sysfd, ra) - if e != 0 { - return os.Errno(e) - } - return nil + return syscall.Connect(fd.sysfd, ra) } // Add a reference to this fd. @@ -317,9 +314,9 @@ func (fd *netFD) shutdown(how int) error { if fd == nil || fd.sysfd == syscall.InvalidHandle { return os.EINVAL } - errno := syscall.Shutdown(fd.sysfd, how) - if errno != 0 { - return &OpError{"shutdown", fd.net, fd.laddr, os.Errno(errno)} + err := syscall.Shutdown(fd.sysfd, how) + if err != nil { + return &OpError{"shutdown", fd.net, fd.laddr, err} } return nil } @@ -338,7 +335,7 @@ type readOp struct { bufOp } -func (o *readOp) Submit() (errno int) { +func (o *readOp) Submit() (err error) { var d, f uint32 return syscall.WSARecv(syscall.Handle(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil) } @@ -375,7 +372,7 @@ type readFromOp struct { rsan int32 } -func (o *readFromOp) Submit() (errno int) { +func (o *readFromOp) Submit() (err error) { var d, f uint32 return syscall.WSARecvFrom(o.fd.sysfd, &o.buf, 1, &d, &f, &o.rsa, &o.rsan, &o.o, nil) } @@ -415,7 +412,7 @@ type writeOp struct { bufOp } -func (o *writeOp) Submit() (errno int) { +func (o *writeOp) Submit() (err error) { var d uint32 return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &d, 0, &o.o, nil) } @@ -447,7 +444,7 @@ type writeToOp struct { sa syscall.Sockaddr } -func (o *writeToOp) Submit() (errno int) { +func (o *writeToOp) Submit() (err error) { var d uint32 return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &d, 0, o.sa, &o.o, nil) } @@ -484,7 +481,7 @@ type acceptOp struct { attrs [2]syscall.RawSockaddrAny // space for local and remote address only } -func (o *acceptOp) Submit() (errno int) { +func (o *acceptOp) Submit() (err error) { var d uint32 l := uint32(unsafe.Sizeof(o.attrs[0])) return syscall.AcceptEx(o.fd.sysfd, o.newsock, @@ -506,17 +503,17 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err // See ../syscall/exec.go for description of ForkLock. syscall.ForkLock.RLock() s, e := syscall.Socket(fd.family, fd.proto, 0) - if e != 0 { + if e != nil { syscall.ForkLock.RUnlock() - return nil, os.Errno(e) + return nil, e } syscall.CloseOnExec(s) syscall.ForkLock.RUnlock() // Associate our new socket with IOCP. onceStartServer.Do(startServer) - if _, e = syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); e != 0 { - return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, os.Errno(e)} + if _, e = syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); e != nil { + return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, e} } // Submit accept request. @@ -531,9 +528,9 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err // Inherit properties of the listening socket. e = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd))) - if e != 0 { + if e != nil { closesocket(s) - return nil, err + return nil, e } // Get local and peer addr out of AcceptEx buffer. diff --git a/libgo/go/net/file.go b/libgo/go/net/file.go index 0ad869dbd57..bf8cd9dae04 100644 --- a/libgo/go/net/file.go +++ b/libgo/go/net/file.go @@ -13,12 +13,12 @@ import ( func newFileFD(f *os.File) (nfd *netFD, err error) { fd, errno := syscall.Dup(f.Fd()) - if errno != 0 { + if errno != nil { return nil, os.NewSyscallError("dup", errno) } proto, errno := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE) - if errno != 0 { + if errno != nil { return nil, os.NewSyscallError("getsockopt", errno) } diff --git a/libgo/go/net/hosts.go b/libgo/go/net/hosts.go index d75e9e038a6..e6674ba3415 100644 --- a/libgo/go/net/hosts.go +++ b/libgo/go/net/hosts.go @@ -7,11 +7,11 @@ package net import ( - "os" "sync" + "time" ) -const cacheMaxAge = int64(300) // 5 minutes. +const cacheMaxAge = 5 * time.Minute // hostsPath points to the file with static IP/address entries. var hostsPath = "/etc/hosts" @@ -21,14 +21,14 @@ var hosts struct { sync.Mutex byName map[string][]string byAddr map[string][]string - time int64 + expire time.Time path string } func readHosts() { - now, _, _ := os.Time() + now := time.Now() hp := hostsPath - if len(hosts.byName) == 0 || hosts.time+cacheMaxAge <= now || hosts.path != hp { + if len(hosts.byName) == 0 || now.After(hosts.expire) || hosts.path != hp { hs := make(map[string][]string) is := make(map[string][]string) var file *file @@ -51,7 +51,7 @@ func readHosts() { } } // Update the data cache. - hosts.time, _, _ = os.Time() + hosts.expire = time.Now().Add(cacheMaxAge) hosts.path = hp hosts.byName = hs hosts.byAddr = is diff --git a/libgo/go/net/http/cgi/host_test.go b/libgo/go/net/http/cgi/host_test.go index 2bc913a1696..849cb008b76 100644 --- a/libgo/go/net/http/cgi/host_test.go +++ b/libgo/go/net/http/cgi/host_test.go @@ -363,14 +363,13 @@ func TestCopyError(t *testing.T) { } conn.Close() - if tries := 0; childRunning() { - for tries < 15 && childRunning() { - time.Sleep(50e6 * int64(tries)) - tries++ - } - if childRunning() { - t.Fatalf("post-conn.Close, expected child to be gone") - } + tries := 0 + for tries < 15 && childRunning() { + time.Sleep(50 * time.Millisecond * time.Duration(tries)) + tries++ + } + if childRunning() { + t.Fatalf("post-conn.Close, expected child to be gone") } } diff --git a/libgo/go/net/http/chunked.go b/libgo/go/net/http/chunked.go index b012dd18496..74c41aabd41 100644 --- a/libgo/go/net/http/chunked.go +++ b/libgo/go/net/http/chunked.go @@ -2,20 +2,137 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// The wire protocol for HTTP's "chunked" Transfer-Encoding. + +// This code is duplicated in httputil/chunked.go. +// Please make any changes in both files. + package http import ( "bufio" + "bytes" + "errors" "io" "strconv" ) +const maxLineLength = 4096 // assumed <= bufio.defaultBufSize + +var ErrLineTooLong = errors.New("header line too long") + +// newChunkedReader returns a new chunkedReader that translates the data read from r +// out of HTTP "chunked" format before returning it. +// The chunkedReader returns io.EOF when the final 0-length chunk is read. +// +// newChunkedReader is not needed by normal applications. The http package +// automatically decodes chunking when reading response bodies. +func newChunkedReader(r io.Reader) io.Reader { + br, ok := r.(*bufio.Reader) + if !ok { + br = bufio.NewReader(r) + } + return &chunkedReader{r: br} +} + +type chunkedReader struct { + r *bufio.Reader + n uint64 // unread bytes in chunk + err error +} + +func (cr *chunkedReader) beginChunk() { + // chunk-size CRLF + var line string + line, cr.err = readLine(cr.r) + if cr.err != nil { + return + } + cr.n, cr.err = strconv.Btoui64(line, 16) + if cr.err != nil { + return + } + if cr.n == 0 { + cr.err = io.EOF + } +} + +func (cr *chunkedReader) Read(b []uint8) (n int, err error) { + if cr.err != nil { + return 0, cr.err + } + if cr.n == 0 { + cr.beginChunk() + if cr.err != nil { + return 0, cr.err + } + } + if uint64(len(b)) > cr.n { + b = b[0:cr.n] + } + n, cr.err = cr.r.Read(b) + cr.n -= uint64(n) + if cr.n == 0 && cr.err == nil { + // end of chunk (CRLF) + b := make([]byte, 2) + if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil { + if b[0] != '\r' || b[1] != '\n' { + cr.err = errors.New("malformed chunked encoding") + } + } + } + return n, cr.err +} + +// Read a line of bytes (up to \n) from b. +// Give up if the line exceeds maxLineLength. +// The returned bytes are a pointer into storage in +// the bufio, so they are only valid until the next bufio read. +func readLineBytes(b *bufio.Reader) (p []byte, err error) { + if p, err = b.ReadSlice('\n'); err != nil { + // We always know when EOF is coming. + // If the caller asked for a line, there should be a line. + if err == io.EOF { + err = io.ErrUnexpectedEOF + } else if err == bufio.ErrBufferFull { + err = ErrLineTooLong + } + return nil, err + } + if len(p) >= maxLineLength { + return nil, ErrLineTooLong + } + + // Chop off trailing white space. + p = bytes.TrimRight(p, " \r\t\n") + + return p, nil +} + +// readLineBytes, but convert the bytes into a string. +func readLine(b *bufio.Reader) (s string, err error) { + p, e := readLineBytes(b) + if e != nil { + return "", e + } + return string(p), nil +} + +// newChunkedWriter returns a new chunkedWriter that translates writes into HTTP +// "chunked" format before writing them to w. Closing the returned chunkedWriter +// sends the final 0-length chunk that marks the end of the stream. +// +// newChunkedWriter is not needed by normal applications. The http +// package adds chunking automatically if handlers don't set a +// Content-Length header. Using newChunkedWriter inside a handler +// would result in double chunking or chunking with a Content-Length +// length, both of which are wrong. func newChunkedWriter(w io.Writer) io.WriteCloser { return &chunkedWriter{w} } -// Writing to ChunkedWriter translates to writing in HTTP chunked Transfer -// Encoding wire format to the underlying Wire writer. +// Writing to chunkedWriter translates to writing in HTTP chunked Transfer +// Encoding wire format to the underlying Wire chunkedWriter. type chunkedWriter struct { Wire io.Writer } @@ -51,7 +168,3 @@ func (cw *chunkedWriter) Close() error { _, err := io.WriteString(cw.Wire, "0\r\n") return err } - -func newChunkedReader(r *bufio.Reader) io.Reader { - return &chunkedReader{r: r} -} diff --git a/libgo/go/net/http/chunked_test.go b/libgo/go/net/http/chunked_test.go new file mode 100644 index 00000000000..b77ee2ff26c --- /dev/null +++ b/libgo/go/net/http/chunked_test.go @@ -0,0 +1,39 @@ +// 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 code is duplicated in httputil/chunked_test.go. +// Please make any changes in both files. + +package http + +import ( + "bytes" + "io/ioutil" + "testing" +) + +func TestChunk(t *testing.T) { + var b bytes.Buffer + + w := newChunkedWriter(&b) + const chunk1 = "hello, " + const chunk2 = "world! 0123456789abcdef" + w.Write([]byte(chunk1)) + w.Write([]byte(chunk2)) + w.Close() + + if g, e := b.String(), "7\r\nhello, \r\n17\r\nworld! 0123456789abcdef\r\n0\r\n"; g != e { + t.Fatalf("chunk writer wrote %q; want %q", g, e) + } + + r := newChunkedReader(&b) + data, err := ioutil.ReadAll(r) + if err != nil { + t.Logf(`data: "%s"`, data) + t.Fatalf("ReadAll from reader: %v", err) + } + if g, e := string(data), chunk1+chunk2; g != e { + t.Errorf("chunk reader read %q; want %q", g, e) + } +} diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go index d224380298c..57a9dd9574d 100644 --- a/libgo/go/net/http/client_test.go +++ b/libgo/go/net/http/client_test.go @@ -26,6 +26,31 @@ var robotsTxtHandler = HandlerFunc(func(w ResponseWriter, r *Request) { fmt.Fprintf(w, "User-agent: go\nDisallow: /something/") }) +// pedanticReadAll works like ioutil.ReadAll but additionally +// verifies that r obeys the documented io.Reader contract. +func pedanticReadAll(r io.Reader) (b []byte, err error) { + var bufa [64]byte + buf := bufa[:] + for { + n, err := r.Read(buf) + if n == 0 && err == nil { + return nil, fmt.Errorf("Read: n=0 with err=nil") + } + b = append(b, buf[:n]...) + if err == io.EOF { + n, err := r.Read(buf) + if n != 0 || err != io.EOF { + return nil, fmt.Errorf("Read: n=%d err=%#v after EOF", n, err) + } + return b, nil + } + if err != nil { + return b, err + } + } + panic("unreachable") +} + func TestClient(t *testing.T) { ts := httptest.NewServer(robotsTxtHandler) defer ts.Close() @@ -33,7 +58,7 @@ func TestClient(t *testing.T) { r, err := Get(ts.URL) var b []byte if err == nil { - b, err = ioutil.ReadAll(r.Body) + b, err = pedanticReadAll(r.Body) r.Body.Close() } if err != nil { diff --git a/libgo/go/net/http/cookie.go b/libgo/go/net/http/cookie.go index 69350143248..cad852242e2 100644 --- a/libgo/go/net/http/cookie.go +++ b/libgo/go/net/http/cookie.go @@ -115,7 +115,7 @@ func readSetCookies(h Header) []*Cookie { break } } - c.Expires = *exptime + c.Expires = exptime.UTC() continue case "path": c.Path = val @@ -146,8 +146,8 @@ func (c *Cookie) String() string { if len(c.Domain) > 0 { fmt.Fprintf(&b, "; Domain=%s", sanitizeValue(c.Domain)) } - if len(c.Expires.Zone) > 0 { - fmt.Fprintf(&b, "; Expires=%s", c.Expires.Format(time.RFC1123)) + if c.Expires.Unix() > 0 { + fmt.Fprintf(&b, "; Expires=%s", c.Expires.UTC().Format(time.RFC1123)) } if c.MaxAge > 0 { fmt.Fprintf(&b, "; Max-Age=%d", c.MaxAge) diff --git a/libgo/go/net/http/cookie_test.go b/libgo/go/net/http/cookie_test.go index 24adf202981..26bff93f643 100644 --- a/libgo/go/net/http/cookie_test.go +++ b/libgo/go/net/http/cookie_test.go @@ -123,7 +123,7 @@ var readSetCookiesTests = []struct { Path: "/", Domain: ".google.ch", HttpOnly: true, - Expires: time.Time{Year: 2011, Month: 11, Day: 23, Hour: 1, Minute: 5, Second: 3, ZoneOffset: 0, Zone: "GMT"}, + Expires: time.Date(2011, 11, 23, 1, 5, 3, 0, time.UTC), RawExpires: "Wed, 23-Nov-2011 01:05:03 GMT", Raw: "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly", }}, diff --git a/libgo/go/net/http/export_test.go b/libgo/go/net/http/export_test.go index 3fe658641f8..13640ca85ee 100644 --- a/libgo/go/net/http/export_test.go +++ b/libgo/go/net/http/export_test.go @@ -7,6 +7,8 @@ package http +import "time" + func (t *Transport) IdleConnKeysForTesting() (keys []string) { keys = make([]string, 0) t.lk.Lock() @@ -33,8 +35,8 @@ func (t *Transport) IdleConnCountForTesting(cacheKey string) int { return len(conns) } -func NewTestTimeoutHandler(handler Handler, ch <-chan int64) Handler { - f := func() <-chan int64 { +func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler { + f := func() <-chan time.Time { return ch } return &timeoutHandler{handler, f, ""} diff --git a/libgo/go/net/http/fcgi/child.go b/libgo/go/net/http/fcgi/child.go index 7b563951ccf..c94b9a7b249 100644 --- a/libgo/go/net/http/fcgi/child.go +++ b/libgo/go/net/http/fcgi/child.go @@ -7,6 +7,7 @@ package fcgi // This file implements FastCGI from the perspective of a child process. import ( + "errors" "fmt" "io" "net" @@ -102,7 +103,7 @@ func (r *response) WriteHeader(code int) { } if r.header.Get("Date") == "" { - r.header.Set("Date", time.UTC().Format(http.TimeFormat)) + r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat)) } fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code)) @@ -123,89 +124,101 @@ func (r *response) Close() error { } type child struct { - conn *conn - handler http.Handler + conn *conn + handler http.Handler + requests map[uint16]*request // keyed by request ID } -func newChild(rwc net.Conn, handler http.Handler) *child { - return &child{newConn(rwc), handler} +func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child { + return &child{ + conn: newConn(rwc), + handler: handler, + requests: make(map[uint16]*request), + } } func (c *child) serve() { - requests := map[uint16]*request{} defer c.conn.Close() var rec record - var br beginRequest for { if err := rec.read(c.conn.rwc); err != nil { return } - - req, ok := requests[rec.h.Id] - if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues { - // The spec says to ignore unknown request IDs. - continue - } - if ok && rec.h.Type == typeBeginRequest { - // The server is trying to begin a request with the same ID - // as an in-progress request. This is an error. + if err := c.handleRecord(&rec); err != nil { return } + } +} - switch rec.h.Type { - case typeBeginRequest: - if err := br.read(rec.content()); err != nil { - return - } - if br.role != roleResponder { - c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole) - break - } - requests[rec.h.Id] = newRequest(rec.h.Id, br.flags) - case typeParams: - // NOTE(eds): Technically a key-value pair can straddle the boundary - // between two packets. We buffer until we've received all parameters. - if len(rec.content()) > 0 { - req.rawParams = append(req.rawParams, rec.content()...) - break - } - req.parseParams() - case typeStdin: - content := rec.content() - if req.pw == nil { - var body io.ReadCloser - if len(content) > 0 { - // body could be an io.LimitReader, but it shouldn't matter - // as long as both sides are behaving. - body, req.pw = io.Pipe() - } - go c.serveRequest(req, body) - } +var errCloseConn = errors.New("fcgi: connection should be closed") + +func (c *child) handleRecord(rec *record) error { + req, ok := c.requests[rec.h.Id] + if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues { + // The spec says to ignore unknown request IDs. + return nil + } + if ok && rec.h.Type == typeBeginRequest { + // The server is trying to begin a request with the same ID + // as an in-progress request. This is an error. + return errors.New("fcgi: received ID that is already in-flight") + } + + switch rec.h.Type { + case typeBeginRequest: + var br beginRequest + if err := br.read(rec.content()); err != nil { + return err + } + if br.role != roleResponder { + c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole) + return nil + } + c.requests[rec.h.Id] = newRequest(rec.h.Id, br.flags) + case typeParams: + // NOTE(eds): Technically a key-value pair can straddle the boundary + // between two packets. We buffer until we've received all parameters. + if len(rec.content()) > 0 { + req.rawParams = append(req.rawParams, rec.content()...) + return nil + } + req.parseParams() + case typeStdin: + content := rec.content() + if req.pw == nil { + var body io.ReadCloser if len(content) > 0 { - // TODO(eds): This blocks until the handler reads from the pipe. - // If the handler takes a long time, it might be a problem. - req.pw.Write(content) - } else if req.pw != nil { - req.pw.Close() - } - case typeGetValues: - values := map[string]string{"FCGI_MPXS_CONNS": "1"} - c.conn.writePairs(0, typeGetValuesResult, values) - case typeData: - // If the filter role is implemented, read the data stream here. - case typeAbortRequest: - delete(requests, rec.h.Id) - c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete) - if !req.keepConn { - // connection will close upon return - return + // body could be an io.LimitReader, but it shouldn't matter + // as long as both sides are behaving. + body, req.pw = io.Pipe() } - default: - b := make([]byte, 8) - b[0] = rec.h.Type - c.conn.writeRecord(typeUnknownType, 0, b) + go c.serveRequest(req, body) + } + if len(content) > 0 { + // TODO(eds): This blocks until the handler reads from the pipe. + // If the handler takes a long time, it might be a problem. + req.pw.Write(content) + } else if req.pw != nil { + req.pw.Close() + } + case typeGetValues: + values := map[string]string{"FCGI_MPXS_CONNS": "1"} + c.conn.writePairs(typeGetValuesResult, 0, values) + case typeData: + // If the filter role is implemented, read the data stream here. + case typeAbortRequest: + delete(c.requests, rec.h.Id) + c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete) + if !req.keepConn { + // connection will close upon return + return errCloseConn } + default: + b := make([]byte, 8) + b[0] = byte(rec.h.Type) + c.conn.writeRecord(typeUnknownType, 0, b) } + return nil } func (c *child) serveRequest(req *request, body io.ReadCloser) { diff --git a/libgo/go/net/http/fcgi/fcgi.go b/libgo/go/net/http/fcgi/fcgi.go index 70cf781e228..d35aa84d229 100644 --- a/libgo/go/net/http/fcgi/fcgi.go +++ b/libgo/go/net/http/fcgi/fcgi.go @@ -19,19 +19,22 @@ import ( "sync" ) +// recType is a record type, as defined by +// http://www.fastcgi.com/devkit/doc/fcgi-spec.html#S8 +type recType uint8 + const ( - // Packet Types - typeBeginRequest = iota + 1 - typeAbortRequest - typeEndRequest - typeParams - typeStdin - typeStdout - typeStderr - typeData - typeGetValues - typeGetValuesResult - typeUnknownType + typeBeginRequest recType = 1 + typeAbortRequest recType = 2 + typeEndRequest recType = 3 + typeParams recType = 4 + typeStdin recType = 5 + typeStdout recType = 6 + typeStderr recType = 7 + typeData recType = 8 + typeGetValues recType = 9 + typeGetValuesResult recType = 10 + typeUnknownType recType = 11 ) // keep the connection between web-server and responder open after request @@ -59,7 +62,7 @@ const headerLen = 8 type header struct { Version uint8 - Type uint8 + Type recType Id uint16 ContentLength uint16 PaddingLength uint8 @@ -85,7 +88,7 @@ func (br *beginRequest) read(content []byte) error { // not synchronized because we don't care what the contents are var pad [maxPad]byte -func (h *header) init(recType uint8, reqId uint16, contentLength int) { +func (h *header) init(recType recType, reqId uint16, contentLength int) { h.Version = 1 h.Type = recType h.Id = reqId @@ -137,7 +140,7 @@ func (r *record) content() []byte { } // writeRecord writes and sends a single record. -func (c *conn) writeRecord(recType uint8, reqId uint16, b []byte) error { +func (c *conn) writeRecord(recType recType, reqId uint16, b []byte) error { c.mutex.Lock() defer c.mutex.Unlock() c.buf.Reset() @@ -167,12 +170,12 @@ func (c *conn) writeEndRequest(reqId uint16, appStatus int, protocolStatus uint8 return c.writeRecord(typeEndRequest, reqId, b) } -func (c *conn) writePairs(recType uint8, reqId uint16, pairs map[string]string) error { +func (c *conn) writePairs(recType recType, reqId uint16, pairs map[string]string) error { w := newWriter(c, recType, reqId) b := make([]byte, 8) for k, v := range pairs { n := encodeSize(b, uint32(len(k))) - n += encodeSize(b[n:], uint32(len(k))) + n += encodeSize(b[n:], uint32(len(v))) if _, err := w.Write(b[:n]); err != nil { return err } @@ -235,7 +238,7 @@ func (w *bufWriter) Close() error { return w.closer.Close() } -func newWriter(c *conn, recType uint8, reqId uint16) *bufWriter { +func newWriter(c *conn, recType recType, reqId uint16) *bufWriter { s := &streamWriter{c: c, recType: recType, reqId: reqId} w, _ := bufio.NewWriterSize(s, maxWrite) return &bufWriter{s, w} @@ -245,7 +248,7 @@ func newWriter(c *conn, recType uint8, reqId uint16) *bufWriter { // It only writes maxWrite bytes at a time. type streamWriter struct { c *conn - recType uint8 + recType recType reqId uint16 } diff --git a/libgo/go/net/http/fcgi/fcgi_test.go b/libgo/go/net/http/fcgi/fcgi_test.go index e42f8efd658..6c7e1a9ce83 100644 --- a/libgo/go/net/http/fcgi/fcgi_test.go +++ b/libgo/go/net/http/fcgi/fcgi_test.go @@ -6,6 +6,7 @@ package fcgi import ( "bytes" + "errors" "io" "testing" ) @@ -40,25 +41,25 @@ func TestSize(t *testing.T) { var streamTests = []struct { desc string - recType uint8 + recType recType reqId uint16 content []byte raw []byte }{ {"single record", typeStdout, 1, nil, - []byte{1, typeStdout, 0, 1, 0, 0, 0, 0}, + []byte{1, byte(typeStdout), 0, 1, 0, 0, 0, 0}, }, // this data will have to be split into two records {"two records", typeStdin, 300, make([]byte, 66000), bytes.Join([][]byte{ // header for the first record - {1, typeStdin, 0x01, 0x2C, 0xFF, 0xFF, 1, 0}, + {1, byte(typeStdin), 0x01, 0x2C, 0xFF, 0xFF, 1, 0}, make([]byte, 65536), // header for the second - {1, typeStdin, 0x01, 0x2C, 0x01, 0xD1, 7, 0}, + {1, byte(typeStdin), 0x01, 0x2C, 0x01, 0xD1, 7, 0}, make([]byte, 472), // header for the empty record - {1, typeStdin, 0x01, 0x2C, 0, 0, 0, 0}, + {1, byte(typeStdin), 0x01, 0x2C, 0, 0, 0, 0}, }, nil), }, @@ -111,3 +112,39 @@ outer: } } } + +type writeOnlyConn struct { + buf []byte +} + +func (c *writeOnlyConn) Write(p []byte) (int, error) { + c.buf = append(c.buf, p...) + return len(p), nil +} + +func (c *writeOnlyConn) Read(p []byte) (int, error) { + return 0, errors.New("conn is write-only") +} + +func (c *writeOnlyConn) Close() error { + return nil +} + +func TestGetValues(t *testing.T) { + var rec record + rec.h.Type = typeGetValues + + wc := new(writeOnlyConn) + c := newChild(wc, nil) + err := c.handleRecord(&rec) + if err != nil { + t.Fatalf("handleRecord: %v", err) + } + + const want = "\x01\n\x00\x00\x00\x12\x06\x00" + + "\x0f\x01FCGI_MPXS_CONNS1" + + "\x00\x00\x00\x00\x00\x00\x01\n\x00\x00\x00\x00\x00\x00" + if got := string(wc.buf); got != want { + t.Errorf(" got: %q\nwant: %q\n", got, want) + } +} diff --git a/libgo/go/net/http/filetransport_test.go b/libgo/go/net/http/filetransport_test.go index 265a3b903e9..039926b5382 100644 --- a/libgo/go/net/http/filetransport_test.go +++ b/libgo/go/net/http/filetransport_test.go @@ -7,6 +7,7 @@ package http_test import ( "io/ioutil" "net/http" + "os" "path/filepath" "testing" ) @@ -28,6 +29,8 @@ func TestFileTransport(t *testing.T) { fname := filepath.Join(dname, "foo.txt") err = ioutil.WriteFile(fname, []byte("Bar"), 0644) check("WriteFile", err) + defer os.Remove(dname) + defer os.Remove(fname) tr := &http.Transport{} tr.RegisterProtocol("file", http.NewFileTransport(http.Dir(dname))) diff --git a/libgo/go/net/http/fs.go b/libgo/go/net/http/fs.go index 5f91ff5cbf6..70e7849f167 100644 --- a/libgo/go/net/http/fs.go +++ b/libgo/go/net/http/fs.go @@ -22,13 +22,19 @@ import ( // A Dir implements http.FileSystem using the native file // system restricted to a specific directory tree. +// +// An empty Dir is treated as ".". type Dir string func (d Dir) Open(name string) (File, error) { if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 { return nil, errors.New("http: invalid character in file path") } - f, err := os.Open(filepath.Join(string(d), filepath.FromSlash(path.Clean("/"+name)))) + dir := string(d) + if dir == "" { + dir = "." + } + f, err := os.Open(filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))) if err != nil { return nil, err } @@ -46,7 +52,7 @@ type FileSystem interface { // served by the FileServer implementation. type File interface { Close() error - Stat() (*os.FileInfo, error) + Stat() (os.FileInfo, error) Readdir(count int) ([]os.FileInfo, error) Read([]byte) (int, error) Seek(offset int64, whence int) (int64, error) @@ -87,8 +93,8 @@ func dirList(w ResponseWriter, f File) { break } for _, d := range dirs { - name := d.Name - if d.IsDirectory() { + name := d.Name() + if d.IsDir() { name += "/" } // TODO htmlescape @@ -129,7 +135,7 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec // redirect to canonical path: / at end of directory url // r.URL.Path always begins with / url := r.URL.Path - if d.IsDirectory() { + if d.IsDir() { if url[len(url)-1] != '/' { localRedirect(w, r, path.Base(url)+"/") return @@ -142,14 +148,14 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec } } - if t, _ := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); t != nil && d.Mtime_ns/1e9 <= t.Seconds() { + if t, err := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && !d.ModTime().After(t) { w.WriteHeader(StatusNotModified) return } - w.Header().Set("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format(TimeFormat)) + w.Header().Set("Last-Modified", d.ModTime().UTC().Format(TimeFormat)) // use contents of index.html for directory, if present - if d.IsDirectory() { + if d.IsDir() { index := name + indexPage ff, err := fs.Open(index) if err == nil { @@ -163,13 +169,13 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec } } - if d.IsDirectory() { + if d.IsDir() { dirList(w, f) return } // serve file - size := d.Size + size := d.Size() code := StatusOK // If Content-Type isn't set, use the file's extension to find it. @@ -209,7 +215,7 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec } size = ra.length code = StatusPartialContent - w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, d.Size)) + w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, d.Size())) } w.Header().Set("Accept-Ranges", "bytes") diff --git a/libgo/go/net/http/fs_test.go b/libgo/go/net/http/fs_test.go index e1a784c1f6d..976ee75c7dd 100644 --- a/libgo/go/net/http/fs_test.go +++ b/libgo/go/net/http/fs_test.go @@ -190,8 +190,8 @@ func TestDirJoin(t *testing.T) { if err != nil { t.Fatalf("stat of %s: %v", name, err) } - if gfi.Ino != wfi.Ino { - t.Errorf("%s got different inode", name) + if !gfi.(*os.FileStat).SameFile(wfi.(*os.FileStat)) { + t.Errorf("%s got different file", name) } } test(Dir("/etc/"), "/hosts") @@ -208,6 +208,20 @@ func TestDirJoin(t *testing.T) { test(Dir("/etc/hosts"), "../") } +func TestEmptyDirOpenCWD(t *testing.T) { + test := func(d Dir) { + name := "fs_test.go" + f, err := d.Open(name) + if err != nil { + t.Fatalf("open of %s: %v", name, err) + } + defer f.Close() + } + test(Dir("")) + test(Dir(".")) + test(Dir("./")) +} + func TestServeFileContentType(t *testing.T) { const ctype = "icecream/chocolate" override := false @@ -247,6 +261,20 @@ func TestServeFileMimeType(t *testing.T) { } } +func TestServeFileFromCWD(t *testing.T) { + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + ServeFile(w, r, "fs_test.go") + })) + defer ts.Close() + r, err := Get(ts.URL) + if err != nil { + t.Fatal(err) + } + if r.StatusCode != 200 { + t.Fatalf("expected 200 OK, got %s", r.Status) + } +} + func TestServeFileWithContentEncoding(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { w.Header().Set("Content-Encoding", "foo") diff --git a/libgo/go/net/http/httptest/server.go b/libgo/go/net/http/httptest/server.go index f09e826d9c9..5b02e143d4a 100644 --- a/libgo/go/net/http/httptest/server.go +++ b/libgo/go/net/http/httptest/server.go @@ -7,14 +7,12 @@ package httptest import ( - "crypto/rand" "crypto/tls" "flag" "fmt" "net" "net/http" "os" - "time" ) // A Server is an HTTP server listening on a system-chosen port on the @@ -113,8 +111,6 @@ func (s *Server) StartTLS() { } s.TLS = &tls.Config{ - Rand: rand.Reader, - Time: time.Seconds, NextProtos: []string{"http/1.1"}, Certificates: []tls.Certificate{cert}, } diff --git a/libgo/go/net/http/httputil/chunked.go b/libgo/go/net/http/httputil/chunked.go index 34e47c796c1..69bcc0e816f 100644 --- a/libgo/go/net/http/httputil/chunked.go +++ b/libgo/go/net/http/httputil/chunked.go @@ -2,18 +2,126 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// The wire protocol for HTTP's "chunked" Transfer-Encoding. + +// This code is a duplicate of ../chunked.go with these edits: +// s/newChunked/NewChunked/g +// s/package http/package httputil/ +// Please make any changes in both files. + package httputil import ( "bufio" + "bytes" + "errors" "io" - "net/http" "strconv" - "strings" ) -// NewChunkedWriter returns a new writer that translates writes into HTTP -// "chunked" format before writing them to w. Closing the returned writer +const maxLineLength = 4096 // assumed <= bufio.defaultBufSize + +var ErrLineTooLong = errors.New("header line too long") + +// NewChunkedReader returns a new chunkedReader that translates the data read from r +// out of HTTP "chunked" format before returning it. +// The chunkedReader returns io.EOF when the final 0-length chunk is read. +// +// NewChunkedReader is not needed by normal applications. The http package +// automatically decodes chunking when reading response bodies. +func NewChunkedReader(r io.Reader) io.Reader { + br, ok := r.(*bufio.Reader) + if !ok { + br = bufio.NewReader(r) + } + return &chunkedReader{r: br} +} + +type chunkedReader struct { + r *bufio.Reader + n uint64 // unread bytes in chunk + err error +} + +func (cr *chunkedReader) beginChunk() { + // chunk-size CRLF + var line string + line, cr.err = readLine(cr.r) + if cr.err != nil { + return + } + cr.n, cr.err = strconv.Btoui64(line, 16) + if cr.err != nil { + return + } + if cr.n == 0 { + cr.err = io.EOF + } +} + +func (cr *chunkedReader) Read(b []uint8) (n int, err error) { + if cr.err != nil { + return 0, cr.err + } + if cr.n == 0 { + cr.beginChunk() + if cr.err != nil { + return 0, cr.err + } + } + if uint64(len(b)) > cr.n { + b = b[0:cr.n] + } + n, cr.err = cr.r.Read(b) + cr.n -= uint64(n) + if cr.n == 0 && cr.err == nil { + // end of chunk (CRLF) + b := make([]byte, 2) + if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil { + if b[0] != '\r' || b[1] != '\n' { + cr.err = errors.New("malformed chunked encoding") + } + } + } + return n, cr.err +} + +// Read a line of bytes (up to \n) from b. +// Give up if the line exceeds maxLineLength. +// The returned bytes are a pointer into storage in +// the bufio, so they are only valid until the next bufio read. +func readLineBytes(b *bufio.Reader) (p []byte, err error) { + if p, err = b.ReadSlice('\n'); err != nil { + // We always know when EOF is coming. + // If the caller asked for a line, there should be a line. + if err == io.EOF { + err = io.ErrUnexpectedEOF + } else if err == bufio.ErrBufferFull { + err = ErrLineTooLong + } + return nil, err + } + if len(p) >= maxLineLength { + return nil, ErrLineTooLong + } + + // Chop off trailing white space. + p = bytes.TrimRight(p, " \r\t\n") + + return p, nil +} + +// readLineBytes, but convert the bytes into a string. +func readLine(b *bufio.Reader) (s string, err error) { + p, e := readLineBytes(b) + if e != nil { + return "", e + } + return string(p), nil +} + +// NewChunkedWriter returns a new chunkedWriter that translates writes into HTTP +// "chunked" format before writing them to w. Closing the returned chunkedWriter // sends the final 0-length chunk that marks the end of the stream. // // NewChunkedWriter is not needed by normal applications. The http @@ -25,8 +133,8 @@ func NewChunkedWriter(w io.Writer) io.WriteCloser { return &chunkedWriter{w} } -// Writing to ChunkedWriter translates to writing in HTTP chunked Transfer -// Encoding wire format to the underlying Wire writer. +// Writing to chunkedWriter translates to writing in HTTP chunked Transfer +// Encoding wire format to the underlying Wire chunkedWriter. type chunkedWriter struct { Wire io.Writer } @@ -62,23 +170,3 @@ func (cw *chunkedWriter) Close() error { _, err := io.WriteString(cw.Wire, "0\r\n") return err } - -// NewChunkedReader returns a new reader that translates the data read from r -// out of HTTP "chunked" format before returning it. -// The reader returns io.EOF when the final 0-length chunk is read. -// -// NewChunkedReader is not needed by normal applications. The http package -// automatically decodes chunking when reading response bodies. -func NewChunkedReader(r io.Reader) io.Reader { - // This is a bit of a hack so we don't have to copy chunkedReader into - // httputil. It's a bit more complex than chunkedWriter, which is copied - // above. - req, err := http.ReadRequest(bufio.NewReader(io.MultiReader( - strings.NewReader("POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"), - r, - strings.NewReader("\r\n")))) - if err != nil { - panic("bad fake request: " + err.Error()) - } - return req.Body -} diff --git a/libgo/go/net/http/httputil/chunked_test.go b/libgo/go/net/http/httputil/chunked_test.go index 258d39b93cb..155a32bdf9a 100644 --- a/libgo/go/net/http/httputil/chunked_test.go +++ b/libgo/go/net/http/httputil/chunked_test.go @@ -2,6 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// This code is a duplicate of ../chunked_test.go with these edits: +// s/newChunked/NewChunked/g +// s/package http/package httputil/ +// Please make any changes in both files. + package httputil import ( @@ -27,7 +32,8 @@ func TestChunk(t *testing.T) { r := NewChunkedReader(&b) data, err := ioutil.ReadAll(r) if err != nil { - t.Fatalf("ReadAll from NewChunkedReader: %v", err) + t.Logf(`data: "%s"`, data) + t.Fatalf("ReadAll from reader: %v", err) } if g, e := string(data), chunk1+chunk2; g != e { t.Errorf("chunk reader read %q; want %q", g, e) diff --git a/libgo/go/net/http/httputil/persist.go b/libgo/go/net/http/httputil/persist.go index d7b670110c4..1266bd3ad22 100644 --- a/libgo/go/net/http/httputil/persist.go +++ b/libgo/go/net/http/httputil/persist.go @@ -22,6 +22,10 @@ var ( ErrPipeline = &http.ProtocolError{"pipeline error"} ) +// This is an API usage error - the local side is closed. +// ErrPersistEOF (above) reports that the remote side is closed. +var errClosed = errors.New("i/o operation on closed connection") + // A ServerConn reads requests and sends responses over an underlying // connection, until the HTTP keepalive logic commands an end. ServerConn // also allows hijacking the underlying connection by calling Hijack @@ -108,7 +112,7 @@ func (sc *ServerConn) Read() (req *http.Request, err error) { } if sc.r == nil { // connection closed by user in the meantime defer sc.lk.Unlock() - return nil, os.EBADF + return nil, errClosed } r := sc.r lastbody := sc.lastbody @@ -313,7 +317,7 @@ func (cc *ClientConn) Write(req *http.Request) (err error) { } if cc.c == nil { // connection closed by user in the meantime defer cc.lk.Unlock() - return os.EBADF + return errClosed } c := cc.c if req.Close { @@ -369,7 +373,7 @@ func (cc *ClientConn) Read(req *http.Request) (resp *http.Response, err error) { } if cc.r == nil { // connection closed by user in the meantime defer cc.lk.Unlock() - return nil, os.EBADF + return nil, errClosed } r := cc.r lastbody := cc.lastbody diff --git a/libgo/go/net/http/httputil/reverseproxy.go b/libgo/go/net/http/httputil/reverseproxy.go index bfcb3ca6b11..1dc83e7d032 100644 --- a/libgo/go/net/http/httputil/reverseproxy.go +++ b/libgo/go/net/http/httputil/reverseproxy.go @@ -31,11 +31,11 @@ type ReverseProxy struct { // If nil, http.DefaultTransport is used. Transport http.RoundTripper - // FlushInterval specifies the flush interval, in - // nanoseconds, to flush to the client while - // coping the response body. + // FlushInterval specifies the flush interval + // to flush to the client while copying the + // response body. // If zero, no periodic flushing is done. - FlushInterval int64 + FlushInterval time.Duration } func singleJoiningSlash(a, b string) string { @@ -135,7 +135,7 @@ type writeFlusher interface { type maxLatencyWriter struct { dst writeFlusher - latency int64 // nanos + latency time.Duration lk sync.Mutex // protects init of done, as well Write + Flush done chan bool diff --git a/libgo/go/net/http/pprof/pprof.go b/libgo/go/net/http/pprof/pprof.go index c0327a94824..2de147579d1 100644 --- a/libgo/go/net/http/pprof/pprof.go +++ b/libgo/go/net/http/pprof/pprof.go @@ -80,7 +80,7 @@ func Profile(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Could not enable CPU profiling: %s\n", err) return } - time.Sleep(sec * 1e9) + time.Sleep(time.Duration(sec) * time.Second) pprof.StopCPUProfile() } diff --git a/libgo/go/net/http/readrequest_test.go b/libgo/go/net/http/readrequest_test.go index 2219d433165..c64fff6109f 100644 --- a/libgo/go/net/http/readrequest_test.go +++ b/libgo/go/net/http/readrequest_test.go @@ -70,7 +70,6 @@ var reqTests = []reqTest{ Close: false, ContentLength: 7, Host: "www.techcrunch.com", - Form: url.Values{}, }, "abcdef\n", @@ -94,10 +93,10 @@ var reqTests = []reqTest{ Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, + Header: Header{}, Close: false, ContentLength: 0, Host: "foo.com", - Form: url.Values{}, }, noBody, @@ -131,7 +130,6 @@ var reqTests = []reqTest{ Close: false, ContentLength: 0, Host: "test", - Form: url.Values{}, }, noBody, @@ -180,9 +178,9 @@ var reqTests = []reqTest{ Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, + Header: Header{}, ContentLength: -1, Host: "foo.com", - Form: url.Values{}, }, "foobar", diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go index 4410ca1d11c..66178490e37 100644 --- a/libgo/go/net/http/request.go +++ b/libgo/go/net/http/request.go @@ -19,12 +19,10 @@ import ( "mime/multipart" "net/textproto" "net/url" - "strconv" "strings" ) const ( - maxLineLength = 4096 // assumed <= bufio.defaultBufSize maxValueLength = 4096 maxHeaderLines = 1024 chunkSize = 4 << 10 // 4 KB chunks @@ -43,7 +41,6 @@ type ProtocolError struct { func (err *ProtocolError) Error() string { return err.ErrorString } var ( - ErrLineTooLong = &ProtocolError{"header line too long"} ErrHeaderTooLong = &ProtocolError{"header too long"} ErrShortBody = &ProtocolError{"entity body too short"} ErrNotSupported = &ProtocolError{"feature not supported"} @@ -375,44 +372,6 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err return nil } -// Read a line of bytes (up to \n) from b. -// Give up if the line exceeds maxLineLength. -// The returned bytes are a pointer into storage in -// the bufio, so they are only valid until the next bufio read. -func readLineBytes(b *bufio.Reader) (p []byte, err error) { - if p, err = b.ReadSlice('\n'); err != nil { - // We always know when EOF is coming. - // If the caller asked for a line, there should be a line. - if err == io.EOF { - err = io.ErrUnexpectedEOF - } else if err == bufio.ErrBufferFull { - err = ErrLineTooLong - } - return nil, err - } - if len(p) >= maxLineLength { - return nil, ErrLineTooLong - } - - // Chop off trailing white space. - var i int - for i = len(p); i > 0; i-- { - if c := p[i-1]; c != ' ' && c != '\r' && c != '\t' && c != '\n' { - break - } - } - return p[0:i], nil -} - -// readLineBytes, but convert the bytes into a string. -func readLine(b *bufio.Reader) (s string, err error) { - p, e := readLineBytes(b) - if e != nil { - return "", e - } - return string(p), nil -} - // Convert decimal at s[i:len(s)] to integer, // returning value, string position where the digits stopped, // and whether there was a valid number (digits, not too big). @@ -448,55 +407,6 @@ func ParseHTTPVersion(vers string) (major, minor int, ok bool) { return major, minor, true } -type chunkedReader struct { - r *bufio.Reader - n uint64 // unread bytes in chunk - err error -} - -func (cr *chunkedReader) beginChunk() { - // chunk-size CRLF - var line string - line, cr.err = readLine(cr.r) - if cr.err != nil { - return - } - cr.n, cr.err = strconv.Btoui64(line, 16) - if cr.err != nil { - return - } - if cr.n == 0 { - cr.err = io.EOF - } -} - -func (cr *chunkedReader) Read(b []uint8) (n int, err error) { - if cr.err != nil { - return 0, cr.err - } - if cr.n == 0 { - cr.beginChunk() - if cr.err != nil { - return 0, cr.err - } - } - if uint64(len(b)) > cr.n { - b = b[0:cr.n] - } - n, cr.err = cr.r.Read(b) - cr.n -= uint64(n) - if cr.n == 0 && cr.err == nil { - // end of chunk (CRLF) - b := make([]byte, 2) - if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil { - if b[0] != '\r' || b[1] != '\n' { - cr.err = errors.New("malformed chunked encoding") - } - } - } - return n, cr.err -} - // NewRequest returns a new Request given a method, URL, and optional body. func NewRequest(method, urlStr string, body io.Reader) (*Request, error) { u, err := url.Parse(urlStr) diff --git a/libgo/go/net/http/response_test.go b/libgo/go/net/http/response_test.go index be717aa83c3..e5d01698e55 100644 --- a/libgo/go/net/http/response_test.go +++ b/libgo/go/net/http/response_test.go @@ -65,6 +65,7 @@ var respTests = []respTest{ Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, + Header: Header{}, Request: dummyReq("GET"), Close: true, ContentLength: -1, @@ -85,6 +86,7 @@ var respTests = []respTest{ Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, + Header: Header{}, Request: dummyReq("GET"), Close: false, ContentLength: 0, @@ -315,7 +317,7 @@ func TestReadResponseCloseInMiddle(t *testing.T) { } var wr io.Writer = &buf if test.chunked { - wr = &chunkedWriter{wr} + wr = newChunkedWriter(wr) } if test.compressed { buf.WriteString("Content-Encoding: gzip\r\n") diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go index e278396091d..670b5418fcd 100644 --- a/libgo/go/net/http/serve_test.go +++ b/libgo/go/net/http/serve_test.go @@ -266,19 +266,19 @@ func TestServerTimeouts(t *testing.T) { } // Slow client that should timeout. - t1 := time.Nanoseconds() + t1 := time.Now() conn, err := net.Dial("tcp", addr.String()) if err != nil { t.Fatalf("Dial: %v", err) } buf := make([]byte, 1) n, err := conn.Read(buf) - latency := time.Nanoseconds() - t1 + latency := time.Now().Sub(t1) if n != 0 || err != io.EOF { t.Errorf("Read = %v, %v, wanted %v, %v", n, err, 0, io.EOF) } - if latency < second*0.20 /* fudge from 0.25 above */ { - t.Errorf("got EOF after %d ns, want >= %d", latency, second*0.20) + if latency < 200*time.Millisecond /* fudge from 0.25 above */ { + t.Errorf("got EOF after %s, want >= %s", latency, 200*time.Millisecond) } // Hit the HTTP server successfully again, verifying that the @@ -760,7 +760,7 @@ func TestTimeoutHandler(t *testing.T) { _, werr := w.Write([]byte("hi")) writeErrors <- werr }) - timeout := make(chan int64, 1) // write to this to force timeouts + timeout := make(chan time.Time, 1) // write to this to force timeouts ts := httptest.NewServer(NewTestTimeoutHandler(sayHi, timeout)) defer ts.Close() @@ -782,7 +782,7 @@ func TestTimeoutHandler(t *testing.T) { } // Times out: - timeout <- 1 + timeout <- time.Time{} res, err = Get(ts.URL) if err != nil { t.Error(err) @@ -1077,6 +1077,31 @@ func TestClientWriteShutdown(t *testing.T) { } } +// Tests that chunked server responses that write 1 byte at a time are +// buffered before chunk headers are added, not after chunk headers. +func TestServerBufferedChunking(t *testing.T) { + if true { + t.Logf("Skipping known broken test; see Issue 2357") + return + } + conn := new(testConn) + conn.readBuf.Write([]byte("GET / HTTP/1.1\r\n\r\n")) + done := make(chan bool) + ls := &oneConnListener{conn} + go Serve(ls, HandlerFunc(func(rw ResponseWriter, req *Request) { + defer close(done) + rw.Header().Set("Content-Type", "text/plain") // prevent sniffing, which buffers + rw.Write([]byte{'x'}) + rw.Write([]byte{'y'}) + rw.Write([]byte{'z'}) + })) + <-done + if !bytes.HasSuffix(conn.writeBuf.Bytes(), []byte("\r\n\r\n3\r\nxyz\r\n0\r\n\r\n")) { + t.Errorf("response didn't end with a single 3 byte 'xyz' chunk; got:\n%q", + conn.writeBuf.Bytes()) + } +} + // goTimeout runs f, failing t if f takes more than ns to complete. func goTimeout(t *testing.T, ns int64, f func()) { ch := make(chan bool, 2) @@ -1120,7 +1145,7 @@ func TestAcceptMaxFds(t *testing.T) { ln := &errorListener{[]error{ &net.OpError{ Op: "accept", - Err: os.Errno(syscall.EMFILE), + Err: syscall.EMFILE, }}} err := Serve(ln, HandlerFunc(HandlerFunc(func(ResponseWriter, *Request) {}))) if err != io.EOF { diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go index 8c4889436f1..125f3f214bb 100644 --- a/libgo/go/net/http/server.go +++ b/libgo/go/net/http/server.go @@ -149,11 +149,13 @@ type writerOnly struct { } func (w *response) ReadFrom(src io.Reader) (n int64, err error) { - // Flush before checking w.chunking, as Flush will call - // WriteHeader if it hasn't been called yet, and WriteHeader - // is what sets w.chunking. - w.Flush() + // Call WriteHeader before checking w.chunking if it hasn't + // been called yet, since WriteHeader is what sets w.chunking. + if !w.wroteHeader { + w.WriteHeader(StatusOK) + } if !w.chunking && w.bodyAllowed() && !w.needSniff { + w.Flush() if rf, ok := w.conn.rwc.(io.ReaderFrom); ok { n, err = rf.ReadFrom(src) w.written += n @@ -345,7 +347,7 @@ func (w *response) WriteHeader(code int) { } if _, ok := w.header["Date"]; !ok { - w.Header().Set("Date", time.UTC().Format(TimeFormat)) + w.Header().Set("Date", time.Now().UTC().Format(TimeFormat)) } te := w.header.Get("Transfer-Encoding") @@ -465,7 +467,7 @@ func (w *response) Write(data []byte) (n int, err error) { // determine the content type. Accumulate the // initial writes in w.conn.body. // Cap m so that append won't allocate. - m := cap(w.conn.body) - len(w.conn.body) + m = cap(w.conn.body) - len(w.conn.body) if m > len(data) { m = len(data) } @@ -1011,8 +1013,8 @@ func (srv *Server) Serve(l net.Listener) error { // package main // // import ( -// "http" // "io" +// "net/http" // "log" // ) // @@ -1042,8 +1044,8 @@ func ListenAndServe(addr string, handler Handler) error { // A trivial example server is: // // import ( -// "http" // "log" +// "net/http" // ) // // func handler(w http.ResponseWriter, req *http.Request) { @@ -1082,7 +1084,6 @@ func (s *Server) ListenAndServeTLS(certFile, keyFile string) error { } config := &tls.Config{ Rand: rand.Reader, - Time: time.Seconds, NextProtos: []string{"http/1.1"}, } @@ -1110,9 +1111,9 @@ func (s *Server) ListenAndServeTLS(certFile, keyFile string) error { // (If msg is empty, a suitable default message will be sent.) // After such a timeout, writes by h to its ResponseWriter will return // ErrHandlerTimeout. -func TimeoutHandler(h Handler, ns int64, msg string) Handler { - f := func() <-chan int64 { - return time.After(ns) +func TimeoutHandler(h Handler, dt time.Duration, msg string) Handler { + f := func() <-chan time.Time { + return time.After(dt) } return &timeoutHandler{h, f, msg} } @@ -1123,7 +1124,7 @@ var ErrHandlerTimeout = errors.New("http: Handler timeout") type timeoutHandler struct { handler Handler - timeout func() <-chan int64 // returns channel producing a timeout + timeout func() <-chan time.Time // returns channel producing a timeout body string } diff --git a/libgo/go/net/http/sniff.go b/libgo/go/net/http/sniff.go index 5707c7f057f..c1c78e2417d 100644 --- a/libgo/go/net/http/sniff.go +++ b/libgo/go/net/http/sniff.go @@ -48,23 +48,23 @@ type sniffSig interface { // Data matching the table in section 6. var sniffSignatures = []sniffSig{ - htmlSig([]byte("<!DOCTYPE HTML")), - htmlSig([]byte("<HTML")), - htmlSig([]byte("<HEAD")), - htmlSig([]byte("<SCRIPT")), - htmlSig([]byte("<IFRAME")), - htmlSig([]byte("<H1")), - htmlSig([]byte("<DIV")), - htmlSig([]byte("<FONT")), - htmlSig([]byte("<TABLE")), - htmlSig([]byte("<A")), - htmlSig([]byte("<STYLE")), - htmlSig([]byte("<TITLE")), - htmlSig([]byte("<B")), - htmlSig([]byte("<BODY")), - htmlSig([]byte("<BR")), - htmlSig([]byte("<P")), - htmlSig([]byte("<!--")), + htmlSig("<!DOCTYPE HTML"), + htmlSig("<HTML"), + htmlSig("<HEAD"), + htmlSig("<SCRIPT"), + htmlSig("<IFRAME"), + htmlSig("<H1"), + htmlSig("<DIV"), + htmlSig("<FONT"), + htmlSig("<TABLE"), + htmlSig("<A"), + htmlSig("<STYLE"), + htmlSig("<TITLE"), + htmlSig("<B"), + htmlSig("<BODY"), + htmlSig("<BR"), + htmlSig("<P"), + htmlSig("<!--"), &maskedSig{mask: []byte("\xFF\xFF\xFF\xFF\xFF"), pat: []byte("<?xml"), skipWS: true, ct: "text/xml; charset=utf-8"}, diff --git a/libgo/go/net/http/sniff_test.go b/libgo/go/net/http/sniff_test.go index a414e6420db..6efa8ce1ca2 100644 --- a/libgo/go/net/http/sniff_test.go +++ b/libgo/go/net/http/sniff_test.go @@ -6,11 +6,14 @@ package http_test import ( "bytes" + "fmt" + "io" "io/ioutil" "log" . "net/http" "net/http/httptest" "strconv" + "strings" "testing" ) @@ -79,3 +82,56 @@ func TestServerContentType(t *testing.T) { resp.Body.Close() } } + +func TestContentTypeWithCopy(t *testing.T) { + const ( + input = "\n<html>\n\t<head>\n" + expected = "text/html; charset=utf-8" + ) + + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + // Use io.Copy from a bytes.Buffer to trigger ReadFrom. + buf := bytes.NewBuffer([]byte(input)) + n, err := io.Copy(w, buf) + if int(n) != len(input) || err != nil { + t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input)) + } + })) + defer ts.Close() + + resp, err := Get(ts.URL) + if err != nil { + t.Fatalf("Get: %v", err) + } + if ct := resp.Header.Get("Content-Type"); ct != expected { + t.Errorf("Content-Type = %q, want %q", ct, expected) + } + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Errorf("reading body: %v", err) + } else if !bytes.Equal(data, []byte(input)) { + t.Errorf("data is %q, want %q", data, input) + } + resp.Body.Close() +} + +func TestSniffWriteSize(t *testing.T) { + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + size, _ := strconv.Atoi(r.FormValue("size")) + written, err := io.WriteString(w, strings.Repeat("a", size)) + if err != nil { + t.Errorf("write of %d bytes: %v", size, err) + return + } + if written != size { + t.Errorf("write of %d bytes wrote %d bytes", size, written) + } + })) + defer ts.Close() + for _, size := range []int{0, 1, 200, 600, 999, 1000, 1023, 1024, 512 << 10, 1 << 20} { + _, err := Get(fmt.Sprintf("%s/?size=%d", ts.URL, size)) + if err != nil { + t.Fatalf("size %d: %v", size, err) + } + } +} diff --git a/libgo/go/net/http/transfer.go b/libgo/go/net/http/transfer.go index 2670d77ef00..d25c8fcde42 100644 --- a/libgo/go/net/http/transfer.go +++ b/libgo/go/net/http/transfer.go @@ -537,7 +537,9 @@ func (b *body) Read(p []byte) (n int, err error) { // Read the final trailer once we hit EOF. if err == io.EOF && b.hdr != nil { - err = b.readTrailer() + if e := b.readTrailer(); e != nil { + err = e + } b.hdr = nil } return n, err diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go index da5244b2c12..e622e41f0a2 100644 --- a/libgo/go/net/http/transport.go +++ b/libgo/go/net/http/transport.go @@ -504,7 +504,7 @@ func (pc *persistConn) expectingResponse() bool { var remoteSideClosedFunc func(error) bool // or nil to use default func remoteSideClosed(err error) bool { - if err == io.EOF || err == os.EINVAL { + if err == io.EOF { return true } if remoteSideClosedFunc != nil { diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go index 77297972449..6f50f6f2767 100644 --- a/libgo/go/net/http/transport_test.go +++ b/libgo/go/net/http/transport_test.go @@ -263,7 +263,7 @@ func TestTransportServerClosingUnexpectedly(t *testing.T) { t.Fatalf(format, arg...) } t.Logf("retrying shortly after expected error: "+format, arg...) - time.Sleep(1e9 / int64(retries)) + time.Sleep(time.Second / time.Duration(retries)) } for retries >= 0 { retries-- diff --git a/libgo/go/net/http/transport_windows.go b/libgo/go/net/http/transport_windows.go index 2a20d2224ae..c9ef2c2ab6e 100644 --- a/libgo/go/net/http/transport_windows.go +++ b/libgo/go/net/http/transport_windows.go @@ -6,14 +6,14 @@ package http import ( "net" - "os" + "syscall" ) func init() { remoteSideClosedFunc = func(err error) (out bool) { op, ok := err.(*net.OpError) - if ok && op.Op == "WSARecv" && op.Net == "tcp" && op.Err == os.Errno(10058) { - // TODO(bradfitz): find the symbol for 10058 + if ok && op.Op == "WSARecv" && op.Net == "tcp" && op.Err == syscall.Errno(10058) { + // TODO(brainman,rsc): Fix whatever is generating this. return true } return false diff --git a/libgo/go/net/interface_bsd.go b/libgo/go/net/interface_bsd.go index b026e01104d..e896d43c321 100644 --- a/libgo/go/net/interface_bsd.go +++ b/libgo/go/net/interface_bsd.go @@ -20,18 +20,18 @@ import ( func interfaceTable(ifindex int) ([]Interface, error) { var ( tab []byte - e int + e error msgs []syscall.RoutingMessage ift []Interface ) tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route rib", e) } msgs, e = syscall.ParseRoutingMessage(tab) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route message", e) } @@ -55,7 +55,7 @@ func newLink(m *syscall.InterfaceMessage) ([]Interface, error) { var ift []Interface sas, e := syscall.ParseRoutingSockaddr(m) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route sockaddr", e) } @@ -110,18 +110,18 @@ func linkFlags(rawFlags int32) Flags { func interfaceAddrTable(ifindex int) ([]Addr, error) { var ( tab []byte - e int + e error msgs []syscall.RoutingMessage ifat []Addr ) tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route rib", e) } msgs, e = syscall.ParseRoutingMessage(tab) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route message", e) } @@ -145,7 +145,7 @@ func newAddr(m *syscall.InterfaceAddrMessage) ([]Addr, error) { var ifat []Addr sas, e := syscall.ParseRoutingSockaddr(m) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route sockaddr", e) } diff --git a/libgo/go/net/interface_darwin.go b/libgo/go/net/interface_darwin.go index 1472afb8846..2da447adc84 100644 --- a/libgo/go/net/interface_darwin.go +++ b/libgo/go/net/interface_darwin.go @@ -17,18 +17,18 @@ import ( func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) { var ( tab []byte - e int + e error msgs []syscall.RoutingMessage ifmat []Addr ) tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST2, ifindex) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route rib", e) } msgs, e = syscall.ParseRoutingMessage(tab) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route message", e) } @@ -52,7 +52,7 @@ func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) var ifmat []Addr sas, e := syscall.ParseRoutingSockaddr(m) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route sockaddr", e) } diff --git a/libgo/go/net/interface_freebsd.go b/libgo/go/net/interface_freebsd.go index b0274f68d75..a12877e251b 100644 --- a/libgo/go/net/interface_freebsd.go +++ b/libgo/go/net/interface_freebsd.go @@ -17,18 +17,18 @@ import ( func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) { var ( tab []byte - e int + e error msgs []syscall.RoutingMessage ifmat []Addr ) tab, e = syscall.RouteRIB(syscall.NET_RT_IFMALIST, ifindex) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route rib", e) } msgs, e = syscall.ParseRoutingMessage(tab) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route message", e) } @@ -52,7 +52,7 @@ func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) var ifmat []Addr sas, e := syscall.ParseRoutingSockaddr(m) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route sockaddr", e) } diff --git a/libgo/go/net/interface_linux.go b/libgo/go/net/interface_linux.go index cd0339d61bd..96db7186af5 100644 --- a/libgo/go/net/interface_linux.go +++ b/libgo/go/net/interface_linux.go @@ -21,16 +21,16 @@ func interfaceTable(ifindex int) ([]Interface, error) { ift []Interface tab []byte msgs []syscall.NetlinkMessage - e int + e error ) tab, e = syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("netlink rib", e) } msgs, e = syscall.ParseNetlinkMessage(tab) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("netlink message", e) } @@ -42,7 +42,7 @@ func interfaceTable(ifindex int) ([]Interface, error) { ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0])) if ifindex == 0 || ifindex == int(ifim.Index) { attrs, e := syscall.ParseNetlinkRouteAttr(&m) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("netlink routeattr", e) } ifi := newLink(attrs, ifim) @@ -102,27 +102,19 @@ func linkFlags(rawFlags uint32) Flags { // for all network interfaces. Otherwise it returns addresses // for a specific interface. func interfaceAddrTable(ifindex int) ([]Addr, error) { - var ( - tab []byte - e int - err error - ifat []Addr - msgs []syscall.NetlinkMessage - ) - - tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC) - if e != 0 { + tab, e := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC) + if e != nil { return nil, os.NewSyscallError("netlink rib", e) } - msgs, e = syscall.ParseNetlinkMessage(tab) - if e != 0 { + msgs, e := syscall.ParseNetlinkMessage(tab) + if e != nil { return nil, os.NewSyscallError("netlink message", e) } - ifat, err = addrTable(msgs, ifindex) - if err != nil { - return nil, err + ifat, e := addrTable(msgs, ifindex) + if e != nil { + return nil, e } return ifat, nil @@ -139,7 +131,7 @@ func addrTable(msgs []syscall.NetlinkMessage, ifindex int) ([]Addr, error) { ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0])) if ifindex == 0 || ifindex == int(ifam.Index) { attrs, e := syscall.ParseNetlinkRouteAttr(&m) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("netlink routeattr", e) } ifat = append(ifat, newAddr(attrs, int(ifam.Family))...) diff --git a/libgo/go/net/interface_windows.go b/libgo/go/net/interface_windows.go index a1c8d95161c..2ed66cdce37 100644 --- a/libgo/go/net/interface_windows.go +++ b/libgo/go/net/interface_windows.go @@ -39,7 +39,7 @@ func getAdapterList() (*syscall.IpAdapterInfo, error) { func getInterfaceList() ([]syscall.InterfaceInfo, error) { s, e := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("Socket", e) } defer syscall.Closesocket(s) @@ -48,7 +48,7 @@ func getInterfaceList() ([]syscall.InterfaceInfo, error) { ret := uint32(0) size := uint32(unsafe.Sizeof(ii)) e = syscall.WSAIoctl(s, syscall.SIO_GET_INTERFACE_LIST, nil, 0, (*byte)(unsafe.Pointer(&ii[0])), size, &ret, nil, 0) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("WSAIoctl", e) } c := ret / uint32(unsafe.Sizeof(ii[0])) diff --git a/libgo/go/net/ipsock.go b/libgo/go/net/ipsock.go index 716454d8a98..9234f5aff65 100644 --- a/libgo/go/net/ipsock.go +++ b/libgo/go/net/ipsock.go @@ -9,7 +9,7 @@ package net var supportsIPv6, supportsIPv4map = probeIPv6Stack() func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) { - if filter == anyaddr { + if filter == nil { // We'll take any IP address, but since the dialing code // does not yet try multiple addresses, prefer to use // an IPv4 address if possible. This is especially relevant @@ -113,7 +113,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err error) { // Try as an IP address. addr = ParseIP(host) if addr == nil { - filter := anyaddr + var filter func(IP) IP if net != "" && net[len(net)-1] == '4' { filter = ipv4only } diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go index d5b8f2189c4..f0ca7dad345 100644 --- a/libgo/go/net/ipsock_posix.go +++ b/libgo/go/net/ipsock_posix.go @@ -33,8 +33,8 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { } for i := range probes { - s, errno := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) - if errno != 0 { + s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + if err != nil { continue } defer closesocket(s) @@ -42,8 +42,8 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { if err != nil { continue } - errno = syscall.Bind(s, sa) - if errno != 0 { + err = syscall.Bind(s, sa) + if err != nil { continue } probes[i].ok = true diff --git a/libgo/go/net/lookup_windows.go b/libgo/go/net/lookup_windows.go index 61d8a8871e1..020871b46d6 100644 --- a/libgo/go/net/lookup_windows.go +++ b/libgo/go/net/lookup_windows.go @@ -22,7 +22,7 @@ func lookupProtocol(name string) (proto int, err error) { protoentLock.Lock() defer protoentLock.Unlock() p, e := syscall.GetProtoByName(name) - if e != 0 { + if e != nil { return 0, os.NewSyscallError("GetProtoByName", e) } return int(p.Proto), nil @@ -44,7 +44,7 @@ func LookupIP(name string) (addrs []IP, err error) { hostentLock.Lock() defer hostentLock.Unlock() h, e := syscall.GetHostByName(name) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("GetHostByName", e) } switch h.AddrType { @@ -71,7 +71,7 @@ func LookupPort(network, service string) (port int, err error) { serventLock.Lock() defer serventLock.Unlock() s, e := syscall.GetServByName(service, network) - if e != 0 { + if e != nil { return 0, os.NewSyscallError("GetServByName", e) } return int(syscall.Ntohs(s.Port)), nil @@ -81,7 +81,7 @@ func LookupCNAME(name string) (cname string, err error) { var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil) if e != 0 { - return "", os.NewSyscallError("LookupCNAME", int(e)) + return "", os.NewSyscallError("LookupCNAME", e) } defer syscall.DnsRecordListFree(r, 1) if r != nil && r.Type == syscall.DNS_TYPE_CNAME { @@ -110,7 +110,7 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err var r *syscall.DNSRecord e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &r, nil) if e != 0 { - return "", nil, os.NewSyscallError("LookupSRV", int(e)) + return "", nil, os.NewSyscallError("LookupSRV", e) } defer syscall.DnsRecordListFree(r, 1) addrs = make([]*SRV, 0, 10) @@ -126,7 +126,7 @@ func LookupMX(name string) (mx []*MX, err error) { var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil) if e != 0 { - return nil, os.NewSyscallError("LookupMX", int(e)) + return nil, os.NewSyscallError("LookupMX", e) } defer syscall.DnsRecordListFree(r, 1) mx = make([]*MX, 0, 10) @@ -142,7 +142,7 @@ func LookupTXT(name string) (txt []string, err error) { var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil) if e != 0 { - return nil, os.NewSyscallError("LookupTXT", int(e)) + return nil, os.NewSyscallError("LookupTXT", e) } defer syscall.DnsRecordListFree(r, 1) txt = make([]string, 0, 10) @@ -164,7 +164,7 @@ func LookupAddr(addr string) (name []string, err error) { var r *syscall.DNSRecord e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &r, nil) if e != 0 { - return nil, os.NewSyscallError("LookupAddr", int(e)) + return nil, os.NewSyscallError("LookupAddr", e) } defer syscall.DnsRecordListFree(r, 1) name = make([]string, 0, 10) diff --git a/libgo/go/net/mail/message.go b/libgo/go/net/mail/message.go index 95246b2fa1d..e1afa32062f 100644 --- a/libgo/go/net/mail/message.go +++ b/libgo/go/net/mail/message.go @@ -89,14 +89,14 @@ func init() { } } -func parseDate(date string) (*time.Time, error) { +func parseDate(date string) (time.Time, error) { for _, layout := range dateLayouts { t, err := time.Parse(layout, date) if err == nil { return t, nil } } - return nil, errors.New("mail: header could not be parsed") + return time.Time{}, errors.New("mail: header could not be parsed") } // A Header represents the key-value pairs in a mail message header. @@ -111,10 +111,10 @@ func (h Header) Get(key string) string { var ErrHeaderNotPresent = errors.New("mail: header not in message") // Date parses the Date header field. -func (h Header) Date() (*time.Time, error) { +func (h Header) Date() (time.Time, error) { hdr := h.Get("Date") if hdr == "" { - return nil, ErrHeaderNotPresent + return time.Time{}, ErrHeaderNotPresent } return parseDate(hdr) } @@ -185,7 +185,7 @@ func (a *Address) String() string { type addrParser []byte func newAddrParser(s string) *addrParser { - p := addrParser([]byte(s)) + p := addrParser(s) return &p } diff --git a/libgo/go/net/mail/message_test.go b/libgo/go/net/mail/message_test.go index 5653647b8cc..1f71cc480af 100644 --- a/libgo/go/net/mail/message_test.go +++ b/libgo/go/net/mail/message_test.go @@ -82,34 +82,18 @@ func headerEq(a, b Header) bool { func TestDateParsing(t *testing.T) { tests := []struct { dateStr string - exp *time.Time + exp time.Time }{ // RFC 5322, Appendix A.1.1 { "Fri, 21 Nov 1997 09:55:06 -0600", - &time.Time{ - Year: 1997, - Month: 11, - Day: 21, - Hour: 9, - Minute: 55, - Second: 6, - ZoneOffset: -6 * 60 * 60, - }, + time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)), }, // RFC5322, Appendix A.6.2 // Obsolete date. { "21 Nov 97 09:55:06 GMT", - &time.Time{ - Year: 1997, - Month: 11, - Day: 21, - Hour: 9, - Minute: 55, - Second: 6, - Zone: "GMT", - }, + time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("GMT", 0)), }, } for _, test := range tests { diff --git a/libgo/go/net/newpollserver.go b/libgo/go/net/newpollserver.go index 9ad6f7ba276..035df4a6ff1 100644 --- a/libgo/go/net/newpollserver.go +++ b/libgo/go/net/newpollserver.go @@ -18,11 +18,10 @@ func newPollServer() (s *pollServer, err error) { if s.pr, s.pw, err = os.Pipe(); err != nil { return nil, err } - var e int - if e = syscall.SetNonblock(s.pr.Fd(), true); e != 0 { + if err = syscall.SetNonblock(s.pr.Fd(), true); err != nil { goto Errno } - if e = syscall.SetNonblock(s.pw.Fd(), true); e != 0 { + if err = syscall.SetNonblock(s.pw.Fd(), true); err != nil { goto Errno } if s.poll, err = newpollster(); err != nil { @@ -37,7 +36,7 @@ func newPollServer() (s *pollServer, err error) { return s, nil Errno: - err = &os.PathError{"setnonblock", s.pr.Name(), os.Errno(e)} + err = &os.PathError{"setnonblock", s.pr.Name(), err} Error: s.pr.Close() s.pw.Close() diff --git a/libgo/go/net/pipe.go b/libgo/go/net/pipe.go index b99e6e658de..0ce7ccb9d7b 100644 --- a/libgo/go/net/pipe.go +++ b/libgo/go/net/pipe.go @@ -1,3 +1,7 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package net import ( diff --git a/libgo/go/net/sendfile_linux.go b/libgo/go/net/sendfile_linux.go index 36c75785789..350abe451f3 100644 --- a/libgo/go/net/sendfile_linux.go +++ b/libgo/go/net/sendfile_linux.go @@ -62,18 +62,18 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { written += int64(n) remain -= int64(n) } - if n == 0 && errno == 0 { + if n == 0 && errno == nil { break } if errno == syscall.EAGAIN && c.wdeadline >= 0 { pollserver.WaitWrite(c) continue } - if errno != 0 { + if errno != nil { // This includes syscall.ENOSYS (no kernel // support) and syscall.EINVAL (fd types which // don't implement sendfile together) - err = &OpError{"sendfile", c.net, c.raddr, os.Errno(errno)} + err = &OpError{"sendfile", c.net, c.raddr, errno} break } } diff --git a/libgo/go/net/sendfile_windows.go b/libgo/go/net/sendfile_windows.go index 0b31572771c..ee7ff8b98c2 100644 --- a/libgo/go/net/sendfile_windows.go +++ b/libgo/go/net/sendfile_windows.go @@ -16,7 +16,7 @@ type sendfileOp struct { n uint32 } -func (o *sendfileOp) Submit() (errno int) { +func (o *sendfileOp) Submit() (err error) { return syscall.TransmitFile(o.fd.sysfd, o.src, o.n, 0, &o.o, nil, syscall.TF_WRITE_BEHIND) } diff --git a/libgo/go/net/sock.go b/libgo/go/net/sock.go index d9df02cd63f..33f11f219c9 100644 --- a/libgo/go/net/sock.go +++ b/libgo/go/net/sock.go @@ -28,9 +28,9 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal // See ../syscall/exec.go for description of ForkLock. syscall.ForkLock.RLock() s, e := syscall.Socket(f, p, t) - if e != 0 { + if err != nil { syscall.ForkLock.RUnlock() - return nil, os.Errno(e) + return nil, err } syscall.CloseOnExec(s) syscall.ForkLock.RUnlock() @@ -39,9 +39,9 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal if la != nil { e = syscall.Bind(s, la) - if e != 0 { + if e != nil { closesocket(s) - return nil, os.Errno(e) + return nil, e } } diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go index a726b45c15a..44890ba66bb 100644 --- a/libgo/go/net/tcpsock_posix.go +++ b/libgo/go/net/tcpsock_posix.go @@ -250,9 +250,9 @@ func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err error) { return nil, err } errno := syscall.Listen(fd.sysfd, listenBacklog()) - if errno != 0 { + if errno != nil { closesocket(fd.sysfd) - return nil, &OpError{"listen", "tcp", laddr, os.Errno(errno)} + return nil, &OpError{"listen", "tcp", laddr, errno} } l = new(TCPListener) l.fd = fd diff --git a/libgo/go/net/timeout_test.go b/libgo/go/net/timeout_test.go index 3c884ca7cfe..f6e5238c1b1 100644 --- a/libgo/go/net/timeout_test.go +++ b/libgo/go/net/timeout_test.go @@ -17,7 +17,7 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) { return } defer fd.Close() - t0 := time.Nanoseconds() + t0 := time.Now() fd.SetReadTimeout(1e8) // 100ms var b [100]byte var n int @@ -27,7 +27,7 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) { } else { n, err1 = fd.Read(b[0:]) } - t1 := time.Nanoseconds() + t1 := time.Now() what := "Read" if readFrom { what = "ReadFrom" @@ -35,8 +35,8 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) { if n != 0 || err1 == nil || !err1.(Error).Timeout() { t.Errorf("fd.%s on %s %s did not return 0, timeout: %v, %v", what, network, addr, n, err1) } - if t1-t0 < 0.5e8 || t1-t0 > 1.5e8 { - t.Errorf("fd.%s on %s %s took %f seconds, expected 0.1", what, network, addr, float64(t1-t0)/1e9) + if dt := t1.Sub(t0); dt < 50*time.Millisecond || dt > 150*time.Millisecond { + t.Errorf("fd.%s on %s %s took %s, expected 0.1s", what, network, addr, dt) } } diff --git a/libgo/go/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go index 6ba692e5083..929f6409a4f 100644 --- a/libgo/go/net/unixsock_posix.go +++ b/libgo/go/net/unixsock_posix.go @@ -327,9 +327,9 @@ func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err error) { return nil, err } e1 := syscall.Listen(fd.sysfd, 8) // listenBacklog()); - if e1 != 0 { + if e1 != nil { closesocket(fd.sysfd) - return nil, &OpError{Op: "listen", Net: "unix", Addr: laddr, Err: os.Errno(e1)} + return nil, &OpError{Op: "listen", Net: "unix", Addr: laddr, Err: e1} } return &UnixListener{fd, laddr.Name}, nil } diff --git a/libgo/go/old/netchan/common.go b/libgo/go/old/netchan/common.go index dfd1fd03427..03fa8ff6c41 100644 --- a/libgo/go/old/netchan/common.go +++ b/libgo/go/old/netchan/common.go @@ -129,8 +129,8 @@ func (ed *encDec) encode(hdr *header, payloadType int, payload interface{}) erro } // See the comment for Exporter.Drain. -func (cs *clientSet) drain(timeout int64) error { - startTime := time.Nanoseconds() +func (cs *clientSet) drain(timeout time.Duration) error { + deadline := time.Now().Add(timeout) for { pending := false cs.mu.Lock() @@ -152,7 +152,7 @@ func (cs *clientSet) drain(timeout int64) error { if !pending { break } - if timeout > 0 && time.Nanoseconds()-startTime >= timeout { + if timeout > 0 && time.Now().After(deadline) { return errors.New("timeout") } time.Sleep(100 * 1e6) // 100 milliseconds @@ -161,8 +161,8 @@ func (cs *clientSet) drain(timeout int64) error { } // See the comment for Exporter.Sync. -func (cs *clientSet) sync(timeout int64) error { - startTime := time.Nanoseconds() +func (cs *clientSet) sync(timeout time.Duration) error { + deadline := time.Now().Add(timeout) // seq remembers the clients and their seqNum at point of entry. seq := make(map[unackedCounter]int64) for client := range cs.clients { @@ -185,7 +185,7 @@ func (cs *clientSet) sync(timeout int64) error { if !pending { break } - if timeout > 0 && time.Nanoseconds()-startTime >= timeout { + if timeout > 0 && time.Now().After(deadline) { return errors.New("timeout") } time.Sleep(100 * 1e6) // 100 milliseconds diff --git a/libgo/go/old/netchan/export.go b/libgo/go/old/netchan/export.go index d698dd53a90..d94c4b16b21 100644 --- a/libgo/go/old/netchan/export.go +++ b/libgo/go/old/netchan/export.go @@ -29,6 +29,7 @@ import ( "reflect" "strconv" "sync" + "time" ) // Export @@ -322,9 +323,9 @@ func (exp *Exporter) delClient(client *expClient) { // those not yet sent to any client and possibly including those sent while // Drain was executing, have been received by the importer. In short, it // waits until all the exporter's messages have been received by a client. -// If the timeout (measured in nanoseconds) is positive and Drain takes -// longer than that to complete, an error is returned. -func (exp *Exporter) Drain(timeout int64) error { +// If the timeout is positive and Drain takes longer than that to complete, +// an error is returned. +func (exp *Exporter) Drain(timeout time.Duration) error { // This wrapper function is here so the method's comment will appear in godoc. return exp.clientSet.drain(timeout) } @@ -332,10 +333,9 @@ func (exp *Exporter) Drain(timeout int64) error { // Sync waits until all clients of the exporter have received the messages // that were sent at the time Sync was invoked. Unlike Drain, it does not // wait for messages sent while it is running or messages that have not been -// dispatched to any client. If the timeout (measured in nanoseconds) is -// positive and Sync takes longer than that to complete, an error is -// returned. -func (exp *Exporter) Sync(timeout int64) error { +// dispatched to any client. If the timeout is positive and Sync takes longer +// than that to complete, an error is returned. +func (exp *Exporter) Sync(timeout time.Duration) error { // This wrapper function is here so the method's comment will appear in godoc. return exp.clientSet.sync(timeout) } diff --git a/libgo/go/old/netchan/import.go b/libgo/go/old/netchan/import.go index 7243672ecd3..a6da8210b99 100644 --- a/libgo/go/old/netchan/import.go +++ b/libgo/go/old/netchan/import.go @@ -276,9 +276,9 @@ func (imp *Importer) unackedCount() int64 { // If the timeout (measured in nanoseconds) is positive and Drain takes // longer than that to complete, an error is returned. func (imp *Importer) Drain(timeout int64) error { - startTime := time.Nanoseconds() + deadline := time.Now().Add(time.Duration(timeout)) for imp.unackedCount() > 0 { - if timeout > 0 && time.Nanoseconds()-startTime >= timeout { + if timeout > 0 && time.Now().After(deadline) { return errors.New("timeout") } time.Sleep(100 * 1e6) diff --git a/libgo/go/os/dir_largefile.go b/libgo/go/os/dir_largefile.go index c723ec92400..d6b4239610e 100644 --- a/libgo/go/os/dir_largefile.go +++ b/libgo/go/os/dir_largefile.go @@ -9,4 +9,4 @@ package os import "syscall" -func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) int __asm__ ("readdir64_r") +func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) syscall.Errno __asm__ ("readdir64_r") diff --git a/libgo/go/os/dir_regfile.go b/libgo/go/os/dir_regfile.go index 22fb5febbb2..7effdf7851a 100644 --- a/libgo/go/os/dir_regfile.go +++ b/libgo/go/os/dir_regfile.go @@ -9,4 +9,4 @@ package os import "syscall" -func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) int __asm__ ("readdir_r") +func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) syscall.Errno __asm__ ("readdir_r") diff --git a/libgo/go/os/dir_unix.go b/libgo/go/os/dir_unix.go index a16bcf63f41..e4dff835d89 100644 --- a/libgo/go/os/dir_unix.go +++ b/libgo/go/os/dir_unix.go @@ -47,9 +47,9 @@ func (f *File) Readdirnames(n int) (names []string, err error) { // Refill the buffer if necessary if d.bufp >= d.nbuf { d.bufp = 0 - var errno int + var errno error d.nbuf, errno = syscall.ReadDirent(f.fd, d.buf) - if errno != 0 { + if errno != nil { return names, NewSyscallError("readdirent", errno) } if d.nbuf <= 0 { diff --git a/libgo/go/os/env.go b/libgo/go/os/env.go index 4844fa3e26d..7e3f52502e5 100644 --- a/libgo/go/os/env.go +++ b/libgo/go/os/env.go @@ -6,7 +6,10 @@ package os -func setenv_c(k, v string) +import ( + "errors" + "syscall" +) // Expand replaces ${var} or $var in the string based on the mapping function. // Invocations of undefined variables are replaced with the empty string. @@ -73,3 +76,47 @@ func getShellName(s string) (string, int) { } return s[:i], i } + +// ENOENV is the error indicating that an environment variable does not exist. +var ENOENV = errors.New("no such environment variable") + +// Getenverror retrieves the value of the environment variable named by the key. +// It returns the value and an error, if any. +func Getenverror(key string) (value string, err error) { + if len(key) == 0 { + return "", EINVAL + } + val, found := syscall.Getenv(key) + if !found { + return "", ENOENV + } + return val, nil +} + +// Getenv retrieves the value of the environment variable named by the key. +// It returns the value, which will be empty if the variable is not present. +func Getenv(key string) string { + v, _ := Getenverror(key) + return v +} + +// Setenv sets the value of the environment variable named by the key. +// It returns an error, if any. +func Setenv(key, value string) error { + err := syscall.Setenv(key, value) + if err != nil { + return NewSyscallError("setenv", err) + } + return nil +} + +// Clearenv deletes all environment variables. +func Clearenv() { + syscall.Clearenv() +} + +// Environ returns an array of strings representing the environment, +// in the form "key=value". +func Environ() []string { + return syscall.Environ() +} diff --git a/libgo/go/os/env_plan9.go b/libgo/go/os/env_plan9.go deleted file mode 100644 index 9757aa902af..00000000000 --- a/libgo/go/os/env_plan9.go +++ /dev/null @@ -1,99 +0,0 @@ -// 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. - -// Plan 9 environment variables. - -package os - -import ( - "errors" - "syscall" -) - -// ENOENV is the error indicating that an environment variable does not exist. -var ENOENV = errors.New("no such environment variable") - -// Getenverror retrieves the value of the environment variable named by the key. -// It returns the value and an error, if any. -func Getenverror(key string) (value string, err error) { - if len(key) == 0 { - return "", EINVAL - } - f, e := Open("/env/" + key) - if iserror(e) { - return "", ENOENV - } - defer f.Close() - - l, _ := f.Seek(0, 2) - f.Seek(0, 0) - buf := make([]byte, l) - n, e := f.Read(buf) - if iserror(e) { - return "", ENOENV - } - - if n > 0 && buf[n-1] == 0 { - buf = buf[:n-1] - } - return string(buf), nil -} - -// Getenv retrieves the value of the environment variable named by the key. -// It returns the value, which will be empty if the variable is not present. -func Getenv(key string) string { - v, _ := Getenverror(key) - return v -} - -// Setenv sets the value of the environment variable named by the key. -// It returns an error, if any. -func Setenv(key, value string) error { - if len(key) == 0 { - return EINVAL - } - - f, e := Create("/env/" + key) - if iserror(e) { - return e - } - defer f.Close() - - _, e = f.Write([]byte(value)) - return nil -} - -// Clearenv deletes all environment variables. -func Clearenv() { - syscall.RawSyscall(syscall.SYS_RFORK, syscall.RFCENVG, 0, 0) -} - -// Environ returns an array of strings representing the environment, -// in the form "key=value". -func Environ() []string { - env := make([]string, 0, 100) - - f, e := Open("/env") - if iserror(e) { - panic(e) - } - defer f.Close() - - names, e := f.Readdirnames(-1) - if iserror(e) { - panic(e) - } - - for _, k := range names { - if v, e := Getenverror(k); !iserror(e) { - env = append(env, k+"="+v) - } - } - return env[0:len(env)] -} - -// TempDir returns the default directory to use for temporary files. -func TempDir() string { - return "/tmp" -} diff --git a/libgo/go/os/env_unix.go b/libgo/go/os/env_unix.go deleted file mode 100644 index 01fd9d449f3..00000000000 --- a/libgo/go/os/env_unix.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin freebsd linux openbsd - -// Unix environment variables. - -package os - -import ( - "errors" - "sync" -) - -// ENOENV is the error indicating that an environment variable does not exist. -var ENOENV = errors.New("no such environment variable") - -var env map[string]string -var once sync.Once - -func copyenv() { - env = make(map[string]string) - for _, s := range Envs { - for j := 0; j < len(s); j++ { - if s[j] == '=' { - env[s[0:j]] = s[j+1:] - break - } - } - } -} - -var envLock sync.RWMutex - -// Getenverror retrieves the value of the environment variable named by the key. -// It returns the value and an error, if any. -func Getenverror(key string) (value string, err error) { - once.Do(copyenv) - - if len(key) == 0 { - return "", EINVAL - } - - envLock.RLock() - defer envLock.RUnlock() - - v, ok := env[key] - if !ok { - return "", ENOENV - } - return v, nil -} - -// Getenv retrieves the value of the environment variable named by the key. -// It returns the value, which will be empty if the variable is not present. -func Getenv(key string) string { - v, _ := Getenverror(key) - return v -} - -// Setenv sets the value of the environment variable named by the key. -// It returns an error, if any. -func Setenv(key, value string) error { - once.Do(copyenv) - if len(key) == 0 { - return EINVAL - } - - envLock.Lock() - defer envLock.Unlock() - - env[key] = value - setenv_c(key, value) // is a no-op if cgo isn't loaded - return nil -} - -// Clearenv deletes all environment variables. -func Clearenv() { - once.Do(copyenv) // prevent copyenv in Getenv/Setenv - - envLock.Lock() - defer envLock.Unlock() - - env = make(map[string]string) - - // TODO(bradfitz): pass through to C -} - -// Environ returns an array of strings representing the environment, -// in the form "key=value". -func Environ() []string { - once.Do(copyenv) - envLock.RLock() - defer envLock.RUnlock() - a := make([]string, len(env)) - i := 0 - for k, v := range env { - a[i] = k + "=" + v - i++ - } - return a -} - -// TempDir returns the default directory to use for temporary files. -func TempDir() string { - dir := Getenv("TMPDIR") - if dir == "" { - dir = "/tmp" - } - return dir -} diff --git a/libgo/go/os/env_windows.go b/libgo/go/os/env_windows.go deleted file mode 100644 index 4e90385da96..00000000000 --- a/libgo/go/os/env_windows.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2010 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. - -// Windows environment variables. - -package os - -import ( - "errors" - "syscall" - "unicode/utf16" - "unsafe" -) - -// ENOENV is the error indicating that an environment variable does not exist. -var ENOENV = errors.New("no such environment variable") - -// Getenverror retrieves the value of the environment variable named by the key. -// It returns the value and an error, if any. -func Getenverror(key string) (value string, err error) { - b := make([]uint16, 100) - n, e := syscall.GetEnvironmentVariable(syscall.StringToUTF16Ptr(key), &b[0], uint32(len(b))) - if n == 0 && e == syscall.ERROR_ENVVAR_NOT_FOUND { - return "", ENOENV - } - if n > uint32(len(b)) { - b = make([]uint16, n) - n, e = syscall.GetEnvironmentVariable(syscall.StringToUTF16Ptr(key), &b[0], uint32(len(b))) - if n > uint32(len(b)) { - n = 0 - } - } - if n == 0 { - return "", NewSyscallError("GetEnvironmentVariable", e) - } - return string(utf16.Decode(b[0:n])), nil -} - -// Getenv retrieves the value of the environment variable named by the key. -// It returns the value, which will be empty if the variable is not present. -func Getenv(key string) string { - v, _ := Getenverror(key) - return v -} - -// Setenv sets the value of the environment variable named by the key. -// It returns an error, if any. -func Setenv(key, value string) error { - var v *uint16 - if len(value) > 0 { - v = syscall.StringToUTF16Ptr(value) - } - e := syscall.SetEnvironmentVariable(syscall.StringToUTF16Ptr(key), v) - if e != 0 { - return NewSyscallError("SetEnvironmentVariable", e) - } - return nil -} - -// Clearenv deletes all environment variables. -func Clearenv() { - for _, s := range Environ() { - // Environment variables can begin with = - // so start looking for the separator = at j=1. - // http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx - for j := 1; j < len(s); j++ { - if s[j] == '=' { - Setenv(s[0:j], "") - break - } - } - } -} - -// Environ returns an array of strings representing the environment, -// in the form "key=value". -func Environ() []string { - s, e := syscall.GetEnvironmentStrings() - if e != 0 { - return nil - } - defer syscall.FreeEnvironmentStrings(s) - r := make([]string, 0, 50) // Empty with room to grow. - for from, i, p := 0, 0, (*[1 << 24]uint16)(unsafe.Pointer(s)); true; i++ { - if p[i] == 0 { - // empty string marks the end - if i <= from { - break - } - r = append(r, string(utf16.Decode(p[from:i]))) - from = i + 1 - } - } - return r -} - -// TempDir returns the default directory to use for temporary files. -func TempDir() string { - const pathSep = '\\' - dirw := make([]uint16, syscall.MAX_PATH) - n, _ := syscall.GetTempPath(uint32(len(dirw)), &dirw[0]) - if n > uint32(len(dirw)) { - dirw = make([]uint16, n) - n, _ = syscall.GetTempPath(uint32(len(dirw)), &dirw[0]) - if n > uint32(len(dirw)) { - n = 0 - } - } - if n > 0 && dirw[n-1] == pathSep { - n-- - } - return string(utf16.Decode(dirw[0:n])) -} - -func init() { - var argc int32 - cmd := syscall.GetCommandLine() - argv, e := syscall.CommandLineToArgv(cmd, &argc) - if e != 0 { - return - } - defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv)))) - Args = make([]string, argc) - for i, v := range (*argv)[:argc] { - Args[i] = string(syscall.UTF16ToString((*v)[:])) - } -} diff --git a/libgo/go/os/error_plan9.go b/libgo/go/os/error_plan9.go index e08707078ec..8f005efbe05 100644 --- a/libgo/go/os/error_plan9.go +++ b/libgo/go/os/error_plan9.go @@ -24,7 +24,7 @@ func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err } // NewSyscallError returns, as an error, a new SyscallError // with the given system call name and error details. // As a convenience, if err is nil, NewSyscallError returns nil. -func NewSyscallError(syscall string, err syscall.Error) error { +func NewSyscallError(syscall string, err error) error { if err == nil { return nil } @@ -57,9 +57,3 @@ var ( EPIPE = errors.New("Broken Pipe") EPLAN9 = errors.New("not supported by plan 9") ) - -func iserror(err syscall.Error) bool { - return err != nil -} - -func Errno(e syscall.Error) syscall.Error { return e } diff --git a/libgo/go/os/error_posix.go b/libgo/go/os/error_posix.go index c3d79425062..dbe1b9a8d5e 100644 --- a/libgo/go/os/error_posix.go +++ b/libgo/go/os/error_posix.go @@ -8,67 +8,53 @@ package os import syscall "syscall" -// Errno is the Unix error number. Names such as EINVAL are simple -// wrappers to convert the error number into an error. -type Errno int64 - -func (e Errno) Error() string { return syscall.Errstr(int(e)) } - -func (e Errno) Temporary() bool { - return e == Errno(syscall.EINTR) || e == Errno(syscall.EMFILE) || e.Timeout() -} - -func (e Errno) Timeout() bool { - return e == Errno(syscall.EAGAIN) || e == Errno(syscall.EWOULDBLOCK) || e == Errno(syscall.ETIMEDOUT) -} - // Commonly known Unix errors. var ( - EPERM error = Errno(syscall.EPERM) - ENOENT error = Errno(syscall.ENOENT) - ESRCH error = Errno(syscall.ESRCH) - EINTR error = Errno(syscall.EINTR) - EIO error = Errno(syscall.EIO) - ENXIO error = Errno(syscall.ENXIO) - E2BIG error = Errno(syscall.E2BIG) - ENOEXEC error = Errno(syscall.ENOEXEC) - EBADF error = Errno(syscall.EBADF) - ECHILD error = Errno(syscall.ECHILD) - EDEADLK error = Errno(syscall.EDEADLK) - ENOMEM error = Errno(syscall.ENOMEM) - EACCES error = Errno(syscall.EACCES) - EFAULT error = Errno(syscall.EFAULT) - EBUSY error = Errno(syscall.EBUSY) - EEXIST error = Errno(syscall.EEXIST) - EXDEV error = Errno(syscall.EXDEV) - ENODEV error = Errno(syscall.ENODEV) - ENOTDIR error = Errno(syscall.ENOTDIR) - EISDIR error = Errno(syscall.EISDIR) - EINVAL error = Errno(syscall.EINVAL) - ENFILE error = Errno(syscall.ENFILE) - EMFILE error = Errno(syscall.EMFILE) - ENOTTY error = Errno(syscall.ENOTTY) - EFBIG error = Errno(syscall.EFBIG) - ENOSPC error = Errno(syscall.ENOSPC) - ESPIPE error = Errno(syscall.ESPIPE) - EROFS error = Errno(syscall.EROFS) - EMLINK error = Errno(syscall.EMLINK) - EPIPE error = Errno(syscall.EPIPE) - EAGAIN error = Errno(syscall.EAGAIN) - EDOM error = Errno(syscall.EDOM) - ERANGE error = Errno(syscall.ERANGE) - EADDRINUSE error = Errno(syscall.EADDRINUSE) - ECONNREFUSED error = Errno(syscall.ECONNREFUSED) - ENAMETOOLONG error = Errno(syscall.ENAMETOOLONG) - EAFNOSUPPORT error = Errno(syscall.EAFNOSUPPORT) - ETIMEDOUT error = Errno(syscall.ETIMEDOUT) - ENOTCONN error = Errno(syscall.ENOTCONN) + EPERM error = syscall.EPERM + ENOENT error = syscall.ENOENT + ESRCH error = syscall.ESRCH + EINTR error = syscall.EINTR + EIO error = syscall.EIO + ENXIO error = syscall.ENXIO + E2BIG error = syscall.E2BIG + ENOEXEC error = syscall.ENOEXEC + EBADF error = syscall.EBADF + ECHILD error = syscall.ECHILD + EDEADLK error = syscall.EDEADLK + ENOMEM error = syscall.ENOMEM + EACCES error = syscall.EACCES + EFAULT error = syscall.EFAULT + EBUSY error = syscall.EBUSY + EEXIST error = syscall.EEXIST + EXDEV error = syscall.EXDEV + ENODEV error = syscall.ENODEV + ENOTDIR error = syscall.ENOTDIR + EISDIR error = syscall.EISDIR + EINVAL error = syscall.EINVAL + ENFILE error = syscall.ENFILE + EMFILE error = syscall.EMFILE + ENOTTY error = syscall.ENOTTY + EFBIG error = syscall.EFBIG + ENOSPC error = syscall.ENOSPC + ESPIPE error = syscall.ESPIPE + EROFS error = syscall.EROFS + EMLINK error = syscall.EMLINK + EPIPE error = syscall.EPIPE + EAGAIN error = syscall.EAGAIN + EDOM error = syscall.EDOM + ERANGE error = syscall.ERANGE + EADDRINUSE error = syscall.EADDRINUSE + ECONNREFUSED error = syscall.ECONNREFUSED + ENAMETOOLONG error = syscall.ENAMETOOLONG + EAFNOSUPPORT error = syscall.EAFNOSUPPORT + ETIMEDOUT error = syscall.ETIMEDOUT + ENOTCONN error = syscall.ENOTCONN ) // SyscallError records an error from a specific system call. type SyscallError struct { Syscall string - Errno Errno + Errno error } func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Errno.Error() } @@ -79,14 +65,10 @@ func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Errno.Error( // NewSyscallError returns, as an error, a new SyscallError // with the given system call name and error details. -// As a convenience, if errno is 0, NewSyscallError returns nil. -func NewSyscallError(syscall string, errno int) error { - if errno == 0 { +// As a convenience, if err is nil, NewSyscallError returns nil. +func NewSyscallError(syscall string, err error) error { + if err == nil { return nil } - return &SyscallError{syscall, Errno(errno)} -} - -func iserror(errno int) bool { - return errno != 0 + return &SyscallError{syscall, err} } diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go index ebdfd54a734..4c95c1b0dac 100644 --- a/libgo/go/os/exec/exec.go +++ b/libgo/go/os/exec/exec.go @@ -50,14 +50,14 @@ type Cmd struct { // calling process's current directory. Dir string - // Stdin specifies the process's standard input. - // If Stdin is nil, the process reads from DevNull. + // Stdin specifies the process's standard input. If Stdin is + // nil, the process reads from the null device (os.DevNull). Stdin io.Reader // Stdout and Stderr specify the process's standard output and error. // - // If either is nil, Run connects the - // corresponding file descriptor to /dev/null. + // If either is nil, Run connects the corresponding file descriptor + // to the null device (os.DevNull). // // If Stdout and Stderr are are the same writer, at most one // goroutine at a time will call Write. diff --git a/libgo/go/os/exec/lp_unix.go b/libgo/go/os/exec/lp_unix.go index d234641acc4..9665ea8f413 100644 --- a/libgo/go/os/exec/lp_unix.go +++ b/libgo/go/os/exec/lp_unix.go @@ -20,7 +20,7 @@ func findExecutable(file string) error { if err != nil { return err } - if d.IsRegular() && d.Permission()&0111 != 0 { + if m := d.Mode(); !m.IsDir() && m&0111 != 0 { return nil } return os.EPERM diff --git a/libgo/go/os/exec/lp_windows.go b/libgo/go/os/exec/lp_windows.go index db326236ee8..ef5bd921668 100644 --- a/libgo/go/os/exec/lp_windows.go +++ b/libgo/go/os/exec/lp_windows.go @@ -18,10 +18,10 @@ func chkStat(file string) error { if err != nil { return err } - if d.IsRegular() { - return nil + if d.IsDir() { + return os.EPERM } - return os.EPERM + return nil } func findExecutable(file string, exts []string) (string, error) { diff --git a/libgo/go/os/exec_plan9.go b/libgo/go/os/exec_plan9.go index a1a335359dc..9a0db6dd3ef 100644 --- a/libgo/go/os/exec_plan9.go +++ b/libgo/go/os/exec_plan9.go @@ -32,7 +32,7 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e sysattr.Files = intfd pid, h, e := syscall.StartProcess(name, argv, sysattr) - if iserror(e) { + if e != nil { return nil, &PathError{"fork/exec", name, e} } @@ -52,7 +52,7 @@ func (p *Process) Signal(sig Signal) error { } f, e := OpenFile("/proc/"+itoa(p.Pid)+"/note", O_WRONLY, 0) - if iserror(e) { + if e != nil { return NewSyscallError("signal", e) } defer f.Close() @@ -63,7 +63,7 @@ func (p *Process) Signal(sig Signal) error { // Kill causes the Process to exit immediately. func (p *Process) Kill() error { f, e := OpenFile("/proc/"+itoa(p.Pid)+"/ctl", O_WRONLY, 0) - if iserror(e) { + if e != nil { return NewSyscallError("kill", e) } defer f.Close() @@ -77,7 +77,7 @@ func (p *Process) Kill() error { // ForkExec is almost always a better way to execute a program. func Exec(name string, argv []string, envv []string) error { e := syscall.Exec(name, argv, envv) - if iserror(e) { + if e != nil { return &PathError{"exec", name, e} } @@ -102,7 +102,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) { for true { err = syscall.Await(&waitmsg) - if iserror(err) { + if err != nil { return nil, NewSyscallError("wait", err) } diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go index 12b44e5f33b..8b08eebd0da 100644 --- a/libgo/go/os/exec_posix.go +++ b/libgo/go/os/exec_posix.go @@ -40,8 +40,8 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e } pid, h, e := syscall.StartProcess(name, argv, sysattr) - if iserror(e) { - return nil, &PathError{"fork/exec", name, Errno(e)} + if e != nil { + return nil, &PathError{"fork/exec", name, e} } return newProcess(pid, h), nil } @@ -62,8 +62,8 @@ func Exec(name string, argv []string, envv []string) error { envv = Environ() } e := syscall.Exec(name, argv, envv) - if iserror(e) { - return &PathError{"exec", name, Errno(e)} + if e != nil { + return &PathError{"exec", name, e} } return nil } diff --git a/libgo/go/os/exec_unix.go b/libgo/go/os/exec_unix.go index 242bda702c6..3dcac414c5a 100644 --- a/libgo/go/os/exec_unix.go +++ b/libgo/go/os/exec_unix.go @@ -38,7 +38,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) { options ^= WRUSAGE } pid1, e := syscall.Wait4(p.Pid, &status, options, rusage) - if e != 0 { + if e != nil { return nil, NewSyscallError("wait", e) } // With WNOHANG pid is 0 if child has not exited. @@ -57,8 +57,8 @@ func (p *Process) Signal(sig Signal) error { if p.done { return errors.New("os: process already finished") } - if e := syscall.Kill(p.Pid, int(sig.(UnixSignal))); e != 0 { - return Errno(e) + if e := syscall.Kill(p.Pid, int(sig.(UnixSignal))); e != nil { + return e } return nil } diff --git a/libgo/go/os/exec_windows.go b/libgo/go/os/exec_windows.go index 866757e3129..c4c9dcfe829 100644 --- a/libgo/go/os/exec_windows.go +++ b/libgo/go/os/exec_windows.go @@ -8,6 +8,7 @@ import ( "errors" "runtime" "syscall" + "unsafe" ) func (p *Process) Wait(options int) (w *Waitmsg, err error) { @@ -22,7 +23,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) { } var ec uint32 e = syscall.GetExitCodeProcess(syscall.Handle(p.handle), &ec) - if e != 0 { + if e != nil { return nil, NewSyscallError("GetExitCodeProcess", e) } p.done = true @@ -39,7 +40,7 @@ func (p *Process) Signal(sig Signal) error { e := syscall.TerminateProcess(syscall.Handle(p.handle), 1) return NewSyscallError("TerminateProcess", e) } - return Errno(syscall.EWINDOWS) + return syscall.Errno(syscall.EWINDOWS) } func (p *Process) Release() error { @@ -47,7 +48,7 @@ func (p *Process) Release() error { return EINVAL } e := syscall.CloseHandle(syscall.Handle(p.handle)) - if e != 0 { + if e != nil { return NewSyscallError("CloseHandle", e) } p.handle = -1 @@ -60,8 +61,22 @@ func FindProcess(pid int) (p *Process, err error) { const da = syscall.STANDARD_RIGHTS_READ | syscall.PROCESS_QUERY_INFORMATION | syscall.SYNCHRONIZE h, e := syscall.OpenProcess(da, false, uint32(pid)) - if e != 0 { + if e != nil { return nil, NewSyscallError("OpenProcess", e) } return newProcess(pid, int(h)), nil } + +func init() { + var argc int32 + cmd := syscall.GetCommandLine() + argv, e := syscall.CommandLineToArgv(cmd, &argc) + if e != nil { + return + } + defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv)))) + Args = make([]string, argc) + for i, v := range (*argv)[:argc] { + Args[i] = string(syscall.UTF16ToString((*v)[:])) + } +} diff --git a/libgo/go/os/export_test.go b/libgo/go/os/export_test.go new file mode 100644 index 00000000000..9c6ef429744 --- /dev/null +++ b/libgo/go/os/export_test.go @@ -0,0 +1,9 @@ +// 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 os + +// Export for testing. + +var Atime = atime diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go index 0f3b2db7ea2..71845d3c9c9 100644 --- a/libgo/go/os/file.go +++ b/libgo/go/os/file.go @@ -14,7 +14,7 @@ import ( ) // Name returns the name of the file as presented to Open. -func (file *File) Name() string { return file.name } +func (f *File) Name() string { return f.name } // Stdin, Stdout, and Stderr are open Files pointing to the standard input, // standard output, and standard error file descriptors. @@ -51,38 +51,38 @@ const ( // Read reads up to len(b) bytes from the File. // It returns the number of bytes read and an error, if any. // EOF is signaled by a zero count with err set to io.EOF. -func (file *File) Read(b []byte) (n int, err error) { - if file == nil { +func (f *File) Read(b []byte) (n int, err error) { + if f == nil { return 0, EINVAL } - n, e := file.read(b) + n, e := f.read(b) if n < 0 { n = 0 } - if n == 0 && len(b) > 0 && !iserror(e) { + if n == 0 && len(b) > 0 && e == nil { return 0, io.EOF } - if iserror(e) { - err = &PathError{"read", file.name, Errno(e)} + if e != nil { + err = &PathError{"read", f.name, e} } return n, err } // ReadAt reads len(b) bytes from the File starting at byte offset off. // It returns the number of bytes read and the error, if any. -// EOF is signaled by a zero count with err set to io.EOF. -// ReadAt always returns a non-nil error when n != len(b). -func (file *File) ReadAt(b []byte, off int64) (n int, err error) { - if file == nil { +// ReadAt always returns a non-nil error when n < len(b). +// At end of file, that error is io.EOF. +func (f *File) ReadAt(b []byte, off int64) (n int, err error) { + if f == nil { return 0, EINVAL } for len(b) > 0 { - m, e := file.pread(b, off) - if m == 0 && !iserror(e) { + m, e := f.pread(b, off) + if m == 0 && e == nil { return n, io.EOF } - if iserror(e) { - err = &PathError{"read", file.name, Errno(e)} + if e != nil { + err = &PathError{"read", f.name, e} break } n += m @@ -95,19 +95,19 @@ func (file *File) ReadAt(b []byte, off int64) (n int, err error) { // Write writes len(b) bytes to the File. // It returns the number of bytes written and an error, if any. // Write returns a non-nil error when n != len(b). -func (file *File) Write(b []byte) (n int, err error) { - if file == nil { +func (f *File) Write(b []byte) (n int, err error) { + if f == nil { return 0, EINVAL } - n, e := file.write(b) + n, e := f.write(b) if n < 0 { n = 0 } - epipecheck(file, e) + epipecheck(f, e) - if iserror(e) { - err = &PathError{"write", file.name, Errno(e)} + if e != nil { + err = &PathError{"write", f.name, e} } return n, err } @@ -115,14 +115,14 @@ func (file *File) Write(b []byte) (n int, err error) { // WriteAt writes len(b) bytes to the File starting at byte offset off. // It returns the number of bytes written and an error, if any. // WriteAt returns a non-nil error when n != len(b). -func (file *File) WriteAt(b []byte, off int64) (n int, err error) { - if file == nil { +func (f *File) WriteAt(b []byte, off int64) (n int, err error) { + if f == nil { return 0, EINVAL } for len(b) > 0 { - m, e := file.pwrite(b, off) - if iserror(e) { - err = &PathError{"write", file.name, Errno(e)} + m, e := f.pwrite(b, off) + if e != nil { + err = &PathError{"write", f.name, e} break } n += m @@ -136,40 +136,40 @@ func (file *File) WriteAt(b []byte, off int64) (n int, err error) { // according to whence: 0 means relative to the origin of the file, 1 means // relative to the current offset, and 2 means relative to the end. // It returns the new offset and an error, if any. -func (file *File) Seek(offset int64, whence int) (ret int64, err error) { - r, e := file.seek(offset, whence) - if !iserror(e) && file.dirinfo != nil && r != 0 { +func (f *File) Seek(offset int64, whence int) (ret int64, err error) { + r, e := f.seek(offset, whence) + if e == nil && f.dirinfo != nil && r != 0 { e = syscall.EISDIR } - if iserror(e) { - return 0, &PathError{"seek", file.name, Errno(e)} + if e != nil { + return 0, &PathError{"seek", f.name, e} } return r, nil } // WriteString is like Write, but writes the contents of string s rather than // an array of bytes. -func (file *File) WriteString(s string) (ret int, err error) { - if file == nil { +func (f *File) WriteString(s string) (ret int, err error) { + if f == nil { return 0, EINVAL } - return file.Write([]byte(s)) + return f.Write([]byte(s)) } // Mkdir creates a new directory with the specified name and permission bits. // It returns an error, if any. func Mkdir(name string, perm uint32) error { e := syscall.Mkdir(name, perm) - if iserror(e) { - return &PathError{"mkdir", name, Errno(e)} + if e != nil { + return &PathError{"mkdir", name, e} } return nil } // Chdir changes the current working directory to the named directory. func Chdir(dir string) error { - if e := syscall.Chdir(dir); iserror(e) { - return &PathError{"chdir", dir, Errno(e)} + if e := syscall.Chdir(dir); e != nil { + return &PathError{"chdir", dir, e} } return nil } @@ -177,8 +177,8 @@ func Chdir(dir string) error { // Chdir changes the current working directory to the file, // which must be a directory. func (f *File) Chdir() error { - if e := syscall.Fchdir(f.fd); iserror(e) { - return &PathError{"chdir", f.name, Errno(e)} + if e := syscall.Fchdir(f.fd); e != nil { + return &PathError{"chdir", f.name, e} } return nil } diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go index 42332e157ef..42fefa96fe8 100644 --- a/libgo/go/os/file_plan9.go +++ b/libgo/go/os/file_plan9.go @@ -11,6 +11,14 @@ import ( // File represents an open file descriptor. type File struct { + *file +} + +// file is the real representation of *File. +// The extra level of indirection ensures that no clients of os +// can overwrite this data, which could cause the finalizer +// to close the wrong file descriptor. +type file struct { fd int name string dirinfo *dirInfo // nil unless directory being read @@ -29,8 +37,8 @@ func NewFile(fd int, name string) *File { if fd < 0 { return nil } - f := &File{fd: fd, name: name} - runtime.SetFinalizer(f, (*File).Close) + f := &File{&file{fd: fd, name: name}} + runtime.SetFinalizer(f.file, (*file).close) return f } @@ -41,7 +49,7 @@ type dirInfo struct { bufp int // location of next record in buf. } -func epipecheck(file *File, e syscall.Error) { +func epipecheck(file *File, e error) { } // DevNull is the name of the operating system's ``null device.'' @@ -56,7 +64,7 @@ const DevNull = "/dev/null" func OpenFile(name string, flag int, perm uint32) (file *File, err error) { var ( fd int - e syscall.Error + e error create bool excl bool trunc bool @@ -85,7 +93,7 @@ func OpenFile(name string, flag int, perm uint32) (file *File, err error) { } else { fd, e = syscall.Open(name, flag) if e != nil && create { - var e1 syscall.Error + var e1 error fd, e1 = syscall.Create(name, flag, perm) if e1 == nil { e = nil @@ -110,6 +118,10 @@ func OpenFile(name string, flag int, perm uint32) (file *File, err error) { // Close closes the File, rendering it unusable for I/O. // It returns an error, if any. func (file *File) Close() error { + return file.file.close() +} + +func (file *file) close() error { if file == nil || file.fd < 0 { return Ebadfd } @@ -130,7 +142,7 @@ func (file *File) Close() error { // It returns the FileInfo and an error, if any. func (f *File) Stat() (fi *FileInfo, err error) { d, err := dirstat(f) - if iserror(err) { + if err != nil { return nil, err } return fileInfoFromStat(new(FileInfo), d), err @@ -144,7 +156,7 @@ func (f *File) Truncate(size int64) error { d.Length = uint64(size) - if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) { + if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil { return &PathError{"truncate", f.name, e} } return nil @@ -157,12 +169,12 @@ func (f *File) Chmod(mode uint32) error { d.Null() odir, e := dirstat(f) - if iserror(e) { + if e != nil { return &PathError{"chmod", f.name, e} } d.Mode = (odir.Mode & mask) | (mode &^ mask) - if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) { + if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil { return &PathError{"chmod", f.name, e} } return nil @@ -179,7 +191,7 @@ func (f *File) Sync() (err error) { var d Dir d.Null() - if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) { + if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil { return NewSyscallError("fsync", e) } return nil @@ -187,26 +199,26 @@ func (f *File) Sync() (err error) { // read reads up to len(b) bytes from the File. // It returns the number of bytes read and an error, if any. -func (f *File) read(b []byte) (n int, err syscall.Error) { +func (f *File) read(b []byte) (n int, err error) { return syscall.Read(f.fd, b) } // pread reads len(b) bytes from the File starting at byte offset off. // It returns the number of bytes read and the error, if any. // EOF is signaled by a zero count with err set to nil. -func (f *File) pread(b []byte, off int64) (n int, err syscall.Error) { +func (f *File) pread(b []byte, off int64) (n int, err error) { return syscall.Pread(f.fd, b, off) } // write writes len(b) bytes to the File. // It returns the number of bytes written and an error, if any. -func (f *File) write(b []byte) (n int, err syscall.Error) { +func (f *File) write(b []byte) (n int, err error) { return syscall.Write(f.fd, b) } // pwrite writes len(b) bytes to the File starting at byte offset off. // It returns the number of bytes written and an error, if any. -func (f *File) pwrite(b []byte, off int64) (n int, err syscall.Error) { +func (f *File) pwrite(b []byte, off int64) (n int, err error) { return syscall.Pwrite(f.fd, b, off) } @@ -214,7 +226,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err syscall.Error) { // according to whence: 0 means relative to the origin of the file, 1 means // relative to the current offset, and 2 means relative to the end. // It returns the new offset and an error, if any. -func (f *File) seek(offset int64, whence int) (ret int64, err syscall.Error) { +func (f *File) seek(offset int64, whence int) (ret int64, err error) { return syscall.Seek(f.fd, offset, whence) } @@ -226,7 +238,7 @@ func Truncate(name string, size int64) error { d.Length = uint64(size) - if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) { + if e := syscall.Wstat(name, pdir(nil, &d)); e != nil { return &PathError{"truncate", name, e} } return nil @@ -234,7 +246,7 @@ func Truncate(name string, size int64) error { // Remove removes the named file or directory. func Remove(name string) error { - if e := syscall.Remove(name); iserror(e) { + if e := syscall.Remove(name); e != nil { return &PathError{"remove", name, e} } return nil @@ -247,7 +259,7 @@ func Rename(oldname, newname string) error { d.Name = newname - if e := syscall.Wstat(oldname, pdir(nil, &d)); iserror(e) { + if e := syscall.Wstat(oldname, pdir(nil, &d)); e != nil { return &PathError{"rename", oldname, e} } return nil @@ -260,12 +272,12 @@ func Chmod(name string, mode uint32) error { d.Null() odir, e := dirstat(name) - if iserror(e) { + if e != nil { return &PathError{"chmod", name, e} } d.Mode = (odir.Mode & mask) | (mode &^ mask) - if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) { + if e := syscall.Wstat(name, pdir(nil, &d)); e != nil { return &PathError{"chmod", name, e} } return nil @@ -284,7 +296,7 @@ func Chtimes(name string, atimeNs int64, mtimeNs int64) error { d.Atime = uint32(atimeNs / 1e9) d.Mtime = uint32(mtimeNs / 1e9) - if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) { + if e := syscall.Wstat(name, pdir(nil, &d)); e != nil { return &PathError{"chtimes", name, e} } return nil @@ -294,7 +306,7 @@ func Pipe() (r *File, w *File, err error) { var p [2]int syscall.ForkLock.RLock() - if e := syscall.Pipe(p[0:]); iserror(e) { + if e := syscall.Pipe(p[0:]); e != nil { syscall.ForkLock.RUnlock() return nil, nil, NewSyscallError("pipe", e) } @@ -329,3 +341,8 @@ func Lchown(name string, uid, gid int) error { func (f *File) Chown(uid, gid int) error { return EPLAN9 } + +// TempDir returns the default directory to use for temporary files. +func TempDir() string { + return "/tmp" +} diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go index c937b6d6266..a4ab5d6ae25 100644 --- a/libgo/go/os/file_posix.go +++ b/libgo/go/os/file_posix.go @@ -8,11 +8,12 @@ package os import ( "syscall" + "time" ) func sigpipe() // implemented in package runtime -func epipecheck(file *File, e int) { +func epipecheck(file *File, e error) { if e == syscall.EPIPE { file.nepipe++ if file.nepipe >= 10 { @@ -30,11 +31,11 @@ func Remove(name string) error { // Try both: it is cheaper on average than // doing a Stat plus the right one. e := syscall.Unlink(name) - if !iserror(e) { + if e == nil { return nil } e1 := syscall.Rmdir(name) - if !iserror(e1) { + if e1 == nil { return nil } @@ -53,7 +54,7 @@ func Remove(name string) error { if e1 != syscall.ENOTDIR { e = e1 } - return &PathError{"remove", name, Errno(e)} + return &PathError{"remove", name, e} } // LinkError records an error during a link or symlink or rename @@ -72,8 +73,8 @@ func (e *LinkError) Error() string { // Link creates a hard link. func Link(oldname, newname string) error { e := syscall.Link(oldname, newname) - if iserror(e) { - return &LinkError{"link", oldname, newname, Errno(e)} + if e != nil { + return &LinkError{"link", oldname, newname, e} } return nil } @@ -81,8 +82,8 @@ func Link(oldname, newname string) error { // Symlink creates a symbolic link. func Symlink(oldname, newname string) error { e := syscall.Symlink(oldname, newname) - if iserror(e) { - return &LinkError{"symlink", oldname, newname, Errno(e)} + if e != nil { + return &LinkError{"symlink", oldname, newname, e} } return nil } @@ -93,8 +94,8 @@ func Readlink(name string) (string, error) { for len := 128; ; len *= 2 { b := make([]byte, len) n, e := syscall.Readlink(name, b) - if iserror(e) { - return "", &PathError{"readlink", name, Errno(e)} + if e != nil { + return "", &PathError{"readlink", name, e} } if n < len { return string(b[0:n]), nil @@ -107,8 +108,8 @@ func Readlink(name string) (string, error) { // Rename renames a file. func Rename(oldname, newname string) error { e := syscall.Rename(oldname, newname) - if iserror(e) { - return &LinkError{"rename", oldname, newname, Errno(e)} + if e != nil { + return &LinkError{"rename", oldname, newname, e} } return nil } @@ -116,16 +117,16 @@ func Rename(oldname, newname string) error { // Chmod changes the mode of the named file to mode. // If the file is a symbolic link, it changes the mode of the link's target. func Chmod(name string, mode uint32) error { - if e := syscall.Chmod(name, mode); iserror(e) { - return &PathError{"chmod", name, Errno(e)} + if e := syscall.Chmod(name, mode); e != nil { + return &PathError{"chmod", name, e} } return nil } // Chmod changes the mode of the file to mode. func (f *File) Chmod(mode uint32) error { - if e := syscall.Fchmod(f.fd, mode); iserror(e) { - return &PathError{"chmod", f.name, Errno(e)} + if e := syscall.Fchmod(f.fd, mode); e != nil { + return &PathError{"chmod", f.name, e} } return nil } @@ -133,8 +134,8 @@ func (f *File) Chmod(mode uint32) error { // Chown changes the numeric uid and gid of the named file. // If the file is a symbolic link, it changes the uid and gid of the link's target. func Chown(name string, uid, gid int) error { - if e := syscall.Chown(name, uid, gid); iserror(e) { - return &PathError{"chown", name, Errno(e)} + if e := syscall.Chown(name, uid, gid); e != nil { + return &PathError{"chown", name, e} } return nil } @@ -142,16 +143,16 @@ func Chown(name string, uid, gid int) error { // Lchown changes the numeric uid and gid of the named file. // If the file is a symbolic link, it changes the uid and gid of the link itself. func Lchown(name string, uid, gid int) error { - if e := syscall.Lchown(name, uid, gid); iserror(e) { - return &PathError{"lchown", name, Errno(e)} + if e := syscall.Lchown(name, uid, gid); e != nil { + return &PathError{"lchown", name, e} } return nil } // Chown changes the numeric uid and gid of the named file. func (f *File) Chown(uid, gid int) error { - if e := syscall.Fchown(f.fd, uid, gid); iserror(e) { - return &PathError{"chown", f.name, Errno(e)} + if e := syscall.Fchown(f.fd, uid, gid); e != nil { + return &PathError{"chown", f.name, e} } return nil } @@ -159,8 +160,8 @@ func (f *File) Chown(uid, gid int) error { // Truncate changes the size of the file. // It does not change the I/O offset. func (f *File) Truncate(size int64) error { - if e := syscall.Ftruncate(f.fd, size); iserror(e) { - return &PathError{"truncate", f.name, Errno(e)} + if e := syscall.Ftruncate(f.fd, size); e != nil { + return &PathError{"truncate", f.name, e} } return nil } @@ -168,11 +169,11 @@ func (f *File) Truncate(size int64) error { // Sync commits the current contents of the file to stable storage. // Typically, this means flushing the file system's in-memory copy // of recently written data to disk. -func (file *File) Sync() (err error) { - if file == nil { +func (f *File) Sync() (err error) { + if f == nil { return EINVAL } - if e := syscall.Fsync(file.fd); iserror(e) { + if e := syscall.Fsync(f.fd); e != nil { return NewSyscallError("fsync", e) } return nil @@ -181,15 +182,16 @@ func (file *File) Sync() (err error) { // Chtimes changes the access and modification times of the named // file, similar to the Unix utime() or utimes() functions. // -// The argument times are in nanoseconds, although the underlying -// filesystem may truncate or round the values to a more -// coarse time unit. -func Chtimes(name string, atime_ns int64, mtime_ns int64) error { +// The underlying filesystem may truncate or round the values to a +// less precise time unit. +func Chtimes(name string, atime time.Time, mtime time.Time) error { var utimes [2]syscall.Timeval + atime_ns := atime.Unix()*1e9 + int64(atime.Nanosecond()) + mtime_ns := mtime.Unix()*1e9 + int64(mtime.Nanosecond()) utimes[0] = syscall.NsecToTimeval(atime_ns) utimes[1] = syscall.NsecToTimeval(mtime_ns) - if e := syscall.Utimes(name, utimes[0:]); iserror(e) { - return &PathError{"chtimes", name, Errno(e)} + if e := syscall.Utimes(name, utimes[0:]); e != nil { + return &PathError{"chtimes", name, e} } return nil } diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go index a7ccb337eb5..671d1a4cb62 100644 --- a/libgo/go/os/file_unix.go +++ b/libgo/go/os/file_unix.go @@ -13,6 +13,14 @@ import ( // File represents an open file descriptor. type File struct { + *file +} + +// file is the real representation of *File. +// The extra level of indirection ensures that no clients of os +// can overwrite this data, which could cause the finalizer +// to close the wrong file descriptor. +type file struct { fd int name string dirinfo *dirInfo // nil unless directory being read @@ -20,11 +28,11 @@ type File struct { } // Fd returns the integer Unix file descriptor referencing the open file. -func (file *File) Fd() int { - if file == nil { +func (f *File) Fd() int { + if f == nil { return -1 } - return file.fd + return f.fd } // NewFile returns a new File with the given file descriptor and name. @@ -32,8 +40,8 @@ func NewFile(fd int, name string) *File { if fd < 0 { return nil } - f := &File{fd: fd, name: name} - runtime.SetFinalizer(f, (*File).Close) + f := &File{&file{fd: fd, name: name}} + runtime.SetFinalizer(f.file, (*file).close) return f } @@ -54,8 +62,8 @@ const DevNull = "/dev/null" // It returns the File and an error, if any. func OpenFile(name string, flag int, perm uint32) (file *File, err error) { r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, perm) - if e != 0 { - return nil, &PathError{"open", name, Errno(e)} + if e != nil { + return nil, &PathError{"open", name, e} } // There's a race here with fork/exec, which we are @@ -69,18 +77,22 @@ func OpenFile(name string, flag int, perm uint32) (file *File, err error) { // Close closes the File, rendering it unusable for I/O. // It returns an error, if any. -func (file *File) Close() error { +func (f *File) Close() error { + return f.file.close() +} + +func (file *file) close() error { if file == nil || file.fd < 0 { return EINVAL } var err error - if e := syscall.Close(file.fd); e != 0 { - err = &PathError{"close", file.name, Errno(e)} + if e := syscall.Close(file.fd); e != nil { + err = &PathError{"close", file.name, e} } if file.dirinfo != nil { if libc_closedir(file.dirinfo.dir) < 0 && err == nil { - err = &PathError{"closedir", file.name, Errno(syscall.GetErrno())} + err = &PathError{"closedir", file.name, syscall.GetErrno()} } } @@ -93,50 +105,43 @@ func (file *File) Close() error { // Stat returns the FileInfo structure describing file. // It returns the FileInfo and an error, if any. -func (file *File) Stat() (fi *FileInfo, err error) { +func (f *File) Stat() (fi FileInfo, err error) { var stat syscall.Stat_t - e := syscall.Fstat(file.fd, &stat) - if e != 0 { - return nil, &PathError{"stat", file.name, Errno(e)} + err = syscall.Fstat(f.fd, &stat) + if err != nil { + return nil, &PathError{"stat", f.name, err} } - return fileInfoFromStat(file.name, new(FileInfo), &stat, &stat), nil + return fileInfoFromStat(&stat, f.name), nil } -// Stat returns a FileInfo structure describing the named file and an error, if any. +// Stat returns a FileInfo describing the named file and an error, if any. // If name names a valid symbolic link, the returned FileInfo describes // the file pointed at by the link and has fi.FollowedSymlink set to true. // If name names an invalid symbolic link, the returned FileInfo describes // the link itself and has fi.FollowedSymlink set to false. -func Stat(name string) (fi *FileInfo, err error) { - var lstat, stat syscall.Stat_t - e := syscall.Lstat(name, &lstat) - if iserror(e) { - return nil, &PathError{"stat", name, Errno(e)} - } - statp := &lstat - if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK { - e := syscall.Stat(name, &stat) - if !iserror(e) { - statp = &stat - } +func Stat(name string) (fi FileInfo, err error) { + var stat syscall.Stat_t + err = syscall.Stat(name, &stat) + if err != nil { + return nil, &PathError{"stat", name, err} } - return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil + return fileInfoFromStat(&stat, name), nil } -// Lstat returns the FileInfo structure describing the named file and an +// Lstat returns a FileInfo describing the named file and an // error, if any. If the file is a symbolic link, the returned FileInfo // describes the symbolic link. Lstat makes no attempt to follow the link. -func Lstat(name string) (fi *FileInfo, err error) { +func Lstat(name string) (fi FileInfo, err error) { var stat syscall.Stat_t - e := syscall.Lstat(name, &stat) - if iserror(e) { - return nil, &PathError{"lstat", name, Errno(e)} + err = syscall.Lstat(name, &stat) + if err != nil { + return nil, &PathError{"lstat", name, err} } - return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil + return fileInfoFromStat(&stat, name), nil } // Readdir reads the contents of the directory associated with file and -// returns an array of up to n FileInfo structures, as would be returned +// returns an array of up to n FileInfo values, as would be returned // by Lstat, in directory order. Subsequent calls on the same file will yield // further FileInfos. // @@ -150,47 +155,47 @@ func Lstat(name string) (fi *FileInfo, err error) { // nil error. If it encounters an error before the end of the // directory, Readdir returns the FileInfo read until that point // and a non-nil error. -func (file *File) Readdir(n int) (fi []FileInfo, err error) { - dirname := file.name +func (f *File) Readdir(n int) (fi []FileInfo, err error) { + dirname := f.name if dirname == "" { dirname = "." } dirname += "/" - names, err := file.Readdirnames(n) + names, err := f.Readdirnames(n) fi = make([]FileInfo, len(names)) for i, filename := range names { fip, err := Lstat(dirname + filename) - if fip == nil || err != nil { - fi[i].Name = filename // rest is already zeroed out + if err == nil { + fi[i] = fip } else { - fi[i] = *fip + fi[i] = &FileStat{name: filename} } } - return + return fi, err } // read reads up to len(b) bytes from the File. // It returns the number of bytes read and an error, if any. -func (f *File) read(b []byte) (n int, err int) { +func (f *File) read(b []byte) (n int, err error) { return syscall.Read(f.fd, b) } // pread reads len(b) bytes from the File starting at byte offset off. // It returns the number of bytes read and the error, if any. // EOF is signaled by a zero count with err set to 0. -func (f *File) pread(b []byte, off int64) (n int, err int) { +func (f *File) pread(b []byte, off int64) (n int, err error) { return syscall.Pread(f.fd, b, off) } // write writes len(b) bytes to the File. // It returns the number of bytes written and an error, if any. -func (f *File) write(b []byte) (n int, err int) { +func (f *File) write(b []byte) (n int, err error) { return syscall.Write(f.fd, b) } // pwrite writes len(b) bytes to the File starting at byte offset off. // It returns the number of bytes written and an error, if any. -func (f *File) pwrite(b []byte, off int64) (n int, err int) { +func (f *File) pwrite(b []byte, off int64) (n int, err error) { return syscall.Pwrite(f.fd, b, off) } @@ -198,15 +203,15 @@ func (f *File) pwrite(b []byte, off int64) (n int, err int) { // according to whence: 0 means relative to the origin of the file, 1 means // relative to the current offset, and 2 means relative to the end. // It returns the new offset and an error, if any. -func (f *File) seek(offset int64, whence int) (ret int64, err int) { +func (f *File) seek(offset int64, whence int) (ret int64, err error) { return syscall.Seek(f.fd, offset, whence) } // Truncate changes the size of the named file. // If the file is a symbolic link, it changes the size of the link's target. func Truncate(name string, size int64) error { - if e := syscall.Truncate(name, size); e != 0 { - return &PathError{"truncate", name, Errno(e)} + if e := syscall.Truncate(name, size); e != nil { + return &PathError{"truncate", name, e} } return nil } @@ -237,7 +242,7 @@ func Pipe() (r *File, w *File, err error) { // See ../syscall/exec.go for description of lock. syscall.ForkLock.RLock() e := syscall.Pipe(p[0:]) - if iserror(e) { + if e != nil { syscall.ForkLock.RUnlock() return nil, nil, NewSyscallError("pipe", e) } @@ -247,3 +252,12 @@ func Pipe() (r *File, w *File, err error) { return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil } + +// TempDir returns the default directory to use for temporary files. +func TempDir() string { + dir := Getenv("TMPDIR") + if dir == "" { + dir = "/tmp" + } + return dir +} diff --git a/libgo/go/os/getwd.go b/libgo/go/os/getwd.go index d5f5ae6bed7..a0d3c99a503 100644 --- a/libgo/go/os/getwd.go +++ b/libgo/go/os/getwd.go @@ -12,7 +12,7 @@ import ( // current directory. If the current directory can be // reached via multiple paths (due to symbolic links), // Getwd may return any one of them. -func Getwd() (string, error) { +func Getwd() (pwd string, err error) { // If the operating system provides a Getwd call, use it. if syscall.ImplementsGetwd { s, e := syscall.Getwd() @@ -27,10 +27,10 @@ func Getwd() (string, error) { // Clumsy but widespread kludge: // if $PWD is set and matches ".", use it. - pwd := Getenv("PWD") + pwd = Getenv("PWD") if len(pwd) > 0 && pwd[0] == '/' { d, err := Stat(pwd) - if err == nil && d.Dev == dot.Dev && d.Ino == dot.Ino { + if err == nil && dot.(*FileStat).SameFile(d.(*FileStat)) { return pwd, nil } } @@ -42,7 +42,7 @@ func Getwd() (string, error) { // Can't stat root - no hope. return "", err } - if root.Dev == dot.Dev && root.Ino == dot.Ino { + if root.(*FileStat).SameFile(dot.(*FileStat)) { return "/", nil } @@ -67,7 +67,7 @@ func Getwd() (string, error) { } for _, name := range names { d, _ := Lstat(parent + "/" + name) - if d.Dev == dot.Dev && d.Ino == dot.Ino { + if d.(*FileStat).SameFile(dot.(*FileStat)) { pwd = "/" + name + pwd goto Found } @@ -82,7 +82,7 @@ func Getwd() (string, error) { return "", err } fd.Close() - if pd.Dev == root.Dev && pd.Ino == root.Ino { + if pd.(*FileStat).SameFile(root.(*FileStat)) { break } // Set up for next round. diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go index 1d6d872d5c7..299d2e86155 100644 --- a/libgo/go/os/os_test.go +++ b/libgo/go/os/os_test.go @@ -14,11 +14,12 @@ import ( "strings" "syscall" "testing" + "time" ) var dot = []string{ "dir_unix.go", - "env_unix.go", + "env.go", "error.go", "file.go", "os_test.go", @@ -119,12 +120,12 @@ func TestStat(t *testing.T) { if err != nil { t.Fatal("stat failed:", err) } - if !equal(sfname, dir.Name) { - t.Error("name should be ", sfname, "; is", dir.Name) + if !equal(sfname, dir.Name()) { + t.Error("name should be ", sfname, "; is", dir.Name()) } filesize := size(path, t) - if dir.Size != filesize { - t.Error("size should be", filesize, "; is", dir.Size) + if dir.Size() != filesize { + t.Error("size should be", filesize, "; is", dir.Size()) } } @@ -139,12 +140,12 @@ func TestFstat(t *testing.T) { if err2 != nil { t.Fatal("fstat failed:", err2) } - if !equal(sfname, dir.Name) { - t.Error("name should be ", sfname, "; is", dir.Name) + if !equal(sfname, dir.Name()) { + t.Error("name should be ", sfname, "; is", dir.Name()) } filesize := size(path, t) - if dir.Size != filesize { - t.Error("size should be", filesize, "; is", dir.Size) + if dir.Size() != filesize { + t.Error("size should be", filesize, "; is", dir.Size()) } } @@ -154,12 +155,12 @@ func TestLstat(t *testing.T) { if err != nil { t.Fatal("lstat failed:", err) } - if !equal(sfname, dir.Name) { - t.Error("name should be ", sfname, "; is", dir.Name) + if !equal(sfname, dir.Name()) { + t.Error("name should be ", sfname, "; is", dir.Name()) } filesize := size(path, t) - if dir.Size != filesize { - t.Error("size should be", filesize, "; is", dir.Size) + if dir.Size() != filesize { + t.Error("size should be", filesize, "; is", dir.Size()) } } @@ -226,7 +227,7 @@ func testReaddir(dir string, contents []string, t *testing.T) { for _, m := range contents { found := false for _, n := range s { - if equal(m, n.Name) { + if equal(m, n.Name()) { if found { t.Error("present twice:", m) } @@ -405,7 +406,7 @@ func TestHardLink(t *testing.T) { if err != nil { t.Fatalf("stat %q failed: %v", from, err) } - if tostat.Dev != fromstat.Dev || tostat.Ino != fromstat.Ino { + if !tostat.(*FileStat).SameFile(fromstat.(*FileStat)) { t.Errorf("link %q, %q did not create hard link", to, from) } } @@ -430,32 +431,32 @@ func TestSymLink(t *testing.T) { t.Fatalf("symlink %q, %q failed: %v", to, from, err) } defer Remove(from) - tostat, err := Stat(to) + tostat, err := Lstat(to) if err != nil { t.Fatalf("stat %q failed: %v", to, err) } - if tostat.FollowedSymlink { - t.Fatalf("stat %q claims to have followed a symlink", to) + if tostat.Mode()&ModeSymlink != 0 { + t.Fatalf("stat %q claims to have found a symlink", to) } fromstat, err := Stat(from) if err != nil { t.Fatalf("stat %q failed: %v", from, err) } - if tostat.Dev != fromstat.Dev || tostat.Ino != fromstat.Ino { + if !tostat.(*FileStat).SameFile(fromstat.(*FileStat)) { t.Errorf("symlink %q, %q did not create symlink", to, from) } fromstat, err = Lstat(from) if err != nil { t.Fatalf("lstat %q failed: %v", from, err) } - if !fromstat.IsSymlink() { + if fromstat.Mode()&ModeSymlink == 0 { t.Fatalf("symlink %q, %q did not create symlink", to, from) } fromstat, err = Stat(from) if err != nil { t.Fatalf("stat %q failed: %v", from, err) } - if !fromstat.FollowedSymlink { + if fromstat.Mode()&ModeSymlink != 0 { t.Fatalf("stat %q did not follow symlink", from) } s, err := Readlink(from) @@ -563,13 +564,13 @@ func TestStartProcess(t *testing.T) { exec(t, cmddir, cmdbase, args, filepath.Clean(cmddir)+le) } -func checkMode(t *testing.T, path string, mode uint32) { +func checkMode(t *testing.T, path string, mode FileMode) { dir, err := Stat(path) if err != nil { t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err) } - if dir.Mode&0777 != mode { - t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode, mode) + if dir.Mode()&0777 != mode { + t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode(), mode) } } @@ -593,73 +594,13 @@ func TestChmod(t *testing.T) { checkMode(t, f.Name(), 0123) } -func checkUidGid(t *testing.T, path string, uid, gid int) { - dir, err := Stat(path) - if err != nil { - t.Fatalf("Stat %q (looking for uid/gid %d/%d): %s", path, uid, gid, err) - } - if dir.Uid != uid { - t.Errorf("Stat %q: uid %d want %d", path, dir.Uid, uid) - } - if dir.Gid != gid { - t.Errorf("Stat %q: gid %d want %d", path, dir.Gid, gid) - } -} - -func TestChown(t *testing.T) { - // Chown is not supported under windows or Plan 9. - // Plan9 provides a native ChownPlan9 version instead. - if syscall.OS == "windows" || syscall.OS == "plan9" { - return - } - // Use TempDir() to make sure we're on a local file system, - // so that the group ids returned by Getgroups will be allowed - // on the file. On NFS, the Getgroups groups are - // basically useless. - f := newFile("TestChown", t) - defer Remove(f.Name()) - defer f.Close() - dir, err := f.Stat() - if err != nil { - t.Fatalf("stat %s: %s", f.Name(), err) - } - - // Can't change uid unless root, but can try - // changing the group id. First try our current group. - gid := Getgid() - t.Log("gid:", gid) - if err = Chown(f.Name(), -1, gid); err != nil { - t.Fatalf("chown %s -1 %d: %s", f.Name(), gid, err) - } - checkUidGid(t, f.Name(), dir.Uid, gid) - - // Then try all the auxiliary groups. - groups, err := Getgroups() - if err != nil { - t.Fatalf("getgroups: %s", err) - } - t.Log("groups: ", groups) - for _, g := range groups { - if err = Chown(f.Name(), -1, g); err != nil { - t.Fatalf("chown %s -1 %d: %s", f.Name(), g, err) - } - checkUidGid(t, f.Name(), dir.Uid, g) - - // change back to gid to test fd.Chown - if err = f.Chown(-1, gid); err != nil { - t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err) - } - checkUidGid(t, f.Name(), dir.Uid, gid) - } -} - func checkSize(t *testing.T, f *File, size int64) { dir, err := f.Stat() if err != nil { t.Fatalf("Stat %q (looking for size %d): %s", f.Name(), size, err) } - if dir.Size != size { - t.Errorf("Stat %q: size %d want %d", f.Name(), dir.Size, size) + if dir.Size() != size { + t.Errorf("Stat %q: size %d want %d", f.Name(), dir.Size(), size) } } @@ -711,37 +652,38 @@ func TestChtimes(t *testing.T) { f.Write([]byte("hello, world\n")) f.Close() - preStat, err := Stat(f.Name()) + st, err := Stat(f.Name()) if err != nil { t.Fatalf("Stat %s: %s", f.Name(), err) } + preStat := st.(*FileStat) // Move access and modification time back a second - const OneSecond = 1e9 // in nanoseconds - err = Chtimes(f.Name(), preStat.Atime_ns-OneSecond, preStat.Mtime_ns-OneSecond) + at := Atime(preStat) + mt := preStat.ModTime() + err = Chtimes(f.Name(), at.Add(-time.Second), mt.Add(-time.Second)) if err != nil { t.Fatalf("Chtimes %s: %s", f.Name(), err) } - postStat, err := Stat(f.Name()) + st, err = Stat(f.Name()) if err != nil { t.Fatalf("second Stat %s: %s", f.Name(), err) } + postStat := st.(*FileStat) /* Plan 9: Mtime is the time of the last change of content. Similarly, atime is set whenever the contents are accessed; also, it is set whenever mtime is set. */ - if postStat.Atime_ns >= preStat.Atime_ns && syscall.OS != "plan9" { - t.Errorf("Atime_ns didn't go backwards; was=%d, after=%d", - preStat.Atime_ns, - postStat.Atime_ns) + pat := Atime(postStat) + pmt := postStat.ModTime() + if !pat.Before(at) && syscall.OS != "plan9" { + t.Errorf("AccessTime didn't go backwards; was=%d, after=%d", at, pat) } - if postStat.Mtime_ns >= preStat.Mtime_ns { - t.Errorf("Mtime_ns didn't go backwards; was=%d, after=%d", - preStat.Mtime_ns, - postStat.Mtime_ns) + if !pmt.Before(mt) { + t.Errorf("ModTime didn't go backwards; was=%d, after=%d", mt, pmt) } } @@ -883,7 +825,7 @@ func TestOpenError(t *testing.T) { } perr, ok := err.(*PathError) if !ok { - t.Errorf("Open(%q, %d) returns error of %T type; want *os.PathError", tt.path, tt.mode, err) + t.Errorf("Open(%q, %d) returns error of %T type; want *PathError", tt.path, tt.mode, err) } if perr.Err != tt.error { if syscall.OS == "plan9" { @@ -899,6 +841,14 @@ func TestOpenError(t *testing.T) { } } +func TestOpenNoName(t *testing.T) { + f, err := Open("") + if err == nil { + t.Fatal(`Open("") succeeded`) + f.Close() + } +} + func run(t *testing.T, cmd []string) string { // Run /bin/hostname and collect output. r, w, err := Pipe() @@ -940,11 +890,6 @@ func TestHostname(t *testing.T) { return } - // TODO(jsing): Fix nametomib() on OpenBSD - if syscall.OS == "openbsd" { - return - } - // Check internal Hostname() against the output of /bin/hostname. // Allow that the internal Hostname returns a Fully Qualified Domain Name // and the /bin/hostname only returns the first component diff --git a/libgo/go/os/os_unix_test.go b/libgo/go/os/os_unix_test.go new file mode 100644 index 00000000000..3109a8171a5 --- /dev/null +++ b/libgo/go/os/os_unix_test.go @@ -0,0 +1,75 @@ +// 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. + +// +build darwin freebsd linux openbsd + +package os_test + +import ( + . "os" + "syscall" + "testing" +) + +func checkUidGid(t *testing.T, path string, uid, gid int) { + dir, err := Stat(path) + if err != nil { + t.Fatalf("Stat %q (looking for uid/gid %d/%d): %s", path, uid, gid, err) + } + sys := dir.(*FileStat).Sys.(*syscall.Stat_t) + if int(sys.Uid) != uid { + t.Errorf("Stat %q: uid %d want %d", path, sys.Uid, uid) + } + if int(sys.Gid) != gid { + t.Errorf("Stat %q: gid %d want %d", path, sys.Gid, gid) + } +} + +func TestChown(t *testing.T) { + // Chown is not supported under windows or Plan 9. + // Plan9 provides a native ChownPlan9 version instead. + if syscall.OS == "windows" || syscall.OS == "plan9" { + return + } + // Use TempDir() to make sure we're on a local file system, + // so that the group ids returned by Getgroups will be allowed + // on the file. On NFS, the Getgroups groups are + // basically useless. + f := newFile("TestChown", t) + defer Remove(f.Name()) + defer f.Close() + dir, err := f.Stat() + if err != nil { + t.Fatalf("stat %s: %s", f.Name(), err) + } + + // Can't change uid unless root, but can try + // changing the group id. First try our current group. + gid := Getgid() + t.Log("gid:", gid) + if err = Chown(f.Name(), -1, gid); err != nil { + t.Fatalf("chown %s -1 %d: %s", f.Name(), gid, err) + } + sys := dir.(*FileStat).Sys.(*syscall.Stat_t) + checkUidGid(t, f.Name(), int(sys.Uid), gid) + + // Then try all the auxiliary groups. + groups, err := Getgroups() + if err != nil { + t.Fatalf("getgroups: %s", err) + } + t.Log("groups: ", groups) + for _, g := range groups { + if err = Chown(f.Name(), -1, g); err != nil { + t.Fatalf("chown %s -1 %d: %s", f.Name(), g, err) + } + checkUidGid(t, f.Name(), int(sys.Uid), g) + + // change back to gid to test fd.Chown + if err = f.Chown(-1, gid); err != nil { + t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err) + } + checkUidGid(t, f.Name(), int(sys.Uid), gid) + } +} diff --git a/libgo/go/os/path.go b/libgo/go/os/path.go index 82fade63ae2..bc14a783188 100644 --- a/libgo/go/os/path.go +++ b/libgo/go/os/path.go @@ -17,7 +17,7 @@ func MkdirAll(path string, perm uint32) error { // If path exists, stop with success or error. dir, err := Stat(path) if err == nil { - if dir.IsDirectory() { + if dir.IsDir() { return nil } return &PathError{"mkdir", path, ENOTDIR} @@ -48,7 +48,7 @@ func MkdirAll(path string, perm uint32) error { // Handle arguments like "foo/." by // double-checking that directory doesn't exist. dir, err1 := Lstat(path) - if err1 == nil && dir.IsDirectory() { + if err1 == nil && dir.IsDir() { return nil } return err @@ -75,7 +75,7 @@ func RemoveAll(path string) error { } return serr } - if !dir.IsDirectory() { + if !dir.IsDir() { // Not a directory; return the error from Remove. return err } diff --git a/libgo/go/os/proc.go b/libgo/go/os/proc.go index d21f849f210..61545f4456a 100644 --- a/libgo/go/os/proc.go +++ b/libgo/go/os/proc.go @@ -8,8 +8,8 @@ package os import "syscall" -var Args []string // provided by runtime -var Envs []string // provided by runtime +// Args hold the command-line arguments, starting with the program name. +var Args []string // Getuid returns the numeric user id of the caller. func Getuid() int { return syscall.Getuid() } diff --git a/libgo/go/os/stat.go b/libgo/go/os/stat.go index 8eb4ab4391d..c664fc189b8 100644 --- a/libgo/go/os/stat.go +++ b/libgo/go/os/stat.go @@ -2,39 +2,55 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// AMD64, Linux - package os -import syscall "syscall" +import ( + "syscall" + "time" +) -func isSymlink(stat *syscall.Stat_t) bool { - return stat.Mode & syscall.S_IFMT == syscall.S_IFLNK +func sameFile(fs1, fs2 *FileStat) bool { + sys1 := fs1.Sys.(*syscall.Stat_t) + sys2 := fs2.Sys.(*syscall.Stat_t) + return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino } -func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *FileInfo { - fi.Dev = uint64(stat.Dev) - fi.Ino = uint64(stat.Ino) - fi.Nlink = uint64(stat.Nlink) - fi.Mode = uint32(stat.Mode) - fi.Uid = int(stat.Uid) - fi.Gid = int(stat.Gid) - fi.Rdev = uint64(stat.Rdev) - fi.Size = int64(stat.Size) - fi.Blksize = int64(stat.Blksize) - fi.Blocks = int64(stat.Blocks) - fi.Atime_ns = int64(stat.Atime.Sec)*1e9 + int64(stat.Atime.Nsec) - fi.Mtime_ns = int64(stat.Mtime.Sec)*1e9 + int64(stat.Mtime.Nsec) - fi.Ctime_ns = int64(stat.Ctime.Sec)*1e9 + int64(stat.Ctime.Nsec) - for i := len(name)-1; i >= 0; i-- { - if name[i] == '/' { - name = name[i+1:] - break - } +func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo { + fs := &FileStat{ + name: basename(name), + size: int64(st.Size), + modTime: timespecToTime(st.Mtime), + Sys: st, + } + fs.mode = FileMode(st.Mode & 0777) + switch st.Mode & syscall.S_IFMT { + case syscall.S_IFBLK, syscall.S_IFCHR: + fs.mode |= ModeDevice + case syscall.S_IFDIR: + fs.mode |= ModeDir + case syscall.S_IFIFO: + fs.mode |= ModeNamedPipe + case syscall.S_IFLNK: + fs.mode |= ModeSymlink + case syscall.S_IFREG: + // nothing to do + case syscall.S_IFSOCK: + fs.mode |= ModeSocket } - fi.Name = name - if isSymlink(lstat) && !isSymlink(stat) { - fi.FollowedSymlink = true + if st.Mode&syscall.S_ISGID != 0 { + fs.mode |= ModeSetgid } - return fi + if st.Mode&syscall.S_ISUID != 0 { + fs.mode |= ModeSetuid + } + return fs +} + +func timespecToTime(ts syscall.Timespec) time.Time { + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} + +// For testing. +func atime(fi FileInfo) time.Time { + return timespecToTime(fi.(*FileStat).Sys.(*syscall.Stat_t).Atime) } diff --git a/libgo/go/os/stat_openbsd.go b/libgo/go/os/stat_openbsd.go index 6d3a3813b09..66189a6b9ba 100644 --- a/libgo/go/os/stat_openbsd.go +++ b/libgo/go/os/stat_openbsd.go @@ -4,29 +4,53 @@ package os -import "syscall" +import ( + "syscall" + "time" +) -func isSymlink(stat *syscall.Stat_t) bool { - return stat.Mode&syscall.S_IFMT == syscall.S_IFLNK +func sameFile(fs1, fs2 *FileStat) bool { + sys1 := fs1.Sys.(*syscall.Stat_t) + sys2 := fs2.Sys.(*syscall.Stat_t) + return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino } -func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *FileInfo { - fi.Dev = uint64(stat.Dev) - fi.Ino = uint64(stat.Ino) - fi.Nlink = uint64(stat.Nlink) - fi.Mode = uint32(stat.Mode) - fi.Uid = int(stat.Uid) - fi.Gid = int(stat.Gid) - fi.Rdev = uint64(stat.Rdev) - fi.Size = int64(stat.Size) - fi.Blksize = int64(stat.Blksize) - fi.Blocks = stat.Blocks - fi.Atime_ns = syscall.TimespecToNsec(stat.Atim) - fi.Mtime_ns = syscall.TimespecToNsec(stat.Mtim) - fi.Ctime_ns = syscall.TimespecToNsec(stat.Ctim) - fi.Name = basename(name) - if isSymlink(lstat) && !isSymlink(stat) { - fi.FollowedSymlink = true +func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo { + fs := &FileStat{ + name: basename(name), + size: int64(st.Size), + modTime: timespecToTime(st.Mtim), + Sys: st, } - return fi + fs.mode = FileMode(st.Mode & 0777) + switch st.Mode & syscall.S_IFMT { + case syscall.S_IFBLK, syscall.S_IFCHR: + fs.mode |= ModeDevice + case syscall.S_IFDIR: + fs.mode |= ModeDir + case syscall.S_IFIFO: + fs.mode |= ModeNamedPipe + case syscall.S_IFLNK: + fs.mode |= ModeSymlink + case syscall.S_IFREG: + // nothing to do + case syscall.S_IFSOCK: + fs.mode |= ModeSocket + } + if st.Mode&syscall.S_ISGID != 0 { + fs.mode |= ModeSetgid + } + if st.Mode&syscall.S_ISUID != 0 { + fs.mode |= ModeSetuid + } + return fs +} + +func timespecToTime(ts syscall.Timespec) time.Time { + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} + +// For testing. +func atime(fi FileInfo) time.Time { + return timespecToTime(fi.(*FileStat).Sys.(*syscall.Stat_t).Atim) } diff --git a/libgo/go/os/stat_plan9.go b/libgo/go/os/stat_plan9.go index 8df9e580cc4..e4a1dbbaea3 100644 --- a/libgo/go/os/stat_plan9.go +++ b/libgo/go/os/stat_plan9.go @@ -34,7 +34,7 @@ func dirstat(arg interface{}) (d *Dir, err error) { buf := make([]byte, nd) var n int - var e syscall.Error + var e error switch syscallArg := arg.(type) { case *File: @@ -72,7 +72,7 @@ func dirstat(arg interface{}) (d *Dir, err error) { // Stat returns a FileInfo structure describing the named file and an error, if any. func Stat(name string) (fi *FileInfo, err error) { d, err := dirstat(name) - if iserror(err) { + if err != nil { return nil, err } return fileInfoFromStat(new(FileInfo), d), err @@ -83,7 +83,7 @@ func Stat(name string) (fi *FileInfo, err error) { // the returned FileInfo describes the symbolic link. Lstat makes no attempt to follow the link. func Lstat(name string) (fi *FileInfo, err error) { d, err := dirstat(name) - if iserror(err) { + if err != nil { return nil, err } return fileInfoFromStat(new(FileInfo), d), err diff --git a/libgo/go/os/sys_bsd.go b/libgo/go/os/sys_bsd.go index 67133a1898b..c6a6de5c816 100644 --- a/libgo/go/os/sys_bsd.go +++ b/libgo/go/os/sys_bsd.go @@ -12,10 +12,9 @@ package os import "syscall" func Hostname() (name string, err error) { - var errno int - name, errno = syscall.Sysctl("kern.hostname") - if errno != 0 { - return "", NewSyscallError("sysctl kern.hostname", errno) + name, err = syscall.Sysctl("kern.hostname") + if err != nil { + return "", NewSyscallError("sysctl kern.hostname", err) } return name, nil } diff --git a/libgo/go/os/sys_uname.go b/libgo/go/os/sys_uname.go index 0e68647acdc..313ee62aef6 100644 --- a/libgo/go/os/sys_uname.go +++ b/libgo/go/os/sys_uname.go @@ -10,7 +10,7 @@ import "syscall" func Hostname() (name string, err error) { var u syscall.Utsname - if errno := syscall.Uname(&u); errno != 0 { + if errno := syscall.Uname(&u); errno != nil { return "", NewSyscallError("uname", errno) } b := make([]byte, len(u.Nodename)) diff --git a/libgo/go/os/time.go b/libgo/go/os/time.go index a4678ee2a16..eb564e57a6c 100644 --- a/libgo/go/os/time.go +++ b/libgo/go/os/time.go @@ -12,7 +12,7 @@ import "syscall" // time is the Unix epoch. func Time() (sec int64, nsec int64, err error) { var tv syscall.Timeval - if e := syscall.Gettimeofday(&tv); iserror(e) { + if e := syscall.Gettimeofday(&tv); e != nil { return 0, 0, NewSyscallError("gettimeofday", e) } return int64(tv.Sec), int64(tv.Usec) * 1000, err diff --git a/libgo/go/os/types.go b/libgo/go/os/types.go index df57b59a388..2638153ddbe 100644 --- a/libgo/go/os/types.go +++ b/libgo/go/os/types.go @@ -4,53 +4,111 @@ package os -import "syscall" - -// An operating-system independent representation of Unix data structures. -// OS-specific routines in this directory convert the OS-local versions to these. +import ( + "syscall" + "time" +) // Getpagesize returns the underlying system's memory page size. func Getpagesize() int { return syscall.Getpagesize() } -// A FileInfo describes a file and is returned by Stat, Fstat, and Lstat -type FileInfo struct { - Dev uint64 // device number of file system holding file. - Ino uint64 // inode number. - Nlink uint64 // number of hard links. - Mode uint32 // permission and mode bits. - Uid int // user id of owner. - Gid int // group id of owner. - Rdev uint64 // device type for special file. - Size int64 // length in bytes. - Blksize int64 // size of blocks, in bytes. - Blocks int64 // number of blocks allocated for file. - Atime_ns int64 // access time; nanoseconds since epoch. - Mtime_ns int64 // modified time; nanoseconds since epoch. - Ctime_ns int64 // status change time; nanoseconds since epoch. - Name string // base name of the file name provided in Open, Stat, etc. - FollowedSymlink bool // followed a symlink to get this information +// A FileInfo describes a file and is returned by Stat and Lstat +type FileInfo interface { + Name() string // base name of the file + Size() int64 // length in bytes + Mode() FileMode // file mode bits + ModTime() time.Time // modification time + IsDir() bool // abbreviation for Mode().IsDir() } -// IsFifo reports whether the FileInfo describes a FIFO file. -func (f *FileInfo) IsFifo() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFIFO } +// A FileMode represents a file's mode and permission bits. +// The bits have the same definition on all systems, so that +// information about files can be moved from one system +// to another portably. Not all bits apply to all systems. +// The only required bit is ModeDir for directories. +type FileMode uint32 -// IsChar reports whether the FileInfo describes a character special file. -func (f *FileInfo) IsChar() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFCHR } +// The defined file mode bits are the most significant bits of the FileMode. +// The nine least-significant bits are the standard Unix rwxrwxrwx permissions. +const ( + // The single letters are the abbreviations + // used by the String method's formatting. + ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory + ModeAppend // a: append-only + ModeExclusive // l: exclusive use + ModeTemporary // t: temporary file (not backed up) + ModeSymlink // L: symbolic link + ModeDevice // D: device file + ModeNamedPipe // p: named pipe (FIFO) + ModeSocket // S: Unix domain socket + ModeSetuid // u: setuid + ModeSetgid // g: setgid -// IsDirectory reports whether the FileInfo describes a directory. -func (f *FileInfo) IsDirectory() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFDIR } + // Mask for the type bits. For regular files, none will be set. + ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice -// IsBlock reports whether the FileInfo describes a block special file. -func (f *FileInfo) IsBlock() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFBLK } + ModePerm FileMode = 0777 // permission bits +) -// IsRegular reports whether the FileInfo describes a regular file. -func (f *FileInfo) IsRegular() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFREG } +func (m FileMode) String() string { + const str = "daltLDpSug" + var buf [20]byte + w := 0 + for i, c := range str { + if m&(1<<uint(32-1-i)) != 0 { + buf[w] = byte(c) + w++ + } + } + if w == 0 { + buf[w] = '-' + w++ + } + const rwx = "rwxrwxrwx" + for i, c := range rwx { + if m&(1<<uint(9-1-i)) != 0 { + buf[w] = byte(c) + } else { + buf[w] = '-' + } + w++ + } + return string(buf[:w]) +} -// IsSymlink reports whether the FileInfo describes a symbolic link. -func (f *FileInfo) IsSymlink() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFLNK } +// IsDir reports whether m describes a directory. +// That is, it tests for the ModeDir bit being set in m. +func (m FileMode) IsDir() bool { + return m&ModeDir != 0 +} + +// Perm returns the Unix permission bits in m. +func (m FileMode) Perm() FileMode { + return m & ModePerm +} -// IsSocket reports whether the FileInfo describes a socket. -func (f *FileInfo) IsSocket() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFSOCK } +// A FileStat is the implementation of FileInfo returned by Stat and Lstat. +// Clients that need access to the underlying system-specific stat information +// can test for *os.FileStat and then consult the Sys field. +type FileStat struct { + name string + size int64 + mode FileMode + modTime time.Time -// Permission returns the file permission bits. -func (f *FileInfo) Permission() uint32 { return f.Mode & 0777 } + Sys interface{} +} + +func (fs *FileStat) Name() string { return fs.name } +func (fs *FileStat) Size() int64 { return fs.size } +func (fs *FileStat) Mode() FileMode { return fs.mode } +func (fs *FileStat) ModTime() time.Time { return fs.modTime } +func (fs *FileStat) IsDir() bool { return fs.mode.IsDir() } + +// SameFile reports whether fs and other describe the same file. +// For example, on Unix this means that the device and inode fields +// of the two underlying structures are identical; on other systems +// the decision may be based on the path names. +func (fs *FileStat) SameFile(other *FileStat) bool { + return sameFile(fs, other) +} diff --git a/libgo/go/os/user/lookup_unix.go b/libgo/go/os/user/lookup_unix.go index ba4e1ac7991..89886cb03cf 100644 --- a/libgo/go/os/user/lookup_unix.go +++ b/libgo/go/os/user/lookup_unix.go @@ -8,7 +8,6 @@ package user import ( "fmt" - "os" "strings" "syscall" "unsafe" @@ -70,7 +69,7 @@ func lookup(uid int, username string, lookupByName bool) (*User, error) { bufSize, &result) if rv != 0 { - return nil, fmt.Errorf("user: lookup username %s: %s", username, os.Errno(syscall.GetErrno())) + return nil, fmt.Errorf("user: lookup username %s: %s", username, syscall.GetErrno()) } if result == nil { return nil, UnknownUserError(username) @@ -82,7 +81,7 @@ func lookup(uid int, username string, lookupByName bool) (*User, error) { bufSize, &result) if rv != 0 { - return nil, fmt.Errorf("user: lookup userid %d: %s", uid, os.Errno(syscall.GetErrno())) + return nil, fmt.Errorf("user: lookup userid %d: %s", uid, syscall.GetErrno()) } if result == nil { return nil, UnknownUserIdError(uid) diff --git a/libgo/go/os/user/user_test.go b/libgo/go/os/user/user_test.go index 59f15e4c675..f9f44af8a93 100644 --- a/libgo/go/os/user/user_test.go +++ b/libgo/go/os/user/user_test.go @@ -41,8 +41,8 @@ func TestLookup(t *testing.T) { t.Errorf("expected Uid of %d; got %d", e, g) } fi, err := os.Stat(u.HomeDir) - if err != nil || !fi.IsDirectory() { - t.Errorf("expected a valid HomeDir; stat(%q): err=%v, IsDirectory=%v", u.HomeDir, err, fi.IsDirectory()) + if err != nil || !fi.IsDir() { + t.Errorf("expected a valid HomeDir; stat(%q): err=%v, IsDir=%v", u.HomeDir, err, fi.IsDir()) } if u.Username == "" { t.Fatalf("didn't get a username") diff --git a/libgo/go/patch/git.go b/libgo/go/patch/git.go index 454eadececa..5c233fbaebf 100644 --- a/libgo/go/patch/git.go +++ b/libgo/go/patch/git.go @@ -22,7 +22,7 @@ func gitSHA1(data []byte) []byte { h := sha1.New() fmt.Fprintf(h, "blob %d\x00", len(data)) h.Write(data) - return h.Sum() + return h.Sum(nil) } // BUG(rsc): The Git binary delta format is not implemented, only Git binary literals. diff --git a/libgo/go/path/filepath/match.go b/libgo/go/path/filepath/match.go index 8cf1f9ad107..c3678f541d4 100644 --- a/libgo/go/path/filepath/match.go +++ b/libgo/go/path/filepath/match.go @@ -260,7 +260,7 @@ func glob(dir, pattern string, matches []string) (m []string, e error) { if err != nil { return } - if !fi.IsDirectory() { + if !fi.IsDir() { return } d, err := os.Open(dir) diff --git a/libgo/go/path/filepath/path.go b/libgo/go/path/filepath/path.go index 1b5d6c36495..e3d6c342ca6 100644 --- a/libgo/go/path/filepath/path.go +++ b/libgo/go/path/filepath/path.go @@ -223,7 +223,7 @@ func EvalSymlinks(path string) (string, error) { if err != nil { return "", err } - if !fi.IsSymlink() { + if fi.Mode()&os.ModeSymlink == 0 { b.WriteString(p) if path != "" { b.WriteRune(Separator) @@ -312,7 +312,11 @@ func Rel(basepath, targpath string) (string, error) { if b0 != bl { // Base elements left. Must go up before going down. seps := strings.Count(base[b0:bl], string(Separator)) - buf := make([]byte, 3+seps*3+tl-t0) + size := 2 + seps*3 + if tl != t0 { + size += 1 + tl - t0 + } + buf := make([]byte, size) n := copy(buf, "..") for i := 0; i < seps; i++ { buf[n] = Separator @@ -341,19 +345,19 @@ var SkipDir = errors.New("skip this directory") // sole exception is that if path is a directory and the function returns the // special value SkipDir, the contents of the directory are skipped // and processing continues as usual on the next file. -type WalkFunc func(path string, info *os.FileInfo, err error) error +type WalkFunc func(path string, info os.FileInfo, err error) error // walk recursively descends path, calling w. -func walk(path string, info *os.FileInfo, walkFn WalkFunc) error { +func walk(path string, info os.FileInfo, walkFn WalkFunc) error { err := walkFn(path, info, nil) if err != nil { - if info.IsDirectory() && err == SkipDir { + if info.IsDir() && err == SkipDir { return nil } return err } - if !info.IsDirectory() { + if !info.IsDir() { return nil } @@ -363,7 +367,7 @@ func walk(path string, info *os.FileInfo, walkFn WalkFunc) error { } for _, fileInfo := range list { - if err = walk(Join(path, fileInfo.Name), fileInfo, walkFn); err != nil { + if err = walk(Join(path, fileInfo.Name()), fileInfo, walkFn); err != nil { return err } } @@ -386,7 +390,7 @@ func Walk(root string, walkFn WalkFunc) error { // readDir reads the directory named by dirname and returns // a sorted list of directory entries. // Copied from io/ioutil to avoid the circular import. -func readDir(dirname string) ([]*os.FileInfo, error) { +func readDir(dirname string) ([]os.FileInfo, error) { f, err := os.Open(dirname) if err != nil { return nil, err @@ -396,20 +400,16 @@ func readDir(dirname string) ([]*os.FileInfo, error) { if err != nil { return nil, err } - fi := make(fileInfoList, len(list)) - for i := range list { - fi[i] = &list[i] - } - sort.Sort(fi) - return fi, nil + sort.Sort(byName(list)) + return list, nil } -// A dirList implements sort.Interface. -type fileInfoList []*os.FileInfo +// byName implements sort.Interface. +type byName []os.FileInfo -func (f fileInfoList) Len() int { return len(f) } -func (f fileInfoList) Less(i, j int) bool { return f[i].Name < f[j].Name } -func (f fileInfoList) Swap(i, j int) { f[i], f[j] = f[j], f[i] } +func (f byName) Len() int { return len(f) } +func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() } +func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } // Base returns the last element of path. // Trailing path separators are removed before extracting the last element. diff --git a/libgo/go/path/filepath/path_test.go b/libgo/go/path/filepath/path_test.go index c81cbf0ddc7..2bd62d34aeb 100644 --- a/libgo/go/path/filepath/path_test.go +++ b/libgo/go/path/filepath/path_test.go @@ -317,7 +317,7 @@ func checkMarks(t *testing.T, report bool) { // Assumes that each node name is unique. Good enough for a test. // If clear is true, any incoming error is cleared before return. The errors // are always accumulated, though. -func mark(path string, info *os.FileInfo, err error, errors *[]error, clear bool) error { +func mark(path string, info os.FileInfo, err error, errors *[]error, clear bool) error { if err != nil { *errors = append(*errors, err) if clear { @@ -325,8 +325,9 @@ func mark(path string, info *os.FileInfo, err error, errors *[]error, clear bool } return err } + name := info.Name() walkTree(tree, tree.name, func(path string, n *Node) { - if n.name == info.Name { + if n.name == name { n.mark++ } }) @@ -337,7 +338,7 @@ func TestWalk(t *testing.T) { makeTree(t) errors := make([]error, 0, 10) clear := true - markFn := func(path string, info *os.FileInfo, err error) error { + markFn := func(path string, info os.FileInfo, err error) error { return mark(path, info, err, &errors, clear) } // Expect no errors. @@ -548,7 +549,7 @@ func TestEvalSymlinks(t *testing.T) { // relative testEvalSymlinks(t, tests) // absolute -/* These tests do not work in the gccgo test environment. + /* These tests do not work in the gccgo test environment. goroot, err := filepath.EvalSymlinks(os.Getenv("GOROOT")) if err != nil { t.Fatalf("EvalSymlinks(%q) error: %v", os.Getenv("GOROOT"), err) @@ -564,7 +565,7 @@ func TestEvalSymlinks(t *testing.T) { } } testEvalSymlinks(t, tests) -*/ + */ } /* These tests do not work in the gccgo test environment. @@ -603,7 +604,7 @@ func TestAbs(t *testing.T) { t.Errorf("Abs(%q) error: %v", path, err) } absinfo, err := os.Stat(abspath) - if err != nil || absinfo.Ino != info.Ino { + if err != nil || !absinfo.(*os.FileStat).SameFile(info.(*os.FileStat)) { t.Errorf("Abs(%q)=%q, not the same file", path, abspath) } if !filepath.IsAbs(abspath) { @@ -634,6 +635,10 @@ var reltests = []RelTests{ {"a/b/../c", "a/b", "../b"}, {"a/b/c", "a/c/d", "../../c/d"}, {"a/b", "c/d", "../../c/d"}, + {"a/b/c/d", "a/b", "../.."}, + {"a/b/c/d", "a/b/", "../.."}, + {"a/b/c/d/", "a/b", "../.."}, + {"a/b/c/d/", "a/b/", "../.."}, {"../../a/b", "../../a/b/c/d", "c/d"}, {"/a/b", "/a/b", "."}, {"/a/b/.", "/a/b", "."}, @@ -645,6 +650,10 @@ var reltests = []RelTests{ {"/a/b/../c", "/a/b", "../b"}, {"/a/b/c", "/a/c/d", "../../c/d"}, {"/a/b", "/c/d", "../../c/d"}, + {"/a/b/c/d", "/a/b", "../.."}, + {"/a/b/c/d", "/a/b/", "../.."}, + {"/a/b/c/d/", "/a/b", "../.."}, + {"/a/b/c/d/", "/a/b/", "../.."}, {"/../../a/b", "/../../a/b/c/d", "c/d"}, {".", "a/b", "a/b"}, {".", "..", ".."}, diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go index 7c1022ecb48..cf31d1ba39d 100644 --- a/libgo/go/reflect/all_test.go +++ b/libgo/go/reflect/all_test.go @@ -16,6 +16,13 @@ import ( "unsafe" ) +func TestBool(t *testing.T) { + v := ValueOf(true) + if v.Bool() != true { + t.Fatal("ValueOf(true).Bool() = false") + } +} + type integer int type T struct { a int @@ -215,7 +222,8 @@ func TestTypes(t *testing.T) { func TestSet(t *testing.T) { for i, tt := range valueTests { - v := ValueOf(tt.i).Elem() + v := ValueOf(tt.i) + v = v.Elem() switch v.Kind() { case Int: v.SetInt(132) @@ -651,6 +659,14 @@ var deepEqualTests = []DeepEqualTest{ {nil, 1, false}, {1, nil, false}, + // Nil vs empty: not the same. + {[]int{}, []int(nil), false}, + {[]int{}, []int{}, true}, + {[]int(nil), []int(nil), true}, + {map[int]int{}, map[int]int(nil), false}, + {map[int]int{}, map[int]int{}, true}, + {map[int]int(nil), map[int]int(nil), true}, + // Mismatched types {1, 1.0, false}, {int32(1), int64(1), false}, @@ -1092,21 +1108,38 @@ func TestMethod(t *testing.T) { } // Curried method of value. - i = ValueOf(p).Method(1).Call([]Value{ValueOf(10)})[0].Int() + tfunc := TypeOf(func(int) int(nil)) + v := ValueOf(p).Method(1) + if tt := v.Type(); tt != tfunc { + t.Errorf("Value Method Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Value Method returned %d; want 250", i) } - i = ValueOf(p).MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int() + v = ValueOf(p).MethodByName("Dist") + if tt := v.Type(); tt != tfunc { + t.Errorf("Value MethodByName Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Value MethodByName returned %d; want 250", i) } // Curried method of pointer. - i = ValueOf(&p).Method(1).Call([]Value{ValueOf(10)})[0].Int() + v = ValueOf(&p).Method(1) + if tt := v.Type(); tt != tfunc { + t.Errorf("Pointer Value Method Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Pointer Value Method returned %d; want 250", i) } - i = ValueOf(&p).MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int() + v = ValueOf(&p).MethodByName("Dist") + if tt := v.Type(); tt != tfunc { + t.Errorf("Pointer Value MethodByName Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Pointer Value MethodByName returned %d; want 250", i) } @@ -1121,11 +1154,19 @@ func TestMethod(t *testing.T) { } }{p} pv := ValueOf(s).Field(0) - i = pv.Method(0).Call([]Value{ValueOf(10)})[0].Int() + v = pv.Method(0) + if tt := v.Type(); tt != tfunc { + t.Errorf("Interface Method Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Interface Method returned %d; want 250", i) } - i = pv.MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int() + v = pv.MethodByName("Dist") + if tt := v.Type(); tt != tfunc { + t.Errorf("Interface MethodByName Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Interface MethodByName returned %d; want 250", i) } diff --git a/libgo/go/reflect/deepequal.go b/libgo/go/reflect/deepequal.go index 63c28fe2024..df5ec0a6099 100644 --- a/libgo/go/reflect/deepequal.go +++ b/libgo/go/reflect/deepequal.go @@ -69,6 +69,9 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool } return true case Slice: + if v1.IsNil() != v2.IsNil() { + return false + } if v1.Len() != v2.Len() { return false } @@ -93,6 +96,9 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool } return true case Map: + if v1.IsNil() != v2.IsNil() { + return false + } if v1.Len() != v2.Len() { return false } diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index 9be7f830875..07cc0f5ba40 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -188,7 +188,7 @@ type Type interface { // A Kind represents the specific kind of type that a Type represents. // The zero Kind is not a valid kind. -type Kind uint8 +type Kind uint const ( Invalid Kind = iota @@ -455,15 +455,16 @@ func (t *uncommonType) Method(i int) (m Method) { if p.name != nil { m.Name = *p.name } - flag := uint32(0) + fl := flag(Func) << flagKindShift if p.pkgPath != nil { m.PkgPath = *p.pkgPath - flag |= flagRO + fl |= flagRO } - m.Type = toType(p.typ) + mt := toCommonType(p.typ) + m.Type = mt.toType() x := new(unsafe.Pointer) *x = p.tfn - m.Func = valueFromIword(flag, m.Type, iword(uintptr(unsafe.Pointer(x)))) + m.Func = Value{mt, unsafe.Pointer(x), fl|flagIndir} m.Index = i return } @@ -769,7 +770,7 @@ func (t *structType) Field(i int) (f StructField) { if i < 0 || i >= len(t.fields) { return } - p := t.fields[i] + p := &t.fields[i] f.Type = toType(p.typ) if p.name != nil { f.Name = *p.name @@ -868,10 +869,12 @@ L: if n == 1 { // Found matching field. - if len(ff.Index) <= depth { + if depth >= len(ff.Index) { ff.Index = make([]int, depth+1) } - ff.Index[depth] = fi + if len(ff.Index) > 1 { + ff.Index[depth] = fi + } } else { // None or more than one matching field found. fd = inf @@ -903,9 +906,6 @@ func toCommonType(p *runtime.Type) *commonType { return nil } x := unsafe.Pointer(p) - if uintptr(x)&reflectFlags != 0 { - panic("reflect: invalid interface value") - } return (*commonType)(x) } @@ -967,10 +967,12 @@ func (t *commonType) runtimeType() *runtime.Type { // PtrTo returns the pointer type with element t. // For example, if t represents type Foo, PtrTo(t) represents *Foo. func PtrTo(t Type) Type { - // If t records its pointer-to type, use it. - ct := t.(*commonType) + return t.(*commonType).ptrTo() +} + +func (ct *commonType) ptrTo() *commonType { if p := ct.ptrToThis; p != nil { - return toType(p) + return toCommonType(p) } // Otherwise, synthesize one. @@ -982,7 +984,7 @@ func PtrTo(t Type) Type { if m := ptrMap.m; m != nil { if p := m[ct]; p != nil { ptrMap.RUnlock() - return p.commonType.toType() + return &p.commonType } } ptrMap.RUnlock() @@ -994,7 +996,7 @@ func PtrTo(t Type) Type { if p != nil { // some other goroutine won the race and created it ptrMap.Unlock() - return p + return &p.commonType } rt := (*runtime.Type)(unsafe.Pointer(ct)) @@ -1024,7 +1026,7 @@ func PtrTo(t Type) Type { ptrMap.m[ct] = p ptrMap.Unlock() - return p.commonType.toType() + return &p.commonType } func (t *commonType) Implements(u Type) bool { diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go index dbdfa09242f..b57ed462c84 100644 --- a/libgo/go/reflect/value.go +++ b/libgo/go/reflect/value.go @@ -11,6 +11,7 @@ import ( "unsafe" ) +const bigEndian = false // can be smarter if we find a big-endian machine const ptrSize = unsafe.Sizeof((*byte)(nil)) const cannotSet = "cannot set value obtained from unexported struct field" @@ -53,14 +54,54 @@ func memmove(adst, asrc unsafe.Pointer, n uintptr) { // its String method returns "<invalid Value>", and all other methods panic. // Most functions and methods never return an invalid value. // If one does, its documentation states the conditions explicitly. -// -// The fields of Value are exported so that clients can copy and -// pass Values around, but they should not be edited or inspected -// directly. A future language change may make it possible not to -// export these fields while still keeping Values usable as values. type Value struct { - Internal interface{} - InternalMethod int + // typ holds the type of the value represented by a Value. + typ *commonType + + // val holds the 1-word representation of the value. + // If flag's flagIndir bit is set, then val is a pointer to the data. + // Otherwise val is a word holding the actual data. + // When the data is smaller than a word, it begins at + // the first byte (in the memory address sense) of val. + // We use unsafe.Pointer so that the garbage collector + // knows that val could be a pointer. + val unsafe.Pointer + + // flag holds metadata about the value. + // The lowest bits are flag bits: + // - flagRO: obtained via unexported field, so read-only + // - flagIndir: val holds a pointer to the data + // - flagAddr: v.CanAddr is true (implies flagIndir) + // - flagMethod: v is a method value. + // The next five bits give the Kind of the value. + // This repeats typ.Kind() except for method values. + // The remaining 23+ bits give a method number for method values. + // If flag.kind() != Func, code can assume that flagMethod is unset. + // If typ.size > ptrSize, code can assume that flagIndir is set. + flag + + // A method value represents a curried method invocation + // like r.Read for some receiver r. The typ+val+flag bits describe + // the receiver r, but the flag's Kind bits say Func (methods are + // functions), and the top bits of the flag give the method number + // in r's type's method table. +} + +type flag uintptr + +const ( + flagRO flag = 1 << iota + flagIndir + flagAddr + flagMethod + flagKindShift = iota + flagKindWidth = 5 // there are 27 kinds + flagKindMask flag = 1<<flagKindWidth - 1 + flagMethodShift = flagKindShift + flagKindWidth +) + +func (f flag) kind() Kind { + return Kind((f >> flagKindShift) & flagKindMask) } // A ValueError occurs when a Value method is invoked on @@ -92,17 +133,30 @@ func methodName() string { // An iword is the word that would be stored in an // interface to represent a given value v. Specifically, if v is // bigger than a pointer, its word is a pointer to v's data. -// Otherwise, its word is a zero uintptr with the data stored -// in the leading bytes. -type iword uintptr +// Otherwise, its word holds the data stored +// in its leading bytes (so is not a pointer). +// Because the value sometimes holds a pointer, we use +// unsafe.Pointer to represent it, so that if iword appears +// in a struct, the garbage collector knows that might be +// a pointer. +type iword unsafe.Pointer + +func (v Value) iword() iword { + if v.flag&flagIndir != 0 && (v.kind() == Ptr || v.kind() == UnsafePointer) { + // Have indirect but want direct word. + return loadIword(v.val, v.typ.size) + } + return iword(v.val) +} -func loadIword(p unsafe.Pointer, size uintptr) iword { +// loadIword loads n bytes at p from memory into an iword. +func loadIword(p unsafe.Pointer, n uintptr) iword { // Run the copy ourselves instead of calling memmove - // to avoid moving v to the heap. - w := iword(0) - switch size { + // to avoid moving w to the heap. + var w iword + switch n { default: - panic("reflect: internal error: loadIword of " + strconv.Itoa(int(size)) + "-byte value") + panic("reflect: internal error: loadIword of " + strconv.Itoa(int(n)) + "-byte value") case 0: case 1: *(*uint8)(unsafe.Pointer(&w)) = *(*uint8)(p) @@ -124,12 +178,13 @@ func loadIword(p unsafe.Pointer, size uintptr) iword { return w } -func storeIword(p unsafe.Pointer, w iword, size uintptr) { +// storeIword stores n bytes from w into p. +func storeIword(p unsafe.Pointer, w iword, n uintptr) { // Run the copy ourselves instead of calling memmove - // to avoid moving v to the heap. - switch size { + // to avoid moving w to the heap. + switch n { default: - panic("reflect: internal error: storeIword of " + strconv.Itoa(int(size)) + "-byte value") + panic("reflect: internal error: storeIword of " + strconv.Itoa(int(n)) + "-byte value") case 0: case 1: *(*uint8)(p) = *(*uint8)(unsafe.Pointer(&w)) @@ -166,237 +221,42 @@ type nonEmptyInterface struct { word iword } -// Regarding the implementation of Value: -// -// The Internal interface is a true interface value in the Go sense, -// but it also serves as a (type, address) pair in which one cannot -// be changed separately from the other. That is, it serves as a way -// to prevent unsafe mutations of the Internal state even though -// we cannot (yet?) hide the field while preserving the ability for -// clients to make copies of Values. -// -// The internal method converts a Value into the expanded internalValue struct. -// If we could avoid exporting fields we'd probably make internalValue the -// definition of Value. -// -// If a Value is addressable (CanAddr returns true), then the Internal -// interface value holds a pointer to the actual field data, and Set stores -// through that pointer. If a Value is not addressable (CanAddr returns false), -// then the Internal interface value holds the actual value. -// -// In addition to whether a value is addressable, we track whether it was -// obtained by using an unexported struct field. Such values are allowed -// to be read, mainly to make fmt.Print more useful, but they are not -// allowed to be written. We call such values read-only. -// -// A Value can be set (via the Set, SetUint, etc. methods) only if it is both -// addressable and not read-only. -// -// The two permission bits - addressable and read-only - are stored in -// the bottom two bits of the type pointer in the interface value. -// -// ordinary value: Internal = value -// addressable value: Internal = value, Internal.typ |= flagAddr -// read-only value: Internal = value, Internal.typ |= flagRO -// addressable, read-only value: Internal = value, Internal.typ |= flagAddr | flagRO -// -// It is important that the read-only values have the extra bit set -// (as opposed to using the bit to mean writable), because client code -// can grab the interface field and try to use it. Having the extra bit -// set makes the type pointer compare not equal to any real type, -// so that a client cannot, say, write through v.Internal.(*int). -// The runtime routines that access interface types reject types with -// low bits set. -// -// If a Value fv = v.Method(i), then fv = v with the InternalMethod -// field set to i+1. Methods are never addressable. -// -// All in all, this is a lot of effort just to avoid making this new API -// depend on a language change we'll probably do anyway, but -// it's helpful to keep the two separate, and much of the logic is -// necessary to implement the Interface method anyway. - -const ( - flagAddr uint32 = 1 << iota // holds address of value - flagRO // read-only - - reflectFlags = 3 -) - -// An internalValue is the unpacked form of a Value. -// The zero Value unpacks to a zero internalValue -type internalValue struct { - typ *commonType // type of value - kind Kind // kind of value - flag uint32 - word iword - addr unsafe.Pointer - rcvr iword - method bool - nilmethod bool -} - -func (v Value) internal() internalValue { - var iv internalValue - eface := *(*emptyInterface)(unsafe.Pointer(&v.Internal)) - p := uintptr(unsafe.Pointer(eface.typ)) - iv.typ = toCommonType((*runtime.Type)(unsafe.Pointer(p &^ reflectFlags))) - if iv.typ == nil { - return iv - } - iv.flag = uint32(p & reflectFlags) - iv.word = eface.word - if iv.flag&flagAddr != 0 { - iv.addr = unsafe.Pointer(uintptr(iv.word)) - iv.typ = iv.typ.Elem().common() - if Kind(iv.typ.kind) == Ptr || Kind(iv.typ.kind) == UnsafePointer { - iv.word = loadIword(iv.addr, iv.typ.size) - } - } else { - if Kind(iv.typ.kind) != Ptr && Kind(iv.typ.kind) != UnsafePointer { - iv.addr = unsafe.Pointer(uintptr(iv.word)) - } +// mustBe panics if f's kind is not expected. +// Making this a method on flag instead of on Value +// (and embedding flag in Value) means that we can write +// the very clear v.mustBe(Bool) and have it compile into +// v.flag.mustBe(Bool), which will only bother to copy the +// single important word for the receiver. +func (f flag) mustBe(expected Kind) { + k := f.kind() + if k != expected { + panic(&ValueError{methodName(), k}) } - iv.kind = iv.typ.Kind() - - // Is this a method? If so, iv describes the receiver. - // Rewrite to describe the method function. - if v.InternalMethod != 0 { - // If this Value is a method value (x.Method(i) for some Value x) - // then we will invoke it using the interface form of the method, - // which always passes the receiver as a single word. - // Record that information. - i := v.InternalMethod - 1 - if iv.kind == Interface { - it := (*interfaceType)(unsafe.Pointer(iv.typ)) - if i < 0 || i >= len(it.methods) { - panic("reflect: broken Value") - } - m := &it.methods[i] - if m.pkgPath != nil { - iv.flag |= flagRO - } - iv.typ = toCommonType(m.typ) - iface := (*nonEmptyInterface)(iv.addr) - if iface.itab == nil { - iv.word = 0 - iv.nilmethod = true - } else { - iv.word = iword(uintptr(iface.itab.fun[i])) - } - iv.rcvr = iface.word - } else { - ut := iv.typ.uncommon() - if ut == nil || i < 0 || i >= len(ut.methods) { - panic("reflect: broken Value") - } - m := &ut.methods[i] - if m.pkgPath != nil { - iv.flag |= flagRO - } - iv.typ = toCommonType(m.mtyp) - iv.rcvr = iv.word - iv.word = iword(uintptr(m.tfn)) - } - if iv.word != 0 { - p := new(iword) - *p = iv.word - iv.word = iword(uintptr(unsafe.Pointer(p))) - } - iv.kind = Func - iv.method = true - iv.flag &^= flagAddr - iv.addr = unsafe.Pointer(uintptr(iv.word)) - } - - return iv } -// packValue returns a Value with the given flag bits, type, and interface word. -func packValue(flag uint32, typ *runtime.Type, word iword) Value { - if typ == nil { - panic("packValue") +// mustBeExported panics if f records that the value was obtained using +// an unexported field. +func (f flag) mustBeExported() { + if f == 0 { + panic(&ValueError{methodName(), 0}) } - t := uintptr(unsafe.Pointer(typ)) - t |= uintptr(flag) - eface := emptyInterface{(*runtime.Type)(unsafe.Pointer(t)), word} - return Value{Internal: *(*interface{})(unsafe.Pointer(&eface))} -} - -var dummy struct { - b bool - x interface{} -} - -// Dummy annotation marking that the value x escapes, -// for use in cases where the reflect code is so clever that -// the compiler cannot follow. -func escapes(x interface{}) { - if dummy.b { - dummy.x = x - } -} - -// valueFromAddr returns a Value using the given type and address. -func valueFromAddr(flag uint32, typ Type, addr unsafe.Pointer) Value { - // TODO(rsc): Eliminate this terrible hack. - // The escape analysis knows that addr is a pointer - // but it doesn't see addr get passed to anything - // that keeps it. packValue keeps it, but packValue - // takes a uintptr (iword(addr)), and integers (non-pointers) - // are assumed not to matter. The escapes function works - // because return values always escape (for now). - escapes(addr) - - if flag&flagAddr != 0 { - // Addressable, so the internal value is - // an interface containing a pointer to the real value. - return packValue(flag, PtrTo(typ).runtimeType(), iword(uintptr(addr))) - } - - var w iword - if k := typ.Kind(); k == Ptr || k == UnsafePointer { - // In line, so the interface word is the actual value. - w = loadIword(addr, typ.Size()) - } else { - // Not in line: the interface word is the address. - w = iword(uintptr(addr)) - } - return packValue(flag, typ.runtimeType(), w) -} - -// valueFromIword returns a Value using the given type and interface word. -func valueFromIword(flag uint32, typ Type, w iword) Value { - if flag&flagAddr != 0 { - panic("reflect: internal error: valueFromIword addressable") - } - return packValue(flag, typ.runtimeType(), w) -} - -func (iv internalValue) mustBe(want Kind) { - if iv.kind != want { - panic(&ValueError{methodName(), iv.kind}) - } -} - -func (iv internalValue) mustBeExported() { - if iv.kind == 0 { - panic(&ValueError{methodName(), iv.kind}) - } - if iv.flag&flagRO != 0 { + if f&flagRO != 0 { panic(methodName() + " using value obtained using unexported field") } } -func (iv internalValue) mustBeAssignable() { - if iv.kind == 0 { - panic(&ValueError{methodName(), iv.kind}) +// mustBeAssignable panics if f records that the value is not assignable, +// which is to say that either it was obtained using an unexported field +// or it is not addressable. +func (f flag) mustBeAssignable() { + if f == 0 { + panic(&ValueError{methodName(), Invalid}) } // Assignable if addressable and not read-only. - if iv.flag&flagRO != 0 { + if f&flagRO != 0 { panic(methodName() + " using value obtained using unexported field") } - if iv.flag&flagAddr == 0 { + if f&flagAddr == 0 { panic(methodName() + " using unaddressable value") } } @@ -407,31 +267,31 @@ func (iv internalValue) mustBeAssignable() { // or slice element in order to call a method that requires a // pointer receiver. func (v Value) Addr() Value { - iv := v.internal() - if iv.flag&flagAddr == 0 { + if v.flag&flagAddr == 0 { panic("reflect.Value.Addr of unaddressable value") } - return valueFromIword(iv.flag&flagRO, PtrTo(iv.typ.toType()), iword(uintptr(iv.addr))) + return Value{v.typ.ptrTo(), v.val, (v.flag & flagRO) | flag(Ptr)<<flagKindShift} } // Bool returns v's underlying value. // It panics if v's kind is not Bool. func (v Value) Bool() bool { - iv := v.internal() - iv.mustBe(Bool) - return *(*bool)(unsafe.Pointer(iv.addr)) + v.mustBe(Bool) + if v.flag&flagIndir != 0 { + return *(*bool)(v.val) + } + return *(*bool)(unsafe.Pointer(&v.val)) } // Bytes returns v's underlying value. // It panics if v's underlying value is not a slice of bytes. func (v Value) Bytes() []byte { - iv := v.internal() - iv.mustBe(Slice) - typ := iv.typ.toType() - if typ.Elem().Kind() != Uint8 { + v.mustBe(Slice) + if v.typ.Elem().Kind() != Uint8 { panic("reflect.Value.Bytes of non-byte slice") } - return *(*[]byte)(iv.addr) + // Slice is always bigger than a word; assume flagIndir. + return *(*[]byte)(v.val) } // CanAddr returns true if the value's address can be obtained with Addr. @@ -440,8 +300,7 @@ func (v Value) Bytes() []byte { // a field of an addressable struct, or the result of dereferencing a pointer. // If CanAddr returns false, calling Addr will panic. func (v Value) CanAddr() bool { - iv := v.internal() - return iv.flag&flagAddr != 0 + return v.flag&flagAddr != 0 } // CanSet returns true if the value of v can be changed. @@ -450,8 +309,7 @@ func (v Value) CanAddr() bool { // If CanSet returns false, calling Set or any type-specific // setter (e.g., SetBool, SetInt64) will panic. func (v Value) CanSet() bool { - iv := v.internal() - return iv.flag&(flagAddr|flagRO) == flagAddr + return v.flag&(flagAddr|flagRO) == flagAddr } // Call calls the function v with the input arguments in. @@ -463,10 +321,9 @@ func (v Value) CanSet() bool { // If v is a variadic function, Call creates the variadic slice parameter // itself, copying in the corresponding values. func (v Value) Call(in []Value) []Value { - iv := v.internal() - iv.mustBe(Func) - iv.mustBeExported() - return iv.call("Call", in) + v.mustBe(Func) + v.mustBeExported() + return v.call("Call", in) } // CallSlice calls the variadic function v with the input arguments in, @@ -477,22 +334,60 @@ func (v Value) Call(in []Value) []Value { // As in Go, each input argument must be assignable to the // type of the function's corresponding input parameter. func (v Value) CallSlice(in []Value) []Value { - iv := v.internal() - iv.mustBe(Func) - iv.mustBeExported() - return iv.call("CallSlice", in) -} - -func (iv internalValue) call(method string, in []Value) []Value { - if iv.word == 0 { - if iv.nilmethod { - panic("reflect.Value.Call: call of method on nil interface value") + v.mustBe(Func) + v.mustBeExported() + return v.call("CallSlice", in) +} + +func (v Value) call(method string, in []Value) []Value { + // Get function pointer, type. + t := v.typ + var ( + fn unsafe.Pointer + rcvr iword + ) + if v.flag&flagMethod != 0 { + i := int(v.flag) >> flagMethodShift + if v.typ.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(v.typ)) + if i < 0 || i >= len(tt.methods) { + panic("reflect: broken Value") + } + m := &tt.methods[i] + if m.pkgPath != nil { + panic(method + " of unexported method") + } + t = toCommonType(m.typ) + iface := (*nonEmptyInterface)(v.val) + if iface.itab == nil { + panic(method + " of method on nil interface value") + } + fn = iface.itab.fun[i] + rcvr = iface.word + } else { + ut := v.typ.uncommon() + if ut == nil || i < 0 || i >= len(ut.methods) { + panic("reflect: broken Value") + } + m := &ut.methods[i] + if m.pkgPath != nil { + panic(method + " of unexported method") + } + fn = m.tfn + t = toCommonType(m.mtyp) + rcvr = v.iword() } + } else if v.flag&flagIndir != 0 { + fn = *(*unsafe.Pointer)(v.val) + } else { + fn = v.val + } + + if fn == nil { panic("reflect.Value.Call: call of nil function") } isSlice := method == "CallSlice" - t := iv.typ n := t.NumIn() if isSlice { if !t.IsVariadic() { @@ -549,34 +444,32 @@ func (iv internalValue) call(method string, in []Value) []Value { } nout := t.NumOut() - if iv.method { + if v.flag&flagMethod != 0 { nin++ } params := make([]unsafe.Pointer, nin) delta := 0 off := 0 - if iv.method { + if v.flag&flagMethod != 0 { // Hard-wired first argument. p := new(iword) - *p = iv.rcvr + *p = rcvr params[0] = unsafe.Pointer(p) off = 1 } - first_pointer := false - for i, v := range in { - siv := v.internal() - siv.mustBeExported() + for i, pv := range in { + pv.mustBeExported() targ := t.In(i).(*commonType) - siv = convertForAssignment("reflect.Value.Call", nil, targ, siv) - if siv.addr == nil { + pv = pv.assignTo("reflect.Value.Call", targ, nil) + if pv.flag&flagIndir == 0 { p := new(unsafe.Pointer) - *p = unsafe.Pointer(uintptr(siv.word)) + *p = pv.val params[off] = unsafe.Pointer(p) } else { - params[off] = siv.addr + params[off] = pv.val } - if i == 0 && Kind(targ.kind) != Ptr && !iv.method && isMethod(iv.typ) { + if i == 0 && Kind(targ.kind) != Ptr && v.flag&flagMethod == 0 && isMethod(v.typ) { p := new(unsafe.Pointer) *p = params[off] params[off] = unsafe.Pointer(p) @@ -602,7 +495,7 @@ func (iv internalValue) call(method string, in []Value) []Value { pr = &results[0] } - call(t, *(*unsafe.Pointer)(iv.addr), iv.method, first_pointer, pp, pr) + call(t, fn, v.flag&flagMethod != 0, first_pointer, pp, pr) return ret } @@ -635,39 +528,42 @@ func isMethod(t *commonType) bool { // Cap returns v's capacity. // It panics if v's Kind is not Array, Chan, or Slice. func (v Value) Cap() int { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Array: - return iv.typ.Len() + return v.typ.Len() case Chan: - return int(chancap(*(*iword)(iv.addr))) + return int(chancap(*(*iword)(v.iword()))) case Slice: - return (*SliceHeader)(iv.addr).Cap + // Slice is always bigger than a word; assume flagIndir. + return (*SliceHeader)(v.val).Cap } - panic(&ValueError{"reflect.Value.Cap", iv.kind}) + panic(&ValueError{"reflect.Value.Cap", k}) } // Close closes the channel v. // It panics if v's Kind is not Chan. func (v Value) Close() { - iv := v.internal() - iv.mustBe(Chan) - iv.mustBeExported() - ch := *(*iword)(iv.addr) - chanclose(ch) + v.mustBe(Chan) + v.mustBeExported() + chanclose(*(*iword)(v.iword())) } // Complex returns v's underlying value, as a complex128. // It panics if v's Kind is not Complex64 or Complex128 func (v Value) Complex() complex128 { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Complex64: - return complex128(*(*complex64)(iv.addr)) + if v.flag&flagIndir != 0 { + return complex128(*(*complex64)(v.val)) + } + return complex128(*(*complex64)(unsafe.Pointer(&v.val))) case Complex128: - return *(*complex128)(iv.addr) + // complex128 is always bigger than a word; assume flagIndir. + return *(*complex128)(v.val) } - panic(&ValueError{"reflect.Value.Complex", iv.kind}) + panic(&ValueError{"reflect.Value.Complex", k}) } // Elem returns the value that the interface v contains @@ -675,90 +571,94 @@ func (v Value) Complex() complex128 { // It panics if v's Kind is not Interface or Ptr. // It returns the zero Value if v is nil. func (v Value) Elem() Value { - iv := v.internal() - return iv.Elem() -} - -func (iv internalValue) Elem() Value { - switch iv.kind { + k := v.kind() + switch k { case Interface: - // Empty interface and non-empty interface have different layouts. - // Convert to empty interface. - var eface emptyInterface - if iv.typ.NumMethod() == 0 { - eface = *(*emptyInterface)(iv.addr) + var ( + typ *commonType + val unsafe.Pointer + ) + if v.typ.NumMethod() == 0 { + eface := (*emptyInterface)(v.val) + if eface.typ == nil { + // nil interface value + return Value{} + } + typ = toCommonType(eface.typ) + val = unsafe.Pointer(eface.word) } else { - iface := (*nonEmptyInterface)(iv.addr) - if iface.itab != nil { - eface.typ = iface.itab.typ + iface := (*nonEmptyInterface)(v.val) + if iface.itab == nil { + // nil interface value + return Value{} } - eface.word = iface.word + typ = toCommonType(iface.itab.typ) + val = unsafe.Pointer(iface.word) } - if eface.typ == nil { - return Value{} + fl := v.flag & flagRO + fl |= flag(typ.Kind()) << flagKindShift + if typ.Kind() != Ptr && typ.Kind() != UnsafePointer { + fl |= flagIndir } - return valueFromIword(iv.flag&flagRO, toType(eface.typ), eface.word) + return Value{typ, val, fl} case Ptr: + val := v.val + if v.flag&flagIndir != 0 { + val = *(*unsafe.Pointer)(val) + } // The returned value's address is v's value. - if iv.word == 0 { + if val == nil { return Value{} } - return valueFromAddr(iv.flag&flagRO|flagAddr, iv.typ.Elem(), unsafe.Pointer(uintptr(iv.word))) + tt := (*ptrType)(unsafe.Pointer(v.typ)) + typ := toCommonType(tt.elem) + fl := v.flag&flagRO | flagIndir | flagAddr + fl |= flag(typ.Kind() << flagKindShift) + return Value{typ, val, fl} } - panic(&ValueError{"reflect.Value.Elem", iv.kind}) + panic(&ValueError{"reflect.Value.Elem", k}) } // Field returns the i'th field of the struct v. // It panics if v's Kind is not Struct or i is out of range. func (v Value) Field(i int) Value { - iv := v.internal() - iv.mustBe(Struct) - t := iv.typ.toType() - if i < 0 || i >= t.NumField() { + v.mustBe(Struct) + tt := (*structType)(unsafe.Pointer(v.typ)) + if i < 0 || i >= len(tt.fields) { panic("reflect: Field index out of range") } - f := t.Field(i) + field := &tt.fields[i] + typ := toCommonType(field.typ) // Inherit permission bits from v. - flag := iv.flag + fl := v.flag & (flagRO | flagIndir | flagAddr) // Using an unexported field forces flagRO. - if f.PkgPath != "" { - flag |= flagRO + if field.pkgPath != nil { + fl |= flagRO } - return valueFromValueOffset(flag, f.Type, iv, f.Offset) -} + fl |= flag(typ.Kind()) << flagKindShift -// valueFromValueOffset returns a sub-value of outer -// (outer is an array or a struct) with the given flag and type -// starting at the given byte offset into outer. -func valueFromValueOffset(flag uint32, typ Type, outer internalValue, offset uintptr) Value { - if outer.addr != nil { - return valueFromAddr(flag, typ, unsafe.Pointer(uintptr(outer.addr)+offset)) + var val unsafe.Pointer + switch { + case fl&flagIndir != 0: + // Indirect. Just bump pointer. + val = unsafe.Pointer(uintptr(v.val) + field.offset) + case bigEndian: + // Direct. Discard leading bytes. + val = unsafe.Pointer(uintptr(v.val) << (field.offset * 8)) + default: + // Direct. Discard leading bytes. + val = unsafe.Pointer(uintptr(v.val) >> (field.offset * 8)) } - // outer is so tiny it is in line. - // We have to use outer.word and derive - // the new word (it cannot possibly be bigger). - // In line, so not addressable. - if flag&flagAddr != 0 { - panic("reflect: internal error: misuse of valueFromValueOffset") - } - b := *(*[ptrSize]byte)(unsafe.Pointer(&outer.word)) - for i := uintptr(0); i < typ.Size(); i++ { - b[i] = b[offset+i] - } - for i := typ.Size(); i < ptrSize; i++ { - b[i] = 0 - } - w := *(*iword)(unsafe.Pointer(&b)) - return valueFromIword(flag, typ, w) + return Value{typ, val, fl} } // FieldByIndex returns the nested field corresponding to index. // It panics if v's Kind is not struct. func (v Value) FieldByIndex(index []int) Value { - v.internal().mustBe(Struct) + v.mustBe(Struct) for i, x := range index { if i > 0 { if v.Kind() == Ptr && v.Elem().Kind() == Struct { @@ -774,9 +674,8 @@ func (v Value) FieldByIndex(index []int) Value { // It returns the zero Value if no field was found. // It panics if v's Kind is not struct. func (v Value) FieldByName(name string) Value { - iv := v.internal() - iv.mustBe(Struct) - if f, ok := iv.typ.FieldByName(name); ok { + v.mustBe(Struct) + if f, ok := v.typ.FieldByName(name); ok { return v.FieldByIndex(f.Index) } return Value{} @@ -787,8 +686,8 @@ func (v Value) FieldByName(name string) Value { // It panics if v's Kind is not struct. // It returns the zero Value if no field was found. func (v Value) FieldByNameFunc(match func(string) bool) Value { - v.internal().mustBe(Struct) - if f, ok := v.Type().FieldByNameFunc(match); ok { + v.mustBe(Struct) + if f, ok := v.typ.FieldByNameFunc(match); ok { return v.FieldByIndex(f.Index) } return Value{} @@ -797,74 +696,101 @@ func (v Value) FieldByNameFunc(match func(string) bool) Value { // Float returns v's underlying value, as an float64. // It panics if v's Kind is not Float32 or Float64 func (v Value) Float() float64 { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Float32: - return float64(*(*float32)(iv.addr)) + if v.flag&flagIndir != 0 { + return float64(*(*float32)(v.val)) + } + return float64(*(*float32)(unsafe.Pointer(&v.val))) case Float64: - return *(*float64)(iv.addr) + if v.flag&flagIndir != 0 { + return *(*float64)(v.val) + } + return *(*float64)(unsafe.Pointer(&v.val)) } - panic(&ValueError{"reflect.Value.Float", iv.kind}) + panic(&ValueError{"reflect.Value.Float", k}) } // Index returns v's i'th element. // It panics if v's Kind is not Array or Slice or i is out of range. func (v Value) Index(i int) Value { - iv := v.internal() - switch iv.kind { - default: - panic(&ValueError{"reflect.Value.Index", iv.kind}) + k := v.kind() + switch k { case Array: - flag := iv.flag // element flag same as overall array - t := iv.typ.toType() - if i < 0 || i > t.Len() { + tt := (*arrayType)(unsafe.Pointer(v.typ)) + if i < 0 || i > int(tt.len) { panic("reflect: array index out of range") } - typ := t.Elem() - return valueFromValueOffset(flag, typ, iv, uintptr(i)*typ.Size()) + typ := toCommonType(tt.elem) + fl := v.flag & (flagRO | flagIndir | flagAddr) // bits same as overall array + fl |= flag(typ.Kind()) << flagKindShift + offset := uintptr(i) * typ.size + + var val unsafe.Pointer + switch { + case fl&flagIndir != 0: + // Indirect. Just bump pointer. + val = unsafe.Pointer(uintptr(v.val) + offset) + case bigEndian: + // Direct. Discard leading bytes. + val = unsafe.Pointer(uintptr(v.val) << (offset * 8)) + default: + // Direct. Discard leading bytes. + val = unsafe.Pointer(uintptr(v.val) >> (offset * 8)) + } + return Value{typ, val, fl} case Slice: // Element flag same as Elem of Ptr. - // Addressable, possibly read-only. - flag := iv.flag&flagRO | flagAddr - s := (*SliceHeader)(iv.addr) + // Addressable, indirect, possibly read-only. + fl := flagAddr | flagIndir | v.flag&flagRO + s := (*SliceHeader)(v.val) if i < 0 || i >= s.Len { panic("reflect: slice index out of range") } - typ := iv.typ.Elem() - addr := unsafe.Pointer(s.Data + uintptr(i)*typ.Size()) - return valueFromAddr(flag, typ, addr) + tt := (*sliceType)(unsafe.Pointer(v.typ)) + typ := toCommonType(tt.elem) + fl |= flag(typ.Kind()) << flagKindShift + val := unsafe.Pointer(s.Data + uintptr(i)*typ.size) + return Value{typ, val, fl} } - - panic("not reached") + panic(&ValueError{"reflect.Value.Index", k}) } // Int returns v's underlying value, as an int64. // It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64. func (v Value) Int() int64 { - iv := v.internal() - switch iv.kind { + k := v.kind() + var p unsafe.Pointer + if v.flag&flagIndir != 0 { + p = v.val + } else { + // The escape analysis is good enough that &v.val + // does not trigger a heap allocation. + p = unsafe.Pointer(&v.val) + } + switch k { case Int: - return int64(*(*int)(iv.addr)) + return int64(*(*int)(p)) case Int8: - return int64(*(*int8)(iv.addr)) + return int64(*(*int8)(p)) case Int16: - return int64(*(*int16)(iv.addr)) + return int64(*(*int16)(p)) case Int32: - return int64(*(*int32)(iv.addr)) + return int64(*(*int32)(p)) case Int64: - return *(*int64)(iv.addr) + return int64(*(*int64)(p)) } - panic(&ValueError{"reflect.Value.Int", iv.kind}) + panic(&ValueError{"reflect.Value.Int", k}) } // CanInterface returns true if Interface can be used without panicking. func (v Value) CanInterface() bool { - iv := v.internal() - if iv.kind == Invalid { - panic(&ValueError{"reflect.Value.CanInterface", iv.kind}) + if v.flag == 0 { + panic(&ValueError{"reflect.Value.CanInterface", Invalid}) } - return v.InternalMethod == 0 && iv.flag&flagRO == 0 + return v.flag&(flagMethod|flagRO) == 0 } // Interface returns v's value as an interface{}. @@ -876,75 +802,72 @@ func (v Value) Interface() interface{} { } func valueInterface(v Value, safe bool) interface{} { - iv := v.internal() - return iv.valueInterface(safe) -} - -func (iv internalValue) valueInterface(safe bool) interface{} { - if iv.kind == 0 { - panic(&ValueError{"reflect.Value.Interface", iv.kind}) + if v.flag == 0 { + panic(&ValueError{"reflect.Value.Interface", 0}) } - if iv.method { + if v.flag&flagMethod != 0 { panic("reflect.Value.Interface: cannot create interface value for method with bound receiver") } - if safe && iv.flag&flagRO != 0 { + if safe && v.flag&flagRO != 0 { // Do not allow access to unexported values via Interface, // because they might be pointers that should not be // writable or methods or function that should not be callable. panic("reflect.Value.Interface: cannot return value obtained from unexported field or method") } - if iv.kind == Interface { + + k := v.kind() + if k == Interface { // Special case: return the element inside the interface. - // Won't recurse further because an interface cannot contain an interface. - if iv.IsNil() { - return nil + // Empty interface has one layout, all interfaces with + // methods have a second layout. + if v.NumMethod() == 0 { + return *(*interface{})(v.val) } - return iv.Elem().Interface() + return *(*interface { + M() + })(v.val) } // Non-interface value. var eface emptyInterface - eface.typ = iv.typ.runtimeType() - eface.word = iv.word + eface.typ = v.typ.runtimeType() + eface.word = v.iword() return *(*interface{})(unsafe.Pointer(&eface)) } // InterfaceData returns the interface v's value as a uintptr pair. // It panics if v's Kind is not Interface. func (v Value) InterfaceData() [2]uintptr { - iv := v.internal() - iv.mustBe(Interface) + v.mustBe(Interface) // We treat this as a read operation, so we allow // it even for unexported data, because the caller // has to import "unsafe" to turn it into something // that can be abused. - return *(*[2]uintptr)(iv.addr) + // Interface value is always bigger than a word; assume flagIndir. + return *(*[2]uintptr)(v.val) } // IsNil returns true if v is a nil value. // It panics if v's Kind is not Chan, Func, Interface, Map, Ptr, or Slice. func (v Value) IsNil() bool { - return v.internal().IsNil() -} - -func (iv internalValue) IsNil() bool { - switch iv.kind { - case Ptr: - if iv.method { + k := v.kind() + switch k { + case Chan, Func, Map, Ptr: + if v.flag&flagMethod != 0 { panic("reflect: IsNil of method Value") } - return iv.word == 0 - case Chan, Func, Map: - if iv.method { - panic("reflect: IsNil of method Value") + ptr := v.val + if v.flag&flagIndir != 0 { + ptr = *(*unsafe.Pointer)(ptr) } - return *(*uintptr)(iv.addr) == 0 + return ptr == nil case Interface, Slice: // Both interface and slice are nil if first word is 0. - return *(*uintptr)(iv.addr) == 0 + // Both are always bigger than a word; assume flagIndir. + return *(*unsafe.Pointer)(v.val) == nil } - panic(&ValueError{"reflect.Value.IsNil", iv.kind}) + panic(&ValueError{"reflect.Value.IsNil", k}) } // IsValid returns true if v represents a value. @@ -953,32 +876,35 @@ func (iv internalValue) IsNil() bool { // Most functions and methods never return an invalid value. // If one does, its documentation states the conditions explicitly. func (v Value) IsValid() bool { - return v.Internal != nil + return v.flag != 0 } // Kind returns v's Kind. // If v is the zero Value (IsValid returns false), Kind returns Invalid. func (v Value) Kind() Kind { - return v.internal().kind + return v.kind() } // Len returns v's length. // It panics if v's Kind is not Array, Chan, Map, Slice, or String. func (v Value) Len() int { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Array: - return iv.typ.Len() + tt := (*arrayType)(unsafe.Pointer(v.typ)) + return int(tt.len) case Chan: - return int(chanlen(*(*iword)(iv.addr))) + return int(chanlen(*(*iword)(v.iword()))) case Map: - return int(maplen(*(*iword)(iv.addr))) + return int(maplen(*(*iword)(v.iword()))) case Slice: - return (*SliceHeader)(iv.addr).Len + // Slice is bigger than a word; assume flagIndir. + return (*SliceHeader)(v.val).Len case String: - return (*StringHeader)(iv.addr).Len + // String is bigger than a word; assume flagIndir. + return (*StringHeader)(v.val).Len } - panic(&ValueError{"reflect.Value.Len", iv.kind}) + panic(&ValueError{"reflect.Value.Len", k}) } // MapIndex returns the value associated with key in the map v. @@ -986,29 +912,29 @@ func (v Value) Len() int { // It returns the zero Value if key is not found in the map or if v represents a nil map. // As in Go, the key's value must be assignable to the map's key type. func (v Value) MapIndex(key Value) Value { - iv := v.internal() - iv.mustBe(Map) - typ := iv.typ.toType() + v.mustBe(Map) + tt := (*mapType)(unsafe.Pointer(v.typ)) - // Do not require ikey to be exported, so that DeepEqual + // Do not require key to be exported, so that DeepEqual // and other programs can use all the keys returned by // MapKeys as arguments to MapIndex. If either the map // or the key is unexported, though, the result will be - // considered unexported. - - ikey := key.internal() - ikey = convertForAssignment("reflect.Value.MapIndex", nil, typ.Key(), ikey) - if iv.word == 0 { - return Value{} - } + // considered unexported. This is consistent with the + // behavior for structs, which allow read but not write + // of unexported fields. + key = key.assignTo("reflect.Value.MapIndex", toCommonType(tt.key), nil) - flag := (iv.flag | ikey.flag) & flagRO - elemType := typ.Elem() - elemWord, ok := mapaccess(typ.runtimeType(), *(*iword)(iv.addr), ikey.word) + word, ok := mapaccess(v.typ.runtimeType(), *(*iword)(v.iword()), key.iword()) if !ok { return Value{} } - return valueFromIword(flag, elemType, elemWord) + typ := toCommonType(tt.elem) + fl := (v.flag | key.flag) & flagRO + if typ.Kind() != Ptr && typ.Kind() != UnsafePointer { + fl |= flagIndir + } + fl |= flag(typ.Kind()) << flagKindShift + return Value{typ, unsafe.Pointer(word), fl} } // MapKeys returns a slice containing all the keys present in the map, @@ -1016,17 +942,22 @@ func (v Value) MapIndex(key Value) Value { // It panics if v's Kind is not Map. // It returns an empty slice if v represents a nil map. func (v Value) MapKeys() []Value { - iv := v.internal() - iv.mustBe(Map) - keyType := iv.typ.Key() + v.mustBe(Map) + tt := (*mapType)(unsafe.Pointer(v.typ)) + keyType := toCommonType(tt.key) + + fl := v.flag & flagRO + fl |= flag(keyType.Kind()) << flagKindShift + if keyType.Kind() != Ptr && keyType.Kind() != UnsafePointer { + fl |= flagIndir + } - flag := iv.flag & flagRO - m := *(*iword)(iv.addr) + m := *(*iword)(v.iword()) mlen := int32(0) - if m != 0 { + if m != nil { mlen = maplen(m) } - it := mapiterinit(iv.typ.runtimeType(), m) + it := mapiterinit(v.typ.runtimeType(), m) a := make([]Value, mlen) var i int for i = 0; i < len(a); i++ { @@ -1034,7 +965,7 @@ func (v Value) MapKeys() []Value { if !ok { break } - a[i] = valueFromIword(flag, keyType, keyWord) + a[i] = Value{keyType, unsafe.Pointer(keyWord), fl} mapiternext(it) } return a[:i] @@ -1045,23 +976,27 @@ func (v Value) MapKeys() []Value { // a receiver; the returned function will always use v as the receiver. // Method panics if i is out of range. func (v Value) Method(i int) Value { - iv := v.internal() - if iv.kind == Invalid { + if v.typ == nil { panic(&ValueError{"reflect.Value.Method", Invalid}) } - if i < 0 || i >= iv.typ.NumMethod() { + if v.flag&flagMethod != 0 || i < 0 || i >= v.typ.NumMethod() { panic("reflect: Method index out of range") } - return Value{v.Internal, i + 1} + fl := v.flag & (flagRO | flagAddr | flagIndir) + fl |= flag(Func) << flagKindShift + fl |= flag(i)<<flagMethodShift | flagMethod + return Value{v.typ, v.val, fl} } // NumMethod returns the number of methods in the value's method set. func (v Value) NumMethod() int { - iv := v.internal() - if iv.kind == Invalid { + if v.typ == nil { panic(&ValueError{"reflect.Value.NumMethod", Invalid}) } - return iv.typ.NumMethod() + if v.flag&flagMethod != 0 { + return 0 + } + return v.typ.NumMethod() } // MethodByName returns a function value corresponding to the method @@ -1070,49 +1005,51 @@ func (v Value) NumMethod() int { // a receiver; the returned function will always use v as the receiver. // It returns the zero Value if no method was found. func (v Value) MethodByName(name string) Value { - iv := v.internal() - if iv.kind == Invalid { + if v.typ == nil { panic(&ValueError{"reflect.Value.MethodByName", Invalid}) } - m, ok := iv.typ.MethodByName(name) - if ok { - return Value{v.Internal, m.Index + 1} + if v.flag&flagMethod != 0 { + return Value{} } - return Value{} + m, ok := v.typ.MethodByName(name) + if !ok { + return Value{} + } + return v.Method(m.Index) } // NumField returns the number of fields in the struct v. // It panics if v's Kind is not Struct. func (v Value) NumField() int { - iv := v.internal() - iv.mustBe(Struct) - return iv.typ.NumField() + v.mustBe(Struct) + tt := (*structType)(unsafe.Pointer(v.typ)) + return len(tt.fields) } // OverflowComplex returns true if the complex128 x cannot be represented by v's type. // It panics if v's Kind is not Complex64 or Complex128. func (v Value) OverflowComplex(x complex128) bool { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Complex64: return overflowFloat32(real(x)) || overflowFloat32(imag(x)) case Complex128: return false } - panic(&ValueError{"reflect.Value.OverflowComplex", iv.kind}) + panic(&ValueError{"reflect.Value.OverflowComplex", k}) } // OverflowFloat returns true if the float64 x cannot be represented by v's type. // It panics if v's Kind is not Float32 or Float64. func (v Value) OverflowFloat(x float64) bool { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Float32: return overflowFloat32(x) case Float64: return false } - panic(&ValueError{"reflect.Value.OverflowFloat", iv.kind}) + panic(&ValueError{"reflect.Value.OverflowFloat", k}) } func overflowFloat32(x float64) bool { @@ -1125,27 +1062,27 @@ func overflowFloat32(x float64) bool { // OverflowInt returns true if the int64 x cannot be represented by v's type. // It panics if v's Kind is not Int, Int8, int16, Int32, or Int64. func (v Value) OverflowInt(x int64) bool { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Int, Int8, Int16, Int32, Int64: - bitSize := iv.typ.size * 8 + bitSize := v.typ.size * 8 trunc := (x << (64 - bitSize)) >> (64 - bitSize) return x != trunc } - panic(&ValueError{"reflect.Value.OverflowInt", iv.kind}) + panic(&ValueError{"reflect.Value.OverflowInt", k}) } // OverflowUint returns true if the uint64 x cannot be represented by v's type. // It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64. func (v Value) OverflowUint(x uint64) bool { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Uint, Uintptr, Uint8, Uint16, Uint32, Uint64: - bitSize := iv.typ.size * 8 + bitSize := v.typ.size * 8 trunc := (x << (64 - bitSize)) >> (64 - bitSize) return x != trunc } - panic(&ValueError{"reflect.Value.OverflowUint", iv.kind}) + panic(&ValueError{"reflect.Value.OverflowUint", k}) } // Pointer returns v's value as a uintptr. @@ -1154,22 +1091,21 @@ func (v Value) OverflowUint(x uint64) bool { // without importing the unsafe package explicitly. // It panics if v's Kind is not Chan, Func, Map, Ptr, Slice, or UnsafePointer. func (v Value) Pointer() uintptr { - iv := v.internal() - switch iv.kind { - case Ptr, UnsafePointer: - if iv.kind == Func && v.InternalMethod != 0 { + k := v.kind() + switch k { + case Chan, Func, Map, Ptr, UnsafePointer: + if k == Func && v.flag&flagMethod != 0 { panic("reflect.Value.Pointer of method Value") } - return uintptr(iv.word) - case Chan, Func, Map: - if iv.kind == Func && v.InternalMethod != 0 { - panic("reflect.Value.Pointer of method Value") + p := v.val + if v.flag&flagIndir != 0 { + p = *(*unsafe.Pointer)(p) } - return *(*uintptr)(iv.addr) + return uintptr(p) case Slice: - return (*SliceHeader)(iv.addr).Data + return (*SliceHeader)(v.val).Data } - panic(&ValueError{"reflect.Value.Pointer", iv.kind}) + panic(&ValueError{"reflect.Value.Pointer", k}) } // Recv receives and returns a value from the channel v. @@ -1178,25 +1114,26 @@ func (v Value) Pointer() uintptr { // The boolean value ok is true if the value x corresponds to a send // on the channel, false if it is a zero value received because the channel is closed. func (v Value) Recv() (x Value, ok bool) { - iv := v.internal() - iv.mustBe(Chan) - iv.mustBeExported() - return iv.recv(false) + v.mustBe(Chan) + v.mustBeExported() + return v.recv(false) } -// internal recv, possibly non-blocking (nb) -func (iv internalValue) recv(nb bool) (val Value, ok bool) { - t := iv.typ.toType() - if t.ChanDir()&RecvDir == 0 { +// internal recv, possibly non-blocking (nb). +// v is known to be a channel. +func (v Value) recv(nb bool) (val Value, ok bool) { + tt := (*chanType)(unsafe.Pointer(v.typ)) + if ChanDir(tt.dir)&RecvDir == 0 { panic("recv on send-only channel") } - ch := *(*iword)(iv.addr) - if ch == 0 { - panic("recv on nil channel") - } - valWord, selected, ok := chanrecv(iv.typ.runtimeType(), ch, nb) + word, selected, ok := chanrecv(v.typ.runtimeType(), *(*iword)(v.iword()), nb) if selected { - val = valueFromIword(0, t.Elem(), valWord) + typ := toCommonType(tt.elem) + fl := flag(typ.Kind()) << flagKindShift + if typ.Kind() != Ptr && typ.Kind() != UnsafePointer { + fl |= flagIndir + } + val = Value{typ, unsafe.Pointer(word), fl} } return } @@ -1205,128 +1142,114 @@ func (iv internalValue) recv(nb bool) (val Value, ok bool) { // It panics if v's kind is not Chan or if x's type is not the same type as v's element type. // As in Go, x's value must be assignable to the channel's element type. func (v Value) Send(x Value) { - iv := v.internal() - iv.mustBe(Chan) - iv.mustBeExported() - iv.send(x, false) + v.mustBe(Chan) + v.mustBeExported() + v.send(x, false) } -// internal send, possibly non-blocking -func (iv internalValue) send(x Value, nb bool) (selected bool) { - t := iv.typ.toType() - if t.ChanDir()&SendDir == 0 { +// internal send, possibly non-blocking. +// v is known to be a channel. +func (v Value) send(x Value, nb bool) (selected bool) { + tt := (*chanType)(unsafe.Pointer(v.typ)) + if ChanDir(tt.dir)&SendDir == 0 { panic("send on recv-only channel") } - ix := x.internal() - ix.mustBeExported() // do not let unexported x leak - ix = convertForAssignment("reflect.Value.Send", nil, t.Elem(), ix) - ch := *(*iword)(iv.addr) - if ch == 0 { - panic("send on nil channel") - } - return chansend(iv.typ.runtimeType(), ch, ix.word, nb) + x.mustBeExported() + x = x.assignTo("reflect.Value.Send", toCommonType(tt.elem), nil) + return chansend(v.typ.runtimeType(), *(*iword)(v.iword()), x.iword(), nb) } // Set assigns x to the value v. // It panics if CanSet returns false. // As in Go, x's value must be assignable to v's type. func (v Value) Set(x Value) { - iv := v.internal() - ix := x.internal() - - iv.mustBeAssignable() - ix.mustBeExported() // do not let unexported x leak - - ix = convertForAssignment("reflect.Set", iv.addr, iv.typ, ix) - - n := ix.typ.size - if Kind(ix.typ.kind) == Ptr || Kind(ix.typ.kind) == UnsafePointer { - storeIword(iv.addr, ix.word, n) + v.mustBeAssignable() + x.mustBeExported() // do not let unexported x leak + var target *interface{} + if v.kind() == Interface { + target = (*interface{})(v.val) + } + x = x.assignTo("reflect.Set", v.typ, target) + if x.flag&flagIndir != 0 { + memmove(v.val, x.val, v.typ.size) } else { - memmove(iv.addr, ix.addr, n) + storeIword(v.val, iword(x.val), v.typ.size) } } // SetBool sets v's underlying value. // It panics if v's Kind is not Bool or if CanSet() is false. func (v Value) SetBool(x bool) { - iv := v.internal() - iv.mustBeAssignable() - iv.mustBe(Bool) - *(*bool)(iv.addr) = x + v.mustBeAssignable() + v.mustBe(Bool) + *(*bool)(v.val) = x } // SetBytes sets v's underlying value. // It panics if v's underlying value is not a slice of bytes. func (v Value) SetBytes(x []byte) { - iv := v.internal() - iv.mustBeAssignable() - iv.mustBe(Slice) - typ := iv.typ.toType() - if typ.Elem().Kind() != Uint8 { + v.mustBeAssignable() + v.mustBe(Slice) + if v.typ.Elem().Kind() != Uint8 { panic("reflect.Value.SetBytes of non-byte slice") } - *(*[]byte)(iv.addr) = x + *(*[]byte)(v.val) = x } // SetComplex sets v's underlying value to x. // It panics if v's Kind is not Complex64 or Complex128, or if CanSet() is false. func (v Value) SetComplex(x complex128) { - iv := v.internal() - iv.mustBeAssignable() - switch iv.kind { + v.mustBeAssignable() + switch k := v.kind(); k { default: - panic(&ValueError{"reflect.Value.SetComplex", iv.kind}) + panic(&ValueError{"reflect.Value.SetComplex", k}) case Complex64: - *(*complex64)(iv.addr) = complex64(x) + *(*complex64)(v.val) = complex64(x) case Complex128: - *(*complex128)(iv.addr) = x + *(*complex128)(v.val) = x } } // SetFloat sets v's underlying value to x. // It panics if v's Kind is not Float32 or Float64, or if CanSet() is false. func (v Value) SetFloat(x float64) { - iv := v.internal() - iv.mustBeAssignable() - switch iv.kind { + v.mustBeAssignable() + switch k := v.kind(); k { default: - panic(&ValueError{"reflect.Value.SetFloat", iv.kind}) + panic(&ValueError{"reflect.Value.SetFloat", k}) case Float32: - *(*float32)(iv.addr) = float32(x) + *(*float32)(v.val) = float32(x) case Float64: - *(*float64)(iv.addr) = x + *(*float64)(v.val) = x } } // SetInt sets v's underlying value to x. // It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64, or if CanSet() is false. func (v Value) SetInt(x int64) { - iv := v.internal() - iv.mustBeAssignable() - switch iv.kind { + v.mustBeAssignable() + switch k := v.kind(); k { default: - panic(&ValueError{"reflect.Value.SetInt", iv.kind}) + panic(&ValueError{"reflect.Value.SetInt", k}) case Int: - *(*int)(iv.addr) = int(x) + *(*int)(v.val) = int(x) case Int8: - *(*int8)(iv.addr) = int8(x) + *(*int8)(v.val) = int8(x) case Int16: - *(*int16)(iv.addr) = int16(x) + *(*int16)(v.val) = int16(x) case Int32: - *(*int32)(iv.addr) = int32(x) + *(*int32)(v.val) = int32(x) case Int64: - *(*int64)(iv.addr) = x + *(*int64)(v.val) = x } } // SetLen sets v's length to n. // It panics if v's Kind is not Slice. func (v Value) SetLen(n int) { - iv := v.internal() - iv.mustBeAssignable() - iv.mustBe(Slice) - s := (*SliceHeader)(iv.addr) + v.mustBeAssignable() + v.mustBe(Slice) + s := (*SliceHeader)(v.val) if n < 0 || n > int(s.Cap) { panic("reflect: slice length out of range in SetLen") } @@ -1339,88 +1262,84 @@ func (v Value) SetLen(n int) { // As in Go, key's value must be assignable to the map's key type, // and val's value must be assignable to the map's value type. func (v Value) SetMapIndex(key, val Value) { - iv := v.internal() - ikey := key.internal() - ival := val.internal() - - iv.mustBe(Map) - iv.mustBeExported() - - ikey.mustBeExported() - ikey = convertForAssignment("reflect.Value.SetMapIndex", nil, iv.typ.Key(), ikey) - - if ival.kind != Invalid { - ival.mustBeExported() - ival = convertForAssignment("reflect.Value.SetMapIndex", nil, iv.typ.Elem(), ival) + v.mustBe(Map) + v.mustBeExported() + key.mustBeExported() + tt := (*mapType)(unsafe.Pointer(v.typ)) + key = key.assignTo("reflect.Value.SetMapIndex", toCommonType(tt.key), nil) + if val.typ != nil { + val.mustBeExported() + val = val.assignTo("reflect.Value.SetMapIndex", toCommonType(tt.elem), nil) } - - mapassign(iv.typ.runtimeType(), *(*iword)(iv.addr), ikey.word, ival.word, ival.kind != Invalid) + mapassign(v.typ.runtimeType(), *(*iword)(v.iword()), key.iword(), val.iword(), val.typ != nil) } // SetUint sets v's underlying value to x. // It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64, or if CanSet() is false. func (v Value) SetUint(x uint64) { - iv := v.internal() - iv.mustBeAssignable() - switch iv.kind { + v.mustBeAssignable() + switch k := v.kind(); k { default: - panic(&ValueError{"reflect.Value.SetUint", iv.kind}) + panic(&ValueError{"reflect.Value.SetUint", k}) case Uint: - *(*uint)(iv.addr) = uint(x) + *(*uint)(v.val) = uint(x) case Uint8: - *(*uint8)(iv.addr) = uint8(x) + *(*uint8)(v.val) = uint8(x) case Uint16: - *(*uint16)(iv.addr) = uint16(x) + *(*uint16)(v.val) = uint16(x) case Uint32: - *(*uint32)(iv.addr) = uint32(x) + *(*uint32)(v.val) = uint32(x) case Uint64: - *(*uint64)(iv.addr) = x + *(*uint64)(v.val) = x case Uintptr: - *(*uintptr)(iv.addr) = uintptr(x) + *(*uintptr)(v.val) = uintptr(x) } } // SetPointer sets the unsafe.Pointer value v to x. // It panics if v's Kind is not UnsafePointer. func (v Value) SetPointer(x unsafe.Pointer) { - iv := v.internal() - iv.mustBeAssignable() - iv.mustBe(UnsafePointer) - *(*unsafe.Pointer)(iv.addr) = x + v.mustBeAssignable() + v.mustBe(UnsafePointer) + *(*unsafe.Pointer)(v.val) = x } // SetString sets v's underlying value to x. // It panics if v's Kind is not String or if CanSet() is false. func (v Value) SetString(x string) { - iv := v.internal() - iv.mustBeAssignable() - iv.mustBe(String) - *(*string)(iv.addr) = x + v.mustBeAssignable() + v.mustBe(String) + *(*string)(v.val) = x } // Slice returns a slice of v. // It panics if v's Kind is not Array or Slice. func (v Value) Slice(beg, end int) Value { - iv := v.internal() - if iv.kind != Array && iv.kind != Slice { - panic(&ValueError{"reflect.Value.Slice", iv.kind}) - } - cap := v.Cap() - if beg < 0 || end < beg || end > cap { - panic("reflect.Value.Slice: slice index out of bounds") - } - var typ Type - var base uintptr - switch iv.kind { + var ( + cap int + typ *sliceType + base unsafe.Pointer + ) + switch k := v.kind(); k { + default: + panic(&ValueError{"reflect.Value.Slice", k}) case Array: - if iv.flag&flagAddr == 0 { + if v.flag&flagAddr == 0 { panic("reflect.Value.Slice: slice of unaddressable array") } - typ = toType((*arrayType)(unsafe.Pointer(iv.typ)).slice) - base = uintptr(iv.addr) + tt := (*arrayType)(unsafe.Pointer(v.typ)) + cap = int(tt.len) + typ = (*sliceType)(unsafe.Pointer(toCommonType(tt.slice))) + base = v.val case Slice: - typ = iv.typ.toType() - base = (*SliceHeader)(iv.addr).Data + typ = (*sliceType)(unsafe.Pointer(v.typ)) + s := (*SliceHeader)(v.val) + base = unsafe.Pointer(s.Data) + cap = s.Cap + + } + if beg < 0 || end < beg || end > cap { + panic("reflect.Value.Slice: slice index out of bounds") } // Declare slice so that gc can see the base pointer in it. @@ -1428,11 +1347,12 @@ func (v Value) Slice(beg, end int) Value { // Reinterpret as *SliceHeader to edit. s := (*SliceHeader)(unsafe.Pointer(&x)) - s.Data = base + uintptr(beg)*typ.Elem().Size() + s.Data = uintptr(base) + uintptr(beg)*toCommonType(typ.elem).Size() s.Len = end - beg s.Cap = end - beg - return valueFromAddr(iv.flag&flagRO, typ, unsafe.Pointer(&x)) + fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift + return Value{typ.common(), unsafe.Pointer(&x), fl} } // String returns the string v's underlying value, as a string. @@ -1440,16 +1360,15 @@ func (v Value) Slice(beg, end int) Value { // Unlike the other getters, it does not panic if v's Kind is not String. // Instead, it returns a string of the form "<T value>" where T is v's type. func (v Value) String() string { - iv := v.internal() - switch iv.kind { + switch k := v.kind(); k { case Invalid: return "<invalid Value>" case String: - return *(*string)(iv.addr) + return *(*string)(v.val) } // If you call String on a reflect.Value of other type, it's better to // print something than to panic. Useful in debugging. - return "<" + iv.typ.String() + " Value>" + return "<" + v.typ.String() + " Value>" } // TryRecv attempts to receive a value from the channel v but will not block. @@ -1458,10 +1377,9 @@ func (v Value) String() string { // The boolean ok is true if the value x corresponds to a send // on the channel, false if it is a zero value received because the channel is closed. func (v Value) TryRecv() (x Value, ok bool) { - iv := v.internal() - iv.mustBe(Chan) - iv.mustBeExported() - return iv.recv(true) + v.mustBe(Chan) + v.mustBeExported() + return v.recv(true) } // TrySend attempts to send x on the channel v but will not block. @@ -1469,54 +1387,83 @@ func (v Value) TryRecv() (x Value, ok bool) { // It returns true if the value was sent, false otherwise. // As in Go, x's value must be assignable to the channel's element type. func (v Value) TrySend(x Value) bool { - iv := v.internal() - iv.mustBe(Chan) - iv.mustBeExported() - return iv.send(x, true) + v.mustBe(Chan) + v.mustBeExported() + return v.send(x, true) } // Type returns v's type. func (v Value) Type() Type { - t := v.internal().typ - if t == nil { + f := v.flag + if f == 0 { panic(&ValueError{"reflect.Value.Type", Invalid}) } - return t.toType() + if f&flagMethod == 0 { + // Easy case + return v.typ.toType() + } + + // Method value. + // v.typ describes the receiver, not the method type. + i := int(v.flag) >> flagMethodShift + if v.typ.Kind() == Interface { + // Method on interface. + tt := (*interfaceType)(unsafe.Pointer(v.typ)) + if i < 0 || i >= len(tt.methods) { + panic("reflect: broken Value") + } + m := &tt.methods[i] + return toCommonType(m.typ).toType() + } + // Method on concrete type. + ut := v.typ.uncommon() + if ut == nil || i < 0 || i >= len(ut.methods) { + panic("reflect: broken Value") + } + m := &ut.methods[i] + return toCommonType(m.mtyp).toType() } // Uint returns v's underlying value, as a uint64. // It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64. func (v Value) Uint() uint64 { - iv := v.internal() - switch iv.kind { + k := v.kind() + var p unsafe.Pointer + if v.flag&flagIndir != 0 { + p = v.val + } else { + // The escape analysis is good enough that &v.val + // does not trigger a heap allocation. + p = unsafe.Pointer(&v.val) + } + switch k { case Uint: - return uint64(*(*uint)(iv.addr)) + return uint64(*(*uint)(p)) case Uint8: - return uint64(*(*uint8)(iv.addr)) + return uint64(*(*uint8)(p)) case Uint16: - return uint64(*(*uint16)(iv.addr)) + return uint64(*(*uint16)(p)) case Uint32: - return uint64(*(*uint32)(iv.addr)) - case Uintptr: - return uint64(*(*uintptr)(iv.addr)) + return uint64(*(*uint32)(p)) case Uint64: - return *(*uint64)(iv.addr) + return uint64(*(*uint64)(p)) + case Uintptr: + return uint64(*(*uintptr)(p)) } - panic(&ValueError{"reflect.Value.Uint", iv.kind}) + panic(&ValueError{"reflect.Value.Uint", k}) } // UnsafeAddr returns a pointer to v's data. // It is for advanced clients that also import the "unsafe" package. // It panics if v is not addressable. func (v Value) UnsafeAddr() uintptr { - iv := v.internal() - if iv.kind == Invalid { - panic(&ValueError{"reflect.Value.UnsafeAddr", iv.kind}) + if v.typ == nil { + panic(&ValueError{"reflect.Value.UnsafeAddr", Invalid}) } - if iv.flag&flagAddr == 0 { + if v.flag&flagAddr == 0 { panic("reflect.Value.UnsafeAddr of unaddressable value") } - return uintptr(iv.addr) + return uintptr(v.val) } // StringHeader is the runtime representation of a string. @@ -1536,7 +1483,7 @@ type SliceHeader struct { func typesMustMatch(what string, t1, t2 Type) { if t1 != t2 { - panic("reflect: " + what + ": " + t1.String() + " != " + t2.String()) + panic(what + ": " + t1.String() + " != " + t2.String()) } } @@ -1571,7 +1518,7 @@ func grow(s Value, extra int) (Value, int, int) { // Append appends the values x to a slice s and returns the resulting slice. // As in Go, each x's value must be assignable to the slice's element type. func Append(s Value, x ...Value) Value { - s.internal().mustBe(Slice) + s.mustBe(Slice) s, i0, i1 := grow(s, len(x)) for i, j := i0, 0; i < i1; i, j = i+1, j+1 { s.Index(i).Set(x[j]) @@ -1582,8 +1529,8 @@ func Append(s Value, x ...Value) Value { // AppendSlice appends a slice t to a slice s and returns the resulting slice. // The slices s and t must have the same element type. func AppendSlice(s, t Value) Value { - s.internal().mustBe(Slice) - t.internal().mustBe(Slice) + s.mustBe(Slice) + t.mustBe(Slice) typesMustMatch("reflect.AppendSlice", s.Type().Elem(), t.Type().Elem()) s, i0, i1 := grow(s, t.Len()) Copy(s.Slice(i0, i1), t) @@ -1596,23 +1543,23 @@ func AppendSlice(s, t Value) Value { // Dst and src each must have kind Slice or Array, and // dst and src must have the same element type. func Copy(dst, src Value) int { - idst := dst.internal() - isrc := src.internal() - - if idst.kind != Array && idst.kind != Slice { - panic(&ValueError{"reflect.Copy", idst.kind}) + dk := dst.kind() + if dk != Array && dk != Slice { + panic(&ValueError{"reflect.Copy", dk}) } - if idst.kind == Array { - idst.mustBeAssignable() + if dk == Array { + dst.mustBeAssignable() } - idst.mustBeExported() - if isrc.kind != Array && isrc.kind != Slice { - panic(&ValueError{"reflect.Copy", isrc.kind}) + dst.mustBeExported() + + sk := src.kind() + if sk != Array && sk != Slice { + panic(&ValueError{"reflect.Copy", sk}) } - isrc.mustBeExported() + src.mustBeExported() - de := idst.typ.Elem() - se := isrc.typ.Elem() + de := dst.typ.Elem() + se := src.typ.Elem() typesMustMatch("reflect.Copy", de, se) n := dst.Len() @@ -1622,7 +1569,7 @@ func Copy(dst, src Value) int { // If sk is an in-line array, cannot take its address. // Instead, copy element by element. - if isrc.addr == nil { + if src.flag&flagIndir == 0 { for i := 0; i < n; i++ { dst.Index(i).Set(src.Index(i)) } @@ -1631,15 +1578,15 @@ func Copy(dst, src Value) int { // Copy via memmove. var da, sa unsafe.Pointer - if idst.kind == Array { - da = idst.addr + if dk == Array { + da = dst.val } else { - da = unsafe.Pointer((*SliceHeader)(idst.addr).Data) + da = unsafe.Pointer((*SliceHeader)(dst.val).Data) } - if isrc.kind == Array { - sa = isrc.addr + if sk == Array { + sa = src.val } else { - sa = unsafe.Pointer((*SliceHeader)(isrc.addr).Data) + sa = unsafe.Pointer((*SliceHeader)(src.val).Data) } memmove(da, sa, uintptr(n)*de.Size()) return n @@ -1653,7 +1600,7 @@ func Copy(dst, src Value) int { // for the specified slice type, length, and capacity. func MakeSlice(typ Type, len, cap int) Value { if typ.Kind() != Slice { - panic("reflect: MakeSlice of non-slice type") + panic("reflect.MakeSlice of non-slice type") } // Declare slice so that gc can see the base pointer in it. @@ -1665,31 +1612,31 @@ func MakeSlice(typ Type, len, cap int) Value { s.Len = len s.Cap = cap - return valueFromAddr(0, typ, unsafe.Pointer(&x)) + return Value{typ.common(), unsafe.Pointer(&x), flagIndir | flag(Slice)<<flagKindShift} } // MakeChan creates a new channel with the specified type and buffer size. func MakeChan(typ Type, buffer int) Value { if typ.Kind() != Chan { - panic("reflect: MakeChan of non-chan type") + panic("reflect.MakeChan of non-chan type") } if buffer < 0 { - panic("MakeChan: negative buffer size") + panic("reflect.MakeChan: negative buffer size") } if typ.ChanDir() != BothDir { - panic("MakeChan: unidirectional channel type") + panic("reflect.MakeChan: unidirectional channel type") } ch := makechan(typ.runtimeType(), uint32(buffer)) - return valueFromIword(0, typ, ch) + return Value{typ.common(), unsafe.Pointer(ch), flagIndir | (flag(Chan)<<flagKindShift)} } // MakeMap creates a new map of the specified type. func MakeMap(typ Type) Value { if typ.Kind() != Map { - panic("reflect: MakeMap of non-map type") + panic("reflect.MakeMap of non-map type") } m := makemap(typ.runtimeType()) - return valueFromIword(0, typ, m) + return Value{typ.common(), unsafe.Pointer(m), flagIndir | (flag(Map)<<flagKindShift)} } // Indirect returns the value that v points to. @@ -1719,7 +1666,12 @@ func ValueOf(i interface{}) Value { // For an interface value with the noAddr bit set, // the representation is identical to an empty interface. eface := *(*emptyInterface)(unsafe.Pointer(&i)) - return packValue(0, eface.typ, eface.word) + typ := toCommonType(eface.typ) + fl := flag(typ.Kind()) << flagKindShift + if typ.Kind() != Ptr && typ.Kind() != UnsafePointer { + fl |= flagIndir + } + return Value{typ, unsafe.Pointer(eface.word), fl} } // Zero returns a Value representing a zero value for the specified type. @@ -1730,10 +1682,12 @@ func Zero(typ Type) Value { if typ == nil { panic("reflect: Zero(nil)") } - if typ.Kind() == Ptr || typ.Kind() == UnsafePointer { - return valueFromIword(0, typ, 0) + t := typ.common() + fl := flag(t.Kind()) << flagKindShift + if t.Kind() == Ptr || t.Kind() == UnsafePointer { + return Value{t, nil, fl} } - return valueFromAddr(0, typ, unsafe.New(typ)) + return Value{t, unsafe.New(typ), fl | flagIndir} } // New returns a Value representing a pointer to a new zero value @@ -1743,40 +1697,42 @@ func New(typ Type) Value { panic("reflect: New(nil)") } ptr := unsafe.New(typ) - return valueFromIword(0, PtrTo(typ), iword(uintptr(ptr))) + fl := flag(Ptr) << flagKindShift + return Value{typ.common().ptrTo(), ptr, fl} } -// convertForAssignment -func convertForAssignment(what string, addr unsafe.Pointer, dst Type, iv internalValue) internalValue { - if iv.method { - panic(what + ": cannot assign method value to type " + dst.String()) +// assignTo returns a value v that can be assigned directly to typ. +// It panics if v is not assignable to typ. +// For a conversion to an interface type, target is a suggested scratch space to use. +func (v Value) assignTo(context string, dst *commonType, target *interface{}) Value { + if v.flag&flagMethod != 0 { + panic(context + ": cannot assign method value to type " + dst.String()) } - dst1 := dst.(*commonType) - if directlyAssignable(dst1, iv.typ) { + switch { + case directlyAssignable(dst, v.typ): // Overwrite type so that they match. // Same memory layout, so no harm done. - iv.typ = dst1 - return iv - } - if implements(dst1, iv.typ) { - if addr == nil { - addr = unsafe.Pointer(new(interface{})) + v.typ = dst + fl := v.flag & (flagRO | flagAddr | flagIndir) + fl |= flag(dst.Kind()) << flagKindShift + return Value{dst, v.val, fl} + + case implements(dst, v.typ): + if target == nil { + target = new(interface{}) } - x := iv.valueInterface(false) + x := valueInterface(v, false) if dst.NumMethod() == 0 { - *(*interface{})(addr) = x + *target = x } else { - ifaceE2I(dst1.runtimeType(), x, addr) + ifaceE2I(dst.runtimeType(), x, unsafe.Pointer(target)) } - iv.addr = addr - iv.word = iword(uintptr(addr)) - iv.typ = dst1 - return iv + return Value{dst, unsafe.Pointer(target), flagIndir | flag(Interface)<<flagKindShift} } // Failed. - panic(what + ": value of type " + iv.typ.String() + " is not assignable to type " + dst.String()) + panic(context + ": value of type " + v.typ.String() + " is not assignable to type " + dst.String()) } // implemented in ../pkg/runtime @@ -1787,7 +1743,7 @@ func chanrecv(t *runtime.Type, ch iword, nb bool) (val iword, selected, received func chansend(t *runtime.Type, ch iword, val iword, nb bool) bool func makechan(typ *runtime.Type, size uint32) (ch iword) -func makemap(t *runtime.Type) iword +func makemap(t *runtime.Type) (m iword) func mapaccess(t *runtime.Type, m iword, key iword) (val iword, ok bool) func mapassign(t *runtime.Type, m iword, key, val iword, ok bool) func mapiterinit(t *runtime.Type, m iword) *byte @@ -1797,3 +1753,17 @@ func maplen(m iword) int32 func call(typ *commonType, fnaddr unsafe.Pointer, isInterface bool, isMethod bool, params *unsafe.Pointer, results *unsafe.Pointer) func ifaceE2I(t *runtime.Type, src interface{}, dst unsafe.Pointer) + +// Dummy annotation marking that the value x escapes, +// for use in cases where the reflect code is so clever that +// the compiler cannot follow. +func escapes(x interface{}) { + if dummy.b { + dummy.x = x + } +} + +var dummy struct { + b bool + x interface{} +} diff --git a/libgo/go/regexp/regexp.go b/libgo/go/regexp/regexp.go index b906076f9eb..59f3be39d29 100644 --- a/libgo/go/regexp/regexp.go +++ b/libgo/go/regexp/regexp.go @@ -1,7 +1,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package regexp implements a simple regular expression library. +// Package regexp implements regular expression search. // // The syntax of the regular expressions accepted is the same // general syntax used by Perl, Python, and other languages. diff --git a/libgo/go/runtime/gc_test.go b/libgo/go/runtime/gc_test.go index fad60a36804..156d3bc7d30 100644 --- a/libgo/go/runtime/gc_test.go +++ b/libgo/go/runtime/gc_test.go @@ -6,16 +6,24 @@ import ( ) func TestGcSys(t *testing.T) { + runtime.GC() + runtime.UpdateMemStats() + sys := runtime.MemStats.Sys + for i := 0; i < 1000000; i++ { workthegc() } // Should only be using a few MB. runtime.UpdateMemStats() - sys := runtime.MemStats.Sys - t.Logf("using %d MB", sys>>20) - if sys > 10e6 { - t.Fatalf("using too much memory: %d MB", sys>>20) + if sys > runtime.MemStats.Sys { + sys = 0 + } else { + sys = runtime.MemStats.Sys - sys + } + t.Logf("used %d extra bytes", sys) + if sys > 2<<20 { + t.Fatalf("using too much memory: %d bytes", sys) } } diff --git a/libgo/go/strconv/decimal.go b/libgo/go/strconv/decimal.go index f572ea4a22d..541553097bb 100644 --- a/libgo/go/strconv/decimal.go +++ b/libgo/go/strconv/decimal.go @@ -102,12 +102,6 @@ func (a *decimal) Assign(v uint64) { trim(a) } -func newDecimal(i uint64) *decimal { - a := new(decimal) - a.Assign(i) - return a -} - // Maximum shift that we can do in one pass without overflow. // Signed int has 31 bits, and we have to be able to accommodate 9<<k. const maxShift = 27 @@ -303,32 +297,32 @@ func shouldRoundUp(a *decimal, nd int) bool { // If nd is zero, it means we're rounding // just to the left of the digits, as in // 0.09 -> 0.1. -func (a *decimal) Round(nd int) *decimal { +func (a *decimal) Round(nd int) { if nd < 0 || nd >= a.nd { - return a + return } if shouldRoundUp(a, nd) { - return a.RoundUp(nd) + a.RoundUp(nd) + } else { + a.RoundDown(nd) } - return a.RoundDown(nd) } // Round a down to nd digits (or fewer). // Returns receiver for convenience. -func (a *decimal) RoundDown(nd int) *decimal { +func (a *decimal) RoundDown(nd int) { if nd < 0 || nd >= a.nd { - return a + return } a.nd = nd trim(a) - return a } // Round a up to nd digits (or fewer). // Returns receiver for convenience. -func (a *decimal) RoundUp(nd int) *decimal { +func (a *decimal) RoundUp(nd int) { if nd < 0 || nd >= a.nd { - return a + return } // round up @@ -337,7 +331,7 @@ func (a *decimal) RoundUp(nd int) *decimal { if c < '9' { // can stop after this digit a.d[i]++ a.nd = i + 1 - return a + return } } @@ -346,7 +340,6 @@ func (a *decimal) RoundUp(nd int) *decimal { a.d[0] = '1' a.nd = 1 a.dp++ - return a } // Extract integer part, rounded appropriately. diff --git a/libgo/go/strconv/decimal_test.go b/libgo/go/strconv/decimal_test.go index deb2e02f610..13a127f5b2c 100644 --- a/libgo/go/strconv/decimal_test.go +++ b/libgo/go/strconv/decimal_test.go @@ -70,17 +70,23 @@ var roundtests = []roundTest{ func TestDecimalRound(t *testing.T) { for i := 0; i < len(roundtests); i++ { test := &roundtests[i] - s := NewDecimal(test.i).RoundDown(test.nd).String() + d := NewDecimal(test.i) + d.RoundDown(test.nd) + s := d.String() if s != test.down { t.Errorf("Decimal %v RoundDown %d = %v, want %v", test.i, test.nd, s, test.down) } - s = NewDecimal(test.i).Round(test.nd).String() + d = NewDecimal(test.i) + d.Round(test.nd) + s = d.String() if s != test.round { t.Errorf("Decimal %v Round %d = %v, want %v", test.i, test.nd, s, test.down) } - s = NewDecimal(test.i).RoundUp(test.nd).String() + d = NewDecimal(test.i) + d.RoundUp(test.nd) + s = d.String() if s != test.up { t.Errorf("Decimal %v RoundUp %d = %v, want %v", test.i, test.nd, s, test.up) diff --git a/libgo/go/strconv/ftoa.go b/libgo/go/strconv/ftoa.go index 07fe806b97d..8342b6abe79 100644 --- a/libgo/go/strconv/ftoa.go +++ b/libgo/go/strconv/ftoa.go @@ -98,7 +98,8 @@ func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string { // The shift is exp - flt.mantbits because mant is a 1-bit integer // followed by a flt.mantbits fraction, and we are treating it as // a 1+flt.mantbits-bit integer. - d := newDecimal(mant) + d := new(decimal) + d.Assign(mant) d.Shift(exp - int(flt.mantbits)) // Round appropriately. @@ -184,7 +185,8 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { // d = mant << (exp - mantbits) // Next highest floating point number is mant+1 << exp-mantbits. // Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1. - upper := newDecimal(mant*2 + 1) + upper := new(decimal) + upper.Assign(mant*2 + 1) upper.Shift(exp - int(flt.mantbits) - 1) // d = mant << (exp - mantbits) @@ -203,7 +205,8 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { mantlo = mant*2 - 1 explo = exp - 1 } - lower := newDecimal(mantlo*2 + 1) + lower := new(decimal) + lower.Assign(mantlo*2 + 1) lower.Shift(explo - int(flt.mantbits) - 1) // The upper and lower bounds are possible outputs only if diff --git a/libgo/go/strconv/ftoa_test.go b/libgo/go/strconv/ftoa_test.go index 6d361a138ef..8bac5da4526 100644 --- a/libgo/go/strconv/ftoa_test.go +++ b/libgo/go/strconv/ftoa_test.go @@ -148,3 +148,27 @@ func TestFtoa(t *testing.T) { } } } + +func BenchmarkFtoa64Decimal(b *testing.B) { + for i := 0; i < b.N; i++ { + Ftoa64(33909, 'g', -1) + } +} + +func BenchmarkFtoa64Float(b *testing.B) { + for i := 0; i < b.N; i++ { + Ftoa64(339.7784, 'g', -1) + } +} + +func BenchmarkFtoa64FloatExp(b *testing.B) { + for i := 0; i < b.N; i++ { + Ftoa64(-5.09e75, 'g', -1) + } +} + +func BenchmarkFtoa64Big(b *testing.B) { + for i := 0; i < b.N; i++ { + Ftoa64(123456789123456789123456789, 'g', -1) + } +} diff --git a/libgo/go/strconv/internal_test.go b/libgo/go/strconv/internal_test.go index 9a7f4f0867c..d0fa80edfb6 100644 --- a/libgo/go/strconv/internal_test.go +++ b/libgo/go/strconv/internal_test.go @@ -6,7 +6,11 @@ package strconv -func NewDecimal(i uint64) *decimal { return newDecimal(i) } +func NewDecimal(i uint64) *decimal { + d := new(decimal) + d.Assign(i) + return d +} func SetOptimize(b bool) bool { old := optimize diff --git a/libgo/go/strings/strings.go b/libgo/go/strings/strings.go index b4d920714ac..53fdeadf97b 100644 --- a/libgo/go/strings/strings.go +++ b/libgo/go/strings/strings.go @@ -64,7 +64,17 @@ func Count(s, sep string) int { // Contains returns true if substr is within s. func Contains(s, substr string) bool { - return Index(s, substr) != -1 + return Index(s, substr) >= 0 +} + +// ContainsAny returns true if any Unicode code points in chars are within s. +func ContainsAny(s, chars string) bool { + return IndexAny(s, chars) >= 0 +} + +// ContainsRune returns true if the Unicode code point r is within s. +func ContainsRune(s string, r rune) bool { + return IndexRune(s, r) >= 0 } // Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. @@ -269,7 +279,7 @@ func FieldsFunc(s string, f func(rune) bool) []string { fieldStart = i } } - if fieldStart != -1 { // Last field might end at EOF. + if fieldStart >= 0 { // Last field might end at EOF. a[na] = s[fieldStart:] } return a @@ -512,7 +522,7 @@ func lastIndexFunc(s string, f func(rune) bool, truth bool) int { } func makeCutsetFunc(cutset string) func(rune) bool { - return func(r rune) bool { return IndexRune(cutset, r) != -1 } + return func(r rune) bool { return IndexRune(cutset, r) >= 0 } } // Trim returns a slice of the string s with all leading and diff --git a/libgo/go/strings/strings_test.go b/libgo/go/strings/strings_test.go index 304d69a19d7..957af67b2ba 100644 --- a/libgo/go/strings/strings_test.go +++ b/libgo/go/strings/strings_test.go @@ -489,46 +489,47 @@ func TestSpecialCase(t *testing.T) { func TestTrimSpace(t *testing.T) { runStringTests(t, TrimSpace, "TrimSpace", trimSpaceTests) } var trimTests = []struct { - f func(string, string) string + f string in, cutset, out string }{ - {Trim, "abba", "a", "bb"}, - {Trim, "abba", "ab", ""}, - {TrimLeft, "abba", "ab", ""}, - {TrimRight, "abba", "ab", ""}, - {TrimLeft, "abba", "a", "bba"}, - {TrimRight, "abba", "a", "abb"}, - {Trim, "<tag>", "<>", "tag"}, - {Trim, "* listitem", " *", "listitem"}, - {Trim, `"quote"`, `"`, "quote"}, - {Trim, "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, + {"Trim", "abba", "a", "bb"}, + {"Trim", "abba", "ab", ""}, + {"TrimLeft", "abba", "ab", ""}, + {"TrimRight", "abba", "ab", ""}, + {"TrimLeft", "abba", "a", "bba"}, + {"TrimRight", "abba", "a", "abb"}, + {"Trim", "<tag>", "<>", "tag"}, + {"Trim", "* listitem", " *", "listitem"}, + {"Trim", `"quote"`, `"`, "quote"}, + {"Trim", "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, //empty string tests - {Trim, "abba", "", "abba"}, - {Trim, "", "123", ""}, - {Trim, "", "", ""}, - {TrimLeft, "abba", "", "abba"}, - {TrimLeft, "", "123", ""}, - {TrimLeft, "", "", ""}, - {TrimRight, "abba", "", "abba"}, - {TrimRight, "", "123", ""}, - {TrimRight, "", "", ""}, - {TrimRight, "☺\xc0", "☺", "☺\xc0"}, + {"Trim", "abba", "", "abba"}, + {"Trim", "", "123", ""}, + {"Trim", "", "", ""}, + {"TrimLeft", "abba", "", "abba"}, + {"TrimLeft", "", "123", ""}, + {"TrimLeft", "", "", ""}, + {"TrimRight", "abba", "", "abba"}, + {"TrimRight", "", "123", ""}, + {"TrimRight", "", "", ""}, + {"TrimRight", "☺\xc0", "☺", "☺\xc0"}, } func TestTrim(t *testing.T) { for _, tc := range trimTests { - actual := tc.f(tc.in, tc.cutset) - var name string - switch tc.f { - case Trim: - name = "Trim" - case TrimLeft: - name = "TrimLeft" - case TrimRight: - name = "TrimRight" + name := tc.f + var f func(string, string) string + switch name { + case "Trim": + f = Trim + case "TrimLeft": + f = TrimLeft + case "TrimRight": + f = TrimRight default: - t.Error("Undefined trim function") + t.Errorf("Undefined trim function %s", name) } + actual := f(tc.in, tc.cutset) if actual != tc.out { t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.cutset, actual, tc.out) } @@ -907,6 +908,56 @@ func TestContains(t *testing.T) { } } +var ContainsAnyTests = []struct { + str, substr string + expected bool +}{ + {"", "", false}, + {"", "a", false}, + {"", "abc", false}, + {"a", "", false}, + {"a", "a", true}, + {"aaa", "a", true}, + {"abc", "xyz", false}, + {"abc", "xcz", true}, + {"a☺b☻c☹d", "uvw☻xyz", true}, + {"aRegExp*", ".(|)*+?^$[]", true}, + {dots + dots + dots, " ", false}, +} + +func TestContainsAny(t *testing.T) { + for _, ct := range ContainsAnyTests { + if ContainsAny(ct.str, ct.substr) != ct.expected { + t.Errorf("ContainsAny(%s, %s) = %v, want %v", + ct.str, ct.substr, !ct.expected, ct.expected) + } + } +} + +var ContainsRuneTests = []struct { + str string + r rune + expected bool +}{ + {"", 'a', false}, + {"a", 'a', true}, + {"aaa", 'a', true}, + {"abc", 'y', false}, + {"abc", 'c', true}, + {"a☺b☻c☹d", 'x', false}, + {"a☺b☻c☹d", '☻', true}, + {"aRegExp*", '*', true}, +} + +func TestContainsRune(t *testing.T) { + for _, ct := range ContainsRuneTests { + if ContainsRune(ct.str, ct.r) != ct.expected { + t.Errorf("ContainsRune(%s, %s) = %v, want %v", + ct.str, ct.r, !ct.expected, ct.expected) + } + } +} + var EqualFoldTests = []struct { s, t string out bool diff --git a/libgo/go/sync/mutex.go b/libgo/go/sync/mutex.go index 2d46c89948f..4fc02743c6e 100644 --- a/libgo/go/sync/mutex.go +++ b/libgo/go/sync/mutex.go @@ -6,6 +6,8 @@ // exclusion locks. Other than the Once and WaitGroup types, most are intended // for use by low-level library routines. Higher-level synchronization is // better done via channels and communication. +// +// Values containing the types defined in this package should not be copied. package sync import ( diff --git a/libgo/go/syscall/bpf_bsd.go b/libgo/go/syscall/bpf_bsd.go index 06a2953e7fe..f94b7233b67 100644 --- a/libgo/go/syscall/bpf_bsd.go +++ b/libgo/go/syscall/bpf_bsd.go @@ -20,54 +20,54 @@ func BpfJump(code, k, jt, jf int) *BpfInsn { return &BpfInsn{Code: uint16(code), Jt: uint8(jt), Jf: uint8(jf), K: uint32(k)} } -func BpfBuflen(fd int) (int, int) { +func BpfBuflen(fd int) (int, error) { var l int - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGBLEN, uintptr(unsafe.Pointer(&l))) - if e := int(ep); e != 0 { - return 0, e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGBLEN, uintptr(unsafe.Pointer(&l))) + if err != 0 { + return 0, Errno(err) } - return l, 0 + return l, nil } -func SetBpfBuflen(fd, l int) (int, int) { - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSBLEN, uintptr(unsafe.Pointer(&l))) - if e := int(ep); e != 0 { - return 0, e +func SetBpfBuflen(fd, l int) (int, error) { + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSBLEN, uintptr(unsafe.Pointer(&l))) + if err != 0 { + return 0, Errno(err) } - return l, 0 + return l, nil } -func BpfDatalink(fd int) (int, int) { +func BpfDatalink(fd int) (int, error) { var t int - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGDLT, uintptr(unsafe.Pointer(&t))) - if e := int(ep); e != 0 { - return 0, e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGDLT, uintptr(unsafe.Pointer(&t))) + if err != 0 { + return 0, Errno(err) } - return t, 0 + return t, nil } -func SetBpfDatalink(fd, t int) (int, int) { - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSDLT, uintptr(unsafe.Pointer(&t))) - if e := int(ep); e != 0 { - return 0, e +func SetBpfDatalink(fd, t int) (int, error) { + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSDLT, uintptr(unsafe.Pointer(&t))) + if err != 0 { + return 0, Errno(err) } - return t, 0 + return t, nil } -func SetBpfPromisc(fd, m int) int { - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCPROMISC, uintptr(unsafe.Pointer(&m))) - if e := int(ep); e != 0 { - return e +func SetBpfPromisc(fd, m int) error { + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCPROMISC, uintptr(unsafe.Pointer(&m))) + if err != 0 { + return Errno(err) } - return 0 + return nil } -func FlushBpf(fd int) int { - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCFLUSH, 0) - if e := int(ep); e != 0 { - return e +func FlushBpf(fd int) error { + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCFLUSH, 0) + if err != 0 { + return Errno(err) } - return 0 + return nil } type ivalue struct { @@ -75,95 +75,95 @@ type ivalue struct { value int16 } -func BpfInterface(fd int, name string) (string, int) { +func BpfInterface(fd int, name string) (string, error) { var iv ivalue - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGETIF, uintptr(unsafe.Pointer(&iv))) - if e := int(ep); e != 0 { - return "", e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGETIF, uintptr(unsafe.Pointer(&iv))) + if err != 0 { + return "", Errno(err) } - return name, 0 + return name, nil } -func SetBpfInterface(fd int, name string) int { +func SetBpfInterface(fd int, name string) error { var iv ivalue copy(iv.name[:], []byte(name)) - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSETIF, uintptr(unsafe.Pointer(&iv))) - if e := int(ep); e != 0 { - return e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSETIF, uintptr(unsafe.Pointer(&iv))) + if err != 0 { + return Errno(err) } - return 0 + return nil } -func BpfTimeout(fd int) (*Timeval, int) { +func BpfTimeout(fd int) (*Timeval, error) { var tv Timeval - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGRTIMEOUT, uintptr(unsafe.Pointer(&tv))) - if e := int(ep); e != 0 { - return nil, e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGRTIMEOUT, uintptr(unsafe.Pointer(&tv))) + if err != 0 { + return nil, Errno(err) } - return &tv, 0 + return &tv, nil } -func SetBpfTimeout(fd int, tv *Timeval) int { - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSRTIMEOUT, uintptr(unsafe.Pointer(tv))) - if e := int(ep); e != 0 { - return e +func SetBpfTimeout(fd int, tv *Timeval) error { + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSRTIMEOUT, uintptr(unsafe.Pointer(tv))) + if err != 0 { + return Errno(err) } - return 0 + return nil } -func BpfStats(fd int) (*BpfStat, int) { +func BpfStats(fd int) (*BpfStat, error) { var s BpfStat - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGSTATS, uintptr(unsafe.Pointer(&s))) - if e := int(ep); e != 0 { - return nil, e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGSTATS, uintptr(unsafe.Pointer(&s))) + if err != 0 { + return nil, Errno(err) } - return &s, 0 + return &s, nil } -func SetBpfImmediate(fd, m int) int { - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCIMMEDIATE, uintptr(unsafe.Pointer(&m))) - if e := int(ep); e != 0 { - return e +func SetBpfImmediate(fd, m int) error { + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCIMMEDIATE, uintptr(unsafe.Pointer(&m))) + if err != 0 { + return Errno(err) } - return 0 + return nil } -func SetBpf(fd int, i []BpfInsn) int { +func SetBpf(fd int, i []BpfInsn) error { var p BpfProgram p.Len = uint32(len(i)) p.Insns = (*BpfInsn)(unsafe.Pointer(&i[0])) - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSETF, uintptr(unsafe.Pointer(&p))) - if e := int(ep); e != 0 { - return e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSETF, uintptr(unsafe.Pointer(&p))) + if err != 0 { + return Errno(err) } - return 0 + return nil } -func CheckBpfVersion(fd int) int { +func CheckBpfVersion(fd int) error { var v BpfVersion - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCVERSION, uintptr(unsafe.Pointer(&v))) - if e := int(ep); e != 0 { - return e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCVERSION, uintptr(unsafe.Pointer(&v))) + if err != 0 { + return Errno(err) } if v.Major != BPF_MAJOR_VERSION || v.Minor != BPF_MINOR_VERSION { return EINVAL } - return 0 + return nil } -func BpfHeadercmpl(fd int) (int, int) { +func BpfHeadercmpl(fd int) (int, error) { var f int - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGHDRCMPLT, uintptr(unsafe.Pointer(&f))) - if e := int(ep); e != 0 { - return 0, e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGHDRCMPLT, uintptr(unsafe.Pointer(&f))) + if err != 0 { + return 0, Errno(err) } - return f, 0 + return f, nil } -func SetBpfHeadercmpl(fd, f int) int { - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSHDRCMPLT, uintptr(unsafe.Pointer(&f))) - if e := int(ep); e != 0 { - return e +func SetBpfHeadercmpl(fd, f int) error { + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSHDRCMPLT, uintptr(unsafe.Pointer(&f))) + if err != 0 { + return Errno(err) } - return 0 + return nil } diff --git a/libgo/go/syscall/env_plan9.go b/libgo/go/syscall/env_plan9.go new file mode 100644 index 00000000000..518573318ef --- /dev/null +++ b/libgo/go/syscall/env_plan9.go @@ -0,0 +1,74 @@ +// 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. + +// Plan 9 environment variables. + +package syscall + +import "errors" + +func Getenv(key string) (value string, found bool) { + if len(key) == 0 { + return "", false + } + f, e := Open("/env/"+key, O_RDONLY) + if e != nil { + return "", false + } + defer Close(f) + + l, _ := Seek(f, 0, 2) + Seek(f, 0, 0) + buf := make([]byte, l) + n, e := Read(f, buf) + if e != nil { + return "", false + } + + if n > 0 && buf[n-1] == 0 { + buf = buf[:n-1] + } + return string(buf), true +} + +func Setenv(key, value string) error { + if len(key) == 0 { + return errors.New("bad arg in system call") + } + + f, e := Create("/env/"+key, O_RDWR, 0666) + if e != nil { + return e + } + defer Close(f) + + _, e = Write(f, []byte(value)) + return nil +} + +func Clearenv() { + RawSyscall(SYS_RFORK, RFCENVG, 0, 0) +} + +func Environ() []string { + env := make([]string, 0, 100) + + f, e := Open("/env", O_RDONLY) + if e != nil { + panic(e) + } + defer Close(f) + + names, e := readdirnames(f) + if e != nil { + panic(e) + } + + for _, k := range names { + if v, ok := Getenv(k); ok { + env = append(env, k+"="+v) + } + } + return env[0:len(env)] +} diff --git a/libgo/go/syscall/env_unix.go b/libgo/go/syscall/env_unix.go new file mode 100644 index 00000000000..df259097c6a --- /dev/null +++ b/libgo/go/syscall/env_unix.go @@ -0,0 +1,85 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux openbsd + +// Unix environment variables. + +package syscall + +import "sync" + +var env map[string]string +var envOnce sync.Once +var Envs []string // provided by runtime + +func setenv_c(k, v string) + +func copyenv() { + env = make(map[string]string) + for _, s := range Envs { + for j := 0; j < len(s); j++ { + if s[j] == '=' { + env[s[0:j]] = s[j+1:] + break + } + } + } +} + +var envLock sync.RWMutex + +func Getenv(key string) (value string, found bool) { + envOnce.Do(copyenv) + if len(key) == 0 { + return "", false + } + + envLock.RLock() + defer envLock.RUnlock() + + v, ok := env[key] + if !ok { + return "", false + } + return v, true +} + +func Setenv(key, value string) error { + envOnce.Do(copyenv) + if len(key) == 0 { + return EINVAL + } + + envLock.Lock() + defer envLock.Unlock() + + env[key] = value + setenv_c(key, value) // is a no-op if cgo isn't loaded + return nil +} + +func Clearenv() { + envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv + + envLock.Lock() + defer envLock.Unlock() + + env = make(map[string]string) + + // TODO(bradfitz): pass through to C +} + +func Environ() []string { + envOnce.Do(copyenv) + envLock.RLock() + defer envLock.RUnlock() + a := make([]string, len(env)) + i := 0 + for k, v := range env { + a[i] = k + "=" + v + i++ + } + return a +} diff --git a/libgo/go/syscall/env_windows.go b/libgo/go/syscall/env_windows.go new file mode 100644 index 00000000000..8308f10a2dc --- /dev/null +++ b/libgo/go/syscall/env_windows.go @@ -0,0 +1,77 @@ +// Copyright 2010 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. + +// Windows environment variables. + +package syscall + +import ( + "unicode/utf16" + "unsafe" +) + +func Getenv(key string) (value string, found bool) { + b := make([]uint16, 100) + n, e := GetEnvironmentVariable(StringToUTF16Ptr(key), &b[0], uint32(len(b))) + if n == 0 && e == ERROR_ENVVAR_NOT_FOUND { + return "", false + } + if n > uint32(len(b)) { + b = make([]uint16, n) + n, e = GetEnvironmentVariable(StringToUTF16Ptr(key), &b[0], uint32(len(b))) + if n > uint32(len(b)) { + n = 0 + } + } + if n == 0 { + return "", false + } + return string(utf16.Decode(b[0:n])), true +} + +func Setenv(key, value string) error { + var v *uint16 + if len(value) > 0 { + v = StringToUTF16Ptr(value) + } + e := SetEnvironmentVariable(StringToUTF16Ptr(key), v) + if e != nil { + return e + } + return nil +} + +func Clearenv() { + for _, s := range Environ() { + // Environment variables can begin with = + // so start looking for the separator = at j=1. + // http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx + for j := 1; j < len(s); j++ { + if s[j] == '=' { + Setenv(s[0:j], "") + break + } + } + } +} + +func Environ() []string { + s, e := GetEnvironmentStrings() + if e != nil { + return nil + } + defer FreeEnvironmentStrings(s) + r := make([]string, 0, 50) // Empty with room to grow. + for from, i, p := 0, 0, (*[1 << 24]uint16)(unsafe.Pointer(s)); true; i++ { + if p[i] == 0 { + // empty string marks the end + if i <= from { + break + } + r = append(r, string(utf16.Decode(p[from:i]))) + from = i + 1 + } + } + return r +} diff --git a/libgo/go/syscall/errno.c b/libgo/go/syscall/errno.c index 854b5aaec28..8e57811151a 100644 --- a/libgo/go/syscall/errno.c +++ b/libgo/go/syscall/errno.c @@ -5,21 +5,22 @@ license that can be found in the LICENSE file. */ #include <errno.h> +#include <stdint.h> /* errno is typically a macro. These functions set and get errno specific to the libc being used. */ -int GetErrno() asm ("libgo_syscall.syscall.GetErrno"); -void SetErrno(int) asm ("libgo_syscall.syscall.SetErrno"); +uintptr_t GetErrno() asm ("libgo_syscall.syscall.GetErrno"); +void SetErrno(uintptr_t) asm ("libgo_syscall.syscall.SetErrno"); -int +uintptr_t GetErrno() { - return errno; + return (uintptr_t) errno; } void -SetErrno(int value) +SetErrno(uintptr_t value) { - errno = value; + errno = (int) value; } diff --git a/libgo/go/syscall/errstr.go b/libgo/go/syscall/errstr.go index d9f3fe82eb4..5ef10da1fd4 100644 --- a/libgo/go/syscall/errstr.go +++ b/libgo/go/syscall/errstr.go @@ -6,14 +6,14 @@ package syscall -//sysnb strerror_r(errnum int, buf []byte) (errno int) +//sysnb strerror_r(errnum int, buf []byte) (err error) //strerror_r(errnum int, buf *byte, buflen Size_t) int func Errstr(errnum int) string { for len := 128; ; len *= 2 { b := make([]byte, len) err := strerror_r(errnum, b) - if err == 0 { + if err == nil { i := 0 for b[i] != 0 { i++ diff --git a/libgo/go/syscall/exec_stubs.go b/libgo/go/syscall/exec_stubs.go index 02b9ec34cf3..35bb17487b7 100644 --- a/libgo/go/syscall/exec_stubs.go +++ b/libgo/go/syscall/exec_stubs.go @@ -7,17 +7,27 @@ package syscall func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []int) (pid int, err int) { - return -1, ENOSYS; + return -1, ENOSYS } func Exec(argv0 string, argv []string, envv []string) (err int) { - return ENOSYS; + return ENOSYS } -func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, errno int) { - return -1, ENOSYS; +func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) { + return -1, ENOSYS } -func raw_ptrace(request int, pid int, addr *byte, data *byte) int { +func (w WaitStatus) Exited() bool { return false } +func (w WaitStatus) Signaled() bool { return false } +func (w WaitStatus) Stopped() bool { return false } +func (w WaitStatus) Continued() bool { return false } +func (w WaitStatus) CoreDump() bool { return false } +func (w WaitStatus) ExitStatus() int { return 0 } +func (w WaitStatus) Signal() int { return 0 } +func (w WaitStatus) StopSignal() int { return 0 } +func (w WaitStatus) TrapCause() int { return 0 } + +func raw_ptrace(request int, pid int, addr *byte, data *byte) Errno { return ENOSYS } diff --git a/libgo/go/syscall/exec_unix.go b/libgo/go/syscall/exec_unix.go index 60e9770ce23..c9814b7050e 100644 --- a/libgo/go/syscall/exec_unix.go +++ b/libgo/go/syscall/exec_unix.go @@ -11,39 +11,43 @@ import ( "unsafe" ) -//sysnb raw_fork() (pid Pid_t, errno int) +//sysnb raw_fork() (pid Pid_t, err Errno) //fork() Pid_t -//sysnb raw_setsid() (errno int) +//sysnb raw_setsid() (err Errno) //setsid() Pid_t -//sysnb raw_chroot(path *byte) (errno int) +//sysnb raw_setpgid(pid int, pgid int) (err Errno) +//setpgid(pid Pid_t, pgid Pid_t) int + +//sysnb raw_chroot(path *byte) (err Errno) //chroot(path *byte) int -//sysnb raw_chdir(path *byte) (errno int) +//sysnb raw_chdir(path *byte) (err Errno) //chdir(path *byte) int -//sysnb raw_fcntl(fd int, cmd int, arg int) (val int, errno int) +//sysnb raw_fcntl(fd int, cmd int, arg int) (val int, err Errno) //fcntl(fd int, cmd int, arg int) int -//sysnb raw_close(fd int) (errno int) +//sysnb raw_close(fd int) (err Errno) //close(fd int) int -//sysnb raw_ioctl(fd int, cmd int, val int) (rval int, errno int) +//sysnb raw_ioctl(fd int, cmd int, val int) (rval int, err Errno) //ioctl(fd int, cmd int, val int) int -//sysnb raw_execve(argv0 *byte, argv **byte, envv **byte) (errno int) +//sysnb raw_execve(argv0 *byte, argv **byte, envv **byte) (err Errno) //execve(argv0 *byte, argv **byte, envv **byte) int -//sysnb raw_read(fd int, p *byte, np int) (n int, errno int) -//read(fd int, buf *byte, count Size_t) Ssize_t - -//sysnb raw_write(fd int, buf *byte, count int) int +//sysnb raw_write(fd int, buf *byte, count int) (err Errno) //write(fd int, buf *byte, count Size_t) Ssize_t //sysnb raw_exit(status int) //_exit(status int) +// Note: not raw, returns error rather than Errno. +//sys read(fd int, p *byte, np int) (n int, err error) +//read(fd int, buf *byte, count Size_t) Ssize_t + // Lock synchronizing creation of new file descriptors with fork. // // We want the child in a fork/exec sequence to inherit only the @@ -106,9 +110,9 @@ func StringSlicePtr(ss []string) []*byte { func CloseOnExec(fd int) { fcntl(fd, F_SETFD, FD_CLOEXEC) } -func SetNonblock(fd int, nonblocking bool) (errno int) { +func SetNonblock(fd int, nonblocking bool) (err error) { flag, err := fcntl(fd, F_GETFL, 0) - if err != 0 { + if err != nil { return err } if nonblocking { @@ -121,20 +125,22 @@ func SetNonblock(fd int, nonblocking bool) (errno int) { } // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child. -// If a dup or exec fails, write the errno int to pipe. +// If a dup or exec fails, write the errno error to pipe. // (Pipe is close-on-exec so if exec succeeds, it will be closed.) // In the child, this function must not acquire any locks, because // they might have been locked at the time of the fork. This means // no rescheduling, no malloc calls, and no new stack segments. // The calls to RawSyscall are okay because they are assembly // functions that do not grow the stack. -func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err int) { +func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { // Declare all variables at top in case any // declarations require heap allocation (e.g., err1). - var r1 Pid_t - var err1 int - var nextfd int - var i int + var ( + r1 Pid_t + err1 Errno + nextfd int + i int + ) // guard against side effects of shuffling fds below. fd := append([]int(nil), attr.Files...) @@ -143,7 +149,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // No more allocation or calls of non-assembly functions. r1, err1 = raw_fork() if err1 != 0 { - return 0, int(err1) + return 0, err1 } if r1 != 0 { @@ -171,7 +177,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // Set process group if sys.Setpgid { - err1 = Setpgid(0, 0) + err1 = raw_setpgid(0, 0) if err1 != 0 { goto childerror } @@ -189,23 +195,35 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr if cred := sys.Credential; cred != nil { ngroups := len(cred.Groups) if ngroups == 0 { - err1 = setgroups(0, nil) + err2 := setgroups(0, nil) + if err2 == nil { + err1 = 0 + } else { + err1 = err2.(Errno) + } } else { groups := make([]Gid_t, ngroups) for i, v := range cred.Groups { groups[i] = Gid_t(v) } - err1 = setgroups(ngroups, &groups[0]) + err2 := setgroups(ngroups, &groups[0]) + if err2 == nil { + err1 = 0 + } else { + err1 = err2.(Errno) + } } if err1 != 0 { goto childerror } - err1 = Setgid(int(cred.Gid)) - if err1 != 0 { + err2 := Setgid(int(cred.Gid)) + if err2 != nil { + err1 = err2.(Errno) goto childerror } - err1 = Setuid(int(cred.Uid)) - if err1 != 0 { + err2 = Setuid(int(cred.Uid)) + if err2 != nil { + err1 = err2.(Errno) goto childerror } } @@ -222,8 +240,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // so that pass 2 won't stomp on an fd it needs later. nextfd = int(len(fd)) if pipe < nextfd { - _, err1 = Dup2(pipe, nextfd) - if err1 != 0 { + _, err2 := Dup2(pipe, nextfd) + if err2 != nil { + err1 = err2.(Errno) goto childerror } raw_fcntl(nextfd, F_SETFD, FD_CLOEXEC) @@ -232,8 +251,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } for i = 0; i < len(fd); i++ { if fd[i] >= 0 && fd[i] < int(i) { - _, err1 = Dup2(fd[i], nextfd) - if err1 != 0 { + _, err2 := Dup2(fd[i], nextfd) + if err2 != nil { + err1 = err2.(Errno) goto childerror } raw_fcntl(nextfd, F_SETFD, FD_CLOEXEC) @@ -262,8 +282,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } // The new fd is created NOT close-on-exec, // which is exactly what we want. - _, err1 = Dup2(fd[i], i) - if err1 != 0 { + _, err2 := Dup2(fd[i], i) + if err2 != nil { + err1 = err2.(Errno) goto childerror } } @@ -338,10 +359,10 @@ type SysProcAttr struct { var zeroProcAttr ProcAttr var zeroSysProcAttr SysProcAttr -func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { +func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) { var p [2]int var n int - var err1 uintptr + var err1 Errno var wstatus WaitStatus if attr == nil { @@ -379,32 +400,32 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { ForkLock.Lock() // Allocate child status pipe close on exec. - if err = Pipe(p[0:]); err != 0 { + if err = Pipe(p[0:]); err != nil { goto error } - if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != 0 { + if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != nil { goto error } - if _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC); err != 0 { + if _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC); err != nil { goto error } // Kick off child. - pid, err = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1]) - if err != 0 { + pid, err1 = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1]) + if err1 != 0 { goto error } ForkLock.Unlock() // Read child error status from pipe. Close(p[1]) - n, err = raw_read(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1))) + n, err = read(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1))) Close(p[0]) - if err != 0 || n != 0 { + if err != nil || n != 0 { if n == int(unsafe.Sizeof(err1)) { - err = int(err1) + err = Errno(err1) } - if err == 0 { + if err == nil { err = EPIPE } @@ -418,7 +439,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { } // Read got EOF, so pipe closed on exec, so exec succeeded. - return pid, 0 + return pid, nil error: if p[0] >= 0 { @@ -430,20 +451,20 @@ error: } // Combination of fork and exec, careful to be thread safe. -func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { +func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) { return forkExec(argv0, argv, attr) } // StartProcess wraps ForkExec for package os. -func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err int) { +func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err error) { pid, err = forkExec(argv0, argv, attr) return pid, 0, err } // Ordinary exec. -func Exec(argv0 string, argv []string, envv []string) (err int) { +func Exec(argv0 string, argv []string, envv []string) (err error) { err1 := raw_execve(StringBytePtr(argv0), &StringSlicePtr(argv)[0], &StringSlicePtr(envv)[0]) - return int(err1) + return Errno(err1) } diff --git a/libgo/go/syscall/exec_windows.go b/libgo/go/syscall/exec_windows.go index e8b540ad160..2826e2f35a6 100644 --- a/libgo/go/syscall/exec_windows.go +++ b/libgo/go/syscall/exec_windows.go @@ -8,8 +8,8 @@ package syscall import ( "sync" + "unicode/utf16" "unsafe" - "utf16" ) var ForkLock sync.RWMutex @@ -100,7 +100,7 @@ func makeCmdLine(args []string) string { // Last bytes are two UCS-2 NULs, or four NUL bytes. func createEnvBlock(envv []string) *uint16 { if len(envv) == 0 { - return &utf16.Encode([]int("\x00\x00"))[0] + return &utf16.Encode([]rune("\x00\x00"))[0] } length := 0 for _, s := range envv { @@ -118,54 +118,54 @@ func createEnvBlock(envv []string) *uint16 { } copy(b[i:i+1], []byte{0}) - return &utf16.Encode([]int(string(b)))[0] + return &utf16.Encode([]rune(string(b)))[0] } func CloseOnExec(fd Handle) { SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0) } -func SetNonblock(fd Handle, nonblocking bool) (errno int) { - return 0 +func SetNonblock(fd Handle, nonblocking bool) (err error) { + return nil } // getFullPath retrieves the full path of the specified file. // Just a wrapper for Windows GetFullPathName api. -func getFullPath(name string) (path string, err int) { +func getFullPath(name string) (path string, err error) { p := StringToUTF16Ptr(name) buf := make([]uint16, 100) n, err := GetFullPathName(p, uint32(len(buf)), &buf[0], nil) - if err != 0 { + if err != nil { return "", err } if n > uint32(len(buf)) { // Windows is asking for bigger buffer. buf = make([]uint16, n) n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil) - if err != 0 { + if err != nil { return "", err } if n > uint32(len(buf)) { return "", EINVAL } } - return UTF16ToString(buf[:n]), 0 + return UTF16ToString(buf[:n]), nil } func isSlash(c uint8) bool { return c == '\\' || c == '/' } -func normalizeDir(dir string) (name string, err int) { +func normalizeDir(dir string) (name string, err error) { ndir, err := getFullPath(dir) - if err != 0 { + if err != nil { return "", err } if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) { // dir cannot have \\server\share\path form return "", EINVAL } - return ndir, 0 + return ndir, nil } func volToUpper(ch int) int { @@ -175,13 +175,13 @@ func volToUpper(ch int) int { return ch } -func joinExeDirAndFName(dir, p string) (name string, err int) { +func joinExeDirAndFName(dir, p string) (name string, err error) { if len(p) == 0 { return "", EINVAL } if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) { // \\server\share\path form - return p, 0 + return p, nil } if len(p) > 1 && p[1] == ':' { // has drive letter @@ -189,10 +189,10 @@ func joinExeDirAndFName(dir, p string) (name string, err int) { return "", EINVAL } if isSlash(p[2]) { - return p, 0 + return p, nil } else { d, err := normalizeDir(dir) - if err != 0 { + if err != nil { return "", err } if volToUpper(int(p[0])) == volToUpper(int(d[0])) { @@ -204,7 +204,7 @@ func joinExeDirAndFName(dir, p string) (name string, err int) { } else { // no drive letter d, err := normalizeDir(dir) - if err != 0 { + if err != nil { return "", err } if isSlash(p[0]) { @@ -232,7 +232,7 @@ type SysProcAttr struct { var zeroProcAttr ProcAttr var zeroSysProcAttr SysProcAttr -func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err int) { +func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err error) { if len(argv0) == 0 { return 0, 0, EWINDOWS } @@ -255,9 +255,9 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, // argv0 relative to the current directory, and, only once the new // process is started, it does Chdir(attr.Dir). We are adjusting // for that difference here by making argv0 absolute. - var err int + var err error argv0, err = joinExeDirAndFName(attr.Dir, argv0) - if err != 0 { + if err != nil { return 0, 0, err } } @@ -294,7 +294,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, for i := range attr.Files { if attr.Files[i] > 0 { err := DuplicateHandle(p, Handle(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) - if err != 0 { + if err != nil { return 0, 0, err } defer CloseHandle(Handle(fd[i])) @@ -314,14 +314,14 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, pi := new(ProcessInformation) err = CreateProcess(argv0p, argvp, nil, nil, true, CREATE_UNICODE_ENVIRONMENT, createEnvBlock(attr.Env), dirp, si, pi) - if err != 0 { + if err != nil { return 0, 0, err } defer CloseHandle(Handle(pi.Thread)) - return int(pi.ProcessId), int(pi.Process), 0 + return int(pi.ProcessId), int(pi.Process), nil } -func Exec(argv0 string, argv []string, envv []string) (err int) { +func Exec(argv0 string, argv []string, envv []string) (err error) { return EWINDOWS } diff --git a/libgo/go/syscall/libcall_irix.go b/libgo/go/syscall/libcall_irix.go index ae453559713..69e0db264bd 100644 --- a/libgo/go/syscall/libcall_irix.go +++ b/libgo/go/syscall/libcall_irix.go @@ -4,5 +4,5 @@ package syscall -//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (errno int) +//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (err Errno) //ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long diff --git a/libgo/go/syscall/libcall_linux.go b/libgo/go/syscall/libcall_linux.go index 3948e51ae28..79f5d48ae8b 100644 --- a/libgo/go/syscall/libcall_linux.go +++ b/libgo/go/syscall/libcall_linux.go @@ -8,31 +8,31 @@ package syscall import "unsafe" -//sys Openat(dirfd int, path string, flags int, mode uint32) (fd int, errno int) +//sys Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) //openat(dirfd int, path *byte, flags int, mode Mode_t) int -//sys futimesat(dirfd int, path *byte, times *[2]Timeval) (errno int) +//sys futimesat(dirfd int, path *byte, times *[2]Timeval) (err error) //futimesat(dirfd int, path *byte, times *[2]Timeval) int -func Futimesat(dirfd int, path string, tv []Timeval) (errno int) { +func Futimesat(dirfd int, path string, tv []Timeval) (err error) { if len(tv) != 2 { return EINVAL } return futimesat(dirfd, StringBytePtr(path), (*[2]Timeval)(unsafe.Pointer(&tv[0]))) } -func Futimes(fd int, tv []Timeval) (errno int) { +func Futimes(fd int, tv []Timeval) (err error) { // Believe it or not, this is the best we can do on GNU/Linux // (and is what glibc does). return Utimes("/proc/self/fd/"+itoa(fd), tv) } -//sys ptrace(request int, pid int, addr uintptr, data uintptr) (errno int) +//sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error) //ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long -//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (errno int) +//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (err Errno) //ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long -func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, errno int) { +func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err error) { // The peek requests are machine-size oriented, so we wrap it // to retrieve arbitrary-length data. @@ -48,9 +48,9 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, errno in // boundary. n := 0 if addr%sizeofPtr != 0 { - errno = ptrace(req, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0]))) - if errno != 0 { - return 0, errno + err = ptrace(req, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0]))) + if err != nil { + return 0, err } n += copy(out, buf[addr%sizeofPtr:]) out = out[n:] @@ -60,27 +60,27 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, errno in for len(out) > 0 { // We use an internal buffer to gaurantee alignment. // It's not documented if this is necessary, but we're paranoid. - errno = ptrace(req, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0]))) - if errno != 0 { - return n, errno + err = ptrace(req, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0]))) + if err != nil { + return n, err } copied := copy(out, buf[0:]) n += copied out = out[copied:] } - return n, 0 + return n, nil } -func PtracePeekText(pid int, addr uintptr, out []byte) (count int, errno int) { +func PtracePeekText(pid int, addr uintptr, out []byte) (count int, err error) { return ptracePeek(PTRACE_PEEKTEXT, pid, addr, out) } -func PtracePeekData(pid int, addr uintptr, out []byte) (count int, errno int) { +func PtracePeekData(pid int, addr uintptr, out []byte) (count int, err error) { return ptracePeek(PTRACE_PEEKDATA, pid, addr, out) } -func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (count int, errno int) { +func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (count int, err error) { // As for ptracePeek, we need to align our accesses to deal // with the possibility of straddling an invalid page. @@ -88,15 +88,15 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c n := 0 if addr%sizeofPtr != 0 { var buf [sizeofPtr]byte - errno = ptrace(peekReq, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0]))) - if errno != 0 { - return 0, errno + err = ptrace(peekReq, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0]))) + if err != nil { + return 0, err } n += copy(buf[addr%sizeofPtr:], data) word := *((*uintptr)(unsafe.Pointer(&buf[0]))) - errno = ptrace(pokeReq, pid, addr-addr%sizeofPtr, word) - if errno != 0 { - return 0, errno + err = ptrace(pokeReq, pid, addr-addr%sizeofPtr, word) + if err != nil { + return 0, err } data = data[n:] } @@ -104,9 +104,9 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c // Interior. for len(data) > int(sizeofPtr) { word := *((*uintptr)(unsafe.Pointer(&data[0]))) - errno = ptrace(pokeReq, pid, addr+uintptr(n), word) - if errno != 0 { - return n, errno + err = ptrace(pokeReq, pid, addr+uintptr(n), word) + if err != nil { + return n, err } n += int(sizeofPtr) data = data[sizeofPtr:] @@ -115,167 +115,167 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c // Trailing edge. if len(data) > 0 { var buf [sizeofPtr]byte - errno = ptrace(peekReq, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0]))) - if errno != 0 { - return n, errno + err = ptrace(peekReq, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0]))) + if err != nil { + return n, err } copy(buf[0:], data) word := *((*uintptr)(unsafe.Pointer(&buf[0]))) - errno = ptrace(pokeReq, pid, addr+uintptr(n), word) - if errno != 0 { - return n, errno + err = ptrace(pokeReq, pid, addr+uintptr(n), word) + if err != nil { + return n, err } n += len(data) } - return n, 0 + return n, nil } -func PtracePokeText(pid int, addr uintptr, data []byte) (count int, errno int) { +func PtracePokeText(pid int, addr uintptr, data []byte) (count int, err error) { return ptracePoke(PTRACE_POKETEXT, PTRACE_PEEKTEXT, pid, addr, data) } -func PtracePokeData(pid int, addr uintptr, data []byte) (count int, errno int) { +func PtracePokeData(pid int, addr uintptr, data []byte) (count int, err error) { return ptracePoke(PTRACE_POKEDATA, PTRACE_PEEKDATA, pid, addr, data) } -func PtraceGetRegs(pid int, regsout *PtraceRegs) (errno int) { +func PtraceGetRegs(pid int, regsout *PtraceRegs) (err error) { return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout))) } -func PtraceSetRegs(pid int, regs *PtraceRegs) (errno int) { +func PtraceSetRegs(pid int, regs *PtraceRegs) (err error) { return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) } -func PtraceSetOptions(pid int, options int) (errno int) { +func PtraceSetOptions(pid int, options int) (err error) { return ptrace(PTRACE_SETOPTIONS, pid, 0, uintptr(options)) } -func PtraceGetEventMsg(pid int) (msg uint, errno int) { +func PtraceGetEventMsg(pid int) (msg uint, err error) { var data _C_long - errno = ptrace(PTRACE_GETEVENTMSG, pid, 0, uintptr(unsafe.Pointer(&data))) + err = ptrace(PTRACE_GETEVENTMSG, pid, 0, uintptr(unsafe.Pointer(&data))) msg = uint(data) return } -func PtraceCont(pid int, signal int) (errno int) { +func PtraceCont(pid int, signal int) (err error) { return ptrace(PTRACE_CONT, pid, 0, uintptr(signal)) } -func PtraceSingleStep(pid int) (errno int) { return ptrace(PTRACE_SINGLESTEP, pid, 0, 0) } +func PtraceSingleStep(pid int) (err error) { return ptrace(PTRACE_SINGLESTEP, pid, 0, 0) } -func PtraceAttach(pid int) (errno int) { return ptrace(PTRACE_ATTACH, pid, 0, 0) } +func PtraceAttach(pid int) (err error) { return ptrace(PTRACE_ATTACH, pid, 0, 0) } -func PtraceDetach(pid int) (errno int) { return ptrace(PTRACE_DETACH, pid, 0, 0) } +func PtraceDetach(pid int) (err error) { return ptrace(PTRACE_DETACH, pid, 0, 0) } // FIXME: mksysinfo needs to produce LINUX_REBOOT_MAGIC[12]. -// //sys reboot(magic1 uint, magic2 uint, cmd int, arg string) (errno int) +// //sys reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) // //reboot(magic1 uint, magic2 uint, cmd int, arg *byte) int -// func Reboot(cmd int) (errno int) { +// func Reboot(cmd int) (err error) { // return reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, "") // } -//sys Acct(path string) (errno int) +//sys Acct(path string) (err error) //acct(path *byte) int // FIXME: mksysinfo Timex -// //sys Adjtimex(buf *Timex) (state int, errno int) +// //sys Adjtimex(buf *Timex) (state int, err error) // //adjtimex(buf *Timex) int -//sys Faccessat(dirfd int, path string, mode uint32, flags int) (errno int) +//sys Faccessat(dirfd int, path string, mode uint32, flags int) (err error) //faccessat(dirfd int, pathname *byte, mode int, flags int) int // FIXME: Only in glibc 2.10 and later. -// //sys Fallocate(fd int, mode uint32, off int64, len int64) (errno int) +// //sys Fallocate(fd int, mode uint32, off int64, len int64) (err error) // //fallocate(fd int, mode int, offset Offset_t, len Offset_t) int -//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (errno int) +//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) //fchmodat(dirfd int, pathname *byte, mode Mode_t, flags int) int -//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (errno int) +//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) //fchownat(dirfd int, path *byte, owner Uid_t, group Gid_t, flags int) int -//sys Flock(fd int, how int) (errno int) +//sys Flock(fd int, how int) (err error) //flock(fd int, how int) int // FIXME: mksysinfo statfs -// //sys Fstatfs(fd int, buf *Statfs_t) (errno int) +// //sys Fstatfs(fd int, buf *Statfs_t) (err error) // //fstatfs(fd int, buf *Statfs_t) int // FIXME: Only available as a syscall. // //sysnb Gettid() (tid int) // //gettid() Pid_t -//sys Ioperm(from int, num int, on int) (errno int) +//sys Ioperm(from int, num int, on int) (err error) //ioperm(from _C_long, num _C_long, on int) int -//sys Iopl(level int) (errno int) +//sys Iopl(level int) (err error) //iopl(level int) int // FIXME: mksysinfo linux_dirent // Or just abandon this function. -// //sys Getdents(fd int, buf []byte) (n int, errno int) +// //sys Getdents(fd int, buf []byte) (n int, err error) // //getdents64(fd int, buf *byte, count uint) -//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, errno int) +//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) //inotify_add_watch(fd int, pathname *byte, mask uint32) int -//sysnb InotifyInit() (fd int, errno int) +//sysnb InotifyInit() (fd int, err error) //inotify_init() int // FIXME: Only in glibc 2.9 and later. -// //sysnb InotifyInit1(flags int) (fd int, errno int) +// //sysnb InotifyInit1(flags int) (fd int, err error) // //inotify_init1(flags int) int -//sysnb InotifyRmWatch(fd int, watchdesc uint32) (success int, errno int) +//sysnb InotifyRmWatch(fd int, watchdesc uint32) (success int, err error) //inotify_rm_watch(fd int, wd uint32) int -//sys Klogctl(typ int, buf []byte) (n int, errno int) +//sys Klogctl(typ int, buf []byte) (n int, err error) //klogctl(typ int, bufp *byte, len int) int -//sys Mkdirat(dirfd int, path string, mode uint32) (errno int) +//sys Mkdirat(dirfd int, path string, mode uint32) (err error) //mkdirat(dirfd int, path *byte, mode Mode_t) int -//sys Mknodat(dirfd int, path string, mode uint32, dev int) (errno int) +//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error) //mknodat(dirfd int, path *byte, mode Mode_t, dev _dev_t) int -//sys PivotRoot(newroot string, putold string) (errno int) +//sys PivotRoot(newroot string, putold string) (err error) //pivot_root(newroot *byte, putold *byte) int -//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (errno int) +//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //renameat(olddirfd int, oldpath *byte, newdirfd int, newpath *byte) int -//sys sendfile(outfd int, infd int, offset *Offset_t, count int) (written int, errno int) +//sys sendfile(outfd int, infd int, offset *Offset_t, count int) (written int, err error) //sendfile64(outfd int, infd int, offset *Offset_t, count Size_t) Ssize_t -func Sendfile(outfd int, infd int, offset *int64, count int) (written int, errno int) { +func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { var soff Offset_t var psoff *Offset_t if offset != nil { psoff = &soff } - written, errno = sendfile(outfd, infd, psoff, count) + written, err = sendfile(outfd, infd, psoff, count) if offset != nil { *offset = int64(soff) } return } -//sys Setfsgid(gid int) (errno int) +//sys Setfsgid(gid int) (err error) //setfsgid(gid Gid_t) int -//sys Setfsuid(uid int) (errno int) +//sys Setfsuid(uid int) (err error) //setfsuid(uid Uid_t) int -//sysnb Setresgid(rgid int, egid int, sgid int) (errno int) +//sysnb Setresgid(rgid int, egid int, sgid int) (err error) //setresgid(rgid Gid_t, egid Gid_t, sgid Gid_t) int -//sysnb Setresuid(ruid int, eguid int, suid int) (errno int) +//sysnb Setresuid(ruid int, eguid int, suid int) (err error) //setresuid(ruid Uid_t, euid Uid_t, suid Uid_t) int -//sys splice(rfd int, roff *_loff_t, wfd int, woff *_loff_t, len int, flags int) (n int64, errno int) +//sys splice(rfd int, roff *_loff_t, wfd int, woff *_loff_t, len int, flags int) (n int64, err error) //splice(rfd int, roff *_loff_t, wfd int, woff *_loff_t, len Size_t, flags uint) Ssize_t -func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, errno int) { +func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error) { var lroff _loff_t var plroff *_loff_t if (roff != nil) { @@ -286,7 +286,7 @@ func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n i if (woff != nil) { plwoff = &lwoff } - n, errno = splice(rfd, plroff, wfd, plwoff, len, flags) + n, err = splice(rfd, plroff, wfd, plwoff, len, flags) if (roff != nil) { *roff = int64(lroff) } @@ -297,37 +297,37 @@ func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n i } // FIXME: mksysinfo statfs -// //sys Statfs(path string, buf *Statfs_t) (errno int) +// //sys Statfs(path string, buf *Statfs_t) (err error) // //statfs(path *byte, buf *Statfs_t) int // FIXME: Only in glibc 2.6 and later. -// //sys SyncFileRange(fd int, off int64, n int64, flags int) (errno int) +// //sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) // //sync_file_range(fd int, off Offset_t, n Offset_t, flags uint) int // FIXME: mksysinfo Sysinfo_t -// //sysnb Sysinfo(info *Sysinfo_t) (errno int) +// //sysnb Sysinfo(info *Sysinfo_t) (err error) // //sysinfo(info *Sysinfo_t) int -//sys Tee(rfd int, wfd int, len int, flags int) (n int64, errno int) +//sys Tee(rfd int, wfd int, len int, flags int) (n int64, err error) //tee(rfd int, wfd int, len Size_t, flags uint) Ssize_t // FIXME: Only available as a syscall. -// //sysnb Tgkill(tgid int, tid int, sig int) (errno int) +// //sysnb Tgkill(tgid int, tid int, sig int) (err error) // //tgkill(tgid int, tid int, sig int) int -//sys unlinkat(dirfd int, path string, flags int) (errno int) +//sys unlinkat(dirfd int, path string, flags int) (err error) //unlinkat(dirfd int, path *byte, flags int) int -func Unlinkat(dirfd int, path string) (errno int) { +func Unlinkat(dirfd int, path string) (err error) { return unlinkat(dirfd, path, 0) } -//sys Unmount(target string, flags int) (errno int) = SYS_UMOUNT2 +//sys Unmount(target string, flags int) (err error) = SYS_UMOUNT2 //umount2(target *byte, flags int) int -//sys Unshare(flags int) (errno int) +//sys Unshare(flags int) (err error) //unshare(flags int) int // FIXME: mksysinfo Ustat_t -// //sys Ustat(dev int, ubuf *Ustat_t) (errno int) +// //sys Ustat(dev int, ubuf *Ustat_t) (err error) // //ustat(dev _dev_t, ubuf *Ustat_t) int diff --git a/libgo/go/syscall/libcall_posix.go b/libgo/go/syscall/libcall_posix.go index 87ed4e628fd..d90e595dba3 100644 --- a/libgo/go/syscall/libcall_posix.go +++ b/libgo/go/syscall/libcall_posix.go @@ -17,43 +17,43 @@ import "unsafe" * Wrapped */ -//sysnb pipe(p *[2]int) (errno int) +//sysnb pipe(p *[2]int) (err error) //pipe(p *[2]int) int -func Pipe(p []int) (errno int) { +func Pipe(p []int) (err error) { if len(p) != 2 { return EINVAL } var pp [2]int - errno = pipe(&pp) + err = pipe(&pp) p[0] = pp[0] p[1] = pp[1] return } -//sys utimes(path string, times *[2]Timeval) (errno int) +//sys utimes(path string, times *[2]Timeval) (err error) //utimes(path *byte, times *[2]Timeval) int -func Utimes(path string, tv []Timeval) (errno int) { +func Utimes(path string, tv []Timeval) (err error) { if len(tv) != 2 { return EINVAL } return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0]))) } -//sys getcwd(buf *byte, size Size_t) (errno int) +//sys getcwd(buf *byte, size Size_t) (err error) //getcwd(buf *byte, size Size_t) *byte const ImplementsGetwd = true -func Getwd() (ret string, errno int) { +func Getwd() (ret string, err error) { for len := Size_t(4096); ; len *= 2 { b := make([]byte, len) err := getcwd(&b[0], len) - if err == 0 { - i := 0; + if err == nil { + i := 0 for b[i] != 0 { - i++; + i++ } - return string(b[0:i]), 0; + return string(b[0:i]), nil } if err != ERANGE { return "", err @@ -61,16 +61,16 @@ func Getwd() (ret string, errno int) { } } -//sysnb getgroups(size int, list *Gid_t) (nn int, errno int) +//sysnb getgroups(size int, list *Gid_t) (nn int, err error) //getgroups(size int, list *Gid_t) int -func Getgroups() (gids []int, errno int) { +func Getgroups() (gids []int, err error) { n, err := getgroups(0, nil) - if err != 0 { - return nil, errno + if err != nil { + return nil, err } if n == 0 { - return nil, 0 + return nil, nil } // Sanity check group count. Max is 1<<16 on GNU/Linux. @@ -80,8 +80,8 @@ func Getgroups() (gids []int, errno int) { a := make([]Gid_t, n) n, err = getgroups(n, &a[0]) - if err != 0 { - return nil, errno + if err != nil { + return nil, err } gids = make([]int, n) for i, v := range a[0:n] { @@ -90,10 +90,10 @@ func Getgroups() (gids []int, errno int) { return } -//sysnb setgroups(n int, list *Gid_t) (errno int) +//sysnb setgroups(n int, list *Gid_t) (err error) //setgroups(n Size_t, list *Gid_t) int -func Setgroups(gids []int) (errno int) { +func Setgroups(gids []int) (err error) { if len(gids) == 0 { return setgroups(0, nil) } @@ -120,10 +120,10 @@ func (w WaitStatus) Signal() int func (w WaitStatus) StopSignal() int func (w WaitStatus) TrapCause() int -//sys Mkfifo(path string, mode uint32) (errno int) +//sys Mkfifo(path string, mode uint32) (err error) //mkfifo(path *byte, mode Mode_t) int -//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, errno int) +//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) //select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) int const nfdbits = unsafe.Sizeof(fds_bits_type) * 8 @@ -154,52 +154,52 @@ func FDZero(set *FdSet) { } } -//sys Access(path string, mode uint32) (errno int) +//sys Access(path string, mode uint32) (err error) //access(path *byte, mode int) int -//sys Chdir(path string) (errno int) +//sys Chdir(path string) (err error) //chdir(path *byte) int -//sys Chmod(path string, mode uint32) (errno int) +//sys Chmod(path string, mode uint32) (err error) //chmod(path *byte, mode Mode_t) int -//sys Chown(path string, uid int, gid int) (errno int) +//sys Chown(path string, uid int, gid int) (err error) //chown(path *byte, uid Uid_t, gid Gid_t) int -//sys Chroot(path string) (errno int) +//sys Chroot(path string) (err error) //chroot(path *byte) int -//sys Close(fd int) (errno int) +//sys Close(fd int) (err error) //close(fd int) int -//sys Creat(path string, mode uint32) (fd int, errno int) +//sys Creat(path string, mode uint32) (fd int, err error) //creat(path *byte, mode Mode_t) int -//sysnb Dup(oldfd int) (fd int, errno int) +//sysnb Dup(oldfd int) (fd int, err error) //dup(oldfd int) int -//sysnb Dup2(oldfd int, newfd int) (fd int, errno int) +//sysnb Dup2(oldfd int, newfd int) (fd int, err error) //dup2(oldfd int, newfd int) int //sys Exit(code int) //exit(code int) -//sys Fchdir(fd int) (errno int) +//sys Fchdir(fd int) (err error) //fchdir(fd int) int -//sys Fchmod(fd int, mode uint32) (errno int) +//sys Fchmod(fd int, mode uint32) (err error) //fchmod(fd int, mode Mode_t) int -//sys Fchown(fd int, uid int, gid int) (errno int) +//sys Fchown(fd int, uid int, gid int) (err error) //fchown(fd int, uid Uid_t, gid Gid_t) int -//sys fcntl(fd int, cmd int, arg int) (val int, errno int) +//sys fcntl(fd int, cmd int, arg int) (val int, err error) //fcntl(fd int, cmd int, arg int) int -//sys Fdatasync(fd int) (errno int) +//sys Fdatasync(fd int) (err error) //fdatasync(fd int) int -//sys Fsync(fd int) (errno int) +//sys Fsync(fd int) (err error) //fsync(fd int) int //sysnb Getegid() (egid int) @@ -214,7 +214,7 @@ func FDZero(set *FdSet) { //sysnb Getpagesize() (pagesize int) //getpagesize() int -//sysnb Getpgid(pid int) (pgid int, errno int) +//sysnb Getpgid(pid int) (pgid int, err error) //getpgid(pid Pid_t) Pid_t //sysnb Getpgrp() (pid int) @@ -227,138 +227,138 @@ func FDZero(set *FdSet) { //getppid() Pid_t // FIXME: mksysinfo Rlimit -// //sysnb Getrlimit(resource int, rlim *Rlimit) (errno int) +// //sysnb Getrlimit(resource int, rlim *Rlimit) (err error) // //getrlimit(resource int, rlim *Rlimit) int -//sysnb Getrusage(who int, rusage *Rusage) (errno int) +//sysnb Getrusage(who int, rusage *Rusage) (err error) //getrusage(who int, rusage *Rusage) int -//sysnb gettimeofday(tv *Timeval, tz *byte) (errno int) +//sysnb gettimeofday(tv *Timeval, tz *byte) (err error) //gettimeofday(tv *Timeval, tz *byte) int -func Gettimeofday(tv *Timeval) (errno int) { +func Gettimeofday(tv *Timeval) (err error) { return gettimeofday(tv, nil) } //sysnb Getuid() (uid int) //getuid() Uid_t -//sysnb Kill(pid int, sig int) (errno int) +//sysnb Kill(pid int, sig int) (err error) //kill(pid Pid_t, sig int) int -//sys Lchown(path string, uid int, gid int) (errno int) +//sys Lchown(path string, uid int, gid int) (err error) //lchown(path *byte, uid Uid_t, gid Gid_t) int -//sys Link(oldpath string, newpath string) (errno int) +//sys Link(oldpath string, newpath string) (err error) //link(oldpath *byte, newpath *byte) int -//sys Mkdir(path string, mode uint32) (errno int) +//sys Mkdir(path string, mode uint32) (err error) //mkdir(path *byte, mode Mode_t) int -//sys Mknod(path string, mode uint32, dev int) (errno int) +//sys Mknod(path string, mode uint32, dev int) (err error) //mknod(path *byte, mode Mode_t, dev _dev_t) int -//sys Mount(source string, target string, fstype string, flags int, data string) (errno int) +//sys Mount(source string, target string, fstype string, flags int, data string) (err error) //mount(source *byte, target *byte, fstype *byte, flags _C_long, data *byte) int -//sys Nanosleep(time *Timespec, leftover *Timespec) (errno int) +//sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //nanosleep(time *Timespec, leftover *Timespec) int -//sys Pause() (errno int) +//sys Pause() (err error) //pause() int -//sys Read(fd int, p []byte) (n int, errno int) +//sys Read(fd int, p []byte) (n int, err error) //read(fd int, buf *byte, count Size_t) Ssize_t -//sys Readlink(path string, buf []byte) (n int, errno int) +//sys Readlink(path string, buf []byte) (n int, err error) //readlink(path *byte, buf *byte, bufsiz Size_t) Ssize_t -//sys Rename(oldpath string, newpath string) (errno int) +//sys Rename(oldpath string, newpath string) (err error) //rename(oldpath *byte, newpath *byte) int -//sys Rmdir(path string) (errno int) +//sys Rmdir(path string) (err error) //rmdir(path *byte) int -//sys Setdomainname(p []byte) (errno int) +//sys Setdomainname(p []byte) (err error) //setdomainname(name *byte, len Size_t) int -//sys Sethostname(p []byte) (errno int) +//sys Sethostname(p []byte) (err error) //sethostname(name *byte, len Size_t) int -//sysnb Setgid(gid int) (errno int) +//sysnb Setgid(gid int) (err error) //setgid(gid Gid_t) int -//sysnb Setregid(rgid int, egid int) (errno int) +//sysnb Setregid(rgid int, egid int) (err error) //setregid(rgid Gid_t, egid Gid_t) int -//sysnb Setpgid(pid int, pgid int) (errno int) +//sysnb Setpgid(pid int, pgid int) (err error) //setpgid(pid Pid_t, pgid Pid_t) int -//sysnb Setreuid(ruid int, euid int) (errno int) +//sysnb Setreuid(ruid int, euid int) (err error) //setreuid(ruid Uid_t, euid Uid_t) int // FIXME: mksysinfo Rlimit -// //sysnb Setrlimit(resource int, rlim *Rlimit) (errno int) +// //sysnb Setrlimit(resource int, rlim *Rlimit) (err error) // //setrlimit(resource int, rlim *Rlimit) int -//sysnb Setsid() (pid int, errno int) +//sysnb Setsid() (pid int, err error) //setsid() Pid_t -//sysnb settimeofday(tv *Timeval, tz *byte) (errno int) +//sysnb settimeofday(tv *Timeval, tz *byte) (err error) //settimeofday(tv *Timeval, tz *byte) int -func Settimeofday(tv *Timeval) (errno int) { +func Settimeofday(tv *Timeval) (err error) { return settimeofday(tv, nil) } -//sysnb Setuid(uid int) (errno int) +//sysnb Setuid(uid int) (err error) //setuid(uid Uid_t) int -//sys Symlink(oldpath string, newpath string) (errno int) +//sys Symlink(oldpath string, newpath string) (err error) //symlink(oldpath *byte, newpath *byte) int //sys Sync() //sync() // FIXME: mksysinfo Time_t -// //sysnb Time(t *Time_t) (tt Time_t, errno int) +// //sysnb Time(t *Time_t) (tt Time_t, err error) // //time(t *Time_t) Time_t // FIXME: mksysinfo Tms -// //sysnb Times(tms *Tms) (ticks uintptr, errno int) +// //sysnb Times(tms *Tms) (ticks uintptr, err error) // //times(tms *Tms) _clock_t //sysnb Umask(mask int) (oldmask int) //umask(mask Mode_t) Mode_t -//sys Unlink(path string) (errno int) +//sys Unlink(path string) (err error) //unlink(path *byte) int // FIXME: mksysinfo Utimbuf -// //sys Utime(path string, buf *Utimbuf) (errno int) +// //sys Utime(path string, buf *Utimbuf) (err error) // //utime(path *byte, buf *Utimbuf) int -//sys Write(fd int, p []byte) (n int, errno int) +//sys Write(fd int, p []byte) (n int, err error) //write(fd int, buf *byte, count Size_t) Ssize_t -//sys munmap(addr uintptr, length uintptr) (errno int) +//sys munmap(addr uintptr, length uintptr) (err error) //munmap(addr *byte, length Size_t) int -//sys Madvise(b []byte, advice int) (errno int) +//sys Madvise(b []byte, advice int) (err error) //madvise(addr *byte, len Size_t, advice int) int -//sys Mprotect(b []byte, prot int) (errno int) +//sys Mprotect(b []byte, prot int) (err error) //mprotect(addr *byte, len Size_t, prot int) int -//sys Mlock(b []byte) (errno int) +//sys Mlock(b []byte) (err error) //mlock(addr *byte, len Size_t) int -//sys Munlock(b []byte) (errno int) +//sys Munlock(b []byte) (err error) //munlock(addr *byte, len Size_t) int -//sys Mlockall(flags int) (errno int) +//sys Mlockall(flags int) (err error) //mlockall(flags int) int -//sys Munlockall() (errno int) +//sys Munlockall() (err error) //munlockall() int func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } @@ -378,8 +378,8 @@ func NsecToTimeval(nsec int64) (tv Timeval) { return } -//sysnb Tcgetattr(fd int, p *Termios) (errno int) +//sysnb Tcgetattr(fd int, p *Termios) (err error) //tcgetattr(fd int, p *Termios) int -//sys Tcsetattr(fd int, actions int, p *Termios) (errno int) +//sys Tcsetattr(fd int, actions int, p *Termios) (err error) //tcsetattr(fd int, actions int, p *Termios) int diff --git a/libgo/go/syscall/libcall_posix_largefile.go b/libgo/go/syscall/libcall_posix_largefile.go index acfafecc581..e898648157d 100644 --- a/libgo/go/syscall/libcall_posix_largefile.go +++ b/libgo/go/syscall/libcall_posix_largefile.go @@ -6,32 +6,32 @@ package syscall -//sys Fstat(fd int, stat *Stat_t) (errno int) +//sys Fstat(fd int, stat *Stat_t) (err error) //fstat64(fd int, stat *Stat_t) int -//sys Ftruncate(fd int, length int64) (errno int) +//sys Ftruncate(fd int, length int64) (err error) //ftruncate64(fd int, length Offset_t) int -//sys Lstat(path string, stat *Stat_t) (errno int) +//sys Lstat(path string, stat *Stat_t) (err error) //lstat64(path *byte, stat *Stat_t) int -//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, errno int) +//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) //mmap64(addr *byte, length Size_t, prot int, flags int, fd int, offset Offset_t) *byte -//sys Open(path string, mode int, perm uint32) (fd int, errno int) +//sys Open(path string, mode int, perm uint32) (fd int, err error) //open64(path *byte, mode int, perm Mode_t) int -//sys Pread(fd int, p []byte, offset int64) (n int, errno int) +//sys Pread(fd int, p []byte, offset int64) (n int, err error) //pread64(fd int, buf *byte, count Size_t, offset Offset_t) Ssize_t -//sys Pwrite(fd int, p []byte, offset int64) (n int, errno int) +//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) //pwrite64(fd int, buf *byte, count Size_t, offset Offset_t) Ssize_t -//sys Seek(fd int, offset int64, whence int) (off int64, errno int) +//sys Seek(fd int, offset int64, whence int) (off int64, err error) //lseek64(fd int, offset Offset_t, whence int) Offset_t -//sys Stat(path string, stat *Stat_t) (errno int) +//sys Stat(path string, stat *Stat_t) (err error) //stat64(path *byte, stat *Stat_t) int -//sys Truncate(path string, length int64) (errno int) +//sys Truncate(path string, length int64) (err error) //truncate64(path *byte, length Offset_t) int diff --git a/libgo/go/syscall/libcall_posix_regfile.go b/libgo/go/syscall/libcall_posix_regfile.go index b71da0ceb48..97167013a18 100644 --- a/libgo/go/syscall/libcall_posix_regfile.go +++ b/libgo/go/syscall/libcall_posix_regfile.go @@ -7,32 +7,32 @@ package syscall -//sys Fstat(fd int, stat *Stat_t) (errno int) +//sys Fstat(fd int, stat *Stat_t) (err error) //fstat(fd int, stat *Stat_t) int -//sys Ftruncate(fd int, length int64) (errno int) +//sys Ftruncate(fd int, length int64) (err error) //ftruncate(fd int, length Offset_t) int -//sys Lstat(path string, stat *Stat_t) (errno int) +//sys Lstat(path string, stat *Stat_t) (err error) //lstat(path *byte, stat *Stat_t) int -//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, errno int) +//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) //mmap(addr *byte, length Size_t, prot int, flags int, fd int, offset Offset_t) *byte -//sys Open(path string, mode int, perm uint32) (fd int, errno int) +//sys Open(path string, mode int, perm uint32) (fd int, err error) //open(path *byte, mode int, perm Mode_t) int -//sys Pread(fd int, p []byte, offset int64) (n int, errno int) +//sys Pread(fd int, p []byte, offset int64) (n int, err error) //pread(fd int, buf *byte, count Size_t, offset Offset_t) Ssize_t -//sys Pwrite(fd int, p []byte, offset int64) (n int, errno int) +//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) //pwrite(fd int, buf *byte, count Size_t, offset Offset_t) Ssize_t -//sys Seek(fd int, offset int64, whence int) (off int64, errno int) +//sys Seek(fd int, offset int64, whence int) (off int64, err error) //lseek(fd int, offset Offset_t, whence int) Offset_t -//sys Stat(path string, stat *Stat_t) (errno int) +//sys Stat(path string, stat *Stat_t) (err error) //stat(path *byte, stat *Stat_t) int -//sys Truncate(path string, length int64) (errno int) +//sys Truncate(path string, length int64) (err error) //truncate(path *byte, length Offset_t) int diff --git a/libgo/go/syscall/libcall_solaris_386.go b/libgo/go/syscall/libcall_solaris_386.go index 9c4e966f1ae..e94deecf8cd 100644 --- a/libgo/go/syscall/libcall_solaris_386.go +++ b/libgo/go/syscall/libcall_solaris_386.go @@ -5,8 +5,8 @@ package syscall // 32-bit Solaris 2/x86 needs to use _nuname internally, cf. <sys/utsname.h>. -//sysnb Uname(buf *Utsname) (errno int) +//sysnb Uname(buf *Utsname) (err error) //_nuname(buf *Utsname) int -//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (errno int) +//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (err Errno) //ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long diff --git a/libgo/go/syscall/libcall_solaris_amd64.go b/libgo/go/syscall/libcall_solaris_amd64.go index f0d335dbb67..69b11ba5ee6 100644 --- a/libgo/go/syscall/libcall_solaris_amd64.go +++ b/libgo/go/syscall/libcall_solaris_amd64.go @@ -5,6 +5,6 @@ package syscall // 64-bit ptrace(3C) doesn't exist -func raw_ptrace(request int, pid int, addr *byte, data *byte) int { +func raw_ptrace(request int, pid int, addr *byte, data *byte) Errno { return ENOSYS } diff --git a/libgo/go/syscall/libcall_solaris_sparc.go b/libgo/go/syscall/libcall_solaris_sparc.go index ae453559713..69e0db264bd 100644 --- a/libgo/go/syscall/libcall_solaris_sparc.go +++ b/libgo/go/syscall/libcall_solaris_sparc.go @@ -4,5 +4,5 @@ package syscall -//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (errno int) +//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (err Errno) //ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long diff --git a/libgo/go/syscall/libcall_solaris_sparc64.go b/libgo/go/syscall/libcall_solaris_sparc64.go index f0d335dbb67..69b11ba5ee6 100644 --- a/libgo/go/syscall/libcall_solaris_sparc64.go +++ b/libgo/go/syscall/libcall_solaris_sparc64.go @@ -5,6 +5,6 @@ package syscall // 64-bit ptrace(3C) doesn't exist -func raw_ptrace(request int, pid int, addr *byte, data *byte) int { +func raw_ptrace(request int, pid int, addr *byte, data *byte) Errno { return ENOSYS } diff --git a/libgo/go/syscall/libcall_support.go b/libgo/go/syscall/libcall_support.go index daed8073caf..7449a0adf79 100644 --- a/libgo/go/syscall/libcall_support.go +++ b/libgo/go/syscall/libcall_support.go @@ -8,5 +8,5 @@ package syscall func entersyscall() func exitsyscall() -func GetErrno() int -func SetErrno(int) +func GetErrno() Errno +func SetErrno(Errno) diff --git a/libgo/go/syscall/libcall_uname.go b/libgo/go/syscall/libcall_uname.go index e4c32b12d6a..519e6dc25d0 100644 --- a/libgo/go/syscall/libcall_uname.go +++ b/libgo/go/syscall/libcall_uname.go @@ -4,5 +4,5 @@ package syscall -//sysnb Uname(buf *Utsname) (errno int) +//sysnb Uname(buf *Utsname) (err error) //uname(buf *Utsname) int diff --git a/libgo/go/syscall/libcall_wait4.go b/libgo/go/syscall/libcall_wait4.go index 7a63bc71e97..578686926f3 100644 --- a/libgo/go/syscall/libcall_wait4.go +++ b/libgo/go/syscall/libcall_wait4.go @@ -6,14 +6,13 @@ package syscall -//sys wait4(pid Pid_t, status *int, options int, rusage *Rusage) (wpid Pid_t, errno int) +//sys wait4(pid Pid_t, status *int, options int, rusage *Rusage) (wpid Pid_t, err error) //wait4(pid Pid_t, status *int, options int, rusage *Rusage) Pid_t -func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, errno int) { +func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) { var status int r, err := wait4(Pid_t(pid), &status, options, rusage) wpid = int(r) - errno = err if wstatus != nil { *wstatus = WaitStatus(status) } diff --git a/libgo/go/syscall/libcall_waitpid.go b/libgo/go/syscall/libcall_waitpid.go index 014446307e3..1c476d829d0 100644 --- a/libgo/go/syscall/libcall_waitpid.go +++ b/libgo/go/syscall/libcall_waitpid.go @@ -6,14 +6,13 @@ package syscall -//sys waitpid(pid Pid_t, status *int, options int) (wpid Pid_t, errno int) +//sys waitpid(pid Pid_t, status *int, options int) (wpid Pid_t, err error) //waitpid(pid Pid_t, status *int, options int) Pid_t -func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, errno int) { +func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) { var status int r, err := waitpid(Pid_t(pid), &status, options) wpid = int(r) - errno = err if wstatus != nil { *wstatus = WaitStatus(status) } diff --git a/libgo/go/syscall/lsf_linux.go b/libgo/go/syscall/lsf_linux.go index 0976688b9e7..05d653b4aa0 100644 --- a/libgo/go/syscall/lsf_linux.go +++ b/libgo/go/syscall/lsf_linux.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// GNU/Linux socket filter +// Linux socket filter package syscall @@ -18,10 +18,10 @@ func LsfJump(code, k, jt, jf int) *SockFilter { return &SockFilter{Code: uint16(code), Jt: uint8(jt), Jf: uint8(jf), K: uint32(k)} } -func LsfSocket(ifindex, proto int) (int, int) { +func LsfSocket(ifindex, proto int) (int, error) { var lsall SockaddrLinklayer s, e := Socket(AF_PACKET, SOCK_RAW, proto) - if e != 0 { + if e != nil { return 0, e } p := (*[2]byte)(unsafe.Pointer(&lsall.Protocol)) @@ -29,11 +29,11 @@ func LsfSocket(ifindex, proto int) (int, int) { p[1] = byte(proto) lsall.Ifindex = ifindex e = Bind(s, &lsall) - if e != 0 { + if e != nil { Close(s) return 0, e } - return s, 0 + return s, nil } type iflags struct { @@ -41,17 +41,17 @@ type iflags struct { flags uint16 } -func SetLsfPromisc(name string, m bool) int { +func SetLsfPromisc(name string, m bool) error { s, e := Socket(AF_INET, SOCK_DGRAM, 0) - if e != 0 { + if e != nil { return e } defer Close(s) var ifl iflags copy(ifl.name[:], []byte(name)) _, _, ep := Syscall(SYS_IOCTL, uintptr(s), SIOCGIFFLAGS, uintptr(unsafe.Pointer(&ifl))) - if e := int(ep); e != 0 { - return e + if ep != 0 { + return Errno(ep) } if m { ifl.flags |= uint16(IFF_PROMISC) @@ -59,20 +59,20 @@ func SetLsfPromisc(name string, m bool) int { ifl.flags &= ^uint16(IFF_PROMISC) } _, _, ep = Syscall(SYS_IOCTL, uintptr(s), SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifl))) - if e := int(ep); e != 0 { - return e + if ep != 0 { + return Errno(ep) } - return 0 + return nil } -func AttachLsf(fd int, i []SockFilter) int { +func AttachLsf(fd int, i []SockFilter) error { var p SockFprog p.Len = uint16(len(i)) p.Filter = (*SockFilter)(unsafe.Pointer(&i[0])) return setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, uintptr(unsafe.Pointer(&p)), unsafe.Sizeof(p)) } -func DetachLsf(fd int) int { +func DetachLsf(fd int) error { var dummy int return setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, uintptr(unsafe.Pointer(&dummy)), unsafe.Sizeof(dummy)) } diff --git a/libgo/go/syscall/mksyscall.awk b/libgo/go/syscall/mksyscall.awk index 49828d94ce3..b02989cc323 100644 --- a/libgo/go/syscall/mksyscall.awk +++ b/libgo/go/syscall/mksyscall.awk @@ -12,7 +12,7 @@ # This includes return parameters. # * The parameter lists must give a type for each argument: # the (x, y, z int) shorthand is not allowed. -# * If the return parameter is an error number, it must be named errno. +# * If the return parameter is an error, it must be named err. # A line beginning with //sysnb is like //sys, except that the # goroutine will not be suspended during the execution of the library @@ -217,13 +217,13 @@ BEGIN { goname = goparam[1] gotype = goparam[2] - if (goname == "errno") { + if (goname == "err") { if (cfnresult ~ /^\*/) { print "\tif _r == nil {" } else { print "\tif _r < 0 {" } - print "\t\terrno = GetErrno()" + print "\t\terr = GetErrno()" print "\t}" } else if (gotype == "uintptr" && cfnresult ~ /^\*/) { printf("\t%s = (%s)(unsafe.Pointer(_r))\n", goname, gotype) diff --git a/libgo/go/syscall/netlink_linux.go b/libgo/go/syscall/netlink_linux.go index 4ee78d62f25..8683bb3dacb 100644 --- a/libgo/go/syscall/netlink_linux.go +++ b/libgo/go/syscall/netlink_linux.go @@ -63,31 +63,28 @@ func newNetlinkRouteRequest(proto, seq, family int) []byte { // NetlinkRIB returns routing information base, as known as RIB, // which consists of network facility information, states and // parameters. -func NetlinkRIB(proto, family int) ([]byte, int) { +func NetlinkRIB(proto, family int) ([]byte, error) { var ( - s int - e int lsanl SockaddrNetlink - seq int tab []byte ) - s, e = Socket(AF_NETLINK, SOCK_RAW, 0) - if e != 0 { + s, e := Socket(AF_NETLINK, SOCK_RAW, 0) + if e != nil { return nil, e } defer Close(s) lsanl.Family = AF_NETLINK e = Bind(s, &lsanl) - if e != 0 { + if e != nil { return nil, e } - seq++ + seq := 1 wb := newNetlinkRouteRequest(proto, seq, family) e = Sendto(s, wb, 0, &lsanl) - if e != 0 { + if e != nil { return nil, e } @@ -100,7 +97,7 @@ func NetlinkRIB(proto, family int) ([]byte, int) { rb = make([]byte, Getpagesize()) nr, _, e = Recvfrom(s, rb, 0) - if e != 0 { + if e != nil { return nil, e } if nr < NLMSG_HDRLEN { @@ -111,7 +108,7 @@ func NetlinkRIB(proto, family int) ([]byte, int) { msgs, _ := ParseNetlinkMessage(rb) for _, m := range msgs { - if lsa, e = Getsockname(s); e != 0 { + if lsa, e = Getsockname(s); e != nil { return nil, e } switch v := lsa.(type) { @@ -132,7 +129,7 @@ func NetlinkRIB(proto, family int) ([]byte, int) { } done: - return tab, 0 + return tab, nil } // NetlinkMessage represents the netlink message. @@ -143,18 +140,18 @@ type NetlinkMessage struct { // ParseNetlinkMessage parses buf as netlink messages and returns // the slice containing the NetlinkMessage structs. -func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, int) { +func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, error) { var ( h *NlMsghdr dbuf []byte dlen int - e int + e error msgs []NetlinkMessage ) for len(buf) >= NLMSG_HDRLEN { h, dbuf, dlen, e = netlinkMessageHeaderAndData(buf) - if e != 0 { + if e != nil { break } m := NetlinkMessage{} @@ -167,12 +164,12 @@ func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, int) { return msgs, e } -func netlinkMessageHeaderAndData(buf []byte) (*NlMsghdr, []byte, int, int) { +func netlinkMessageHeaderAndData(buf []byte) (*NlMsghdr, []byte, int, error) { h := (*NlMsghdr)(unsafe.Pointer(&buf[0])) if int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(buf) { return nil, nil, 0, EINVAL } - return h, buf[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), 0 + return h, buf[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil } // NetlinkRouteAttr represents the netlink route attribute. @@ -184,13 +181,13 @@ type NetlinkRouteAttr struct { // ParseNetlinkRouteAttr parses msg's payload as netlink route // attributes and returns the slice containing the NetlinkRouteAttr // structs. -func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, int) { +func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, error) { var ( buf []byte a *RtAttr alen int vbuf []byte - e int + e error attrs []NetlinkRouteAttr ) @@ -207,7 +204,7 @@ func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, int) { for len(buf) >= SizeofRtAttr { a, vbuf, alen, e = netlinkRouteAttrAndValue(buf) - if e != 0 { + if e != nil { break } ra := NetlinkRouteAttr{} @@ -217,13 +214,13 @@ func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, int) { buf = buf[alen:] } - return attrs, 0 + return attrs, nil } -func netlinkRouteAttrAndValue(buf []byte) (*RtAttr, []byte, int, int) { +func netlinkRouteAttrAndValue(buf []byte) (*RtAttr, []byte, int, error) { h := (*RtAttr)(unsafe.Pointer(&buf[0])) if int(h.Len) < SizeofRtAttr || int(h.Len) > len(buf) { return nil, nil, 0, EINVAL } - return h, buf[SizeofRtAttr:], rtaAlignOf(int(h.Len)), 0 + return h, buf[SizeofRtAttr:], rtaAlignOf(int(h.Len)), nil } diff --git a/libgo/go/syscall/route_bsd.go b/libgo/go/syscall/route_bsd.go index f6b124b64e4..bc4c15e950b 100644 --- a/libgo/go/syscall/route_bsd.go +++ b/libgo/go/syscall/route_bsd.go @@ -29,29 +29,24 @@ func rsaAlignOf(salen int) int { // RouteRIB returns routing information base, as known as RIB, // which consists of network facility information, states and // parameters. -func RouteRIB(facility, param int) ([]byte, int) { - var ( - tab []byte - e int - ) - +func RouteRIB(facility, param int) ([]byte, error) { mib := []_C_int{CTL_NET, AF_ROUTE, 0, 0, _C_int(facility), _C_int(param)} // Find size. n := uintptr(0) - if e = sysctl(mib, nil, &n, nil, 0); e != 0 { - return nil, e + if err := sysctl(mib, nil, &n, nil, 0); err != nil { + return nil, err } if n == 0 { - return nil, 0 + return nil, nil } - tab = make([]byte, n) - if e = sysctl(mib, &tab[0], &n, nil, 0); e != 0 { - return nil, e + tab := make([]byte, n) + if err := sysctl(mib, &tab[0], &n, nil, 0); err != nil { + return nil, err } - return tab[:n], 0 + return tab[:n], nil } // RoutingMessage represents a routing message. @@ -91,7 +86,7 @@ func (m *RouteMessage) sockaddr() []Sockaddr { switch i { case RTAX_DST, RTAX_GATEWAY: sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) - if e != 0 { + if e != nil { return nil } if i == RTAX_DST { @@ -134,7 +129,7 @@ func (m *InterfaceMessage) sockaddr() (sas []Sockaddr) { return nil } sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(&m.Data[0]))) - if e != 0 { + if e != nil { return nil } return append(sas, sa) @@ -163,7 +158,7 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) { switch i { case RTAX_IFA: sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) - if e != 0 { + if e != nil { return nil } sas = append(sas, sa) @@ -178,7 +173,7 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) { // ParseRoutingMessage parses buf as routing messages and returns // the slice containing the RoutingMessage interfaces. -func ParseRoutingMessage(buf []byte) (msgs []RoutingMessage, errno int) { +func ParseRoutingMessage(buf []byte) (msgs []RoutingMessage, err error) { for len(buf) >= anyMessageLen { any := (*anyMessage)(unsafe.Pointer(&buf[0])) if any.Version != RTM_VERSION { @@ -187,11 +182,11 @@ func ParseRoutingMessage(buf []byte) (msgs []RoutingMessage, errno int) { msgs = append(msgs, any.toRoutingMessage(buf)) buf = buf[any.Msglen:] } - return msgs, 0 + return msgs, nil } // ParseRoutingMessage parses msg's payload as raw sockaddrs and // returns the slice containing the Sockaddr interfaces. -func ParseRoutingSockaddr(msg RoutingMessage) (sas []Sockaddr, errno int) { - return append(sas, msg.sockaddr()...), 0 +func ParseRoutingSockaddr(msg RoutingMessage) (sas []Sockaddr, err error) { + return append(sas, msg.sockaddr()...), nil } diff --git a/libgo/go/syscall/route_darwin.go b/libgo/go/syscall/route_darwin.go index 9d3a701daba..410e70a138a 100644 --- a/libgo/go/syscall/route_darwin.go +++ b/libgo/go/syscall/route_darwin.go @@ -63,7 +63,7 @@ func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) { switch i { case RTAX_IFA: sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) - if e != 0 { + if e != nil { return nil } sas = append(sas, sa) diff --git a/libgo/go/syscall/route_freebsd.go b/libgo/go/syscall/route_freebsd.go index 0d61d08b08c..094e17044db 100644 --- a/libgo/go/syscall/route_freebsd.go +++ b/libgo/go/syscall/route_freebsd.go @@ -63,7 +63,7 @@ func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) { switch i { case RTAX_IFA: sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) - if e != 0 { + if e != nil { return nil } sas = append(sas, sa) diff --git a/libgo/go/syscall/sleep_rtems.go b/libgo/go/syscall/sleep_rtems.go index 8992eb53c3e..9d72203e8ef 100644 --- a/libgo/go/syscall/sleep_rtems.go +++ b/libgo/go/syscall/sleep_rtems.go @@ -6,8 +6,8 @@ package syscall -func Sleep(nsec int64) (errno int) { +func Sleep(nsec int64) (err error) { ts := NsecToTimespec(nsec) - errno = Nanosleep(&ts, nil) + err = Nanosleep(&ts, nil) return } diff --git a/libgo/go/syscall/sleep_select.go b/libgo/go/syscall/sleep_select.go index 3ebaf58f969..533f554da05 100644 --- a/libgo/go/syscall/sleep_select.go +++ b/libgo/go/syscall/sleep_select.go @@ -6,8 +6,8 @@ package syscall -func Sleep(nsec int64) (errno int) { +func Sleep(nsec int64) (err error) { tv := NsecToTimeval(nsec); - _, err := Select(0, nil, nil, nil, &tv); + _, err = Select(0, nil, nil, nil, &tv); return err; } diff --git a/libgo/go/syscall/sockcmsg_linux.go b/libgo/go/syscall/sockcmsg_linux.go index b025ca52101..0b4caa1d055 100644 --- a/libgo/go/syscall/sockcmsg_linux.go +++ b/libgo/go/syscall/sockcmsg_linux.go @@ -26,7 +26,7 @@ func UnixCredentials(ucred *Ucred) []byte { // ParseUnixCredentials decodes a socket control message that contains // credentials in a Ucred structure. To receive such a message, the // SO_PASSCRED option must be enabled on the socket. -func ParseUnixCredentials(msg *SocketControlMessage) (*Ucred, int) { +func ParseUnixCredentials(msg *SocketControlMessage) (*Ucred, error) { if msg.Header.Level != SOL_SOCKET { return nil, EINVAL } @@ -34,5 +34,5 @@ func ParseUnixCredentials(msg *SocketControlMessage) (*Ucred, int) { return nil, EINVAL } ucred := *(*Ucred)(unsafe.Pointer(&msg.Data[0])) - return &ucred, 0 + return &ucred, nil } diff --git a/libgo/go/syscall/sockcmsg_unix.go b/libgo/go/syscall/sockcmsg_unix.go index c9872aeba31..84c1383d7e2 100644 --- a/libgo/go/syscall/sockcmsg_unix.go +++ b/libgo/go/syscall/sockcmsg_unix.go @@ -47,17 +47,17 @@ type SocketControlMessage struct { Data []byte } -func ParseSocketControlMessage(buf []byte) ([]SocketControlMessage, int) { +func ParseSocketControlMessage(buf []byte) ([]SocketControlMessage, error) { var ( h *Cmsghdr dbuf []byte - e int + e error cmsgs []SocketControlMessage ) for len(buf) >= CmsgLen(0) { h, dbuf, e = socketControlMessageHeaderAndData(buf) - if e != 0 { + if e != nil { break } m := SocketControlMessage{} @@ -70,12 +70,12 @@ func ParseSocketControlMessage(buf []byte) ([]SocketControlMessage, int) { return cmsgs, e } -func socketControlMessageHeaderAndData(buf []byte) (*Cmsghdr, []byte, int) { +func socketControlMessageHeaderAndData(buf []byte) (*Cmsghdr, []byte, error) { h := (*Cmsghdr)(unsafe.Pointer(&buf[0])) if h.Len < SizeofCmsghdr || int(h.Len) > len(buf) { return nil, nil, EINVAL } - return h, buf[cmsgAlignOf(SizeofCmsghdr):], 0 + return h, buf[cmsgAlignOf(SizeofCmsghdr):], nil } // UnixRights encodes a set of open file descriptors into a socket @@ -99,7 +99,7 @@ func UnixRights(fds ...int) []byte { // ParseUnixRights decodes a socket control message that contains an // integer array of open file descriptors from another process. -func ParseUnixRights(msg *SocketControlMessage) ([]int, int) { +func ParseUnixRights(msg *SocketControlMessage) ([]int, error) { if msg.Header.Level != SOL_SOCKET { return nil, EINVAL } @@ -111,5 +111,5 @@ func ParseUnixRights(msg *SocketControlMessage) ([]int, int) { fds[j] = int(*(*int32)(unsafe.Pointer(&msg.Data[i]))) j++ } - return fds, 0 + return fds, nil } diff --git a/libgo/go/syscall/socket.go b/libgo/go/syscall/socket.go index e0218ba359d..005fd843486 100644 --- a/libgo/go/syscall/socket.go +++ b/libgo/go/syscall/socket.go @@ -17,7 +17,7 @@ import "unsafe" var SocketDisableIPv6 bool type Sockaddr interface { - sockaddr() (ptr *RawSockaddrAny, len Socklen_t, errno int) // lowercase; only we can define Sockaddrs + sockaddr() (ptr *RawSockaddrAny, len Socklen_t, err error) // lowercase; only we can define Sockaddrs } type RawSockaddrAny struct { @@ -33,7 +33,7 @@ type SockaddrInet4 struct { raw RawSockaddrInet4 } -func (sa *SockaddrInet4) sockaddr() (*RawSockaddrAny, Socklen_t, int) { +func (sa *SockaddrInet4) sockaddr() (*RawSockaddrAny, Socklen_t, error) { if sa.Port < 0 || sa.Port > 0xFFFF { return nil, 0, EINVAL } @@ -45,7 +45,7 @@ func (sa *SockaddrInet4) sockaddr() (*RawSockaddrAny, Socklen_t, int) { for i := 0; i < len(sa.Addr); i++ { sa.raw.Addr[i] = sa.Addr[i] } - return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), n, 0 + return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), n, nil } type SockaddrInet6 struct { @@ -55,7 +55,7 @@ type SockaddrInet6 struct { raw RawSockaddrInet6 } -func (sa *SockaddrInet6) sockaddr() (*RawSockaddrAny, Socklen_t, int) { +func (sa *SockaddrInet6) sockaddr() (*RawSockaddrAny, Socklen_t, error) { if sa.Port < 0 || sa.Port > 0xFFFF { return nil, 0, EINVAL } @@ -68,7 +68,7 @@ func (sa *SockaddrInet6) sockaddr() (*RawSockaddrAny, Socklen_t, int) { for i := 0; i < len(sa.Addr); i++ { sa.raw.Addr[i] = sa.Addr[i] } - return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), n, 0 + return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), n, nil } type SockaddrUnix struct { @@ -76,7 +76,7 @@ type SockaddrUnix struct { raw RawSockaddrUnix } -func (sa *SockaddrUnix) sockaddr() (*RawSockaddrAny, Socklen_t, int) { +func (sa *SockaddrUnix) sockaddr() (*RawSockaddrAny, Socklen_t, error) { name := sa.Name n := len(name) if n >= len(sa.raw.Path) || n == 0 { @@ -92,186 +92,186 @@ func (sa *SockaddrUnix) sockaddr() (*RawSockaddrAny, Socklen_t, int) { } // length is family (uint16), name, NUL. - return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), 2 + Socklen_t(n) + 1, 0 + return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), 2 + Socklen_t(n) + 1, nil } -func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, int) { +func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) { switch rsa.Addr.Family { case AF_UNIX: pp := (*RawSockaddrUnix)(unsafe.Pointer(rsa)) sa := new(SockaddrUnix) n, err := pp.getLen() - if err != 0 { + if err != nil { return nil, err } - bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0])); - sa.Name = string(bytes[0:n]); - return sa, 0; + bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0])) + sa.Name = string(bytes[0:n]) + return sa, nil case AF_INET: - pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa)); - sa := new(SockaddrInet4); - p := (*[2]byte)(unsafe.Pointer(&pp.Port)); - sa.Port = int(p[0])<<8 + int(p[1]); + pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa)) + sa := new(SockaddrInet4) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + sa.Port = int(p[0])<<8 + int(p[1]) for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i]; + sa.Addr[i] = pp.Addr[i] } - return sa, 0; + return sa, nil case AF_INET6: - pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa)); - sa := new(SockaddrInet6); - p := (*[2]byte)(unsafe.Pointer(&pp.Port)); - sa.Port = int(p[0])<<8 + int(p[1]); + pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa)) + sa := new(SockaddrInet6) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + sa.Port = int(p[0])<<8 + int(p[1]) for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i]; + sa.Addr[i] = pp.Addr[i] } - return sa, 0; + return sa, nil } return anyToSockaddrOS(rsa) } -//sys accept(fd int, sa *RawSockaddrAny, len *Socklen_t) (nfd int, errno int) +//sys accept(fd int, sa *RawSockaddrAny, len *Socklen_t) (nfd int, err error) //accept(fd int, sa *RawSockaddrAny, len *Socklen_t) int -func Accept(fd int) (nfd int, sa Sockaddr, errno int) { +func Accept(fd int) (nfd int, sa Sockaddr, err error) { var rsa RawSockaddrAny var len Socklen_t = SizeofSockaddrAny - nfd, errno = accept(fd, &rsa, &len) - if errno != 0 { + nfd, err = accept(fd, &rsa, &len) + if err != nil { return } - sa, errno = anyToSockaddr(&rsa) - if errno != 0 { + sa, err = anyToSockaddr(&rsa) + if err != nil { Close(nfd) nfd = 0 } return } -//sysnb getsockname(fd int, sa *RawSockaddrAny, len *Socklen_t) (errno int) +//sysnb getsockname(fd int, sa *RawSockaddrAny, len *Socklen_t) (err error) //getsockname(fd int, sa *RawSockaddrAny, len *Socklen_t) int -func Getsockname(fd int) (sa Sockaddr, errno int) { +func Getsockname(fd int) (sa Sockaddr, err error) { var rsa RawSockaddrAny var len Socklen_t = SizeofSockaddrAny - if errno = getsockname(fd, &rsa, &len); errno != 0 { + if err = getsockname(fd, &rsa, &len); err != nil { return } return anyToSockaddr(&rsa) } -//sysnb getpeername(fd int, sa *RawSockaddrAny, len *Socklen_t) (errno int) +//sysnb getpeername(fd int, sa *RawSockaddrAny, len *Socklen_t) (err error) //getpeername(fd int, sa *RawSockaddrAny, len *Socklen_t) int -func Getpeername(fd int) (sa Sockaddr, errno int) { +func Getpeername(fd int) (sa Sockaddr, err error) { var rsa RawSockaddrAny var len Socklen_t = SizeofSockaddrAny - if getpeername(fd, &rsa, &len); errno != 0 { + if err = getpeername(fd, &rsa, &len); err != nil { return } return anyToSockaddr(&rsa) } -//sys bind(fd int, sa *RawSockaddrAny, len Socklen_t) (errno int) +//sys bind(fd int, sa *RawSockaddrAny, len Socklen_t) (err error) //bind(fd int, sa *RawSockaddrAny, len Socklen_t) int -func Bind(fd int, sa Sockaddr) (errno int) { +func Bind(fd int, sa Sockaddr) (err error) { ptr, n, err := sa.sockaddr() - if err != 0 { + if err != nil { return err } return bind(fd, ptr, n) } -//sys connect(s int, addr *RawSockaddrAny, addrlen Socklen_t) (errno int) +//sys connect(s int, addr *RawSockaddrAny, addrlen Socklen_t) (err error) //connect(s int, addr *RawSockaddrAny, addrlen Socklen_t) int -func Connect(fd int, sa Sockaddr) (errno int) { +func Connect(fd int, sa Sockaddr) (err error) { ptr, n, err := sa.sockaddr() - if err != 0 { + if err != nil { return err } return connect(fd, ptr, n) } -//sysnb socket(domain int, typ int, proto int) (fd int, errno int) +//sysnb socket(domain int, typ int, proto int) (fd int, err error) //socket(domain int, typ int, protocol int) int -func Socket(domain, typ, proto int) (fd, errno int) { +func Socket(domain, typ, proto int) (fd int, err error) { if domain == AF_INET6 && SocketDisableIPv6 { return -1, EAFNOSUPPORT } - fd, errno = socket(domain, typ, proto) + fd, err = socket(domain, typ, proto) return } -//sysnb socketpair(domain int, typ int, proto int, fd *[2]int) (errno int) +//sysnb socketpair(domain int, typ int, proto int, fd *[2]int) (err error) //socketpair(domain int, typ int, protocol int, fd *[2]int) int -func Socketpair(domain, typ, proto int) (fd [2]int, errno int) { - errno = socketpair(domain, typ, proto, &fd) +func Socketpair(domain, typ, proto int) (fd [2]int, err error) { + err = socketpair(domain, typ, proto, &fd) return } -//sys getsockopt(s int, level int, name int, val uintptr, vallen *Socklen_t) (errno int) +//sys getsockopt(s int, level int, name int, val uintptr, vallen *Socklen_t) (err error) //getsockopt(s int, level int, name int, val *byte, vallen *Socklen_t) int -func GetsockoptInt(fd, level, opt int) (value, errno int) { +func GetsockoptInt(fd, level, opt int) (value int, err error) { var n int32 vallen := Socklen_t(4) - errno = getsockopt(fd, level, opt, (uintptr)(unsafe.Pointer(&n)), &vallen) - return int(n), errno + err = getsockopt(fd, level, opt, (uintptr)(unsafe.Pointer(&n)), &vallen) + return int(n), err } -func GetsockoptInet4Addr(fd, level, opt int) (value [4]byte, errno int) { +func GetsockoptInet4Addr(fd, level, opt int) (value [4]byte, err error) { vallen := Socklen_t(4) - errno = getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value[0])), &vallen) - return value, errno + err = getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value[0])), &vallen) + return value, err } -func GetsockoptIPMreq(fd, level, opt int) (*IPMreq, int) { +func GetsockoptIPMreq(fd, level, opt int) (*IPMreq, error) { var value IPMreq vallen := Socklen_t(SizeofIPMreq) - errno := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen) - return &value, errno + err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen) + return &value, err } /* FIXME: mksysinfo needs to support IPMreqn. -func GetsockoptIPMreqn(fd, level, opt int) (*IPMreqn, int) { +func GetsockoptIPMreqn(fd, level, opt int) (*IPMreqn, error) { var value IPMreqn vallen := Socklen_t(SizeofIPMreqn) - errno := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen) - return &value, errno + err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen) + return &value, err } */ /* FIXME: mksysinfo needs to support IPv6Mreq. -func GetsockoptIPv6Mreq(fd, level, opt int) (*IPv6Mreq, int) { +func GetsockoptIPv6Mreq(fd, level, opt int) (*IPv6Mreq, error) { var value IPv6Mreq vallen := Socklen_t(SizeofIPv6Mreq) - errno := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen) - return &value, errno + err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen) + return &value, err } */ -//sys setsockopt(s int, level int, name int, val *byte, vallen Socklen_t) (errno int) +//sys setsockopt(s int, level int, name int, val *byte, vallen Socklen_t) (err error) //setsockopt(s int, level int, optname int, val *byte, vallen Socklen_t) int -func SetsockoptInt(fd, level, opt int, value int) (errno int) { +func SetsockoptInt(fd, level, opt int, value int) (err error) { var n = int32(value) return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(&n)), 4) } -func SetsockoptInet4Addr(fd, level, opt int, value [4]byte) (errno int) { +func SetsockoptInet4Addr(fd, level, opt int, value [4]byte) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(&value[0])), 4) } -func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (errno int) { +func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(tv)), Socklen_t(unsafe.Sizeof(*tv))) } @@ -280,58 +280,58 @@ type Linger struct { Linger int32; } -func SetsockoptLinger(fd, level, opt int, l *Linger) (errno int) { +func SetsockoptLinger(fd, level, opt int, l *Linger) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(l)), Socklen_t(unsafe.Sizeof(*l))); } -func SetsockoptIPMreq(fd, level, opt int, mreq *IPMreq) (errno int) { +func SetsockoptIPMreq(fd, level, opt int, mreq *IPMreq) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(mreq)), Socklen_t(unsafe.Sizeof(*mreq))) } /* FIXME: mksysinfo needs to support IMPreqn. -func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (errno int) { +func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(mreq)), Socklen_t(unsafe.Sizeof(*mreq))) } */ -func SetsockoptIPv6Mreq(fd, level, opt int, mreq *IPv6Mreq) (errno int) { +func SetsockoptIPv6Mreq(fd, level, opt int, mreq *IPv6Mreq) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(mreq)), Socklen_t(unsafe.Sizeof(*mreq))) } -func SetsockoptString(fd, level, opt int, s string) (errno int) { +func SetsockoptString(fd, level, opt int, s string) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(&[]byte(s)[0])), Socklen_t(len(s))) } -//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *Socklen_t) (n int, errno int) +//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *Socklen_t) (n int, err error) //recvfrom(fd int, buf *byte, len Size_t, flags int, from *RawSockaddrAny, fromlen *Socklen_t) Ssize_t -func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, errno int) { +func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) { var rsa RawSockaddrAny var len Socklen_t = SizeofSockaddrAny - if n, errno = recvfrom(fd, p, flags, &rsa, &len); errno != 0 { + if n, err = recvfrom(fd, p, flags, &rsa, &len); err != nil { return } - from, errno = anyToSockaddr(&rsa) + from, err = anyToSockaddr(&rsa) return } -//sys sendto(s int, buf []byte, flags int, to *RawSockaddrAny, tolen Socklen_t) (errno int) +//sys sendto(s int, buf []byte, flags int, to *RawSockaddrAny, tolen Socklen_t) (err error) //sendto(s int, buf *byte, len Size_t, flags int, to *RawSockaddrAny, tolen Socklen_t) Ssize_t -func Sendto(fd int, p []byte, flags int, to Sockaddr) (errno int) { +func Sendto(fd int, p []byte, flags int, to Sockaddr) (err error) { ptr, n, err := to.sockaddr() - if err != 0 { + if err != nil { return err } return sendto(fd, p, flags, ptr, n) } -//sys recvmsg(s int, msg *Msghdr, flags int) (n int, errno int) +//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error) //recvmsg(s int, msg *Msghdr, flags int) Ssize_t -func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, errno int) { +func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { var msg Msghdr var rsa RawSockaddrAny msg.Name = (*byte)(unsafe.Pointer(&rsa)) @@ -353,28 +353,28 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from } msg.Iov = &iov msg.Iovlen = 1 - if n, errno = recvmsg(fd, &msg, flags); errno != 0 { + if n, err = recvmsg(fd, &msg, flags); err != nil { return } oobn = int(msg.Controllen) recvflags = int(msg.Flags) // source address is only specified if the socket is unconnected if rsa.Addr.Family != AF_UNSPEC { - from, errno = anyToSockaddr(&rsa) + from, err = anyToSockaddr(&rsa) } return } -//sys sendmsg(s int, msg *Msghdr, flags int) (errno int) +//sys sendmsg(s int, msg *Msghdr, flags int) (err error) //sendmsg(s int, msg *Msghdr, flags int) Ssize_t -func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (errno int) { +func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { var ptr *RawSockaddrAny var salen Socklen_t if to != nil { - var err int + var err error ptr, salen, err = to.sockaddr() - if err != 0 { + if err != nil { return err } } @@ -398,16 +398,16 @@ func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (errno int) { } msg.Iov = &iov msg.Iovlen = 1 - if errno = sendmsg(fd, &msg, flags); errno != 0 { + if err = sendmsg(fd, &msg, flags); err != nil { return } return } -//sys Listen(fd int, n int) (errno int) +//sys Listen(fd int, n int) (err error) //listen(fd int, n int) int -//sys Shutdown(fd int, how int) (errno int) +//sys Shutdown(fd int, how int) (err error) //shutdown(fd int, how int) int func (iov *Iovec) SetLen(length int) { diff --git a/libgo/go/syscall/socket_bsd.go b/libgo/go/syscall/socket_bsd.go index 735baf98684..be559915951 100644 --- a/libgo/go/syscall/socket_bsd.go +++ b/libgo/go/syscall/socket_bsd.go @@ -47,7 +47,7 @@ func (sa *RawSockaddrUnix) setLen(n int) { sa.Len = uint8(3 + n) // 2 for Family, Len; 1 for NUL. } -func (sa *RawSockaddrUnix) getLen() (int, int) { +func (sa *RawSockaddrUnix) getLen() (int, error) { if sa.Len < 3 || sa.Len > SizeofSockaddrUnix { return 0, EINVAL } @@ -59,7 +59,7 @@ func (sa *RawSockaddrUnix) getLen() (int, int) { break } } - return n, 0 + return n, nil } type RawSockaddr struct { @@ -69,10 +69,10 @@ type RawSockaddr struct { } // BindToDevice binds the socket associated with fd to device. -func BindToDevice(fd int, device string) (errno int) { +func BindToDevice(fd int, device string) (err error) { return ENOSYS } -func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { - return nil, EAFNOSUPPORT; +func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, error) { + return nil, EAFNOSUPPORT } diff --git a/libgo/go/syscall/socket_irix.go b/libgo/go/syscall/socket_irix.go index 8bd55b8b4d9..c1fdc656ab0 100644 --- a/libgo/go/syscall/socket_irix.go +++ b/libgo/go/syscall/socket_irix.go @@ -41,7 +41,7 @@ type RawSockaddrUnix struct { func (sa *RawSockaddrUnix) setLen(int) { } -func (sa *RawSockaddrUnix) getLen() (int, int) { +func (sa *RawSockaddrUnix) getLen() (int, error) { if sa.Path[0] == 0 { // "Abstract" Unix domain socket. // Rewrite leading NUL as @ for textual display. @@ -61,7 +61,7 @@ func (sa *RawSockaddrUnix) getLen() (int, int) { n++ } - return n, 0 + return n, nil } type RawSockaddr struct { @@ -70,7 +70,7 @@ type RawSockaddr struct { } // BindToDevice binds the socket associated with fd to device. -func BindToDevice(fd int, device string) (errno int) { +func BindToDevice(fd int, device string) (err error) { return ENOSYS } @@ -124,6 +124,6 @@ const ( EAI_MAX = 14 ) -func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { - return nil, EAFNOSUPPORT; +func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, error) { + return nil, EAFNOSUPPORT } diff --git a/libgo/go/syscall/socket_linux.go b/libgo/go/syscall/socket_linux.go index 49aac87bb7a..212e0b2d418 100644 --- a/libgo/go/syscall/socket_linux.go +++ b/libgo/go/syscall/socket_linux.go @@ -24,7 +24,7 @@ type SockaddrLinklayer struct { raw RawSockaddrLinklayer } -func (sa *SockaddrLinklayer) sockaddr() (*RawSockaddrAny, Socklen_t, int) { +func (sa *SockaddrLinklayer) sockaddr() (*RawSockaddrAny, Socklen_t, error) { if sa.Ifindex < 0 || sa.Ifindex > 0x7fffffff { return nil, 0, EINVAL } @@ -37,7 +37,7 @@ func (sa *SockaddrLinklayer) sockaddr() (*RawSockaddrAny, Socklen_t, int) { for i := 0; i < len(sa.Addr); i++ { sa.raw.Addr[i] = sa.Addr[i] } - return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), SizeofSockaddrLinklayer, 0 + return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), SizeofSockaddrLinklayer, nil } type SockaddrNetlink struct { @@ -48,12 +48,12 @@ type SockaddrNetlink struct { raw RawSockaddrNetlink } -func (sa *SockaddrNetlink) sockaddr() (*RawSockaddrAny, Socklen_t, int) { +func (sa *SockaddrNetlink) sockaddr() (*RawSockaddrAny, Socklen_t, error) { sa.raw.Family = AF_NETLINK sa.raw.Pad = sa.Pad sa.raw.Pid = sa.Pid sa.raw.Groups = sa.Groups - return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), SizeofSockaddrNetlink, 0 + return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), SizeofSockaddrNetlink, nil } type RawSockaddrInet4 struct { @@ -87,7 +87,7 @@ type RawSockaddrUnix struct { func (sa *RawSockaddrUnix) setLen(int) { } -func (sa *RawSockaddrUnix) getLen() (int, int) { +func (sa *RawSockaddrUnix) getLen() (int, error) { if sa.Path[0] == 0 { // "Abstract" Unix domain socket. // Rewrite leading NUL as @ for textual display. @@ -107,7 +107,7 @@ func (sa *RawSockaddrUnix) getLen() (int, int) { n++ } - return n, 0 + return n, nil } type RawSockaddrLinklayer struct { @@ -133,11 +133,11 @@ type RawSockaddr struct { } // BindToDevice binds the socket associated with fd to device. -func BindToDevice(fd int, device string) (errno int) { +func BindToDevice(fd int, device string) (err error) { return SetsockoptString(fd, SOL_SOCKET, SO_BINDTODEVICE, device) } -func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { +func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, error) { switch rsa.Addr.Family { case AF_NETLINK: pp := (*RawSockaddrNetlink)(unsafe.Pointer(rsa)) @@ -146,7 +146,7 @@ func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { sa.Pad = pp.Pad sa.Pid = pp.Pid sa.Groups = pp.Groups - return sa, 0 + return sa, nil case AF_PACKET: pp := (*RawSockaddrLinklayer)(unsafe.Pointer(rsa)) @@ -159,16 +159,16 @@ func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { for i := 0; i < len(sa.Addr); i++ { sa.Addr[i] = pp.Addr[i] } - return sa, 0 + return sa, nil } return nil, EAFNOSUPPORT } -//sysnb EpollCreate(size int) (fd int, errno int) +//sysnb EpollCreate(size int) (fd int, err error) //epoll_create(size int) int -//sysnb EpollCtl(epfd int, op int, fd int, event *EpollEvent) (errno int) +//sysnb EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) //epoll_ctl(epfd int, op int, fd int, event *EpollEvent) int -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, errno int) +//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //epoll_wait(epfd int, events *EpollEvent, maxevents int, timeout int) int diff --git a/libgo/go/syscall/socket_solaris.go b/libgo/go/syscall/socket_solaris.go index 0c3e6c9724e..0a03465a338 100644 --- a/libgo/go/syscall/socket_solaris.go +++ b/libgo/go/syscall/socket_solaris.go @@ -42,7 +42,7 @@ type RawSockaddrUnix struct { func (sa *RawSockaddrUnix) setLen(int) { } -func (sa *RawSockaddrUnix) getLen() (int, int) { +func (sa *RawSockaddrUnix) getLen() (int, error) { if sa.Path[0] == 0 { // "Abstract" Unix domain socket. // Rewrite leading NUL as @ for textual display. @@ -62,7 +62,7 @@ func (sa *RawSockaddrUnix) getLen() (int, int) { n++ } - return n, 0 + return n, nil } type RawSockaddr struct { @@ -71,10 +71,10 @@ type RawSockaddr struct { } // BindToDevice binds the socket associated with fd to device. -func BindToDevice(fd int, device string) (errno int) { +func BindToDevice(fd int, device string) (err error) { return ENOSYS } -func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { - return nil, EAFNOSUPPORT; +func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, error) { + return nil, EAFNOSUPPORT } diff --git a/libgo/go/syscall/syscall.go b/libgo/go/syscall/syscall.go index a802ba0bbf2..ad9df6cff98 100644 --- a/libgo/go/syscall/syscall.go +++ b/libgo/go/syscall/syscall.go @@ -9,8 +9,9 @@ // packages rather than this one if you can. // For details of the functions and data types in this package consult // the manuals for the appropriate operating system. -// These calls return errno == 0 to indicate success; otherwise -// errno is an operating system error number describing the failure. +// These calls return err == nil to indicate success; otherwise +// err is an operating system error describing the failure. +// On most systems, that error has type syscall.Errno. package syscall import "unsafe" diff --git a/libgo/go/syscall/syscall_unix.go b/libgo/go/syscall/syscall_unix.go index c734b2cf06a..899a65ca9b4 100644 --- a/libgo/go/syscall/syscall_unix.go +++ b/libgo/go/syscall/syscall_unix.go @@ -23,7 +23,7 @@ func c_syscall64(trap int64, a1, a2, a3, a4, a5, a6 int64) int64 __asm__ ("sysca // Do a system call. We look at the size of uintptr to see how to pass // the arguments, so that we don't pass a 64-bit value when the function // expects a 32-bit one. -func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) { +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { entersyscall() var r uintptr if unsafe.Sizeof(r) == 4 { @@ -33,12 +33,12 @@ func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) { r1 := c_syscall64(int64(trap), int64(a1), int64(a2), int64(a3), 0, 0, 0) r = uintptr(r1) } - errno := GetErrno() + err = GetErrno() exitsyscall() - return r, 0, uintptr(errno) + return r, 0, err } -func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { +func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { entersyscall() var r uintptr if unsafe.Sizeof(r) == 4 { @@ -50,12 +50,12 @@ func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { int64(a4), int64(a5), int64(a6)) r = uintptr(r1) } - errno := GetErrno() + err = GetErrno() exitsyscall() - return r, 0, uintptr(errno) + return r, 0, err } -func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) { +func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { var r uintptr if unsafe.Sizeof(r) == 4 { r1 := c_syscall32(int32(trap), int32(a1), int32(a2), int32(a3), 0, 0, 0) @@ -64,11 +64,11 @@ func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) { r1 := c_syscall64(int64(trap), int64(a1), int64(a2), int64(a3), 0, 0, 0) r = uintptr(r1) } - errno := GetErrno() - return r, 0, uintptr(errno) + err = GetErrno() + return r, 0, err } -func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { +func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { var r uintptr if unsafe.Sizeof(r) == 4 { r1 := c_syscall32(int32(trap), int32(a1), int32(a2), int32(a3), @@ -79,8 +79,8 @@ func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { int64(a4), int64(a5), int64(a6)) r = uintptr(r1) } - errno := GetErrno() - return r, 0, uintptr(errno) + err = GetErrno() + return r, 0, err } // Mmap manager, for use by operating system-specific implementations. @@ -89,18 +89,18 @@ func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { type mmapper struct { sync.Mutex active map[*byte][]byte // active mappings; key is last byte in mapping - mmap func(addr, length uintptr, prot, flags, fd int, offset int64) (uintptr, int) - munmap func(addr uintptr, length uintptr) int + mmap func(addr, length uintptr, prot, flags, fd int, offset int64) (uintptr, error) + munmap func(addr uintptr, length uintptr) error } -func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, errno int) { +func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { if length <= 0 { return nil, EINVAL } // Map the requested memory. addr, errno := m.mmap(0, uintptr(length), prot, flags, fd, offset) - if errno != 0 { + if errno != nil { return nil, errno } @@ -119,10 +119,10 @@ func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (d m.Lock() defer m.Unlock() m.active[p] = b - return b, 0 + return b, nil } -func (m *mmapper) Munmap(data []byte) (errno int) { +func (m *mmapper) Munmap(data []byte) (err error) { if len(data) == 0 || len(data) != cap(data) { return EINVAL } @@ -137,11 +137,11 @@ func (m *mmapper) Munmap(data []byte) (errno int) { } // Unmap the memory and update m. - if errno := m.munmap(uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))); errno != 0 { + if errno := m.munmap(uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))); errno != nil { return errno } m.active[p] = nil, false - return 0 + return nil } var mapper = &mmapper{ @@ -150,10 +150,32 @@ var mapper = &mmapper{ munmap: munmap, } -func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, errno int) { +func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { return mapper.Mmap(fd, offset, length, prot, flags) } -func Munmap(b []byte) (errno int) { +func Munmap(b []byte) (err error) { return mapper.Munmap(b) } + + +// An Errno is an unsigned number describing an error condition. +// It implements the error interface. The zero Errno is by convention +// a non-error, so code to convert from Errno to error should use: +// err = nil +// if errno != 0 { +// err = errno +// } +type Errno uintptr + +func (e Errno) Error() string { + return Errstr(int(e)) +} + +func (e Errno) Temporary() bool { + return e == EINTR || e == EMFILE || e.Timeout() +} + +func (e Errno) Timeout() bool { + return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT +} diff --git a/libgo/go/testing/benchmark.go b/libgo/go/testing/benchmark.go index df4c4a1a29b..e81e5c5845c 100644 --- a/libgo/go/testing/benchmark.go +++ b/libgo/go/testing/benchmark.go @@ -27,17 +27,19 @@ type InternalBenchmark struct { type B struct { N int benchmark InternalBenchmark - ns int64 + ns time.Duration bytes int64 - start int64 + start time.Time + timerOn bool } // StartTimer starts timing a test. This function is called automatically // before a benchmark starts, but it can also used to resume timing after // a call to StopTimer. func (b *B) StartTimer() { - if b.start == 0 { - b.start = time.Nanoseconds() + if !b.timerOn { + b.start = time.Now() + b.timerOn = true } } @@ -45,17 +47,17 @@ func (b *B) StartTimer() { // while performing complex initialization that you don't // want to measure. func (b *B) StopTimer() { - if b.start > 0 { - b.ns += time.Nanoseconds() - b.start + if b.timerOn { + b.ns += time.Now().Sub(b.start) + b.timerOn = false } - b.start = 0 } // ResetTimer sets the elapsed benchmark time to zero. // It does not affect whether the timer is running. func (b *B) ResetTimer() { - if b.start > 0 { - b.start = time.Nanoseconds() + if b.timerOn { + b.start = time.Now() } b.ns = 0 } @@ -68,7 +70,7 @@ func (b *B) nsPerOp() int64 { if b.N <= 0 { return 0 } - return b.ns / int64(b.N) + return b.ns.Nanoseconds() / int64(b.N) } // runN runs a single benchmark for the specified number of iterations. @@ -134,14 +136,14 @@ func (b *B) run() BenchmarkResult { n := 1 b.runN(n) // Run the benchmark for at least the specified amount of time. - time := int64(*benchTime * 1e9) - for b.ns < time && n < 1e9 { + d := time.Duration(*benchTime * float64(time.Second)) + for b.ns < d && n < 1e9 { last := n // Predict iterations/sec. if b.nsPerOp() == 0 { n = 1e9 } else { - n = int(time / b.nsPerOp()) + n = int(d.Nanoseconds() / b.nsPerOp()) } // Run more iterations than we think we'll need for a second (1.5x). // Don't grow too fast in case we had timing errors previously. @@ -156,23 +158,23 @@ func (b *B) run() BenchmarkResult { // The results of a benchmark run. type BenchmarkResult struct { - N int // The number of iterations. - Ns int64 // The total time taken. - Bytes int64 // Bytes processed in one iteration. + N int // The number of iterations. + T time.Duration // The total time taken. + Bytes int64 // Bytes processed in one iteration. } func (r BenchmarkResult) NsPerOp() int64 { if r.N <= 0 { return 0 } - return r.Ns / int64(r.N) + return r.T.Nanoseconds() / int64(r.N) } func (r BenchmarkResult) mbPerSec() float64 { - if r.Bytes <= 0 || r.Ns <= 0 || r.N <= 0 { + if r.Bytes <= 0 || r.T <= 0 || r.N <= 0 { return 0 } - return float64(r.Bytes) * float64(r.N) / float64(r.Ns) * 1e3 + return (float64(r.Bytes) * float64(r.N) / 1e6) / r.T.Seconds() } func (r BenchmarkResult) String() string { @@ -187,9 +189,9 @@ func (r BenchmarkResult) String() string { // The format specifiers here make sure that // the ones digits line up for all three possible formats. if nsop < 10 { - ns = fmt.Sprintf("%13.2f ns/op", float64(r.Ns)/float64(r.N)) + ns = fmt.Sprintf("%13.2f ns/op", float64(r.T.Nanoseconds())/float64(r.N)) } else { - ns = fmt.Sprintf("%12.1f ns/op", float64(r.Ns)/float64(r.N)) + ns = fmt.Sprintf("%12.1f ns/op", float64(r.T.Nanoseconds())/float64(r.N)) } } return fmt.Sprintf("%8d\t%s%s", r.N, ns, mb) @@ -205,7 +207,7 @@ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [ for _, Benchmark := range benchmarks { matched, err := matchString(*matchBenchmarks, Benchmark.Name) if err != nil { - println("invalid regexp for -test.bench:", err.Error()) + fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.bench: %s\n", err) os.Exit(1) } if !matched { @@ -218,11 +220,11 @@ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [ if procs != 1 { benchName = fmt.Sprintf("%s-%d", Benchmark.Name, procs) } - print(fmt.Sprintf("%s\t", benchName)) + fmt.Printf("%s\t", benchName) r := b.run() - print(fmt.Sprintf("%v\n", r)) + fmt.Printf("%v\n", r) if p := runtime.GOMAXPROCS(-1); p != procs { - print(fmt.Sprintf("%s left GOMAXPROCS set to %d\n", benchName, p)) + fmt.Fprintf(os.Stderr, "testing: %s left GOMAXPROCS set to %d\n", benchName, p) } } } diff --git a/libgo/go/testing/example.go b/libgo/go/testing/example.go index 5b3e322b593..e23f13b6f16 100644 --- a/libgo/go/testing/example.go +++ b/libgo/go/testing/example.go @@ -21,24 +21,23 @@ type InternalExample struct { func RunExamples(examples []InternalExample) (ok bool) { ok = true + var eg InternalExample + stdout, stderr := os.Stdout, os.Stderr defer func() { os.Stdout, os.Stderr = stdout, stderr if e := recover(); e != nil { - if err, ok := e.(error); ok { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - panic(e) + fmt.Printf("--- FAIL: %s\npanic: %v\n", eg.Name, e) + os.Exit(1) } }() - for _, eg := range examples { + for _, eg = range examples { if *chatty { - fmt.Fprintln(os.Stderr, "=== RUN:", eg.Name) + fmt.Printf("=== RUN: %s\n", eg.Name) } - // capture stdout and stderr for testing purposes + // capture stdout and stderr r, w, err := os.Pipe() if err != nil { fmt.Fprintln(os.Stderr, err) @@ -50,16 +49,16 @@ func RunExamples(examples []InternalExample) (ok bool) { buf := new(bytes.Buffer) _, err := io.Copy(buf, r) if err != nil { - fmt.Fprintln(os.Stderr, err) + fmt.Fprintf(stderr, "testing: copying pipe: %v\n", err) os.Exit(1) } outC <- buf.String() }() // run example - ns := -time.Nanoseconds() + t0 := time.Now() eg.F() - ns += time.Nanoseconds() + dt := time.Now().Sub(t0) // close pipe, restore stdout/stderr, get output w.Close() @@ -67,16 +66,15 @@ func RunExamples(examples []InternalExample) (ok bool) { out := <-outC // report any errors + tstr := fmt.Sprintf("(%.2f seconds)", dt.Seconds()) if out != eg.Output { - fmt.Fprintf( - os.Stderr, - "--- FAIL: %s\ngot:\n%s\nwant:\n%s\n", - eg.Name, out, eg.Output, + fmt.Printf( + "--- FAIL: %s %s\ngot:\n%s\nwant:\n%s\n", + eg.Name, tstr, out, eg.Output, ) ok = false } else if *chatty { - tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9) - fmt.Fprintln(os.Stderr, "--- PASS:", eg.Name, tstr) + fmt.Printf("--- PASS: %s %s\n", eg.Name, tstr) } } diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go index 5869642c7e1..0b3a07108cc 100644 --- a/libgo/go/testing/testing.go +++ b/libgo/go/testing/testing.go @@ -75,8 +75,25 @@ func Short() bool { return *short } -// Insert final newline if needed and tabs after internal newlines. -func tabify(s string) string { +// decorate inserts the a final newline if needed and indentation tabs for formatting. +// If addFileLine is true, it also prefixes the string with the file and line of the call site. +func decorate(s string, addFileLine bool) string { + if addFileLine { + _, file, line, ok := runtime.Caller(3) // decorate + log + public function. + if ok { + // Truncate file name at last file name separator. + if index := strings.LastIndex(file, "/"); index >= 0 { + file = file[index+1:] + } else if index = strings.LastIndex(file, "\\"); index >= 0 { + file = file[index+1:] + } + } else { + file = "???" + line = 1 + } + s = fmt.Sprintf("%s:%d: %s", file, line, s) + } + s = "\t" + s // Every line is indented at least one tab. n := len(s) if n > 0 && s[n-1] != '\n' { s += "\n" @@ -84,7 +101,8 @@ func tabify(s string) string { } for i := 0; i < n-1; i++ { // -1 to avoid final newline if s[i] == '\n' { - return s[0:i+1] + "\t" + tabify(s[i+1:n]) + // Second and subsequent lines are indented an extra tab. + return s[0:i+1] + "\t" + decorate(s[i+1:n], false) } } return s @@ -93,12 +111,13 @@ func tabify(s string) string { // T is a type passed to Test functions to manage test state and support formatted test logs. // Logs are accumulated during execution and dumped to standard error when done. type T struct { - name string // Name of test. - errors string // Error string from test. - failed bool // Test has failed. - ch chan *T // Output for serial tests. - startParallel chan bool // Parallel tests will wait on this. - ns int64 // Duration of test in nanoseconds. + name string // Name of test. + errors string // Error string from test. + failed bool // Test has failed. + ch chan *T // Output for serial tests. + startParallel chan bool // Parallel tests will wait on this. + start time.Time // Time test started + dt time.Duration // Length of test } // Fail marks the Test function as having failed but continues execution. @@ -110,43 +129,44 @@ func (t *T) Failed() bool { return t.failed } // FailNow marks the Test function as having failed and stops its execution. // Execution will continue at the next Test. func (t *T) FailNow() { - t.ns = time.Nanoseconds() - t.ns + t.dt = time.Now().Sub(t.start) t.Fail() t.ch <- t runtime.Goexit() } +// log generates the output. It's always at the same stack depth. +func (t *T) log(s string) { t.errors += decorate(s, true) } + // Log formats its arguments using default formatting, analogous to Print(), // and records the text in the error log. -func (t *T) Log(args ...interface{}) { t.errors += "\t" + tabify(fmt.Sprintln(args...)) } +func (t *T) Log(args ...interface{}) { t.log(fmt.Sprintln(args...)) } // Logf formats its arguments according to the format, analogous to Printf(), // and records the text in the error log. -func (t *T) Logf(format string, args ...interface{}) { - t.errors += "\t" + tabify(fmt.Sprintf(format, args...)) -} +func (t *T) Logf(format string, args ...interface{}) { t.log(fmt.Sprintf(format, args...)) } // Error is equivalent to Log() followed by Fail(). func (t *T) Error(args ...interface{}) { - t.Log(args...) + t.log(fmt.Sprintln(args...)) t.Fail() } // Errorf is equivalent to Logf() followed by Fail(). func (t *T) Errorf(format string, args ...interface{}) { - t.Logf(format, args...) + t.log(fmt.Sprintf(format, args...)) t.Fail() } // Fatal is equivalent to Log() followed by FailNow(). func (t *T) Fatal(args ...interface{}) { - t.Log(args...) + t.log(fmt.Sprintln(args...)) t.FailNow() } // Fatalf is equivalent to Logf() followed by FailNow(). func (t *T) Fatalf(format string, args ...interface{}) { - t.Logf(format, args...) + t.log(fmt.Sprintf(format, args...)) t.FailNow() } @@ -165,9 +185,9 @@ type InternalTest struct { } func tRunner(t *T, test *InternalTest) { - t.ns = time.Nanoseconds() + t.start = time.Now() test.F(t) - t.ns = time.Nanoseconds() - t.ns + t.dt = time.Now().Sub(t.start) t.ch <- t } @@ -182,22 +202,22 @@ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, testOk := RunTests(matchString, tests) exampleOk := RunExamples(examples) if !testOk || !exampleOk { - fmt.Fprintln(os.Stderr, "FAIL") + fmt.Println("FAIL") os.Exit(1) } - fmt.Fprintln(os.Stderr, "PASS") + fmt.Println("PASS") stopAlarm() RunBenchmarks(matchString, benchmarks) after() } func report(t *T) { - tstr := fmt.Sprintf("(%.2f seconds)", float64(t.ns)/1e9) + tstr := fmt.Sprintf("(%.2f seconds)", t.dt.Seconds()) format := "--- %s: %s %s\n%s" if t.failed { - fmt.Fprintf(os.Stderr, format, "FAIL", t.name, tstr, t.errors) + fmt.Printf(format, "FAIL", t.name, tstr, t.errors) } else if *chatty { - fmt.Fprintf(os.Stderr, format, "PASS", t.name, tstr, t.errors) + fmt.Printf(format, "PASS", t.name, tstr, t.errors) } } @@ -217,7 +237,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT for i := 0; i < len(tests); i++ { matched, err := matchString(*match, tests[i].Name) if err != nil { - println("invalid regexp for -test.run:", err.Error()) + fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.run: %s\n", err) os.Exit(1) } if !matched { @@ -229,7 +249,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT } t := &T{ch: ch, name: testName, startParallel: startParallel} if *chatty { - println("=== RUN", t.name) + fmt.Printf("=== RUN %s\n", t.name) } go tRunner(t, &tests[i]) out := <-t.ch @@ -325,7 +345,7 @@ func parseCpuList() { for _, val := range strings.Split(*cpuListStr, ",") { cpu, err := strconv.Atoi(val) if err != nil || cpu <= 0 { - println("invalid value for -test.cpu") + fmt.Fprintf(os.Stderr, "testing: invalid value %q for -test.cpu", val) os.Exit(1) } cpuList = append(cpuList, cpu) diff --git a/libgo/go/text/tabwriter/tabwriter.go b/libgo/go/text/tabwriter/tabwriter.go index c136ca2a175..201a685c630 100644 --- a/libgo/go/text/tabwriter/tabwriter.go +++ b/libgo/go/text/tabwriter/tabwriter.go @@ -13,7 +13,6 @@ package tabwriter import ( "bytes" "io" - "os" "unicode/utf8" ) @@ -221,7 +220,7 @@ type osError struct { func (b *Writer) write0(buf []byte) { n, err := b.output.Write(buf) if n != len(buf) && err == nil { - err = os.EIO + err = io.ErrShortWrite } if err != nil { panic(osError{err}) diff --git a/libgo/go/text/template/doc.go b/libgo/go/text/template/doc.go index 42f9e560be1..4208d53a0a4 100644 --- a/libgo/go/text/template/doc.go +++ b/libgo/go/text/template/doc.go @@ -18,8 +18,7 @@ The input text for a template is UTF-8-encoded text in any format. "{{" and "}}"; all text outside actions is copied to the output unchanged. Actions may not span newlines, although comments can. -Once constructed, templates and template sets can be executed safely in -parallel. +Once constructed, a template may be executed safely in parallel. Actions @@ -221,10 +220,9 @@ All produce the quoted word "output": Functions -During execution functions are found in three function maps: first in the -template, then in the "template set" (described below), and finally in the -global function map. By default, no functions are defined in the template or -the set but the Funcs methods can be used to add them. +During execution functions are found in two function maps: first in the +template, then in the global function map. By default, no functions are defined +in the template but the Funcs methods can be used to add them. Predefined global functions are named as follows. @@ -265,49 +263,63 @@ Predefined global functions are named as follows. The boolean functions take any zero value to be false and a non-zero value to be true. -Template sets +Associated templates -Each template is named by a string specified when it is created. A template may -use a template invocation to instantiate another template directly or by its -name; see the explanation of the template action above. The name is looked up -in the template set associated with the template. +Each template is named by a string specified when it is created. Also, each +template is associated with zero or more other templates that it may invoke by +name; such associations are transitive and form a name space of templates. -If no template invocation actions occur in the template, the issue of template -sets can be ignored. If it does contain invocations, though, the template -containing the invocations must be part of a template set in which to look up -the names. +A template may use a template invocation to instantiate another associated +template; see the explanation of the "template" action above. The name must be +that of a template associated with the template that contains the invocation. -There are two ways to construct template sets. +Nested template definitions -The first is to use a Set's Parse method to create a set of named templates from -a single input defining multiple templates. The syntax of the definitions is to -surround each template declaration with a define and end action. +When parsing a template, another template may be defined and associated with the +template being parsed. Template definitions must appear at the top level of the +template, much like global variables in a Go program. + +The syntax of such definitions is to surround each template declaration with a +"define" and "end" action. The define action names the template being created by providing a string -constant. Here is a simple example of input to Set.Parse: +constant. Here is a simple example: - `{{define "T1"}} definition of template T1 {{end}} - {{define "T2"}} definition of template T2 {{end}} - {{define "T3"}} {{template "T1"}} {{template "T2"}} {{end}}` + `{{define "T1"}}ONE{{end}} + {{define "T2"}}TWO{{end}} + {{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}} + {{template "T3"}}` This defines two templates, T1 and T2, and a third T3 that invokes the other two -when it is executed. +when it is executed. Finally it invokes T3. If executed this template will +produce the text + + ONE TWO + +By construction, a template may reside in only one association. If it's +necessary to have a template addressable from multiple associations, the +template definition must be parsed multiple times to create distinct *Template +values. + +Parse may be called multiple times to assemble the various associated templates; +see the ParseFiles and ParseGlob functions and methods for simple ways to parse +related templates stored in files. -The second way to build a template set is to use Set's Add method to add a -parsed template to a set. A template may be bound to at most one set. If it's -necessary to have a template in multiple sets, the template definition must be -parsed multiple times to create distinct *Template values. +A template may be executed directly or through ExecuteTemplate, which executes +an associated template identified by name. To invoke our example above, we +might write, -Set.Parse may be called multiple times on different inputs to construct the set. -Two sets may therefore be constructed with a common base set of templates plus, -through a second Parse call each, specializations for some elements. + err := tmpl.Execute(os.Stdout, "no data needed") + if err != nil { + log.Fatalf("execution failed: %s", err) + } -A template may be executed directly or through Set.Execute, which executes a -named template from the set. To invoke our example above, we might write, +or to invoke a particular template explicitly by name, - err := set.Execute(os.Stdout, "T3", "no data needed") + err := tmpl.ExecuteTemplate(os.Stdout, "T2", "no data needed") if err != nil { log.Fatalf("execution failed: %s", err) } + */ package template diff --git a/libgo/go/text/template/exec.go b/libgo/go/text/template/exec.go index 19108825d58..b74bc3b01c9 100644 --- a/libgo/go/text/template/exec.go +++ b/libgo/go/text/template/exec.go @@ -85,8 +85,18 @@ func errRecover(errp *error) { } } +// ExecuteTemplate applies the template associated with t that has the given name +// to the specified data object and writes the output to wr. +func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error { + tmpl := t.tmpl[name] + if tmpl == nil { + return fmt.Errorf("template: no template %q associated with template %q", name, t.name) + } + return tmpl.Execute(wr, data) +} + // Execute applies a parsed template to the specified data object, -// writing the output to wr. +// and writes the output to wr. func (t *Template) Execute(wr io.Writer, data interface{}) (err error) { defer errRecover(&err) value := reflect.ValueOf(data) @@ -251,13 +261,9 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { } func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) { - set := s.tmpl.set - if set == nil { - s.errorf("no set defined in which to invoke template named %q", t.Name) - } - tmpl := set.tmpl[t.Name] + tmpl := s.tmpl.tmpl[t.Name] if tmpl == nil { - s.errorf("template %q not in set", t.Name) + s.errorf("template %q not defined", t.Name) } // Variables declared by the pipeline persist. dot = s.evalPipeline(dot, t.Pipe) @@ -376,7 +382,7 @@ func (s *state) evalFieldChain(dot, receiver reflect.Value, ident []string, args } func (s *state) evalFunction(dot reflect.Value, name string, args []parse.Node, final reflect.Value) reflect.Value { - function, ok := findFunction(name, s.tmpl, s.tmpl.set) + function, ok := findFunction(name, s.tmpl) if !ok { s.errorf("%q is not a defined function", name) } @@ -398,7 +404,7 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node if ptr.Kind() != reflect.Interface && ptr.CanAddr() { ptr = ptr.Addr() } - if method, ok := methodByName(ptr, fieldName); ok { + if method := ptr.MethodByName(fieldName); method.IsValid() { return s.evalCall(dot, method, fieldName, args, final) } hasArgs := len(args) > 1 || final.IsValid() @@ -433,17 +439,6 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node panic("not reached") } -// TODO: delete when reflect's own MethodByName is released. -func methodByName(receiver reflect.Value, name string) (reflect.Value, bool) { - typ := receiver.Type() - for i := 0; i < typ.NumMethod(); i++ { - if typ.Method(i).Name == name { - return receiver.Method(i), true // This value includes the receiver. - } - } - return zero, false -} - var ( errorType = reflect.TypeOf((*error)(nil)).Elem() fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() diff --git a/libgo/go/text/template/exec_test.go b/libgo/go/text/template/exec_test.go index 57216676410..cf3c4157281 100644 --- a/libgo/go/text/template/exec_test.go +++ b/libgo/go/text/template/exec_test.go @@ -476,7 +476,7 @@ func vfunc(V, *V) string { return "vfunc" } -func testExecute(execTests []execTest, set *Set, t *testing.T) { +func testExecute(execTests []execTest, template *Template, t *testing.T) { b := new(bytes.Buffer) funcs := FuncMap{ "count": count, @@ -486,8 +486,13 @@ func testExecute(execTests []execTest, set *Set, t *testing.T) { "zeroArgs": zeroArgs, } for _, test := range execTests { - tmpl := New(test.name).Funcs(funcs) - _, err := tmpl.ParseInSet(test.input, set) + var tmpl *Template + var err error + if template == nil { + tmpl, err = New(test.name).Funcs(funcs).Parse(test.input) + } else { + tmpl, err = template.New(test.name).Funcs(funcs).Parse(test.input) + } if err != nil { t.Errorf("%s: parse error: %s", test.name, err) continue @@ -659,24 +664,34 @@ func TestTree(t *testing.T) { }, }, } - set := new(Set) - _, err := set.Delims("(", ")").Parse(treeTemplate) + tmpl, err := New("root").Delims("(", ")").Parse(treeTemplate) if err != nil { t.Fatal("parse error:", err) } var b bytes.Buffer - err = set.Execute(&b, "tree", tree) - if err != nil { - t.Fatal("exec error:", err) - } stripSpace := func(r rune) rune { if r == '\t' || r == '\n' { return -1 } return r } - result := strings.Map(stripSpace, b.String()) const expect = "[1[2[3[4]][5[6]]][7[8[9]][10[11]]]]" + // First by looking up the template. + err = tmpl.Lookup("tree").Execute(&b, tree) + if err != nil { + t.Fatal("exec error:", err) + } + result := strings.Map(stripSpace, b.String()) + if result != expect { + t.Errorf("expected %q got %q", expect, result) + } + // Then direct to execution. + b.Reset() + err = tmpl.ExecuteTemplate(&b, "tree", tree) + if err != nil { + t.Fatal("exec error:", err) + } + result = strings.Map(stripSpace, b.String()) if result != expect { t.Errorf("expected %q got %q", expect, result) } diff --git a/libgo/go/text/template/funcs.go b/libgo/go/text/template/funcs.go index 2ca09a7c17f..d6e4bf1a216 100644 --- a/libgo/go/text/template/funcs.go +++ b/libgo/go/text/template/funcs.go @@ -17,8 +17,9 @@ import ( // FuncMap is the type of the map defining the mapping from names to functions. // Each function must have either a single return value, or two return values of -// which the second has type error. If the second argument evaluates to non-nil -// during execution, execution terminates and Execute returns an error. +// which the second has type error. In that case, if the second (error) +// argument evaluates to non-nil during execution, execution terminates and +// Execute returns that error. type FuncMap map[string]interface{} var builtins = FuncMap{ @@ -78,18 +79,13 @@ func goodFunc(typ reflect.Type) bool { return false } -// findFunction looks for a function in the template, set, and global map. -func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) { - if tmpl != nil { +// findFunction looks for a function in the template, and global map. +func findFunction(name string, tmpl *Template) (reflect.Value, bool) { + if tmpl != nil && tmpl.common != nil { if fn := tmpl.execFuncs[name]; fn.IsValid() { return fn, true } } - if set != nil { - if fn := set.execFuncs[name]; fn.IsValid() { - return fn, true - } - } if fn := builtinFuncs[name]; fn.IsValid() { return fn, true } @@ -310,7 +306,6 @@ func JSEscape(w io.Writer, b []byte) { if unicode.IsPrint(r) { w.Write(b[i : i+size]) } else { - // TODO(dsymonds): Do this without fmt? fmt.Fprintf(w, "\\u%04X", r) } i += size - 1 diff --git a/libgo/go/text/template/helper.go b/libgo/go/text/template/helper.go index a743a8326ec..3636fb54d69 100644 --- a/libgo/go/text/template/helper.go +++ b/libgo/go/text/template/helper.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Helper functions to make constructing templates and sets easier. +// Helper functions to make constructing templates easier. package template @@ -12,11 +12,11 @@ import ( "path/filepath" ) -// Functions and methods to parse a single template. +// Functions and methods to parse templates. // Must is a helper that wraps a call to a function returning (*Template, error) -// and panics if the error is non-nil. It is intended for use in variable initializations -// such as +// and panics if the error is non-nil. It is intended for use in variable +// initializations such as // var t = template.Must(template.New("name").Parse("text")) func Must(t *Template, err error) *Template { if err != nil { @@ -25,217 +25,84 @@ func Must(t *Template, err error) *Template { return t } -// ParseFile creates a new Template and parses the template definition from -// the named file. The template name is the base name of the file. -func ParseFile(filename string) (*Template, error) { - t := New(filepath.Base(filename)) - return t.ParseFile(filename) +// ParseFiles creates a new Template and parses the template definitions from +// the named files. The returned template's name will have the (base) name and +// (parsed) contents of the first file. There must be at least one file. +// If an error occurs, parsing stops and the returned *Template is nil. +func ParseFiles(filenames ...string) (*Template, error) { + return parseFiles(nil, filenames...) } -// parseFileInSet creates a new Template and parses the template -// definition from the named file. The template name is the base name -// of the file. It also adds the template to the set. Function bindings are -// checked against those in the set. -func parseFileInSet(filename string, set *Set) (*Template, error) { - t := New(filepath.Base(filename)) - return t.parseFileInSet(filename, set) +// ParseFiles parses the named files and associates the resulting templates with +// t. If an error occurs, parsing stops and the returned template is nil; +// otherwise it is t. There must be at least one file. +func (t *Template) ParseFiles(filenames ...string) (*Template, error) { + return parseFiles(t, filenames...) } -// ParseFile reads the template definition from a file and parses it to -// construct an internal representation of the template for execution. -// The returned template will be nil if an error occurs. -func (t *Template) ParseFile(filename string) (*Template, error) { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - return t.Parse(string(b)) -} - -// parseFileInSet is the same as ParseFile except that function bindings -// are checked against those in the set and the template is added -// to the set. -// The returned template will be nil if an error occurs. -func (t *Template) parseFileInSet(filename string, set *Set) (*Template, error) { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - return t.ParseInSet(string(b), set) -} - -// Functions and methods to parse a set. - -// SetMust is a helper that wraps a call to a function returning (*Set, error) -// and panics if the error is non-nil. It is intended for use in variable initializations -// such as -// var s = template.SetMust(template.ParseSetFiles("file")) -func SetMust(s *Set, err error) *Set { - if err != nil { - panic(err) +// parseFiles is the helper for the method and function. If the argument +// template is nil, it is created from the first file. +func parseFiles(t *Template, filenames ...string) (*Template, error) { + if len(filenames) == 0 { + // Not really a problem, but be consistent. + return nil, fmt.Errorf("template: no files named in call to ParseFiles") } - return s -} - -// ParseFiles parses the named files into a set of named templates. -// Each file must be parseable by itself. -// If an error occurs, parsing stops and the returned set is nil. -func (s *Set) ParseFiles(filenames ...string) (*Set, error) { for _, filename := range filenames { b, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - _, err = s.Parse(string(b)) - if err != nil { - return nil, err + s := string(b) + name := filepath.Base(filename) + // First template becomes return value if not already defined, + // and we use that one for subsequent New calls to associate + // all the templates together. Also, if this file has the same name + // as t, this file becomes the contents of t, so + // t, err := New(name).Funcs(xxx).ParseFiles(name) + // works. Otherwise we create a new template associated with t. + var tmpl *Template + if t == nil { + t = New(name) } - } - return s, nil -} - -// ParseSetFiles creates a new Set and parses the set definition from the -// named files. Each file must be individually parseable. -func ParseSetFiles(filenames ...string) (*Set, error) { - s := new(Set) - for _, filename := range filenames { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err + if name == t.Name() { + tmpl = t + } else { + tmpl = t.New(name) } - _, err = s.Parse(string(b)) + _, err = tmpl.Parse(s) if err != nil { return nil, err } } - return s, nil + return t, nil } -// ParseGlob parses the set definition from the files identified by the -// pattern. The pattern is processed by filepath.Glob and must match at -// least one file. -// If an error occurs, parsing stops and the returned set is nil. -func (s *Set) ParseGlob(pattern string) (*Set, error) { - filenames, err := filepath.Glob(pattern) - if err != nil { - return nil, err - } - if len(filenames) == 0 { - return nil, fmt.Errorf("pattern matches no files: %#q", pattern) - } - return s.ParseFiles(filenames...) +// ParseGlob creates a new Template and parses the template definitions from the +// files identified by the pattern, which must match at least one file. The +// returned template will have the (base) name and (parsed) contents of the +// first file matched by the pattern. ParseGlob is equivalent to calling +// ParseFiles with the list of files matched by the pattern. +func ParseGlob(pattern string) (*Template, error) { + return parseGlob(nil, pattern) } -// ParseSetGlob creates a new Set and parses the set definition from the -// files identified by the pattern. The pattern is processed by filepath.Glob -// and must match at least one file. -func ParseSetGlob(pattern string) (*Set, error) { - set, err := new(Set).ParseGlob(pattern) - if err != nil { - return nil, err - } - return set, nil +// ParseGlob parses the template definitions in the files identified by the +// pattern and associates the resulting templates with t. The pattern is +// processed by filepath.Glob and must match at least one file. ParseGlob is +// equivalent to calling t.ParseFiles with the list of files matched by the +// pattern. +func (t *Template) ParseGlob(pattern string) (*Template, error) { + return parseGlob(t, pattern) } -// Functions and methods to parse stand-alone template files into a set. - -// ParseTemplateFiles parses the named template files and adds -// them to the set. Each template will be named the base name of -// its file. -// Unlike with ParseFiles, each file should be a stand-alone template -// definition suitable for Template.Parse (not Set.Parse); that is, the -// file does not contain {{define}} clauses. ParseTemplateFiles is -// therefore equivalent to calling the ParseFile function to create -// individual templates, which are then added to the set. -// Each file must be parseable by itself. -// If an error occurs, parsing stops and the returned set is nil. -func (s *Set) ParseTemplateFiles(filenames ...string) (*Set, error) { - for _, filename := range filenames { - _, err := parseFileInSet(filename, s) - if err != nil { - return nil, err - } - } - return s, nil -} - -// ParseTemplateGlob parses the template files matched by the -// patern and adds them to the set. Each template will be named -// the base name of its file. -// Unlike with ParseGlob, each file should be a stand-alone template -// definition suitable for Template.Parse (not Set.Parse); that is, the -// file does not contain {{define}} clauses. ParseTemplateGlob is -// therefore equivalent to calling the ParseFile function to create -// individual templates, which are then added to the set. -// Each file must be parseable by itself. -// If an error occurs, parsing stops and the returned set is nil. -func (s *Set) ParseTemplateGlob(pattern string) (*Set, error) { - filenames, err := filepath.Glob(pattern) - if err != nil { - return nil, err - } - for _, filename := range filenames { - _, err := parseFileInSet(filename, s) - if err != nil { - return nil, err - } - } - return s, nil -} - -// ParseTemplateFiles creates a set by parsing the named files, -// each of which defines a single template. Each template will be -// named the base name of its file. -// Unlike with ParseFiles, each file should be a stand-alone template -// definition suitable for Template.Parse (not Set.Parse); that is, the -// file does not contain {{define}} clauses. ParseTemplateFiles is -// therefore equivalent to calling the ParseFile function to create -// individual templates, which are then added to the set. -// Each file must be parseable by itself. Parsing stops if an error is -// encountered. -func ParseTemplateFiles(filenames ...string) (*Set, error) { - set := new(Set) - set.init() - for _, filename := range filenames { - t, err := ParseFile(filename) - if err != nil { - return nil, err - } - if err := set.add(t); err != nil { - return nil, err - } - } - return set, nil -} - -// ParseTemplateGlob creates a set by parsing the files matched -// by the pattern, each of which defines a single template. The pattern -// is processed by filepath.Glob and must match at least one file. Each -// template will be named the base name of its file. -// Unlike with ParseGlob, each file should be a stand-alone template -// definition suitable for Template.Parse (not Set.Parse); that is, the -// file does not contain {{define}} clauses. ParseTemplateGlob is -// therefore equivalent to calling the ParseFile function to create -// individual templates, which are then added to the set. -// Each file must be parseable by itself. Parsing stops if an error is -// encountered. -func ParseTemplateGlob(pattern string) (*Set, error) { - set := new(Set) +// parseGlob is the implementation of the function and method ParseGlob. +func parseGlob(t *Template, pattern string) (*Template, error) { filenames, err := filepath.Glob(pattern) if err != nil { return nil, err } if len(filenames) == 0 { - return nil, fmt.Errorf("pattern matches no files: %#q", pattern) - } - for _, filename := range filenames { - t, err := ParseFile(filename) - if err != nil { - return nil, err - } - if err := set.add(t); err != nil { - return nil, err - } + return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern) } - return set, nil + return parseFiles(t, filenames...) } diff --git a/libgo/go/text/template/multi_test.go b/libgo/go/text/template/multi_test.go new file mode 100644 index 00000000000..7b35d2633d5 --- /dev/null +++ b/libgo/go/text/template/multi_test.go @@ -0,0 +1,288 @@ +// 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 template + +// Tests for mulitple-template parsing and execution. + +import ( + "bytes" + "fmt" + "testing" + "text/template/parse" +) + +type isEmptyTest struct { + name string + input string + empty bool +} + +var isEmptyTests = []isEmptyTest{ + {"empty", ``, true}, + {"nonempty", `hello`, false}, + {"spaces only", " \t\n \t\n", true}, + {"definition", `{{define "x"}}something{{end}}`, true}, + {"definitions and space", "{{define `x`}}something{{end}}\n\n{{define `y`}}something{{end}}\n\n", true}, + {"definitions and text", "{{define `x`}}something{{end}}\nx\n{{define `y`}}something{{end}}\ny\n}}", false}, + {"definition and action", "{{define `x`}}something{{end}}{{if 3}}foo{{end}}", false}, +} + +func TestIsEmpty(t *testing.T) { + for _, test := range isEmptyTests { + template, err := New("root").Parse(test.input) + if err != nil { + t.Errorf("%q: unexpected error: %v", test.name, err) + continue + } + if empty := isEmpty(template.Root); empty != test.empty { + t.Errorf("%q: expected %t got %t", test.name, test.empty, empty) + } + } +} + +const ( + noError = true + hasError = false +) + +type multiParseTest struct { + name string + input string + ok bool + names []string + results []string +} + +var multiParseTests = []multiParseTest{ + {"empty", "", noError, + nil, + nil}, + {"one", `{{define "foo"}} FOO {{end}}`, noError, + []string{"foo"}, + []string{`[(text: " FOO ")]`}}, + {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError, + []string{"foo", "bar"}, + []string{`[(text: " FOO ")]`, `[(text: " BAR ")]`}}, + // errors + {"missing end", `{{define "foo"}} FOO `, hasError, + nil, + nil}, + {"malformed name", `{{define "foo}} FOO `, hasError, + nil, + nil}, +} + +func TestMultiParse(t *testing.T) { + for _, test := range multiParseTests { + template, err := New("root").Parse(test.input) + switch { + case err == nil && !test.ok: + t.Errorf("%q: expected error; got none", test.name) + continue + case err != nil && test.ok: + t.Errorf("%q: unexpected error: %v", test.name, err) + continue + case err != nil && !test.ok: + // expected error, got one + if *debug { + fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) + } + continue + } + if template == nil { + continue + } + if len(template.tmpl) != len(test.names)+1 { // +1 for root + t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(template.tmpl)) + continue + } + for i, name := range test.names { + tmpl, ok := template.tmpl[name] + if !ok { + t.Errorf("%s: can't find template %q", test.name, name) + continue + } + result := tmpl.Root.String() + if result != test.results[i] { + t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i]) + } + } + } +} + +var multiExecTests = []execTest{ + {"empty", "", "", nil, true}, + {"text", "some text", "some text", nil, true}, + {"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true}, + {"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true}, + {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true}, + {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true}, + {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true}, + {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true}, + {"variable declared by template", `{{template "nested" $x=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true}, + + // User-defined function: test argument evaluator. + {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true}, + {"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true}, +} + +// These strings are also in testdata/*. +const multiText1 = ` + {{define "x"}}TEXT{{end}} + {{define "dotV"}}{{.V}}{{end}} +` + +const multiText2 = ` + {{define "dot"}}{{.}}{{end}} + {{define "nested"}}{{template "dot" .}}{{end}} +` + +func TestMultiExecute(t *testing.T) { + // Declare a couple of templates first. + template, err := New("root").Parse(multiText1) + if err != nil { + t.Fatalf("parse error for 1: %s", err) + } + _, err = template.Parse(multiText2) + if err != nil { + t.Fatalf("parse error for 2: %s", err) + } + testExecute(multiExecTests, template, t) +} + +func TestParseFiles(t *testing.T) { + _, err := ParseFiles("DOES NOT EXIST") + if err == nil { + t.Error("expected error for non-existent file; got none") + } + template := New("root") + _, err = template.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(multiExecTests, template, t) +} + +func TestParseGlob(t *testing.T) { + _, err := ParseGlob("DOES NOT EXIST") + if err == nil { + t.Error("expected error for non-existent file; got none") + } + _, err = New("error").ParseGlob("[x") + if err == nil { + t.Error("expected error for bad pattern; got none") + } + template := New("root") + _, err = template.ParseGlob("testdata/file*.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(multiExecTests, template, t) +} + +// In these tests, actual content (not just template definitions) comes from the parsed files. + +var templateFileExecTests = []execTest{ + {"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\n\ny\ntemplate2\n\nx\n", 0, true}, +} + +func TestParseFilesWithData(t *testing.T) { + template, err := New("root").ParseFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(templateFileExecTests, template, t) +} + +func TestParseGlobWithData(t *testing.T) { + template, err := New("root").ParseGlob("testdata/tmpl*.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(templateFileExecTests, template, t) +} + +const ( + cloneText1 = `{{define "a"}}{{template "b"}}{{template "c"}}{{end}}` + cloneText2 = `{{define "b"}}b{{end}}` + cloneText3 = `{{define "c"}}root{{end}}` + cloneText4 = `{{define "c"}}clone{{end}}` +) + +func TestClone(t *testing.T) { + // Create some templates and clone the root. + root, err := New("root").Parse(cloneText1) + if err != nil { + t.Fatal(err) + } + _, err = root.Parse(cloneText2) + if err != nil { + t.Fatal(err) + } + clone := root.Clone() + // Add variants to both. + _, err = root.Parse(cloneText3) + if err != nil { + t.Fatal(err) + } + _, err = clone.Parse(cloneText4) + if err != nil { + t.Fatal(err) + } + // Verify that the clone is self-consistent. + for k, v := range clone.tmpl { + if k == clone.name && v.tmpl[k] != clone { + t.Error("clone does not contain root") + } + if v != v.tmpl[v.name] { + t.Errorf("clone does not contain self for %q", k) + } + } + // Execute root. + var b bytes.Buffer + err = root.ExecuteTemplate(&b, "a", 0) + if err != nil { + t.Fatal(err) + } + if b.String() != "broot" { + t.Errorf("expected %q got %q", "broot", b.String()) + } + // Execute copy. + b.Reset() + err = clone.ExecuteTemplate(&b, "a", 0) + if err != nil { + t.Fatal(err) + } + if b.String() != "bclone" { + t.Errorf("expected %q got %q", "bclone", b.String()) + } +} + +func TestAddParseTree(t *testing.T) { + // Create some templates. + root, err := New("root").Parse(cloneText1) + if err != nil { + t.Fatal(err) + } + _, err = root.Parse(cloneText2) + if err != nil { + t.Fatal(err) + } + // Add a new parse tree. + tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins) + if err != nil { + t.Fatal(err) + } + added, err := root.AddParseTree("c", tree["c"]) + // Execute. + var b bytes.Buffer + err = added.ExecuteTemplate(&b, "a", 0) + if err != nil { + t.Fatal(err) + } + if b.String() != "broot" { + t.Errorf("expected %q got %q", "broot", b.String()) + } +} diff --git a/libgo/go/text/template/parse.go b/libgo/go/text/template/parse.go deleted file mode 100644 index fa562141c29..00000000000 --- a/libgo/go/text/template/parse.go +++ /dev/null @@ -1,89 +0,0 @@ -// 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 template - -import ( - "reflect" - "text/template/parse" -) - -// Template is the representation of a parsed template. -type Template struct { - name string - *parse.Tree - leftDelim string - rightDelim string - // We use two maps, one for parsing and one for execution. - // This separation makes the API cleaner since it doesn't - // expose reflection to the client. - parseFuncs FuncMap - execFuncs map[string]reflect.Value - set *Set // can be nil. -} - -// Name returns the name of the template. -func (t *Template) Name() string { - return t.name -} - -// Parsing. - -// New allocates a new template with the given name. -func New(name string) *Template { - return &Template{ - name: name, - parseFuncs: make(FuncMap), - execFuncs: make(map[string]reflect.Value), - } -} - -// Delims sets the action delimiters, to be used in a subsequent -// parse, to the specified strings. -// An empty delimiter stands for the corresponding default: {{ or }}. -// The return value is the template, so calls can be chained. -func (t *Template) Delims(left, right string) *Template { - t.leftDelim = left - t.rightDelim = right - return t -} - -// Funcs adds the elements of the argument map to the template's function -// map. It panics if a value in the map is not a function with appropriate -// return type. -// The return value is the template, so calls can be chained. -func (t *Template) Funcs(funcMap FuncMap) *Template { - addValueFuncs(t.execFuncs, funcMap) - addFuncs(t.parseFuncs, funcMap) - return t -} - -// Parse parses the template definition string to construct an internal -// representation of the template for execution. -func (t *Template) Parse(s string) (tmpl *Template, err error) { - t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, t.parseFuncs, builtins) - if err != nil { - return nil, err - } - return t, nil -} - -// ParseInSet parses the template definition string to construct an internal -// representation of the template for execution. It also adds the template -// to the set. It is an error if s is already defined in the set. -// Function bindings are checked against those in the set. -func (t *Template) ParseInSet(s string, set *Set) (tmpl *Template, err error) { - var setFuncs FuncMap - if set != nil { - setFuncs = set.parseFuncs - } - t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, t.parseFuncs, setFuncs, builtins) - if err != nil { - return nil, err - } - if set != nil { - err = set.add(t) - } - return t, err -} diff --git a/libgo/go/text/template/parse/parse.go b/libgo/go/text/template/parse/parse.go index 1b6ab3af4f0..346f613b048 100644 --- a/libgo/go/text/template/parse/parse.go +++ b/libgo/go/text/template/parse/parse.go @@ -13,10 +13,10 @@ import ( "unicode" ) -// Tree is the representation of a parsed template. +// Tree is the representation of a single parsed template. type Tree struct { - Name string // Name is the name of the template. - Root *ListNode // Root is the top-level root of the parse tree. + Name string // name of the template represented by the tree. + Root *ListNode // top-level root of the tree. // Parsing only; cleared after parse. funcs []map[string]interface{} lex *lexer @@ -25,6 +25,16 @@ type Tree struct { vars []string // variables defined at the moment. } +// Parse returns a map from template name to parse.Tree, created by parsing the +// templates described in the argument string. The top-level template will be +// given the specified name. If an error is encountered, parsing stops and an +// empty map is returned with the error. +func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (treeSet map[string]*Tree, err error) { + treeSet = make(map[string]*Tree) + _, err = New(name).Parse(text, leftDelim, rightDelim, treeSet, funcs...) + return +} + // next returns the next token. func (t *Tree) next() item { if t.peekCount > 0 { @@ -58,7 +68,7 @@ func (t *Tree) peek() item { // Parsing. -// New allocates a new template with the given name. +// New allocates a new parse tree with the given name. func New(name string, funcs ...map[string]interface{}) *Tree { return &Tree{ Name: name, @@ -87,6 +97,15 @@ func (t *Tree) expect(expected itemType, context string) item { return token } +// expectEither consumes the next token and guarantees it has one of the required types. +func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item { + token := t.next() + if token.typ != expected1 && token.typ != expected2 { + t.errorf("expected %s or %s in %s; got %s", expected1, expected2, context, token) + } + return token +} + // unexpected complains about the token and terminates processing. func (t *Tree) unexpected(token item, context string) { t.errorf("unexpected %s in %s", token, context) @@ -107,7 +126,7 @@ func (t *Tree) recover(errp *error) { return } -// startParse starts the template parsing from the lexer. +// startParse initializes the parser, using the lexer. func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer) { t.Root = nil t.lex = lex @@ -143,32 +162,77 @@ func (t *Tree) atEOF() bool { return false } -// Parse parses the template definition string to construct an internal -// representation of the template for execution. If either action delimiter -// string is empty, the default ("{{" or "}}") is used. -func (t *Tree) Parse(s, leftDelim, rightDelim string, funcs ...map[string]interface{}) (tree *Tree, err error) { +// Parse parses the template definition string to construct a representation of +// the template for execution. If either action delimiter string is empty, the +// default ("{{" or "}}") is used. Embedded template definitions are added to +// the treeSet map. +func (t *Tree) Parse(s, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) { defer t.recover(&err) t.startParse(funcs, lex(t.Name, s, leftDelim, rightDelim)) - t.parse(true) + t.parse(treeSet) + t.add(treeSet) t.stopParse() return t, nil } -// parse is the helper for Parse. -// It triggers an error if we expect EOF but don't reach it. -func (t *Tree) parse(toEOF bool) (next Node) { - t.Root, next = t.itemList(true) - if toEOF && next != nil { - t.errorf("unexpected %s", next) +// add adds tree to the treeSet. +func (t *Tree) add(treeSet map[string]*Tree) { + if _, present := treeSet[t.Name]; present { + t.errorf("template: multiple definition of template %q", t.Name) + } + treeSet[t.Name] = t +} + +// parse is the top-level parser for a template, essentially the same +// as itemList except it also parses {{define}} actions. +// It runs to EOF. +func (t *Tree) parse(treeSet map[string]*Tree) (next Node) { + t.Root = newList() + for t.peek().typ != itemEOF { + if t.peek().typ == itemLeftDelim { + delim := t.next() + if t.next().typ == itemDefine { + newT := New("definition") // name will be updated once we know it. + newT.startParse(t.funcs, t.lex) + newT.parseDefinition(treeSet) + continue + } + t.backup2(delim) + } + n := t.textOrAction() + if n.Type() == nodeEnd { + t.errorf("unexpected %s", n) + } + t.Root.append(n) + } + return nil +} + +// parseDefinition parses a {{define}} ... {{end}} template definition and +// installs the definition in the treeSet map. The "define" keyword has already +// been scanned. +func (t *Tree) parseDefinition(treeSet map[string]*Tree) { + const context = "define clause" + name := t.expectOneOf(itemString, itemRawString, context) + var err error + t.Name, err = strconv.Unquote(name.val) + if err != nil { + t.error(err) + } + t.expect(itemRightDelim, context) + var end Node + t.Root, end = t.itemList() + if end.Type() != nodeEnd { + t.errorf("unexpected %s in %s", end, context) } - return next + t.stopParse() + t.add(treeSet) } // itemList: // textOrAction* -// Terminates at EOF and at {{end}} or {{else}}, which is returned separately. -// The toEOF flag tells whether we expect to reach EOF. -func (t *Tree) itemList(toEOF bool) (list *ListNode, next Node) { +// Terminates at {{end}} or {{else}}, returned separately. +func (t *Tree) itemList() (list *ListNode, next Node) { list = newList() for t.peek().typ != itemEOF { n := t.textOrAction() @@ -178,10 +242,8 @@ func (t *Tree) itemList(toEOF bool) (list *ListNode, next Node) { } list.append(n) } - if !toEOF { - t.unexpected(t.next(), "input") - } - return list, nil + t.errorf("unexpected EOF") + return } // textOrAction: @@ -276,11 +338,11 @@ func (t *Tree) parseControl(context string) (lineNum int, pipe *PipeNode, list, defer t.popVars(len(t.vars)) pipe = t.pipeline(context) var next Node - list, next = t.itemList(false) + list, next = t.itemList() switch next.Type() { case nodeEnd: //done case nodeElse: - elseList, next = t.itemList(false) + elseList, next = t.itemList() if next.Type() != nodeEnd { t.errorf("expected end; found %s", next) } diff --git a/libgo/go/text/template/parse/parse_test.go b/libgo/go/text/template/parse/parse_test.go index f05f6e3874c..fc93455ecbc 100644 --- a/libgo/go/text/template/parse/parse_test.go +++ b/libgo/go/text/template/parse/parse_test.go @@ -236,7 +236,7 @@ var builtins = map[string]interface{}{ func TestParse(t *testing.T) { for _, test := range parseTests { - tmpl, err := New(test.name).Parse(test.input, "", "", builtins) + tmpl, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree), builtins) switch { case err == nil && !test.ok: t.Errorf("%q: expected error; got none", test.name) diff --git a/libgo/go/text/template/parse/set.go b/libgo/go/text/template/parse/set.go deleted file mode 100644 index d363eeff080..00000000000 --- a/libgo/go/text/template/parse/set.go +++ /dev/null @@ -1,49 +0,0 @@ -// 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 parse - -import ( - "fmt" - "strconv" -) - -// Set returns a slice of Trees created by parsing the template set -// definition in the argument string. If an error is encountered, -// parsing stops and an empty slice is returned with the error. -func Set(text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (tree map[string]*Tree, err error) { - tree = make(map[string]*Tree) - defer (*Tree)(nil).recover(&err) - lex := lex("set", text, leftDelim, rightDelim) - const context = "define clause" - for { - t := New("set") // name will be updated once we know it. - t.startParse(funcs, lex) - // Expect EOF or "{{ define name }}". - if t.atEOF() { - break - } - t.expect(itemLeftDelim, context) - t.expect(itemDefine, context) - name := t.expect(itemString, context) - t.Name, err = strconv.Unquote(name.val) - if err != nil { - t.error(err) - } - t.expect(itemRightDelim, context) - end := t.parse(false) - if end == nil { - t.errorf("unexpected EOF in %s", context) - } - if end.Type() != nodeEnd { - t.errorf("unexpected %s in %s", end, context) - } - t.stopParse() - if _, present := tree[t.Name]; present { - return nil, fmt.Errorf("template: %q multiply defined", name) - } - tree[t.Name] = t - } - return -} diff --git a/libgo/go/text/template/set.go b/libgo/go/text/template/set.go deleted file mode 100644 index 747cc7802b4..00000000000 --- a/libgo/go/text/template/set.go +++ /dev/null @@ -1,120 +0,0 @@ -// 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 template - -import ( - "fmt" - "io" - "reflect" - "text/template/parse" -) - -// Set holds a set of related templates that can refer to one another by name. -// The zero value represents an empty set. -// A template may be a member of multiple sets. -type Set struct { - tmpl map[string]*Template - leftDelim string - rightDelim string - parseFuncs FuncMap - execFuncs map[string]reflect.Value -} - -func (s *Set) init() { - if s.tmpl == nil { - s.tmpl = make(map[string]*Template) - s.parseFuncs = make(FuncMap) - s.execFuncs = make(map[string]reflect.Value) - } -} - -// Delims sets the action delimiters, to be used in a subsequent -// parse, to the specified strings. -// An empty delimiter stands for the corresponding default: {{ or }}. -// The return value is the set, so calls can be chained. -func (s *Set) Delims(left, right string) *Set { - s.leftDelim = left - s.rightDelim = right - return s -} - -// Funcs adds the elements of the argument map to the set's function map. It -// panics if a value in the map is not a function with appropriate return -// type. -// The return value is the set, so calls can be chained. -func (s *Set) Funcs(funcMap FuncMap) *Set { - s.init() - addValueFuncs(s.execFuncs, funcMap) - addFuncs(s.parseFuncs, funcMap) - return s -} - -// Add adds the argument templates to the set. It panics if two templates -// with the same name are added or if a template is already a member of -// a set. -// The return value is the set, so calls can be chained. -func (s *Set) Add(templates ...*Template) *Set { - for _, t := range templates { - if err := s.add(t); err != nil { - panic(err) - } - } - return s -} - -// add adds the argument template to the set. -func (s *Set) add(t *Template) error { - s.init() - if t.set != nil { - return fmt.Errorf("template: %q already in a set", t.name) - } - if _, ok := s.tmpl[t.name]; ok { - return fmt.Errorf("template: %q already defined in set", t.name) - } - s.tmpl[t.name] = t - t.set = s - return nil -} - -// Template returns the template with the given name in the set, -// or nil if there is no such template. -func (s *Set) Template(name string) *Template { - return s.tmpl[name] -} - -// FuncMap returns the set's function map. -func (s *Set) FuncMap() FuncMap { - return s.parseFuncs -} - -// Execute applies the named template to the specified data object, writing -// the output to wr. -func (s *Set) Execute(wr io.Writer, name string, data interface{}) error { - tmpl := s.tmpl[name] - if tmpl == nil { - return fmt.Errorf("template: no template %q in set", name) - } - return tmpl.Execute(wr, data) -} - -// Parse parses a string into a set of named templates. Parse may be called -// multiple times for a given set, adding the templates defined in the string -// to the set. It is an error if a template has a name already defined in the set. -func (s *Set) Parse(text string) (*Set, error) { - trees, err := parse.Set(text, s.leftDelim, s.rightDelim, s.parseFuncs, builtins) - if err != nil { - return nil, err - } - s.init() - for name, tree := range trees { - tmpl := New(name) - tmpl.Tree = tree - err = s.add(tmpl) - if err != nil { - return s, err - } - } - return s, nil -} diff --git a/libgo/go/text/template/set_test.go b/libgo/go/text/template/set_test.go deleted file mode 100644 index f437bc779c2..00000000000 --- a/libgo/go/text/template/set_test.go +++ /dev/null @@ -1,239 +0,0 @@ -// 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 template - -import ( - "fmt" - "testing" -) - -const ( - noError = true - hasError = false -) - -type setParseTest struct { - name string - input string - ok bool - names []string - results []string -} - -var setParseTests = []setParseTest{ - {"empty", "", noError, - nil, - nil}, - {"one", `{{define "foo"}} FOO {{end}}`, noError, - []string{"foo"}, - []string{`[(text: " FOO ")]`}}, - {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError, - []string{"foo", "bar"}, - []string{`[(text: " FOO ")]`, `[(text: " BAR ")]`}}, - // errors - {"missing end", `{{define "foo"}} FOO `, hasError, - nil, - nil}, - {"malformed name", `{{define "foo}} FOO `, hasError, - nil, - nil}, -} - -func TestSetParse(t *testing.T) { - for _, test := range setParseTests { - set, err := new(Set).Parse(test.input) - switch { - case err == nil && !test.ok: - t.Errorf("%q: expected error; got none", test.name) - continue - case err != nil && test.ok: - t.Errorf("%q: unexpected error: %v", test.name, err) - continue - case err != nil && !test.ok: - // expected error, got one - if *debug { - fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) - } - continue - } - if set == nil { - continue - } - if len(set.tmpl) != len(test.names) { - t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(set.tmpl)) - continue - } - for i, name := range test.names { - tmpl, ok := set.tmpl[name] - if !ok { - t.Errorf("%s: can't find template %q", test.name, name) - continue - } - result := tmpl.Root.String() - if result != test.results[i] { - t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i]) - } - } - } -} - -var setExecTests = []execTest{ - {"empty", "", "", nil, true}, - {"text", "some text", "some text", nil, true}, - {"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true}, - {"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true}, - {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true}, - {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true}, - {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true}, - {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true}, - {"variable declared by template", `{{template "nested" $x=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true}, - - // User-defined function: test argument evaluator. - {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true}, - {"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true}, -} - -// These strings are also in testdata/*. -const setText1 = ` - {{define "x"}}TEXT{{end}} - {{define "dotV"}}{{.V}}{{end}} -` - -const setText2 = ` - {{define "dot"}}{{.}}{{end}} - {{define "nested"}}{{template "dot" .}}{{end}} -` - -func TestSetExecute(t *testing.T) { - // Declare a set with a couple of templates first. - set := new(Set) - _, err := set.Parse(setText1) - if err != nil { - t.Fatalf("error parsing set: %s", err) - } - _, err = set.Parse(setText2) - if err != nil { - t.Fatalf("error parsing set: %s", err) - } - testExecute(setExecTests, set, t) -} - -func TestSetParseFiles(t *testing.T) { - set := new(Set) - _, err := set.ParseFiles("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - _, err = set.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(setExecTests, set, t) -} - -func TestParseSetFiles(t *testing.T) { - set := new(Set) - _, err := ParseSetFiles("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - set, err = ParseSetFiles("testdata/file1.tmpl", "testdata/file2.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(setExecTests, set, t) -} - -func TestSetParseGlob(t *testing.T) { - _, err := new(Set).ParseGlob("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - _, err = new(Set).ParseGlob("[x") - if err == nil { - t.Error("expected error for bad pattern; got none") - } - set, err := new(Set).ParseGlob("testdata/file*.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(setExecTests, set, t) -} - -func TestParseSetGlob(t *testing.T) { - _, err := ParseSetGlob("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - _, err = ParseSetGlob("[x") - if err == nil { - t.Error("expected error for bad pattern; got none") - } - set, err := ParseSetGlob("testdata/file*.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(setExecTests, set, t) -} - -var templateFileExecTests = []execTest{ - {"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\ntemplate2\n", 0, true}, -} - -func TestSetParseTemplateFiles(t *testing.T) { - _, err := ParseTemplateFiles("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - set, err := new(Set).ParseTemplateFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(templateFileExecTests, set, t) -} - -func TestParseTemplateFiles(t *testing.T) { - _, err := ParseTemplateFiles("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - set, err := new(Set).ParseTemplateFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(templateFileExecTests, set, t) -} - -func TestSetParseTemplateGlob(t *testing.T) { - _, err := ParseTemplateGlob("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - _, err = new(Set).ParseTemplateGlob("[x") - if err == nil { - t.Error("expected error for bad pattern; got none") - } - set, err := new(Set).ParseTemplateGlob("testdata/tmpl*.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(templateFileExecTests, set, t) -} - -func TestParseTemplateGlob(t *testing.T) { - _, err := ParseTemplateGlob("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - _, err = ParseTemplateGlob("[x") - if err == nil { - t.Error("expected error for bad pattern; got none") - } - set, err := ParseTemplateGlob("testdata/tmpl*.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(templateFileExecTests, set, t) -} diff --git a/libgo/go/text/template/template.go b/libgo/go/text/template/template.go new file mode 100644 index 00000000000..04fca407c10 --- /dev/null +++ b/libgo/go/text/template/template.go @@ -0,0 +1,236 @@ +// 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 template + +import ( + "bytes" + "fmt" + "reflect" + "text/template/parse" +) + +// common holds the information shared by related templates. +type common struct { + tmpl map[string]*Template + // We use two maps, one for parsing and one for execution. + // This separation makes the API cleaner since it doesn't + // expose reflection to the client. + parseFuncs FuncMap + execFuncs map[string]reflect.Value +} + +// Template is the representation of a parsed template. The *parse.Tree +// field is exported only for use by html/template and should be treated +// as unexported by all other clients. +type Template struct { + name string + *parse.Tree + *common + leftDelim string + rightDelim string +} + +// New allocates a new template with the given name. +func New(name string) *Template { + return &Template{ + name: name, + } +} + +// Name returns the name of the template. +func (t *Template) Name() string { + return t.name +} + +// New allocates a new template associated with the given one and with the same +// delimiters. The association, which is transitive, allows one template to +// invoke another with a {{template}} action. +func (t *Template) New(name string) *Template { + t.init() + return &Template{ + name: name, + common: t.common, + leftDelim: t.leftDelim, + rightDelim: t.rightDelim, + } +} + +func (t *Template) init() { + if t.common == nil { + t.common = new(common) + t.tmpl = make(map[string]*Template) + t.parseFuncs = make(FuncMap) + t.execFuncs = make(map[string]reflect.Value) + } +} + +// Clone returns a duplicate of the template, including all associated +// templates. The actual representation is not copied, but the name space of +// associated templates is, so further calls to Parse in the copy will add +// templates to the copy but not to the original. Clone can be used to prepare +// common templates and use them with variant definitions for other templates by +// adding the variants after the clone is made. +func (t *Template) Clone() *Template { + nt := t.copy(nil) + nt.init() + nt.tmpl[t.name] = nt + for k, v := range t.tmpl { + if k == t.name { // Already installed. + continue + } + // The associated templates share nt's common structure. + tmpl := v.copy(nt.common) + nt.tmpl[k] = tmpl + } + for k, v := range t.parseFuncs { + nt.parseFuncs[k] = v + } + for k, v := range t.execFuncs { + nt.execFuncs[k] = v + } + return nt +} + +// copy returns a shallow copy of t, with common set to the argument. +func (t *Template) copy(c *common) *Template { + nt := New(t.name) + nt.Tree = t.Tree + nt.common = c + nt.leftDelim = t.leftDelim + nt.rightDelim = t.rightDelim + return nt +} + +// AddParseTree creates a new template with the name and parse tree +// and associates it with t. +func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) { + if t.tmpl[name] != nil { + return nil, fmt.Errorf("template: redefinition of template %q", name) + } + nt := t.New(name) + nt.Tree = tree + t.tmpl[name] = nt + return nt, nil +} + +// Templates returns a slice of the templates associated with t, including t +// itself. +func (t *Template) Templates() []*Template { + // Return a slice so we don't expose the map. + m := make([]*Template, 0, len(t.tmpl)) + for _, v := range t.tmpl { + m = append(m, v) + } + return m +} + +// Delims sets the action delimiters to the specified strings, to be used in +// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template +// definitions will inherit the settings. An empty delimiter stands for the +// corresponding default: {{ or }}. +// The return value is the template, so calls can be chained. +func (t *Template) Delims(left, right string) *Template { + t.leftDelim = left + t.rightDelim = right + return t +} + +// Funcs adds the elements of the argument map to the template's function map. +// It panics if a value in the map is not a function with appropriate return +// type. However, it is legal to overwrite elements of the map. The return +// value is the template, so calls can be chained. +func (t *Template) Funcs(funcMap FuncMap) *Template { + t.init() + addValueFuncs(t.execFuncs, funcMap) + addFuncs(t.parseFuncs, funcMap) + return t +} + +// Lookup returns the template with the given name that is associated with t, +// or nil if there is no such template. +func (t *Template) Lookup(name string) *Template { + if t.common == nil { + return nil + } + return t.tmpl[name] +} + +// Parse parses a string into a template. Nested template definitions will be +// associated with the top-level template t. Parse may be called multiple times +// to parse definitions of templates to associate with t. It is an error if a +// resulting template is non-empty (contains content other than template +// definitions) and would replace a non-empty template with the same name. +// (In multiple calls to Parse with the same receiver template, only one call +// can contain text other than space, comments, and template definitions.) +func (t *Template) Parse(text string) (*Template, error) { + t.init() + trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins) + if err != nil { + return nil, err + } + // Add the newly parsed trees, including the one for t, into our common structure. + for name, tree := range trees { + // If the name we parsed is the name of this template, overwrite this template. + // The associate method checks it's not a redefinition. + tmpl := t + if name != t.name { + tmpl = t.New(name) + } + // Even if t == tmpl, we need to install it in the common.tmpl map. + if err := t.associate(tmpl); err != nil { + return nil, err + } + tmpl.Tree = tree + tmpl.leftDelim = t.leftDelim + tmpl.rightDelim = t.rightDelim + } + return t, nil +} + +// associate installs the new template into the group of templates associated +// with t. It is an error to reuse a name except to overwrite an empty +// template. The two are already known to share the common structure. +func (t *Template) associate(new *Template) error { + if new.common != t.common { + panic("internal error: associate not common") + } + name := new.name + if old := t.tmpl[name]; old != nil { + oldIsEmpty := isEmpty(old.Root) + newIsEmpty := isEmpty(new.Root) + if !oldIsEmpty && !newIsEmpty { + return fmt.Errorf("template: redefinition of template %q", name) + } + if newIsEmpty { + // Whether old is empty or not, new is empty; no reason to replace old. + return nil + } + } + t.tmpl[name] = new + return nil +} + +// isEmpty reports whether this tree (node) is empty of everything but space. +func isEmpty(n parse.Node) bool { + switch n := n.(type) { + case *parse.ActionNode: + case *parse.IfNode: + case *parse.ListNode: + for _, node := range n.Nodes { + if !isEmpty(node) { + return false + } + } + return true + case *parse.RangeNode: + case *parse.TemplateNode: + case *parse.TextNode: + return len(bytes.TrimSpace(n.Text)) == 0 + case *parse.WithNode: + default: + panic("unknown node: " + n.String()) + } + return false +} diff --git a/libgo/go/text/template/testdata/tmpl1.tmpl b/libgo/go/text/template/testdata/tmpl1.tmpl index 3d15b81735a..b72b3a340c7 100644 --- a/libgo/go/text/template/testdata/tmpl1.tmpl +++ b/libgo/go/text/template/testdata/tmpl1.tmpl @@ -1 +1,3 @@ template1 +{{define "x"}}x{{end}} +{{template "y"}} diff --git a/libgo/go/text/template/testdata/tmpl2.tmpl b/libgo/go/text/template/testdata/tmpl2.tmpl index a374d2fe7fc..16beba6e7dd 100644 --- a/libgo/go/text/template/testdata/tmpl2.tmpl +++ b/libgo/go/text/template/testdata/tmpl2.tmpl @@ -1 +1,3 @@ template2 +{{define "y"}}y{{end}} +{{template "x"}} diff --git a/libgo/go/time/example_test.go b/libgo/go/time/example_test.go new file mode 100644 index 00000000000..153b1a3b660 --- /dev/null +++ b/libgo/go/time/example_test.go @@ -0,0 +1,58 @@ +// 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 time_test + +import ( + "fmt" + "time" +) + +func expensiveCall() {} + +func ExampleDuration() { + t0 := time.Now() + expensiveCall() + t1 := time.Now() + fmt.Printf("The call took %v to run.\n", t1.Sub(t0)) +} + +var c chan int + +func handle(int) {} + +func ExampleAfter() { + select { + case m := <-c: + handle(m) + case <-time.After(5 * time.Minute): + fmt.Println("timed out") + } +} + +func ExampleSleep() { + time.Sleep(100 * time.Millisecond) +} + +func statusUpdate() string { return "" } + +func ExampleTick() { + c := time.Tick(1 * time.Minute) + for now := range c { + fmt.Printf("%v %s\n", now, statusUpdate()) + } +} + +func ExampleMonth() { + _, month, day := time.Now().Date() + if month == time.November && day == 10 { + fmt.Println("Happy Go day!") + } +} + +// Go launched at Tue Nov 10 15:00:00 -0800 PST 2009 +func ExampleDate() { + t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) + fmt.Printf("Go launched at %s\n", t.Local()) +} diff --git a/libgo/go/time/format.go b/libgo/go/time/format.go index 14b712ad086..082a51a1621 100644 --- a/libgo/go/time/format.go +++ b/libgo/go/time/format.go @@ -1,10 +1,6 @@ package time -import ( - "bytes" - "errors" - "strconv" -) +import "errors" const ( numeric = iota @@ -259,8 +255,60 @@ func lookup(tab []string, val string) (int, string, error) { return -1, val, errBad } +// Duplicates functionality in strconv, but avoids dependency. +func itoa(x int) string { + var buf [32]byte + n := len(buf) + if x == 0 { + return "0" + } + u := uint(x) + if x < 0 { + u = -u + } + for u > 0 { + n-- + buf[n] = byte(u%10 + '0') + u /= 10 + } + if x < 0 { + n-- + buf[n] = '-' + } + return string(buf[n:]) +} + +// Never printed, just needs to be non-nil for return by atoi. +var atoiError = errors.New("time: invalid number") + +// Duplicates functionality in strconv, but avoids dependency. +func atoi(s string) (x int, err error) { + i := 0 + if len(s) > 0 && s[0] == '-' { + i++ + } + if i >= len(s) { + return 0, atoiError + } + for ; i < len(s); i++ { + c := s[i] + if c < '0' || c > '9' { + return 0, atoiError + } + if x >= (1<<31-10)/10 { + // will overflow + return 0, atoiError + } + x = x*10 + int(c) - '0' + } + if s[0] == '-' { + x = -x + } + return x, nil +} + func pad(i int, padding string) string { - s := strconv.Itoa(i) + s := itoa(i) if i < 10 { s = padding + s } @@ -273,7 +321,7 @@ func zeroPad(i int) string { return pad(i, "0") } func formatNano(nanosec, n int) string { // User might give us bad data. Make sure it's positive and in range. // They'll get nonsense output but it will have the right format. - s := strconv.Uitoa(uint(nanosec) % 1e9) + s := itoa(int(uint(nanosec) % 1e9)) // Zero pad left without fmt. if len(s) < 9 { s = "000000000"[:9-len(s)] + s @@ -284,14 +332,42 @@ func formatNano(nanosec, n int) string { return "." + s[:n] } +// String returns the time formatted using the format string +// "Mon Jan _2 15:04:05 -0700 MST 2006" +func (t Time) String() string { + return t.Format("Mon Jan _2 15:04:05 -0700 MST 2006") +} + +type buffer []byte + +func (b *buffer) WriteString(s string) { + *b = append(*b, s...) +} + +func (b *buffer) WriteByte(c byte) { + *b = append(*b, c) +} + +func (b *buffer) String() string { + return string([]byte(*b)) +} + // Format returns a textual representation of the time value formatted // according to layout. The layout defines the format by showing the // representation of a standard time, which is then used to describe // the time to be formatted. Predefined layouts ANSIC, UnixDate, // RFC3339 and others describe standard representations. For more // information about the formats, see the documentation for ANSIC. -func (t *Time) Format(layout string) string { - b := new(bytes.Buffer) +func (t Time) Format(layout string) string { + var ( + year int = -1 + month Month + day int + hour int = -1 + min int + sec int + b buffer + ) // Each iteration generates one std value. for { prefix, std, suffix := nextStdChunk(layout) @@ -299,62 +375,92 @@ func (t *Time) Format(layout string) string { if std == "" { break } + + // Compute year, month, day if needed. + if year < 0 { + // Jan 01 02 2006 + if a, z := std[0], std[len(std)-1]; a == 'J' || a == 'j' || z == '1' || z == '2' || z == '6' { + year, month, day = t.Date() + } + } + + // Compute hour, minute, second if needed. + if hour < 0 { + // 03 04 05 15 pm + if z := std[len(std)-1]; z == '3' || z == '4' || z == '5' || z == 'm' || z == 'M' { + hour, min, sec = t.Clock() + } + } + var p string switch std { case stdYear: - p = zeroPad(int(t.Year % 100)) + p = zeroPad(year % 100) case stdLongYear: - p = strconv.Itoa64(t.Year) + p = itoa(year) case stdMonth: - p = shortMonthNames[t.Month] + p = month.String()[:3] case stdLongMonth: - p = longMonthNames[t.Month] + p = month.String() case stdNumMonth: - p = strconv.Itoa(t.Month) + p = itoa(int(month)) case stdZeroMonth: - p = zeroPad(t.Month) + p = zeroPad(int(month)) case stdWeekDay: - p = shortDayNames[t.Weekday()] + p = t.Weekday().String()[:3] case stdLongWeekDay: - p = longDayNames[t.Weekday()] + p = t.Weekday().String() case stdDay: - p = strconv.Itoa(t.Day) + p = itoa(day) case stdUnderDay: - p = pad(t.Day, " ") + p = pad(day, " ") case stdZeroDay: - p = zeroPad(t.Day) + p = zeroPad(day) case stdHour: - p = zeroPad(t.Hour) + p = zeroPad(hour) case stdHour12: // Noon is 12PM, midnight is 12AM. - hr := t.Hour % 12 + hr := hour % 12 if hr == 0 { hr = 12 } - p = strconv.Itoa(hr) + p = itoa(hr) case stdZeroHour12: // Noon is 12PM, midnight is 12AM. - hr := t.Hour % 12 + hr := hour % 12 if hr == 0 { hr = 12 } p = zeroPad(hr) case stdMinute: - p = strconv.Itoa(t.Minute) + p = itoa(min) case stdZeroMinute: - p = zeroPad(t.Minute) + p = zeroPad(min) case stdSecond: - p = strconv.Itoa(t.Second) + p = itoa(sec) case stdZeroSecond: - p = zeroPad(t.Second) + p = zeroPad(sec) + case stdPM: + if hour >= 12 { + p = "PM" + } else { + p = "AM" + } + case stdpm: + if hour >= 12 { + p = "pm" + } else { + p = "am" + } case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumColonTZ: // Ugly special case. We cheat and take the "Z" variants // to mean "the time zone as formatted for ISO 8601". - if t.ZoneOffset == 0 && std[0] == 'Z' { + _, offset := t.Zone() + if offset == 0 && std[0] == 'Z' { p = "Z" break } - zone := t.ZoneOffset / 60 // convert to minutes + zone := offset / 60 // convert to minutes if zone < 0 { p = "-" zone = -zone @@ -366,25 +472,14 @@ func (t *Time) Format(layout string) string { p += ":" } p += zeroPad(zone % 60) - case stdPM: - if t.Hour >= 12 { - p = "PM" - } else { - p = "AM" - } - case stdpm: - if t.Hour >= 12 { - p = "pm" - } else { - p = "am" - } case stdTZ: - if t.Zone != "" { - p = t.Zone + name, offset := t.Zone() + if name != "" { + p = name } else { // No time zone known for this time, but we must print one. // Use the -0700 format. - zone := t.ZoneOffset / 60 // convert to minutes + zone := offset / 60 // convert to minutes if zone < 0 { p = "-" zone = -zone @@ -396,7 +491,7 @@ func (t *Time) Format(layout string) string { } default: if len(std) >= 2 && std[0:2] == ".0" { - p = formatNano(t.Nanosecond, len(std)-1) + p = formatNano(t.Nanosecond(), len(std)-1) } } b.WriteString(p) @@ -405,14 +500,6 @@ func (t *Time) Format(layout string) string { return b.String() } -// String returns a Unix-style representation of the time value. -func (t *Time) String() string { - if t == nil { - return "<nil>" - } - return t.Format(UnixDate) -} - var errBad = errors.New("bad value for field") // placeholder not passed to user // ParseError describes a problem parsing a time string. @@ -424,17 +511,21 @@ type ParseError struct { Message string } -// String is the string representation of a ParseError. +func quote(s string) string { + return "\"" + s + "\"" +} + +// Error returns the string representation of a ParseError. func (e *ParseError) Error() string { if e.Message == "" { return "parsing time " + - strconv.Quote(e.Value) + " as " + - strconv.Quote(e.Layout) + ": cannot parse " + - strconv.Quote(e.ValueElem) + " as " + - strconv.Quote(e.LayoutElem) + quote(e.Value) + " as " + + quote(e.Layout) + ": cannot parse " + + quote(e.ValueElem) + " as " + + quote(e.LayoutElem) } return "parsing time " + - strconv.Quote(e.Value) + e.Message + quote(e.Value) + e.Message } // isDigit returns true if s[i] is a decimal digit, false if not or @@ -498,30 +589,42 @@ func skip(value, prefix string) (string, error) { // representations.For more information about the formats, see the // documentation for ANSIC. // -// Only those elements present in the value will be set in the returned time -// structure. Also, if the input string represents an inconsistent time -// (such as having the wrong day of the week), the returned value will also -// be inconsistent. In any case, the elements of the returned time will be -// sane: hours in 0..23, minutes in 0..59, day of month in 1..31, etc. +// Elements omitted from the value are assumed to be zero, or when +// zero is impossible, one, so parsing "3:04pm" returns the time +// corresponding to Jan 1, year 0, 15:04:00 UTC. // Years must be in the range 0000..9999. The day of the week is checked // for syntax but it is otherwise ignored. -func Parse(alayout, avalue string) (*Time, error) { - var t Time +func Parse(layout, value string) (Time, error) { + alayout, avalue := layout, value rangeErrString := "" // set if a value is out of range amSet := false // do we need to subtract 12 from the hour for midnight? pmSet := false // do we need to add 12 to the hour? - layout, value := alayout, avalue + + // Time being constructed. + var ( + year int + month int = 1 // January + day int = 1 + hour int + min int + sec int + nsec int + z *Location + zoneOffset int = -1 + zoneName string + ) + // Each iteration processes one std value. for { var err error prefix, std, suffix := nextStdChunk(layout) value, err = skip(value, prefix) if err != nil { - return nil, &ParseError{alayout, avalue, prefix, value, ""} + return Time{}, &ParseError{alayout, avalue, prefix, value, ""} } if len(std) == 0 { if len(value) != 0 { - return nil, &ParseError{alayout, avalue, "", value, ": extra text: " + value} + return Time{}, &ParseError{alayout, avalue, "", value, ": extra text: " + value} } break } @@ -534,11 +637,11 @@ func Parse(alayout, avalue string) (*Time, error) { break } p, value = value[0:2], value[2:] - t.Year, err = strconv.Atoi64(p) - if t.Year >= 69 { // Unix time starts Dec 31 1969 in some time zones - t.Year += 1900 + year, err = atoi(p) + if year >= 69 { // Unix time starts Dec 31 1969 in some time zones + year += 1900 } else { - t.Year += 2000 + year += 2000 } case stdLongYear: if len(value) < 4 || !isDigit(value, 0) { @@ -546,14 +649,14 @@ func Parse(alayout, avalue string) (*Time, error) { break } p, value = value[0:4], value[4:] - t.Year, err = strconv.Atoi64(p) + year, err = atoi(p) case stdMonth: - t.Month, value, err = lookup(shortMonthNames, value) + month, value, err = lookup(shortMonthNames, value) case stdLongMonth: - t.Month, value, err = lookup(longMonthNames, value) + month, value, err = lookup(longMonthNames, value) case stdNumMonth, stdZeroMonth: - t.Month, value, err = getnum(value, std == stdZeroMonth) - if t.Month <= 0 || 12 < t.Month { + month, value, err = getnum(value, std == stdZeroMonth) + if month <= 0 || 12 < month { rangeErrString = "month" } case stdWeekDay: @@ -565,29 +668,28 @@ func Parse(alayout, avalue string) (*Time, error) { if std == stdUnderDay && len(value) > 0 && value[0] == ' ' { value = value[1:] } - t.Day, value, err = getnum(value, std == stdZeroDay) - if t.Day < 0 || 31 < t.Day { - // TODO: be more thorough in date check? + day, value, err = getnum(value, std == stdZeroDay) + if day < 0 || 31 < day { rangeErrString = "day" } case stdHour: - t.Hour, value, err = getnum(value, false) - if t.Hour < 0 || 24 <= t.Hour { + hour, value, err = getnum(value, false) + if hour < 0 || 24 <= hour { rangeErrString = "hour" } case stdHour12, stdZeroHour12: - t.Hour, value, err = getnum(value, std == stdZeroHour12) - if t.Hour < 0 || 12 < t.Hour { + hour, value, err = getnum(value, std == stdZeroHour12) + if hour < 0 || 12 < hour { rangeErrString = "hour" } case stdMinute, stdZeroMinute: - t.Minute, value, err = getnum(value, std == stdZeroMinute) - if t.Minute < 0 || 60 <= t.Minute { + min, value, err = getnum(value, std == stdZeroMinute) + if min < 0 || 60 <= min { rangeErrString = "minute" } case stdSecond, stdZeroSecond: - t.Second, value, err = getnum(value, std == stdZeroSecond) - if t.Second < 0 || 60 <= t.Second { + sec, value, err = getnum(value, std == stdZeroSecond) + if sec < 0 || 60 <= sec { rangeErrString = "second" } // Special case: do we have a fractional second but no @@ -602,16 +704,44 @@ func Parse(alayout, avalue string) (*Time, error) { n := 2 for ; n < len(value) && isDigit(value, n); n++ { } - rangeErrString, err = t.parseNanoseconds(value, n) + nsec, rangeErrString, err = parseNanoseconds(value, n) value = value[n:] } + case stdPM: + if len(value) < 2 { + err = errBad + break + } + p, value = value[0:2], value[2:] + switch p { + case "PM": + pmSet = true + case "AM": + amSet = true + default: + err = errBad + } + case stdpm: + if len(value) < 2 { + err = errBad + break + } + p, value = value[0:2], value[2:] + switch p { + case "pm": + pmSet = true + case "am": + amSet = true + default: + err = errBad + } case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ: if std[0] == 'Z' && len(value) >= 1 && value[0] == 'Z' { value = value[1:] - t.Zone = "UTC" + z = UTC break } - var sign, hh, mm string + var sign, hour, min string if std == stdISO8601ColonTZ || std == stdNumColonTZ { if len(value) < 6 { err = errBad @@ -621,65 +751,38 @@ func Parse(alayout, avalue string) (*Time, error) { err = errBad break } - sign, hh, mm, value = value[0:1], value[1:3], value[4:6], value[6:] + sign, hour, min, value = value[0:1], value[1:3], value[4:6], value[6:] } else if std == stdNumShortTZ { if len(value) < 3 { err = errBad break } - sign, hh, mm, value = value[0:1], value[1:3], "00", value[3:] + sign, hour, min, value = value[0:1], value[1:3], "00", value[3:] } else { if len(value) < 5 { err = errBad break } - sign, hh, mm, value = value[0:1], value[1:3], value[3:5], value[5:] + sign, hour, min, value = value[0:1], value[1:3], value[3:5], value[5:] } - var hr, min int - hr, err = strconv.Atoi(hh) + var hr, mm int + hr, err = atoi(hour) if err == nil { - min, err = strconv.Atoi(mm) + mm, err = atoi(min) } - t.ZoneOffset = (hr*60 + min) * 60 // offset is in seconds + zoneOffset = (hr*60 + mm) * 60 // offset is in seconds switch sign[0] { case '+': case '-': - t.ZoneOffset = -t.ZoneOffset - default: - err = errBad - } - case stdPM: - if len(value) < 2 { - err = errBad - break - } - p, value = value[0:2], value[2:] - switch p { - case "PM": - pmSet = true - case "AM": - amSet = true - default: - err = errBad - } - case stdpm: - if len(value) < 2 { - err = errBad - break - } - p, value = value[0:2], value[2:] - switch p { - case "pm": - pmSet = true - case "am": - amSet = true + zoneOffset = -zoneOffset default: err = errBad } case stdTZ: // Does it look like a time zone? if len(value) >= 3 && value[0:3] == "UTC" { - t.Zone, value = value[0:3], value[3:] + z = UTC + value = value[3:] break } @@ -700,47 +803,86 @@ func Parse(alayout, avalue string) (*Time, error) { break } // It's a valid format. - t.Zone = p - // Can we find its offset? - if offset, found := lookupByName(p); found { - t.ZoneOffset = offset - } + zoneName = p default: if len(value) < len(std) { err = errBad break } if len(std) >= 2 && std[0:2] == ".0" { - rangeErrString, err = t.parseNanoseconds(value, len(std)) + nsec, rangeErrString, err = parseNanoseconds(value, len(std)) value = value[len(std):] } } if rangeErrString != "" { - return nil, &ParseError{alayout, avalue, std, value, ": " + rangeErrString + " out of range"} + return Time{}, &ParseError{alayout, avalue, std, value, ": " + rangeErrString + " out of range"} } if err != nil { - return nil, &ParseError{alayout, avalue, std, value, ""} + return Time{}, &ParseError{alayout, avalue, std, value, ""} + } + } + if pmSet && hour < 12 { + hour += 12 + } else if amSet && hour == 12 { + hour = 0 + } + + // TODO: be more aggressive checking day? + if z != nil { + return Date(year, Month(month), day, hour, min, sec, nsec, z), nil + } + + t := Date(year, Month(month), day, hour, min, sec, nsec, UTC) + if zoneOffset != -1 { + t.sec -= int64(zoneOffset) + + // Look for local zone with the given offset. + // If that zone was in effect at the given time, use it. + name, offset, _, _, _ := Local.lookup(t.sec + internalToUnix) + if offset == zoneOffset && (zoneName == "" || name == zoneName) { + t.loc = Local + return t, nil } + + // Otherwise create fake zone to record offset. + t.loc = FixedZone(zoneName, zoneOffset) + return t, nil } - if pmSet && t.Hour < 12 { - t.Hour += 12 - } else if amSet && t.Hour == 12 { - t.Hour = 0 + + if zoneName != "" { + // Look for local zone with the given offset. + // If that zone was in effect at the given time, use it. + offset, _, ok := Local.lookupName(zoneName) + if ok { + name, off, _, _, _ := Local.lookup(t.sec + internalToUnix - int64(offset)) + if name == zoneName && off == offset { + t.sec -= int64(offset) + t.loc = Local + return t, nil + } + } + + // Otherwise, create fake zone with unknown offset. + t.loc = FixedZone(zoneName, 0) + return t, nil } - return &t, nil + + // Otherwise, fall back to UTC. + return t, nil } -func (t *Time) parseNanoseconds(value string, nbytes int) (rangErrString string, err error) { +func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) { if value[0] != '.' { - return "", errBad + err = errBad + return } - var ns int - ns, err = strconv.Atoi(value[1:nbytes]) + ns, err = atoi(value[1:nbytes]) if err != nil { - return "", err + return } if ns < 0 || 1e9 <= ns { - return "fractional second", nil + rangeErrString = "fractional second" + return } // We need nanoseconds, which means scaling by the number // of missing digits in the format, maximum length 10. If it's @@ -749,6 +891,5 @@ func (t *Time) parseNanoseconds(value string, nbytes int) (rangErrString string, for i := 0; i < scaleDigits; i++ { ns *= 10 } - t.Nanosecond = ns return } diff --git a/libgo/go/time/internal_test.go b/libgo/go/time/internal_test.go index d7e7076539f..2c4df335f9b 100644 --- a/libgo/go/time/internal_test.go +++ b/libgo/go/time/internal_test.go @@ -6,7 +6,7 @@ package time func init() { // force US/Pacific for time zone tests - onceSetupZone.Do(setupTestingZone) + localOnce.Do(initTestingZone) } var Interrupt = interrupt diff --git a/libgo/go/time/sleep.go b/libgo/go/time/sleep.go index 314622d0dc9..1e23118f378 100644 --- a/libgo/go/time/sleep.go +++ b/libgo/go/time/sleep.go @@ -4,174 +4,89 @@ package time -import ( - "container/heap" - "sync" -) - -// The Timer type represents a single event. -// When the Timer expires, the current time will be sent on C -// unless the Timer represents an AfterFunc event. -type Timer struct { - C <-chan int64 - t int64 // The absolute time that the event should fire. - f func(int64) // The function to call when the event fires. - i int // The event's index inside eventHeap. +func nano() int64 { + sec, nsec := now() + return sec*1e9 + int64(nsec) } -type timerHeap []*Timer - -// forever is the absolute time (in ns) of an event that is forever away. -const forever = 1 << 62 - -// maxSleepTime is the maximum length of time that a sleeper -// sleeps for before checking if it is defunct. -const maxSleepTime = 1e9 - -var ( - // timerMutex guards the variables inside this var group. - timerMutex sync.Mutex - - // timers holds a binary heap of pending events, terminated with a sentinel. - timers timerHeap - - // currentSleeper is an ever-incrementing counter which represents - // the current sleeper. It allows older sleepers to detect that they are - // defunct and exit. - currentSleeper int64 -) - -func init() { - timers.Push(&Timer{t: forever}) // sentinel +// Interface to timers implemented in package runtime. +// Must be in sync with ../runtime/runtime.h:/^struct.Timer$ +type runtimeTimer struct { + i int32 + when int64 + period int64 + f func(int64, interface{}) + arg interface{} } -// NewTimer creates a new Timer that will send -// the current time on its channel after at least ns nanoseconds. -func NewTimer(ns int64) *Timer { - c := make(chan int64, 1) - e := after(ns, func(t int64) { c <- t }) - e.C = c - return e -} +func startTimer(*runtimeTimer) +func stopTimer(*runtimeTimer) bool -// After waits at least ns nanoseconds before sending the current time -// on the returned channel. -// It is equivalent to NewTimer(ns).C. -func After(ns int64) <-chan int64 { - return NewTimer(ns).C -} - -// AfterFunc waits at least ns nanoseconds before calling f -// in its own goroutine. It returns a Timer that can -// be used to cancel the call using its Stop method. -func AfterFunc(ns int64, f func()) *Timer { - return after(ns, func(_ int64) { - go f() - }) +// The Timer type represents a single event. +// When the Timer expires, the current time will be sent on C, +// unless the Timer was created by AfterFunc. +type Timer struct { + C <-chan Time + r runtimeTimer } // Stop prevents the Timer from firing. // It returns true if the call stops the timer, false if the timer has already // expired or stopped. -func (e *Timer) Stop() (ok bool) { - timerMutex.Lock() - // Avoid removing the first event in the queue so that - // we don't start a new sleeper unnecessarily. - if e.i > 0 { - heap.Remove(timers, e.i) - } - ok = e.f != nil - e.f = nil - timerMutex.Unlock() - return +func (t *Timer) Stop() (ok bool) { + return stopTimer(&t.r) } -// after is the implementation of After and AfterFunc. -// When the current time is after ns, it calls f with the current time. -// It assumes that f will not block. -func after(ns int64, f func(int64)) (e *Timer) { - now := Nanoseconds() - t := now + ns - if ns > 0 && t < now { - panic("time: time overflow") - } - timerMutex.Lock() - t0 := timers[0].t - e = &Timer{nil, t, f, -1} - heap.Push(timers, e) - // Start a new sleeper if the new event is before - // the first event in the queue. If the length of time - // until the new event is at least maxSleepTime, - // then we're guaranteed that the sleeper will wake up - // in time to service it, so no new sleeper is needed. - if t0 > t && (t0 == forever || ns < maxSleepTime) { - currentSleeper++ - go sleeper(currentSleeper) +// NewTimer creates a new Timer that will send +// the current time on its channel after at least ns nanoseconds. +func NewTimer(d Duration) *Timer { + c := make(chan Time, 1) + t := &Timer{ + C: c, + r: runtimeTimer{ + when: nano() + int64(d), + f: sendTime, + arg: c, + }, } - timerMutex.Unlock() - return + startTimer(&t.r) + return t } -// sleeper continually looks at the earliest event in the queue, waits until it happens, -// then removes any events in the queue that are due. It stops when the queue -// is empty or when another sleeper has been started. -func sleeper(sleeperId int64) { - timerMutex.Lock() - e := timers[0] - t := Nanoseconds() - for e.t != forever { - if dt := e.t - t; dt > 0 { - if dt > maxSleepTime { - dt = maxSleepTime - } - timerMutex.Unlock() - sysSleep(dt) - timerMutex.Lock() - if currentSleeper != sleeperId { - // Another sleeper has been started, making this one redundant. - break - } - } - e = timers[0] - t = Nanoseconds() - for t >= e.t { - if e.f != nil { - e.f(t) - e.f = nil - } - heap.Pop(timers) - e = timers[0] - } +func sendTime(now int64, c interface{}) { + // Non-blocking send of time on c. + // Used in NewTimer, it cannot block anyway (buffer). + // Used in NewTicker, dropping sends on the floor is + // the desired behavior when the reader gets behind, + // because the sends are periodic. + select { + case c.(chan Time) <- Unix(0, now): + default: } - timerMutex.Unlock() -} - -func (timerHeap) Len() int { - return len(timers) } -func (timerHeap) Less(i, j int) bool { - return timers[i].t < timers[j].t -} - -func (timerHeap) Swap(i, j int) { - timers[i], timers[j] = timers[j], timers[i] - timers[i].i = i - timers[j].i = j +// After waits for the duration to elapse and then sends the current time +// on the returned channel. +// It is equivalent to NewTimer(ns).C. +func After(d Duration) <-chan Time { + return NewTimer(d).C } -func (timerHeap) Push(x interface{}) { - e := x.(*Timer) - e.i = len(timers) - timers = append(timers, e) +// AfterFunc waits at least ns nanoseconds before calling f +// in its own goroutine. It returns a Timer that can +// be used to cancel the call using its Stop method. +func AfterFunc(ns int64, f func()) *Timer { + t := &Timer{ + r: runtimeTimer{ + when: nano() + ns, + f: goFunc, + arg: f, + }, + } + startTimer(&t.r) + return t } -func (timerHeap) Pop() interface{} { - // TODO: possibly shrink array. - n := len(timers) - 1 - e := timers[n] - timers[n] = nil - timers = timers[0:n] - e.i = -1 - return e +func goFunc(now int64, arg interface{}) { + go arg.(func())() } diff --git a/libgo/go/time/sleep_test.go b/libgo/go/time/sleep_test.go index 0662e3359cf..cbcc897fd40 100644 --- a/libgo/go/time/sleep_test.go +++ b/libgo/go/time/sleep_test.go @@ -7,22 +7,24 @@ package time_test import ( "errors" "fmt" + "runtime" "sort" + "sync/atomic" "testing" . "time" ) func TestSleep(t *testing.T) { - const delay = int64(100e6) + const delay = 100 * Millisecond go func() { Sleep(delay / 2) Interrupt() }() - start := Nanoseconds() + start := Now() Sleep(delay) - duration := Nanoseconds() - start + duration := Now().Sub(start) if duration < delay { - t.Fatalf("Sleep(%d) slept for only %d ns", delay, duration) + t.Fatalf("Sleep(%s) slept for only %s", delay, duration) } } @@ -47,6 +49,23 @@ func TestAfterFunc(t *testing.T) { <-c } +func TestAfterStress(t *testing.T) { + stop := uint32(0) + go func() { + for atomic.LoadUint32(&stop) == 0 { + runtime.GC() + // Need to yield, because otherwise + // the main goroutine will never set the stop flag. + runtime.Gosched() + } + }() + c := Tick(1) + for i := 0; i < 100; i++ { + <-c + } + atomic.StoreUint32(&stop, 1) +} + func BenchmarkAfterFunc(b *testing.B) { i := b.N c := make(chan bool) @@ -77,32 +96,32 @@ func BenchmarkStop(b *testing.B) { } func TestAfter(t *testing.T) { - const delay = int64(100e6) - start := Nanoseconds() + const delay = 100 * Millisecond + start := Now() end := <-After(delay) - if duration := Nanoseconds() - start; duration < delay { - t.Fatalf("After(%d) slept for only %d ns", delay, duration) + if duration := Now().Sub(start); duration < delay { + t.Fatalf("After(%s) slept for only %d ns", delay, duration) } - if min := start + delay; end < min { - t.Fatalf("After(%d) expect >= %d, got %d", delay, min, end) + if min := start.Add(delay); end.Before(min) { + t.Fatalf("After(%s) expect >= %s, got %s", delay, min, end) } } func TestAfterTick(t *testing.T) { const ( - Delta = 100 * 1e6 + Delta = 100 * Millisecond Count = 10 ) - t0 := Nanoseconds() + t0 := Now() for i := 0; i < Count; i++ { <-After(Delta) } - t1 := Nanoseconds() - ns := t1 - t0 - target := int64(Delta * Count) + t1 := Now() + d := t1.Sub(t0) + target := Delta * Count slop := target * 2 / 10 - if ns < target-slop || ns > target+slop { - t.Fatalf("%d ticks of %g ns took %g ns, expected %g", Count, float64(Delta), float64(ns), float64(target)) + if d < target-slop || d > target+slop { + t.Fatalf("%d ticks of %s took %s, expected %s", Count, Delta, d, target) } } @@ -152,38 +171,54 @@ var slots = []int{5, 3, 6, 6, 6, 1, 1, 2, 7, 9, 4, 8 /*0*/ } type afterResult struct { slot int - t int64 + t Time } -func await(slot int, result chan<- afterResult, ac <-chan int64) { +func await(slot int, result chan<- afterResult, ac <-chan Time) { result <- afterResult{slot, <-ac} } func testAfterQueuing(t *testing.T) error { const ( - Delta = 100 * 1e6 + Delta = 100 * Millisecond ) // make the result channel buffered because we don't want // to depend on channel queueing semantics that might // possibly change in the future. result := make(chan afterResult, len(slots)) - t0 := Nanoseconds() + t0 := Now() for _, slot := range slots { - go await(slot, result, After(int64(slot)*Delta)) + go await(slot, result, After(Duration(slot)*Delta)) } sort.Ints(slots) for _, slot := range slots { r := <-result if r.slot != slot { - return fmt.Errorf("after queue got slot %d, expected %d", r.slot, slot) + return fmt.Errorf("after slot %d, expected %d", r.slot, slot) } - ns := r.t - t0 - target := int64(slot * Delta) - slop := int64(Delta) / 4 - if ns < target-slop || ns > target+slop { - return fmt.Errorf("after queue slot %d arrived at %g, expected [%g,%g]", slot, float64(ns), float64(target-slop), float64(target+slop)) + dt := r.t.Sub(t0) + target := Duration(slot) * Delta + slop := Delta / 4 + if dt < target-slop || dt > target+slop { + return fmt.Errorf("After(%s) arrived at %s, expected [%s,%s]", target, dt, target-slop, target+slop) } } return nil } + +func TestTimerStopStress(t *testing.T) { + if testing.Short() { + return + } + for i := 0; i < 100; i++ { + go func(i int) { + timer := AfterFunc(2e9, func() { + t.Fatalf("timer %d was not stopped", i) + }) + Sleep(1e9) + timer.Stop() + }(i) + } + Sleep(3e9) +} diff --git a/libgo/go/time/sys.go b/libgo/go/time/sys.go index ca1d334a5b7..fe6bc27d301 100644 --- a/libgo/go/time/sys.go +++ b/libgo/go/time/sys.go @@ -4,38 +4,33 @@ package time -// Seconds reports the number of seconds since the Unix epoch, -// January 1, 1970 00:00:00 UTC. -func Seconds() int64 { - return Nanoseconds() / 1e9 -} - -// Nanoseconds is implemented by package runtime. - -// Nanoseconds reports the number of nanoseconds since the Unix epoch, -// January 1, 1970 00:00:00 UTC. -func Nanoseconds() int64 +import "syscall" -// Sleep pauses the current goroutine for at least ns nanoseconds. -// Higher resolution sleeping may be provided by syscall.Nanosleep -// on some operating systems. -func Sleep(ns int64) error { - _, err := sleep(Nanoseconds(), ns) - return err -} +// Sleep pauses the current goroutine for the duration d. +func Sleep(d Duration) -// sleep takes the current time and a duration, -// pauses for at least ns nanoseconds, and -// returns the current time and an error. -func sleep(t, ns int64) (int64, error) { - // TODO(cw): use monotonic-time once it's available - end := t + ns - for t < end { - err := sysSleep(end - t) - if err != nil { - return 0, err +// readFile reads and returns the content of the named file. +// It is a trivial implementation of ioutil.ReadFile, reimplemented +// here to avoid depending on io/ioutil or os. +func readFile(name string) ([]byte, error) { + f, err := syscall.Open(name, syscall.O_RDONLY, 0) + if err != nil { + return nil, err + } + defer syscall.Close(f) + var ( + buf [4096]byte + ret []byte + n int + ) + for { + n, err = syscall.Read(f, buf[:]) + if n > 0 { + ret = append(ret, buf[:n]...) + } + if n == 0 || err != nil { + break } - t = Nanoseconds() } - return t, nil + return ret, err } diff --git a/libgo/go/time/sys_plan9.go b/libgo/go/time/sys_plan9.go index a630b3ee030..e58fb519ea3 100644 --- a/libgo/go/time/sys_plan9.go +++ b/libgo/go/time/sys_plan9.go @@ -4,19 +4,6 @@ package time -import ( - "os" - "syscall" -) - -func sysSleep(t int64) error { - err := syscall.Sleep(t) - if err != nil { - return os.NewSyscallError("sleep", err) - } - return nil -} - // for testing: whatever interrupts a sleep func interrupt() { // cannot predict pid, don't want to kill group diff --git a/libgo/go/time/sys_unix.go b/libgo/go/time/sys_unix.go index 17a6a2d63e0..715d186be17 100644 --- a/libgo/go/time/sys_unix.go +++ b/libgo/go/time/sys_unix.go @@ -6,20 +6,9 @@ package time -import ( - "os" - "syscall" -) - -func sysSleep(t int64) error { - errno := syscall.Sleep(t) - if errno != 0 && errno != syscall.EINTR { - return os.NewSyscallError("sleep", errno) - } - return nil -} +import "syscall" // for testing: whatever interrupts a sleep func interrupt() { - syscall.Kill(os.Getpid(), syscall.SIGCHLD) + syscall.Kill(syscall.Getpid(), syscall.SIGCHLD) } diff --git a/libgo/go/time/sys_windows.go b/libgo/go/time/sys_windows.go index f9d6e89281c..8c7242f4275 100644 --- a/libgo/go/time/sys_windows.go +++ b/libgo/go/time/sys_windows.go @@ -4,19 +4,6 @@ package time -import ( - "os" - "syscall" -) - -func sysSleep(t int64) error { - errno := syscall.Sleep(t) - if errno != 0 && errno != syscall.EINTR { - return os.NewSyscallError("sleep", errno) - } - return nil -} - // for testing: whatever interrupts a sleep func interrupt() { } diff --git a/libgo/go/time/tick.go b/libgo/go/time/tick.go index 92f9eb893e4..4440c2207b3 100644 --- a/libgo/go/time/tick.go +++ b/libgo/go/time/tick.go @@ -4,174 +4,50 @@ package time -import ( - "errors" - "sync" -) +import "errors" // A Ticker holds a synchronous channel that delivers `ticks' of a clock // at intervals. type Ticker struct { - C <-chan int64 // The channel on which the ticks are delivered. - c chan<- int64 // The same channel, but the end we use. - ns int64 - shutdown chan bool // Buffered channel used to signal shutdown. - nextTick int64 - next *Ticker + C <-chan Time // The channel on which the ticks are delivered. + r runtimeTimer +} + +// NewTicker returns a new Ticker containing a channel that will send the +// time, in nanoseconds, with a period specified by the duration argument. +// It adjusts the intervals or drops ticks to make up for slow receivers. +// The duration d must be greater than zero; if not, NewTicker will panic. +func NewTicker(d Duration) *Ticker { + if d <= 0 { + panic(errors.New("non-positive interval for NewTicker")) + } + // Give the channel a 1-element time buffer. + // If the client falls behind while reading, we drop ticks + // on the floor until the client catches up. + c := make(chan Time, 1) + t := &Ticker{ + C: c, + r: runtimeTimer{ + when: nano() + int64(d), + period: int64(d), + f: sendTime, + arg: c, + }, + } + startTimer(&t.r) + return t } // Stop turns off a ticker. After Stop, no more ticks will be sent. func (t *Ticker) Stop() { - select { - case t.shutdown <- true: - // ok - default: - // Stop in progress already - } + stopTimer(&t.r) } // Tick is a convenience wrapper for NewTicker providing access to the ticking // channel only. Useful for clients that have no need to shut down the ticker. -func Tick(ns int64) <-chan int64 { - if ns <= 0 { +func Tick(d Duration) <-chan Time { + if d <= 0 { return nil } - return NewTicker(ns).C -} - -type alarmer struct { - wakeUp chan bool // wakeup signals sent/received here - wakeMeAt chan int64 - wakeTime int64 -} - -// Set alarm to go off at time ns, if not already set earlier. -func (a *alarmer) set(ns int64) { - switch { - case a.wakeTime > ns: - // Next tick we expect is too late; shut down the late runner - // and (after fallthrough) start a new wakeLoop. - close(a.wakeMeAt) - fallthrough - case a.wakeMeAt == nil: - // There's no wakeLoop, start one. - a.wakeMeAt = make(chan int64) - a.wakeUp = make(chan bool, 1) - go wakeLoop(a.wakeMeAt, a.wakeUp) - fallthrough - case a.wakeTime == 0: - // Nobody else is waiting; it's just us. - a.wakeTime = ns - a.wakeMeAt <- ns - default: - // There's already someone scheduled. - } -} - -// Channel to notify tickerLoop of new Tickers being created. -var newTicker chan *Ticker - -func startTickerLoop() { - newTicker = make(chan *Ticker) - go tickerLoop() -} - -// wakeLoop delivers ticks at scheduled times, sleeping until the right moment. -// If another, earlier Ticker is created while it sleeps, tickerLoop() will start a new -// wakeLoop and signal that this one is done by closing the wakeMeAt channel. -func wakeLoop(wakeMeAt chan int64, wakeUp chan bool) { - for wakeAt := range wakeMeAt { - Sleep(wakeAt - Nanoseconds()) - wakeUp <- true - } -} - -// A single tickerLoop serves all ticks to Tickers. It waits for two events: -// either the creation of a new Ticker or a tick from the alarm, -// signaling a time to wake up one or more Tickers. -func tickerLoop() { - // Represents the next alarm to be delivered. - var alarm alarmer - var now, wakeTime int64 - var tickers *Ticker - for { - select { - case t := <-newTicker: - // Add Ticker to list - t.next = tickers - tickers = t - // Arrange for a new alarm if this one precedes the existing one. - alarm.set(t.nextTick) - case <-alarm.wakeUp: - now = Nanoseconds() - wakeTime = now + 1e15 // very long in the future - var prev *Ticker = nil - // Scan list of tickers, delivering updates to those - // that need it and determining the next wake time. - // TODO(r): list should be sorted in time order. - for t := tickers; t != nil; t = t.next { - select { - case <-t.shutdown: - // Ticker is done; remove it from list. - if prev == nil { - tickers = t.next - } else { - prev.next = t.next - } - continue - default: - } - if t.nextTick <= now { - if len(t.c) == 0 { - // Only send if there's room. We must not block. - // The channel is allocated with a one-element - // buffer, which is sufficient: if he hasn't picked - // up the last tick, no point in sending more. - t.c <- now - } - t.nextTick += t.ns - if t.nextTick <= now { - // Still behind; advance in one big step. - t.nextTick += (now - t.nextTick + t.ns) / t.ns * t.ns - } - } - if t.nextTick < wakeTime { - wakeTime = t.nextTick - } - prev = t - } - if tickers != nil { - // Please send wakeup at earliest required time. - // If there are no tickers, don't bother. - alarm.wakeTime = wakeTime - alarm.wakeMeAt <- wakeTime - } else { - alarm.wakeTime = 0 - } - } - } -} - -var onceStartTickerLoop sync.Once - -// NewTicker returns a new Ticker containing a channel that will -// send the time, in nanoseconds, every ns nanoseconds. It adjusts the -// intervals to make up for pauses in delivery of the ticks. The value of -// ns must be greater than zero; if not, NewTicker will panic. -func NewTicker(ns int64) *Ticker { - if ns <= 0 { - panic(errors.New("non-positive interval for NewTicker")) - } - c := make(chan int64, 1) // See comment on send in tickerLoop - t := &Ticker{ - C: c, - c: c, - ns: ns, - shutdown: make(chan bool, 1), - nextTick: Nanoseconds() + ns, - } - onceStartTickerLoop.Do(startTickerLoop) - // must be run in background so global Tickers can be created - go func() { newTicker <- t }() - return t + return NewTicker(d).C } diff --git a/libgo/go/time/tick_test.go b/libgo/go/time/tick_test.go index 4dcb63956b2..36349349ce0 100644 --- a/libgo/go/time/tick_test.go +++ b/libgo/go/time/tick_test.go @@ -11,21 +11,21 @@ import ( func TestTicker(t *testing.T) { const ( - Delta = 100 * 1e6 + Delta = 100 * Millisecond Count = 10 ) ticker := NewTicker(Delta) - t0 := Nanoseconds() + t0 := Now() for i := 0; i < Count; i++ { <-ticker.C } ticker.Stop() - t1 := Nanoseconds() - ns := t1 - t0 - target := int64(Delta * Count) + t1 := Now() + dt := t1.Sub(t0) + target := Delta * Count slop := target * 2 / 10 - if ns < target-slop || ns > target+slop { - t.Fatalf("%d ticks of %g ns took %g ns, expected %g", Count, float64(Delta), float64(ns), float64(target)) + if dt < target-slop || dt > target+slop { + t.Fatalf("%d %s ticks took %s, expected [%s,%s]", Count, Delta, dt, target-slop, target+slop) } // Now test that the ticker stopped Sleep(2 * Delta) diff --git a/libgo/go/time/time.go b/libgo/go/time/time.go index 859b3167278..04ed86cf25f 100644 --- a/libgo/go/time/time.go +++ b/libgo/go/time/time.go @@ -3,11 +3,109 @@ // license that can be found in the LICENSE file. // Package time provides functionality for measuring and displaying time. +// +// The calendrical calculations always assume a Gregorian calendar. package time -// Days of the week. +// A Time represents an instant in time with nanosecond precision. +// +// Programs using times should typically store and pass them as values, +// not pointers. That is, time variables and struct fields should be of +// type time.Time, not *time.Time. +// +// Time instants can be compared using the Before, After, and Equal methods. +// The Sub method subtracts two instants, producing a Duration. +// The Add method adds a Time and a Duration, producing a Time. +// +// The zero value of type Time is January 1, year 1, 00:00:00.000000000 UTC. +// As this time is unlikely to come up in practice, the IsZero method gives +// a simple way of detecting a time that has not been initialized explicitly. +// +// Each Time has associated with it a Location, consulted when computing the +// presentation form of the time, such as in the Format, Hour, and Year methods. +// The methods Local, UTC, and In return a Time with a specific location. +// Changing the location in this way changes only the presentation; it does not +// change the instant in time being denoted and therefore does not affect the +// computations described in earlier paragraphs. +// +type Time struct { + // sec gives the number of seconds elapsed since + // January 1, year 1 00:00:00 UTC. + sec int64 + + // nsec specifies a non-negative nanosecond + // offset within the second named by Seconds. + // It must be in the range [0, 999999999]. + nsec int32 + + // loc specifies the Location that should be used to + // determine the minute, hour, month, day, and year + // that correspond to this Time. + // Only the zero Time has a nil Location. + // In that case it is interpreted to mean UTC. + loc *Location +} + +// After reports whether the time instant t is after u. +func (t Time) After(u Time) bool { + return t.sec > u.sec || t.sec == u.sec && t.nsec > u.nsec +} + +// Before reports whether the time instant t is before u. +func (t Time) Before(u Time) bool { + return t.sec < u.sec || t.sec == u.sec && t.nsec < u.nsec +} + +// Equal reports whether t and u represent the same time instant. +// Two times can be equal even if they are in different locations. +// For example, 6:00 +0200 CEST and 4:00 UTC are Equal. +// This comparison is different from using t == u, which also compares +// the locations. +func (t Time) Equal(u Time) bool { + return t.sec == u.sec && t.nsec == u.nsec +} + +// A Month specifies a month of the year (January = 1, ...). +type Month int + const ( - Sunday = iota + January Month = 1 + iota + February + March + April + May + June + July + August + September + October + November + December +) + +var months = [...]string{ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", +} + +// String returns the English name of the month ("January", "February", ...). +func (m Month) String() string { return months[m-1] } + +// A Weekday specifies a day of the week (Sunday = 0, ...). +type Weekday int + +const ( + Sunday Weekday = iota Monday Tuesday Wednesday @@ -16,224 +114,749 @@ const ( Saturday ) -// Time is the struct representing a parsed time value. -type Time struct { - Year int64 // 2006 is 2006 - Month, Day int // Jan-2 is 1, 2 - Hour, Minute, Second int // 15:04:05 is 15, 4, 5. - Nanosecond int // Fractional second. - ZoneOffset int // seconds east of UTC, e.g. -7*60*60 for -0700 - Zone string // e.g., "MST" +var days = [...]string{ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", +} + +// String returns the English name of the day ("Sunday", "Monday", ...). +func (d Weekday) String() string { return days[d] } + +// Computations on time. +// +// The zero value for a Time is defined to be +// January 1, year 1, 00:00:00.000000000 UTC +// which (1) looks like a zero, or as close as you can get in a date +// (1-1-1 00:00:00 UTC), (2) is unlikely enough to arise in practice to +// be a suitable "not set" sentinel, unlike Jan 1 1970, and (3) has a +// non-negative year even in time zones west of UTC, unlike 1-1-0 +// 00:00:00 UTC, which would be 12-31-(-1) 19:00:00 in New York. +// +// The zero Time value does not force a specific epoch for the time +// representation. For example, to use the Unix epoch internally, we +// could define that to distinguish a zero value from Jan 1 1970, that +// time would be represented by sec=-1, nsec=1e9. However, it does +// suggest a representation, namely using 1-1-1 00:00:00 UTC as the +// epoch, and that's what we do. +// +// The Add and Sub computations are oblivious to the choice of epoch. +// +// The presentation computations - year, month, minute, and so on - all +// rely heavily on division and modulus by positive constants. For +// calendrical calculations we want these divisions to round down, even +// for negative values, so that the remainder is always positive, but +// Go's division (like most hardware divison instructions) rounds to +// zero. We can still do those computations and then adjust the result +// for a negative numerator, but it's annoying to write the adjustment +// over and over. Instead, we can change to a different epoch so long +// ago that all the times we care about will be positive, and then round +// to zero and round down coincide. These presentation routines already +// have to add the zone offset, so adding the translation to the +// alternate epoch is cheap. For example, having a non-negative time t +// means that we can write +// +// sec = t % 60 +// +// instead of +// +// sec = t % 60 +// if sec < 0 { +// sec += 60 +// } +// +// everywhere. +// +// The calendar runs on an exact 400 year cycle: a 400-year calendar +// printed for 1970-2469 will apply as well to 2470-2869. Even the days +// of the week match up. It simplifies the computations to choose the +// cycle boundaries so that the exceptional years are always delayed as +// long as possible. That means choosing a year equal to 1 mod 400, so +// that the first leap year is the 4th year, the first missed leap year +// is the 100th year, and the missed missed leap year is the 400th year. +// So we'd prefer instead to print a calendar for 2001-2400 and reuse it +// for 2401-2800. +// +// Finally, it's convenient if the delta between the Unix epoch and +// long-ago epoch is representable by an int64 constant. +// +// These three considerations—choose an epoch as early as possible, that +// uses a year equal to 1 mod 400, and that is no more than 2⁶³ seconds +// earlier than 1970—bring us to the year -292277022399. We refer to +// this year as the absolute zero year, and to times measured as a uint64 +// seconds since this year as absolute times. +// +// Times measured as an int64 seconds since the year 1—the representation +// used for Time's sec field—are called internal times. +// +// Times measured as an int64 seconds since the year 1970 are called Unix +// times. +// +// It is tempting to just use the year 1 as the absolute epoch, defining +// that the routines are only valid for years >= 1. However, the +// routines would then be invalid when displaying the epoch in time zones +// west of UTC, since it is year 0. It doesn't seem tenable to say that +// printing the zero time correctly isn't supported in half the time +// zones. By comparison, it's reasonable to mishandle some times in +// the year -292277022399. +// +// All this is opaque to clients of the API and can be changed if a +// better implementation presents itself. + +const ( + // The unsigned zero year for internal calculations. + // Must be 1 mod 400, and times before it will not compute correctly, + // but otherwise can be changed at will. + absoluteZeroYear = -292277022399 + + // The year of the zero Time. + // Assumed by the unixToInternal computation below. + internalYear = 1 + + // The year of the zero Unix time. + unixYear = 1970 + + // Offsets to convert between internal and absolute or Unix times. + absoluteToInternal int64 = (absoluteZeroYear - internalYear) * 365.2425 * secondsPerDay + internalToAbsolute = -absoluteToInternal + + unixToInternal int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * secondsPerDay + internalToUnix int64 = -unixToInternal +) + +// IsZero reports whether t represents the zero time instant, +// January 1, year 1, 00:00:00 UTC. +func (t Time) IsZero() bool { + return t.sec == 0 && t.nsec == 0 +} + +// abs returns the time t as an absolute time, adjusted by the zone offset. +// It is called when computing a presentation property like Month or Hour. +func (t Time) abs() uint64 { + l := t.loc + if l == nil { + l = &utcLoc + } + // Avoid function call if we hit the local time cache. + sec := t.sec + internalToUnix + if l != &utcLoc { + if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd { + sec += int64(l.cacheZone.offset) + } else { + _, offset, _, _, _ := l.lookup(sec) + sec += int64(offset) + } + } + return uint64(sec + (unixToInternal + internalToAbsolute)) +} + +// Date returns the year, month, and day in which t occurs. +func (t Time) Date() (year int, month Month, day int) { + year, month, day, _ = t.date(true) + return +} + +// Year returns the year in which t occurs. +func (t Time) Year() int { + year, _, _, _ := t.date(false) + return year } -var nonleapyear = []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -var leapyear = []int{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +// Month returns the month of the year specified by t. +func (t Time) Month() Month { + _, month, _, _ := t.date(true) + return month +} -func months(year int64) []int { - if year%4 == 0 && (year%100 != 0 || year%400 == 0) { - return leapyear +// Day returns the day of the month specified by t. +func (t Time) Day() int { + _, _, day, _ := t.date(true) + return day +} + +// Weekday returns the day of the week specified by t. +func (t Time) Weekday() Weekday { + // January 1 of the absolute year, like January 1 of 2001, was a Monday. + sec := (t.abs() + uint64(Monday)*secondsPerDay) % secondsPerWeek + return Weekday(int(sec) / secondsPerDay) +} + +// ISOWeek returns the ISO 8601 year and week number in which t occurs. +// Week ranges from 1 to 53. Jan 01 to Jan 03 of year n might belong to +// week 52 or 53 of year n-1, and Dec 29 to Dec 31 might belong to week 1 +// of year n+1. +func (t Time) ISOWeek() (year, week int) { + year, month, day, yday := t.date(true) + wday := int(t.Weekday()+6) % 7 // weekday but Monday = 0. + const ( + Mon int = iota + Tue + Wed + Thu + Fri + Sat + Sun + ) + + // Calculate week as number of Mondays in year up to + // and including today, plus 1 because the first week is week 0. + // Putting the + 1 inside the numerator as a + 7 keeps the + // numerator from being negative, which would cause it to + // round incorrectly. + week = (yday - wday + 7) / 7 + + // The week number is now correct under the assumption + // that the first Monday of the year is in week 1. + // If Jan 1 is a Tuesday, Wednesday, or Thursday, the first Monday + // is actually in week 2. + jan1wday := (wday - yday + 7*53) % 7 + if Tue <= jan1wday && jan1wday <= Thu { + week++ } - return nonleapyear + + // If the week number is still 0, we're in early January but in + // the last week of last year. + if week == 0 { + year-- + week = 52 + // A year has 53 weeks when Jan 1 or Dec 31 is a Thursday, + // meaning Jan 1 of the next year is a Friday + // or it was a leap year and Jan 1 of the next year is a Saturday. + if jan1wday == Fri || (jan1wday == Sat && isLeap(year)) { + week++ + } + } + + // December 29 to 31 are in week 1 of next year if + // they are after the last Thursday of the year and + // December 31 is a Monday, Tuesday, or Wednesday. + if month == December && day >= 29 && wday < Thu { + if dec31wday := (wday + 31 - day) % 7; Mon <= dec31wday && dec31wday <= Wed { + year++ + week = 1 + } + } + + return +} + +// Clock returns the hour, minute, and second within the day specified by t. +func (t Time) Clock() (hour, min, sec int) { + sec = int(t.abs() % secondsPerDay) + hour = sec / secondsPerHour + sec -= hour * secondsPerHour + min = sec / secondsPerMinute + sec -= min * secondsPerMinute + return +} + +// Hour returns the hour within the day specified by t, in the range [0, 23]. +func (t Time) Hour() int { + return int(t.abs()%secondsPerDay) / secondsPerHour +} + +// Minute returns the minute offset within the hour specified by t, in the range [0, 59]. +func (t Time) Minute() int { + return int(t.abs()%secondsPerHour) / secondsPerMinute +} + +// Second returns the second offset within the minute specified by t, in the range [0, 59]. +func (t Time) Second() int { + return int(t.abs() % secondsPerMinute) } +// Nanosecond returns the nanosecond offset within the second specified by t, +// in the range [0, 999999999]. +func (t Time) Nanosecond() int { + return int(t.nsec) +} + +// A Duration represents the elapsed time between two instants +// as an int64 nanosecond count. The representation limits the +// largest representable duration to approximately 290 years. +type Duration int64 + +// Common durations. There is no definition for units of Day or larger +// to avoid confusion across daylight savings time zone transitions. const ( - secondsPerDay = 24 * 60 * 60 - daysPer400Years = 365*400 + 97 - daysPer100Years = 365*100 + 24 - daysPer4Years = 365*4 + 1 - days1970To2001 = 31*365 + 8 + Nanosecond Duration = 1 + Microsecond = 1000 * Nanosecond + Millisecond = 1000 * Microsecond + Second = 1000 * Millisecond + Minute = 60 * Second + Hour = 60 * Minute ) -// SecondsToUTC converts sec, in number of seconds since the Unix epoch, -// into a parsed Time value in the UTC time zone. -func SecondsToUTC(sec int64) *Time { - t := new(Time) +// Duration returns a string representing the duration in the form "72h3m0.5s". +// Leading zero units are omitted. As a special case, durations less than one +// second format use a smaller unit (milli-, micro-, or nanoseconds) to ensure +// that the leading digit is non-zero. The zero duration formats as 0, +// with no unit. +func (d Duration) String() string { + // Largest time is 2540400h10m10.000000000s + var buf [32]byte + w := len(buf) + + u := uint64(d) + neg := d < 0 + if neg { + u = -u + } + + if u < uint64(Second) { + // Special case: if duration is smaller than a second, + // use smaller units, like 1.2ms + var ( + prec int + unit byte + ) + switch { + case u == 0: + return "0" + case u < uint64(Microsecond): + // print nanoseconds + prec = 0 + unit = 'n' + case u < uint64(Millisecond): + // print microseconds + prec = 3 + unit = 'u' + default: + // print milliseconds + prec = 6 + unit = 'm' + } + w -= 2 + buf[w] = unit + buf[w+1] = 's' + w, u = fmtFrac(buf[:w], u, prec) + w = fmtInt(buf[:w], u) + } else { + w-- + buf[w] = 's' + + w, u = fmtFrac(buf[:w], u, 9) + + // u is now integer seconds + w = fmtInt(buf[:w], u%60) + u /= 60 + + // u is now integer minutes + if u > 0 { + w-- + buf[w] = 'm' + w = fmtInt(buf[:w], u%60) + u /= 60 + + // u is now integer hours + // Stop at hours because days can be different lengths. + if u > 0 { + w-- + buf[w] = 'h' + w = fmtInt(buf[:w], u) + } + } + } - // Split into time and day. - day := sec / secondsPerDay - sec -= day * secondsPerDay - if sec < 0 { - day-- - sec += secondsPerDay + if neg { + w-- + buf[w] = '-' } - // Time - t.Hour = int(sec / 3600) - t.Minute = int((sec / 60) % 60) - t.Second = int(sec % 60) + return string(buf[w:]) +} - // Change day from 0 = 1970 to 0 = 2001, - // to make leap year calculations easier - // (2001 begins 4-, 100-, and 400-year cycles ending in a leap year.) - day -= days1970To2001 +// fmtFrac formats the fraction of v/10**prec (e.g., ".12345") into the +// tail of buf, omitting trailing zeros. it omits the decimal +// point too when the fraction is 0. It returns the index where the +// output bytes begin and the value v/10**prec. +func fmtFrac(buf []byte, v uint64, prec int) (nw int, nv uint64) { + // Omit trailing zeros up to and including decimal point. + w := len(buf) + print := false + for i := 0; i < prec; i++ { + digit := v % 10 + print = print || digit != 0 + if print { + w-- + buf[w] = byte(digit) + '0' + } + v /= 10 + } + if print { + w-- + buf[w] = '.' + } + return w, v +} - year := int64(2001) - if day < 0 { - // Go back enough 400 year cycles to make day positive. - n := -day/daysPer400Years + 1 - year -= 400 * n - day += daysPer400Years * n +// fmtInt formats v into the tail of buf. +// It returns the index where the output begins. +func fmtInt(buf []byte, v uint64) int { + w := len(buf) + if v == 0 { + w-- + buf[w] = '0' + } else { + for v > 0 { + w-- + buf[w] = byte(v%10) + '0' + v /= 10 + } } + return w +} - // Cut off 400 year cycles. - n := day / daysPer400Years - year += 400 * n - day -= daysPer400Years * n +// Nanoseconds returns the duration as an integer nanosecond count. +func (d Duration) Nanoseconds() int64 { return int64(d) } + +// These methods return float64 because the dominant +// use case is for printing a floating point number like 1.5s, and +// a truncation to integer would make them not useful in those cases. +// Splitting the integer and fraction ourselves guarantees that +// converting the returned float64 to an integer rounds the same +// way that a pure integer conversion would have, even in cases +// where, say, float64(d.Nanoseconds())/1e9 would have rounded +// differently. + +// Seconds returns the duration as a floating point number of seconds. +func (d Duration) Seconds() float64 { + sec := d / Second + nsec := d % Second + return float64(sec) + float64(nsec)*1e-9 +} + +// Minutes returns the duration as a floating point number of minutes. +func (d Duration) Minutes() float64 { + min := d / Minute + nsec := d % Minute + return float64(min) + float64(nsec)*(1e-9/60) +} - // Cut off 100-year cycles - n = day / daysPer100Years - if n > 3 { // happens on last day of 400th year - n = 3 +// Hours returns the duration as a floating point number of hours. +func (d Duration) Hours() float64 { + hour := d / Hour + nsec := d % Hour + return float64(hour) + float64(nsec)*(1e-9/60/60) +} + +// Add returns the time t+d. +func (t Time) Add(d Duration) Time { + t.sec += int64(d / 1e9) + t.nsec += int32(d % 1e9) + if t.nsec > 1e9 { + t.sec++ + t.nsec -= 1e9 + } else if t.nsec < 0 { + t.sec-- + t.nsec += 1e9 } - year += 100 * n - day -= daysPer100Years * n + return t +} - // Cut off 4-year cycles - n = day / daysPer4Years - if n > 24 { // happens on last day of 100th year - n = 24 +// Sub returns the duration t-u. +// To compute t-d for a duration d, use t.Add(-d). +func (t Time) Sub(u Time) Duration { + return Duration(t.sec-u.sec)*Second + Duration(t.nsec-u.nsec) +} + +const ( + secondsPerMinute = 60 + secondsPerHour = 60 * 60 + secondsPerDay = 24 * secondsPerHour + secondsPerWeek = 7 * secondsPerDay + daysPer400Years = 365*400 + 97 + daysPer100Years = 365*100 + 24 + daysPer4Years = 365*4 + 1 + days1970To2001 = 31*365 + 8 +) + +// date computes the year and, only when full=true, +// the month and day in which t occurs. +func (t Time) date(full bool) (year int, month Month, day int, yday int) { + // Split into time and day. + d := t.abs() / secondsPerDay + + // Account for 400 year cycles. + n := d / daysPer400Years + y := 400 * n + d -= daysPer400Years * n + + // Cut off 100-year cycles. + // The last cycle has one extra leap year, so on the last day + // of that year, day / daysPer100Years will be 4 instead of 3. + // Cut it back down to 3 by subtracting n>>2. + n = d / daysPer100Years + n -= n >> 2 + y += 100 * n + d -= daysPer100Years * n + + // Cut off 4-year cycles. + // The last cycle has a missing leap year, which does not + // affect the computation. + n = d / daysPer4Years + y += 4 * n + d -= daysPer4Years * n + + // Cut off years within a 4-year cycle. + // The last year is a leap year, so on the last day of that year, + // day / 365 will be 4 instead of 3. Cut it back down to 3 + // by subtracting n>>2. + n = d / 365 + n -= n >> 2 + y += n + d -= 365 * n + + year = int(int64(y) + absoluteZeroYear) + yday = int(d) + + if !full { + return + } + + day = yday + if isLeap(year) { + // Leap year + switch { + case day > 31+29-1: + // After leap day; pretend it wasn't there. + day-- + case day == 31+29-1: + // Leap day. + month = February + day = 29 + return + } } - year += 4 * n - day -= daysPer4Years * n - // Cut off non-leap years. - n = day / 365 - if n > 3 { // happens on last day of 4th year - n = 3 + // Estimate month on assumption that every month has 31 days. + // The estimate may be too low by at most one month, so adjust. + month = Month(day / 31) + end := int(daysBefore[month+1]) + var begin int + if day >= end { + month++ + begin = end + } else { + begin = int(daysBefore[month]) } - year += n - day -= 365 * n - t.Year = year + month++ // because January is 1 + day = day - begin + 1 + return +} - // If someone ever needs yearday, - // tyearday = day (+1?) +// daysBefore[m] counts the number of days in a non-leap year +// before month m begins. There is an entry for m=12, counting +// the number of days before January of next year (365). +var daysBefore = [...]int32{ + 0, + 31, + 31 + 28, + 31 + 28 + 31, + 31 + 28 + 31 + 30, + 31 + 28 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, +} - months := months(year) - var m int - yday := int(day) - for m = 0; m < 12 && yday >= months[m]; m++ { - yday -= months[m] +func daysIn(m Month, year int) int { + if m == February && isLeap(year) { + return 29 } - t.Month = m + 1 - t.Day = yday + 1 - t.Zone = "UTC" + return int(daysBefore[m+1] - daysBefore[m]) +} - return t +// Provided by package runtime. +func now() (sec int64, nsec int32) + +// Now returns the current local time. +func Now() Time { + sec, nsec := now() + return Time{sec + unixToInternal, nsec, Local} } -// NanosecondsToUTC converts nsec, in number of nanoseconds since the Unix epoch, -// into a parsed Time value in the UTC time zone. -func NanosecondsToUTC(nsec int64) *Time { - // This one calls SecondsToUTC rather than the other way around because - // that admits a much larger span of time; NanosecondsToUTC is limited - // to a few hundred years only. - t := SecondsToUTC(nsec / 1e9) - t.Nanosecond = int(nsec % 1e9) +// UTC returns t with the location set to UTC. +func (t Time) UTC() Time { + t.loc = UTC return t } -// UTC returns the current time as a parsed Time value in the UTC time zone. -func UTC() *Time { return NanosecondsToUTC(Nanoseconds()) } - -// SecondsToLocalTime converts sec, in number of seconds since the Unix epoch, -// into a parsed Time value in the local time zone. -func SecondsToLocalTime(sec int64) *Time { - z, offset := lookupTimezone(sec) - t := SecondsToUTC(sec + int64(offset)) - t.Zone = z - t.ZoneOffset = offset +// Local returns t with the location set to local time. +func (t Time) Local() Time { + t.loc = Local return t } -// NanosecondsToLocalTime converts nsec, in number of nanoseconds since the Unix epoch, -// into a parsed Time value in the local time zone. -func NanosecondsToLocalTime(nsec int64) *Time { - t := SecondsToLocalTime(nsec / 1e9) - t.Nanosecond = int(nsec % 1e9) +// In returns t with the location information set to loc. +// +// In panics if loc is nil. +func (t Time) In(loc *Location) Time { + if loc == nil { + panic("time: missing Location in call to Time.In") + } + t.loc = loc return t } -// LocalTime returns the current time as a parsed Time value in the local time zone. -func LocalTime() *Time { return NanosecondsToLocalTime(Nanoseconds()) } +// Location returns the time zone information associated with t. +func (t Time) Location() *Location { + l := t.loc + if l == nil { + l = UTC + } + return l +} + +// Zone computes the time zone in effect at time t, returning the abbreviated +// name of the zone (such as "CET") and its offset in seconds east of UTC. +func (t Time) Zone() (name string, offset int) { + name, offset, _, _, _ = t.loc.lookup(t.sec + internalToUnix) + return +} + +// Unix returns the Unix time, the number of seconds elapsed +// since January 1, 1970 UTC. +func (t Time) Unix() int64 { + return t.sec + internalToUnix +} -// Seconds returns the number of seconds since January 1, 1970 represented by the -// parsed Time value. -func (t *Time) Seconds() int64 { - // First, accumulate days since January 1, 2001. - // Using 2001 instead of 1970 makes the leap-year - // handling easier (see SecondsToUTC), because - // it is at the beginning of the 4-, 100-, and 400-year cycles. - day := int64(0) +// UnixNano returns the Unix time, the number of nanoseconds elapsed +// since January 1, 1970 UTC. +func (t Time) UnixNano() int64 { + return (t.sec+internalToUnix)*1e9 + int64(t.nsec) +} - // Rewrite year to be >= 2001. - year := t.Year - if year < 2001 { - n := (2001-year)/400 + 1 - year += 400 * n - day -= daysPer400Years * n +// Unix returns the local Time corresponding to the given Unix time, +// sec seconds and nsec nanoseconds since January 1, 1970 UTC. +// It is valid to pass nsec outside the range [0, 999999999]. +func Unix(sec int64, nsec int64) Time { + if nsec < 0 || nsec >= 1e9 { + n := nsec / 1e9 + sec += n + nsec -= n * 1e9 + if nsec < 0 { + nsec += 1e9 + sec-- + } } + return Time{sec + unixToInternal, int32(nsec), Local} +} + +func isLeap(year int) bool { + return year%4 == 0 && (year%100 != 0 || year%400 == 0) +} + +// norm returns nhi, nlo such that +// hi * base + lo == nhi * base + nlo +// 0 <= nlo < base +func norm(hi, lo, base int) (nhi, nlo int) { + if lo < 0 { + n := (-lo-1)/base + 1 + hi -= n + lo += n * base + } + if lo >= base { + n := lo / base + hi += n + lo -= n * base + } + return hi, lo +} + +// Date returns the Time corresponding to +// yyyy-mm-dd hh:mm:ss + nsec nanoseconds +// in the appropriate zone for that time in the given location. +// +// The month, day, hour, min, sec, and nsec values may be outside +// their usual ranges and will be normalized during the conversion. +// For example, October 32 converts to November 1. +// +// A daylight savings time transition skips or repeats times. +// For example, in the United States, March 13, 2011 2:15am never occurred, +// while November 6, 2011 1:15am occurred twice. In such cases, the +// choice of time zone, and therefore the time, is not well-defined. +// Date returns a time that is correct in one of the two zones involved +// in the transition, but it does not guarantee which. +// +// Date panics if loc is nil. +func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time { + if loc == nil { + panic("time: missing Location in call to Date") + } + + // Normalize month, overflowing into year. + m := int(month) - 1 + year, m = norm(year, m, 12) + month = Month(m) + 1 + + // Normalize nsec, sec, min, hour, overflowing into day. + sec, nsec = norm(sec, nsec, 1e9) + min, sec = norm(min, sec, 60) + hour, min = norm(hour, min, 60) + day, hour = norm(day, hour, 24) + + y := uint64(int64(year) - absoluteZeroYear) + + // Compute days since the absolute epoch. // Add in days from 400-year cycles. - n := (year - 2001) / 400 - year -= 400 * n - day += daysPer400Years * n + n := y / 400 + y -= 400 * n + d := daysPer400Years * n // Add in 100-year cycles. - n = (year - 2001) / 100 - year -= 100 * n - day += daysPer100Years * n + n = y / 100 + y -= 100 * n + d += daysPer100Years * n // Add in 4-year cycles. - n = (year - 2001) / 4 - year -= 4 * n - day += daysPer4Years * n + n = y / 4 + y -= 4 * n + d += daysPer4Years * n // Add in non-leap years. - n = year - 2001 - day += 365 * n + n = y + d += 365 * n - // Add in days this year. - months := months(t.Year) - for m := 0; m < t.Month-1; m++ { - day += int64(months[m]) + // Add in days before this month. + d += uint64(daysBefore[month-1]) + if isLeap(year) && month >= March { + d++ // February 29 } - day += int64(t.Day - 1) - // Convert days to seconds since January 1, 2001. - sec := day * secondsPerDay + // Add in days before today. + d += uint64(day - 1) // Add in time elapsed today. - sec += int64(t.Hour) * 3600 - sec += int64(t.Minute) * 60 - sec += int64(t.Second) - - // Convert from seconds since 2001 to seconds since 1970. - sec += days1970To2001 * secondsPerDay - - // Account for local time zone. - sec -= int64(t.ZoneOffset) - return sec -} - -// Nanoseconds returns the number of nanoseconds since January 1, 1970 represented by the -// parsed Time value. -func (t *Time) Nanoseconds() int64 { - return t.Seconds()*1e9 + int64(t.Nanosecond) -} - -// Weekday returns the time's day of the week. Sunday is day 0. -func (t *Time) Weekday() int { - sec := t.Seconds() + int64(t.ZoneOffset) - day := sec / secondsPerDay - sec -= day * secondsPerDay - if sec < 0 { - day-- - } - // Day 0 = January 1, 1970 was a Thursday - weekday := int((day + Thursday) % 7) - if weekday < 0 { - weekday += 7 + abs := d * secondsPerDay + abs += uint64(hour*secondsPerHour + min*secondsPerMinute + sec) + + unix := int64(abs) + (absoluteToInternal + internalToUnix) + + // Look for zone offset for t, so we can adjust to UTC. + // The lookup function expects UTC, so we pass t in the + // hope that it will not be too close to a zone transition, + // and then adjust if it is. + _, offset, _, start, end := loc.lookup(unix) + if offset != 0 { + switch utc := unix - int64(offset); { + case utc < start: + _, offset, _, _, _ = loc.lookup(start - 1) + case utc >= end: + _, offset, _, _, _ = loc.lookup(end) + } + unix -= int64(offset) } - return weekday + + return Time{unix + unixToInternal, int32(nsec), loc} } diff --git a/libgo/go/time/time_test.go b/libgo/go/time/time_test.go index 8b373a13bc2..9590e281a66 100644 --- a/libgo/go/time/time_test.go +++ b/libgo/go/time/time_test.go @@ -16,73 +16,89 @@ import ( // won't be. The purpose of this test is to at least explain why some of // the subsequent tests fail. func TestZoneData(t *testing.T) { - lt := LocalTime() + lt := Now() // PST is 8 hours west, PDT is 7 hours west. We could use the name but it's not unique. - if off := lt.ZoneOffset; off != -8*60*60 && off != -7*60*60 { - t.Errorf("Unable to find US Pacific time zone data for testing; time zone is %q offset %d", lt.Zone, off) + if name, off := lt.Zone(); off != -8*60*60 && off != -7*60*60 { + t.Errorf("Unable to find US Pacific time zone data for testing; time zone is %q offset %d", name, off) t.Error("Likely problem: the time zone files have not been installed.") } } +// parsedTime is the struct representing a parsed time value. +type parsedTime struct { + Year int + Month Month + Day int + Hour, Minute, Second int // 15:04:05 is 15, 4, 5. + Nanosecond int // Fractional second. + Weekday Weekday + ZoneOffset int // seconds east of UTC, e.g. -7*60*60 for -0700 + Zone string // e.g., "MST" +} + type TimeTest struct { seconds int64 - golden Time + golden parsedTime } var utctests = []TimeTest{ - {0, Time{1970, 1, 1, 0, 0, 0, 0, 0, "UTC"}}, - {1221681866, Time{2008, 9, 17, 20, 4, 26, 0, 0, "UTC"}}, - {-1221681866, Time{1931, 4, 16, 3, 55, 34, 0, 0, "UTC"}}, - {-11644473600, Time{1601, 1, 1, 0, 0, 0, 0, 0, "UTC"}}, - {599529660, Time{1988, 12, 31, 0, 1, 0, 0, 0, "UTC"}}, - {978220860, Time{2000, 12, 31, 0, 1, 0, 0, 0, "UTC"}}, - {1e18, Time{31688740476, 10, 23, 1, 46, 40, 0, 0, "UTC"}}, - {-1e18, Time{-31688736537, 3, 10, 22, 13, 20, 0, 0, "UTC"}}, - {0x7fffffffffffffff, Time{292277026596, 12, 4, 15, 30, 7, 0, 0, "UTC"}}, - {-0x8000000000000000, Time{-292277022657, 1, 27, 8, 29, 52, 0, 0, "UTC"}}, + {0, parsedTime{1970, January, 1, 0, 0, 0, 0, Thursday, 0, "UTC"}}, + {1221681866, parsedTime{2008, September, 17, 20, 4, 26, 0, Wednesday, 0, "UTC"}}, + {-1221681866, parsedTime{1931, April, 16, 3, 55, 34, 0, Thursday, 0, "UTC"}}, + {-11644473600, parsedTime{1601, January, 1, 0, 0, 0, 0, Monday, 0, "UTC"}}, + {599529660, parsedTime{1988, December, 31, 0, 1, 0, 0, Saturday, 0, "UTC"}}, + {978220860, parsedTime{2000, December, 31, 0, 1, 0, 0, Sunday, 0, "UTC"}}, } var nanoutctests = []TimeTest{ - {0, Time{1970, 1, 1, 0, 0, 0, 1e8, 0, "UTC"}}, - {1221681866, Time{2008, 9, 17, 20, 4, 26, 2e8, 0, "UTC"}}, + {0, parsedTime{1970, January, 1, 0, 0, 0, 1e8, Thursday, 0, "UTC"}}, + {1221681866, parsedTime{2008, September, 17, 20, 4, 26, 2e8, Wednesday, 0, "UTC"}}, } var localtests = []TimeTest{ - {0, Time{1969, 12, 31, 16, 0, 0, 0, -8 * 60 * 60, "PST"}}, - {1221681866, Time{2008, 9, 17, 13, 4, 26, 0, -7 * 60 * 60, "PDT"}}, + {0, parsedTime{1969, December, 31, 16, 0, 0, 0, Wednesday, -8 * 60 * 60, "PST"}}, + {1221681866, parsedTime{2008, September, 17, 13, 4, 26, 0, Wednesday, -7 * 60 * 60, "PDT"}}, } var nanolocaltests = []TimeTest{ - {0, Time{1969, 12, 31, 16, 0, 0, 1e8, -8 * 60 * 60, "PST"}}, - {1221681866, Time{2008, 9, 17, 13, 4, 26, 3e8, -7 * 60 * 60, "PDT"}}, -} - -func same(t, u *Time) bool { - return t.Year == u.Year && - t.Month == u.Month && - t.Day == u.Day && - t.Hour == u.Hour && - t.Minute == u.Minute && - t.Second == u.Second && - t.Nanosecond == u.Nanosecond && - t.Weekday() == u.Weekday() && - t.ZoneOffset == u.ZoneOffset && - t.Zone == u.Zone + {0, parsedTime{1969, December, 31, 16, 0, 0, 1e8, Wednesday, -8 * 60 * 60, "PST"}}, + {1221681866, parsedTime{2008, September, 17, 13, 4, 26, 3e8, Wednesday, -7 * 60 * 60, "PDT"}}, +} + +func same(t Time, u *parsedTime) bool { + // Check aggregates. + year, month, day := t.Date() + hour, min, sec := t.Clock() + name, offset := t.Zone() + if year != u.Year || month != u.Month || day != u.Day || + hour != u.Hour || min != u.Minute || sec != u.Second || + name != u.Zone || offset != u.ZoneOffset { + return false + } + // Check individual entries. + return t.Year() == u.Year && + t.Month() == u.Month && + t.Day() == u.Day && + t.Hour() == u.Hour && + t.Minute() == u.Minute && + t.Second() == u.Second && + t.Nanosecond() == u.Nanosecond && + t.Weekday() == u.Weekday } func TestSecondsToUTC(t *testing.T) { for _, test := range utctests { sec := test.seconds golden := &test.golden - tm := SecondsToUTC(sec) - newsec := tm.Seconds() + tm := Unix(sec, 0).UTC() + newsec := tm.Unix() if newsec != sec { t.Errorf("SecondsToUTC(%d).Seconds() = %d", sec, newsec) } if !same(tm, golden) { - t.Errorf("SecondsToUTC(%d):", sec) + t.Errorf("SecondsToUTC(%d): // %#v", sec, tm) t.Errorf(" want=%+v", *golden) - t.Errorf(" have=%+v", *tm) + t.Errorf(" have=%v", tm.Format(RFC3339+" MST")) } } } @@ -91,15 +107,15 @@ func TestNanosecondsToUTC(t *testing.T) { for _, test := range nanoutctests { golden := &test.golden nsec := test.seconds*1e9 + int64(golden.Nanosecond) - tm := NanosecondsToUTC(nsec) - newnsec := tm.Nanoseconds() + tm := Unix(0, nsec).UTC() + newnsec := tm.Unix()*1e9 + int64(tm.Nanosecond()) if newnsec != nsec { t.Errorf("NanosecondsToUTC(%d).Nanoseconds() = %d", nsec, newnsec) } if !same(tm, golden) { t.Errorf("NanosecondsToUTC(%d):", nsec) t.Errorf(" want=%+v", *golden) - t.Errorf(" have=%+v", *tm) + t.Errorf(" have=%+v", tm.Format(RFC3339+" MST")) } } } @@ -108,38 +124,38 @@ func TestSecondsToLocalTime(t *testing.T) { for _, test := range localtests { sec := test.seconds golden := &test.golden - tm := SecondsToLocalTime(sec) - newsec := tm.Seconds() + tm := Unix(sec, 0) + newsec := tm.Unix() if newsec != sec { t.Errorf("SecondsToLocalTime(%d).Seconds() = %d", sec, newsec) } if !same(tm, golden) { t.Errorf("SecondsToLocalTime(%d):", sec) t.Errorf(" want=%+v", *golden) - t.Errorf(" have=%+v", *tm) + t.Errorf(" have=%+v", tm.Format(RFC3339+" MST")) } } } -func TestNanoecondsToLocalTime(t *testing.T) { +func TestNanosecondsToLocalTime(t *testing.T) { for _, test := range nanolocaltests { golden := &test.golden nsec := test.seconds*1e9 + int64(golden.Nanosecond) - tm := NanosecondsToLocalTime(nsec) - newnsec := tm.Nanoseconds() + tm := Unix(0, nsec) + newnsec := tm.Unix()*1e9 + int64(tm.Nanosecond()) if newnsec != nsec { t.Errorf("NanosecondsToLocalTime(%d).Seconds() = %d", nsec, newnsec) } if !same(tm, golden) { t.Errorf("NanosecondsToLocalTime(%d):", nsec) t.Errorf(" want=%+v", *golden) - t.Errorf(" have=%+v", *tm) + t.Errorf(" have=%+v", tm.Format(RFC3339+" MST")) } } } func TestSecondsToUTCAndBack(t *testing.T) { - f := func(sec int64) bool { return SecondsToUTC(sec).Seconds() == sec } + f := func(sec int64) bool { return Unix(sec, 0).UTC().Unix() == sec } f32 := func(sec int32) bool { return f(int64(sec)) } cfg := &quick.Config{MaxCount: 10000} @@ -153,7 +169,11 @@ func TestSecondsToUTCAndBack(t *testing.T) { } func TestNanosecondsToUTCAndBack(t *testing.T) { - f := func(nsec int64) bool { return NanosecondsToUTC(nsec).Nanoseconds() == nsec } + f := func(nsec int64) bool { + t := Unix(0, nsec).UTC() + ns := t.Unix()*1e9 + int64(t.Nanosecond()) + return ns == nsec + } f32 := func(nsec int32) bool { return f(int64(nsec)) } cfg := &quick.Config{MaxCount: 10000} @@ -173,9 +193,9 @@ type TimeFormatTest struct { } var rfc3339Formats = []TimeFormatTest{ - {Time{2008, 9, 17, 20, 4, 26, 0, 0, "UTC"}, "2008-09-17T20:04:26Z"}, - {Time{1994, 9, 17, 20, 4, 26, 0, -18000, "EST"}, "1994-09-17T20:04:26-05:00"}, - {Time{2000, 12, 26, 1, 15, 6, 0, 15600, "OTO"}, "2000-12-26T01:15:06+04:20"}, + {Date(2008, 9, 17, 20, 4, 26, 0, UTC), "2008-09-17T20:04:26Z"}, + {Date(1994, 9, 17, 20, 4, 26, 0, FixedZone("EST", -18000)), "1994-09-17T20:04:26-05:00"}, + {Date(2000, 12, 26, 1, 15, 6, 0, FixedZone("OTO", 15600)), "2000-12-26T01:15:06+04:20"}, } func TestRFC3339Conversion(t *testing.T) { @@ -216,7 +236,7 @@ var formatTests = []FormatTest{ func TestFormat(t *testing.T) { // The numeric time represents Thu Feb 4 21:00:57.012345678 PST 2010 - time := NanosecondsToLocalTime(1233810057012345678) + time := Unix(0, 1233810057012345678) for _, test := range formatTests { result := time.Format(test.format) if result != test.result { @@ -229,10 +249,10 @@ type ParseTest struct { name string format string value string - hasTZ bool // contains a time zone - hasWD bool // contains a weekday - yearSign int64 // sign of year - fracDigits int // number of digits of fractional second + hasTZ bool // contains a time zone + hasWD bool // contains a weekday + yearSign int // sign of year + fracDigits int // number of digits of fractional second } var parseTests = []ParseTest{ @@ -298,47 +318,48 @@ func TestRubyParse(t *testing.T) { } } -func checkTime(time *Time, test *ParseTest, t *testing.T) { +func checkTime(time Time, test *ParseTest, t *testing.T) { // The time should be Thu Feb 4 21:00:57 PST 2010 - if test.yearSign*time.Year != 2010 { - t.Errorf("%s: bad year: %d not %d", test.name, time.Year, 2010) + if test.yearSign*time.Year() != 2010 { + t.Errorf("%s: bad year: %d not %d", test.name, time.Year(), 2010) } - if time.Month != 2 { - t.Errorf("%s: bad month: %d not %d", test.name, time.Month, 2) + if time.Month() != February { + t.Errorf("%s: bad month: %s not %s", test.name, time.Month(), February) } - if time.Day != 4 { - t.Errorf("%s: bad day: %d not %d", test.name, time.Day, 4) + if time.Day() != 4 { + t.Errorf("%s: bad day: %d not %d", test.name, time.Day(), 4) } - if time.Hour != 21 { - t.Errorf("%s: bad hour: %d not %d", test.name, time.Hour, 21) + if time.Hour() != 21 { + t.Errorf("%s: bad hour: %d not %d", test.name, time.Hour(), 21) } - if time.Minute != 0 { - t.Errorf("%s: bad minute: %d not %d", test.name, time.Minute, 0) + if time.Minute() != 0 { + t.Errorf("%s: bad minute: %d not %d", test.name, time.Minute(), 0) } - if time.Second != 57 { - t.Errorf("%s: bad second: %d not %d", test.name, time.Second, 57) + if time.Second() != 57 { + t.Errorf("%s: bad second: %d not %d", test.name, time.Second(), 57) } // Nanoseconds must be checked against the precision of the input. nanosec, err := strconv.Atoui("012345678"[:test.fracDigits] + "000000000"[:9-test.fracDigits]) if err != nil { panic(err) } - if time.Nanosecond != int(nanosec) { - t.Errorf("%s: bad nanosecond: %d not %d", test.name, time.Nanosecond, nanosec) + if time.Nanosecond() != int(nanosec) { + t.Errorf("%s: bad nanosecond: %d not %d", test.name, time.Nanosecond(), nanosec) } - if test.hasTZ && time.ZoneOffset != -28800 { - t.Errorf("%s: bad tz offset: %d not %d", test.name, time.ZoneOffset, -28800) + name, offset := time.Zone() + if test.hasTZ && offset != -28800 { + t.Errorf("%s: bad tz offset: %s %d not %d", test.name, name, offset, -28800) } - if test.hasWD && time.Weekday() != 4 { - t.Errorf("%s: bad weekday: %d not %d", test.name, time.Weekday(), 4) + if test.hasWD && time.Weekday() != Thursday { + t.Errorf("%s: bad weekday: %s not %s", test.name, time.Weekday(), Thursday) } } func TestFormatAndParse(t *testing.T) { const fmt = "Mon MST " + RFC3339 // all fields f := func(sec int64) bool { - t1 := SecondsToLocalTime(sec) - if t1.Year < 1000 || t1.Year > 9999 { + t1 := Unix(sec, 0) + if t1.Year() < 1000 || t1.Year() > 9999 { // not required to work return true } @@ -347,8 +368,8 @@ func TestFormatAndParse(t *testing.T) { t.Errorf("error: %s", err) return false } - if !same(t1, t2) { - t.Errorf("different: %q %q", t1, t2) + if t1.Unix() != t2.Unix() || t1.Nanosecond() != t2.Nanosecond() { + t.Errorf("FormatAndParse %d: %q(%d) %q(%d)", sec, t1, t1.Unix(), t2, t2.Unix()) return false } return true @@ -394,7 +415,7 @@ func TestParseErrors(t *testing.T) { } func TestNoonIs12PM(t *testing.T) { - noon := Time{Hour: 12} + noon := Date(0, January, 1, 12, 0, 0, 0, UTC) const expect = "12:00PM" got := noon.Format("3:04PM") if got != expect { @@ -407,7 +428,7 @@ func TestNoonIs12PM(t *testing.T) { } func TestMidnightIs12AM(t *testing.T) { - midnight := Time{Hour: 0} + midnight := Date(0, January, 1, 0, 0, 0, 0, UTC) expect := "12:00AM" got := midnight.Format("3:04PM") if got != expect { @@ -424,15 +445,15 @@ func Test12PMIsNoon(t *testing.T) { if err != nil { t.Fatal("error parsing date:", err) } - if noon.Hour != 12 { - t.Errorf("got %d; expect 12", noon.Hour) + if noon.Hour() != 12 { + t.Errorf("got %d; expect 12", noon.Hour()) } noon, err = Parse("03:04PM", "12:00PM") if err != nil { t.Fatal("error parsing date:", err) } - if noon.Hour != 12 { - t.Errorf("got %d; expect 12", noon.Hour) + if noon.Hour() != 12 { + t.Errorf("got %d; expect 12", noon.Hour()) } } @@ -441,15 +462,15 @@ func Test12AMIsMidnight(t *testing.T) { if err != nil { t.Fatal("error parsing date:", err) } - if midnight.Hour != 0 { - t.Errorf("got %d; expect 0", midnight.Hour) + if midnight.Hour() != 0 { + t.Errorf("got %d; expect 0", midnight.Hour()) } midnight, err = Parse("03:04PM", "12:00AM") if err != nil { t.Fatal("error parsing date:", err) } - if midnight.Hour != 0 { - t.Errorf("got %d; expect 0", midnight.Hour) + if midnight.Hour() != 0 { + t.Errorf("got %d; expect 0", midnight.Hour()) } } @@ -463,7 +484,7 @@ func TestMissingZone(t *testing.T) { expect := "Thu Feb 2 16:10:03 -0500 2006" // -0500 not EST str := time.Format(UnixDate) // uses MST as its time zone if str != expect { - t.Errorf("expected %q got %q", expect, str) + t.Errorf("got %s; expect %s", str, expect) } } @@ -473,25 +494,152 @@ func TestMinutesInTimeZone(t *testing.T) { t.Fatal("error parsing date:", err) } expected := (1*60 + 23) * 60 - if time.ZoneOffset != expected { - t.Errorf("ZoneOffset incorrect, expected %d got %d", expected, time.ZoneOffset) + _, offset := time.Zone() + if offset != expected { + t.Errorf("ZoneOffset = %d, want %d", offset, expected) + } +} + +type ISOWeekTest struct { + year int // year + month, day int // month and day + yex int // expected year + wex int // expected week +} + +var isoWeekTests = []ISOWeekTest{ + {1981, 1, 1, 1981, 1}, {1982, 1, 1, 1981, 53}, {1983, 1, 1, 1982, 52}, + {1984, 1, 1, 1983, 52}, {1985, 1, 1, 1985, 1}, {1986, 1, 1, 1986, 1}, + {1987, 1, 1, 1987, 1}, {1988, 1, 1, 1987, 53}, {1989, 1, 1, 1988, 52}, + {1990, 1, 1, 1990, 1}, {1991, 1, 1, 1991, 1}, {1992, 1, 1, 1992, 1}, + {1993, 1, 1, 1992, 53}, {1994, 1, 1, 1993, 52}, {1995, 1, 2, 1995, 1}, + {1996, 1, 1, 1996, 1}, {1996, 1, 7, 1996, 1}, {1996, 1, 8, 1996, 2}, + {1997, 1, 1, 1997, 1}, {1998, 1, 1, 1998, 1}, {1999, 1, 1, 1998, 53}, + {2000, 1, 1, 1999, 52}, {2001, 1, 1, 2001, 1}, {2002, 1, 1, 2002, 1}, + {2003, 1, 1, 2003, 1}, {2004, 1, 1, 2004, 1}, {2005, 1, 1, 2004, 53}, + {2006, 1, 1, 2005, 52}, {2007, 1, 1, 2007, 1}, {2008, 1, 1, 2008, 1}, + {2009, 1, 1, 2009, 1}, {2010, 1, 1, 2009, 53}, {2010, 1, 1, 2009, 53}, + {2011, 1, 1, 2010, 52}, {2011, 1, 2, 2010, 52}, {2011, 1, 3, 2011, 1}, + {2011, 1, 4, 2011, 1}, {2011, 1, 5, 2011, 1}, {2011, 1, 6, 2011, 1}, + {2011, 1, 7, 2011, 1}, {2011, 1, 8, 2011, 1}, {2011, 1, 9, 2011, 1}, + {2011, 1, 10, 2011, 2}, {2011, 1, 11, 2011, 2}, {2011, 6, 12, 2011, 23}, + {2011, 6, 13, 2011, 24}, {2011, 12, 25, 2011, 51}, {2011, 12, 26, 2011, 52}, + {2011, 12, 27, 2011, 52}, {2011, 12, 28, 2011, 52}, {2011, 12, 29, 2011, 52}, + {2011, 12, 30, 2011, 52}, {2011, 12, 31, 2011, 52}, {1995, 1, 1, 1994, 52}, + {2012, 1, 1, 2011, 52}, {2012, 1, 2, 2012, 1}, {2012, 1, 8, 2012, 1}, + {2012, 1, 9, 2012, 2}, {2012, 12, 23, 2012, 51}, {2012, 12, 24, 2012, 52}, + {2012, 12, 30, 2012, 52}, {2012, 12, 31, 2013, 1}, {2013, 1, 1, 2013, 1}, + {2013, 1, 6, 2013, 1}, {2013, 1, 7, 2013, 2}, {2013, 12, 22, 2013, 51}, + {2013, 12, 23, 2013, 52}, {2013, 12, 29, 2013, 52}, {2013, 12, 30, 2014, 1}, + {2014, 1, 1, 2014, 1}, {2014, 1, 5, 2014, 1}, {2014, 1, 6, 2014, 2}, + {2015, 1, 1, 2015, 1}, {2016, 1, 1, 2015, 53}, {2017, 1, 1, 2016, 52}, + {2018, 1, 1, 2018, 1}, {2019, 1, 1, 2019, 1}, {2020, 1, 1, 2020, 1}, + {2021, 1, 1, 2020, 53}, {2022, 1, 1, 2021, 52}, {2023, 1, 1, 2022, 52}, + {2024, 1, 1, 2024, 1}, {2025, 1, 1, 2025, 1}, {2026, 1, 1, 2026, 1}, + {2027, 1, 1, 2026, 53}, {2028, 1, 1, 2027, 52}, {2029, 1, 1, 2029, 1}, + {2030, 1, 1, 2030, 1}, {2031, 1, 1, 2031, 1}, {2032, 1, 1, 2032, 1}, + {2033, 1, 1, 2032, 53}, {2034, 1, 1, 2033, 52}, {2035, 1, 1, 2035, 1}, + {2036, 1, 1, 2036, 1}, {2037, 1, 1, 2037, 1}, {2038, 1, 1, 2037, 53}, + {2039, 1, 1, 2038, 52}, {2040, 1, 1, 2039, 52}, +} + +func TestISOWeek(t *testing.T) { + // Selected dates and corner cases + for _, wt := range isoWeekTests { + dt := Date(wt.year, Month(wt.month), wt.day, 0, 0, 0, 0, UTC) + y, w := dt.ISOWeek() + if w != wt.wex || y != wt.yex { + t.Errorf("got %d/%d; expected %d/%d for %d-%02d-%02d", + y, w, wt.yex, wt.wex, wt.year, wt.month, wt.day) + } + } + + // The only real invariant: Jan 04 is in week 1 + for year := 1950; year < 2100; year++ { + if y, w := Date(year, January, 4, 0, 0, 0, 0, UTC).ISOWeek(); y != year || w != 1 { + t.Errorf("got %d/%d; expected %d/1 for Jan 04", y, w, year) + } } } -func BenchmarkSeconds(b *testing.B) { - for i := 0; i < b.N; i++ { - Seconds() +var durationTests = []struct { + str string + d Duration +}{ + {"0", 0}, + {"1ns", 1 * Nanosecond}, + {"1.1us", 1100 * Nanosecond}, + {"2.2ms", 2200 * Microsecond}, + {"3.3s", 3300 * Millisecond}, + {"4m5s", 4*Minute + 5*Second}, + {"4m5.001s", 4*Minute + 5001*Millisecond}, + {"5h6m7.001s", 5*Hour + 6*Minute + 7001*Millisecond}, + {"8m0.000000001s", 8*Minute + 1*Nanosecond}, + {"2562047h47m16.854775807s", 1<<63 - 1}, + {"-2562047h47m16.854775808s", -1 << 63}, +} + +func TestDurationString(t *testing.T) { + for _, tt := range durationTests { + if str := tt.d.String(); str != tt.str { + t.Errorf("Duration(%d).String() = %s, want %s", int64(tt.d), str, tt.str) + } + if tt.d > 0 { + if str := (-tt.d).String(); str != "-"+tt.str { + t.Errorf("Duration(%d).String() = %s, want %s", int64(-tt.d), str, "-"+tt.str) + } + } + } +} + +var dateTests = []struct { + year, month, day, hour, min, sec, nsec int + z *Location + unix int64 +}{ + {2011, 11, 6, 1, 0, 0, 0, Local, 1320566400}, // 1:00:00 PDT + {2011, 11, 6, 1, 59, 59, 0, Local, 1320569999}, // 1:59:59 PDT + {2011, 11, 6, 2, 0, 0, 0, Local, 1320573600}, // 2:00:00 PST + + {2011, 3, 13, 1, 0, 0, 0, Local, 1300006800}, // 1:00:00 PST + {2011, 3, 13, 1, 59, 59, 0, Local, 1300010399}, // 1:59:59 PST + {2011, 3, 13, 3, 0, 0, 0, Local, 1300010400}, // 3:00:00 PDT + {2011, 3, 13, 2, 30, 0, 0, Local, 1300008600}, // 2:30:00 PDT ≡ 1:30 PST + + // Many names for Fri Nov 18 7:56:35 PST 2011 + {2011, 11, 18, 7, 56, 35, 0, Local, 1321631795}, // Nov 18 7:56:35 + {2011, 11, 19, -17, 56, 35, 0, Local, 1321631795}, // Nov 19 -17:56:35 + {2011, 11, 17, 31, 56, 35, 0, Local, 1321631795}, // Nov 17 31:56:35 + {2011, 11, 18, 6, 116, 35, 0, Local, 1321631795}, // Nov 18 6:116:35 + {2011, 10, 49, 7, 56, 35, 0, Local, 1321631795}, // Oct 49 7:56:35 + {2011, 11, 18, 7, 55, 95, 0, Local, 1321631795}, // Nov 18 7:55:95 + {2011, 11, 18, 7, 56, 34, 1e9, Local, 1321631795}, // Nov 18 7:56:34 + 10⁹ns + {2011, 12, -12, 7, 56, 35, 0, Local, 1321631795}, // Dec -21 7:56:35 + {2012, 1, -43, 7, 56, 35, 0, Local, 1321631795}, // Jan -52 7:56:35 2012 + {2012, int(January - 2), 18, 7, 56, 35, 0, Local, 1321631795}, // (Jan-2) 18 7:56:35 2012 + {2010, int(December + 11), 18, 7, 56, 35, 0, Local, 1321631795}, // (Dec+11) 18 7:56:35 2010 +} + +func TestDate(t *testing.T) { + for _, tt := range dateTests { + time := Date(tt.year, Month(tt.month), tt.day, tt.hour, tt.min, tt.sec, tt.nsec, tt.z) + want := Unix(tt.unix, 0) + if !time.Equal(want) { + t.Errorf("Date(%d, %d, %d, %d, %d, %d, %d, %s) = %v, want %v", + tt.year, tt.month, tt.day, tt.hour, tt.min, tt.sec, tt.nsec, tt.z, + time, want) + } } } -func BenchmarkNanoseconds(b *testing.B) { +func BenchmarkNow(b *testing.B) { for i := 0; i < b.N; i++ { - Nanoseconds() + Now() } } func BenchmarkFormat(b *testing.B) { - time := SecondsToLocalTime(1265346057) + time := Unix(1265346057, 0) for i := 0; i < b.N; i++ { time.Format("Mon Jan 2 15:04:05 2006") } @@ -502,3 +650,31 @@ func BenchmarkParse(b *testing.B) { Parse(ANSIC, "Mon Jan 2 15:04:05 2006") } } + +func BenchmarkHour(b *testing.B) { + t := Now() + for i := 0; i < b.N; i++ { + _ = t.Hour() + } +} + +func BenchmarkSecond(b *testing.B) { + t := Now() + for i := 0; i < b.N; i++ { + _ = t.Second() + } +} + +func BenchmarkYear(b *testing.B) { + t := Now() + for i := 0; i < b.N; i++ { + _ = t.Year() + } +} + +func BenchmarkDay(b *testing.B) { + t := Now() + for i := 0; i < b.N; i++ { + _ = t.Day() + } +} diff --git a/libgo/go/time/zoneinfo.go b/libgo/go/time/zoneinfo.go new file mode 100644 index 00000000000..aca56e746af --- /dev/null +++ b/libgo/go/time/zoneinfo.go @@ -0,0 +1,191 @@ +// 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 time + +import "sync" + +// A Location maps time instants to the zone in use at that time. +// Typically, the Location represents the collection of time offsets +// in use in a geographical area, such as CEST and CET for central Europe. +type Location struct { + name string + zone []zone + tx []zoneTrans + + // Most lookups will be for the current time. + // To avoid the binary search through tx, keep a + // static one-element cache that gives the correct + // zone for the time when the Location was created. + // if cacheStart <= t <= cacheEnd, + // lookup can return cacheZone. + // The units for cacheStart and cacheEnd are seconds + // since January 1, 1970 UTC, to match the argument + // to lookup. + cacheStart int64 + cacheEnd int64 + cacheZone *zone +} + +// A zone represents a single time zone such as CEST or CET. +type zone struct { + name string // abbreviated name, "CET" + offset int // seconds east of UTC + isDST bool // is this zone Daylight Savings Time? +} + +// A zoneTrans represents a single time zone transition. +type zoneTrans struct { + when int64 // transition time, in seconds since 1970 GMT + index uint8 // the index of the zone that goes into effect at that time + isstd, isutc bool // ignored - no idea what these mean +} + +// UTC represents Universal Coordinated Time (UTC). +var UTC *Location = &utcLoc + +// utcLoc is separate so that get can refer to &utcLoc +// and ensure that it never returns a nil *Location, +// even if a badly behaved client has changed UTC. +var utcLoc = Location{name: "UTC"} + +// Local represents the system's local time zone. +var Local *Location = &localLoc + +// localLoc is separate so that initLocal can initialize +// it even if a client has changed Local. +var localLoc Location +var localOnce sync.Once + +func (l *Location) get() *Location { + if l == nil { + return &utcLoc + } + if l == &localLoc { + localOnce.Do(initLocal) + } + return l +} + +// String returns a descriptive name for the time zone information, +// corresponding to the argument to LoadLocation. +func (l *Location) String() string { + return l.get().name +} + +// FixedZone returns a Location that always uses +// the given zone name and offset (seconds east of UTC). +func FixedZone(name string, offset int) *Location { + l := &Location{ + name: name, + zone: []zone{{name, offset, false}}, + tx: []zoneTrans{{-1 << 63, 0, false, false}}, + cacheStart: -1 << 63, + cacheEnd: 1<<63 - 1, + } + l.cacheZone = &l.zone[0] + return l +} + +// lookup returns information about the time zone in use at an +// instant in time expressed as seconds since January 1, 1970 00:00:00 UTC. +// +// The returned information gives the name of the zone (such as "CET"), +// the start and end times bracketing sec when that zone is in effect, +// the offset in seconds east of UTC (such as -5*60*60), and whether +// the daylight savings is being observed at that time. +func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start, end int64) { + l = l.get() + + if len(l.tx) == 0 { + name = "UTC" + offset = 0 + isDST = false + start = -1 << 63 + end = 1<<63 - 1 + return + } + + if zone := l.cacheZone; zone != nil && l.cacheStart <= sec && sec < l.cacheEnd { + name = zone.name + offset = zone.offset + isDST = zone.isDST + start = l.cacheStart + end = l.cacheEnd + return + } + + // Binary search for entry with largest time <= sec. + // Not using sort.Search to avoid dependencies. + tx := l.tx + end = 1<<63 - 1 + for len(tx) > 1 { + m := len(tx) / 2 + lim := tx[m].when + if sec < lim { + end = lim + tx = tx[0:m] + } else { + tx = tx[m:] + } + } + zone := &l.zone[tx[0].index] + name = zone.name + offset = zone.offset + isDST = zone.isDST + start = tx[0].when + // end = maintained during the search + return +} + +// lookupName returns information about the time zone with +// the given name (such as "EST"). +func (l *Location) lookupName(name string) (offset int, isDST bool, ok bool) { + l = l.get() + for i := range l.zone { + zone := &l.zone[i] + if zone.name == name { + return zone.offset, zone.isDST, true + } + } + return +} + +// lookupOffset returns information about the time zone with +// the given offset (such as -5*60*60). +func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) { + l = l.get() + for i := range l.zone { + zone := &l.zone[i] + if zone.offset == offset { + return zone.name, zone.isDST, true + } + } + return +} + +// NOTE(rsc): Eventually we will need to accept the POSIX TZ environment +// syntax too, but I don't feel like implementing it today. + +// NOTE(rsc): Using the IANA names below means ensuring we have access +// to the database. Probably we will ship the files in $GOROOT/lib/zoneinfo/ +// and only look there if there are no system files available (such as on Windows). +// The files total 200 kB. + +// LoadLocation returns the Location with the given name. +// +// If the name is "" or "UTC", LoadLocation returns UTC. +// If the name is "Local", LoadLocation returns Local. +// +// Otherwise, the name is taken to be a location name corresponding to a file +// in the IANA Time Zone database, such as "America/New_York". +func LoadLocation(name string) (*Location, error) { + if name == "" || name == "UTC" { + return UTC, nil + } + if name == "Local" { + return Local, nil + } + return loadLocation(name) +} diff --git a/libgo/go/time/zoneinfo_plan9.go b/libgo/go/time/zoneinfo_plan9.go index 577ef85bd68..38aefc7a977 100644 --- a/libgo/go/time/zoneinfo_plan9.go +++ b/libgo/go/time/zoneinfo_plan9.go @@ -6,11 +6,10 @@ package time -import ( - "os" - "strconv" - "strings" -) +//import ( +// "strconv" +// "strings" +//) func parseZones(s string) (zt []zonetime) { f := strings.Fields(s) @@ -49,7 +48,7 @@ func parseZones(s string) (zt []zonetime) { return } -func setupZone() { +func initLocal() { t, err := os.Getenverror("timezone") if err != nil { // do nothing: use UTC @@ -58,16 +57,8 @@ func setupZone() { zones = parseZones(t) } -func setupTestingZone() { - f, err := os.Open("/adm/timezone/US_Pacific") - if err != nil { - return - } - defer f.Close() - l, _ := f.Seek(0, 2) - f.Seek(0, 0) - buf := make([]byte, l) - _, err = f.Read(buf) +func initTestingZone() { + buf, err := readFile("/adm/timezone/US_Pacific") if err != nil { return } diff --git a/libgo/go/time/zoneinfo_posix.go b/libgo/go/time/zoneinfo_posix.go deleted file mode 100644 index b0fa6c33b65..00000000000 --- a/libgo/go/time/zoneinfo_posix.go +++ /dev/null @@ -1,64 +0,0 @@ -// 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. - -// +build darwin freebsd linux openbsd plan9 - -package time - -import "sync" - -// Parsed representation -type zone struct { - utcoff int - isdst bool - name string -} - -type zonetime struct { - time int32 // transition time, in seconds since 1970 GMT - zone *zone // the zone that goes into effect at that time - isstd, isutc bool // ignored - no idea what these mean -} - -var zones []zonetime -var onceSetupZone sync.Once - -// Look up the correct time zone (daylight savings or not) for the given unix time, in the current location. -func lookupTimezone(sec int64) (zone string, offset int) { - onceSetupZone.Do(setupZone) - if len(zones) == 0 { - return "UTC", 0 - } - - // Binary search for entry with largest time <= sec - tz := zones - for len(tz) > 1 { - m := len(tz) / 2 - if sec < int64(tz[m].time) { - tz = tz[0:m] - } else { - tz = tz[m:] - } - } - z := tz[0].zone - return z.name, z.utcoff -} - -// lookupByName returns the time offset for the -// time zone with the given abbreviation. It only considers -// time zones that apply to the current system. -// For example, for a system configured as being in New York, -// it only recognizes "EST" and "EDT". -// For a system in San Francisco, "PST" and "PDT". -// For a system in Sydney, "EST" and "EDT", though they have -// different meanings than they do in New York. -func lookupByName(name string) (off int, found bool) { - onceSetupZone.Do(setupZone) - for _, z := range zones { - if name == z.zone.name { - return z.zone.utcoff, true - } - } - return 0, false -} diff --git a/libgo/go/time/zoneinfo_unix.go b/libgo/go/time/zoneinfo_unix.go index 0dc42353136..83d5b983c6e 100644 --- a/libgo/go/time/zoneinfo_unix.go +++ b/libgo/go/time/zoneinfo_unix.go @@ -12,8 +12,8 @@ package time import ( - "io/ioutil" - "os" + "errors" + "syscall" ) const ( @@ -65,18 +65,20 @@ func byteString(p []byte) string { return string(p) } -func parseinfo(bytes []byte) (zt []zonetime, ok bool) { +var badData = errors.New("malformed time zone information") + +func loadZoneData(bytes []byte) (l *Location, err error) { d := data{bytes, false} // 4-byte magic "TZif" if magic := d.read(4); string(magic) != "TZif" { - return nil, false + return nil, badData } // 1-byte version, then 15 bytes of padding var p []byte if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' { - return nil, false + return nil, badData } // six big-endian 32-bit integers: @@ -98,7 +100,7 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) { for i := 0; i < 6; i++ { nn, ok := d.big4() if !ok { - return nil, false + return nil, badData } n[i] = int(nn) } @@ -127,7 +129,7 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) { isutc := d.read(n[NUTCLocal]) if d.error { // ran out of data - return nil, false + return nil, badData } // If version == 2, the entire file repeats, this time using @@ -137,84 +139,119 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) { // Now we can build up a useful data structure. // First the zone information. // utcoff[4] isdst[1] nameindex[1] - z := make([]zone, n[NZone]) - for i := 0; i < len(z); i++ { + zone := make([]zone, n[NZone]) + for i := range zone { var ok bool var n uint32 if n, ok = zonedata.big4(); !ok { - return nil, false + return nil, badData } - z[i].utcoff = int(n) + zone[i].offset = int(n) var b byte if b, ok = zonedata.byte(); !ok { - return nil, false + return nil, badData } - z[i].isdst = b != 0 + zone[i].isDST = b != 0 if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) { - return nil, false + return nil, badData } - z[i].name = byteString(abbrev[b:]) + zone[i].name = byteString(abbrev[b:]) } // Now the transition time info. - zt = make([]zonetime, n[NTime]) - for i := 0; i < len(zt); i++ { + tx := make([]zoneTrans, n[NTime]) + for i := range tx { var ok bool var n uint32 if n, ok = txtimes.big4(); !ok { - return nil, false + return nil, badData } - zt[i].time = int32(n) - if int(txzones[i]) >= len(z) { - return nil, false + tx[i].when = int64(int32(n)) + if int(txzones[i]) >= len(zone) { + return nil, badData } - zt[i].zone = &z[txzones[i]] + tx[i].index = txzones[i] if i < len(isstd) { - zt[i].isstd = isstd[i] != 0 + tx[i].isstd = isstd[i] != 0 } if i < len(isutc) { - zt[i].isutc = isutc[i] != 0 + tx[i].isutc = isutc[i] != 0 + } + } + + // Commited to succeed. + l = &Location{zone: zone, tx: tx} + + // Fill in the cache with information about right now, + // since that will be the most common lookup. + sec, _ := now() + for i := range tx { + if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) { + l.cacheStart = tx[i].when + l.cacheEnd = 1<<63 - 1 + if i+1 < len(tx) { + l.cacheEnd = tx[i+1].when + } + l.cacheZone = &l.zone[tx[i].index] } } - return zt, true + + return l, nil } -func readinfofile(name string) ([]zonetime, bool) { - buf, err := ioutil.ReadFile(name) +func loadZoneFile(name string) (l *Location, err error) { + buf, err := readFile(name) if err != nil { - return nil, false + return } - return parseinfo(buf) + return loadZoneData(buf) +} + +func initTestingZone() { + syscall.Setenv("TZ", "America/Los_Angeles") + initLocal() } -func setupTestingZone() { - os.Setenv("TZ", "America/Los_Angeles") - setupZone() +// Many systems use /usr/share/zoneinfo, Solaris 2 has +// /usr/share/lib/zoneinfo, IRIX 6 has /usr/lib/locale/TZ. +var zoneDirs = []string{ + "/usr/share/zoneinfo/", + "/usr/share/lib/zoneinfo/", + "/usr/lib/locale/TZ/", } -func setupZone() { +func initLocal() { // consult $TZ to find the time zone to use. // no $TZ means use the system default /etc/localtime. // $TZ="" means use UTC. // $TZ="foo" means use /usr/share/zoneinfo/foo. - // Many systems use /usr/share/zoneinfo, Solaris 2 has - // /usr/share/lib/zoneinfo, IRIX 6 has /usr/lib/locale/TZ. - zoneDirs := []string{"/usr/share/zoneinfo/", - "/usr/share/lib/zoneinfo/", - "/usr/lib/locale/TZ/"} - tz, err := os.Getenverror("TZ") + tz, ok := syscall.Getenv("TZ") switch { - case err == os.ENOENV: - zones, _ = readinfofile("/etc/localtime") - case len(tz) > 0: - for _, zoneDir := range zoneDirs { - var ok bool - if zones, ok = readinfofile(zoneDir + tz); ok { - break - } + case !ok: + z, err := loadZoneFile("/etc/localtime") + if err == nil { + localLoc = *z + localLoc.name = "Local" + return + } + case tz != "" && tz != "UTC": + if z, err := loadLocation(tz); err == nil { + localLoc = *z + return + } + } + + // Fall back to UTC. + localLoc.name = "UTC" +} + +func loadLocation(name string) (*Location, error) { + for _, zoneDir := range zoneDirs { + if z, err := loadZoneFile(zoneDir + name); err == nil { + z.name = name + return z, nil } - case len(tz) == 0: - // do nothing: use UTC } + return nil, errors.New("unknown time zone " + name) } diff --git a/libgo/go/time/zoneinfo_windows.go b/libgo/go/time/zoneinfo_windows.go index ba152e0882a..beef4de92b0 100644 --- a/libgo/go/time/zoneinfo_windows.go +++ b/libgo/go/time/zoneinfo_windows.go @@ -5,34 +5,21 @@ package time import ( - "os" - "sync" + "errors" "syscall" ) -// BUG(brainman): The Windows implementation assumes that -// this year's rules for daylight savings time apply to all previous -// and future years as well. - -// TODO(brainman): use GetDynamicTimeZoneInformation, whenever possible (Vista and up), -// to improve on situation described in the bug above. - -type zone struct { - name string - offset int - year int64 - month, day, dayofweek int - hour, minute, second int - abssec int64 - prev *zone -} +// TODO(rsc): Fall back to copy of zoneinfo files. -// BUG(rsc): On Windows, time zone abbreviations are unavailable. -// This package constructs them using the capital letters from a longer -// time zone description. +// BUG(brainman,rsc): On Windows, the operating system does not provide complete +// time zone information. +// The implementation assumes that this year's rules for daylight savings +// time apply to all previous and future years as well. +// Also, time zone abbreviations are unavailable. The implementation constructs +// them using the capital letters from a longer time zone description. -// Populate zone struct with Windows supplied information. Returns true, if data is valid. -func (z *zone) populate(bias, biasdelta int32, d *syscall.Systemtime, name []uint16) (dateisgood bool) { +// abbrev returns the abbreviation to use for the given zone name. +func abbrev(name []uint16) string { // name is 'Pacific Standard Time' but we want 'PST'. // Extract just capital letters. It's not perfect but the // information we need is not available from the kernel. @@ -41,147 +28,101 @@ func (z *zone) populate(bias, biasdelta int32, d *syscall.Systemtime, name []uin // // http://social.msdn.microsoft.com/Forums/eu/vclanguage/thread/a87e1d25-fb71-4fe0-ae9c-a9578c9753eb // http://stackoverflow.com/questions/4195948/windows-time-zone-abbreviations-in-asp-net - short := make([]uint16, len(name)) - w := 0 + var short []rune for _, c := range name { if 'A' <= c && c <= 'Z' { - short[w] = c - w++ - } - } - z.name = syscall.UTF16ToString(short[:w]) - - z.offset = int(bias) - z.year = int64(d.Year) - z.month = int(d.Month) - z.day = int(d.Day) - z.dayofweek = int(d.DayOfWeek) - z.hour = int(d.Hour) - z.minute = int(d.Minute) - z.second = int(d.Second) - dateisgood = d.Month != 0 - if dateisgood { - z.offset += int(biasdelta) - } - z.offset = -z.offset * 60 - return -} - -// Pre-calculate cutoff time in seconds since the Unix epoch, if data is supplied in "absolute" format. -func (z *zone) preCalculateAbsSec() { - if z.year != 0 { - t := &Time{ - Year: z.year, - Month: int(z.month), - Day: int(z.day), - Hour: int(z.hour), - Minute: int(z.minute), - Second: int(z.second), + short = append(short, rune(c)) } - z.abssec = t.Seconds() - // Time given is in "local" time. Adjust it for "utc". - z.abssec -= int64(z.prev.offset) } + return string(short) } -// Convert zone cutoff time to sec in number of seconds since the Unix epoch, given particular year. -func (z *zone) cutoffSeconds(year int64) int64 { +// pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*) +// denoted by the system date+time d in the given year. +// It is up to the caller to convert this local time into a UTC-based time. +func pseudoUnix(year int, d *syscall.Systemtime) int64 { // Windows specifies daylight savings information in "day in month" format: - // z.month is month number (1-12) - // z.dayofweek is appropriate weekday (Sunday=0 to Saturday=6) - // z.day is week within the month (1 to 5, where 5 is last week of the month) - // z.hour, z.minute and z.second are absolute time - t := &Time{ - Year: year, - Month: int(z.month), - Day: 1, - Hour: int(z.hour), - Minute: int(z.minute), - Second: int(z.second), - } - t = SecondsToUTC(t.Seconds()) - i := int(z.dayofweek) - t.Weekday() + // d.Month is month number (1-12) + // d.DayOfWeek is appropriate weekday (Sunday=0 to Saturday=6) + // d.Day is week within the month (1 to 5, where 5 is last week of the month) + // d.Hour, d.Minute and d.Second are absolute time + day := 1 + t := Date(year, Month(d.Month), day, int(d.Hour), int(d.Minute), int(d.Second), 0, UTC) + i := int(d.DayOfWeek) - int(t.Weekday()) if i < 0 { i += 7 } - t.Day += i - if week := int(z.day) - 1; week < 4 { - t.Day += week * 7 + day += i + if week := int(d.Day) - 1; week < 4 { + day += week * 7 } else { // "Last" instance of the day. - t.Day += 4 * 7 - if t.Day > months(year)[t.Month] { - t.Day -= 7 + day += 4 * 7 + if day > daysIn(Month(d.Month), year) { + day -= 7 } } - // Result is in "local" time. Adjust it for "utc". - return t.Seconds() - int64(z.prev.offset) + return t.sec + int64(day-1)*secondsPerDay + internalToUnix } -// Is t before the cutoff for switching to z? -func (z *zone) isBeforeCutoff(t *Time) bool { - var coff int64 - if z.year == 0 { - // "day in month" format used - coff = z.cutoffSeconds(t.Year) - } else { - // "absolute" format used - coff = z.abssec - } - return t.Seconds() < coff -} - -type zoneinfo struct { - disabled bool // daylight saving time is not used locally - offsetIfDisabled int - januaryIsStd bool // is january 1 standard time? - std, dst zone -} +func initLocalFromTZI(i *syscall.Timezoneinformation) { + l := &localLoc -// Pick zone (std or dst) t time belongs to. -func (zi *zoneinfo) pickZone(t *Time) *zone { - z := &zi.std - if tz.januaryIsStd { - if !zi.dst.isBeforeCutoff(t) && zi.std.isBeforeCutoff(t) { - // after switch to daylight time and before the switch back to standard - z = &zi.dst - } - } else { - if zi.std.isBeforeCutoff(t) || !zi.dst.isBeforeCutoff(t) { - // before switch to standard time or after the switch back to daylight - z = &zi.dst - } + nzone := 1 + if i.StandardDate.Month > 0 { + nzone++ } - return z -} - -var tz zoneinfo -var initError error -var onceSetupZone sync.Once + l.zone = make([]zone, nzone) -func setupZone() { - var i syscall.Timezoneinformation - if _, e := syscall.GetTimeZoneInformation(&i); e != 0 { - initError = os.NewSyscallError("GetTimeZoneInformation", e) + std := &l.zone[0] + std.name = abbrev(i.StandardName[0:]) + if nzone == 1 { + // No daylight savings. + std.offset = -int(i.Bias) * 60 + l.cacheStart = -1 << 63 + l.cacheEnd = 1<<63 - 1 + l.cacheZone = std return } - setupZoneFromTZI(&i) -} -func setupZoneFromTZI(i *syscall.Timezoneinformation) { - if !tz.std.populate(i.Bias, i.StandardBias, &i.StandardDate, i.StandardName[0:]) { - tz.disabled = true - tz.offsetIfDisabled = tz.std.offset - return + // StandardBias must be ignored if StandardDate is not set, + // so this computation is delayed until after the nzone==1 + // return above. + std.offset = -int(i.Bias+i.StandardBias) * 60 + + dst := &l.zone[1] + dst.name = abbrev(i.DaylightName[0:]) + dst.offset = -int(i.Bias+i.DaylightBias) * 60 + dst.isDST = true + + // Arrange so that d0 is first transition date, d1 second, + // i0 is index of zone after first transition, i1 second. + d0 := &i.StandardDate + d1 := &i.DaylightDate + i0 := 0 + i1 := 1 + if d0.Month > d1.Month { + d0, d1 = d1, d0 + i0, i1 = i1, i0 + } + + // 2 tx per year, 100 years on each side of this year + l.tx = make([]zoneTrans, 400) + + t := Now().UTC() + year := t.Year() + txi := 0 + for y := year - 100; y < year+100; y++ { + tx := &l.tx[txi] + tx.when = pseudoUnix(y, d0) - int64(l.zone[i1].offset) + tx.index = uint8(i0) + txi++ + + tx = &l.tx[txi] + tx.when = pseudoUnix(y, d1) - int64(l.zone[i0].offset) + tx.index = uint8(i1) + txi++ } - tz.std.prev = &tz.dst - tz.dst.populate(i.Bias, i.DaylightBias, &i.DaylightDate, i.DaylightName[0:]) - tz.dst.prev = &tz.std - tz.std.preCalculateAbsSec() - tz.dst.preCalculateAbsSec() - // Is january 1 standard time this year? - t := UTC() - tz.januaryIsStd = tz.dst.cutoffSeconds(t.Year) < tz.std.cutoffSeconds(t.Year) } var usPacific = syscall.Timezoneinformation{ @@ -197,53 +138,20 @@ var usPacific = syscall.Timezoneinformation{ DaylightBias: -60, } -func setupTestingZone() { - setupZoneFromTZI(&usPacific) +func initTestingZone() { + initLocalFromTZI(&usPacific) } -// Look up the correct time zone (daylight savings or not) for the given unix time, in the current location. -func lookupTimezone(sec int64) (zone string, offset int) { - onceSetupZone.Do(setupZone) - if initError != nil { - return "", 0 - } - if tz.disabled { - return "", tz.offsetIfDisabled - } - t := SecondsToUTC(sec) - z := &tz.std - if tz.std.year == 0 { - // "day in month" format used - z = tz.pickZone(t) - } else { - // "absolute" format used - if tz.std.year == t.Year { - // we have rule for the year in question - z = tz.pickZone(t) - } else { - // we do not have any information for that year, - // will assume standard offset all year around - } +func initLocal() { + var i syscall.Timezoneinformation + if _, err := syscall.GetTimeZoneInformation(&i); err != nil { + localLoc.name = "UTC" + return } - return z.name, z.offset + initLocalFromTZI(&i) } -// lookupByName returns the time offset for the -// time zone with the given abbreviation. It only considers -// time zones that apply to the current system. -func lookupByName(name string) (off int, found bool) { - onceSetupZone.Do(setupZone) - if initError != nil { - return 0, false - } - if tz.disabled { - return tz.offsetIfDisabled, false - } - switch name { - case tz.std.name: - return tz.std.offset, true - case tz.dst.name: - return tz.dst.offset, true - } - return 0, false +// TODO(rsc): Implement. +func loadLocation(name string) (*Location, error) { + return nil, errors.New("unknown time zone " + name) } diff --git a/libgo/go/websocket/client.go b/libgo/go/websocket/client.go index 5dfd824e6e5..89cdcda71ae 100644 --- a/libgo/go/websocket/client.go +++ b/libgo/go/websocket/client.go @@ -72,8 +72,8 @@ A trivial example client: package main import ( - "http" "log" + "net/http" "strings" "websocket" ) diff --git a/libgo/go/websocket/hixie.go b/libgo/go/websocket/hixie.go index 4d5360ff4b9..ec7b7ae0a45 100644 --- a/libgo/go/websocket/hixie.go +++ b/libgo/go/websocket/hixie.go @@ -274,7 +274,7 @@ func getChallengeResponse(number1, number2 uint32, key3 []byte) (expected []byte if _, err = h.Write(challenge); err != nil { return } - expected = h.Sum() + expected = h.Sum(nil) return } diff --git a/libgo/go/websocket/hybi.go b/libgo/go/websocket/hybi.go index b17d9470bbc..ff386dc7f21 100644 --- a/libgo/go/websocket/hybi.go +++ b/libgo/go/websocket/hybi.go @@ -371,7 +371,7 @@ func getNonceAccept(nonce []byte) (expected []byte, err error) { return } expected = make([]byte, 28) - base64.StdEncoding.Encode(expected, h.Sum()) + base64.StdEncoding.Encode(expected, h.Sum(nil)) return } diff --git a/libgo/go/websocket/server.go b/libgo/go/websocket/server.go index 57dc4fd1dff..8320b032ead 100644 --- a/libgo/go/websocket/server.go +++ b/libgo/go/websocket/server.go @@ -60,8 +60,8 @@ A trivial example server: package main import ( - "http" "io" + "net/http" "websocket" ) diff --git a/libgo/go/websocket/websocket.go b/libgo/go/websocket/websocket.go index 1e4036ce391..df4416e22ed 100644 --- a/libgo/go/websocket/websocket.go +++ b/libgo/go/websocket/websocket.go @@ -10,12 +10,12 @@ import ( "bufio" "crypto/tls" "encoding/json" + "errors" "io" "io/ioutil" "net" "net/http" "net/url" - "os" "sync" ) @@ -243,12 +243,14 @@ func (ws *Conn) RemoteAddr() net.Addr { return &Addr{ws.config.Origin} } +var errSetTimeout = errors.New("websocket: cannot set timeout: not using a net.Conn") + // SetTimeout sets the connection's network timeout in nanoseconds. func (ws *Conn) SetTimeout(nsec int64) error { if conn, ok := ws.rwc.(net.Conn); ok { return conn.SetTimeout(nsec) } - return os.EINVAL + return errSetTimeout } // SetReadTimeout sets the connection's network read timeout in nanoseconds. @@ -256,7 +258,7 @@ func (ws *Conn) SetReadTimeout(nsec int64) error { if conn, ok := ws.rwc.(net.Conn); ok { return conn.SetReadTimeout(nsec) } - return os.EINVAL + return errSetTimeout } // SetWriteTimeout sets the connection's network write timeout in nanoseconds. @@ -264,7 +266,7 @@ func (ws *Conn) SetWriteTimeout(nsec int64) error { if conn, ok := ws.rwc.(net.Conn); ok { return conn.SetWriteTimeout(nsec) } - return os.EINVAL + return errSetTimeout } // Config returns the WebSocket config. diff --git a/libgo/merge.sh b/libgo/merge.sh index e6d7898dfe1..496abe53ad5 100755 --- a/libgo/merge.sh +++ b/libgo/merge.sh @@ -137,9 +137,6 @@ merge_c() { } (cd ${NEWDIR}/src/pkg && find . -name '*.go' -print) | while read f; do - if test `dirname $f` = "./syscall"; then - continue - fi oldfile=${OLDDIR}/src/pkg/$f newfile=${NEWDIR}/src/pkg/$f libgofile=go/$f diff --git a/libgo/mksysinfo.sh b/libgo/mksysinfo.sh index e493ef072c1..a5e8144cd77 100755 --- a/libgo/mksysinfo.sh +++ b/libgo/mksysinfo.sh @@ -120,9 +120,10 @@ grep -v '^// ' gen-sysinfo.go | \ -e 's/\([^a-zA-Z0-9_]\)_timestruc_t\([^a-zA-Z0-9_]\)/\1Timestruc\2/g' \ >> ${OUT} -# The errno constants. -grep '^const _E' gen-sysinfo.go | \ - sed -e 's/^\(const \)_\(E[^= ]*\)\(.*\)$/\1\2 = _\2/' >> ${OUT} +# The errno constants. These get type Errno. +echo '#include <errno.h>' | ${CC} -x c - -E -dM | \ + egrep '#define E[A-Z0-9_]+ ' | \ + sed -e 's/^#define \(E[A-Z0-9_]*\) .*$/const \1 = Errno(_\1)/' >> ${OUT} # The O_xxx flags. egrep '^const _(O|F|FD)_' gen-sysinfo.go | \ @@ -182,17 +183,21 @@ grep '^const _SOMAXCONN' gen-sysinfo.go | grep '^const _SHUT_' gen-sysinfo.go | sed -e 's/^\(const \)_\(SHUT[^= ]*\)\(.*\)$/\1\2 = _\2/' >> ${OUT} -# The net package requires a definition for IPV6ONLY. -if ! grep '^const IPV6_V6ONLY ' ${OUT} >/dev/null 2>&1; then - echo "const IPV6_V6ONLY = 0" >> ${OUT} -fi +# The net package requires some const definitions. +for m in IPV6_V6ONLY IPPROTO_IPV6 IPV6_JOIN_GROUP IPV6_LEAVE_GROUP; do + if ! grep "^const $m " ${OUT} >/dev/null 2>&1; then + echo "const $m = 0" >> ${OUT} + fi +done # pathconf constants. grep '^const __PC' gen-sysinfo.go | sed -e 's/^\(const \)__\(PC[^= ]*\)\(.*\)$/\1\2 = __\2/' >> ${OUT} -# The epoll constants were picked up by the errno constants, but we -# need to be sure the EPOLLRDHUP is defined. +# epoll constants. +grep '^const _EPOLL' gen-sysinfo.go | + sed -e 's/^\(const \)_\(EPOLL[^= ]*\)\(.*\)$/\1\2 = _\2/' >> ${OUT} +# Make sure EPOLLRDHUP is defined. if ! grep '^const EPOLLRDHUP' ${OUT} >/dev/null 2>&1; then echo "const EPOLLRDHUP = 0x2000" >> ${OUT} fi @@ -471,11 +476,14 @@ grep '^type _ip_mreq ' gen-sysinfo.go | \ -e 's/_in_addr/[4]byte/g' \ >> ${OUT} -# The size of the ip_mreq struct. -if grep 'type IPMreq ' ${OUT} > /dev/null 2>&1; then - echo 'var SizeofIPMreq = int(unsafe.Sizeof(IPMreq{}))' >> ${OUT} +# We need IPMreq to compile the net package. +if ! grep 'type IPMreq ' ${OUT} >/dev/null 2>&1; then + echo 'type IPMreq struct { Multiaddr [4]byte; Interface [4]byte; }' >> ${OUT} fi +# The size of the ip_mreq struct. +echo 'var SizeofIPMreq = int(unsafe.Sizeof(IPMreq{}))' >> ${OUT} + # The ipv6_mreq struct. grep '^type _ipv6_mreq ' gen-sysinfo.go | \ sed -e 's/_ipv6_mreq/IPv6Mreq/' \ @@ -484,6 +492,11 @@ grep '^type _ipv6_mreq ' gen-sysinfo.go | \ -e 's/_in6_addr/[16]byte/' \ >> ${OUT} +# We need IPv6Mreq to compile the net package. +if ! grep 'type IPv6Mreq ' ${OUT} >/dev/null 2>&1; then + echo 'type IPv6Mreq struct { Multiaddr [16]byte; Interface uint32; }' >> ${OUT} +fi + # Try to guess the type to use for fd_set. fd_set=`grep '^type _fd_set ' gen-sysinfo.go || true` fds_bits_type="_C_long" @@ -498,9 +511,11 @@ grep '^type _addrinfo ' gen-sysinfo.go | \ -e 's/ ai_/ Ai_/g' \ >> ${OUT} -# The addrinfo flags. +# The addrinfo flags and errors. grep '^const _AI_' gen-sysinfo.go | \ sed -e 's/^\(const \)_\(AI_[^= ]*\)\(.*\)$/\1\2 = _\2/' >> ${OUT} +grep '^const _EAI_' gen-sysinfo.go | \ + sed -e 's/^\(const \)_\(EAI_[^= ]*\)\(.*\)$/\1\2 = _\2/' >> ${OUT} # The passwd struct. grep '^type _passwd ' gen-sysinfo.go | \ @@ -644,17 +659,18 @@ grep '^type _termios ' gen-sysinfo.go | \ -e 's/c_ospeed/Ospeed/' \ >> ${OUT} -# The termios constants. The ones starting with 'E' were picked up above. +# The termios constants. for n in IGNBRK BRKINT IGNPAR PARMRK INPCK ISTRIP INLCR IGNCR ICRNL IUCLC \ IXON IXANY IXOFF IMAXBEL IUTF8 OPOST OLCUC ONLCR OCRNL ONOCR ONLRET \ OFILL OFDEL NLDLY NL0 NL1 CRDLY CR0 CR1 CR2 CR3 TABDLY BSDLY VTDLY \ FFDLY CBAUD CBAUDEX CSIZE CSTOPB CREAD PARENB PARODD HUPCL CLOCAL \ - LOBLK CIBAUD CMSPAR CRTSCTS ISIG ICANON XCASE DEFECHK FLUSHO NOFLSH \ - TOSTOP PENDIN IEXTEN VINTR VQUIT VERASE VKILL VEOF VMIN VEOL VTIME VEOL2 \ - VSWTCH VSTART VSTOP VSUSP VDSUSP VLNEXT VWERASE VREPRINT VDISCARD VSTATUS \ - TCSANOW TCSADRAIN, TCSAFLUSH TCIFLUSH TCOFLUSH TCIOFLUSH TCOOFF TCOON \ - TCIOFF TCION B0 B50 B75 B110 B134 B150 B200 B300 B600 B1200 B1800 B2400 \ - B4800 B9600 B19200 B38400 B57600 B115200 B230400; do + LOBLK CIBAUD CMSPAR CRTSCTS ISIG ICANON XCASE ECHO ECHOE ECHOK ECHONL \ + ECHOCTL ECHOPRT ECHOKE DEFECHO FLUSHO NOFLSH TOSTOP PENDIN IEXTEN VINTR \ + VQUIT VERASE VKILL VEOF VMIN VEOL VTIME VEOL2 VSWTCH VSTART VSTOP VSUSP \ + VDSUSP VLNEXT VWERASE VREPRINT VDISCARD VSTATUS TCSANOW TCSADRAIN \ + TCSAFLUSH TCIFLUSH TCOFLUSH TCIOFLUSH TCOOFF TCOON TCIOFF TCION B0 B50 \ + B75 B110 B134 B150 B200 B300 B600 B1200 B1800 B2400 B4800 B9600 B19200 \ + B38400 B57600 B115200 B230400; do grep "^const _$n " gen-sysinfo.go | \ sed -e 's/^\(const \)_\([^=]*\)\(.*\)$/\1\2 = _\2/' >> ${OUT} diff --git a/libgo/runtime/go-nanotime.c b/libgo/runtime/go-nanotime.c index 197fb15d2a5..7e5e3e098a9 100644 --- a/libgo/runtime/go-nanotime.c +++ b/libgo/runtime/go-nanotime.c @@ -6,17 +6,16 @@ #include <sys/time.h> -#include "go-assert.h" #include "runtime.h" +int64 runtime_nanotime (void) + __attribute__ ((no_split_stack)); + int64 runtime_nanotime (void) { - int i; struct timeval tv; - i = gettimeofday (&tv, NULL); - __go_assert (i == 0); - + gettimeofday (&tv, NULL); return (int64) tv.tv_sec * 1000000000 + (int64) tv.tv_usec * 1000; } diff --git a/libgo/runtime/go-now.c b/libgo/runtime/go-now.c new file mode 100644 index 00000000000..5df8085c6a8 --- /dev/null +++ b/libgo/runtime/go-now.c @@ -0,0 +1,31 @@ +// 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. + +#include <stddef.h> +#include <stdint.h> +#include <sys/time.h> + +// Return current time. This is the implementation of time.now(). + +struct time_now_ret +{ + int64_t sec; + int32_t nsec; +}; + +struct time_now_ret now() + __asm__ ("libgo_time.time.now") + __attribute__ ((no_split_stack)); + +struct time_now_ret +now() +{ + struct timeval tv; + struct time_now_ret ret; + + gettimeofday (&tv, NULL); + ret.sec = tv.tv_sec; + ret.nsec = tv.tv_usec * 1000; + return ret; +} diff --git a/libgo/runtime/go-setenv.c b/libgo/runtime/go-setenv.c index 9c93f52668e..78717f4705a 100644 --- a/libgo/runtime/go-setenv.c +++ b/libgo/runtime/go-setenv.c @@ -12,10 +12,10 @@ #include "go-alloc.h" #include "go-string.h" -/* Set the C environment from Go. This is called by os.Setenv. */ +/* Set the C environment from Go. This is called by syscall.Setenv. */ void setenv_c (struct __go_string, struct __go_string) - __asm__ ("libgo_os.os.setenv_c"); + __asm__ ("libgo_syscall.syscall.setenv_c"); void setenv_c (struct __go_string k, struct __go_string v) diff --git a/libgo/runtime/go-signal.c b/libgo/runtime/go-signal.c index cd165f45112..eb624097b36 100644 --- a/libgo/runtime/go-signal.c +++ b/libgo/runtime/go-signal.c @@ -122,12 +122,14 @@ sighandler (int sig) const char *msg; int i; +#ifdef SIGPROF if (sig == SIGPROF) { /* FIXME. */ runtime_sigprof (0, 0, nil, nil); return; } +#endif /* FIXME: Should check siginfo for more information when available. */ @@ -257,6 +259,7 @@ runtime_initsig (int32 queue) void runtime_resetcpuprofiler(int32 hz) { +#ifdef SIGPROF struct itimerval it; struct sigaction sa; int i; @@ -289,6 +292,7 @@ runtime_resetcpuprofiler(int32 hz) i = setitimer (ITIMER_PROF, &it, NULL); __go_assert (i == 0); } +#endif runtime_m()->profilehz = hz; } diff --git a/libgo/runtime/mem_posix_memalign.c b/libgo/runtime/mem_posix_memalign.c index 7d04f999f9f..8acdf070570 100644 --- a/libgo/runtime/mem_posix_memalign.c +++ b/libgo/runtime/mem_posix_memalign.c @@ -36,10 +36,13 @@ runtime_SysFree(void *v, uintptr n) void* runtime_SysReserve(void *v, uintptr n) { + USED(v); return runtime_SysAlloc(n); } void runtime_SysMap(void *v, uintptr n) { + USED(v); + USED(n); } diff --git a/libgo/runtime/mgc0.c b/libgo/runtime/mgc0.c index 0f1cb49e40f..c4ab1454c5b 100644 --- a/libgo/runtime/mgc0.c +++ b/libgo/runtime/mgc0.c @@ -741,6 +741,7 @@ mark(void (*scan)(byte*, int64)) scan((byte*)&runtime_allg, sizeof runtime_allg); scan((byte*)&runtime_allm, sizeof runtime_allm); runtime_MProf_Mark(scan); + runtime_time_scan(scan); // mark stacks for(gp=runtime_allg; gp!=nil; gp=gp->alllink) { diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c index 88831d41a67..34566fbf9a0 100644 --- a/libgo/runtime/proc.c +++ b/libgo/runtime/proc.c @@ -42,7 +42,6 @@ extern void *__splitstack_find(void *, void *, size_t *, void **, void **, #endif static void schedule(G*); -static M *startm(void); typedef struct Sched Sched; @@ -128,7 +127,7 @@ struct Sched { volatile uint32 atomic; // atomic scheduling word (see below) int32 profilehz; // cpu profiling rate - + bool init; // running initialization bool lockmain; // init called runtime.LockOSThread @@ -826,7 +825,7 @@ runtime_starttheworld(bool extra) // but m is not running a specific goroutine, // so set the helpgc flag as a signal to m's // first schedule(nil) to mcpu-- and grunning--. - m = startm(); + m = runtime_newm(); m->helpgc = 1; runtime_sched.grunning++; } @@ -876,8 +875,6 @@ struct CgoThreadStart }; // Kick off new m's as needed (up to mcpumax). -// There are already `other' other cpus that will -// start looking for goroutines shortly. // Sched is locked. static void matchmg(void) @@ -895,13 +892,14 @@ matchmg(void) // Find the m that will run gp. if((mp = mget(gp)) == nil) - mp = startm(); + mp = runtime_newm(); mnextg(mp, gp); } } -static M* -startm(void) +// Create a new m. It will start off with a call to runtime_mstart. +M* +runtime_newm(void) { M *m; pthread_attr_t attr; @@ -1135,6 +1133,7 @@ runtime_exitsyscall(void) runtime_memclr(gp->gcregs, sizeof gp->gcregs); } +// Allocate a new g, with a stack big enough for stacksize bytes. G* runtime_malg(int32 stacksize, byte** ret_stack, size_t* ret_stacksize) { @@ -1283,6 +1282,7 @@ runtime_Gosched(void) runtime_gosched(); } +// Implementation of runtime.GOMAXPROCS. // delete when scheduler is stronger int32 runtime_gomaxprocsfunc(int32 n) @@ -1390,6 +1390,7 @@ static struct { uintptr pcbuf[100]; } prof; +// Called if we receive a SIGPROF signal. void runtime_sigprof(uint8 *pc __attribute__ ((unused)), uint8 *sp __attribute__ ((unused)), @@ -1412,6 +1413,7 @@ runtime_sigprof(uint8 *pc __attribute__ ((unused)), runtime_unlock(&prof); } +// Arrange to call fn with a traceback hz times a second. void runtime_setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz) { diff --git a/libgo/runtime/runtime.c b/libgo/runtime/runtime.c index 8e4433b0d6c..ec96f5b615f 100644 --- a/libgo/runtime/runtime.c +++ b/libgo/runtime/runtime.c @@ -87,7 +87,7 @@ static int32 argc; static byte** argv; extern Slice os_Args asm ("libgo_os.os.Args"); -extern Slice os_Envs asm ("libgo_os.os.Envs"); +extern Slice syscall_Envs asm ("libgo_syscall.syscall.Envs"); void runtime_args(int32 c, byte **v) @@ -126,9 +126,9 @@ runtime_goenvs(void) s = runtime_malloc(n*sizeof s[0]); for(i=0; i<n; i++) s[i] = runtime_gostringnocopy(argv[argc+1+i]); - os_Envs.__values = (void*)s; - os_Envs.__count = n; - os_Envs.__capacity = n; + syscall_Envs.__values = (void*)s; + syscall_Envs.__count = n; + syscall_Envs.__capacity = n; } const byte* @@ -141,8 +141,8 @@ runtime_getenv(const char *s) bs = (const byte*)s; len = runtime_findnull(bs); - envv = (String*)os_Envs.__values; - envc = os_Envs.__count; + envv = (String*)syscall_Envs.__values; + envc = syscall_Envs.__count; for(i=0; i<envc; i++){ if(envv[i].__length <= len) continue; diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h index 00443197095..1c7ede92722 100644 --- a/libgo/runtime/runtime.h +++ b/libgo/runtime/runtime.h @@ -56,6 +56,8 @@ typedef union Note Note; typedef struct MCache MCache; typedef struct FixAlloc FixAlloc; typedef struct Hchan Hchan; +typedef struct Timers Timers; +typedef struct Timer Timer; typedef struct __go_open_array Slice; typedef struct __go_string String; @@ -190,6 +192,38 @@ enum { }; #endif +struct Timers +{ + Lock; + G *timerproc; + bool sleeping; + bool rescheduling; + Note waitnote; + Timer **t; + int32 len; + int32 cap; +}; + +// Package time knows the layout of this structure. +// If this struct changes, adjust ../time/sleep.go:/runtimeTimer. +struct Timer +{ + int32 i; // heap index + + // Timer wakes up at when, and then at when+period, ... (period > 0 only) + // each time calling f(now, arg) in the timer goroutine, so f must be + // a well-behaved function and not block. + int64 when; + int64 period; + void (*f)(int64, Eface); + Eface arg; +}; + +/* + * defined macros + * you need super-gopher-guru privilege + * to add this list. + */ #define nelem(x) (sizeof(x)/sizeof((x)[0])) #define nil ((void*)0) #define USED(v) ((void) v) @@ -229,6 +263,8 @@ G* runtime_malg(int32, byte**, size_t*); void runtime_minit(void); void runtime_mallocinit(void); void runtime_gosched(void); +void runtime_tsleep(int64); +M* runtime_newm(void); void runtime_goexit(void); void runtime_entersyscall(void) __asm__("libgo_syscall.syscall.entersyscall"); void runtime_exitsyscall(void) __asm__("libgo_syscall.syscall.exitsyscall"); @@ -341,3 +377,5 @@ void reflect_call(const struct __go_func_type *, const void *, _Bool, _Bool, #ifdef __rtems__ void __wrap_rtems_task_variable_add(void **); #endif + +void runtime_time_scan(void (*)(byte*, int64)); diff --git a/libgo/runtime/thread-linux.c b/libgo/runtime/thread-linux.c index c08cc0dba29..a0ee3600650 100644 --- a/libgo/runtime/thread-linux.c +++ b/libgo/runtime/thread-linux.c @@ -30,7 +30,7 @@ runtime_futexsleep(uint32 *addr, uint32 val, int64 ns) else { ts.tv_sec = ns/1000000000LL; ts.tv_nsec = ns%1000000000LL; - // Avoid overflowdefs + // Avoid overflow if(ts.tv_sec > 1<<30) ts.tv_sec = 1<<30; tsp = &ts; diff --git a/libgo/runtime/time.goc b/libgo/runtime/time.goc index f5a412a710f..cae15e5946a 100644 --- a/libgo/runtime/time.goc +++ b/libgo/runtime/time.goc @@ -2,12 +2,262 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Runtime implementations to help package time. +// Time-related runtime and pieces of package time. package time #include "runtime.h" +#include "defs.h" +#include "arch.h" +#include "malloc.h" -func Nanoseconds() (ret int64) { - ret = runtime_nanotime(); +static Timers timers; +static void addtimer(Timer*); +static bool deltimer(Timer*); + +// Package time APIs. +// Godoc uses the comments in package time, not these. + +// time.now is implemented in assembly. + +// Sleep puts the current goroutine to sleep for at least ns nanoseconds. +func Sleep(ns int64) { + G *g; + + g = runtime_g(); + g->status = Gwaiting; + g->waitreason = "sleep"; + runtime_tsleep(ns); +} + +// startTimer adds t to the timer heap. +func startTimer(t *Timer) { + addtimer(t); +} + +// stopTimer removes t from the timer heap if it is there. +// It returns true if t was removed, false if t wasn't even there. +func stopTimer(t *Timer) (stopped bool) { + stopped = deltimer(t); +} + +// C runtime. + +static void timerproc(void*); +static void siftup(int32); +static void siftdown(int32); + +// Ready the goroutine e.data. +static void +ready(int64 now, Eface e) +{ + USED(now); + + runtime_ready(e.__object); +} + +// Put the current goroutine to sleep for ns nanoseconds. +// The caller must have set g->status and g->waitreason. +void +runtime_tsleep(int64 ns) +{ + Timer t; + + if(ns <= 0) + return; + + t.when = runtime_nanotime() + ns; + t.period = 0; + t.f = ready; + t.arg.__object = runtime_g(); + addtimer(&t); + runtime_gosched(); +} + +// Add a timer to the heap and start or kick the timer proc +// if the new timer is earlier than any of the others. +static void +addtimer(Timer *t) +{ + int32 n; + Timer **nt; + + runtime_lock(&timers); + if(timers.len >= timers.cap) { + // Grow slice. + n = 16; + if(n <= timers.cap) + n = timers.cap*3 / 2; + nt = runtime_malloc(n*sizeof nt[0]); + runtime_memmove(nt, timers.t, timers.len*sizeof nt[0]); + runtime_free(timers.t); + timers.t = nt; + timers.cap = n; + } + t->i = timers.len++; + timers.t[t->i] = t; + siftup(t->i); + if(t->i == 0) { + // siftup moved to top: new earliest deadline. + if(timers.sleeping) { + timers.sleeping = false; + runtime_notewakeup(&timers.waitnote); + } + if(timers.rescheduling) { + timers.rescheduling = false; + runtime_ready(timers.timerproc); + } + } + if(timers.timerproc == nil) + timers.timerproc = __go_go(timerproc, nil); + runtime_unlock(&timers); +} + +// Delete timer t from the heap. +// Do not need to update the timerproc: +// if it wakes up early, no big deal. +static bool +deltimer(Timer *t) +{ + int32 i; + + runtime_lock(&timers); + + // t may not be registered anymore and may have + // a bogus i (typically 0, if generated by Go). + // Verify it before proceeding. + i = t->i; + if(i < 0 || i >= timers.len || timers.t[i] != t) { + runtime_unlock(&timers); + return false; + } + + timers.len--; + if(i == timers.len) { + timers.t[i] = nil; + } else { + timers.t[i] = timers.t[timers.len]; + timers.t[timers.len] = nil; + timers.t[i]->i = i; + siftup(i); + siftdown(i); + } + runtime_unlock(&timers); + return true; +} + +// Timerproc runs the time-driven events. +// It sleeps until the next event in the timers heap. +// If addtimer inserts a new earlier event, addtimer +// wakes timerproc early. +static void +timerproc(void* dummy __attribute__ ((unused))) +{ + G *g; + int64 delta, now; + Timer *t; + void (*f)(int64, Eface); + Eface arg; + + g = runtime_g(); + for(;;) { + runtime_lock(&timers); + now = runtime_nanotime(); + for(;;) { + if(timers.len == 0) { + delta = -1; + break; + } + t = timers.t[0]; + delta = t->when - now; + if(delta > 0) + break; + if(t->period > 0) { + // leave in heap but adjust next time to fire + t->when += t->period * (1 + -delta/t->period); + siftdown(0); + } else { + // remove from heap + timers.t[0] = timers.t[--timers.len]; + timers.t[0]->i = 0; + siftdown(0); + t->i = -1; // mark as removed + } + f = t->f; + arg = t->arg; + runtime_unlock(&timers); + f(now, arg); + runtime_lock(&timers); + } + if(delta < 0) { + // No timers left - put goroutine to sleep. + timers.rescheduling = true; + g->status = Gwaiting; + g->waitreason = "timer goroutine (idle)"; + runtime_unlock(&timers); + runtime_gosched(); + continue; + } + // At least one timer pending. Sleep until then. + timers.sleeping = true; + runtime_noteclear(&timers.waitnote); + runtime_unlock(&timers); + runtime_entersyscall(); + runtime_notetsleep(&timers.waitnote, delta); + runtime_exitsyscall(); + } +} + +// heap maintenance algorithms. + +static void +siftup(int32 i) +{ + int32 p; + Timer **t, *tmp; + + t = timers.t; + while(i > 0) { + p = (i-1)/2; // parent + if(t[i]->when >= t[p]->when) + break; + tmp = t[i]; + t[i] = t[p]; + t[p] = tmp; + t[i]->i = i; + t[p]->i = p; + i = p; + } +} + +static void +siftdown(int32 i) +{ + int32 c, len; + Timer **t, *tmp; + + t = timers.t; + len = timers.len; + for(;;) { + c = i*2 + 1; // left child + if(c >= len) { + break; + } + if(c+1 < len && t[c+1]->when < t[c]->when) + c++; + if(t[c]->when >= t[i]->when) + break; + tmp = t[i]; + t[i] = t[c]; + t[c] = tmp; + t[i]->i = i; + t[c]->i = c; + i = c; + } +} + +void +runtime_time_scan(void (*scan)(byte*, int64)) +{ + scan((byte*)&timers, sizeof timers); } |