diff options
1516 files changed, 83887 insertions, 18953 deletions
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 3af5bd4..175db5d 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -92a14213215fd93df7240fa9d376a1213b1d5a74 +7b25b4dff4778fc4d6b5d6e10594814146b3e5dd The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/testsuite/go.test/test/fixedbugs/bug273.go b/gcc/testsuite/go.test/test/fixedbugs/bug273.go index c04f211..7305c60 100644 --- a/gcc/testsuite/go.test/test/fixedbugs/bug273.go +++ b/gcc/testsuite/go.test/test/fixedbugs/bug273.go @@ -8,13 +8,15 @@ package main +import "unsafe" + var bug = false var minus1 = -1 var five = 5 -var big int64 = 10 | 1<<32 +var big int64 = 10 | 1<<40 -type block [1<<19]byte +type block [1 << 19]byte var g1 []block @@ -48,9 +50,10 @@ func bigcap() { g1 = make([]block, 10, big) } -type cblock [1<<16-1]byte +type cblock [1<<16 - 1]byte var g4 chan cblock + func badchancap() { g4 = make(chan cblock, minus1) } @@ -60,7 +63,8 @@ func bigchancap() { } func overflowchan() { - g4 = make(chan cblock, 1<<30) + const ptrSize = unsafe.Sizeof(uintptr(0)) + g4 = make(chan cblock, 1<<(30*(ptrSize/4))) } func main() { diff --git a/gcc/testsuite/go.test/test/fixedbugs/issue4085b.go b/gcc/testsuite/go.test/test/fixedbugs/issue4085b.go index 63aca23..6bf315f 100644 --- a/gcc/testsuite/go.test/test/fixedbugs/issue4085b.go +++ b/gcc/testsuite/go.test/test/fixedbugs/issue4085b.go @@ -1,6 +1,6 @@ // run -// Copyright 2013 The Go Authors. All rights reserved. +// Copyright 2013 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. @@ -15,22 +15,31 @@ type T []int func main() { n := -1 - shouldPanic("len out of range", func() {_ = make(T, n)}) - shouldPanic("cap out of range", func() {_ = make(T, 0, n)}) + shouldPanic("len out of range", func() { _ = make(T, n) }) + shouldPanic("cap out of range", func() { _ = make(T, 0, n) }) + shouldPanic("len out of range", func() { _ = make(T, int64(n)) }) + shouldPanic("cap out of range", func() { _ = make(T, 0, int64(n)) }) var t *byte if unsafe.Sizeof(t) == 8 { - n = 1<<20 - n <<= 20 - shouldPanic("len out of range", func() {_ = make(T, n)}) - shouldPanic("cap out of range", func() {_ = make(T, 0, n)}) - n <<= 20 - shouldPanic("len out of range", func() {_ = make(T, n)}) - shouldPanic("cap out of range", func() {_ = make(T, 0, n)}) + var n2 int64 = 1 << 50 + shouldPanic("len out of range", func() { _ = make(T, int(n2)) }) + shouldPanic("cap out of range", func() { _ = make(T, 0, int(n2)) }) + n2 = 1<<63 - 1 + shouldPanic("len out of range", func() { _ = make(T, int(n2)) }) + shouldPanic("cap out of range", func() { _ = make(T, 0, int(n2)) }) } else { n = 1<<31 - 1 - shouldPanic("len out of range", func() {_ = make(T, n)}) - shouldPanic("cap out of range", func() {_ = make(T, 0, n)}) + shouldPanic("len out of range", func() { _ = make(T, n) }) + shouldPanic("cap out of range", func() { _ = make(T, 0, n) }) + shouldPanic("len out of range", func() { _ = make(T, int64(n)) }) + shouldPanic("cap out of range", func() { _ = make(T, 0, int64(n)) }) } + + // Test make in append panics since the gc compiler optimizes makes in appends. + shouldPanic("len out of range", func() { _ = append(T{}, make(T, n)...) }) + shouldPanic("cap out of range", func() { _ = append(T{}, make(T, 0, n)...) }) + shouldPanic("len out of range", func() { _ = append(T{}, make(T, int64(n))...) }) + shouldPanic("cap out of range", func() { _ = append(T{}, make(T, 0, int64(n))...) }) } func shouldPanic(str string, f func()) { @@ -44,6 +53,6 @@ func shouldPanic(str string, f func()) { panic("got panic " + s + ", want " + str) } }() - + f() } diff --git a/gotools/ChangeLog b/gotools/ChangeLog index 0e67186..afcd7a2 100644 --- a/gotools/ChangeLog +++ b/gotools/ChangeLog @@ -1,3 +1,11 @@ +2018-09-24 Ian Lance Taylor <iant@golang.org> + + * Makefile.am (mostlyclean-local): Run chmod on check-go-dir to + make sure it is writable. + (check-go-tools): Likewise. + (check-vet): Copy internal/objabi to check-vet-dir. + * Makefile.in: Rebuild. + 2018-05-09 Ian Lance Taylor <iant@golang.org> * Makefile.am (check-go-tool): Don't copy zstdpkglist.go. diff --git a/gotools/Makefile.am b/gotools/Makefile.am index 06be89d..cb4ffe7 100644 --- a/gotools/Makefile.am +++ b/gotools/Makefile.am @@ -123,6 +123,7 @@ MOSTLYCLEANFILES = \ *.sent mostlyclean-local: + chmod -R u+w check-go-dir rm -rf check-go-dir check-runtime-dir cgo-test-dir carchive-test-dir if NATIVE @@ -228,6 +229,7 @@ ECHO_ENV = PATH=`echo $(abs_builddir):$${PATH} | sed 's,::*,:,g;s,^:*,,;s,:*$$,, # check-go-tool runs `go test cmd/go` in our environment. check-go-tool: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc + chmod -R u+w check-go-dir rm -rf check-go-dir cmd_go-testlog $(MKDIR_P) check-go-dir/src/cmd/go cp $(cmdsrcdir)/go/*.go check-go-dir/src/cmd/go/ @@ -297,8 +299,10 @@ check-carchive-test: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check # check-vet runs `go test cmd/vet` in our environment. check-vet: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc rm -rf check-vet-dir cmd_vet-testlog - $(MKDIR_P) check-vet-dir/src/cmd + $(MKDIR_P) check-vet-dir/src/cmd/internal cp -r $(cmdsrcdir)/vet check-vet-dir/src/cmd/ + cp -r $(cmdsrcdir)/internal/objabi check-vet-dir/src/cmd/internal + cp $(libgodir)/objabi.go check-vet-dir/src/cmd/internal/objabi/ @abs_libgodir=`cd $(libgodir) && $(PWD_COMMAND)`; \ abs_checkdir=`cd check-vet-dir && $(PWD_COMMAND)`; \ echo "cd check-vet-dir/src/cmd/vet && $(ECHO_ENV) GOPATH=$${abs_checkdir} $(abs_builddir)/go$(EXEEXT) test -test.short -test.timeout=$(GOTOOLS_TEST_TIMEOUT)s -test.v" > cmd_vet-testlog diff --git a/gotools/Makefile.in b/gotools/Makefile.in index 503ec4e..acadfc6 100644 --- a/gotools/Makefile.in +++ b/gotools/Makefile.in @@ -637,8 +637,8 @@ distclean-generic: maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -@NATIVE_FALSE@uninstall-local: @NATIVE_FALSE@install-exec-local: +@NATIVE_FALSE@uninstall-local: clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-noinstPROGRAMS \ @@ -744,6 +744,7 @@ s-zdefaultcc: Makefile $(STAMP) $@ mostlyclean-local: + chmod -R u+w check-go-dir rm -rf check-go-dir check-runtime-dir cgo-test-dir carchive-test-dir @NATIVE_TRUE@go$(EXEEXT): $(go_cmd_go_files) $(LIBGOTOOL) $(LIBGODEP) @@ -807,6 +808,7 @@ mostlyclean-local: # check-go-tool runs `go test cmd/go` in our environment. @NATIVE_TRUE@check-go-tool: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc +@NATIVE_TRUE@ chmod -R u+w check-go-dir @NATIVE_TRUE@ rm -rf check-go-dir cmd_go-testlog @NATIVE_TRUE@ $(MKDIR_P) check-go-dir/src/cmd/go @NATIVE_TRUE@ cp $(cmdsrcdir)/go/*.go check-go-dir/src/cmd/go/ @@ -876,8 +878,10 @@ mostlyclean-local: # check-vet runs `go test cmd/vet` in our environment. @NATIVE_TRUE@check-vet: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc @NATIVE_TRUE@ rm -rf check-vet-dir cmd_vet-testlog -@NATIVE_TRUE@ $(MKDIR_P) check-vet-dir/src/cmd +@NATIVE_TRUE@ $(MKDIR_P) check-vet-dir/src/cmd/internal @NATIVE_TRUE@ cp -r $(cmdsrcdir)/vet check-vet-dir/src/cmd/ +@NATIVE_TRUE@ cp -r $(cmdsrcdir)/internal/objabi check-vet-dir/src/cmd/internal +@NATIVE_TRUE@ cp $(libgodir)/objabi.go check-vet-dir/src/cmd/internal/objabi/ @NATIVE_TRUE@ @abs_libgodir=`cd $(libgodir) && $(PWD_COMMAND)`; \ @NATIVE_TRUE@ abs_checkdir=`cd check-vet-dir && $(PWD_COMMAND)`; \ @NATIVE_TRUE@ echo "cd check-vet-dir/src/cmd/vet && $(ECHO_ENV) GOPATH=$${abs_checkdir} $(abs_builddir)/go$(EXEEXT) test -test.short -test.timeout=$(GOTOOLS_TEST_TIMEOUT)s -test.v" > cmd_vet-testlog diff --git a/libgo/MERGE b/libgo/MERGE index c54da0d..9b90798 100644 --- a/libgo/MERGE +++ b/libgo/MERGE @@ -1,4 +1,4 @@ -fe8a0d12b14108cbe2408b417afcaab722b0727c +41e62b8c49d21659b48a95216e3062032285250f The first line of this file holds the git revision number of the last merge done from the master library sources. diff --git a/libgo/Makefile.am b/libgo/Makefile.am index d847413..373dd0c 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -541,6 +541,7 @@ s-objabi: Makefile echo 'const defaultGO386 = `sse2`' >> objabi.go.tmp echo 'const defaultGOARM = `5`' >> objabi.go.tmp echo 'const defaultGOMIPS = `hardfloat`' >> objabi.go.tmp + echo 'const defaultGOMIPS64 = `hardfloat`' >> objabi.go.tmp echo 'const defaultGOOS = runtime.GOOS' >> objabi.go.tmp echo 'const defaultGOARCH = runtime.GOARCH' >> objabi.go.tmp echo 'const defaultGO_EXTLINK_ENABLED = ``' >> objabi.go.tmp @@ -608,7 +609,7 @@ noinst_DATA += zdefaultcc.go zstdpkglist.go: s-zstdpkglist; @true s-zstdpkglist: Makefile rm -f zstdpkglist.go.tmp - echo 'package build' > zstdpkglist.go.tmp + echo 'package goroot' > zstdpkglist.go.tmp echo "" >> zstdpkglist.go.tmp echo 'var stdpkg = map[string]bool{' >> zstdpkglist.go.tmp echo $(libgo_go_objs) 'unsafe.lo' 'runtime/cgo.lo' | sed 's|[a-z0-9_/]*_c\.lo||g' | sed 's|\([a-z0-9_/]*\)\.lo|"\1": true,|g' >> zstdpkglist.go.tmp @@ -707,6 +708,7 @@ PACKAGES = $(shell cat $(srcdir)/libgo-packages.txt) libgo_go_objs = \ $(addsuffix .lo,$(PACKAGES)) \ bytes/index.lo \ + internal/bytealg/bytealg.lo \ reflect/makefunc_ffi_c.lo \ strings/index.lo \ $(syscall_lib_clone_lo) \ @@ -718,7 +720,8 @@ libgo_go_objs = \ log/syslog/syslog_c.lo \ $(os_lib_inotify_lo) \ runtime/internal/atomic_c.lo \ - sync/atomic_c.lo + sync/atomic_c.lo \ + internal/cpu/cpu_gccgo.lo libgo_ldflags = \ -version-info $(libtool_VERSION) $(PTHREAD_CFLAGS) $(AM_LDFLAGS) @@ -960,8 +963,8 @@ runtime_pprof_check_GOCFLAGS = -static-libgo -fno-inline extra_go_files_runtime_internal_sys = version.go runtime/internal/sys.lo.dep: $(extra_go_files_runtime_internal_sys) -extra_go_files_go_build = zstdpkglist.go -go/build.lo.dep: $(extra_go_files_go_build) +extra_go_files_internal_goroot = zstdpkglist.go +internal/goroot.lo.dep: $(extra_go_files_internal_goroot) extra_go_files_go_types = gccgosizes.go go/types.lo.dep: $(extra_go_files_go_types) @@ -976,6 +979,16 @@ extra_check_libs_cmd_go_internal_cache = $(abs_builddir)/libgotool.a extra_check_libs_cmd_go_internal_generate = $(abs_builddir)/libgotool.a extra_check_libs_cmd_go_internal_get = $(abs_builddir)/libgotool.a extra_check_libs_cmd_go_internal_load = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_imports = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_modconv = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_modfetch = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_modfetch_codehost = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_modfile = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_modload = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_module = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_mvs = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_search = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_web2 = $(abs_builddir)/libgotool.a extra_check_libs_cmd_go_internal_work = $(abs_builddir)/libgotool.a extra_check_libs_cmd_vet_internal_cfg = $(abs_builddir)/libgotool.a @@ -990,6 +1003,9 @@ bytes/index.lo: go/bytes/indexbyte.c runtime.inc strings/index.lo: go/strings/indexbyte.c runtime.inc @$(MKDIR_P) strings $(LTCOMPILE) -c -o $@ $(srcdir)/go/strings/indexbyte.c +internal/bytealg/bytealg.lo: go/internal/bytealg/bytealg.c runtime.inc + @$(MKDIR_P) internal/bytealg + $(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/bytealg/bytealg.c # Use a C function with a fixed number of arguments to call a C # varargs function. @@ -1024,6 +1040,11 @@ syscall/wait.lo: go/syscall/wait.c runtime.inc @$(MKDIR_P) syscall $(LTCOMPILE) -c -o $@ $(srcdir)/go/syscall/wait.c +# internal/cpu needs some C code. +internal/cpu/cpu_gccgo.lo: go/internal/cpu/cpu_gccgo.c runtime.inc + @$(MKDIR_P) internal/cpu + $(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/cpu/cpu_gccgo.c + # Solaris 11.4 changed the type of fields in struct stat. # Use a build tag, based on a configure check, to cope. if LIBGO_IS_SOLARIS diff --git a/libgo/Makefile.in b/libgo/Makefile.in index 6b3c597..dfc2c66 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -174,11 +174,12 @@ libgotool_a_OBJECTS = $(am_libgotool_a_OBJECTS) LTLIBRARIES = $(toolexeclib_LTLIBRARIES) @LIBGO_IS_LINUX_TRUE@am__DEPENDENCIES_1 = syscall/clone_linux.lo am__DEPENDENCIES_2 = $(addsuffix .lo,$(PACKAGES)) bytes/index.lo \ - reflect/makefunc_ffi_c.lo strings/index.lo \ - $(am__DEPENDENCIES_1) syscall/errno.lo syscall/signame.lo \ - syscall/wait.lo $(golang_org_x_net_lif_lo) \ + internal/bytealg/bytealg.lo reflect/makefunc_ffi_c.lo \ + strings/index.lo $(am__DEPENDENCIES_1) syscall/errno.lo \ + syscall/signame.lo syscall/wait.lo $(golang_org_x_net_lif_lo) \ $(golang_org_x_net_route_lo) log/syslog/syslog_c.lo \ - runtime/internal/atomic_c.lo sync/atomic_c.lo + runtime/internal/atomic_c.lo sync/atomic_c.lo \ + internal/cpu/cpu_gccgo.lo am__DEPENDENCIES_3 = am__DEPENDENCIES_4 = $(am__DEPENDENCIES_2) \ ../libbacktrace/libbacktrace.la $(am__DEPENDENCIES_3) \ @@ -824,6 +825,7 @@ PACKAGES = $(shell cat $(srcdir)/libgo-packages.txt) libgo_go_objs = \ $(addsuffix .lo,$(PACKAGES)) \ bytes/index.lo \ + internal/bytealg/bytealg.lo \ reflect/makefunc_ffi_c.lo \ strings/index.lo \ $(syscall_lib_clone_lo) \ @@ -835,7 +837,8 @@ libgo_go_objs = \ log/syslog/syslog_c.lo \ $(os_lib_inotify_lo) \ runtime/internal/atomic_c.lo \ - sync/atomic_c.lo + sync/atomic_c.lo \ + internal/cpu/cpu_gccgo.lo libgo_ldflags = \ -version-info $(libtool_VERSION) $(PTHREAD_CFLAGS) $(AM_LDFLAGS) @@ -999,7 +1002,7 @@ runtime_internal_sys_lo_check_GOCFLAGS = -fgo-compiling-runtime # Also use -fno-inline to get better results from the memory profiler. runtime_pprof_check_GOCFLAGS = -static-libgo -fno-inline extra_go_files_runtime_internal_sys = version.go -extra_go_files_go_build = zstdpkglist.go +extra_go_files_internal_goroot = zstdpkglist.go extra_go_files_go_types = gccgosizes.go extra_go_files_cmd_internal_objabi = objabi.go extra_go_files_cmd_go_internal_cfg = zdefaultcc.go @@ -1007,6 +1010,16 @@ extra_check_libs_cmd_go_internal_cache = $(abs_builddir)/libgotool.a extra_check_libs_cmd_go_internal_generate = $(abs_builddir)/libgotool.a extra_check_libs_cmd_go_internal_get = $(abs_builddir)/libgotool.a extra_check_libs_cmd_go_internal_load = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_imports = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_modconv = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_modfetch = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_modfetch_codehost = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_modfile = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_modload = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_module = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_mvs = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_search = $(abs_builddir)/libgotool.a +extra_check_libs_cmd_go_internal_web2 = $(abs_builddir)/libgotool.a extra_check_libs_cmd_go_internal_work = $(abs_builddir)/libgotool.a extra_check_libs_cmd_vet_internal_cfg = $(abs_builddir)/libgotool.a @HAVE_STAT_TIMESPEC_FALSE@@LIBGO_IS_SOLARIS_TRUE@matchargs_os = @@ -2755,6 +2768,7 @@ s-objabi: Makefile echo 'const defaultGO386 = `sse2`' >> objabi.go.tmp echo 'const defaultGOARM = `5`' >> objabi.go.tmp echo 'const defaultGOMIPS = `hardfloat`' >> objabi.go.tmp + echo 'const defaultGOMIPS64 = `hardfloat`' >> objabi.go.tmp echo 'const defaultGOOS = runtime.GOOS' >> objabi.go.tmp echo 'const defaultGOARCH = runtime.GOARCH' >> objabi.go.tmp echo 'const defaultGO_EXTLINK_ENABLED = ``' >> objabi.go.tmp @@ -2816,7 +2830,7 @@ s-runtime-inc: runtime.lo Makefile zstdpkglist.go: s-zstdpkglist; @true s-zstdpkglist: Makefile rm -f zstdpkglist.go.tmp - echo 'package build' > zstdpkglist.go.tmp + echo 'package goroot' > zstdpkglist.go.tmp echo "" >> zstdpkglist.go.tmp echo 'var stdpkg = map[string]bool{' >> zstdpkglist.go.tmp echo $(libgo_go_objs) 'unsafe.lo' 'runtime/cgo.lo' | sed 's|[a-z0-9_/]*_c\.lo||g' | sed 's|\([a-z0-9_/]*\)\.lo|"\1": true,|g' >> zstdpkglist.go.tmp @@ -2943,7 +2957,7 @@ $(foreach package,$(GOTOOL_PACKAGES),$(eval $(call PACKAGE_template,$(package))) runtime.lo.dep: $(extra_go_files_runtime) syscall.lo.dep: $(extra_go_files_syscall) runtime/internal/sys.lo.dep: $(extra_go_files_runtime_internal_sys) -go/build.lo.dep: $(extra_go_files_go_build) +internal/goroot.lo.dep: $(extra_go_files_internal_goroot) go/types.lo.dep: $(extra_go_files_go_types) cmd/internal/objabi.lo.dep: $(extra_go_files_cmd_internal_objabi) cmd/go/internal/cfg.lo.dep: $(extra_go_files_cmd_go_internal_cfg) @@ -2958,6 +2972,9 @@ bytes/index.lo: go/bytes/indexbyte.c runtime.inc strings/index.lo: go/strings/indexbyte.c runtime.inc @$(MKDIR_P) strings $(LTCOMPILE) -c -o $@ $(srcdir)/go/strings/indexbyte.c +internal/bytealg/bytealg.lo: go/internal/bytealg/bytealg.c runtime.inc + @$(MKDIR_P) internal/bytealg + $(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/bytealg/bytealg.c # Use a C function with a fixed number of arguments to call a C # varargs function. @@ -2992,6 +3009,11 @@ syscall/wait.lo: go/syscall/wait.c runtime.inc @$(MKDIR_P) syscall $(LTCOMPILE) -c -o $@ $(srcdir)/go/syscall/wait.c +# internal/cpu needs some C code. +internal/cpu/cpu_gccgo.lo: go/internal/cpu/cpu_gccgo.c runtime.inc + @$(MKDIR_P) internal/cpu + $(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/cpu/cpu_gccgo.c + # Build golang_org/x/net/route only on BSD systems. @LIBGO_IS_BSD_TRUE@$(eval $(call PACKAGE_template,golang_org/x/net/route)) diff --git a/libgo/VERSION b/libgo/VERSION index 038f0b9..f3220fb 100644 --- a/libgo/VERSION +++ b/libgo/VERSION @@ -1 +1 @@ -go1.10.3 +go1.11 diff --git a/libgo/check-packages.txt b/libgo/check-packages.txt index 82a08c6..6c307d3 100644 --- a/libgo/check-packages.txt +++ b/libgo/check-packages.txt @@ -3,9 +3,23 @@ archive/zip bufio bytes cmd/go/internal/cache +cmd/go/internal/dirhash cmd/go/internal/generate cmd/go/internal/get +cmd/go/internal/imports cmd/go/internal/load +cmd/go/internal/modconv +cmd/go/internal/modfetch +cmd/go/internal/modfetch/codehost +cmd/go/internal/modfile +cmd/go/internal/modload +cmd/go/internal/module +cmd/go/internal/mvs +cmd/go/internal/par +cmd/go/internal/search +cmd/go/internal/semver +cmd/go/internal/txtar +cmd/go/internal/web2 cmd/go/internal/work cmd/internal/buildid cmd/internal/edit @@ -29,6 +43,7 @@ crypto/dsa crypto/ecdsa crypto/elliptic crypto/hmac +crypto/internal/subtle crypto/md5 crypto/rand crypto/rc4 @@ -76,11 +91,15 @@ go/printer go/scanner go/token go/types +golang_org/x/crypto/internal/chacha20 golang_org/x/crypto/chacha20poly1305 golang_org/x/crypto/chacha20poly1305/internal/chacha20 golang_org/x/crypto/cryptobyte golang_org/x/crypto/curve25519 golang_org/x/crypto/poly1305 +golang_org/x/net/dns/dnsmessage +golang_org/x/net/http/httpguts +golang_org/x/net/http/httpproxy golang_org/x/net/http2/hpack golang_org/x/net/idna golang_org/x/net/lex/httplex @@ -98,6 +117,7 @@ image/draw image/jpeg image/png index/suffixarray +internal/cpu internal/poll internal/singleflight internal/trace diff --git a/libgo/go/archive/tar/common.go b/libgo/go/archive/tar/common.go index 4a2c173..dee9e47 100644 --- a/libgo/go/archive/tar/common.go +++ b/libgo/go/archive/tar/common.go @@ -56,7 +56,7 @@ func (he headerError) Error() string { const ( // Type '0' indicates a regular file. TypeReg = '0' - TypeRegA = '\x00' // For legacy support; use TypeReg instead + TypeRegA = '\x00' // Deprecated: Use TypeReg instead. // Type '1' to '6' are header-only flags and may not have a data body. TypeLink = '1' // Hard link @@ -138,7 +138,10 @@ var basicKeys = map[string]bool{ // should do so by creating a new Header and copying the fields // that they are interested in preserving. type Header struct { - Typeflag byte // Type of header entry (should be TypeReg for most files) + // Typeflag is the type of header entry. + // The zero value is automatically promoted to either TypeReg or TypeDir + // depending on the presence of a trailing slash in Name. + Typeflag byte Name string // Name of file entry Linkname string // Target name of link (valid for TypeLink or TypeSymlink) @@ -184,7 +187,7 @@ type Header struct { // The key and value should be non-empty UTF-8 strings. // // When Writer.WriteHeader is called, PAX records derived from the - // the other fields in Header take precedence over PAXRecords. + // other fields in Header take precedence over PAXRecords. PAXRecords map[string]string // Format specifies the format of the tar header. diff --git a/libgo/go/archive/tar/format.go b/libgo/go/archive/tar/format.go index 6e29698..1f89d0c 100644 --- a/libgo/go/archive/tar/format.go +++ b/libgo/go/archive/tar/format.go @@ -94,7 +94,7 @@ const ( // application can only parse GNU formatted archives. // // Reference: - // http://www.gnu.org/software/tar/manual/html_node/Standard.html + // https://www.gnu.org/software/tar/manual/html_node/Standard.html FormatGNU // Schily's tar format, which is incompatible with USTAR. diff --git a/libgo/go/archive/tar/reader.go b/libgo/go/archive/tar/reader.go index f4eeb55..3943718 100644 --- a/libgo/go/archive/tar/reader.go +++ b/libgo/go/archive/tar/reader.go @@ -64,7 +64,6 @@ func (tr *Reader) next() (*Header, error) { // normally be visible to the outside. As such, this loop iterates through // one or more "header files" until it finds a "normal file". format := FormatUSTAR | FormatPAX | FormatGNU -loop: for { // Discard the remainder of the file and any padding. if err := discard(tr.r, tr.curr.PhysicalRemaining()); err != nil { @@ -102,7 +101,7 @@ loop: Format: format, }, nil } - continue loop // This is a meta header affecting the next header + continue // This is a meta header affecting the next header case TypeGNULongName, TypeGNULongLink: format.mayOnlyBe(FormatGNU) realname, err := ioutil.ReadAll(tr) @@ -117,7 +116,7 @@ loop: case TypeGNULongLink: gnuLongLink = p.parseString(realname) } - continue loop // This is a meta header affecting the next header + continue // This is a meta header affecting the next header default: // The old GNU sparse format is handled here since it is technically // just a regular file with additional attributes. @@ -131,8 +130,12 @@ loop: if gnuLongLink != "" { hdr.Linkname = gnuLongLink } - if hdr.Typeflag == TypeRegA && strings.HasSuffix(hdr.Name, "/") { - hdr.Typeflag = TypeDir // Legacy archives use trailing slash for directories + if hdr.Typeflag == TypeRegA { + if strings.HasSuffix(hdr.Name, "/") { + hdr.Typeflag = TypeDir // Legacy archives use trailing slash for directories + } else { + hdr.Typeflag = TypeReg + } } // The extended headers may have updated the size. @@ -200,7 +203,7 @@ func (tr *Reader) handleSparseFile(hdr *Header, rawHdr *block) error { // readGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers. // If they are found, then this function reads the sparse map and returns it. // This assumes that 0.0 headers have already been converted to 0.1 headers -// by the the PAX header parsing logic. +// by the PAX header parsing logic. func (tr *Reader) readGNUSparsePAXHeaders(hdr *Header) (sparseDatas, error) { // Identify the version of GNU headers. var is1x0 bool diff --git a/libgo/go/archive/tar/reader_test.go b/libgo/go/archive/tar/reader_test.go index a6832d3..f153b66 100644 --- a/libgo/go/archive/tar/reader_test.go +++ b/libgo/go/archive/tar/reader_test.go @@ -189,7 +189,7 @@ func TestReader(t *testing.T) { Gid: 5000, Size: 5, ModTime: time.Unix(1244593104, 0), - Typeflag: '\x00', + Typeflag: '0', }, { Name: "small2.txt", Mode: 0444, @@ -197,7 +197,7 @@ func TestReader(t *testing.T) { Gid: 5000, Size: 11, ModTime: time.Unix(1244593104, 0), - Typeflag: '\x00', + Typeflag: '0', }}, }, { file: "testdata/pax.tar", @@ -378,9 +378,9 @@ func TestReader(t *testing.T) { "security.selinux": "unconfined_u:object_r:default_t:s0\x00", }, PAXRecords: map[string]string{ - "mtime": "1386065770.449252304", - "atime": "1389782991.41987522", - "ctime": "1386065770.449252304", + "mtime": "1386065770.449252304", + "atime": "1389782991.41987522", + "ctime": "1386065770.449252304", "SCHILY.xattr.security.selinux": "unconfined_u:object_r:default_t:s0\x00", }, Format: FormatPAX, @@ -534,9 +534,10 @@ func TestReader(t *testing.T) { // a buggy pre-Go1.8 tar.Writer. file: "testdata/invalid-go17.tar", headers: []*Header{{ - Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/foo", - Uid: 010000000, - ModTime: time.Unix(0, 0), + Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/foo", + Uid: 010000000, + ModTime: time.Unix(0, 0), + Typeflag: '0', }}, }, { // USTAR archive with a regular entry with non-zero device numbers. diff --git a/libgo/go/archive/tar/tar_test.go b/libgo/go/archive/tar/tar_test.go index af80d6e..2676853 100644 --- a/libgo/go/archive/tar/tar_test.go +++ b/libgo/go/archive/tar/tar_test.go @@ -306,6 +306,7 @@ func TestRoundTrip(t *testing.T) { ModTime: time.Now().Round(time.Second), PAXRecords: map[string]string{"uid": "2097152"}, Format: FormatPAX, + Typeflag: TypeReg, } if err := tw.WriteHeader(hdr); err != nil { t.Fatalf("tw.WriteHeader: %v", err) diff --git a/libgo/go/archive/tar/testdata/file-and-dir.tar b/libgo/go/archive/tar/testdata/file-and-dir.tar Binary files differnew file mode 100644 index 0000000..c18d428 --- /dev/null +++ b/libgo/go/archive/tar/testdata/file-and-dir.tar diff --git a/libgo/go/archive/tar/testdata/trailing-slash.tar b/libgo/go/archive/tar/testdata/trailing-slash.tar Binary files differindex bf1b2ec..93718b3 100644 --- a/libgo/go/archive/tar/testdata/trailing-slash.tar +++ b/libgo/go/archive/tar/testdata/trailing-slash.tar diff --git a/libgo/go/archive/tar/writer.go b/libgo/go/archive/tar/writer.go index 97d23f8..e80498d 100644 --- a/libgo/go/archive/tar/writer.go +++ b/libgo/go/archive/tar/writer.go @@ -5,7 +5,6 @@ package tar import ( - "bytes" "fmt" "io" "path" @@ -71,6 +70,16 @@ func (tw *Writer) WriteHeader(hdr *Header) error { } tw.hdr = *hdr // Shallow copy of Header + // Avoid usage of the legacy TypeRegA flag, and automatically promote + // it to use TypeReg or TypeDir. + if tw.hdr.Typeflag == TypeRegA { + if strings.HasSuffix(tw.hdr.Name, "/") { + tw.hdr.Typeflag = TypeDir + } else { + tw.hdr.Typeflag = TypeReg + } + } + // Round ModTime and ignore AccessTime and ChangeTime unless // the format is explicitly chosen. // This ensures nominal usage of WriteHeader (without specifying the format) @@ -166,7 +175,7 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error { sort.Strings(keys) // Write each record to a buffer. - var buf bytes.Buffer + var buf strings.Builder for _, k := range keys { rec, err := formatPAXRecord(k, paxHdrs[k]) if err != nil { diff --git a/libgo/go/archive/tar/writer_test.go b/libgo/go/archive/tar/writer_test.go index 24e8da2..30556d2 100644 --- a/libgo/go/archive/tar/writer_test.go +++ b/libgo/go/archive/tar/writer_test.go @@ -461,6 +461,15 @@ func TestWriter(t *testing.T) { testHeader{Header{Name: strings.Repeat("123456789/", 30)}, nil}, testClose{nil}, }, + }, { + // Automatically promote zero value of Typeflag depending on the name. + file: "testdata/file-and-dir.tar", + tests: []testFnc{ + testHeader{Header{Name: "small.txt", Size: 5}, nil}, + testWrite{"Kilts", 5, nil}, + testHeader{Header{Name: "dir/"}, nil}, + testClose{nil}, + }, }} equalError := func(x, y error) bool { @@ -809,8 +818,8 @@ func TestValidTypeflagWithPAXHeader(t *testing.T) { if err != nil { t.Fatalf("Failed to read header: %s", err) } - if header.Typeflag != 0 { - t.Fatalf("Typeflag should've been 0, found %d", header.Typeflag) + if header.Typeflag != TypeReg { + t.Fatalf("Typeflag should've been %d, found %d", TypeReg, header.Typeflag) } } } diff --git a/libgo/go/archive/zip/struct.go b/libgo/go/archive/zip/struct.go index f613ebd..c90151d 100644 --- a/libgo/go/archive/zip/struct.go +++ b/libgo/go/archive/zip/struct.go @@ -81,8 +81,17 @@ const ( // See the zip spec for details. type FileHeader struct { // Name is the name of the file. - // It must be a relative path, not start with a drive letter (e.g. C:), - // and must use forward slashes instead of back slashes. + // + // It must be a relative path, not start with a drive letter (such as "C:"), + // and must use forward slashes instead of back slashes. A trailing slash + // indicates that this file is a directory and should have no data. + // + // When reading zip files, the Name field is populated from + // the zip file directly and is not validated for correctness. + // It is the caller's responsibility to sanitize it as + // appropriate, including canonicalizing slash directions, + // validating that paths are relative, and preventing path + // traversal through filenames ("../../../"). Name string // Comment is any arbitrary user-defined string shorter than 64KiB. @@ -201,7 +210,7 @@ func timeZone(offset time.Duration) *time.Location { // msDosTimeToTime converts an MS-DOS date and time into a time.Time. // The resolution is 2s. -// See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx +// See: https://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx func msDosTimeToTime(dosDate, dosTime uint16) time.Time { return time.Date( // date bits 0-4: day of month; 5-8: month; 9-15: years since 1980 @@ -221,7 +230,7 @@ func msDosTimeToTime(dosDate, dosTime uint16) time.Time { // timeToMsDosTime converts a time.Time to an MS-DOS date and time. // The resolution is 2s. -// See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx +// See: https://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) { fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9) fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11) diff --git a/libgo/go/archive/zip/writer.go b/libgo/go/archive/zip/writer.go index 14a5ee4..5f0c0a1 100644 --- a/libgo/go/archive/zip/writer.go +++ b/libgo/go/archive/zip/writer.go @@ -11,6 +11,7 @@ import ( "hash" "hash/crc32" "io" + "strings" "unicode/utf8" ) @@ -71,7 +72,7 @@ func (w *Writer) SetComment(comment string) error { } // Close finishes writing the zip file by writing the central directory. -// It does not (and cannot) close the underlying writer. +// It does not close the underlying writer. func (w *Writer) Close() error { if w.last != nil && !w.last.closed { if err := w.last.close(); err != nil { @@ -209,7 +210,8 @@ func (w *Writer) Close() error { // The file contents will be compressed using the Deflate method. // The name must be a relative path: it must not start with a drive // letter (e.g. C:) or leading slash, and only forward slashes are -// allowed. +// allowed. To create a directory instead of a file, add a trailing +// slash to the name. // The file's contents must be written to the io.Writer before the next // call to Create, CreateHeader, or Close. func (w *Writer) Create(name string) (io.Writer, error) { @@ -261,8 +263,6 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { return nil, errors.New("archive/zip: invalid duplicate FileHeader") } - fh.Flags |= 0x8 // we will write a data descriptor - // The ZIP format has a sad state of affairs regarding character encoding. // Officially, the name and comment fields are supposed to be encoded // in CP-437 (which is mostly compatible with ASCII), unless the UTF-8 @@ -319,35 +319,58 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { fh.Extra = append(fh.Extra, mbuf[:]...) } - fw := &fileWriter{ - zipw: w.cw, - compCount: &countWriter{w: w.cw}, - crc32: crc32.NewIEEE(), - } - comp := w.compressor(fh.Method) - if comp == nil { - return nil, ErrAlgorithm - } - var err error - fw.comp, err = comp(fw.compCount) - if err != nil { - return nil, err - } - fw.rawCount = &countWriter{w: fw.comp} - + var ( + ow io.Writer + fw *fileWriter + ) h := &header{ FileHeader: fh, offset: uint64(w.cw.count), } - w.dir = append(w.dir, h) - fw.header = h + if strings.HasSuffix(fh.Name, "/") { + // Set the compression method to Store to ensure data length is truly zero, + // which the writeHeader method always encodes for the size fields. + // This is necessary as most compression formats have non-zero lengths + // even when compressing an empty string. + fh.Method = Store + fh.Flags &^= 0x8 // we will not write a data descriptor + + // Explicitly clear sizes as they have no meaning for directories. + fh.CompressedSize = 0 + fh.CompressedSize64 = 0 + fh.UncompressedSize = 0 + fh.UncompressedSize64 = 0 + + ow = dirWriter{} + } else { + fh.Flags |= 0x8 // we will write a data descriptor + + fw = &fileWriter{ + zipw: w.cw, + compCount: &countWriter{w: w.cw}, + crc32: crc32.NewIEEE(), + } + comp := w.compressor(fh.Method) + if comp == nil { + return nil, ErrAlgorithm + } + var err error + fw.comp, err = comp(fw.compCount) + if err != nil { + return nil, err + } + fw.rawCount = &countWriter{w: fw.comp} + fw.header = h + ow = fw + } + w.dir = append(w.dir, h) if err := writeHeader(w.cw, fh); err != nil { return nil, err } - + // If we're creating a directory, fw is nil. w.last = fw - return fw, nil + return ow, nil } func writeHeader(w io.Writer, h *FileHeader) error { @@ -400,6 +423,15 @@ func (w *Writer) compressor(method uint16) Compressor { return comp } +type dirWriter struct{} + +func (dirWriter) Write(b []byte) (int, error) { + if len(b) == 0 { + return 0, nil + } + return 0, errors.New("zip: write to directory") +} + type fileWriter struct { *header zipw io.Writer diff --git a/libgo/go/archive/zip/writer_test.go b/libgo/go/archive/zip/writer_test.go index 38f3229..1fedfd8 100644 --- a/libgo/go/archive/zip/writer_test.go +++ b/libgo/go/archive/zip/writer_test.go @@ -6,6 +6,7 @@ package zip import ( "bytes" + "encoding/binary" "fmt" "io" "io/ioutil" @@ -299,6 +300,59 @@ func TestWriterFlush(t *testing.T) { } } +func TestWriterDir(t *testing.T) { + w := NewWriter(ioutil.Discard) + dw, err := w.Create("dir/") + if err != nil { + t.Fatal(err) + } + if _, err := dw.Write(nil); err != nil { + t.Errorf("Write(nil) to directory: got %v, want nil", err) + } + if _, err := dw.Write([]byte("hello")); err == nil { + t.Error(`Write("hello") to directory: got nil error, want non-nil`) + } +} + +func TestWriterDirAttributes(t *testing.T) { + var buf bytes.Buffer + w := NewWriter(&buf) + if _, err := w.CreateHeader(&FileHeader{ + Name: "dir/", + Method: Deflate, + CompressedSize64: 1234, + UncompressedSize64: 5678, + }); err != nil { + t.Fatal(err) + } + if err := w.Close(); err != nil { + t.Fatal(err) + } + b := buf.Bytes() + + var sig [4]byte + binary.LittleEndian.PutUint32(sig[:], uint32(fileHeaderSignature)) + + idx := bytes.Index(b, sig[:]) + if idx == -1 { + t.Fatal("file header not found") + } + b = b[idx:] + + if !bytes.Equal(b[6:10], []byte{0, 0, 0, 0}) { // FileHeader.Flags: 0, FileHeader.Method: 0 + t.Errorf("unexpected method and flags: %v", b[6:10]) + } + + if !bytes.Equal(b[14:26], make([]byte, 12)) { // FileHeader.{CRC32,CompressSize,UncompressedSize} all zero. + t.Errorf("unexpected crc, compress and uncompressed size to be 0 was: %v", b[14:26]) + } + + binary.LittleEndian.PutUint32(sig[:], uint32(dataDescriptorSignature)) + if bytes.Index(b, sig[:]) != -1 { + t.Error("there should be no data descriptor") + } +} + func testCreate(t *testing.T, w *Writer, wt *WriteTest) { header := &FileHeader{ Name: wt.Name, diff --git a/libgo/go/archive/zip/zip_test.go b/libgo/go/archive/zip/zip_test.go index 7e02cb0..50218a2 100644 --- a/libgo/go/archive/zip/zip_test.go +++ b/libgo/go/archive/zip/zip_test.go @@ -15,6 +15,7 @@ import ( "internal/testenv" "io" "io/ioutil" + "runtime" "sort" "strings" "testing" @@ -140,14 +141,7 @@ func (r *rleBuffer) Write(p []byte) (n int, err error) { rp = &r.buf[len(r.buf)-1] // Fast path, if p is entirely the same byte repeated. if lastByte := rp.b; len(p) > 0 && p[0] == lastByte { - all := true - for _, b := range p { - if b != lastByte { - all = false - break - } - } - if all { + if bytes.Count(p, []byte{lastByte}) == len(p) { rp.n += int64(len(p)) return len(p), nil } @@ -165,6 +159,25 @@ func (r *rleBuffer) Write(p []byte) (n int, err error) { return len(p), nil } +func min(x, y int) int { + if x < y { + return x + } + return y +} + +func memset(a []byte, b byte) { + if len(a) == 0 { + return + } + // Double, until we reach power of 2 >= len(a), same as bytes.Repeat, + // but without allocation. + a[0] = b + for i, l := 1, len(a); i < l; i *= 2 { + copy(a[i:], a[:i]) + } +} + func (r *rleBuffer) ReadAt(p []byte, off int64) (n int, err error) { if len(p) == 0 { return @@ -176,16 +189,13 @@ func (r *rleBuffer) ReadAt(p []byte, off int64) (n int, err error) { parts := r.buf[skipParts:] if len(parts) > 0 { skipBytes := off - parts[0].off - for len(parts) > 0 { - part := parts[0] - for i := skipBytes; i < part.n; i++ { - if n == len(p) { - return - } - p[n] = part.b - n++ + for _, part := range parts { + repeat := min(int(part.n-skipBytes), len(p)-n) + memset(p[n:n+repeat], part.b) + n += repeat + if n == len(p) { + return } - parts = parts[1:] skipBytes = 0 } } @@ -452,6 +462,9 @@ func suffixIsZip64(t *testing.T, zip sizedReaderAt) bool { // Zip64 is required if the total size of the records is uint32max. func TestZip64LargeDirectory(t *testing.T) { + if runtime.GOARCH == "wasm" { + t.Skip("too slow on wasm") + } if testing.Short() { t.Skip("skipping in short mode") } diff --git a/libgo/go/bufio/bufio.go b/libgo/go/bufio/bufio.go index ad9c9f5..72545a7 100644 --- a/libgo/go/bufio/bufio.go +++ b/libgo/go/bufio/bufio.go @@ -462,6 +462,8 @@ func (b *Reader) ReadString(delim byte) (string, error) { // WriteTo implements io.WriterTo. // This may make multiple calls to the Read method of the underlying Reader. +// If the underlying reader supports the WriteTo method, +// this calls the underlying WriteTo without buffering. func (b *Reader) WriteTo(w io.Writer) (n int64, err error) { n, err = b.writeBuf(w) if err != nil { @@ -684,7 +686,9 @@ func (b *Writer) WriteString(s string) (int, error) { return nn, nil } -// ReadFrom implements io.ReaderFrom. +// ReadFrom implements io.ReaderFrom. If the underlying writer +// supports the ReadFrom method, and b has no buffered data yet, +// this calls the underlying ReadFrom without buffering. func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) { if b.Buffered() == 0 { if w, ok := b.wr.(io.ReaderFrom); ok { diff --git a/libgo/go/bufio/scan.go b/libgo/go/bufio/scan.go index 40aaa4a..cefd261 100644 --- a/libgo/go/bufio/scan.go +++ b/libgo/go/bufio/scan.go @@ -45,14 +45,19 @@ type Scanner struct { // input. The arguments are an initial substring of the remaining unprocessed // data and a flag, atEOF, that reports whether the Reader has no more data // to give. The return values are the number of bytes to advance the input -// and the next token to return to the user, plus an error, if any. If the -// data does not yet hold a complete token, for instance if it has no newline -// while scanning lines, SplitFunc can return (0, nil, nil) to signal the -// Scanner to read more data into the slice and try again with a longer slice -// starting at the same point in the input. +// and the next token to return to the user, if any, plus an error, if any. // -// If the returned error is non-nil, scanning stops and the error -// is returned to the client. +// Scanning stops if the function returns an error, in which case some of +// the input may be discarded. +// +// Otherwise, the Scanner advances the input. If the token is not nil, +// the Scanner returns it to the user. If the token is nil, the +// Scanner reads more data and continues scanning; if there is no more +// data--if atEOF was true--the Scanner returns. If the data does not +// yet hold a complete token, for instance if it has no newline while +// scanning lines, a SplitFunc can return (0, nil, nil) to signal the +// Scanner to read more data into the slice and try again with a +// longer slice starting at the same point in the input. // // The function is never called with an empty data slice unless atEOF // is true. If atEOF is true, however, data may be non-empty and, diff --git a/libgo/go/bytes/buffer.go b/libgo/go/bytes/buffer.go index dc9d5e9..a2eca2e 100644 --- a/libgo/go/bytes/buffer.go +++ b/libgo/go/bytes/buffer.go @@ -202,6 +202,7 @@ func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) { b.lastRead = opInvalid for { i := b.grow(MinRead) + b.buf = b.buf[:i] m, e := r.Read(b.buf[i:cap(b.buf)]) if m < 0 { panic(errNegativeRead) diff --git a/libgo/go/bytes/buffer_test.go b/libgo/go/bytes/buffer_test.go index e4bbc12..acbe5ca 100644 --- a/libgo/go/bytes/buffer_test.go +++ b/libgo/go/bytes/buffer_test.go @@ -269,6 +269,39 @@ func TestReadFrom(t *testing.T) { } } +type panicReader struct{ panic bool } + +func (r panicReader) Read(p []byte) (int, error) { + if r.panic { + panic(nil) + } + return 0, io.EOF +} + +// Make sure that an empty Buffer remains empty when +// it is "grown" before a Read that panics +func TestReadFromPanicReader(t *testing.T) { + + // First verify non-panic behaviour + var buf Buffer + i, err := buf.ReadFrom(panicReader{}) + if err != nil { + t.Fatal(err) + } + if i != 0 { + t.Fatalf("unexpected return from bytes.ReadFrom (1): got: %d, want %d", i, 0) + } + check(t, "TestReadFromPanicReader (1)", &buf, "") + + // Confirm that when Reader panics, the emtpy buffer remains empty + var buf2 Buffer + defer func() { + recover() + check(t, "TestReadFromPanicReader (2)", &buf2, "") + }() + buf2.ReadFrom(panicReader{panic: true}) +} + func TestReadFromNegativeReader(t *testing.T) { var b Buffer defer func() { diff --git a/libgo/go/bytes/bytes.go b/libgo/go/bytes/bytes.go index 9af177f..437a6e1 100644 --- a/libgo/go/bytes/bytes.go +++ b/libgo/go/bytes/bytes.go @@ -7,6 +7,7 @@ package bytes import ( + "internal/bytealg" "unicode" "unicode/utf8" ) @@ -46,12 +47,16 @@ func explode(s []byte, n int) [][]byte { return a[0:na] } -// countGeneric actually implements Count -func countGeneric(s, sep []byte) int { +// Count counts the number of non-overlapping instances of sep in s. +// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s. +func Count(s, sep []byte) int { // special case if len(sep) == 0 { return utf8.RuneCount(s) + 1 } + if len(sep) == 1 { + return bytealg.Count(s, sep[0]) + } n := 0 for { i := Index(s, sep) @@ -800,9 +805,9 @@ func EqualFold(s, t []byte) bool { tr, sr = sr, tr } // Fast check for ASCII. - if tr < utf8.RuneSelf && 'A' <= sr && sr <= 'Z' { - // ASCII, and sr is upper case. tr must be lower case. - if tr == sr+'a'-'A' { + if tr < utf8.RuneSelf { + // ASCII only, sr/tr must be upper/lower case + if 'A' <= sr && sr <= 'Z' && tr == sr+'a'-'A' { continue } return false @@ -824,6 +829,92 @@ func EqualFold(s, t []byte) bool { return len(s) == len(t) } +// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. +func Index(s, sep []byte) int { + n := len(sep) + switch { + case n == 0: + return 0 + case n == 1: + return IndexByte(s, sep[0]) + case n == len(s): + if Equal(sep, s) { + return 0 + } + return -1 + case n > len(s): + return -1 + case n <= bytealg.MaxLen: + // Use brute force when s and sep both are small + if len(s) <= bytealg.MaxBruteForce { + return bytealg.Index(s, sep) + } + c := sep[0] + i := 0 + t := s[:len(s)-n+1] + fails := 0 + for i < len(t) { + if t[i] != c { + // IndexByte is faster than bytealg.Index, so use it as long as + // we're not getting lots of false positives. + o := IndexByte(t[i:], c) + if o < 0 { + return -1 + } + i += o + } + if Equal(s[i:i+n], sep) { + return i + } + fails++ + i++ + // Switch to bytealg.Index when IndexByte produces too many false positives. + if fails > bytealg.Cutover(i) { + r := bytealg.Index(s[i:], sep) + if r >= 0 { + return r + i + } + return -1 + } + } + return -1 + } + c := sep[0] + i := 0 + fails := 0 + t := s[:len(s)-n+1] + for i < len(t) { + if t[i] != c { + o := IndexByte(t[i:], c) + if o < 0 { + break + } + i += o + } + if Equal(s[i:i+n], sep) { + return i + } + i++ + fails++ + if fails >= 4+i>>4 && i < len(t) { + // Give up on IndexByte, it isn't skipping ahead + // far enough to be better than Rabin-Karp. + // Experiments (using IndexPeriodic) suggest + // the cutover is about 16 byte skips. + // TODO: if large prefixes of sep are matching + // we should cutover at even larger average skips, + // because Equal becomes that much more expensive. + // This code does not take that effect into account. + j := indexRabinKarp(s[i:], sep) + if j < 0 { + return -1 + } + return i + j + } + } + return -1 +} + func indexRabinKarp(s, sep []byte) int { // Rabin-Karp search hashsep, pow := hashStr(sep) diff --git a/libgo/go/bytes/bytes_amd64.go b/libgo/go/bytes/bytes_amd64.go deleted file mode 100644 index 2fbbbb0..0000000 --- a/libgo/go/bytes/bytes_amd64.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2016 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 ignore - -package bytes - -import "internal/cpu" - -//go:noescape - -// indexShortStr returns the index of the first instance of c in s, or -1 if c is not present in s. -// indexShortStr requires 2 <= len(c) <= shortStringLen -func indexShortStr(s, c []byte) int // ../runtime/asm_amd64.s -func countByte(s []byte, c byte) int // ../runtime/asm_amd64.s - -var shortStringLen int - -func init() { - if cpu.X86.HasAVX2 { - shortStringLen = 63 - } else { - shortStringLen = 31 - } -} - -// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. -func Index(s, sep []byte) int { - n := len(sep) - switch { - case n == 0: - return 0 - case n == 1: - return IndexByte(s, sep[0]) - case n == len(s): - if Equal(sep, s) { - return 0 - } - return -1 - case n > len(s): - return -1 - case n <= shortStringLen: - // Use brute force when s and sep both are small - if len(s) <= 64 { - return indexShortStr(s, sep) - } - c := sep[0] - i := 0 - t := s[:len(s)-n+1] - fails := 0 - for i < len(t) { - if t[i] != c { - // IndexByte skips 16/32 bytes per iteration, - // so it's faster than indexShortStr. - o := IndexByte(t[i:], c) - if o < 0 { - return -1 - } - i += o - } - if Equal(s[i:i+n], sep) { - return i - } - fails++ - i++ - // Switch to indexShortStr when IndexByte produces too many false positives. - // Too many means more that 1 error per 8 characters. - // Allow some errors in the beginning. - if fails > (i+16)/8 { - r := indexShortStr(s[i:], sep) - if r >= 0 { - return r + i - } - return -1 - } - } - return -1 - } - return indexRabinKarp(s, sep) -} - -// Count counts the number of non-overlapping instances of sep in s. -// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s. -func Count(s, sep []byte) int { - if len(sep) == 1 && cpu.X86.HasPOPCNT { - return countByte(s, sep[0]) - } - return countGeneric(s, sep) -} diff --git a/libgo/go/bytes/bytes_arm64.go b/libgo/go/bytes/bytes_arm64.go deleted file mode 100644 index 1213b06..0000000 --- a/libgo/go/bytes/bytes_arm64.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2017 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 ignore - -package bytes - -func countByte(s []byte, c byte) int // bytes_arm64.s - -// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. -func Index(s, sep []byte) int { - n := len(sep) - switch { - case n == 0: - return 0 - case n == 1: - return IndexByte(s, sep[0]) - case n == len(s): - if Equal(sep, s) { - return 0 - } - return -1 - case n > len(s): - return -1 - } - c := sep[0] - i := 0 - fails := 0 - t := s[:len(s)-n+1] - for i < len(t) { - if t[i] != c { - o := IndexByte(t[i:], c) - if o < 0 { - break - } - i += o - } - if Equal(s[i:i+n], sep) { - return i - } - i++ - fails++ - if fails >= 4+i>>4 && i < len(t) { - // Give up on IndexByte, it isn't skipping ahead - // far enough to be better than Rabin-Karp. - // Experiments (using IndexPeriodic) suggest - // the cutover is about 16 byte skips. - // TODO: if large prefixes of sep are matching - // we should cutover at even larger average skips, - // because Equal becomes that much more expensive. - // This code does not take that effect into account. - j := indexRabinKarp(s[i:], sep) - if j < 0 { - return -1 - } - return i + j - } - } - return -1 -} - -// Count counts the number of non-overlapping instances of sep in s. -// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s. -func Count(s, sep []byte) int { - if len(sep) == 1 { - return countByte(s, sep[0]) - } - return countGeneric(s, sep) -} diff --git a/libgo/go/bytes/bytes_decl.go b/libgo/go/bytes/bytes_decl.go index df0614f..af0f8b1 100644 --- a/libgo/go/bytes/bytes_decl.go +++ b/libgo/go/bytes/bytes_decl.go @@ -6,19 +6,19 @@ package bytes //go:noescape -// IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s. -func IndexByte(s []byte, c byte) int // ../runtime/asm_$GOARCH.s +// IndexByte returns the index of the first instance of c in b, or -1 if c is not present in b. +func IndexByte(b []byte, c byte) int // in internal/bytealg //go:noescape // Equal returns a boolean reporting whether a and b // are the same length and contain the same bytes. // A nil argument is equivalent to an empty slice. -func Equal(a, b []byte) bool // ../runtime/asm_$GOARCH.s +func Equal(a, b []byte) bool // in internal/bytealg //go:noescape // Compare returns an integer comparing two byte slices lexicographically. // The result will be 0 if a==b, -1 if a < b, and +1 if a > b. // A nil argument is equivalent to an empty slice. -func Compare(a, b []byte) int // ../runtime/noasm.go or ../runtime/asm_{386,amd64}.s +func Compare(a, b []byte) int // in internal/bytealg diff --git a/libgo/go/bytes/bytes_generic.go b/libgo/go/bytes/bytes_generic.go deleted file mode 100644 index b52d939..0000000 --- a/libgo/go/bytes/bytes_generic.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2015 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 !amd64,!s390x,!arm64 - -package bytes - -// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. -func Index(s, sep []byte) int { - n := len(sep) - switch { - case n == 0: - return 0 - case n == 1: - return IndexByte(s, sep[0]) - case n == len(s): - if Equal(sep, s) { - return 0 - } - return -1 - case n > len(s): - return -1 - } - c := sep[0] - i := 0 - fails := 0 - t := s[:len(s)-n+1] - for i < len(t) { - if t[i] != c { - o := IndexByte(t[i:], c) - if o < 0 { - break - } - i += o - } - if Equal(s[i:i+n], sep) { - return i - } - i++ - fails++ - if fails >= 4+i>>4 && i < len(t) { - // Give up on IndexByte, it isn't skipping ahead - // far enough to be better than Rabin-Karp. - // Experiments (using IndexPeriodic) suggest - // the cutover is about 16 byte skips. - // TODO: if large prefixes of sep are matching - // we should cutover at even larger average skips, - // because Equal becomes that much more expensive. - // This code does not take that effect into account. - j := indexRabinKarp(s[i:], sep) - if j < 0 { - return -1 - } - return i + j - } - } - return -1 -} - -// Count counts the number of non-overlapping instances of sep in s. -// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s. -func Count(s, sep []byte) int { - return countGeneric(s, sep) -} diff --git a/libgo/go/bytes/bytes_s390x.go b/libgo/go/bytes/bytes_s390x.go deleted file mode 100644 index 0c22848..0000000 --- a/libgo/go/bytes/bytes_s390x.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2016 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 ignore - -package bytes - -//go:noescape - -// indexShortStr returns the index of the first instance of sep in s, -// or -1 if sep is not present in s. -// indexShortStr requires 2 <= len(sep) <= shortStringLen -func indexShortStr(s, c []byte) int // ../runtime/asm_s390x.s - -// supportsVX reports whether the vector facility is available. -// indexShortStr must not be called if the vector facility is not -// available. -func supportsVX() bool // ../runtime/asm_s390x.s - -var shortStringLen = -1 - -func init() { - if supportsVX() { - shortStringLen = 64 - } -} - -// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. -func Index(s, sep []byte) int { - n := len(sep) - switch { - case n == 0: - return 0 - case n == 1: - return IndexByte(s, sep[0]) - case n == len(s): - if Equal(sep, s) { - return 0 - } - return -1 - case n > len(s): - return -1 - case n <= shortStringLen: - // Use brute force when s and sep both are small - if len(s) <= 64 { - return indexShortStr(s, sep) - } - c := sep[0] - i := 0 - t := s[:len(s)-n+1] - fails := 0 - for i < len(t) { - if t[i] != c { - // IndexByte skips 16/32 bytes per iteration, - // so it's faster than indexShortStr. - o := IndexByte(t[i:], c) - if o < 0 { - return -1 - } - i += o - } - if Equal(s[i:i+n], sep) { - return i - } - fails++ - i++ - // Switch to indexShortStr when IndexByte produces too many false positives. - // Too many means more that 1 error per 8 characters. - // Allow some errors in the beginning. - if fails > (i+16)/8 { - r := indexShortStr(s[i:], sep) - if r >= 0 { - return r + i - } - return -1 - } - } - return -1 - } - return indexRabinKarp(s, sep) -} - -// Count counts the number of non-overlapping instances of sep in s. -// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s. -func Count(s, sep []byte) int { - return countGeneric(s, sep) -} diff --git a/libgo/go/bytes/bytes_test.go b/libgo/go/bytes/bytes_test.go index 23fce29..11c5ef9 100644 --- a/libgo/go/bytes/bytes_test.go +++ b/libgo/go/bytes/bytes_test.go @@ -415,10 +415,6 @@ func TestCountByte(t *testing.T) { if p != j+1 { t.Errorf("TestCountByte.Count(%q, 100) = %d", b[i:i+window], p) } - pGeneric := CountGeneric(b[i:i+window], []byte{100}) - if pGeneric != j+1 { - t.Errorf("TestCountByte.CountGeneric(%q, 100) = %d", b[i:i+window], p) - } } } @@ -466,10 +462,6 @@ func TestCountByteNoMatch(t *testing.T) { if p != 0 { t.Errorf("TestCountByteNoMatch(%q, 0) = %d", b[i:i+window], p) } - pGeneric := CountGeneric(b[i:i+window], []byte{0}) - if pGeneric != 0 { - t.Errorf("TestCountByteNoMatch.CountGeneric(%q, 100) = %d", b[i:i+window], p) - } for j := 0; j < window; j++ { b[i+j] = byte(0) } diff --git a/libgo/go/bytes/compare_test.go b/libgo/go/bytes/compare_test.go index 35088a1..3e33c27 100644 --- a/libgo/go/bytes/compare_test.go +++ b/libgo/go/bytes/compare_test.go @@ -6,6 +6,7 @@ package bytes_test import ( . "bytes" + "internal/testenv" "testing" ) @@ -58,10 +59,20 @@ func TestCompareIdenticalSlice(t *testing.T) { } func TestCompareBytes(t *testing.T) { - n := 128 + lengths := make([]int, 0) // lengths to test in ascending order + for i := 0; i <= 128; i++ { + lengths = append(lengths, i) + } + lengths = append(lengths, 256, 512, 1024, 1333, 4095, 4096, 4097) + + if !testing.Short() || testenv.Builder() != "" { + lengths = append(lengths, 65535, 65536, 65537, 99999) + } + + n := lengths[len(lengths)-1] a := make([]byte, n+1) b := make([]byte, n+1) - for len := 0; len < 128; len++ { + for _, len := range lengths { // randomish but deterministic data. No 0 or 255. for i := 0; i < len; i++ { a[i] = byte(1 + 31*i%254) diff --git a/libgo/go/bytes/export_test.go b/libgo/go/bytes/export_test.go index 823c8b0..f61523e 100644 --- a/libgo/go/bytes/export_test.go +++ b/libgo/go/bytes/export_test.go @@ -7,4 +7,3 @@ package bytes // Export func for testing var IndexBytePortable = indexBytePortable var EqualPortable = equalPortable -var CountGeneric = countGeneric diff --git a/libgo/go/cmd/buildid/buildid.go b/libgo/go/cmd/buildid/buildid.go index 8d810ff..1c7b228 100644 --- a/libgo/go/cmd/buildid/buildid.go +++ b/libgo/go/cmd/buildid/buildid.go @@ -22,6 +22,21 @@ func usage() { var wflag = flag.Bool("w", false, "write build ID") +// taken from cmd/go/internal/work/buildid.go +func hashToString(h [32]byte) string { + const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" + const chunks = 5 + var dst [chunks * 4]byte + for i := 0; i < chunks; i++ { + v := uint32(h[3*i])<<16 | uint32(h[3*i+1])<<8 | uint32(h[3*i+2]) + dst[4*i+0] = b64[(v>>18)&0x3F] + dst[4*i+1] = b64[(v>>12)&0x3F] + dst[4*i+2] = b64[(v>>6)&0x3F] + dst[4*i+3] = b64[v&0x3F] + } + return string(dst[:]) +} + func main() { log.SetPrefix("buildid: ") log.SetFlags(0) @@ -41,6 +56,8 @@ func main() { return } + // Keep in sync with src/cmd/go/internal/work/buildid.go:updateBuildID + f, err := os.Open(file) if err != nil { log.Fatal(err) @@ -51,14 +68,14 @@ func main() { } f.Close() - tail := id - if i := strings.LastIndex(id, "."); i >= 0 { - tail = tail[i+1:] + newID := id[:strings.LastIndex(id, "/")] + "/" + hashToString(hash) + if len(newID) != len(id) { + log.Fatalf("%s: build ID length mismatch %q vs %q", file, id, newID) } - if len(tail) != len(hash)*2 { - log.Fatalf("%s: cannot find %d-byte hash in id %s", file, len(hash), id) + + if len(matches) == 0 { + return } - newID := id[:len(id)-len(tail)] + fmt.Sprintf("%x", hash) f, err = os.OpenFile(file, os.O_WRONLY, 0) if err != nil { diff --git a/libgo/go/cmd/cgo/ast.go b/libgo/go/cmd/cgo/ast.go index 58e0ee7..4462136 100644 --- a/libgo/go/cmd/cgo/ast.go +++ b/libgo/go/cmd/cgo/ast.go @@ -95,7 +95,7 @@ func (f *File) ParseGo(name string, src []byte) { } } if !sawC { - error_(token.NoPos, `cannot find import "C"`) + error_(ast1.Package, `cannot find import "C"`) } // In ast2, strip the import "C" line. @@ -356,6 +356,7 @@ func (f *File) walk(x interface{}, context astContext, visit func(*File, interfa case *ast.BadExpr: case *ast.Ident: case *ast.Ellipsis: + f.walk(&n.Elt, ctxType, visit) case *ast.BasicLit: case *ast.FuncLit: f.walk(n.Type, ctxType, visit) diff --git a/libgo/go/cmd/cgo/doc.go b/libgo/go/cmd/cgo/doc.go index c16b63a..157cd94 100644 --- a/libgo/go/cmd/cgo/doc.go +++ b/libgo/go/cmd/cgo/doc.go @@ -64,6 +64,11 @@ a full argument: to allow -mfoo=bar, use CGO_CFLAGS_ALLOW='-mfoo.*', not just CGO_CFLAGS_ALLOW='-mfoo'. Similarly named variables control the allowed CPPFLAGS, CXXFLAGS, FFLAGS, and LDFLAGS. +Also for security reasons, only a limited set of characters are +permitted, notably alphanumeric characters and a few symbols, such as +'.', that will not be interpreted in unexpected ways. Attempts to use +forbidden characters will get a "malformed #cgo argument" error. + When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS and CGO_LDFLAGS environment variables are added to the flags derived from these directives. Package-specific flags should be set using the @@ -99,17 +104,24 @@ compiled with the C compiler. Any .cc, .cpp, or .cxx files will be compiled with the C++ compiler. Any .f, .F, .for or .f90 files will be compiled with the fortran compiler. Any .h, .hh, .hpp, or .hxx files will not be compiled separately, but, if these header files are changed, -the C and C++ files will be recompiled. The default C and C++ -compilers may be changed by the CC and CXX environment variables, -respectively; those environment variables may include command line -options. +the package (including its non-Go source files) will be recompiled. +Note that changes to files in other directories do not cause the package +to be recompiled, so all non-Go source code for the package should be +stored in the package directory, not in subdirectories. +The default C and C++ compilers may be changed by the CC and CXX +environment variables, respectively; those environment variables +may include command line options. The cgo tool is enabled by default for native builds on systems where it is expected to work. It is disabled by default when cross-compiling. You can control this by setting the CGO_ENABLED environment variable when running the go tool: set it to 1 to enable the use of cgo, and to 0 to disable it. The go tool will set the -build constraint "cgo" if cgo is enabled. +build constraint "cgo" if cgo is enabled. The special import "C" +implies the "cgo" build constraint, as though the file also said +"// +build cgo". Therefore, if cgo is disabled, files that import +"C" will not be built by the go tool. (For more about build constraints +see https://golang.org/pkg/go/build/#hdr-Build_Constraints). When cross-compiling, you must specify a C cross-compiler for cgo to use. You can do this by setting the generic CC_FOR_TARGET or the @@ -219,6 +231,26 @@ C compilers are aware of this calling convention and adjust the call accordingly, but Go cannot. In Go, you must pass the pointer to the first element explicitly: C.f(&C.x[0]). +Calling variadic C functions is not supported. It is possible to +circumvent this by using a C function wrapper. For example: + + package main + + // #include <stdio.h> + // #include <stdlib.h> + // + // static void myprint(char* s) { + // printf("%s\n", s); + // } + import "C" + import "unsafe" + + func main() { + cs := C.CString("Hello from stdio") + C.myprint(cs) + C.free(unsafe.Pointer(cs)) + } + A few special functions convert between Go and C types by making copies of the data. In pseudo-Go definitions: @@ -348,6 +380,14 @@ and of course there is nothing stopping the C code from doing anything it likes. However, programs that break these rules are likely to fail in unexpected and unpredictable ways. +Note: the current implementation has a bug. While Go code is permitted +to write nil or a C pointer (but not a Go pointer) to C memory, the +current implementation may sometimes cause a runtime error if the +contents of the C memory appear to be a Go pointer. Therefore, avoid +passing uninitialized C memory to Go code if the Go code is going to +store pointer values in it. Zero out the memory in C before passing it +to Go. + Special cases A few special C types which would normally be represented by a pointer diff --git a/libgo/go/cmd/cgo/gcc.go b/libgo/go/cmd/cgo/gcc.go index 534fba1..2a2d008 100644 --- a/libgo/go/cmd/cgo/gcc.go +++ b/libgo/go/cmd/cgo/gcc.go @@ -183,9 +183,29 @@ func (p *Package) Translate(f *File) { cref.Name.C = cname(cref.Name.Go) } p.loadDefines(f) - needType := p.guessKinds(f) - if len(needType) > 0 { - p.loadDWARF(f, needType) + p.typedefs = map[string]bool{} + p.typedefList = nil + numTypedefs := -1 + for len(p.typedefs) > numTypedefs { + numTypedefs = len(p.typedefs) + // Also ask about any typedefs we've seen so far. + for _, a := range p.typedefList { + f.Name[a] = &Name{ + Go: a, + C: a, + } + } + needType := p.guessKinds(f) + if len(needType) > 0 { + p.loadDWARF(f, needType) + } + + // In godefs mode we're OK with the typedefs, which + // will presumably also be defined in the file, we + // don't want to resolve them to their base types. + if *godefs { + break + } } if p.rewriteCalls(f) { // Add `import _cgo_unsafe "unsafe"` after the package statement. @@ -570,6 +590,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) { fatalf("malformed __cgo__ name: %s", name) } types[i] = t.Type + p.recordTypedefs(t.Type) } if e.Tag != dwarf.TagCompileUnit { r.SkipChildren() @@ -605,7 +626,25 @@ func (p *Package) loadDWARF(f *File, names []*Name) { } } case "fconst": - if i < len(floats) { + if i >= len(floats) { + break + } + switch base(types[i]).(type) { + case *dwarf.IntType, *dwarf.UintType: + // This has an integer type so it's + // not really a floating point + // constant. This can happen when the + // C compiler complains about using + // the value as an integer constant, + // but not as a general constant. + // Treat this as a variable of the + // appropriate type, not a constant, + // to get C-style type handling, + // avoiding the problem that C permits + // uint64(-1) but Go does not. + // See issue 26066. + n.Kind = "var" + default: n.Const = fmt.Sprintf("%f", floats[i]) } case "sconst": @@ -618,6 +657,47 @@ func (p *Package) loadDWARF(f *File, names []*Name) { } } +// recordTypedefs remembers in p.typedefs all the typedefs used in dtypes and its children. +func (p *Package) recordTypedefs(dtype dwarf.Type) { + p.recordTypedefs1(dtype, map[dwarf.Type]bool{}) +} +func (p *Package) recordTypedefs1(dtype dwarf.Type, visited map[dwarf.Type]bool) { + if dtype == nil { + return + } + if visited[dtype] { + return + } + visited[dtype] = true + switch dt := dtype.(type) { + case *dwarf.TypedefType: + if strings.HasPrefix(dt.Name, "__builtin") { + // Don't look inside builtin types. There be dragons. + return + } + if !p.typedefs[dt.Name] { + p.typedefs[dt.Name] = true + p.typedefList = append(p.typedefList, dt.Name) + p.recordTypedefs1(dt.Type, visited) + } + case *dwarf.PtrType: + p.recordTypedefs1(dt.Type, visited) + case *dwarf.ArrayType: + p.recordTypedefs1(dt.Type, visited) + case *dwarf.QualType: + p.recordTypedefs1(dt.Type, visited) + case *dwarf.FuncType: + p.recordTypedefs1(dt.ReturnType, visited) + for _, a := range dt.ParamType { + p.recordTypedefs1(a, visited) + } + case *dwarf.StructType: + for _, f := range dt.Field { + p.recordTypedefs1(f.Type, visited) + } + } +} + // mangleName does name mangling to translate names // from the original Go source files to the names // used in the final Go files generated by cgo. @@ -1373,7 +1453,7 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6 if len(data) <= strlen { fatalf("invalid string literal") } - strs[n] = string(data[:strlen]) + strs[n] = data[:strlen] } } @@ -1754,6 +1834,7 @@ type typeConv struct { // Map from types to incomplete pointers to those types. ptrs map[dwarf.Type][]*Type // Keys of ptrs in insertion order (deterministic worklist) + // ptrKeys contains exactly the keys in ptrs. ptrKeys []dwarf.Type // Type names X for which there exists an XGetTypeID function with type func() CFTypeID. @@ -1896,14 +1977,15 @@ func (c *typeConv) FinishType(pos token.Pos) { for len(c.ptrKeys) > 0 { dtype := c.ptrKeys[0] c.ptrKeys = c.ptrKeys[1:] + ptrs := c.ptrs[dtype] + delete(c.ptrs, dtype) // Note Type might invalidate c.ptrs[dtype]. t := c.Type(dtype, pos) - for _, ptr := range c.ptrs[dtype] { + for _, ptr := range ptrs { ptr.Go.(*ast.StarExpr).X = t.Go ptr.C.Set("%s*", t.C) } - c.ptrs[dtype] = nil // retain the map key } } @@ -2180,6 +2262,10 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { s := *sub s.Go = c.uintptr sub = &s + // Make sure we update any previously computed type. + if oldType := typedef[name.Name]; oldType != nil { + oldType.Go = sub.Go + } } t.Go = name if unionWithPointer[sub.Go] { @@ -2341,7 +2427,7 @@ func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type { } // ...or the typedef is one in which we expect bad pointers. // It will be a uintptr instead of *X. - if c.badPointerTypedef(dt) { + if c.baseBadPointerTypedef(dt) { break } @@ -2693,6 +2779,19 @@ func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool { return false } +// baseBadPointerTypedef reports whether the base of a chain of typedefs is a bad typedef +// as badPointerTypedef reports. +func (c *typeConv) baseBadPointerTypedef(dt *dwarf.TypedefType) bool { + for { + if t, ok := dt.Type.(*dwarf.TypedefType); ok { + dt = t + continue + } + break + } + return c.badPointerTypedef(dt) +} + func (c *typeConv) badCFType(dt *dwarf.TypedefType) bool { // The real bad types are CFNumberRef and CFDateRef. // Sometimes non-pointers are stored in these types. @@ -2781,13 +2880,31 @@ func (c *typeConv) badJNI(dt *dwarf.TypedefType) bool { } } - // Check that the typedef is: - // struct _jobject; - // typedef struct _jobject *jobject; + // Check that the typedef is either: + // 1: + // struct _jobject; + // typedef struct _jobject *jobject; + // 2: (in NDK16 in C++) + // class _jobject {}; + // typedef _jobject* jobject; + // 3: (in NDK16 in C) + // typedef void* jobject; if ptr, ok := w.Type.(*dwarf.PtrType); ok { - if str, ok := ptr.Type.(*dwarf.StructType); ok { - if str.StructName == "_jobject" && str.Kind == "struct" && len(str.Field) == 0 && str.Incomplete { - return true + switch v := ptr.Type.(type) { + case *dwarf.VoidType: + return true + case *dwarf.StructType: + if v.StructName == "_jobject" && len(v.Field) == 0 { + switch v.Kind { + case "struct": + if v.Incomplete { + return true + } + case "class": + if !v.Incomplete { + return true + } + } } } } @@ -2796,7 +2913,7 @@ func (c *typeConv) badJNI(dt *dwarf.TypedefType) bool { } // jniTypes maps from JNI types that we want to be uintptrs, to the underlying type to which -// they are mapped. The base "jobject" maps to the empty string. +// they are mapped. The base "jobject" maps to the empty string. var jniTypes = map[string]string{ "jobject": "", "jclass": "jobject", diff --git a/libgo/go/cmd/cgo/godefs.go b/libgo/go/cmd/cgo/godefs.go index 6d638f0..6720945 100644 --- a/libgo/go/cmd/cgo/godefs.go +++ b/libgo/go/cmd/cgo/godefs.go @@ -19,7 +19,7 @@ import ( func (p *Package) godefs(f *File, srcfile string) string { var buf bytes.Buffer - fmt.Fprintf(&buf, "// Created by cgo -godefs - DO NOT EDIT\n") + fmt.Fprintf(&buf, "// Code generated by cmd/cgo -godefs; DO NOT EDIT.\n") fmt.Fprintf(&buf, "// %s %s\n", filepath.Base(os.Args[0]), strings.Join(os.Args[1:], " ")) fmt.Fprintf(&buf, "\n") diff --git a/libgo/go/cmd/cgo/main.go b/libgo/go/cmd/cgo/main.go index 890a365..1238016 100644 --- a/libgo/go/cmd/cgo/main.go +++ b/libgo/go/cmd/cgo/main.go @@ -17,6 +17,7 @@ import ( "go/ast" "go/printer" "go/token" + "io" "io/ioutil" "os" "path/filepath" @@ -42,9 +43,11 @@ type Package struct { Name map[string]*Name // accumulated Name from Files ExpFunc []*ExpFunc // accumulated ExpFunc from Files Decl []ast.Decl - GoFiles []string // list of Go files - GccFiles []string // list of gcc output files - Preamble string // collected preamble for _cgo_export.h + GoFiles []string // list of Go files + GccFiles []string // list of gcc output files + Preamble string // collected preamble for _cgo_export.h + typedefs map[string]bool // type names that appear in the types of the objects we're interested in + typedefList []string } // A File collects information about a single Go input file. @@ -278,6 +281,9 @@ func main() { if arg == "-fsanitize=thread" { tsanProlog = yesTsanProlog } + if arg == "-fsanitize=memory" { + msanProlog = yesMsanProlog + } } p := newPackage(args[:i]) @@ -297,6 +303,7 @@ func main() { // concern is other cgo wrappers for the same functions. // Use the beginning of the md5 of the input to disambiguate. h := md5.New() + io.WriteString(h, *importPath) fs := make([]*File, len(goFiles)) for i, input := range goFiles { if *srcDir != "" { @@ -410,6 +417,14 @@ func (p *Package) Record(f *File) { for k, v := range f.Name { if p.Name[k] == nil { p.Name[k] = v + } else if p.incompleteTypedef(p.Name[k].Type) { + p.Name[k] = v + } else if p.incompleteTypedef(v.Type) { + // Nothing to do. + } else if _, ok := nameToC[k]; ok { + // Names we predefine may appear inconsistent + // if some files typedef them and some don't. + // Issue 26743. } else if !reflect.DeepEqual(p.Name[k], v) { error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k)) } @@ -422,3 +437,9 @@ func (p *Package) Record(f *File) { } p.Decl = append(p.Decl, f.AST.Decls...) } + +// incompleteTypedef reports whether t appears to be an incomplete +// typedef definition. +func (p *Package) incompleteTypedef(t *Type) bool { + return t == nil || (t.Size == 0 && t.Align == -1) +} diff --git a/libgo/go/cmd/cgo/out.go b/libgo/go/cmd/cgo/out.go index 92fad5d..10d4b74 100644 --- a/libgo/go/cmd/cgo/out.go +++ b/libgo/go/cmd/cgo/out.go @@ -17,6 +17,7 @@ import ( "io" "os" "path/filepath" + "regexp" "sort" "strings" ) @@ -78,7 +79,7 @@ func (p *Package) writeDefs() { // Write second Go output: definitions of _C_xxx. // In a separate file so that the import of "unsafe" does not // pollute the original file. - fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n") + fmt.Fprintf(fgo2, "// Code generated by cmd/cgo; DO NOT EDIT.\n\n") fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName) fmt.Fprintf(fgo2, "import \"unsafe\"\n\n") if !*gccgo && *importRuntimeCgo { @@ -277,10 +278,7 @@ func dynimport(obj string) { } } } - sym, err := f.ImportedSymbols() - if err != nil { - fatalf("cannot load imported symbols from ELF file %s: %v", obj, err) - } + sym, _ := f.ImportedSymbols() for _, s := range sym { targ := s.Name if s.Version != "" { @@ -288,10 +286,7 @@ func dynimport(obj string) { } fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, targ, s.Library) } - lib, err := f.ImportedLibraries() - if err != nil { - fatalf("cannot load imported libraries from ELF file %s: %v", obj, err) - } + lib, _ := f.ImportedLibraries() for _, l := range lib { fmt.Fprintf(stdout, "//go:cgo_import_dynamic _ _ %q\n", l) } @@ -299,20 +294,14 @@ func dynimport(obj string) { } if f, err := macho.Open(obj); err == nil { - sym, err := f.ImportedSymbols() - if err != nil { - fatalf("cannot load imported symbols from Mach-O file %s: %v", obj, err) - } + sym, _ := f.ImportedSymbols() for _, s := range sym { if len(s) > 0 && s[0] == '_' { s = s[1:] } fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s, s, "") } - lib, err := f.ImportedLibraries() - if err != nil { - fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err) - } + lib, _ := f.ImportedLibraries() for _, l := range lib { fmt.Fprintf(stdout, "//go:cgo_import_dynamic _ _ %q\n", l) } @@ -320,10 +309,7 @@ func dynimport(obj string) { } if f, err := pe.Open(obj); err == nil { - sym, err := f.ImportedSymbols() - if err != nil { - fatalf("cannot load imported symbols from PE file %s: %v", obj, err) - } + sym, _ := f.ImportedSymbols() for _, s := range sym { ss := strings.Split(s, ":") name := strings.Split(ss[0], "@")[0] @@ -559,8 +545,8 @@ func (p *Package) writeOutput(f *File, srcfile string) { p.GccFiles = append(p.GccFiles, base+".cgo2.c") // Write Go output: Go input with rewrites of C.xxx to _C_xxx. - fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n\n") - fmt.Fprintf(fgo1, "//line %s:1\n", srcfile) + fmt.Fprintf(fgo1, "// Code generated by cmd/cgo; DO NOT EDIT.\n\n") + fmt.Fprintf(fgo1, "//line %s:1:1\n", srcfile) fgo1.Write(f.Edit.Bytes()) // While we process the vars and funcs, also write gcc output. @@ -569,6 +555,7 @@ func (p *Package) writeOutput(f *File, srcfile string) { fmt.Fprintf(fgcc, "%s\n", f.Preamble) fmt.Fprintf(fgcc, "%s\n", gccProlog) fmt.Fprintf(fgcc, "%s\n", tsanProlog) + fmt.Fprintf(fgcc, "%s\n", msanProlog) for _, key := range nameKeys(f.Name) { n := f.Name[key] @@ -632,14 +619,14 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { // We're trying to write a gcc struct that matches gc's layout. // Use packed attribute to force no padding in this struct in case // gcc has different packing requirements. - fmt.Fprintf(fgcc, "\t%s %v *a = v;\n", ctype, p.packedAttribute()) + fmt.Fprintf(fgcc, "\t%s %v *_cgo_a = v;\n", ctype, p.packedAttribute()) if n.FuncType.Result != nil { // Save the stack top for use below. - fmt.Fprintf(fgcc, "\tchar *stktop = _cgo_topofstack();\n") + fmt.Fprintf(fgcc, "\tchar *_cgo_stktop = _cgo_topofstack();\n") } tr := n.FuncType.Result if tr != nil { - fmt.Fprintf(fgcc, "\t__typeof__(a->r) r;\n") + fmt.Fprintf(fgcc, "\t__typeof__(_cgo_a->r) _cgo_r;\n") } fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n") if n.AddError { @@ -647,9 +634,9 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { } fmt.Fprintf(fgcc, "\t") if tr != nil { - fmt.Fprintf(fgcc, "r = ") + fmt.Fprintf(fgcc, "_cgo_r = ") if c := tr.C.String(); c[len(c)-1] == '*' { - fmt.Fprint(fgcc, "(__typeof__(a->r)) ") + fmt.Fprint(fgcc, "(__typeof__(_cgo_a->r)) ") } } if n.Kind == "macro" { @@ -660,7 +647,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { if i > 0 { fmt.Fprintf(fgcc, ", ") } - fmt.Fprintf(fgcc, "a->p%d", i) + fmt.Fprintf(fgcc, "_cgo_a->p%d", i) } fmt.Fprintf(fgcc, ");\n") } @@ -671,9 +658,19 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { if n.FuncType.Result != nil { // The cgo call may have caused a stack copy (via a callback). // Adjust the return value pointer appropriately. - fmt.Fprintf(fgcc, "\ta = (void*)((char*)a + (_cgo_topofstack() - stktop));\n") + fmt.Fprintf(fgcc, "\t_cgo_a = (void*)((char*)_cgo_a + (_cgo_topofstack() - _cgo_stktop));\n") // Save the return value. - fmt.Fprintf(fgcc, "\ta->r = r;\n") + fmt.Fprintf(fgcc, "\t_cgo_a->r = _cgo_r;\n") + // The return value is on the Go stack. If we are using msan, + // and if the C value is partially or completely uninitialized, + // the assignment will mark the Go stack as uninitialized. + // The Go compiler does not update msan for changes to the + // stack. It is possible that the stack will remain + // uninitialized, and then later be used in a way that is + // visible to msan, possibly leading to a false positive. + // Mark the stack space as written, to avoid this problem. + // See issue 26209. + fmt.Fprintf(fgcc, "\t_cgo_msan_write(&_cgo_a->r, sizeof(_cgo_a->r));\n") } if n.AddError { fmt.Fprintf(fgcc, "\treturn _cgo_errno;\n") @@ -708,12 +705,12 @@ func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) { fmt.Fprintf(fgcc, ")\n") fmt.Fprintf(fgcc, "{\n") if t := n.FuncType.Result; t != nil { - fmt.Fprintf(fgcc, "\t%s r;\n", t.C.String()) + fmt.Fprintf(fgcc, "\t%s _cgo_r;\n", t.C.String()) } fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n") fmt.Fprintf(fgcc, "\t") if t := n.FuncType.Result; t != nil { - fmt.Fprintf(fgcc, "r = ") + fmt.Fprintf(fgcc, "_cgo_r = ") // Cast to void* to avoid warnings due to omitted qualifiers. if c := t.C.String(); c[len(c)-1] == '*' { fmt.Fprintf(fgcc, "(void*)") @@ -739,7 +736,7 @@ func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) { if c := t.C.String(); c[len(c)-1] == '*' { fmt.Fprintf(fgcc, "(void*)") } - fmt.Fprintf(fgcc, "r;\n") + fmt.Fprintf(fgcc, "_cgo_r;\n") } fmt.Fprintf(fgcc, "}\n") fmt.Fprintf(fgcc, "\n") @@ -748,7 +745,7 @@ func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) { // packedAttribute returns host compiler struct attribute that will be // used to match gc's struct layout. For example, on 386 Windows, // gcc wants to 8-align int64s, but gc does not. -// Use __gcc_struct__ to work around http://gcc.gnu.org/PR52991 on x86, +// Use __gcc_struct__ to work around https://gcc.gnu.org/PR52991 on x86, // and https://golang.org/issue/5603. func (p *Package) packedAttribute() string { s := "__attribute__((__packed__" @@ -763,7 +760,7 @@ func (p *Package) packedAttribute() string { func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { p.writeExportHeader(fgcch) - fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") + fmt.Fprintf(fgcc, "/* Code generated by cmd/cgo; DO NOT EDIT. */\n\n") fmt.Fprintf(fgcc, "#include <stdlib.h>\n") fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n\n") @@ -772,6 +769,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprintf(fgcc, "extern void _cgo_release_context(__SIZE_TYPE__);\n\n") fmt.Fprintf(fgcc, "extern char* _cgo_topofstack(void);") fmt.Fprintf(fgcc, "%s\n", tsanProlog) + fmt.Fprintf(fgcc, "%s\n", msanProlog) for _, exp := range p.ExpFunc { fn := exp.Func @@ -1004,11 +1002,12 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) { p.writeExportHeader(fgcch) - fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") + fmt.Fprintf(fgcc, "/* Code generated by cmd/cgo; DO NOT EDIT. */\n\n") fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n") fmt.Fprintf(fgcc, "%s\n", gccgoExportFileProlog) fmt.Fprintf(fgcc, "%s\n", tsanProlog) + fmt.Fprintf(fgcc, "%s\n", msanProlog) for _, exp := range p.ExpFunc { fn := exp.Func @@ -1170,7 +1169,7 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) { // writeExportHeader writes out the start of the _cgo_export.h file. func (p *Package) writeExportHeader(fgcch io.Writer) { - fmt.Fprintf(fgcch, "/* Created by \"go tool cgo\" - DO NOT EDIT. */\n\n") + fmt.Fprintf(fgcch, "/* Code generated by cmd/cgo; DO NOT EDIT. */\n\n") pkg := *importPath if pkg == "" { pkg = p.PackagePath @@ -1178,8 +1177,15 @@ func (p *Package) writeExportHeader(fgcch io.Writer) { fmt.Fprintf(fgcch, "/* package %s */\n\n", pkg) fmt.Fprintf(fgcch, "%s\n", builtinExportProlog) + // Remove absolute paths from #line comments in the preamble. + // They aren't useful for people using the header file, + // and they mean that the header files change based on the + // exact location of GOPATH. + re := regexp.MustCompile(`(?m)^(#line\s+[0-9]+\s+")[^"]*[/\\]([^"]*")`) + preamble := re.ReplaceAllString(p.Preamble, "$1$2") + fmt.Fprintf(fgcch, "/* Start of preamble from import \"C\" comments. */\n\n") - fmt.Fprintf(fgcch, "%s\n", p.Preamble) + fmt.Fprintf(fgcch, "%s\n", preamble) fmt.Fprintf(fgcch, "\n/* End of preamble from import \"C\" comments. */\n\n") fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog()) @@ -1414,6 +1420,25 @@ static void _cgo_tsan_release() { // Set to yesTsanProlog if we see -fsanitize=thread in the flags for gcc. var tsanProlog = noTsanProlog +// noMsanProlog is a prologue defining an MSAN function in C. +// This is used when not compiling with -fsanitize=memory. +const noMsanProlog = ` +#define _cgo_msan_write(addr, sz) +` + +// yesMsanProlog is a prologue defining an MSAN function in C. +// This is used when compiling with -fsanitize=memory. +// See the comment above where _cgo_msan_write is called. +const yesMsanProlog = ` +extern void __msan_unpoison(const volatile void *, size_t); + +#define _cgo_msan_write(addr, sz) __msan_unpoison((addr), (sz)) +` + +// msanProlog is set to yesMsanProlog if we see -fsanitize=memory in the flags +// for the C compiler. +var msanProlog = noMsanProlog + const builtinProlog = ` #line 1 "cgo-builtin-prolog" #include <stddef.h> /* for ptrdiff_t and size_t below */ diff --git a/libgo/go/cmd/cgo/util.go b/libgo/go/cmd/cgo/util.go index 4f5c488..921306b7 100644 --- a/libgo/go/cmd/cgo/util.go +++ b/libgo/go/cmd/cgo/util.go @@ -59,6 +59,8 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { var bout, berr bytes.Buffer p.Stdout = &bout p.Stderr = &berr + // Disable escape codes in clang error messages. + p.Env = append(os.Environ(), "TERM=dumb") err := p.Run() if _, ok := err.(*exec.ExitError); err != nil && !ok { fatalf("%s", err) @@ -97,6 +99,8 @@ func error_(pos token.Pos, msg string, args ...interface{}) { nerrors++ if pos.IsValid() { fmt.Fprintf(os.Stderr, "%s: ", fset.Position(pos).String()) + } else { + fmt.Fprintf(os.Stderr, "cgo: ") } fmt.Fprintf(os.Stderr, msg, args...) fmt.Fprintf(os.Stderr, "\n") diff --git a/libgo/go/cmd/go/alldocs.go b/libgo/go/cmd/go/alldocs.go index aadf97c..ebbd154 100644 --- a/libgo/go/cmd/go/alldocs.go +++ b/libgo/go/cmd/go/alldocs.go @@ -2,50 +2,66 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// DO NOT EDIT THIS FILE. GENERATED BY mkalldocs.sh. +// Code generated by mkalldocs.sh; DO NOT EDIT. // Edit the documentation in other files and rerun mkalldocs.sh to generate this one. // Go is a tool for managing Go source code. // // Usage: // -// go command [arguments] +// go <command> [arguments] // // The commands are: // +// bug start a bug report // build compile packages and dependencies // clean remove object files and cached files // doc show documentation for package or symbol // env print Go environment information -// bug start a bug report // fix update packages to use new APIs // fmt gofmt (reformat) package sources // generate generate Go files by processing source // get download and install packages and dependencies // install compile and install packages and dependencies -// list list packages +// list list packages or modules +// mod module maintenance // run compile and run Go program // test test packages // tool run specified go tool // version print Go version // vet report likely mistakes in packages // -// Use "go help [command]" for more information about a command. +// Use "go help <command>" for more information about a command. // // Additional help topics: // -// c calling between Go and C // buildmode build modes +// c calling between Go and C // cache build and test caching +// environment environment variables // filetype file types +// go.mod the go.mod file // gopath GOPATH environment variable -// environment environment variables +// gopath-get legacy GOPATH go get +// goproxy module proxy protocol // importpath import path syntax -// packages package lists +// modules modules, module versions, and more +// module-get module-aware go get +// packages package lists and patterns // testflag testing flags // testfunc testing functions // -// Use "go help [topic]" for more information about that topic. +// Use "go help <topic>" for more information about that topic. +// +// +// Start a bug report +// +// Usage: +// +// go bug +// +// Bug opens the default browser and starts a new bug report. +// The report includes useful system information. // // // Compile packages and dependencies @@ -95,7 +111,7 @@ // Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64. // -msan // enable interoperation with memory sanitizer. -// Supported only on linux/amd64, +// Supported only on linux/amd64, linux/arm64 // and only with Clang/LLVM as the host C compiler. // -v // print the names of packages as they are compiled. @@ -127,6 +143,9 @@ // -linkshared // link against shared libraries previously created with // -buildmode=shared. +// -mod mode +// module download mode to use: readonly, release, or vendor. +// See 'go help modules' for more. // -pkgdir dir // install and load all packages from dir instead of the usual locations. // For example, when building with a non-standard configuration, @@ -175,7 +194,7 @@ // // Usage: // -// go clean [-i] [-r] [-n] [-x] [-cache] [-testcache] [build flags] [packages] +// go clean [clean flags] [build flags] [packages] // // Clean removes object files from package source directories. // The go command builds most objects in a temporary directory, @@ -218,6 +237,10 @@ // The -testcache flag causes clean to expire all test results in the // go build cache. // +// The -modcache flag causes clean to remove the entire module +// download cache, including unpacked source code of versioned +// dependencies. +// // For more about build flags, see 'go help build'. // // For more about specifying packages, see 'go help packages'. @@ -349,16 +372,6 @@ // For more about environment variables, see 'go help environment'. // // -// Start a bug report -// -// Usage: -// -// go bug -// -// Bug opens the default browser and starts a new bug report. -// The report includes useful system information. -// -// // Update packages to use new APIs // // Usage: @@ -419,6 +432,12 @@ // (gofmt), a fully qualified path (/usr/you/bin/mytool), or a // command alias, described below. // +// To convey to humans and machine tools that code is generated, +// generated source should have a line early in the file that +// matches the following regular expression (in Go syntax): +// +// ^// Code generated .* DO NOT EDIT\.$ +// // Note that go generate does not parse the file, so lines that look // like directives in comments or multiline strings will be treated // as directives. @@ -506,7 +525,7 @@ // // Usage: // -// go get [-d] [-f] [-fix] [-insecure] [-t] [-u] [-v] [build flags] [packages] +// go get [-d] [-f] [-t] [-u] [-v] [-fix] [-insecure] [build flags] [packages] // // Get downloads the packages named by the import paths, along with their // dependencies. It then installs the named packages, like 'go install'. @@ -556,6 +575,12 @@ // For more about how 'go get' finds source code to // download, see 'go help importpath'. // +// This text describes the behavior of get when using GOPATH +// to manage source code and dependencies. +// If instead the go command is running in module-aware mode, +// the details of get's flags and effects change, as does 'go help get'. +// See 'go help modules' and 'go help module-get'. +// // See also: go build, go install, go clean. // // @@ -575,13 +600,16 @@ // See also: go build, go get, go clean. // // -// List packages +// List packages or modules // // Usage: // -// go list [-e] [-f format] [-json] [build flags] [packages] +// go list [-f format] [-json] [-m] [list flags] [build flags] [packages] // -// List lists the packages named by the import paths, one per line. +// List lists the named packages, one per line. +// The most commonly-used flags are -f and -json, which control the form +// of the output printed for each package. Other list flags, documented below, +// control more specific details. // // The default output shows the package import path: // @@ -591,40 +619,46 @@ // golang.org/x/net/html // // The -f flag specifies an alternate format for the list, using the -// syntax of package template. The default output is equivalent to -f -// '{{.ImportPath}}'. The struct being passed to the template is: +// syntax of package template. The default output is equivalent +// to -f '{{.ImportPath}}'. The struct being passed to the template is: // // type Package struct { -// Dir string // directory containing package sources -// ImportPath string // import path of package in dir -// ImportComment string // path in import comment on package statement -// Name string // package name -// Doc string // package documentation string -// Target string // install path -// Shlib string // the shared library that contains this package (only set when -linkshared) -// Goroot bool // is this package in the Go root? -// Standard bool // is this package part of the standard Go library? -// Stale bool // would 'go install' do anything for this package? -// StaleReason string // explanation for Stale==true -// Root string // Go root or Go path dir containing this package -// ConflictDir string // this directory shadows Dir in $GOPATH -// BinaryOnly bool // binary-only package: cannot be recompiled from sources +// Dir string // directory containing package sources +// ImportPath string // import path of package in dir +// ImportComment string // path in import comment on package statement +// Name string // package name +// Doc string // package documentation string +// Target string // install path +// Shlib string // the shared library that contains this package (only set when -linkshared) +// Goroot bool // is this package in the Go root? +// Standard bool // is this package part of the standard Go library? +// Stale bool // would 'go install' do anything for this package? +// StaleReason string // explanation for Stale==true +// Root string // Go root or Go path dir containing this package +// ConflictDir string // this directory shadows Dir in $GOPATH +// BinaryOnly bool // binary-only package: cannot be recompiled from sources +// ForTest string // package is only for use in named test +// Export string // file containing export data (when using -export) +// Module *Module // info about package's containing module, if any (can be nil) +// Match []string // command-line patterns matching this package +// DepOnly bool // package is only a dependency, not explicitly listed // // // Source files -// GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) -// CgoFiles []string // .go sources files that import "C" -// IgnoredGoFiles []string // .go sources ignored due to build constraints -// CFiles []string // .c source files -// CXXFiles []string // .cc, .cxx and .cpp source files -// MFiles []string // .m source files -// HFiles []string // .h, .hh, .hpp and .hxx source files -// FFiles []string // .f, .F, .for and .f90 Fortran source files -// SFiles []string // .s source files -// SwigFiles []string // .swig files -// SwigCXXFiles []string // .swigcxx files -// SysoFiles []string // .syso object files to add to archive -// TestGoFiles []string // _test.go files in package -// XTestGoFiles []string // _test.go files outside package +// GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) +// CgoFiles []string // .go source files that import "C" +// CompiledGoFiles []string // .go files presented to compiler (when using -compiled) +// IgnoredGoFiles []string // .go source files ignored due to build constraints +// CFiles []string // .c source files +// CXXFiles []string // .cc, .cxx and .cpp source files +// MFiles []string // .m source files +// HFiles []string // .h, .hh, .hpp and .hxx source files +// FFiles []string // .f, .F, .for and .f90 Fortran source files +// SFiles []string // .s source files +// SwigFiles []string // .swig files +// SwigCXXFiles []string // .swigcxx files +// SysoFiles []string // .syso object files to add to archive +// TestGoFiles []string // _test.go files in package +// XTestGoFiles []string // _test.go files outside package // // // Cgo directives // CgoCFLAGS []string // cgo: flags for C compiler @@ -635,10 +669,11 @@ // CgoPkgConfig []string // cgo: pkg-config names // // // Dependency information -// Imports []string // import paths used by this package -// Deps []string // all (recursively) imported dependencies -// TestImports []string // imports from TestGoFiles -// XTestImports []string // imports from XTestGoFiles +// Imports []string // import paths used by this package +// ImportMap map[string]string // map from source import to ImportPath (identity entries omitted) +// Deps []string // all (recursively) imported dependencies +// TestImports []string // imports from TestGoFiles +// XTestImports []string // imports from XTestGoFiles // // // Error information // Incomplete bool // this package or a dependency has an error @@ -650,7 +685,7 @@ // path to the vendor directory (for example, "d/vendor/p" instead of "p"), // so that the ImportPath uniquely identifies a given copy of a package. // The Imports, Deps, TestImports, and XTestImports lists also contain these -// expanded imports paths. See golang.org/s/go15vendor for more about vendoring. +// expanded import paths. See golang.org/s/go15vendor for more about vendoring. // // The error information, if any, is // @@ -660,22 +695,25 @@ // Err string // the error itself // } // +// The module information is a Module struct, defined in the discussion +// of list -m below. +// // The template function "join" calls strings.Join. // // The template function "context" returns the build context, defined as: // -// type Context struct { -// GOARCH string // target architecture -// GOOS string // target operating system -// GOROOT string // Go root -// GOPATH string // Go path -// CgoEnabled bool // whether cgo can be used -// UseAllFiles bool // use files regardless of +build lines, file names -// Compiler string // compiler to assume when computing target paths -// BuildTags []string // build constraints to match in +build lines -// ReleaseTags []string // releases the current release is compatible with -// InstallSuffix string // suffix to use in the name of the install dir -// } +// type Context struct { +// GOARCH string // target architecture +// GOOS string // target operating system +// GOROOT string // Go root +// GOPATH string // Go path +// CgoEnabled bool // whether cgo can be used +// UseAllFiles bool // use files regardless of +build lines, file names +// Compiler string // compiler to assume when computing target paths +// BuildTags []string // build constraints to match in +build lines +// ReleaseTags []string // releases the current release is compatible with +// InstallSuffix string // suffix to use in the name of the install dir +// } // // For more information about the meaning of these fields see the documentation // for the go/build package's Context type. @@ -683,6 +721,18 @@ // The -json flag causes the package data to be printed in JSON format // instead of using the template format. // +// The -compiled flag causes list to set CompiledGoFiles to the Go source +// files presented to the compiler. Typically this means that it repeats +// the files listed in GoFiles and then also adds the Go code generated +// by processing CgoFiles and SwigFiles. The Imports list contains the +// union of all imports from both GoFiles and CompiledGoFiles. +// +// The -deps flag causes list to iterate over not just the named packages +// but also all their dependencies. It visits them in a depth-first post-order +// traversal, so that a package is listed only after all its dependencies. +// Packages not explicitly listed on the command line will have the DepOnly +// field set to true. +// // The -e flag changes the handling of erroneous packages, those that // cannot be found or are malformed. By default, the list command // prints an error to standard error for each erroneous package and @@ -693,19 +743,380 @@ // a non-nil Error field; other information may or may not be missing // (zeroed). // +// The -export flag causes list to set the Export field to the name of a +// file containing up-to-date export information for the given package. +// +// The -find flag causes list to identify the named packages but not +// resolve their dependencies: the Imports and Deps lists will be empty. +// +// The -test flag causes list to report not only the named packages +// but also their test binaries (for packages with tests), to convey to +// source code analysis tools exactly how test binaries are constructed. +// The reported import path for a test binary is the import path of +// the package followed by a ".test" suffix, as in "math/rand.test". +// When building a test, it is sometimes necessary to rebuild certain +// dependencies specially for that test (most commonly the tested +// package itself). The reported import path of a package recompiled +// for a particular test binary is followed by a space and the name of +// the test binary in brackets, as in "math/rand [math/rand.test]" +// or "regexp [sort.test]". The ForTest field is also set to the name +// of the package being tested ("math/rand" or "sort" in the previous +// examples). +// +// The Dir, Target, Shlib, Root, ConflictDir, and Export file paths +// are all absolute paths. +// +// By default, the lists GoFiles, CgoFiles, and so on hold names of files in Dir +// (that is, paths relative to Dir, not absolute paths). +// The generated files added when using the -compiled and -test flags +// are absolute paths referring to cached copies of generated Go source files. +// Although they are Go source files, the paths may not end in ".go". +// +// The -m flag causes list to list modules instead of packages. +// +// When listing modules, the -f flag still specifies a format template +// applied to a Go struct, but now a Module struct: +// +// type Module struct { +// Path string // module path +// Version string // module version +// Versions []string // available module versions (with -versions) +// Replace *Module // replaced by this module +// Time *time.Time // time version was created +// Update *Module // available update, if any (with -u) +// Main bool // is this the main module? +// Indirect bool // is this module only an indirect dependency of main module? +// Dir string // directory holding files for this module, if any +// GoMod string // path to go.mod file for this module, if any +// Error *ModuleError // error loading module +// } +// +// type ModuleError struct { +// Err string // the error itself +// } +// +// The default output is to print the module path and then +// information about the version and replacement if any. +// For example, 'go list -m all' might print: +// +// my/main/module +// golang.org/x/text v0.3.0 => /tmp/text +// rsc.io/pdf v0.1.1 +// +// The Module struct has a String method that formats this +// line of output, so that the default format is equivalent +// to -f '{{.String}}'. +// +// Note that when a module has been replaced, its Replace field +// describes the replacement module, and its Dir field is set to +// the replacement's source code, if present. (That is, if Replace +// is non-nil, then Dir is set to Replace.Dir, with no access to +// the replaced source code.) +// +// The -u flag adds information about available upgrades. +// When the latest version of a given module is newer than +// the current one, list -u sets the Module's Update field +// to information about the newer module. +// The Module's String method indicates an available upgrade by +// formatting the newer version in brackets after the current version. +// For example, 'go list -m -u all' might print: +// +// my/main/module +// golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text +// rsc.io/pdf v0.1.1 [v0.1.2] +// +// (For tools, 'go list -m -u -json all' may be more convenient to parse.) +// +// The -versions flag causes list to set the Module's Versions field +// to a list of all known versions of that module, ordered according +// to semantic versioning, earliest to latest. The flag also changes +// the default output format to display the module path followed by the +// space-separated version list. +// +// The arguments to list -m are interpreted as a list of modules, not packages. +// The main module is the module containing the current directory. +// The active modules are the main module and its dependencies. +// With no arguments, list -m shows the main module. +// With arguments, list -m shows the modules specified by the arguments. +// Any of the active modules can be specified by its module path. +// The special pattern "all" specifies all the active modules, first the main +// module and then dependencies sorted by module path. +// A pattern containing "..." specifies the active modules whose +// module paths match the pattern. +// A query of the form path@version specifies the result of that query, +// which is not limited to active modules. +// See 'go help modules' for more about module queries. +// +// The template function "module" takes a single string argument +// that must be a module path or query and returns the specified +// module as a Module struct. If an error occurs, the result will +// be a Module struct with a non-nil Error field. +// // For more about build flags, see 'go help build'. // // For more about specifying packages, see 'go help packages'. // +// For more about modules, see 'go help modules'. +// +// +// Module maintenance +// +// Go mod provides access to operations on modules. +// +// Note that support for modules is built into all the go commands, +// not just 'go mod'. For example, day-to-day adding, removing, upgrading, +// and downgrading of dependencies should be done using 'go get'. +// See 'go help modules' for an overview of module functionality. +// +// Usage: +// +// go mod <command> [arguments] +// +// The commands are: +// +// download download modules to local cache +// edit edit go.mod from tools or scripts +// graph print module requirement graph +// init initialize new module in current directory +// tidy add missing and remove unused modules +// vendor make vendored copy of dependencies +// verify verify dependencies have expected content +// why explain why packages or modules are needed +// +// Use "go help mod <command>" for more information about a command. +// +// Download modules to local cache +// +// Usage: +// +// go mod download [-dir] [-json] [modules] +// +// Download downloads the named modules, which can be module patterns selecting +// dependencies of the main module or module queries of the form path@version. +// With no arguments, download applies to all dependencies of the main module. +// +// The go command will automatically download modules as needed during ordinary +// execution. The "go mod download" command is useful mainly for pre-filling +// the local cache or to compute the answers for a Go module proxy. +// +// By default, download reports errors to standard error but is otherwise silent. +// The -json flag causes download to print a sequence of JSON objects +// to standard output, describing each downloaded module (or failure), +// corresponding to this Go struct: +// +// type Module struct { +// Path string // module path +// Version string // module version +// Error string // error loading module +// Info string // absolute path to cached .info file +// GoMod string // absolute path to cached .mod file +// Zip string // absolute path to cached .zip file +// Dir string // absolute path to cached source root directory +// Sum string // checksum for path, version (as in go.sum) +// GoModSum string // checksum for go.mod (as in go.sum) +// } +// +// See 'go help modules' for more about module queries. +// +// +// Edit go.mod from tools or scripts +// +// Usage: +// +// go mod edit [editing flags] [go.mod] +// +// Edit provides a command-line interface for editing go.mod, +// for use primarily by tools or scripts. It reads only go.mod; +// it does not look up information about the modules involved. +// By default, edit reads and writes the go.mod file of the main module, +// but a different target file can be specified after the editing flags. +// +// The editing flags specify a sequence of editing operations. +// +// The -fmt flag reformats the go.mod file without making other changes. +// This reformatting is also implied by any other modifications that use or +// rewrite the go.mod file. The only time this flag is needed is if no other +// flags are specified, as in 'go mod edit -fmt'. +// +// The -module flag changes the module's path (the go.mod file's module line). +// +// The -require=path@version and -droprequire=path flags +// add and drop a requirement on the given module path and version. +// Note that -require overrides any existing requirements on path. +// These flags are mainly for tools that understand the module graph. +// Users should prefer 'go get path@version' or 'go get path@none', +// which make other go.mod adjustments as needed to satisfy +// constraints imposed by other modules. +// +// The -exclude=path@version and -dropexclude=path@version flags +// add and drop an exclusion for the given module path and version. +// Note that -exclude=path@version is a no-op if that exclusion already exists. +// +// The -replace=old[@v]=new[@v] and -dropreplace=old[@v] flags +// add and drop a replacement of the given module path and version pair. +// If the @v in old@v is omitted, the replacement applies to all versions +// with the old module path. If the @v in new@v is omitted, the new path +// should be a local module root directory, not a module path. +// Note that -replace overrides any existing replacements for old[@v]. +// +// The -require, -droprequire, -exclude, -dropexclude, -replace, +// and -dropreplace editing flags may be repeated, and the changes +// are applied in the order given. +// +// The -print flag prints the final go.mod in its text format instead of +// writing it back to go.mod. +// +// The -json flag prints the final go.mod file in JSON format instead of +// writing it back to go.mod. The JSON output corresponds to these Go types: +// +// type Module struct { +// Path string +// Version string +// } +// +// type GoMod struct { +// Module Module +// Require []Require +// Exclude []Module +// Replace []Replace +// } +// +// type Require struct { +// Path string +// Version string +// Indirect bool +// } +// +// type Replace struct { +// Old Module +// New Module +// } +// +// Note that this only describes the go.mod file itself, not other modules +// referred to indirectly. For the full set of modules available to a build, +// use 'go list -m -json all'. +// +// For example, a tool can obtain the go.mod as a data structure by +// parsing the output of 'go mod edit -json' and can then make changes +// by invoking 'go mod edit' with -require, -exclude, and so on. +// +// +// Print module requirement graph +// +// Usage: +// +// go mod graph +// +// Graph prints the module requirement graph (with replacements applied) +// in text form. Each line in the output has two space-separated fields: a module +// and one of its requirements. Each module is identified as a string of the form +// path@version, except for the main module, which has no @version suffix. +// +// +// Initialize new module in current directory +// +// Usage: +// +// go mod init [module] +// +// Init initializes and writes a new go.mod to the current directory, +// in effect creating a new module rooted at the current directory. +// The file go.mod must not already exist. +// If possible, init will guess the module path from import comments +// (see 'go help importpath') or from version control configuration. +// To override this guess, supply the module path as an argument. +// +// +// Add missing and remove unused modules +// +// Usage: +// +// go mod tidy [-v] +// +// Tidy makes sure go.mod matches the source code in the module. +// It adds any missing modules necessary to build the current module's +// packages and dependencies, and it removes unused modules that +// don't provide any relevant packages. It also adds any missing entries +// to go.sum and removes any unnecessary ones. +// +// The -v flag causes tidy to print information about removed modules +// to standard error. +// +// +// Make vendored copy of dependencies +// +// Usage: +// +// go mod vendor [-v] +// +// Vendor resets the main module's vendor directory to include all packages +// needed to build and test all the main module's packages. +// It does not include test code for vendored packages. +// +// The -v flag causes vendor to print the names of vendored +// modules and packages to standard error. +// +// +// Verify dependencies have expected content +// +// Usage: +// +// go mod verify +// +// Verify checks that the dependencies of the current module, +// which are stored in a local downloaded source cache, have not been +// modified since being downloaded. If all the modules are unmodified, +// verify prints "all modules verified." Otherwise it reports which +// modules have been changed and causes 'go mod' to exit with a +// non-zero status. +// +// +// Explain why packages or modules are needed +// +// Usage: +// +// go mod why [-m] [-vendor] packages... +// +// Why shows a shortest path in the import graph from the main module to +// each of the listed packages. If the -m flag is given, why treats the +// arguments as a list of modules and finds a path to any package in each +// of the modules. +// +// By default, why queries the graph of packages matched by "go list all", +// which includes tests for reachable packages. The -vendor flag causes why +// to exclude tests of dependencies. +// +// The output is a sequence of stanzas, one for each package or module +// name on the command line, separated by blank lines. Each stanza begins +// with a comment line "# package" or "# module" giving the target +// package or module. Subsequent lines give a path through the import +// graph, one package per line. If the package or module is not +// referenced from the main module, the stanza will display a single +// parenthesized note indicating that fact. +// +// For example: +// +// $ go mod why golang.org/x/text/language golang.org/x/text/encoding +// # golang.org/x/text/language +// rsc.io/quote +// rsc.io/sampler +// golang.org/x/text/language +// +// # golang.org/x/text/encoding +// (main module does not need package golang.org/x/text/encoding) +// $ +// // // Compile and run Go program // // Usage: // -// go run [build flags] [-exec xprog] gofiles... [arguments...] +// go run [build flags] [-exec xprog] package [arguments...] // -// Run compiles and runs the main package comprising the named Go source files. -// A Go source file is defined to be a file ending in a literal ".go" suffix. +// Run compiles and runs the named main Go package. +// Typically the package is specified as a list of .go source files, +// but it may also be an import path, file system path, or pattern +// matching a single known package, as in 'go run .' or 'go run my/cmd'. // // By default, 'go run' runs the compiled binary directly: 'a.out arguments...'. // If the -exec flag is given, 'go run' invokes the binary using xprog: @@ -717,7 +1128,10 @@ // cross-compiled programs when a simulator or other execution method is // available. // +// The exit status of Run is not the exit status of the compiled binary. +// // For more about build flags, see 'go help build'. +// For more about specifying packages, see 'go help packages'. // // See also: go build. // @@ -753,9 +1167,12 @@ // // As part of building a test binary, go test runs go vet on the package // and its test source files to identify significant problems. If go vet -// finds any problems, go test reports those and does not run the test binary. -// Only a high-confidence subset of the default go vet checks are used. -// To disable the running of go vet, use the -vet=off flag. +// finds any problems, go test reports those and does not run the test +// binary. Only a high-confidence subset of the default go vet checks are +// used. That subset is: 'atomic', 'bool', 'buildtags', 'nilfunc', and +// 'printf'. You can see the documentation for these and other vet tests +// via "go doc cmd/vet". To disable the running of go vet, use the +// -vet=off flag. // // All test output and summary lines are printed to the go command's // standard output, even if the test printed them to its own standard @@ -887,25 +1304,6 @@ // See also: go fmt, go fix. // // -// Calling between Go and C -// -// There are two different ways to call between Go and C/C++ code. -// -// The first is the cgo tool, which is part of the Go distribution. For -// information on how to use it see the cgo documentation (go doc cmd/cgo). -// -// The second is the SWIG program, which is a general tool for -// interfacing between languages. For information on SWIG see -// http://swig.org/. When running go build, any file with a .swig -// extension will be passed to SWIG. Any file with a .swigcxx extension -// will be passed to SWIG with the -c++ option. -// -// When either cgo or SWIG is used, go build will pass any .c, .m, .s, -// or .S files to the C compiler, and any .cc, .cpp, .cxx files to the C++ -// compiler. The CC or CXX environment variables may be set to determine -// the C or C++ compiler, respectively, to use. -// -// // Build modes // // The 'go build' and 'go install' commands take a -buildmode argument which @@ -952,6 +1350,25 @@ // import, into a Go plugin. Packages not named main are ignored. // // +// Calling between Go and C +// +// There are two different ways to call between Go and C/C++ code. +// +// The first is the cgo tool, which is part of the Go distribution. For +// information on how to use it see the cgo documentation (go doc cmd/cgo). +// +// The second is the SWIG program, which is a general tool for +// interfacing between languages. For information on SWIG see +// http://swig.org/. When running go build, any file with a .swig +// extension will be passed to SWIG. Any file with a .swigcxx extension +// will be passed to SWIG with the -c++ option. +// +// When either cgo or SWIG is used, go build will pass any .c, .m, .s, +// or .S files to the C compiler, and any .cc, .cpp, .cxx files to the C++ +// compiler. The CC or CXX environment variables may be set to determine +// the C or C++ compiler, respectively, to use. +// +// // Build and test caching // // The go command caches build outputs for reuse in future builds. @@ -959,6 +1376,7 @@ // in the standard user cache directory for the current operating system. // Setting the GOCACHE environment variable overrides this default, // and running 'go env GOCACHE' prints the current cache directory. +// You can set the variable to 'off' to disable the cache. // // The go command periodically deletes cached data that has not been // used recently. Running 'go clean -cache' deletes all cached data. @@ -991,6 +1409,129 @@ // decisions about whether to reuse a cached test result. // // +// Environment variables +// +// The go command, and the tools it invokes, examine a few different +// environment variables. For many of these, you can see the default +// value of on your system by running 'go env NAME', where NAME is the +// name of the variable. +// +// General-purpose environment variables: +// +// GCCGO +// The gccgo command to run for 'go build -compiler=gccgo'. +// GOARCH +// The architecture, or processor, for which to compile code. +// Examples are amd64, 386, arm, ppc64. +// GOBIN +// The directory where 'go install' will install a command. +// GOCACHE +// The directory where the go command will store cached +// information for reuse in future builds. +// GOFLAGS +// A space-separated list of -flag=value settings to apply +// to go commands by default, when the given flag is known by +// the current command. Flags listed on the command-line +// are applied after this list and therefore override it. +// GOOS +// The operating system for which to compile code. +// Examples are linux, darwin, windows, netbsd. +// GOPATH +// For more details see: 'go help gopath'. +// GOPROXY +// URL of Go module proxy. See 'go help goproxy'. +// GORACE +// Options for the race detector. +// See https://golang.org/doc/articles/race_detector.html. +// GOROOT +// The root of the go tree. +// GOTMPDIR +// The directory where the go command will write +// temporary source files, packages, and binaries. +// +// Environment variables for use with cgo: +// +// CC +// The command to use to compile C code. +// CGO_ENABLED +// Whether the cgo command is supported. Either 0 or 1. +// CGO_CFLAGS +// Flags that cgo will pass to the compiler when compiling +// C code. +// CGO_CFLAGS_ALLOW +// A regular expression specifying additional flags to allow +// to appear in #cgo CFLAGS source code directives. +// Does not apply to the CGO_CFLAGS environment variable. +// CGO_CFLAGS_DISALLOW +// A regular expression specifying flags that must be disallowed +// from appearing in #cgo CFLAGS source code directives. +// Does not apply to the CGO_CFLAGS environment variable. +// CGO_CPPFLAGS, CGO_CPPFLAGS_ALLOW, CGO_CPPFLAGS_DISALLOW +// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, +// but for the C preprocessor. +// CGO_CXXFLAGS, CGO_CXXFLAGS_ALLOW, CGO_CXXFLAGS_DISALLOW +// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, +// but for the C++ compiler. +// CGO_FFLAGS, CGO_FFLAGS_ALLOW, CGO_FFLAGS_DISALLOW +// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, +// but for the Fortran compiler. +// CGO_LDFLAGS, CGO_LDFLAGS_ALLOW, CGO_LDFLAGS_DISALLOW +// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, +// but for the linker. +// CXX +// The command to use to compile C++ code. +// PKG_CONFIG +// Path to pkg-config tool. +// +// Architecture-specific environment variables: +// +// GOARM +// For GOARCH=arm, the ARM architecture for which to compile. +// Valid values are 5, 6, 7. +// GO386 +// For GOARCH=386, the floating point instruction set. +// Valid values are 387, sse2. +// GOMIPS +// For GOARCH=mips{,le}, whether to use floating point instructions. +// Valid values are hardfloat (default), softfloat. +// GOMIPS64 +// For GOARCH=mips64{,le}, whether to use floating point instructions. +// Valid values are hardfloat (default), softfloat. +// +// Special-purpose environment variables: +// +// GCCGOTOOLDIR +// If set, where to find gccgo tools, such as cgo. +// The default is based on how gccgo was configured. +// GOROOT_FINAL +// The root of the installed Go tree, when it is +// installed in a location other than where it is built. +// File names in stack traces are rewritten from GOROOT to +// GOROOT_FINAL. +// GO_EXTLINK_ENABLED +// Whether the linker should use external linking mode +// when using -linkmode=auto with code that uses cgo. +// Set to 0 to disable external linking mode, 1 to enable it. +// GIT_ALLOW_PROTOCOL +// Defined by Git. A colon-separated list of schemes that are allowed to be used +// with git fetch/clone. If set, any scheme not explicitly mentioned will be +// considered insecure by 'go get'. +// +// Additional information available from 'go env' but not read from the environment: +// +// GOEXE +// The executable file name suffix (".exe" on Windows, "" on other systems). +// GOHOSTARCH +// The architecture (GOARCH) of the Go toolchain binaries. +// GOHOSTOS +// The operating system (GOOS) of the Go toolchain binaries. +// GOMOD +// The absolute path to the go.mod of the main module, +// or the empty string if not using modules. +// GOTOOLDIR +// The directory where the go tools (compile, cover, doc, etc...) are installed. +// +// // File types // // The go command examines the contents of a restricted set of files @@ -1036,6 +1577,85 @@ // command. // // +// The go.mod file +// +// A module version is defined by a tree of source files, with a go.mod +// file in its root. When the go command is run, it looks in the current +// directory and then successive parent directories to find the go.mod +// marking the root of the main (current) module. +// +// The go.mod file itself is line-oriented, with // comments but +// no /* */ comments. Each line holds a single directive, made up of a +// verb followed by arguments. For example: +// +// module my/thing +// require other/thing v1.0.2 +// require new/thing v2.3.4 +// exclude old/thing v1.2.3 +// replace bad/thing v1.4.5 => good/thing v1.4.5 +// +// The verbs are module, to define the module path; require, to require +// a particular module at a given version or later; exclude, to exclude +// a particular module version from use; and replace, to replace a module +// version with a different module version. Exclude and replace apply only +// in the main module's go.mod and are ignored in dependencies. +// See https://research.swtch.com/vgo-mvs for details. +// +// The leading verb can be factored out of adjacent lines to create a block, +// like in Go imports: +// +// require ( +// new/thing v2.3.4 +// old/thing v1.2.3 +// ) +// +// The go.mod file is designed both to be edited directly and to be +// easily updated by tools. The 'go mod edit' command can be used to +// parse and edit the go.mod file from programs and tools. +// See 'go help mod edit'. +// +// The go command automatically updates go.mod each time it uses the +// module graph, to make sure go.mod always accurately reflects reality +// and is properly formatted. For example, consider this go.mod file: +// +// module M +// +// require ( +// A v1 +// B v1.0.0 +// C v1.0.0 +// D v1.2.3 +// E dev +// ) +// +// exclude D v1.2.3 +// +// The update rewrites non-canonical version identifiers to semver form, +// so A's v1 becomes v1.0.0 and E's dev becomes the pseudo-version for the +// latest commit on the dev branch, perhaps v0.0.0-20180523231146-b3f5c0f6e5f1. +// +// The update modifies requirements to respect exclusions, so the +// requirement on the excluded D v1.2.3 is updated to use the next +// available version of D, perhaps D v1.2.4 or D v1.3.0. +// +// The update removes redundant or misleading requirements. +// For example, if A v1.0.0 itself requires B v1.2.0 and C v1.0.0, +// then go.mod's requirement of B v1.0.0 is misleading (superseded by +// A's need for v1.2.0), and its requirement of C v1.0.0 is redundant +// (implied by A's need for the same version), so both will be removed. +// If module M contains packages that directly import packages from B or +// C, then the requirements will be kept but updated to the actual +// versions being used. +// +// Finally, the update reformats the go.mod in a canonical formatting, so +// that future mechanical changes will result in minimal diffs. +// +// Because the module graph defines the meaning of import statements, any +// commands that load packages also use and therefore update go.mod, +// including go build, go get, go install, go list, go test, go mod graph, +// go mod tidy, and go mod why. +// +// // GOPATH environment variable // // The Go path is used to resolve import statements. @@ -1102,6 +1722,12 @@ // // See https://golang.org/doc/code.html for an example. // +// GOPATH and Modules +// +// When using modules, GOPATH is no longer used for resolving imports. +// However, it is still used to store downloaded source code (in GOPATH/pkg/mod) +// and compiled commands (in GOPATH/bin). +// // Internal Directories // // Code in or below a directory named "internal" is importable only @@ -1185,103 +1811,67 @@ // See https://golang.org/s/go15vendor for details. // // -// Environment variables +// Module proxy protocol // -// The go command, and the tools it invokes, examine a few different -// environment variables. For many of these, you can see the default -// value of on your system by running 'go env NAME', where NAME is the -// name of the variable. +// The go command by default downloads modules from version control systems +// directly, just as 'go get' always has. The GOPROXY environment variable allows +// further control over the download source. If GOPROXY is unset, is the empty string, +// or is the string "direct", downloads use the default direct connection to version +// control systems. Setting GOPROXY to "off" disallows downloading modules from +// any source. Otherwise, GOPROXY is expected to be the URL of a module proxy, +// in which case the go command will fetch all modules from that proxy. +// No matter the source of the modules, downloaded modules must match existing +// entries in go.sum (see 'go help modules' for discussion of verification). // -// General-purpose environment variables: +// A Go module proxy is any web server that can respond to GET requests for +// URLs of a specified form. The requests have no query parameters, so even +// a site serving from a fixed file system (including a file:/// URL) +// can be a module proxy. // -// GCCGO -// The gccgo command to run for 'go build -compiler=gccgo'. -// GOARCH -// The architecture, or processor, for which to compile code. -// Examples are amd64, 386, arm, ppc64. -// GOBIN -// The directory where 'go install' will install a command. -// GOOS -// The operating system for which to compile code. -// Examples are linux, darwin, windows, netbsd. -// GOPATH -// For more details see: 'go help gopath'. -// GORACE -// Options for the race detector. -// See https://golang.org/doc/articles/race_detector.html. -// GOROOT -// The root of the go tree. -// GOTMPDIR -// The directory where the go command will write -// temporary source files, packages, and binaries. -// GOCACHE -// The directory where the go command will store -// cached information for reuse in future builds. +// The GET requests sent to a Go module proxy are: // -// Environment variables for use with cgo: +// GET $GOPROXY/<module>/@v/list returns a list of all known versions of the +// given module, one per line. // -// CC -// The command to use to compile C code. -// CGO_ENABLED -// Whether the cgo command is supported. Either 0 or 1. -// CGO_CFLAGS -// Flags that cgo will pass to the compiler when compiling -// C code. -// CGO_CFLAGS_ALLOW -// A regular expression specifying additional flags to allow -// to appear in #cgo CFLAGS source code directives. -// Does not apply to the CGO_CFLAGS environment variable. -// CGO_CFLAGS_DISALLOW -// A regular expression specifying flags that must be disallowed -// from appearing in #cgo CFLAGS source code directives. -// Does not apply to the CGO_CFLAGS environment variable. -// CGO_CPPFLAGS, CGO_CPPFLAGS_ALLOW, CGO_CPPFLAGS_DISALLOW -// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, -// but for the C preprocessor. -// CGO_CXXFLAGS, CGO_CXXFLAGS_ALLOW, CGO_CXXFLAGS_DISALLOW -// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, -// but for the C++ compiler. -// CGO_FFLAGS, CGO_FFLAGS_ALLOW, CGO_FFLAGS_DISALLOW -// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, -// but for the Fortran compiler. -// CGO_LDFLAGS, CGO_LDFLAGS_ALLOW, CGO_LDFLAGS_DISALLOW -// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, -// but for the linker. -// CXX -// The command to use to compile C++ code. -// PKG_CONFIG -// Path to pkg-config tool. +// GET $GOPROXY/<module>/@v/<version>.info returns JSON-formatted metadata +// about that version of the given module. // -// Architecture-specific environment variables: +// GET $GOPROXY/<module>/@v/<version>.mod returns the go.mod file +// for that version of the given module. // -// GOARM -// For GOARCH=arm, the ARM architecture for which to compile. -// Valid values are 5, 6, 7. -// GO386 -// For GOARCH=386, the floating point instruction set. -// Valid values are 387, sse2. -// GOMIPS -// For GOARCH=mips{,le}, whether to use floating point instructions. -// Valid values are hardfloat (default), softfloat. +// GET $GOPROXY/<module>/@v/<version>.zip returns the zip archive +// for that version of the given module. // -// Special-purpose environment variables: +// To avoid problems when serving from case-sensitive file systems, +// the <module> and <version> elements are case-encoded, replacing every +// uppercase letter with an exclamation mark followed by the corresponding +// lower-case letter: github.com/Azure encodes as github.com/!azure. // -// GCCGOTOOLDIR -// If set, where to find gccgo tools, such as cgo. -// The default is based on how gccgo was configured. -// GOROOT_FINAL -// The root of the installed Go tree, when it is -// installed in a location other than where it is built. -// File names in stack traces are rewritten from GOROOT to -// GOROOT_FINAL. -// GO_EXTLINK_ENABLED -// Whether the linker should use external linking mode -// when using -linkmode=auto with code that uses cgo. -// Set to 0 to disable external linking mode, 1 to enable it. -// GIT_ALLOW_PROTOCOL -// Defined by Git. A colon-separated list of schemes that are allowed to be used -// with git fetch/clone. If set, any scheme not explicitly mentioned will be -// considered insecure by 'go get'. +// The JSON-formatted metadata about a given module corresponds to +// this Go data structure, which may be expanded in the future: +// +// type Info struct { +// Version string // version string +// Time time.Time // commit time +// } +// +// The zip archive for a specific version of a given module is a +// standard zip file that contains the file tree corresponding +// to the module's source code and related files. The archive uses +// slash-separated paths, and every file path in the archive must +// begin with <module>@<version>/, where the module and version are +// substituted directly, not case-encoded. The root of the module +// file tree corresponds to the <module>@<version>/ prefix in the +// archive. +// +// Even when downloading directly from version control systems, +// the go command synthesizes explicit info, mod, and zip files +// and stores them in its local cache, $GOPATH/pkg/mod/cache/download, +// the same as if it had downloaded them directly from a proxy. +// The cache layout is the same as the proxy URL space, so +// serving $GOPATH/pkg/mod/cache/download at (or copying it to) +// https://example.com/proxy would let other users access those +// cached module versions with GOPROXY=https://example.com/proxy. // // // Import path syntax @@ -1361,6 +1951,7 @@ // that repository. The supported version control systems are: // // Bazaar .bzr +// Fossil .fossil // Git .git // Mercurial .hg // Subversion .svn @@ -1404,7 +1995,7 @@ // In particular, it should appear before any raw JavaScript or CSS, // to avoid confusing the go command's restricted parser. // -// The vcs is one of "git", "hg", "svn", etc, +// The vcs is one of "bzr", "fossil", "git", "hg", "svn". // // The repo-root is the root of the version control system // containing a scheme and not containing a .vcs qualifier. @@ -1426,12 +2017,22 @@ // same meta tag and then git clone https://code.org/r/p/exproj into // GOPATH/src/example.org. // -// New downloaded packages are written to the first directory listed in the GOPATH -// environment variable (For more details see: 'go help gopath'). +// When using GOPATH, downloaded packages are written to the first directory +// listed in the GOPATH environment variable. +// (See 'go help gopath-get' and 'go help gopath'.) +// +// When using modules, downloaded packages are stored in the module cache. +// (See 'go help modules-get' and 'go help goproxy'.) // -// The go command attempts to download the version of the -// package appropriate for the Go release being used. -// Run 'go help get' for more. +// When using modules, an additional variant of the go-import meta tag is +// recognized and is preferred over those listing version control systems. +// That variant uses "mod" as the vcs in the content value, as in: +// +// <meta name="go-import" content="example.org mod https://code.org/moduleproxy"> +// +// This tag means to fetch modules with paths beginning with example.org +// from the module proxy available at the URL https://code.org/moduleproxy. +// See 'go help goproxy' for details about the proxy protocol. // // Import path checking // @@ -1454,10 +2055,484 @@ // This makes it possible to copy code into alternate locations in vendor trees // without needing to update import comments. // +// Import path checking is also disabled when using modules. +// Import path comments are obsoleted by the go.mod file's module statement. +// // See https://golang.org/s/go14customimport for details. // // -// Package lists +// Modules, module versions, and more +// +// A module is a collection of related Go packages. +// Modules are the unit of source code interchange and versioning. +// The go command has direct support for working with modules, +// including recording and resolving dependencies on other modules. +// Modules replace the old GOPATH-based approach to specifying +// which source files are used in a given build. +// +// Preliminary module support +// +// Go 1.11 includes preliminary support for Go modules, +// including a new module-aware 'go get' command. +// We intend to keep revising this support, while preserving compatibility, +// until it can be declared official (no longer preliminary), +// and then at a later point we may remove support for work +// in GOPATH and the old 'go get' command. +// +// The quickest way to take advantage of the new Go 1.11 module support +// is to check out your repository into a directory outside GOPATH/src, +// create a go.mod file (described in the next section) there, and run +// go commands from within that file tree. +// +// For more fine-grained control, the module support in Go 1.11 respects +// a temporary environment variable, GO111MODULE, which can be set to one +// of three string values: off, on, or auto (the default). +// If GO111MODULE=off, then the go command never uses the +// new module support. Instead it looks in vendor directories and GOPATH +// to find dependencies; we now refer to this as "GOPATH mode." +// If GO111MODULE=on, then the go command requires the use of modules, +// never consulting GOPATH. We refer to this as the command being +// module-aware or running in "module-aware mode". +// If GO111MODULE=auto or is unset, then the go command enables or +// disables module support based on the current directory. +// Module support is enabled only when the current directory is outside +// GOPATH/src and itself contains a go.mod file or is below a directory +// containing a go.mod file. +// +// In module-aware mode, GOPATH no longer defines the meaning of imports +// during a build, but it still stores downloaded dependencies (in GOPATH/pkg/mod) +// and installed commands (in GOPATH/bin, unless GOBIN is set). +// +// Defining a module +// +// A module is defined by a tree of Go source files with a go.mod file +// in the tree's root directory. The directory containing the go.mod file +// is called the module root. Typically the module root will also correspond +// to a source code repository root (but in general it need not). +// The module is the set of all Go packages in the module root and its +// subdirectories, but excluding subtrees with their own go.mod files. +// +// The "module path" is the import path prefix corresponding to the module root. +// The go.mod file defines the module path and lists the specific versions +// of other modules that should be used when resolving imports during a build, +// by giving their module paths and versions. +// +// For example, this go.mod declares that the directory containing it is the root +// of the module with path example.com/m, and it also declares that the module +// depends on specific versions of golang.org/x/text and gopkg.in/yaml.v2: +// +// module example.com/m +// +// require ( +// golang.org/x/text v0.3.0 +// gopkg.in/yaml.v2 v2.1.0 +// ) +// +// The go.mod file can also specify replacements and excluded versions +// that only apply when building the module directly; they are ignored +// when the module is incorporated into a larger build. +// For more about the go.mod file, see 'go help go.mod'. +// +// To start a new module, simply create a go.mod file in the root of the +// module's directory tree, containing only a module statement. +// The 'go mod init' command can be used to do this: +// +// go mod init example.com/m +// +// In a project already using an existing dependency management tool like +// godep, glide, or dep, 'go mod init' will also add require statements +// matching the existing configuration. +// +// Once the go.mod file exists, no additional steps are required: +// go commands like 'go build', 'go test', or even 'go list' will automatically +// add new dependencies as needed to satisfy imports. +// +// The main module and the build list +// +// The "main module" is the module containing the directory where the go command +// is run. The go command finds the module root by looking for a go.mod in the +// current directory, or else the current directory's parent directory, +// or else the parent's parent directory, and so on. +// +// The main module's go.mod file defines the precise set of packages available +// for use by the go command, through require, replace, and exclude statements. +// Dependency modules, found by following require statements, also contribute +// to the definition of that set of packages, but only through their go.mod +// files' require statements: any replace and exclude statements in dependency +// modules are ignored. The replace and exclude statements therefore allow the +// main module complete control over its own build, without also being subject +// to complete control by dependencies. +// +// The set of modules providing packages to builds is called the "build list". +// The build list initially contains only the main module. Then the go command +// adds to the list the exact module versions required by modules already +// on the list, recursively, until there is nothing left to add to the list. +// If multiple versions of a particular module are added to the list, +// then at the end only the latest version (according to semantic version +// ordering) is kept for use in the build. +// +// The 'go list' command provides information about the main module +// and the build list. For example: +// +// go list -m # print path of main module +// go list -m -f={{.Dir}} # print root directory of main module +// go list -m all # print build list +// +// Maintaining module requirements +// +// The go.mod file is meant to be readable and editable by both +// programmers and tools. The go command itself automatically updates the go.mod file +// to maintain a standard formatting and the accuracy of require statements. +// +// Any go command that finds an unfamiliar import will look up the module +// containing that import and add the latest version of that module +// to go.mod automatically. In most cases, therefore, it suffices to +// add an import to source code and run 'go build', 'go test', or even 'go list': +// as part of analyzing the package, the go command will discover +// and resolve the import and update the go.mod file. +// +// Any go command can determine that a module requirement is +// missing and must be added, even when considering only a single +// package from the module. On the other hand, determining that a module requirement +// is no longer necessary and can be deleted requires a full view of +// all packages in the module, across all possible build configurations +// (architectures, operating systems, build tags, and so on). +// The 'go mod tidy' command builds that view and then +// adds any missing module requirements and removes unnecessary ones. +// +// As part of maintaining the require statements in go.mod, the go command +// tracks which ones provide packages imported directly by the current module +// and which ones provide packages only used indirectly by other module +// dependencies. Requirements needed only for indirect uses are marked with a +// "// indirect" comment in the go.mod file. Indirect requirements are +// automatically removed from the go.mod file once they are implied by other +// direct requirements. Indirect requirements only arise when using modules +// that fail to state some of their own dependencies or when explicitly +// upgrading a module's dependencies ahead of its own stated requirements. +// +// Because of this automatic maintenance, the information in go.mod is an +// up-to-date, readable description of the build. +// +// The 'go get' command updates go.mod to change the module versions used in a +// build. An upgrade of one module may imply upgrading others, and similarly a +// downgrade of one module may imply downgrading others. The 'go get' command +// makes these implied changes as well. If go.mod is edited directly, commands +// like 'go build' or 'go list' will assume that an upgrade is intended and +// automatically make any implied upgrades and update go.mod to reflect them. +// +// The 'go mod' command provides other functionality for use in maintaining +// and understanding modules and go.mod files. See 'go help mod'. +// +// The -mod build flag provides additional control over updating and use of go.mod. +// +// If invoked with -mod=readonly, the go command is disallowed from the implicit +// automatic updating of go.mod described above. Instead, it fails when any changes +// to go.mod are needed. This setting is most useful to check that go.mod does +// not need updates, such as in a continuous integration and testing system. +// The "go get" command remains permitted to update go.mod even with -mod=readonly, +// and the "go mod" commands do not take the -mod flag (or any other build flags). +// +// If invoked with -mod=vendor, the go command assumes that the vendor +// directory holds the correct copies of dependencies and ignores +// the dependency descriptions in go.mod. +// +// Pseudo-versions +// +// The go.mod file and the go command more generally use semantic versions as +// the standard form for describing module versions, so that versions can be +// compared to determine which should be considered earlier or later than another. +// A module version like v1.2.3 is introduced by tagging a revision in the +// underlying source repository. Untagged revisions can be referred to +// using a "pseudo-version" like v0.0.0-yyyymmddhhmmss-abcdefabcdef, +// where the time is the commit time in UTC and the final suffix is the prefix +// of the commit hash. The time portion ensures that two pseudo-versions can +// be compared to determine which happened later, the commit hash identifes +// the underlying commit, and the prefix (v0.0.0- in this example) is derived from +// the most recent tagged version in the commit graph before this commit. +// +// There are three pseudo-version forms: +// +// vX.0.0-yyyymmddhhmmss-abcdefabcdef is used when there is no earlier +// versioned commit with an appropriate major version before the target commit. +// (This was originally the only form, so some older go.mod files use this form +// even for commits that do follow tags.) +// +// vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef is used when the most +// recent versioned commit before the target commit is vX.Y.Z-pre. +// +// vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef is used when the most +// recent versioned commit before the target commit is vX.Y.Z. +// +// Pseudo-versions never need to be typed by hand: the go command will accept +// the plain commit hash and translate it into a pseudo-version (or a tagged +// version if available) automatically. This conversion is an example of a +// module query. +// +// Module queries +// +// The go command accepts a "module query" in place of a module version +// both on the command line and in the main module's go.mod file. +// (After evaluating a query found in the main module's go.mod file, +// the go command updates the file to replace the query with its result.) +// +// A fully-specified semantic version, such as "v1.2.3", +// evaluates to that specific version. +// +// A semantic version prefix, such as "v1" or "v1.2", +// evaluates to the latest available tagged version with that prefix. +// +// A semantic version comparison, such as "<v1.2.3" or ">=v1.5.6", +// evaluates to the available tagged version nearest to the comparison target +// (the latest version for < and <=, the earliest version for > and >=). +// +// The string "latest" matches the latest available tagged version, +// or else the underlying source repository's latest untagged revision. +// +// A revision identifier for the underlying source repository, +// such as a commit hash prefix, revision tag, or branch name, +// selects that specific code revision. If the revision is +// also tagged with a semantic version, the query evaluates to +// that semantic version. Otherwise the query evaluates to a +// pseudo-version for the commit. +// +// All queries prefer release versions to pre-release versions. +// For example, "<v1.2.3" will prefer to return "v1.2.2" +// instead of "v1.2.3-pre1", even though "v1.2.3-pre1" is nearer +// to the comparison target. +// +// Module versions disallowed by exclude statements in the +// main module's go.mod are considered unavailable and cannot +// be returned by queries. +// +// For example, these commands are all valid: +// +// go get github.com/gorilla/mux@latest # same (@latest is default for 'go get') +// go get github.com/gorilla/mux@v1.6.2 # records v1.6.2 +// go get github.com/gorilla/mux@e3702bed2 # records v1.6.2 +// go get github.com/gorilla/mux@c856192 # records v0.0.0-20180517173623-c85619274f5d +// go get github.com/gorilla/mux@master # records current meaning of master +// +// Module compatibility and semantic versioning +// +// The go command requires that modules use semantic versions and expects that +// the versions accurately describe compatibility: it assumes that v1.5.4 is a +// backwards-compatible replacement for v1.5.3, v1.4.0, and even v1.0.0. +// More generally the go command expects that packages follow the +// "import compatibility rule", which says: +// +// "If an old package and a new package have the same import path, +// the new package must be backwards compatible with the old package." +// +// Because the go command assumes the import compatibility rule, +// a module definition can only set the minimum required version of one +// of its dependencies: it cannot set a maximum or exclude selected versions. +// Still, the import compatibility rule is not a guarantee: it may be that +// v1.5.4 is buggy and not a backwards-compatible replacement for v1.5.3. +// Because of this, the go command never updates from an older version +// to a newer version of a module unasked. +// +// In semantic versioning, changing the major version number indicates a lack +// of backwards compatibility with earlier versions. To preserve import +// compatibility, the go command requires that modules with major version v2 +// or later use a module path with that major version as the final element. +// For example, version v2.0.0 of example.com/m must instead use module path +// example.com/m/v2, and packages in that module would use that path as +// their import path prefix, as in example.com/m/v2/sub/pkg. Including the +// major version number in the module path and import paths in this way is +// called "semantic import versioning". Pseudo-versions for modules with major +// version v2 and later begin with that major version instead of v0, as in +// v2.0.0-20180326061214-4fc5987536ef. +// +// As a special case, module paths beginning with gopkg.in/ continue to use the +// conventions established on that system: the major version is always present, +// and it is preceded by a dot instead of a slash: gopkg.in/yaml.v1 +// and gopkg.in/yaml.v2, not gopkg.in/yaml and gopkg.in/yaml/v2. +// +// The go command treats modules with different module paths as unrelated: +// it makes no connection between example.com/m and example.com/m/v2. +// Modules with different major versions can be used together in a build +// and are kept separate by the fact that their packages use different +// import paths. +// +// In semantic versioning, major version v0 is for initial development, +// indicating no expectations of stability or backwards compatibility. +// Major version v0 does not appear in the module path, because those +// versions are preparation for v1.0.0, and v1 does not appear in the +// module path either. +// +// Code written before the semantic import versioning convention +// was introduced may use major versions v2 and later to describe +// the same set of unversioned import paths as used in v0 and v1. +// To accommodate such code, if a source code repository has a +// v2.0.0 or later tag for a file tree with no go.mod, the version is +// considered to be part of the v1 module's available versions +// and is given an +incompatible suffix when converted to a module +// version, as in v2.0.0+incompatible. The +incompatible tag is also +// applied to pseudo-versions derived from such versions, as in +// v2.0.1-0.yyyymmddhhmmss-abcdefabcdef+incompatible. +// +// In general, having a dependency in the build list (as reported by 'go list -m all') +// on a v0 version, pre-release version, pseudo-version, or +incompatible version +// is an indication that problems are more likely when upgrading that +// dependency, since there is no expectation of compatibility for those. +// +// See https://research.swtch.com/vgo-import for more information about +// semantic import versioning, and see https://semver.org/ for more about +// semantic versioning. +// +// Module code layout +// +// For now, see https://research.swtch.com/vgo-module for information +// about how source code in version control systems is mapped to +// module file trees. +// +// Module downloading and verification +// +// The go command maintains, in the main module's root directory alongside +// go.mod, a file named go.sum containing the expected cryptographic checksums +// of the content of specific module versions. Each time a dependency is +// used, its checksum is added to go.sum if missing or else required to match +// the existing entry in go.sum. +// +// The go command maintains a cache of downloaded packages and computes +// and records the cryptographic checksum of each package at download time. +// In normal operation, the go command checks these pre-computed checksums +// against the main module's go.sum file, instead of recomputing them on +// each command invocation. The 'go mod verify' command checks that +// the cached copies of module downloads still match both their recorded +// checksums and the entries in go.sum. +// +// The go command can fetch modules from a proxy instead of connecting +// to source control systems directly, according to the setting of the GOPROXY +// environment variable. +// +// See 'go help goproxy' for details about the proxy and also the format of +// the cached downloaded packages. +// +// Modules and vendoring +// +// When using modules, the go command completely ignores vendor directories. +// +// By default, the go command satisfies dependencies by downloading modules +// from their sources and using those downloaded copies (after verification, +// as described in the previous section). To allow interoperation with older +// versions of Go, or to ensure that all files used for a build are stored +// together in a single file tree, 'go mod vendor' creates a directory named +// vendor in the root directory of the main module and stores there all the +// packages from dependency modules that are needed to support builds and +// tests of packages in the main module. +// +// To build using the main module's top-level vendor directory to satisfy +// dependencies (disabling use of the usual network sources and local +// caches), use 'go build -mod=vendor'. Note that only the main module's +// top-level vendor directory is used; vendor directories in other locations +// are still ignored. +// +// +// Module-aware go get +// +// The 'go get' command changes behavior depending on whether the +// go command is running in module-aware mode or legacy GOPATH mode. +// This help text, accessible as 'go help module-get' even in legacy GOPATH mode, +// describes 'go get' as it operates in module-aware mode. +// +// Usage: go get [-d] [-m] [-u] [-v] [-insecure] [build flags] [packages] +// +// Get resolves and adds dependencies to the current development module +// and then builds and installs them. +// +// The first step is to resolve which dependencies to add. +// +// For each named package or package pattern, get must decide which version of +// the corresponding module to use. By default, get chooses the latest tagged +// release version, such as v0.4.5 or v1.2.3. If there are no tagged release +// versions, get chooses the latest tagged prerelease version, such as +// v0.0.1-pre1. If there are no tagged versions at all, get chooses the latest +// known commit. +// +// This default version selection can be overridden by adding an @version +// suffix to the package argument, as in 'go get golang.org/x/text@v0.3.0'. +// For modules stored in source control repositories, the version suffix can +// also be a commit hash, branch identifier, or other syntax known to the +// source control system, as in 'go get golang.org/x/text@master'. +// The version suffix @latest explicitly requests the default behavior +// described above. +// +// If a module under consideration is already a dependency of the current +// development module, then get will update the required version. +// Specifying a version earlier than the current required version is valid and +// downgrades the dependency. The version suffix @none indicates that the +// dependency should be removed entirely. +// +// Although get defaults to using the latest version of the module containing +// a named package, it does not use the latest version of that module's +// dependencies. Instead it prefers to use the specific dependency versions +// requested by that module. For example, if the latest A requires module +// B v1.2.3, while B v1.2.4 and v1.3.1 are also available, then 'go get A' +// will use the latest A but then use B v1.2.3, as requested by A. (If there +// are competing requirements for a particular module, then 'go get' resolves +// those requirements by taking the maximum requested version.) +// +// The -u flag instructs get to update dependencies to use newer minor or +// patch releases when available. Continuing the previous example, +// 'go get -u A' will use the latest A with B v1.3.1 (not B v1.2.3). +// +// The -u=patch flag (not -u patch) instructs get to update dependencies +// to use newer patch releases when available. Continuing the previous example, +// 'go get -u=patch A' will use the latest A with B v1.2.4 (not B v1.2.3). +// +// In general, adding a new dependency may require upgrading +// existing dependencies to keep a working build, and 'go get' does +// this automatically. Similarly, downgrading one dependency may +// require downgrading other dependenceis, and 'go get' does +// this automatically as well. +// +// The -m flag instructs get to stop here, after resolving, upgrading, +// and downgrading modules and updating go.mod. When using -m, +// each specified package path must be a module path as well, +// not the import path of a package below the module root. +// +// The -insecure flag permits fetching from repositories and resolving +// custom domains using insecure schemes such as HTTP. Use with caution. +// +// The second step is to download (if needed), build, and install +// the named packages. +// +// If an argument names a module but not a package (because there is no +// Go source code in the module's root directory), then the install step +// is skipped for that argument, instead of causing a build failure. +// For example 'go get golang.org/x/perf' succeeds even though there +// is no code corresponding to that import path. +// +// Note that package patterns are allowed and are expanded after resolving +// the module versions. For example, 'go get golang.org/x/perf/cmd/...' +// adds the latest golang.org/x/perf and then installs the commands in that +// latest version. +// +// The -d flag instructs get to download the source code needed to build +// the named packages, including downloading necessary dependencies, +// but not to build and install them. +// +// With no package arguments, 'go get' applies to the main module, +// and to the Go package in the current directory, if any. In particular, +// 'go get -u' and 'go get -u=patch' update all the dependencies of the +// main module. With no package arguments and also without -u, +// 'go get' is not much more than 'go install', and 'go get -d' not much +// more than 'go list'. +// +// For more about modules, see 'go help modules'. +// +// For more about specifying packages, see 'go help packages'. +// +// This text describes the behavior of get using modules to manage source +// code and dependencies. If instead the go command is running in GOPATH +// mode, the details of get's flags and effects change, as does 'go help get'. +// See 'go help modules' and 'go help gopath-get'. +// +// See also: go build, go install, go clean, go mod. +// +// +// Package lists and patterns // // Many commands apply to a set of packages: // @@ -1481,9 +2556,11 @@ // // - "main" denotes the top-level package in a stand-alone executable. // -// - "all" expands to all package directories found in all the GOPATH +// - "all" expands to all packages found in all the GOPATH // trees. For example, 'go list all' lists all the packages on the local -// system. +// system. When using modules, "all" expands to all packages in +// the main module and their dependencies, including dependencies +// needed by tests of any of those. // // - "std" is like all but expands to just the packages in the standard // Go library. @@ -1682,14 +2759,13 @@ // Writes test binary as -c would. // // -memprofile mem.out -// Write a memory profile to the file after all tests have passed. +// Write an allocation profile to the file after all tests have passed. // Writes test binary as -c would. // // -memprofilerate n -// Enable more precise (and expensive) memory profiles by setting -// runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'. -// To profile all memory allocations, use -test.memprofilerate=1 -// and pass --alloc_space flag to the pprof tool. +// Enable more precise (and expensive) memory allocation profiles by +// setting runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'. +// To profile all memory allocations, use -test.memprofilerate=1. // // -mutexprofile mutex.out // Write a mutex contention profile to the specified file @@ -1739,6 +2815,12 @@ // the package list would have to appear before -myflag, but could appear // on either side of -v. // +// When 'go test' runs in package list mode, 'go test' caches successful +// package test results to avoid unnecessary repeated running of tests. To +// disable test caching, use any test flag or argument other than the +// cacheable flags. The idiomatic way to disable test caching explicitly +// is to use -count=1. +// // To keep an argument for a test binary from being interpreted as a // known flag or a package name, use -args (see 'go help test') which // passes the remainder of the command line through to the test binary diff --git a/libgo/go/cmd/go/go_test.go b/libgo/go/cmd/go/go_test.go index 20606d8..e883388 100644 --- a/libgo/go/cmd/go/go_test.go +++ b/libgo/go/cmd/go/go_test.go @@ -8,12 +8,14 @@ import ( "bytes" "debug/elf" "debug/macho" + "flag" "fmt" "go/format" "internal/race" "internal/testenv" "io" "io/ioutil" + "log" "os" "os/exec" "path/filepath" @@ -48,7 +50,7 @@ func tooSlow(t *testing.T) { func init() { switch runtime.GOOS { - case "android", "nacl": + case "android", "js", "nacl": canRun = false case "darwin": switch runtime.GOARCH { @@ -99,6 +101,11 @@ func init() { var testGOROOT string var testCC string +var testGOCACHE string + +var testGo string +var testTmpDir string +var testBin string // The TestMain function creates a go command for testing purposes and // deletes it after the tests have been run. @@ -111,8 +118,28 @@ func TestMain(m *testing.M) { } os.Unsetenv("GOROOT_FINAL") + flag.Parse() + if *proxyAddr != "" { + StartProxy() + select {} + } + + dir, err := ioutil.TempDir(os.Getenv("GOTMPDIR"), "cmd-go-test-") + if err != nil { + log.Fatal(err) + } + testTmpDir = dir + if !*testWork { + defer removeAll(testTmpDir) + } + if canRun { - args := []string{"build", "-tags", "testgo", "-o", "testgo" + exeSuffix} + testBin = filepath.Join(testTmpDir, "testbin") + if err := os.Mkdir(testBin, 0777); err != nil { + log.Fatal(err) + } + testGo = filepath.Join(testBin, "go"+exeSuffix) + args := []string{"build", "-tags", "testgo", "-o", testGo} if race.Enabled { args = append(args, "-race") } @@ -165,7 +192,7 @@ func TestMain(m *testing.M) { } testCC = strings.TrimSpace(string(out)) - if out, err := exec.Command("./testgo"+exeSuffix, "env", "CGO_ENABLED").Output(); err != nil { + if out, err := exec.Command(testGo, "env", "CGO_ENABLED").Output(); err != nil { fmt.Fprintf(os.Stderr, "running testgo failed: %v\n", err) canRun = false } else { @@ -175,6 +202,13 @@ func TestMain(m *testing.M) { } } + out, err = exec.Command(gotool, "env", "GOCACHE").CombinedOutput() + if err != nil { + fmt.Fprintf(os.Stderr, "could not find testing GOCACHE: %v\n%s", err, out) + os.Exit(2) + } + testGOCACHE = strings.TrimSpace(string(out)) + // As of Sept 2017, MSan is only supported on linux/amd64. // https://github.com/google/sanitizers/wiki/MemorySanitizer#getting-memorysanitizer canMSan = canCgo && runtime.GOOS == "linux" && runtime.GOARCH == "amd64" @@ -190,21 +224,18 @@ func TestMain(m *testing.M) { os.Unsetenv("GOBIN") os.Unsetenv("GOPATH") os.Unsetenv("GIT_ALLOW_PROTOCOL") - if home, ccacheDir := os.Getenv("HOME"), os.Getenv("CCACHE_DIR"); home != "" && ccacheDir == "" { - // On some systems the default C compiler is ccache. - // Setting HOME to a non-existent directory will break - // those systems. Set CCACHE_DIR to cope. Issue 17668. - os.Setenv("CCACHE_DIR", filepath.Join(home, ".ccache")) - } os.Setenv("HOME", "/test-go-home-does-not-exist") + // On some systems the default C compiler is ccache. + // Setting HOME to a non-existent directory will break + // those systems. Disable ccache and use real compiler. Issue 17668. + os.Setenv("CCACHE_DISABLE", "1") if os.Getenv("GOCACHE") == "" { - os.Setenv("GOCACHE", "off") // because $HOME is gone + os.Setenv("GOCACHE", testGOCACHE) // because $HOME is gone } r := m.Run() - - if canRun { - os.Remove("testgo" + exeSuffix) + if !*testWork { + removeAll(testTmpDir) // os.Exit won't run defer } os.Exit(r) @@ -234,6 +265,7 @@ type testgoData struct { ran bool inParallel bool stdout, stderr bytes.Buffer + execDir string // dir for tg.run } // skipIfGccgo skips the test if using gccgo. @@ -352,10 +384,7 @@ func (tg *testgoData) unsetenv(name string) { } func (tg *testgoData) goTool() string { - if tg.wd == "" { - return "./testgo" + exeSuffix - } - return filepath.Join(tg.wd, "testgo"+exeSuffix) + return testGo } // doRun runs the test go command, recording stdout and stderr and @@ -389,6 +418,7 @@ func (tg *testgoData) doRun(args []string) error { cmd := exec.Command(prog, args...) tg.stdout.Reset() tg.stderr.Reset() + cmd.Dir = tg.execDir cmd.Stdout = &tg.stdout cmd.Stderr = &tg.stderr cmd.Env = tg.env @@ -409,7 +439,8 @@ func (tg *testgoData) doRun(args []string) error { func (tg *testgoData) run(args ...string) { tg.t.Helper() if status := tg.doRun(args); status != nil { - tg.t.Logf("go %v failed unexpectedly: %v", args, status) + wd, _ := os.Getwd() + tg.t.Logf("go %v failed unexpectedly in %s: %v", args, wd, status) tg.t.FailNow() } } @@ -735,7 +766,11 @@ func (tg *testgoData) wantStale(pkg, reason, msg string) { if !stale { tg.t.Fatal(msg) } - if reason == "" && why != "" || !strings.Contains(why, reason) { + // We always accept the reason as being "not installed but + // available in build cache", because when that is the case go + // list doesn't try to sort out the underlying reason why the + // package is not installed. + if reason == "" && why != "" || !strings.Contains(why, reason) && !strings.Contains(why, "not installed but available in build cache") { tg.t.Errorf("wrong reason for Stale=true: %q, want %q", why, reason) } } @@ -752,24 +787,51 @@ func (tg *testgoData) wantNotStale(pkg, reason, msg string) { } } +// If -testwork is specified, the test prints the name of the temp directory +// and does not remove it when done, so that a programmer can +// poke at the test file tree afterward. +var testWork = flag.Bool("testwork", false, "") + // cleanup cleans up a test that runs testgo. func (tg *testgoData) cleanup() { tg.t.Helper() if tg.wd != "" { + wd, _ := os.Getwd() + tg.t.Logf("ended in %s", wd) + if err := os.Chdir(tg.wd); err != nil { // We are unlikely to be able to continue. fmt.Fprintln(os.Stderr, "could not restore working directory, crashing:", err) os.Exit(2) } } + if *testWork { + tg.t.Logf("TESTWORK=%s\n", tg.path(".")) + return + } for _, path := range tg.temps { - tg.check(os.RemoveAll(path)) + tg.check(removeAll(path)) } if tg.tempdir != "" { - tg.check(os.RemoveAll(tg.tempdir)) + tg.check(removeAll(tg.tempdir)) } } +func removeAll(dir string) error { + // module cache has 0444 directories; + // make them writable in order to remove content. + filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil // ignore errors walking in file system + } + if info.IsDir() { + os.Chmod(path, 0777) + } + return nil + }) + return os.RemoveAll(dir) +} + // failSSH puts an ssh executable in the PATH that always fails. // This is to stub out uses of ssh by go get. func (tg *testgoData) failSSH() { @@ -782,92 +844,6 @@ func (tg *testgoData) failSSH() { tg.setenv("PATH", fmt.Sprintf("%v%c%v", fail, filepath.ListSeparator, os.Getenv("PATH"))) } -func TestBuildComplex(t *testing.T) { - // Simple smoke test for build configuration. - tg := testgo(t) - defer tg.cleanup() - tg.parallel() - tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) - tg.run("build", "-x", "-o", os.DevNull, "complex") - - if _, err := exec.LookPath("gccgo"); err == nil { - tg.run("build", "-x", "-o", os.DevNull, "-compiler=gccgo", "complex") - } -} - -func TestFileLineInErrorMessages(t *testing.T) { - tg := testgo(t) - defer tg.cleanup() - tg.parallel() - tg.tempFile("err.go", `package main; import "bar"`) - path := tg.path("err.go") - tg.runFail("run", path) - shortPath := path - if rel, err := filepath.Rel(tg.pwd(), path); err == nil && len(rel) < len(path) { - shortPath = rel - } - tg.grepStderr("^"+regexp.QuoteMeta(shortPath)+":", "missing file:line in error message") -} - -func TestProgramNameInCrashMessages(t *testing.T) { - skipIfGccgo(t, "gccgo does not use cmd/link") - tg := testgo(t) - defer tg.cleanup() - tg.parallel() - tg.tempFile("triv.go", `package main; func main() {}`) - tg.runFail("build", "-ldflags", "-crash_for_testing", tg.path("triv.go")) - tg.grepStderr(`[/\\]tool[/\\].*[/\\]link`, "missing linker name in error message") -} - -func TestBrokenTestsWithoutTestFunctionsAllFail(t *testing.T) { - tg := testgo(t) - defer tg.cleanup() - // TODO: tg.parallel() - tg.runFail("test", "./testdata/src/badtest/...") - tg.grepBothNot("^ok", "test passed unexpectedly") - tg.grepBoth("FAIL.*badtest/badexec", "test did not run everything") - tg.grepBoth("FAIL.*badtest/badsyntax", "test did not run everything") - tg.grepBoth("FAIL.*badtest/badvar", "test did not run everything") -} - -func TestGoBuildDashAInDevBranch(t *testing.T) { - if testing.Short() { - t.Skip("don't rebuild the standard library in short mode") - } - - tg := testgo(t) - defer tg.cleanup() - tg.run("install", "math") // should be up to date already but just in case - tg.setenv("TESTGO_IS_GO_RELEASE", "0") - tg.run("build", "-v", "-a", "math") - tg.grepStderr("runtime", "testgo build -a math in dev branch DID NOT build runtime, but should have") - - // Everything is out of date. Rebuild to leave things in a better state. - tg.run("install", "std") -} - -func TestGoBuildDashAInReleaseBranch(t *testing.T) { - if testing.Short() { - t.Skip("don't rebuild the standard library in short mode") - } - - tg := testgo(t) - defer tg.cleanup() - tg.run("install", "math", "net/http") // should be up to date already but just in case - tg.setenv("TESTGO_IS_GO_RELEASE", "1") - tg.run("install", "-v", "-a", "math") - tg.grepStderr("runtime", "testgo build -a math in release branch DID NOT build runtime, but should have") - - // Now runtime.a is updated (newer mtime), so everything would look stale if not for being a release. - tg.run("build", "-v", "net/http") - tg.grepStderrNot("strconv", "testgo build -v net/http in release branch with newer runtime.a DID build strconv but should not have") - tg.grepStderrNot("golang.org/x/net/http2/hpack", "testgo build -v net/http in release branch with newer runtime.a DID build .../golang.org/x/net/http2/hpack but should not have") - tg.grepStderrNot("net/http", "testgo build -v net/http in release branch with newer runtime.a DID build net/http but should not have") - - // Everything is out of date. Rebuild to leave things in a better state. - tg.run("install", "std") -} - func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) { if testing.Short() { t.Skip("don't rebuild the standard library in short mode") @@ -876,13 +852,13 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) { tg := testgo(t) defer tg.cleanup() - addNL := func(name string) (restore func()) { + addVar := func(name string, idx int) (restore func()) { data, err := ioutil.ReadFile(name) if err != nil { t.Fatal(err) } old := data - data = append(data, '\n') + data = append(data, fmt.Sprintf("var DummyUnusedVar%d bool\n", idx)...) if err := ioutil.WriteFile(name, append(data, '\n'), 0666); err != nil { t.Fatal(err) } @@ -894,9 +870,11 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) { } } - tg.tempFile("d1/src/p1/p1.go", `package p1`) + // Every main package depends on the "runtime". + tg.tempFile("d1/src/p1/p1.go", `package main; func main(){}`) tg.setenv("GOPATH", tg.path("d1")) - tg.run("install", "-a", "p1") + // Pass -i flag to rebuild everything outdated. + tg.run("install", "-i", "p1") tg.wantNotStale("p1", "", "./testgo list claims p1 is stale, incorrectly, before any changes") // Changing mtime of runtime/internal/sys/sys.go @@ -904,225 +882,33 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) { // In fact this should be true even outside a release branch. sys := runtime.GOROOT() + "/src/runtime/internal/sys/sys.go" tg.sleep() - restore := addNL(sys) + restore := addVar(sys, 0) restore() tg.wantNotStale("p1", "", "./testgo list claims p1 is stale, incorrectly, after updating mtime of runtime/internal/sys/sys.go") // But changing content of any file should have an effect. // Previously zversion.go was the only one that mattered; // now they all matter, so keep using sys.go. - restore = addNL(sys) + restore = addVar(sys, 1) defer restore() tg.wantStale("p1", "stale dependency: runtime/internal/sys", "./testgo list claims p1 is NOT stale, incorrectly, after changing sys.go") restore() tg.wantNotStale("p1", "", "./testgo list claims p1 is stale, incorrectly, after changing back to old release") - addNL(sys) + addVar(sys, 2) tg.wantStale("p1", "stale dependency: runtime/internal/sys", "./testgo list claims p1 is NOT stale, incorrectly, after changing sys.go again") - tg.run("install", "p1") + tg.run("install", "-i", "p1") tg.wantNotStale("p1", "", "./testgo list claims p1 is stale after building with new release") // Restore to "old" release. restore() tg.wantStale("p1", "stale dependency: runtime/internal/sys", "./testgo list claims p1 is NOT stale, incorrectly, after restoring sys.go") - tg.run("install", "p1") + tg.run("install", "-i", "p1") tg.wantNotStale("p1", "", "./testgo list claims p1 is stale after building with old release") // Everything is out of date. Rebuild to leave things in a better state. tg.run("install", "std") } -func TestGoListStandard(t *testing.T) { - skipIfGccgo(t, "gccgo does not GOROOT") - tooSlow(t) - tg := testgo(t) - defer tg.cleanup() - // TODO: tg.parallel() - tg.cd(runtime.GOROOT() + "/src") - tg.run("list", "-f", "{{if not .Standard}}{{.ImportPath}}{{end}}", "./...") - stdout := tg.getStdout() - for _, line := range strings.Split(stdout, "\n") { - if strings.HasPrefix(line, "_/") && strings.HasSuffix(line, "/src") { - // $GOROOT/src shows up if there are any .go files there. - // We don't care. - continue - } - if line == "" { - continue - } - t.Errorf("package in GOROOT not listed as standard: %v", line) - } - - // Similarly, expanding std should include some of our vendored code. - tg.run("list", "std", "cmd") - tg.grepStdout("golang.org/x/net/http2/hpack", "list std cmd did not mention vendored hpack") - tg.grepStdout("golang.org/x/arch/x86/x86asm", "list std cmd did not mention vendored x86asm") -} - -func TestGoInstallCleansUpAfterGoBuild(t *testing.T) { - tooSlow(t) - tg := testgo(t) - defer tg.cleanup() - // TODO: tg.parallel() - tg.tempFile("src/mycmd/main.go", `package main; func main(){}`) - tg.setenv("GOPATH", tg.path(".")) - tg.cd(tg.path("src/mycmd")) - - doesNotExist := func(file, msg string) { - if _, err := os.Stat(file); err == nil { - t.Fatal(msg) - } else if !os.IsNotExist(err) { - t.Fatal(msg, "error:", err) - } - } - - tg.run("build") - tg.wantExecutable("mycmd"+exeSuffix, "testgo build did not write command binary") - tg.run("install") - doesNotExist("mycmd"+exeSuffix, "testgo install did not remove command binary") - tg.run("build") - tg.wantExecutable("mycmd"+exeSuffix, "testgo build did not write command binary (second time)") - // Running install with arguments does not remove the target, - // even in the same directory. - tg.run("install", "mycmd") - tg.wantExecutable("mycmd"+exeSuffix, "testgo install mycmd removed command binary when run in mycmd") - tg.run("build") - tg.wantExecutable("mycmd"+exeSuffix, "testgo build did not write command binary (third time)") - // And especially not outside the directory. - tg.cd(tg.path(".")) - if data, err := ioutil.ReadFile("src/mycmd/mycmd" + exeSuffix); err != nil { - t.Fatal("could not read file:", err) - } else { - if err := ioutil.WriteFile("mycmd"+exeSuffix, data, 0555); err != nil { - t.Fatal("could not write file:", err) - } - } - tg.run("install", "mycmd") - tg.wantExecutable("src/mycmd/mycmd"+exeSuffix, "testgo install mycmd removed command binary from its source dir when run outside mycmd") - tg.wantExecutable("mycmd"+exeSuffix, "testgo install mycmd removed command binary from current dir when run outside mycmd") -} - -func TestGoInstallRebuildsStalePackagesInOtherGOPATH(t *testing.T) { - tooSlow(t) - tg := testgo(t) - defer tg.cleanup() - tg.parallel() - tg.tempFile("d1/src/p1/p1.go", `package p1 - import "p2" - func F() { p2.F() }`) - tg.tempFile("d2/src/p2/p2.go", `package p2 - func F() {}`) - sep := string(filepath.ListSeparator) - tg.setenv("GOPATH", tg.path("d1")+sep+tg.path("d2")) - tg.run("install", "-i", "p1") - tg.wantNotStale("p1", "", "./testgo list claims p1 is stale, incorrectly") - tg.wantNotStale("p2", "", "./testgo list claims p2 is stale, incorrectly") - tg.sleep() - if f, err := os.OpenFile(tg.path("d2/src/p2/p2.go"), os.O_WRONLY|os.O_APPEND, 0); err != nil { - t.Fatal(err) - } else if _, err = f.WriteString(`func G() {}`); err != nil { - t.Fatal(err) - } else { - tg.must(f.Close()) - } - tg.wantStale("p2", "build ID mismatch", "./testgo list claims p2 is NOT stale, incorrectly") - tg.wantStale("p1", "stale dependency: p2", "./testgo list claims p1 is NOT stale, incorrectly") - - tg.run("install", "-i", "p1") - tg.wantNotStale("p2", "", "./testgo list claims p2 is stale after reinstall, incorrectly") - tg.wantNotStale("p1", "", "./testgo list claims p1 is stale after reinstall, incorrectly") -} - -func TestGoInstallDetectsRemovedFiles(t *testing.T) { - tg := testgo(t) - defer tg.cleanup() - tg.parallel() - tg.tempFile("src/mypkg/x.go", `package mypkg`) - tg.tempFile("src/mypkg/y.go", `package mypkg`) - tg.tempFile("src/mypkg/z.go", `// +build missingtag - - package mypkg`) - tg.setenv("GOPATH", tg.path(".")) - tg.run("install", "mypkg") - tg.wantNotStale("mypkg", "", "./testgo list mypkg claims mypkg is stale, incorrectly") - // z.go was not part of the build; removing it is okay. - tg.must(os.Remove(tg.path("src/mypkg/z.go"))) - tg.wantNotStale("mypkg", "", "./testgo list mypkg claims mypkg is stale after removing z.go; should not be stale") - // y.go was part of the package; removing it should be detected. - tg.must(os.Remove(tg.path("src/mypkg/y.go"))) - tg.wantStale("mypkg", "build ID mismatch", "./testgo list mypkg claims mypkg is NOT stale after removing y.go; should be stale") -} - -func TestWildcardMatchesSyntaxErrorDirs(t *testing.T) { - tg := testgo(t) - defer tg.cleanup() - // TODO: tg.parallel() - tg.tempFile("src/mypkg/x.go", `package mypkg`) - tg.tempFile("src/mypkg/y.go", `pkg mypackage`) - tg.setenv("GOPATH", tg.path(".")) - tg.cd(tg.path("src/mypkg")) - tg.runFail("list", "./...") - tg.runFail("build", "./...") - tg.runFail("install", "./...") -} - -func TestGoListWithTags(t *testing.T) { - tg := testgo(t) - defer tg.cleanup() - tg.tempFile("src/mypkg/x.go", "// +build thetag\n\npackage mypkg\n") - tg.setenv("GOPATH", tg.path(".")) - tg.cd(tg.path("./src")) - tg.run("list", "-tags=thetag", "./my...") - tg.grepStdout("mypkg", "did not find mypkg") -} - -func TestGoInstallErrorOnCrossCompileToBin(t *testing.T) { - if testing.Short() { - t.Skip("don't install into GOROOT in short mode") - } - - tg := testgo(t) - defer tg.cleanup() - tg.tempFile("src/mycmd/x.go", `package main - func main() {}`) - tg.setenv("GOPATH", tg.path(".")) - tg.cd(tg.path("src/mycmd")) - - tg.run("build", "mycmd") - - goarch := "386" - if runtime.GOARCH == "386" { - goarch = "amd64" - } - tg.setenv("GOOS", "linux") - tg.setenv("GOARCH", goarch) - tg.run("install", "mycmd") - tg.setenv("GOBIN", tg.path(".")) - tg.runFail("install", "mycmd") - tg.run("install", "cmd/pack") -} - -func TestGoInstallDetectsRemovedFilesInPackageMain(t *testing.T) { - tooSlow(t) - tg := testgo(t) - defer tg.cleanup() - tg.parallel() - tg.tempFile("src/mycmd/x.go", `package main - func main() {}`) - tg.tempFile("src/mycmd/y.go", `package main`) - tg.tempFile("src/mycmd/z.go", `// +build missingtag - - package main`) - tg.setenv("GOPATH", tg.path(".")) - tg.run("install", "mycmd") - tg.wantNotStale("mycmd", "", "./testgo list mypkg claims mycmd is stale, incorrectly") - // z.go was not part of the build; removing it is okay. - tg.must(os.Remove(tg.path("src/mycmd/z.go"))) - tg.wantNotStale("mycmd", "", "./testgo list mycmd claims mycmd is stale after removing z.go; should not be stale") - // y.go was part of the package; removing it should be detected. - tg.must(os.Remove(tg.path("src/mycmd/y.go"))) - tg.wantStale("mycmd", "build ID mismatch", "./testgo list mycmd claims mycmd is NOT stale after removing y.go; should be stale") -} - func testLocalRun(tg *testgoData, exepath, local, match string) { tg.t.Helper() out, err := exec.Command(exepath).Output() @@ -1250,14 +1036,14 @@ func TestInternalPackagesInGOROOTAreRespected(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.runFail("build", "-v", "./testdata/testinternal") - tg.grepBoth(`testinternal(\/|\\)p\.go\:3\:8\: use of internal package not allowed`, "wrong error message for testdata/testinternal") + tg.grepBoth(`testinternal(\/|\\)p\.go\:3\:8\: use of internal package net/http/internal not allowed`, "wrong error message for testdata/testinternal") } func TestInternalPackagesOutsideGOROOTAreRespected(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.runFail("build", "-v", "./testdata/testinternal2") - tg.grepBoth(`testinternal2(\/|\\)p\.go\:3\:8\: use of internal package not allowed`, "wrote error message for testdata/testinternal2") + tg.grepBoth(`testinternal2(\/|\\)p\.go\:3\:8\: use of internal package .*internal/w not allowed`, "wrote error message for testdata/testinternal2") } func TestRunInternal(t *testing.T) { @@ -1267,7 +1053,19 @@ func TestRunInternal(t *testing.T) { tg.setenv("GOPATH", dir) tg.run("run", filepath.Join(dir, "src/run/good.go")) tg.runFail("run", filepath.Join(dir, "src/run/bad.go")) - tg.grepStderr(`testdata(\/|\\)src(\/|\\)run(\/|\\)bad\.go\:3\:8\: use of internal package not allowed`, "unexpected error for run/bad.go") + tg.grepStderr(`testdata(\/|\\)src(\/|\\)run(\/|\\)bad\.go\:3\:8\: use of internal package run/subdir/internal/private not allowed`, "unexpected error for run/bad.go") +} + +func TestRunPkg(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + dir := filepath.Join(tg.pwd(), "testdata") + tg.setenv("GOPATH", dir) + tg.run("run", "hello") + tg.grepStderr("hello, world", "did not find hello, world") + tg.cd(filepath.Join(dir, "src/hello")) + tg.run("run", ".") + tg.grepStderr("hello, world", "did not find hello, world") } func testMove(t *testing.T, vcs, url, base, config string) { @@ -1368,6 +1166,41 @@ func TestImportCommentConflict(t *testing.T) { tg.grepStderr("found import comments", "go build did not mention comment conflict") } +func TestImportCycle(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/importcycle")) + tg.runFail("build", "selfimport") + + count := tg.grepCountBoth("import cycle not allowed") + if count == 0 { + t.Fatal("go build did not mention cyclical import") + } + if count > 1 { + t.Fatal("go build mentioned import cycle more than once") + } + + // Don't hang forever. + tg.run("list", "-e", "-json", "selfimport") +} + +func TestListImportMap(t *testing.T) { + skipIfGccgo(t, "gccgo does not have standard packages") + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.run("list", "-f", "{{.ImportPath}}: {{.ImportMap}}", "net", "fmt") + tg.grepStdout(`^net: map\[(.* )?golang_org/x/net/dns/dnsmessage:vendor/golang_org/x/net/dns/dnsmessage.*\]`, "net/http should have rewritten dnsmessage import") + tg.grepStdout(`^fmt: map\[\]`, "fmt should have no rewritten imports") + tg.run("list", "-deps", "-test", "-f", "{{.ImportPath}} MAP: {{.ImportMap}}\n{{.ImportPath}} IMPORT: {{.Imports}}", "fmt") + tg.grepStdout(`^flag \[fmt\.test\] MAP: map\[fmt:fmt \[fmt\.test\]\]`, "flag [fmt.test] should import fmt [fmt.test] as fmt") + tg.grepStdout(`^fmt\.test MAP: map\[(.* )?testing:testing \[fmt\.test\]`, "fmt.test should import testing [fmt.test] as testing") + tg.grepStdout(`^fmt\.test MAP: map\[(.* )?testing:testing \[fmt\.test\]`, "fmt.test should import testing [fmt.test] as testing") + tg.grepStdoutNot(`^fmt\.test MAP: map\[(.* )?os:`, "fmt.test should not import a modified os") + tg.grepStdout(`^fmt\.test IMPORT: \[fmt \[fmt\.test\] fmt_test \[fmt\.test\] os testing \[fmt\.test\] testing/internal/testdeps \[fmt\.test\]\]`, "wrong imports for fmt.test") +} + // cmd/go: custom import path checking should not apply to Go packages without import comment. func TestIssue10952(t *testing.T) { testenv.MustHaveExternalNetwork(t) @@ -1459,6 +1292,7 @@ func TestGetGitDefaultBranch(t *testing.T) { tg.grepStdout(`\* another-branch`, "not on correct default branch") } +// Security issue. Don't disable. See golang.org/issue/22125. func TestAccidentalGitCheckout(t *testing.T) { testenv.MustHaveExternalNetwork(t) if _, err := exec.LookPath("git"); err != nil { @@ -1469,13 +1303,17 @@ func TestAccidentalGitCheckout(t *testing.T) { defer tg.cleanup() tg.parallel() tg.tempDir("src") + tg.setenv("GOPATH", tg.path(".")) tg.runFail("get", "-u", "vcs-test.golang.org/go/test1-svn-git") tg.grepStderr("src[\\\\/]vcs-test.* uses git, but parent .*src[\\\\/]vcs-test.* uses svn", "get did not fail for right reason") - tg.runFail("get", "-u", "vcs-test.golang.org/go/test2-svn-git/test2main") - tg.grepStderr("src[\\\\/]vcs-test.* uses git, but parent .*src[\\\\/]vcs-test.* uses svn", "get did not fail for right reason") + if _, err := os.Stat(tg.path("SrC")); err == nil { + // This case only triggers on a case-insensitive file system. + tg.runFail("get", "-u", "vcs-test.golang.org/go/test2-svn-git/test2main") + tg.grepStderr("src[\\\\/]vcs-test.* uses git, but parent .*src[\\\\/]vcs-test.* uses svn", "get did not fail for right reason") + } } func TestErrorMessageForSyntaxErrorInTestGoFileSaysFAIL(t *testing.T) { @@ -1483,7 +1321,8 @@ func TestErrorMessageForSyntaxErrorInTestGoFileSaysFAIL(t *testing.T) { defer tg.cleanup() tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) tg.runFail("test", "syntaxerror") - tg.grepStderr("FAIL", "go test did not say FAIL") + tg.grepStderr("x_test.go:", "did not diagnose error") + tg.grepStdout("FAIL", "go test did not say FAIL") } func TestWildcardsDoNotLookInUselessDirectories(t *testing.T) { @@ -1580,6 +1419,7 @@ func TestRelativeGOBINFail(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.tempFile("triv.go", `package main; func main() {}`) + tg.cd(tg.path(".")) tg.setenv("GOBIN", ".") tg.runFail("install") tg.grepStderr("cannot install, GOBIN must be an absolute path", "go install must fail if $GOBIN is a relative path") @@ -1634,12 +1474,6 @@ func TestPackageMainTestCompilerFlags(t *testing.T) { tg.grepStderr(`([\\/]compile|gccgo).* (-p p1|-fgo-pkgpath=p1).*p1\.go`, "should have run compile -p p1 p1.go") } -// The runtime version string takes one of two forms: -// "go1.X[.Y]" for Go releases, and "devel +hash" at tip. -// Determine whether we are in a released copy by -// inspecting the version. -var isGoRelease = strings.HasPrefix(runtime.Version(), "go1") - // Issue 12690 func TestPackageNotStaleWithTrailingSlash(t *testing.T) { skipIfGccgo(t, "gccgo does not have GOROOT") @@ -1727,8 +1561,8 @@ func TestGoGetTestOnlyPkg(t *testing.T) { defer tg.cleanup() tg.tempDir("gopath") tg.setenv("GOPATH", tg.path("gopath")) - tg.run("get", "golang.org/x/tour/content") - tg.run("get", "-t", "golang.org/x/tour/content") + tg.run("get", "golang.org/x/tour/content...") + tg.run("get", "-t", "golang.org/x/tour/content...") } func TestInstalls(t *testing.T) { @@ -1854,7 +1688,7 @@ func TestGoListStdDoesNotIncludeCommands(t *testing.T) { } func TestGoListCmdOnlyShowsCommands(t *testing.T) { - skipIfGccgo(t, "gccgo has no GOROOT") + skipIfGccgo(t, "gccgo does not have GOROOT") tooSlow(t) tg := testgo(t) defer tg.cleanup() @@ -1894,6 +1728,118 @@ func TestGoListDeps(t *testing.T) { tg.tempFile("src/p1/p2/p3/p4/p.go", "package p4\n") tg.run("list", "-f", "{{.Deps}}", "p1") tg.grepStdout("p1/p2/p3/p4", "Deps(p1) does not mention p4") + + tg.run("list", "-deps", "p1") + tg.grepStdout("p1/p2/p3/p4", "-deps p1 does not mention p4") + + if runtime.Compiler != "gccgo" { + // Check the list is in dependency order. + tg.run("list", "-deps", "math") + want := "internal/cpu\nunsafe\nmath\n" + out := tg.stdout.String() + if !strings.Contains(out, "internal/cpu") { + // Some systems don't use internal/cpu. + want = "unsafe\nmath\n" + } + if tg.stdout.String() != want { + t.Fatalf("list -deps math: wrong order\nhave %q\nwant %q", tg.stdout.String(), want) + } + } +} + +func TestGoListTest(t *testing.T) { + skipIfGccgo(t, "gccgo does not have standard packages") + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.makeTempdir() + tg.setenv("GOCACHE", tg.tempdir) + + tg.run("list", "-test", "-deps", "sort") + tg.grepStdout(`^sort.test$`, "missing test main") + tg.grepStdout(`^sort$`, "missing real sort") + tg.grepStdout(`^sort \[sort.test\]$`, "missing test copy of sort") + tg.grepStdout(`^testing \[sort.test\]$`, "missing test copy of testing") + tg.grepStdoutNot(`^testing$`, "unexpected real copy of testing") + + tg.run("list", "-test", "sort") + tg.grepStdout(`^sort.test$`, "missing test main") + tg.grepStdout(`^sort$`, "missing real sort") + tg.grepStdout(`^sort \[sort.test\]$`, "unexpected test copy of sort") + tg.grepStdoutNot(`^testing \[sort.test\]$`, "unexpected test copy of testing") + tg.grepStdoutNot(`^testing$`, "unexpected real copy of testing") + + tg.run("list", "-test", "cmd/dist", "cmd/doc") + tg.grepStdout(`^cmd/dist$`, "missing cmd/dist") + tg.grepStdout(`^cmd/doc$`, "missing cmd/doc") + tg.grepStdout(`^cmd/doc\.test$`, "missing cmd/doc test") + tg.grepStdoutNot(`^cmd/dist\.test$`, "unexpected cmd/dist test") + tg.grepStdoutNot(`^testing`, "unexpected testing") + + tg.run("list", "-test", "runtime/cgo") + tg.grepStdout(`^runtime/cgo$`, "missing runtime/cgo") + + tg.run("list", "-deps", "-f", "{{if .DepOnly}}{{.ImportPath}}{{end}}", "sort") + tg.grepStdout(`^reflect$`, "missing reflect") + tg.grepStdoutNot(`^sort`, "unexpected sort") +} + +func TestGoListCompiledCgo(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.makeTempdir() + tg.setenv("GOCACHE", tg.tempdir) + + tg.run("list", "-f", `{{join .CgoFiles "\n"}}`, "net") + if tg.stdout.String() == "" { + t.Skip("net does not use cgo") + } + if strings.Contains(tg.stdout.String(), tg.tempdir) { + t.Fatalf(".CgoFiles unexpectedly mentioned cache %s", tg.tempdir) + } + tg.run("list", "-compiled", "-f", `{{.Dir}}{{"\n"}}{{join .CompiledGoFiles "\n"}}`, "net") + if !strings.Contains(tg.stdout.String(), tg.tempdir) { + t.Fatalf(".CompiledGoFiles with -compiled did not mention cache %s", tg.tempdir) + } + dir := "" + for _, file := range strings.Split(tg.stdout.String(), "\n") { + if file == "" { + continue + } + if dir == "" { + dir = file + continue + } + if !strings.Contains(file, "/") && !strings.Contains(file, `\`) { + file = filepath.Join(dir, file) + } + if _, err := os.Stat(file); err != nil { + t.Fatalf("cannot find .CompiledGoFiles result %s: %v", file, err) + } + } +} + +func TestGoListExport(t *testing.T) { + skipIfGccgo(t, "gccgo does not have standard packages") + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.makeTempdir() + tg.setenv("GOCACHE", tg.tempdir) + + tg.run("list", "-f", "{{.Export}}", "strings") + if tg.stdout.String() != "" { + t.Fatalf(".Export without -export unexpectedly set") + } + tg.run("list", "-export", "-f", "{{.Export}}", "strings") + file := strings.TrimSpace(tg.stdout.String()) + if file == "" { + t.Fatalf(".Export with -export was empty") + } + if _, err := os.Stat(file); err != nil { + t.Fatalf("cannot find .Export result %s: %v", file, err) + } } // Issue 4096. Validate the output of unsuccessful go install foo/quxx. @@ -1966,6 +1912,17 @@ func homeEnvName() string { } } +func tempEnvName() string { + switch runtime.GOOS { + case "windows": + return "TMP" + case "plan9": + return "TMPDIR" // actually plan 9 doesn't have one at all but this is fine + default: + return "TMPDIR" + } +} + func TestDefaultGOPATH(t *testing.T) { tg := testgo(t) defer tg.cleanup() @@ -2635,6 +2592,18 @@ func TestCoverageFunc(t *testing.T) { tg.grepStdoutNot(`\tf\t*[0-9]`, "reported coverage for assembly function f") } +// Issue 24588. +func TestCoverageDashC(t *testing.T) { + skipIfGccgo(t, "gccgo has no cover tool") + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.makeTempdir() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.run("test", "-c", "-o", tg.path("coverdep"), "-coverprofile="+tg.path("no/such/dir/cover.out"), "coverdep") + tg.wantExecutable(tg.path("coverdep"), "go -test -c -coverprofile did not create executable") +} + func TestPluginNonMain(t *testing.T) { wd, err := os.Getwd() if err != nil { @@ -2845,7 +2814,7 @@ func TestCgoPkgConfig(t *testing.T) { // OpenBSD's pkg-config is strict about whitespace and only // supports backslash-escaped whitespace. It does not support // quotes, which the normal freedesktop.org pkg-config does - // support. See http://man.openbsd.org/pkg-config.1 + // support. See https://man.openbsd.org/pkg-config.1 tg.tempFile("foo.pc", ` Name: foo Description: The foo library @@ -3048,11 +3017,25 @@ func TestBuildDashIInstallsDependencies(t *testing.T) { checkbar("cmd") } -func TestGoBuildInTestOnlyDirectoryFailsWithAGoodError(t *testing.T) { +func TestGoBuildTestOnly(t *testing.T) { tg := testgo(t) defer tg.cleanup() - tg.runFail("build", "./testdata/testonly") - tg.grepStderr("no non-test Go files in", "go build ./testdata/testonly produced unexpected error") + tg.makeTempdir() + tg.setenv("GOPATH", tg.path(".")) + tg.tempFile("src/testonly/t_test.go", `package testonly`) + tg.tempFile("src/testonly2/t.go", `package testonly2`) + tg.cd(tg.path("src")) + + // Named explicitly, test-only packages should be reported as unbuildable/uninstallable, + // even if there is a wildcard also matching. + tg.runFail("build", "testonly", "testonly...") + tg.grepStderr("no non-test Go files in", "go build ./xtestonly produced unexpected error") + tg.runFail("install", "./testonly") + tg.grepStderr("no non-test Go files in", "go install ./testonly produced unexpected error") + + // Named through a wildcards, the test-only packages should be silently ignored. + tg.run("build", "testonly...") + tg.run("install", "./testonly...") } func TestGoTestDetectsTestOnlyImportCycles(t *testing.T) { @@ -3088,6 +3071,9 @@ func TestGoTestMainAsNormalTest(t *testing.T) { } func TestGoTestMainTwice(t *testing.T) { + if testing.Short() { + t.Skip("Skipping in short mode") + } tg := testgo(t) defer tg.cleanup() tg.makeTempdir() @@ -3182,6 +3168,22 @@ func TestGoGenerateEnv(t *testing.T) { } } +func TestGoGenerateXTestPkgName(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("skipping because windows has no echo command") + } + + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.tempFile("env_test.go", "package main_test\n\n//go:generate echo $GOPACKAGE") + tg.run("generate", tg.path("env_test.go")) + want := "main_test" + if got := strings.TrimSpace(tg.getStdout()); got != want { + t.Errorf("go generate in XTest file got package name %q; want %q", got, want) + } +} + func TestGoGenerateBadImports(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("skipping because windows has no echo command") @@ -3549,24 +3551,43 @@ func TestImportLocal(t *testing.T) { } func TestGoGetInsecure(t *testing.T) { - testenv.MustHaveExternalNetwork(t) + test := func(t *testing.T, modules bool) { + testenv.MustHaveExternalNetwork(t) + + tg := testgo(t) + defer tg.cleanup() + tg.makeTempdir() + tg.failSSH() + + if modules { + tg.setenv("GOPATH", tg.path("gp")) + tg.tempFile("go.mod", "module m") + tg.cd(tg.path(".")) + tg.setenv("GO111MODULE", "on") + } else { + tg.setenv("GOPATH", tg.path(".")) + tg.setenv("GO111MODULE", "off") + } - tg := testgo(t) - defer tg.cleanup() - tg.makeTempdir() - tg.setenv("GOPATH", tg.path(".")) - tg.failSSH() + const repo = "insecure.go-get-issue-15410.appspot.com/pkg/p" - const repo = "insecure.go-get-issue-15410.appspot.com/pkg/p" + // Try go get -d of HTTP-only repo (should fail). + tg.runFail("get", "-d", repo) - // Try go get -d of HTTP-only repo (should fail). - tg.runFail("get", "-d", repo) + // Try again with -insecure (should succeed). + tg.run("get", "-d", "-insecure", repo) - // Try again with -insecure (should succeed). - tg.run("get", "-d", "-insecure", repo) + // Try updating without -insecure (should fail). + tg.runFail("get", "-d", "-u", "-f", repo) + + if modules { + tg.run("list", "-m", "...") + tg.grepStdout("insecure.go-get-issue", "should find insecure module") + } + } - // Try updating without -insecure (should fail). - tg.runFail("get", "-d", "-u", "-f", repo) + t.Run("gopath", func(t *testing.T) { test(t, false) }) + t.Run("modules", func(t *testing.T) { test(t, true) }) } func TestGoGetUpdateInsecure(t *testing.T) { @@ -3593,6 +3614,40 @@ func TestGoGetUpdateInsecure(t *testing.T) { tg.run("get", "-d", "-u", "-f", "-insecure", pkg) } +func TestGoGetUpdateUnknownProtocol(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + tg := testgo(t) + defer tg.cleanup() + tg.makeTempdir() + tg.setenv("GOPATH", tg.path(".")) + + const repo = "github.com/golang/example" + + // Clone the repo via HTTPS manually. + repoDir := tg.path("src/" + repo) + cmd := exec.Command("git", "clone", "-q", "https://"+repo, repoDir) + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("cloning %v repo: %v\n%s", repo, err, out) + } + + // Configure the repo to use a protocol unknown to cmd/go + // that still actually works. + cmd = exec.Command("git", "remote", "set-url", "origin", "xyz://"+repo) + cmd.Dir = repoDir + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("git remote set-url: %v\n%s", err, out) + } + cmd = exec.Command("git", "config", "--local", "url.https://github.com/.insteadOf", "xyz://github.com/") + cmd.Dir = repoDir + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("git config: %v\n%s", err, out) + } + + // We need -f to ignore import comments. + tg.run("get", "-d", "-u", "-f", repo+"/hello") +} + func TestGoGetInsecureCustomDomain(t *testing.T) { testenv.MustHaveExternalNetwork(t) @@ -4175,9 +4230,10 @@ func TestGoGetUpdateWithWildcard(t *testing.T) { tg.setenv("GOPATH", tg.path(".")) const aPkgImportPath = "github.com/tmwh/go-get-issue-14450/a" tg.run("get", aPkgImportPath) - tg.run("get", "-u", ".../") - tg.grepStderrNot("cannot find package", "did not update packages given wildcard path") + tg.runFail("get", "-u", ".../") + tg.grepStderr("cannot find package.*d-dependency/e", "should have detected e missing") + // Even though get -u failed, the source for others should be downloaded. var expectedPkgPaths = []string{ "src/github.com/tmwh/go-get-issue-14450/b", "src/github.com/tmwh/go-get-issue-14450-b-dependency/c", @@ -4754,7 +4810,7 @@ func main() {}`) before() tg.run("install", "mycmd") after() - tg.wantStale("mycmd", "stale dependency: runtime/internal/sys", "should be stale after environment variable change") + tg.wantStale("mycmd", "stale dependency", "should be stale after environment variable change") } } @@ -4795,34 +4851,34 @@ func TestTestRegexps(t *testing.T) { // BenchmarkX/Y is run in full, twice want := `=== RUN TestX === RUN TestX/Y - x_test.go:6: LOG: X running - x_test.go:8: LOG: Y running + x_test.go:6: LOG: X running + x_test.go:8: LOG: Y running === RUN TestXX - z_test.go:10: LOG: XX running + z_test.go:10: LOG: XX running === RUN TestX === RUN TestX/Y - x_test.go:6: LOG: X running - x_test.go:8: LOG: Y running + x_test.go:6: LOG: X running + x_test.go:8: LOG: Y running === RUN TestXX - z_test.go:10: LOG: XX running + z_test.go:10: LOG: XX running --- BENCH: BenchmarkX/Y - x_test.go:15: LOG: Y running N=1 - x_test.go:15: LOG: Y running N=100 - x_test.go:15: LOG: Y running N=10000 - x_test.go:15: LOG: Y running N=1000000 - x_test.go:15: LOG: Y running N=100000000 - x_test.go:15: LOG: Y running N=2000000000 + x_test.go:15: LOG: Y running N=1 + x_test.go:15: LOG: Y running N=100 + x_test.go:15: LOG: Y running N=10000 + x_test.go:15: LOG: Y running N=1000000 + x_test.go:15: LOG: Y running N=100000000 + x_test.go:15: LOG: Y running N=2000000000 --- BENCH: BenchmarkX/Y - x_test.go:15: LOG: Y running N=1 - x_test.go:15: LOG: Y running N=100 - x_test.go:15: LOG: Y running N=10000 - x_test.go:15: LOG: Y running N=1000000 - x_test.go:15: LOG: Y running N=100000000 - x_test.go:15: LOG: Y running N=2000000000 + x_test.go:15: LOG: Y running N=1 + x_test.go:15: LOG: Y running N=100 + x_test.go:15: LOG: Y running N=10000 + x_test.go:15: LOG: Y running N=1000000 + x_test.go:15: LOG: Y running N=100000000 + x_test.go:15: LOG: Y running N=2000000000 --- BENCH: BenchmarkX - x_test.go:13: LOG: X running N=1 + x_test.go:13: LOG: X running N=1 --- BENCH: BenchmarkXX - z_test.go:18: LOG: XX running N=1 + z_test.go:18: LOG: XX running N=1 ` have := strings.Join(lines, "") @@ -4857,7 +4913,8 @@ func TestBuildmodePIE(t *testing.T) { platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) switch platform { case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", - "android/amd64", "android/arm", "android/arm64", "android/386": + "android/amd64", "android/arm", "android/arm64", "android/386", + "freebsd/amd64": case "darwin/amd64": default: t.Skipf("skipping test because buildmode=pie is not supported on %s", platform) @@ -4872,7 +4929,7 @@ func TestBuildmodePIE(t *testing.T) { tg.run("build", "-buildmode=pie", "-o", obj, src) switch runtime.GOOS { - case "linux", "android": + case "linux", "android", "freebsd": f, err := elf.Open(obj) if err != nil { t.Fatal(err) @@ -4983,7 +5040,8 @@ func TestWrongGOOSErrorBeforeLoadError(t *testing.T) { } func TestUpxCompression(t *testing.T) { - if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + if runtime.GOOS != "linux" || + (runtime.GOARCH != "amd64" && runtime.GOARCH != "386") { t.Skipf("skipping upx test on %s/%s", runtime.GOOS, runtime.GOARCH) } @@ -5036,79 +5094,47 @@ func TestUpxCompression(t *testing.T) { } } -func TestGOTMPDIR(t *testing.T) { - tg := testgo(t) - defer tg.cleanup() - tg.parallel() - tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) - tg.makeTempdir() - tg.setenv("GOTMPDIR", tg.tempdir) - tg.setenv("GOCACHE", "off") - - // complex/x is a trivial non-main package. - tg.run("build", "-work", "-x", "complex/w") - tg.grepStderr("WORK="+regexp.QuoteMeta(tg.tempdir), "did not work in $GOTMPDIR") -} - -func TestBuildCache(t *testing.T) { - tooSlow(t) - if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { - t.Skip("GODEBUG gocacheverify") +// Test that Go binaries can be run under QEMU in user-emulation mode +// (See issue #13024). +func TestQEMUUserMode(t *testing.T) { + if testing.Short() && testenv.Builder() == "" { + t.Skipf("skipping in -short mode on non-builder") } - tg := testgo(t) - defer tg.cleanup() - tg.parallel() - tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) - tg.makeTempdir() - tg.setenv("GOCACHE", tg.tempdir) - - // complex/w is a trivial non-main package. - // It imports nothing, so there should be no Deps. - tg.run("list", "-f={{join .Deps \" \"}}", "complex/w") - tg.grepStdoutNot(".+", "complex/w depends on unexpected packages") - - tg.run("build", "-x", "complex/w") - tg.grepStderr(`[\\/]compile|gccgo`, "did not run compiler") - - tg.run("build", "-x", "complex/w") - tg.grepStderrNot(`[\\/]compile|gccgo`, "ran compiler incorrectly") - - tg.run("build", "-a", "-x", "complex/w") - tg.grepStderr(`[\\/]compile|gccgo`, "did not run compiler with -a") - - // complex is a non-trivial main package. - // the link step should not be cached. - tg.run("build", "-o", os.DevNull, "-x", "complex") - tg.grepStderr(`[\\/]link|gccgo`, "did not run linker") - tg.run("build", "-o", os.DevNull, "-x", "complex") - tg.grepStderr(`[\\/]link|gccgo`, "did not run linker") -} - -func TestCacheOutput(t *testing.T) { - // Test that command output is cached and replayed too. - if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { - t.Skip("GODEBUG gocacheverify") + testArchs := []struct { + g, qemu string + }{ + {"arm", "arm"}, + {"arm64", "aarch64"}, } + tg := testgo(t) defer tg.cleanup() + tg.tempFile("main.go", `package main; import "fmt"; func main() { fmt.Print("hello qemu-user") }`) tg.parallel() - tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) - tg.makeTempdir() - tg.setenv("GOCACHE", tg.tempdir) + src, obj := tg.path("main.go"), tg.path("main") - tg.run("build", "-gcflags=-m", "errors") - stdout1 := tg.getStdout() - stderr1 := tg.getStderr() + for _, arch := range testArchs { + out, err := exec.Command("qemu-"+arch.qemu, "--version").CombinedOutput() + if err != nil { + t.Logf("Skipping %s test (qemu-%s not available)", arch.g, arch.qemu) + continue + } - tg.run("build", "-gcflags=-m", "errors") - stdout2 := tg.getStdout() - stderr2 := tg.getStderr() + tg.setenv("GOARCH", arch.g) + tg.run("build", "-o", obj, src) - if stdout2 != stdout1 || stderr2 != stderr1 { - t.Errorf("cache did not reproduce output:\n\nstdout1:\n%s\n\nstdout2:\n%s\n\nstderr1:\n%s\n\nstderr2:\n%s", - stdout1, stdout2, stderr1, stderr2) + out, err = exec.Command("qemu-"+arch.qemu, obj).CombinedOutput() + if err != nil { + t.Logf("qemu-%s output:\n%s\n", arch.qemu, out) + t.Errorf("qemu-%s failed with %v", arch.qemu, err) + continue + } + if want := "hello qemu-user"; string(out) != want { + t.Errorf("bad output from qemu-%s:\ngot %s; want %s", arch.qemu, out, want) + } } + } func TestCacheListStale(t *testing.T) { @@ -5151,6 +5177,32 @@ func TestCacheCoverage(t *testing.T) { tg.run("test", "-cover", "-short", "math", "strings") } +func TestCacheVet(t *testing.T) { + skipIfGccgo(t, "gccgo has no standard packages") + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + + if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { + t.Skip("GODEBUG gocacheverify") + } + if os.Getenv("GOCACHE") == "off" { + tooSlow(t) + tg.makeTempdir() + tg.setenv("GOCACHE", tg.path("cache")) + } + + // Check that second vet reuses cgo-derived inputs. + // The first command could be build instead of vet, + // except that if the cache is empty and there's a net.a + // in GOROOT/pkg, the build will not bother to regenerate + // and cache the cgo outputs, whereas vet always will. + tg.run("vet", "os/user") + tg.run("vet", "-x", "os/user") + tg.grepStderrNot(`^(clang|gcc)`, "should not have run compiler") + tg.grepStderrNot(`[\\/]cgo `, "should not have run cgo") +} + func TestIssue22588(t *testing.T) { // Don't get confused by stderr coming from tools. tg := testgo(t) @@ -5241,14 +5293,14 @@ func TestTestCache(t *testing.T) { // timeout here should not affect result being cached // or being retrieved later. tg.run("test", "-x", "-timeout=10s", "errors") - tg.grepStderr(`[\\/](compile|gccgo) `, "did not run compiler") - tg.grepStderr(`[\\/](link|gccgo) `, "did not run linker") + tg.grepStderr(`[\\/]compile|gccgo`, "did not run compiler") + tg.grepStderr(`[\\/]link|gccgo`, "did not run linker") tg.grepStderr(`errors\.test`, "did not run test") tg.run("test", "-x", "errors") tg.grepStdout(`ok \terrors\t\(cached\)`, "did not report cached result") - tg.grepStderrNot(`[\\/](compile|gccgo) `, "incorrectly ran compiler") - tg.grepStderrNot(`[\\/](link|gccgo) `, "incorrectly ran linker") + tg.grepStderrNot(`[\\/]compile|gccgo`, "incorrectly ran compiler") + tg.grepStderrNot(`[\\/]link|gccgo`, "incorrectly ran linker") tg.grepStderrNot(`errors\.test`, "incorrectly ran test") tg.grepStderrNot("DO NOT USE", "poisoned action status leaked") @@ -5520,9 +5572,29 @@ func TestTestVet(t *testing.T) { tg.runFail("test", "vetfail/...") tg.grepStderr(`Printf format %d`, "did not diagnose bad Printf") tg.grepStdout(`ok\s+vetfail/p2`, "did not run vetfail/p2") + + // Use -a so that we need to recompute the vet-specific export data for + // vetfail/p1. + tg.run("test", "-a", "vetfail/p2") + tg.grepStderrNot(`invalid.*constraint`, "did diagnose bad build constraint in vetxonly mode") +} + +func TestTestSkipVetAfterFailedBuild(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + + tg.tempFile("x_test.go", `package x + func f() { + return 1 + } + `) + + tg.runFail("test", tg.path("x_test.go")) + tg.grepStderrNot(`vet`, "vet should be skipped after the failed build") } -func TestTestRebuild(t *testing.T) { +func TestTestVetRebuild(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.parallel() @@ -5558,6 +5630,7 @@ func TestTestRebuild(t *testing.T) { tg.setenv("GOPATH", tg.path(".")) tg.run("test", "b") + tg.run("vet", "b") } func TestInstallDeps(t *testing.T) { @@ -5619,37 +5692,6 @@ func TestRelativePkgdir(t *testing.T) { tg.run("build", "-i", "-pkgdir=.", "runtime") } -func TestGcflagsPatterns(t *testing.T) { - skipIfGccgo(t, "gccgo has no standard packages") - tg := testgo(t) - defer tg.cleanup() - tg.setenv("GOPATH", "") - tg.setenv("GOCACHE", "off") - - tg.run("build", "-n", "-v", "-gcflags= \t\r\n -e", "fmt") - tg.grepStderr("^# fmt", "did not rebuild fmt") - tg.grepStderrNot("^# reflect", "incorrectly rebuilt reflect") - - tg.run("build", "-n", "-v", "-gcflags=-e", "fmt", "reflect") - tg.grepStderr("^# fmt", "did not rebuild fmt") - tg.grepStderr("^# reflect", "did not rebuild reflect") - tg.grepStderrNot("^# runtime", "incorrectly rebuilt runtime") - - tg.run("build", "-n", "-x", "-v", "-gcflags= \t\r\n reflect \t\r\n = \t\r\n -N", "fmt") - tg.grepStderr("^# fmt", "did not rebuild fmt") - tg.grepStderr("^# reflect", "did not rebuild reflect") - tg.grepStderr("compile.* -N .*-p reflect", "did not build reflect with -N flag") - tg.grepStderrNot("compile.* -N .*-p fmt", "incorrectly built fmt with -N flag") - - tg.run("test", "-c", "-n", "-gcflags=-N", "-ldflags=-X=x.y=z", "strings") - tg.grepStderr("compile.* -N .*compare_test.go", "did not compile strings_test package with -N flag") - tg.grepStderr("link.* -X=x.y=z", "did not link strings.test binary with -X flag") - - tg.run("test", "-c", "-n", "-gcflags=strings=-N", "-ldflags=strings=-X=x.y=z", "strings") - tg.grepStderr("compile.* -N .*compare_test.go", "did not compile strings_test package with -N flag") - tg.grepStderr("link.* -X=x.y=z", "did not link strings.test binary with -X flag") -} - func TestGoTestMinusN(t *testing.T) { // Intent here is to verify that 'go test -n' works without crashing. // This reuses flag_test.go, but really any test would do. @@ -5726,6 +5768,9 @@ func TestFailFast(t *testing.T) { // non-parallel subtests: {"TestFailingSubtestsA", true, 1}, {"TestFailingSubtestsA", false, 2}, + // fatal test + {"TestFatal[CD]", true, 1}, + {"TestFatal[CD]", false, 2}, } for _, tt := range tests { @@ -5789,59 +5834,6 @@ func init() {} tg.run("test", "a") } -// Issue 23150. -func TestCpuprofileTwice(t *testing.T) { - tg := testgo(t) - defer tg.cleanup() - tg.parallel() - tg.tempFile("prof/src/x/x_test.go", ` - package x_test - import ( - "testing" - "time" - ) - func TestSleep(t *testing.T) { time.Sleep(10 * time.Millisecond) }`) - tg.setenv("GOPATH", tg.path("prof")) - bin := tg.path("x.test") - out := tg.path("cpu.out") - tg.run("test", "-o="+bin, "-cpuprofile="+out, "x") - tg.must(os.Remove(out)) - tg.run("test", "-o="+bin, "-cpuprofile="+out, "x") - tg.mustExist(out) -} - -// Issue 23694. -func TestAtomicCoverpkgAll(t *testing.T) { - skipIfGccgo(t, "gccgo has no cover tool") - tg := testgo(t) - defer tg.cleanup() - tg.parallel() - - tg.tempFile("src/x/x.go", `package x; import _ "sync/atomic"; func F() {}`) - tg.tempFile("src/x/x_test.go", `package x; import "testing"; func TestF(t *testing.T) { F() }`) - tg.setenv("GOPATH", tg.path(".")) - tg.run("test", "-coverpkg=all", "-covermode=atomic", "x") - if canRace { - tg.run("test", "-coverpkg=all", "-race", "x") - } -} - -// Issue 23882. -func TestCoverpkgAllRuntime(t *testing.T) { - skipIfGccgo(t, "gccgo has no cover tool") - tg := testgo(t) - defer tg.cleanup() - tg.parallel() - - tg.tempFile("src/x/x.go", `package x; import _ "runtime"; func F() {}`) - tg.tempFile("src/x/x_test.go", `package x; import "testing"; func TestF(t *testing.T) { F() }`) - tg.setenv("GOPATH", tg.path(".")) - tg.run("test", "-coverpkg=all", "x") - if canRace { - tg.run("test", "-coverpkg=all", "-race", "x") - } -} - func TestBadCommandLines(t *testing.T) { tg := testgo(t) defer tg.cleanup() @@ -5871,12 +5863,12 @@ func TestBadCommandLines(t *testing.T) { tg.tempFile("src/@x/x.go", "package x\n") tg.setenv("GOPATH", tg.path(".")) tg.runFail("build", "@x") - tg.grepStderr("invalid input directory name \"@x\"", "did not reject @x directory") + tg.grepStderr("invalid input directory name \"@x\"|cannot use path@version syntax", "did not reject @x directory") tg.tempFile("src/@x/y/y.go", "package y\n") tg.setenv("GOPATH", tg.path(".")) tg.runFail("build", "@x/y") - tg.grepStderr("invalid import path \"@x/y\"", "did not reject @x/y import path") + tg.grepStderr("invalid import path \"@x/y\"|cannot use path@version syntax", "did not reject @x/y import path") tg.tempFile("src/-x/x.go", "package x\n") tg.setenv("GOPATH", tg.path(".")) @@ -6032,3 +6024,198 @@ echo $* >>`+tg.path("pkg-config.out")) t.Errorf("got %q want %q", out, want) } } + +func TestCgoCache(t *testing.T) { + if !canCgo { + t.Skip("no cgo") + } + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.tempFile("src/x/a.go", `package main + // #ifndef VAL + // #define VAL 0 + // #endif + // int val = VAL; + import "C" + import "fmt" + func main() { fmt.Println(C.val) } + `) + tg.setenv("GOPATH", tg.path(".")) + exe := tg.path("x.exe") + tg.run("build", "-o", exe, "x") + tg.setenv("CGO_LDFLAGS", "-lnosuchlibraryexists") + tg.runFail("build", "-o", exe, "x") + tg.grepStderr(`nosuchlibraryexists`, "did not run linker with changed CGO_LDFLAGS") +} + +// Issue 23982 +func TestFilepathUnderCwdFormat(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.run("test", "-x", "-cover", "log") + tg.grepStderrNot(`\.log\.cover\.go`, "-x output should contain correctly formatted filepath under cwd") +} + +// Issue 24396. +func TestDontReportRemoveOfEmptyDir(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.tempFile("src/a/a.go", `package a`) + tg.setenv("GOPATH", tg.path(".")) + tg.run("install", "-x", "a") + tg.run("install", "-x", "a") + // The second install should have printed only a WORK= line, + // nothing else. + if bytes.Count(tg.stdout.Bytes(), []byte{'\n'})+bytes.Count(tg.stderr.Bytes(), []byte{'\n'}) > 1 { + t.Error("unnecessary output when installing installed package") + } +} + +// Issue 23264. +func TestNoRelativeTmpdir(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + + tg.tempFile("src/a/a.go", `package a`) + tg.cd(tg.path(".")) + tg.must(os.Mkdir("tmp", 0777)) + + tg.setenv("GOCACHE", "off") + tg.setenv("GOPATH", tg.path(".")) + tg.setenv("GOTMPDIR", "tmp") + tg.run("build", "-work", "a") + tg.grepStderr("WORK=[^t]", "work should be absolute path") + + tg.unsetenv("GOTMPDIR") + tg.setenv("TMP", "tmp") // windows + tg.setenv("TMPDIR", "tmp") // unix + tg.run("build", "-work", "a") + tg.grepStderr("WORK=[^t]", "work should be absolute path") +} + +// Issue 24704. +func TestLinkerTmpDirIsDeleted(t *testing.T) { + skipIfGccgo(t, "gccgo does not use cmd/link") + if !canCgo { + t.Skip("skipping because cgo not enabled") + } + + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.tempFile("a.go", `package main; import "C"; func main() {}`) + tg.run("build", "-ldflags", "-v", "-o", os.DevNull, tg.path("a.go")) + // Find line that has "host link:" in linker output. + stderr := tg.getStderr() + var hostLinkLine string + for _, line := range strings.Split(stderr, "\n") { + if !strings.Contains(line, "host link:") { + continue + } + hostLinkLine = line + break + } + if hostLinkLine == "" { + t.Fatal(`fail to find with "host link:" string in linker output`) + } + // Find parameter, like "/tmp/go-link-408556474/go.o" inside of + // "host link:" line, and extract temp directory /tmp/go-link-408556474 + // out of it. + tmpdir := hostLinkLine + i := strings.Index(tmpdir, `go.o"`) + if i == -1 { + t.Fatalf(`fail to find "go.o" in "host link:" line %q`, hostLinkLine) + } + tmpdir = tmpdir[:i-1] + i = strings.LastIndex(tmpdir, `"`) + if i == -1 { + t.Fatalf(`fail to find " in "host link:" line %q`, hostLinkLine) + } + tmpdir = tmpdir[i+1:] + // Verify that temp directory has been removed. + _, err := os.Stat(tmpdir) + if err == nil { + t.Fatalf("temp directory %q has not been removed", tmpdir) + } + if !os.IsNotExist(err) { + t.Fatalf("Stat(%q) returns unexpected error: %v", tmpdir, err) + } +} + +func testCDAndGOPATHAreDifferent(tg *testgoData, cd, gopath string) { + skipIfGccgo(tg.t, "gccgo does not support -ldflags -X") + tg.setenv("GOPATH", gopath) + + tg.tempDir("dir") + exe := tg.path("dir/a.exe") + + tg.cd(cd) + + tg.run("build", "-o", exe, "-ldflags", "-X=my.pkg.Text=linkXworked") + out, err := exec.Command(exe).CombinedOutput() + if err != nil { + tg.t.Fatal(err) + } + if string(out) != "linkXworked\n" { + tg.t.Errorf(`incorrect output with GOPATH=%q and CD=%q: expected "linkXworked\n", but have %q`, gopath, cd, string(out)) + } +} + +func TestCDAndGOPATHAreDifferent(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + + gopath := filepath.Join(tg.pwd(), "testdata") + cd := filepath.Join(gopath, "src/my.pkg/main") + + testCDAndGOPATHAreDifferent(tg, cd, gopath) + if runtime.GOOS == "windows" { + testCDAndGOPATHAreDifferent(tg, cd, strings.Replace(gopath, `\`, `/`, -1)) + testCDAndGOPATHAreDifferent(tg, cd, strings.ToUpper(gopath)) + testCDAndGOPATHAreDifferent(tg, cd, strings.ToLower(gopath)) + } +} + +// Issue 26242. +func TestGoTestWithoutTests(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.run("test", "testnorun") + tg.grepStdout(`testnorun\t\[no test files\]`, "do not want test to run") +} + +// Issue 25579. +func TestGoBuildDashODevNull(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.run("build", "-o", os.DevNull, filepath.Join(tg.pwd(), "testdata", "src", "hello", "hello.go")) + tg.mustNotExist("hello") + tg.mustNotExist("hello.exe") +} + +// Issue 25093. +func TestCoverpkgTestOnly(t *testing.T) { + skipIfGccgo(t, "gccgo has no cover tool") + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.tempFile("src/a/a.go", `package a + func F(i int) int { + return i*i + }`) + tg.tempFile("src/atest/a_test.go", ` + package a_test + import ( "a"; "testing" ) + func TestF(t *testing.T) { a.F(2) } + `) + tg.setenv("GOPATH", tg.path(".")) + tg.run("test", "-coverpkg=a", "atest") + tg.grepStderrNot("no packages being tested depend on matches", "bad match message") + tg.grepStdout("coverage: 100", "no coverage") +} diff --git a/libgo/go/cmd/go/go_windows_test.go b/libgo/go/cmd/go/go_windows_test.go index aa68a19..99af3d4 100644 --- a/libgo/go/cmd/go/go_windows_test.go +++ b/libgo/go/cmd/go/go_windows_test.go @@ -12,7 +12,6 @@ import ( "os/exec" "path/filepath" "strings" - "syscall" "testing" ) @@ -57,15 +56,6 @@ func TestAbsolutePath(t *testing.T) { } } -func isWindowsXP(t *testing.T) bool { - v, err := syscall.GetVersion() - if err != nil { - t.Fatalf("GetVersion failed: %v", err) - } - major := byte(v) - return major < 6 -} - func runIcacls(t *testing.T, args ...string) string { t.Helper() out, err := exec.Command("icacls", args...).CombinedOutput() @@ -89,10 +79,6 @@ func runGetACL(t *testing.T, path string) string { // has discretionary access control list (DACL) set as if the file // was created in the destination directory. func TestACL(t *testing.T) { - if isWindowsXP(t) { - t.Skip("Windows XP does not have powershell command") - } - tmpdir, err := ioutil.TempDir("", "TestACL") if err != nil { t.Fatal(err) @@ -111,7 +97,7 @@ func TestACL(t *testing.T) { // will make all files created in TestACL/tmp have different // security attributes to the files created in TestACL. runIcacls(t, newtmpdir, - "/grant", "guest:(oi)(ci)f", // add Guest user to have full access + "/grant", "*S-1-5-32-546:(oi)(ci)f", // add Guests group to have full access ) src := filepath.Join(tmpdir, "main.go") diff --git a/libgo/go/cmd/go/internal/base/base.go b/libgo/go/cmd/go/internal/base/base.go index 286efbc..e7f54c9 100644 --- a/libgo/go/cmd/go/internal/base/base.go +++ b/libgo/go/cmd/go/internal/base/base.go @@ -45,25 +45,43 @@ type Command struct { // CustomFlags indicates that the command will do its own // flag parsing. CustomFlags bool + + // Commands lists the available commands and help topics. + // The order here is the order in which they are printed by 'go help'. + // Note that subcommands are in general best avoided. + Commands []*Command } -// Commands lists the available commands and help topics. -// The order here is the order in which they are printed by 'go help'. -var Commands []*Command +var Go = &Command{ + UsageLine: "go", + Long: `Go is a tool for managing Go source code.`, + // Commands initialized in package main +} -// Name returns the command's name: the first word in the usage line. -func (c *Command) Name() string { +// LongName returns the command's long name: all the words in the usage line between "go" and a flag or argument, +func (c *Command) LongName() string { name := c.UsageLine - i := strings.Index(name, " ") - if i >= 0 { + if i := strings.Index(name, " ["); i >= 0 { name = name[:i] } + if name == "go" { + return "" + } + return strings.TrimPrefix(name, "go ") +} + +// Name returns the command's short name: the last word in the usage line before a flag or argument. +func (c *Command) Name() string { + name := c.LongName() + if i := strings.LastIndex(name, " "); i >= 0 { + name = name[i+1:] + } return name } func (c *Command) Usage() { fmt.Fprintf(os.Stderr, "usage: %s\n", c.UsageLine) - fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", c.Name()) + fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", c.LongName()) os.Exit(2) } diff --git a/libgo/go/cmd/go/internal/base/goflags.go b/libgo/go/cmd/go/internal/base/goflags.go new file mode 100644 index 0000000..2f50b50 --- /dev/null +++ b/libgo/go/cmd/go/internal/base/goflags.go @@ -0,0 +1,152 @@ +// Copyright 2018 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 base + +import ( + "flag" + "fmt" + "os" + "runtime" + "strings" + + "cmd/go/internal/cfg" +) + +var ( + goflags []string // cached $GOFLAGS list; can be -x or --x form + knownFlag = make(map[string]bool) // flags allowed to appear in $GOFLAGS; no leading dashes +) + +// AddKnownFlag adds name to the list of known flags for use in $GOFLAGS. +func AddKnownFlag(name string) { + knownFlag[name] = true +} + +// GOFLAGS returns the flags from $GOFLAGS. +// The list can be assumed to contain one string per flag, +// with each string either beginning with -name or --name. +func GOFLAGS() []string { + InitGOFLAGS() + return goflags +} + +// InitGOFLAGS initializes the goflags list from $GOFLAGS. +// If goflags is already initialized, it does nothing. +func InitGOFLAGS() { + if goflags != nil { // already initialized + return + } + + // Build list of all flags for all commands. + // If no command has that flag, then we report the problem. + // This catches typos while still letting users record flags in GOFLAGS + // that only apply to a subset of go commands. + // Commands using CustomFlags can report their flag names + // by calling AddKnownFlag instead. + var walkFlags func(*Command) + walkFlags = func(cmd *Command) { + for _, sub := range cmd.Commands { + walkFlags(sub) + } + cmd.Flag.VisitAll(func(f *flag.Flag) { + knownFlag[f.Name] = true + }) + } + walkFlags(Go) + + // Ignore bad flag in go env and go bug, because + // they are what people reach for when debugging + // a problem, and maybe they're debugging GOFLAGS. + // (Both will show the GOFLAGS setting if let succeed.) + hideErrors := cfg.CmdName == "env" || cfg.CmdName == "bug" + + goflags = strings.Fields(os.Getenv("GOFLAGS")) + if goflags == nil { + goflags = []string{} // avoid work on later InitGOFLAGS call + } + + // Each of the words returned by strings.Fields must be its own flag. + // To set flag arguments use -x=value instead of -x value. + // For boolean flags, -x is fine instead of -x=true. + for _, f := range goflags { + // Check that every flag looks like -x --x -x=value or --x=value. + if !strings.HasPrefix(f, "-") || f == "-" || f == "--" || strings.HasPrefix(f, "---") || strings.HasPrefix(f, "-=") || strings.HasPrefix(f, "--=") { + if hideErrors { + continue + } + Fatalf("go: parsing $GOFLAGS: non-flag %q", f) + } + + name := f[1:] + if name[0] == '-' { + name = name[1:] + } + if i := strings.Index(name, "="); i >= 0 { + name = name[:i] + } + if !knownFlag[name] { + if hideErrors { + continue + } + Fatalf("go: parsing $GOFLAGS: unknown flag -%s", name) + } + } +} + +// boolFlag is the optional interface for flag.Value known to the flag package. +// (It is not clear why package flag does not export this interface.) +type boolFlag interface { + flag.Value + IsBoolFlag() bool +} + +// SetFromGOFLAGS sets the flags in the given flag set using settings in $GOFLAGS. +func SetFromGOFLAGS(flags flag.FlagSet) { + InitGOFLAGS() + + // This loop is similar to flag.Parse except that it ignores + // unknown flags found in goflags, so that setting, say, GOFLAGS=-ldflags=-w + // does not break commands that don't have a -ldflags. + // It also adjusts the output to be clear that the reported problem is from $GOFLAGS. + where := "$GOFLAGS" + if runtime.GOOS == "windows" { + where = "%GOFLAGS%" + } + for _, goflag := range goflags { + name, value, hasValue := goflag, "", false + if i := strings.Index(goflag, "="); i >= 0 { + name, value, hasValue = goflag[:i], goflag[i+1:], true + } + if strings.HasPrefix(name, "--") { + name = name[1:] + } + f := flags.Lookup(name[1:]) + if f == nil { + continue + } + if fb, ok := f.Value.(boolFlag); ok && fb.IsBoolFlag() { + if hasValue { + if err := fb.Set(value); err != nil { + fmt.Fprintf(flags.Output(), "go: invalid boolean value %q for flag %s (from %s): %v\n", value, name, where, err) + flags.Usage() + } + } else { + if err := fb.Set("true"); err != nil { + fmt.Fprintf(flags.Output(), "go: invalid boolean flag %s (from %s): %v\n", name, where, err) + flags.Usage() + } + } + } else { + if !hasValue { + fmt.Fprintf(flags.Output(), "go: flag needs an argument: %s (from %s)\n", name, where) + flags.Usage() + } + if err := f.Value.Set(value); err != nil { + fmt.Fprintf(flags.Output(), "go: invalid value %q for flag %s (from %s): %v\n", value, name, where, err) + flags.Usage() + } + } + } +} diff --git a/libgo/go/cmd/go/internal/base/signal_unix.go b/libgo/go/cmd/go/internal/base/signal_unix.go index b90f3a2..c109eec 100644 --- a/libgo/go/cmd/go/internal/base/signal_unix.go +++ b/libgo/go/cmd/go/internal/base/signal_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js linux nacl netbsd openbsd solaris package base diff --git a/libgo/go/cmd/go/internal/bug/bug.go b/libgo/go/cmd/go/internal/bug/bug.go index 963da94..e701f6e 100644 --- a/libgo/go/cmd/go/internal/bug/bug.go +++ b/libgo/go/cmd/go/internal/bug/bug.go @@ -25,7 +25,7 @@ import ( var CmdBug = &base.Command{ Run: runBug, - UsageLine: "bug", + UsageLine: "go bug", Short: "start a bug report", Long: ` Bug opens the default browser and starts a new bug report. @@ -38,6 +38,9 @@ func init() { } func runBug(cmd *base.Command, args []string) { + if len(args) > 0 { + base.Fatalf("go bug: bug takes no arguments") + } var buf bytes.Buffer buf.WriteString(bugHeader) inspectGoVersion(&buf) diff --git a/libgo/go/cmd/go/internal/cache/cache.go b/libgo/go/cmd/go/internal/cache/cache.go index edb5882..0cf0155 100644 --- a/libgo/go/cmd/go/internal/cache/cache.go +++ b/libgo/go/cmd/go/internal/cache/cache.go @@ -189,6 +189,21 @@ func (c *Cache) get(id ActionID) (Entry, error) { return Entry{buf, size, time.Unix(0, tm)}, nil } +// GetFile looks up the action ID in the cache and returns +// the name of the corresponding data file. +func (c *Cache) GetFile(id ActionID) (file string, entry Entry, err error) { + entry, err = c.Get(id) + if err != nil { + return "", Entry{}, err + } + file = c.OutputFile(entry.OutputID) + info, err := os.Stat(file) + if err != nil || info.Size() != entry.Size { + return "", Entry{}, errMissing + } + return file, entry, nil +} + // GetBytes looks up the action ID in the cache and returns // the corresponding output bytes. // GetBytes should only be used for data that can be expected to fit in memory. diff --git a/libgo/go/cmd/go/internal/cache/default.go b/libgo/go/cmd/go/internal/cache/default.go index 9728376..02fc1e8 100644 --- a/libgo/go/cmd/go/internal/cache/default.go +++ b/libgo/go/cmd/go/internal/cache/default.go @@ -35,12 +35,14 @@ See golang.org to learn more about Go. // initDefaultCache does the work of finding the default cache // the first time Default is called. func initDefaultCache() { - dir := DefaultDir() + dir, showWarnings := defaultDir() if dir == "off" { return } if err := os.MkdirAll(dir, 0777); err != nil { - fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err) + if showWarnings { + fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err) + } return } if _, err := os.Stat(filepath.Join(dir, "README")); err != nil { @@ -50,7 +52,9 @@ func initDefaultCache() { c, err := Open(dir) if err != nil { - fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err) + if showWarnings { + fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err) + } return } defaultCache = c @@ -59,14 +63,24 @@ func initDefaultCache() { // DefaultDir returns the effective GOCACHE setting. // It returns "off" if the cache is disabled. func DefaultDir() string { + dir, _ := defaultDir() + return dir +} + +// defaultDir returns the effective GOCACHE setting. +// It returns "off" if the cache is disabled. +// The second return value reports whether warnings should +// be shown if the cache fails to initialize. +func defaultDir() (string, bool) { dir := os.Getenv("GOCACHE") if dir != "" { - return dir + return dir, true } // Compute default location. // TODO(rsc): This code belongs somewhere else, // like maybe ioutil.CacheDir or os.CacheDir. + showWarnings := true switch runtime.GOOS { case "windows": dir = os.Getenv("LocalAppData") @@ -76,20 +90,20 @@ func DefaultDir() string { dir = os.Getenv("AppData") } if dir == "" { - return "off" + return "off", true } case "darwin": dir = os.Getenv("HOME") if dir == "" { - return "off" + return "off", true } dir += "/Library/Caches" case "plan9": dir = os.Getenv("home") if dir == "" { - return "off" + return "off", true } // Plan 9 has no established per-user cache directory, // but $home/lib/xyz is the usual equivalent of $HOME/.xyz on Unix. @@ -101,10 +115,15 @@ func DefaultDir() string { if dir == "" { dir = os.Getenv("HOME") if dir == "" { - return "off" + return "off", true + } + if dir == "/" { + // probably docker run with -u flag + // https://golang.org/issue/26280 + showWarnings = false } dir += "/.cache" } } - return filepath.Join(dir, "go-build") + return filepath.Join(dir, "go-build"), showWarnings } diff --git a/libgo/go/cmd/go/internal/cache/default_unix_test.go b/libgo/go/cmd/go/internal/cache/default_unix_test.go new file mode 100644 index 0000000..a207497 --- /dev/null +++ b/libgo/go/cmd/go/internal/cache/default_unix_test.go @@ -0,0 +1,67 @@ +// Copyright 2018 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 !windows,!darwin,!plan9 + +package cache + +import ( + "os" + "strings" + "testing" +) + +func TestDefaultDir(t *testing.T) { + goCacheDir := "/tmp/test-go-cache" + xdgCacheDir := "/tmp/test-xdg-cache" + homeDir := "/tmp/test-home" + + // undo env changes when finished + defer func(GOCACHE, XDG_CACHE_HOME, HOME string) { + os.Setenv("GOCACHE", GOCACHE) + os.Setenv("XDG_CACHE_HOME", XDG_CACHE_HOME) + os.Setenv("HOME", HOME) + }(os.Getenv("GOCACHE"), os.Getenv("XDG_CACHE_HOME"), os.Getenv("HOME")) + + os.Setenv("GOCACHE", goCacheDir) + os.Setenv("XDG_CACHE_HOME", xdgCacheDir) + os.Setenv("HOME", homeDir) + + dir, showWarnings := defaultDir() + if dir != goCacheDir { + t.Errorf("Cache DefaultDir %q should be $GOCACHE %q", dir, goCacheDir) + } + if !showWarnings { + t.Error("Warnings should be shown when $GOCACHE is set") + } + + os.Unsetenv("GOCACHE") + dir, showWarnings = defaultDir() + if !strings.HasPrefix(dir, xdgCacheDir+"/") { + t.Errorf("Cache DefaultDir %q should be under $XDG_CACHE_HOME %q when $GOCACHE is unset", dir, xdgCacheDir) + } + if !showWarnings { + t.Error("Warnings should be shown when $XDG_CACHE_HOME is set") + } + + os.Unsetenv("XDG_CACHE_HOME") + dir, showWarnings = defaultDir() + if !strings.HasPrefix(dir, homeDir+"/.cache/") { + t.Errorf("Cache DefaultDir %q should be under $HOME/.cache %q when $GOCACHE and $XDG_CACHE_HOME are unset", dir, homeDir+"/.cache") + } + if !showWarnings { + t.Error("Warnings should be shown when $HOME is not /") + } + + os.Unsetenv("HOME") + if dir, _ := defaultDir(); dir != "off" { + t.Error("Cache not disabled when $GOCACHE, $XDG_CACHE_HOME, and $HOME are unset") + } + + os.Setenv("HOME", "/") + if _, showWarnings := defaultDir(); showWarnings { + // https://golang.org/issue/26280 + t.Error("Cache initalization warnings should be squelched when $GOCACHE and $XDG_CACHE_HOME are unset and $HOME is /") + } +} diff --git a/libgo/go/cmd/go/internal/cfg/cfg.go b/libgo/go/cmd/go/internal/cfg/cfg.go index f0a2277..8dc4d1f 100644 --- a/libgo/go/cmd/go/internal/cfg/cfg.go +++ b/libgo/go/cmd/go/internal/cfg/cfg.go @@ -20,7 +20,8 @@ import ( var ( BuildA bool // -a flag BuildBuildmode string // -buildmode flag - BuildContext = build.Default + BuildContext = defaultContext() + BuildMod string // -mod flag BuildI bool // -i flag BuildLinkshared bool // -linkshared flag BuildMSan bool // -msan flag @@ -42,6 +43,12 @@ var ( DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable) ) +func defaultContext() build.Context { + ctxt := build.Default + ctxt.JoinPath = filepath.Join // back door to say "do not use go command" + return ctxt +} + func init() { BuildToolchainCompiler = func() string { return "missing-compiler" } BuildToolchainLinker = func() string { return "missing-linker" } @@ -67,6 +74,16 @@ var ( Goos = BuildContext.GOOS ExeSuffix string Gopath = filepath.SplitList(BuildContext.GOPATH) + + // ModulesEnabled specifies whether the go command is running + // in module-aware mode (as opposed to GOPATH mode). + // It is equal to modload.Enabled, but not all packages can import modload. + ModulesEnabled bool + + // GoModInGOPATH records whether we've found a go.mod in GOPATH/src + // in GO111MODULE=auto mode. In that case, we don't use modules + // but people might expect us to, so 'go get' warns. + GoModInGOPATH string ) func init() { @@ -84,9 +101,10 @@ var ( GOROOT_FINAL = findGOROOT_FINAL() // Used in envcmd.MkEnv and build ID computations. - GOARM = fmt.Sprint(objabi.GOARM) - GO386 = objabi.GO386 - GOMIPS = objabi.GOMIPS + GOARM = fmt.Sprint(objabi.GOARM) + GO386 = objabi.GO386 + GOMIPS = objabi.GOMIPS + GOMIPS64 = objabi.GOMIPS64 ) // Update build context to use our computed GOROOT. @@ -102,6 +120,16 @@ func init() { } } +// There is a copy of findGOROOT, isSameDir, and isGOROOT in +// x/tools/cmd/godoc/goroot.go. +// Try to keep them in sync for now. + +// findGOROOT returns the GOROOT value, using either an explicitly +// provided environment variable, a GOROOT that contains the current +// os.Executable value, or else the GOROOT that the binary was built +// with from runtime.GOROOT(). +// +// There is a copy of this code in x/tools/cmd/godoc/goroot.go. func findGOROOT() string { if env := os.Getenv("GOROOT"); env != "" { return filepath.Clean(env) @@ -161,6 +189,8 @@ func isSameDir(dir1, dir2 string) bool { // It does this by looking for the path/pkg/tool directory, // which is necessary for useful operation of the cmd/go tool, // and is not typically present in a GOPATH. +// +// There is a copy of this code in x/tools/cmd/godoc/goroot.go. func isGOROOT(path string) bool { stat, err := os.Stat(filepath.Join(path, "pkg", "tool")) if err != nil { diff --git a/libgo/go/cmd/go/internal/clean/clean.go b/libgo/go/cmd/go/internal/clean/clean.go index fa5af94..d023592 100644 --- a/libgo/go/cmd/go/internal/clean/clean.go +++ b/libgo/go/cmd/go/internal/clean/clean.go @@ -17,11 +17,13 @@ import ( "cmd/go/internal/cache" "cmd/go/internal/cfg" "cmd/go/internal/load" + "cmd/go/internal/modfetch" + "cmd/go/internal/modload" "cmd/go/internal/work" ) var CmdClean = &base.Command{ - UsageLine: "clean [-i] [-r] [-n] [-x] [-cache] [-testcache] [build flags] [packages]", + UsageLine: "go clean [clean flags] [build flags] [packages]", Short: "remove object files and cached files", Long: ` Clean removes object files from package source directories. @@ -65,6 +67,10 @@ The -cache flag causes clean to remove the entire go build cache. The -testcache flag causes clean to expire all test results in the go build cache. +The -modcache flag causes clean to remove the entire module +download cache, including unpacked source code of versioned +dependencies. + For more about build flags, see 'go help build'. For more about specifying packages, see 'go help packages'. @@ -75,6 +81,7 @@ var ( cleanI bool // clean -i flag cleanR bool // clean -r flag cleanCache bool // clean -cache flag + cleanModcache bool // clean -modcache flag cleanTestcache bool // clean -testcache flag ) @@ -85,6 +92,7 @@ func init() { CmdClean.Flag.BoolVar(&cleanI, "i", false, "") CmdClean.Flag.BoolVar(&cleanR, "r", false, "") CmdClean.Flag.BoolVar(&cleanCache, "cache", false, "") + CmdClean.Flag.BoolVar(&cleanModcache, "modcache", false, "") CmdClean.Flag.BoolVar(&cleanTestcache, "testcache", false, "") // -n and -x are important enough to be @@ -95,8 +103,13 @@ func init() { } func runClean(cmd *base.Command, args []string) { - for _, pkg := range load.PackagesAndErrors(args) { - clean(pkg) + if len(args) == 0 && modload.Failed() { + // Don't try to clean current directory, + // which will cause modload to base.Fatalf. + } else { + for _, pkg := range load.PackagesAndErrors(args) { + clean(pkg) + } } if cleanCache { @@ -138,6 +151,29 @@ func runClean(cmd *base.Command, args []string) { } } } + + if cleanModcache { + if modfetch.PkgMod == "" { + base.Fatalf("go clean -modcache: no module cache") + } + if err := removeAll(modfetch.PkgMod); err != nil { + base.Errorf("go clean -modcache: %v", err) + } + } +} + +func removeAll(dir string) error { + // Module cache has 0555 directories; make them writable in order to remove content. + filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil // ignore errors walking in file system + } + if info.IsDir() { + os.Chmod(path, 0777) + } + return nil + }) + return os.RemoveAll(dir) } var cleaned = map[*load.Package]bool{} diff --git a/libgo/go/cmd/go/internal/cmdflag/flag.go b/libgo/go/cmd/go/internal/cmdflag/flag.go index 7ab3022..b2a67e6 100644 --- a/libgo/go/cmd/go/internal/cmdflag/flag.go +++ b/libgo/go/cmd/go/internal/cmdflag/flag.go @@ -69,6 +69,14 @@ func SyntaxError(cmd, msg string) { os.Exit(2) } +// AddKnownFlags registers the flags in defns with base.AddKnownFlag. +func AddKnownFlags(cmd string, defns []*Defn) { + for _, f := range defns { + base.AddKnownFlag(f.Name) + base.AddKnownFlag(cmd + "." + f.Name) + } +} + // Parse sees if argument i is present in the definitions and if so, // returns its definition, value, and whether it consumed an extra word. // If the flag begins (cmd+".") it is ignored for the purpose of this function. @@ -121,3 +129,31 @@ func Parse(cmd string, defns []*Defn, args []string, i int) (f *Defn, value stri f = nil return } + +// FindGOFLAGS extracts and returns the flags matching defns from GOFLAGS. +// Ideally the caller would mention that the flags were from GOFLAGS +// when reporting errors, but that's too hard for now. +func FindGOFLAGS(defns []*Defn) []string { + var flags []string + for _, flag := range base.GOFLAGS() { + // Flags returned by base.GOFLAGS are well-formed, one of: + // -x + // --x + // -x=value + // --x=value + if strings.HasPrefix(flag, "--") { + flag = flag[1:] + } + name := flag[1:] + if i := strings.Index(name, "="); i >= 0 { + name = name[:i] + } + for _, f := range defns { + if name == f.Name { + flags = append(flags, flag) + break + } + } + } + return flags +} diff --git a/libgo/go/cmd/go/internal/dirhash/hash.go b/libgo/go/cmd/go/internal/dirhash/hash.go new file mode 100644 index 0000000..61d8fac --- /dev/null +++ b/libgo/go/cmd/go/internal/dirhash/hash.go @@ -0,0 +1,103 @@ +// Copyright 2018 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 dirhash defines hashes over directory trees. +package dirhash + +import ( + "archive/zip" + "crypto/sha256" + "encoding/base64" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "sort" + "strings" +) + +var DefaultHash = Hash1 + +type Hash func(files []string, open func(string) (io.ReadCloser, error)) (string, error) + +func Hash1(files []string, open func(string) (io.ReadCloser, error)) (string, error) { + h := sha256.New() + files = append([]string(nil), files...) + sort.Strings(files) + for _, file := range files { + if strings.Contains(file, "\n") { + return "", errors.New("filenames with newlines are not supported") + } + r, err := open(file) + if err != nil { + return "", err + } + hf := sha256.New() + _, err = io.Copy(hf, r) + r.Close() + if err != nil { + return "", err + } + fmt.Fprintf(h, "%x %s\n", hf.Sum(nil), file) + } + return "h1:" + base64.StdEncoding.EncodeToString(h.Sum(nil)), nil +} + +func HashDir(dir, prefix string, hash Hash) (string, error) { + files, err := DirFiles(dir, prefix) + if err != nil { + return "", err + } + osOpen := func(name string) (io.ReadCloser, error) { + return os.Open(filepath.Join(dir, strings.TrimPrefix(name, prefix))) + } + return hash(files, osOpen) +} + +func DirFiles(dir, prefix string) ([]string, error) { + var files []string + dir = filepath.Clean(dir) + err := filepath.Walk(dir, func(file string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + rel := file + if dir != "." { + rel = file[len(dir)+1:] + } + f := filepath.Join(prefix, rel) + files = append(files, filepath.ToSlash(f)) + return nil + }) + if err != nil { + return nil, err + } + return files, nil +} + +func HashZip(zipfile string, hash Hash) (string, error) { + z, err := zip.OpenReader(zipfile) + if err != nil { + return "", err + } + defer z.Close() + var files []string + zfiles := make(map[string]*zip.File) + for _, file := range z.File { + files = append(files, file.Name) + zfiles[file.Name] = file + } + zipOpen := func(name string) (io.ReadCloser, error) { + f := zfiles[name] + if f == nil { + return nil, fmt.Errorf("file %q not found in zip", name) // should never happen + } + return f.Open() + } + return hash(files, zipOpen) +} diff --git a/libgo/go/cmd/go/internal/dirhash/hash_test.go b/libgo/go/cmd/go/internal/dirhash/hash_test.go new file mode 100644 index 0000000..ed463c1 --- /dev/null +++ b/libgo/go/cmd/go/internal/dirhash/hash_test.go @@ -0,0 +1,135 @@ +// Copyright 2018 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 dirhash + +import ( + "archive/zip" + "crypto/sha256" + "encoding/base64" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" +) + +func h(s string) string { + return fmt.Sprintf("%x", sha256.Sum256([]byte(s))) +} + +func htop(k string, s string) string { + sum := sha256.Sum256([]byte(s)) + return k + ":" + base64.StdEncoding.EncodeToString(sum[:]) +} + +func TestHash1(t *testing.T) { + files := []string{"xyz", "abc"} + open := func(name string) (io.ReadCloser, error) { + return ioutil.NopCloser(strings.NewReader("data for " + name)), nil + } + want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "abc", h("data for xyz"), "xyz")) + out, err := Hash1(files, open) + if err != nil { + t.Fatal(err) + } + if out != want { + t.Errorf("Hash1(...) = %s, want %s", out, want) + } + + _, err = Hash1([]string{"xyz", "a\nbc"}, open) + if err == nil { + t.Error("Hash1: expected error on newline in filenames") + } +} + +func TestHashDir(t *testing.T) { + dir, err := ioutil.TempDir("", "dirhash-test-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil { + t.Fatal(err) + } + if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil { + t.Fatal(err) + } + want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "prefix/abc", h("data for xyz"), "prefix/xyz")) + out, err := HashDir(dir, "prefix", Hash1) + if err != nil { + t.Fatalf("HashDir: %v", err) + } + if out != want { + t.Errorf("HashDir(...) = %s, want %s", out, want) + } +} + +func TestHashZip(t *testing.T) { + f, err := ioutil.TempFile("", "dirhash-test-") + if err != nil { + t.Fatal(err) + } + defer os.Remove(f.Name()) + defer f.Close() + + z := zip.NewWriter(f) + w, err := z.Create("prefix/xyz") + if err != nil { + t.Fatal(err) + } + w.Write([]byte("data for xyz")) + w, err = z.Create("prefix/abc") + if err != nil { + t.Fatal(err) + } + w.Write([]byte("data for abc")) + if err := z.Close(); err != nil { + t.Fatal(err) + } + if err := f.Close(); err != nil { + t.Fatal(err) + } + + want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "prefix/abc", h("data for xyz"), "prefix/xyz")) + out, err := HashZip(f.Name(), Hash1) + if err != nil { + t.Fatalf("HashDir: %v", err) + } + if out != want { + t.Errorf("HashDir(...) = %s, want %s", out, want) + } +} + +func TestDirFiles(t *testing.T) { + dir, err := ioutil.TempDir("", "dirfiles-test-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil { + t.Fatal(err) + } + if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil { + t.Fatal(err) + } + if err := os.Mkdir(filepath.Join(dir, "subdir"), 0777); err != nil { + t.Fatal(err) + } + if err := ioutil.WriteFile(filepath.Join(dir, "subdir", "xyz"), []byte("data for subdir xyz"), 0666); err != nil { + t.Fatal(err) + } + prefix := "foo/bar@v2.3.4" + out, err := DirFiles(dir, prefix) + if err != nil { + t.Fatalf("DirFiles: %v", err) + } + for _, file := range out { + if !strings.HasPrefix(file, prefix) { + t.Errorf("Dir file = %s, want prefix %s", file, prefix) + } + } +} diff --git a/libgo/go/cmd/go/internal/doc/doc.go b/libgo/go/cmd/go/internal/doc/doc.go index d73dd9a..4e7dca0 100644 --- a/libgo/go/cmd/go/internal/doc/doc.go +++ b/libgo/go/cmd/go/internal/doc/doc.go @@ -12,7 +12,7 @@ import ( var CmdDoc = &base.Command{ Run: runDoc, - UsageLine: "doc [-u] [-c] [package|[package.]symbol[.methodOrField]]", + UsageLine: "go doc [-u] [-c] [package|[package.]symbol[.methodOrField]]", CustomFlags: true, Short: "show documentation for package or symbol", Long: ` diff --git a/libgo/go/cmd/go/internal/envcmd/env.go b/libgo/go/cmd/go/internal/envcmd/env.go index 603f7b5..afadbad 100644 --- a/libgo/go/cmd/go/internal/envcmd/env.go +++ b/libgo/go/cmd/go/internal/envcmd/env.go @@ -9,6 +9,7 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" "runtime" "strings" @@ -16,11 +17,12 @@ import ( "cmd/go/internal/cache" "cmd/go/internal/cfg" "cmd/go/internal/load" + "cmd/go/internal/modload" "cmd/go/internal/work" ) var CmdEnv = &base.Command{ - UsageLine: "env [-json] [var ...]", + UsageLine: "go env [-json] [var ...]", Short: "print Go environment information", Long: ` Env prints Go environment information. @@ -52,17 +54,16 @@ func MkEnv() []cfg.EnvVar { {Name: "GOBIN", Value: cfg.GOBIN}, {Name: "GOCACHE", Value: cache.DefaultDir()}, {Name: "GOEXE", Value: cfg.ExeSuffix}, + {Name: "GOFLAGS", Value: os.Getenv("GOFLAGS")}, {Name: "GOHOSTARCH", Value: runtime.GOARCH}, {Name: "GOHOSTOS", Value: runtime.GOOS}, {Name: "GOOS", Value: cfg.Goos}, {Name: "GOPATH", Value: cfg.BuildContext.GOPATH}, + {Name: "GOPROXY", Value: os.Getenv("GOPROXY")}, {Name: "GORACE", Value: os.Getenv("GORACE")}, {Name: "GOROOT", Value: cfg.GOROOT}, {Name: "GOTMPDIR", Value: os.Getenv("GOTMPDIR")}, {Name: "GOTOOLDIR", Value: base.ToolDir}, - - // disable escape codes in clang errors - {Name: "TERM", Value: "dumb"}, } if work.GccgoBin != "" { @@ -78,6 +79,8 @@ func MkEnv() []cfg.EnvVar { env = append(env, cfg.EnvVar{Name: "GO386", Value: cfg.GO386}) case "mips", "mipsle": env = append(env, cfg.EnvVar{Name: "GOMIPS", Value: cfg.GOMIPS}) + case "mips64", "mips64le": + env = append(env, cfg.EnvVar{Name: "GOMIPS64", Value: cfg.GOMIPS64}) } cc := cfg.DefaultCC(cfg.Goos, cfg.Goarch) @@ -111,6 +114,18 @@ func findEnv(env []cfg.EnvVar, name string) string { // ExtraEnvVars returns environment variables that should not leak into child processes. func ExtraEnvVars() []cfg.EnvVar { + gomod := "" + if modload.Init(); modload.ModRoot != "" { + gomod = filepath.Join(modload.ModRoot, "go.mod") + } + return []cfg.EnvVar{ + {Name: "GOMOD", Value: gomod}, + } +} + +// ExtraEnvVarsCostly returns environment variables that should not leak into child processes +// but are costly to evaluate. +func ExtraEnvVarsCostly() []cfg.EnvVar { var b work.Builder b.Init() cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{}) @@ -120,6 +135,7 @@ func ExtraEnvVars() []cfg.EnvVar { return nil } cmd := b.GccCmd(".", "") + return []cfg.EnvVar{ // Note: Update the switch in runEnv below when adding to this list. {Name: "CGO_CFLAGS", Value: strings.Join(cflags, " ")}, @@ -134,13 +150,14 @@ func ExtraEnvVars() []cfg.EnvVar { func runEnv(cmd *base.Command, args []string) { env := cfg.CmdEnv + env = append(env, ExtraEnvVars()...) - // Do we need to call ExtraEnvVars, which is a bit expensive? + // Do we need to call ExtraEnvVarsCostly, which is a bit expensive? // Only if we're listing all environment variables ("go env") // or the variables being requested are in the extra list. - needExtra := true + needCostly := true if len(args) > 0 { - needExtra = false + needCostly = false for _, arg := range args { switch arg { case "CGO_CFLAGS", @@ -150,12 +167,12 @@ func runEnv(cmd *base.Command, args []string) { "CGO_LDFLAGS", "PKG_CONFIG", "GOGCCFLAGS": - needExtra = true + needCostly = true } } } - if needExtra { - env = append(env, ExtraEnvVars()...) + if needCostly { + env = append(env, ExtraEnvVarsCostly()...) } if len(args) > 0 { diff --git a/libgo/go/cmd/go/internal/fix/fix.go b/libgo/go/cmd/go/internal/fix/fix.go index 99c7ca5..aab1641 100644 --- a/libgo/go/cmd/go/internal/fix/fix.go +++ b/libgo/go/cmd/go/internal/fix/fix.go @@ -9,12 +9,15 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/load" + "cmd/go/internal/modload" "cmd/go/internal/str" + "fmt" + "os" ) var CmdFix = &base.Command{ Run: runFix, - UsageLine: "fix [packages]", + UsageLine: "go fix [packages]", Short: "update packages to use new APIs", Long: ` Fix runs the Go fix command on the packages named by the import paths. @@ -29,7 +32,15 @@ See also: go fmt, go vet. } func runFix(cmd *base.Command, args []string) { + printed := false for _, pkg := range load.Packages(args) { + if modload.Enabled() && !pkg.Module.Main { + if !printed { + fmt.Fprintf(os.Stderr, "go: not fixing packages in dependency modules\n") + printed = true + } + continue + } // Use pkg.gofiles instead of pkg.Dir so that // the command only applies to this package, // not to packages in subdirectories. diff --git a/libgo/go/cmd/go/internal/fmtcmd/fmt.go b/libgo/go/cmd/go/internal/fmtcmd/fmt.go index eb96823..8e4ef37 100644 --- a/libgo/go/cmd/go/internal/fmtcmd/fmt.go +++ b/libgo/go/cmd/go/internal/fmtcmd/fmt.go @@ -6,6 +6,7 @@ package fmtcmd import ( + "fmt" "os" "path/filepath" "runtime" @@ -15,6 +16,7 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/load" + "cmd/go/internal/modload" "cmd/go/internal/str" ) @@ -24,7 +26,7 @@ func init() { var CmdFmt = &base.Command{ Run: runFmt, - UsageLine: "fmt [-n] [-x] [packages]", + UsageLine: "go fmt [-n] [-x] [packages]", Short: "gofmt (reformat) package sources", Long: ` Fmt runs the command 'gofmt -l -w' on the packages named @@ -43,6 +45,7 @@ See also: go fix, go vet. } func runFmt(cmd *base.Command, args []string) { + printed := false gofmt := gofmtPath() procs := runtime.GOMAXPROCS(0) var wg sync.WaitGroup @@ -57,6 +60,13 @@ func runFmt(cmd *base.Command, args []string) { }() } for _, pkg := range load.PackagesAndErrors(args) { + if modload.Enabled() && pkg.Module != nil && !pkg.Module.Main { + if !printed { + fmt.Fprintf(os.Stderr, "go: not formatting packages in dependency modules\n") + printed = true + } + continue + } if pkg.Error != nil { if strings.HasPrefix(pkg.Error.Err, "build constraints exclude all Go files") { // Skip this error, as we will format diff --git a/libgo/go/cmd/go/internal/generate/generate.go b/libgo/go/cmd/go/internal/generate/generate.go index 75c0d3b..9482be9 100644 --- a/libgo/go/cmd/go/internal/generate/generate.go +++ b/libgo/go/cmd/go/internal/generate/generate.go @@ -21,12 +21,13 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/load" + "cmd/go/internal/modload" "cmd/go/internal/work" ) var CmdGenerate = &base.Command{ Run: runGenerate, - UsageLine: "generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]", + UsageLine: "go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]", Short: "generate Go files by processing source", Long: ` Generate runs commands described by directives within existing @@ -47,6 +48,12 @@ that can be run locally. It must either be in the shell path (gofmt), a fully qualified path (/usr/you/bin/mytool), or a command alias, described below. +To convey to humans and machine tools that code is generated, +generated source should have a line early in the file that +matches the following regular expression (in Go syntax): + + ^// Code generated .* DO NOT EDIT\.$ + Note that go generate does not parse the file, so lines that look like directives in comments or multiline strings will be treated as directives. @@ -152,9 +159,28 @@ func runGenerate(cmd *base.Command, args []string) { } } // Even if the arguments are .go files, this loop suffices. + printed := false for _, pkg := range load.Packages(args) { + if modload.Enabled() && !pkg.Module.Main { + if !printed { + fmt.Fprintf(os.Stderr, "go: not generating in packages in dependency modules\n") + printed = true + } + continue + } + + pkgName := pkg.Name + for _, file := range pkg.InternalGoFiles() { - if !generate(pkg.Name, file) { + if !generate(pkgName, file) { + break + } + } + + pkgName += "_test" + + for _, file := range pkg.InternalXGoFiles() { + if !generate(pkgName, file) { break } } diff --git a/libgo/go/cmd/go/internal/get/discovery.go b/libgo/go/cmd/go/internal/get/discovery.go index 97aa1d7..6ba5c09 100644 --- a/libgo/go/cmd/go/internal/get/discovery.go +++ b/libgo/go/cmd/go/internal/get/discovery.go @@ -28,7 +28,7 @@ func charsetReader(charset string, input io.Reader) (io.Reader, error) { // parseMetaGoImports returns meta imports from the HTML in r. // Parsing ends at the end of the <head> section or the beginning of the <body>. -func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) { +func parseMetaGoImports(r io.Reader, mod ModuleMode) (imports []metaImport, err error) { d := xml.NewDecoder(r) d.CharsetReader = charsetReader d.Strict = false @@ -39,13 +39,13 @@ func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) { if err == io.EOF || len(imports) > 0 { err = nil } - return + break } if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { - return + break } if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { - return + break } e, ok := t.(xml.StartElement) if !ok || !strings.EqualFold(e.Name.Local, "meta") { @@ -55,13 +55,6 @@ func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) { continue } if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 { - // Ignore VCS type "mod", which is new Go modules. - // This code is for old go get and must ignore the new mod lines. - // Otherwise matchGoImport will complain about two - // different metaImport lines for the same Prefix. - if f[1] == "mod" { - continue - } imports = append(imports, metaImport{ Prefix: f[0], VCS: f[1], @@ -69,6 +62,27 @@ func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) { }) } } + + // Extract mod entries if we are paying attention to them. + var list []metaImport + var have map[string]bool + if mod == PreferMod { + have = make(map[string]bool) + for _, m := range imports { + if m.VCS == "mod" { + have[m.Prefix] = true + list = append(list, m) + } + } + } + + // Append non-mod entries, ignoring those superseded by a mod entry. + for _, m := range imports { + if m.VCS != "mod" && !have[m.Prefix] { + list = append(list, m) + } + } + return list, nil } // attrValue returns the attribute value for the case-insensitive key diff --git a/libgo/go/cmd/go/internal/get/get.go b/libgo/go/cmd/go/internal/get/get.go index 5bfeac3..e4148bc 100644 --- a/libgo/go/cmd/go/internal/get/get.go +++ b/libgo/go/cmd/go/internal/get/get.go @@ -16,13 +16,14 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/load" + "cmd/go/internal/search" "cmd/go/internal/str" "cmd/go/internal/web" "cmd/go/internal/work" ) var CmdGet = &base.Command{ - UsageLine: "get [-d] [-f] [-fix] [-insecure] [-t] [-u] [-v] [build flags] [packages]", + UsageLine: "go get [-d] [-f] [-t] [-u] [-v] [-fix] [-insecure] [build flags] [packages]", Short: "download and install packages and dependencies", Long: ` Get downloads the packages named by the import paths, along with their @@ -73,23 +74,56 @@ For more about specifying packages, see 'go help packages'. For more about how 'go get' finds source code to download, see 'go help importpath'. +This text describes the behavior of get when using GOPATH +to manage source code and dependencies. +If instead the go command is running in module-aware mode, +the details of get's flags and effects change, as does 'go help get'. +See 'go help modules' and 'go help module-get'. + See also: go build, go install, go clean. `, } -var getD = CmdGet.Flag.Bool("d", false, "") -var getF = CmdGet.Flag.Bool("f", false, "") -var getT = CmdGet.Flag.Bool("t", false, "") -var getU = CmdGet.Flag.Bool("u", false, "") -var getFix = CmdGet.Flag.Bool("fix", false, "") -var getInsecure = CmdGet.Flag.Bool("insecure", false, "") +var HelpGopathGet = &base.Command{ + UsageLine: "gopath-get", + Short: "legacy GOPATH go get", + Long: ` +The 'go get' command changes behavior depending on whether the +go command is running in module-aware mode or legacy GOPATH mode. +This help text, accessible as 'go help gopath-get' even in module-aware mode, +describes 'go get' as it operates in legacy GOPATH mode. + +Usage: ` + CmdGet.UsageLine + ` +` + CmdGet.Long, +} + +var ( + getD = CmdGet.Flag.Bool("d", false, "") + getF = CmdGet.Flag.Bool("f", false, "") + getT = CmdGet.Flag.Bool("t", false, "") + getU = CmdGet.Flag.Bool("u", false, "") + getFix = CmdGet.Flag.Bool("fix", false, "") + + Insecure bool +) func init() { work.AddBuildFlags(CmdGet) CmdGet.Run = runGet // break init loop + CmdGet.Flag.BoolVar(&Insecure, "insecure", Insecure, "") } func runGet(cmd *base.Command, args []string) { + if cfg.ModulesEnabled { + // Should not happen: main.go should install the separate module-enabled get code. + base.Fatalf("go get: modules not implemented") + } + if cfg.GoModInGOPATH != "" { + // Warn about not using modules with GO111MODULE=auto when go.mod exists. + // To silence the warning, users can set GO111MODULE=off. + fmt.Fprintf(os.Stderr, "go get: warning: modules disabled by GO111MODULE=auto in GOPATH/src;\n\tignoring %s;\n\tsee 'go help modules'\n", base.ShortPath(cfg.GoModInGOPATH)) + } + work.BuildInit() if *getF && !*getU { @@ -129,9 +163,8 @@ func runGet(cmd *base.Command, args []string) { if *getT { mode |= load.GetTestDeps } - args = downloadPaths(args) - for _, arg := range args { - download(arg, nil, &stk, mode) + for _, pkg := range downloadPaths(args) { + download(pkg, nil, &stk, mode) } base.ExitIfErrors() @@ -150,8 +183,7 @@ func runGet(cmd *base.Command, args []string) { // This leads to duplicated loads of the standard packages. load.ClearCmdCache() - args = load.ImportPaths(args) - load.PackagesForBuild(args) + pkgs := load.PackagesForBuild(args) // Phase 3. Install. if *getD { @@ -161,7 +193,7 @@ func runGet(cmd *base.Command, args []string) { return } - work.InstallPackages(args, true) + work.InstallPackages(args, pkgs) } // downloadPaths prepares the list of paths to pass to download. @@ -169,28 +201,21 @@ func runGet(cmd *base.Command, args []string) { // for a particular pattern, downloadPaths leaves it in the result list, // in the hope that we can figure out the repository from the // initial ...-free prefix. -func downloadPaths(args []string) []string { - args = load.ImportPathsNoDotExpansion(args) - var out []string - for _, a := range args { - if strings.Contains(a, "...") { - var expand []string - // Use matchPackagesInFS to avoid printing - // warnings. They will be printed by the - // eventual call to importPaths instead. - if build.IsLocalImport(a) { - expand = load.MatchPackagesInFS(a) - } else { - expand = load.MatchPackages(a) - } - if len(expand) > 0 { - out = append(out, expand...) - continue - } +func downloadPaths(patterns []string) []string { + for _, arg := range patterns { + if strings.Contains(arg, "@") { + base.Fatalf("go: cannot use path@version syntax in GOPATH mode") + } + } + var pkgs []string + for _, m := range search.ImportPathsQuiet(patterns) { + if len(m.Pkgs) == 0 && strings.Contains(m.Pattern, "...") { + pkgs = append(pkgs, m.Pattern) + } else { + pkgs = append(pkgs, m.Pkgs...) } - out = append(out, a) } - return out + return pkgs } // downloadCache records the import paths we have already @@ -215,7 +240,7 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) } load1 := func(path string, mode int) *load.Package { if parent == nil { - return load.LoadPackage(path, stk) + return load.LoadPackageNoFlags(path, stk) } return load.LoadImport(path, parent.Dir, parent, stk, nil, mode|load.ResolveModule) } @@ -271,9 +296,9 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) // for p has been replaced in the package cache. if wildcardOkay && strings.Contains(arg, "...") { if build.IsLocalImport(arg) { - args = load.MatchPackagesInFS(arg) + args = search.MatchPackagesInFS(arg).Pkgs } else { - args = load.MatchPackages(arg) + args = search.MatchPackages(arg).Pkgs } isWildcard = true } @@ -304,7 +329,7 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) base.Run(cfg.BuildToolexec, str.StringList(base.Tool("fix"), files)) // The imports might have changed, so reload again. - p = load.ReloadPackage(arg, stk) + p = load.ReloadPackageNoFlags(arg, stk) if p.Error != nil { base.Errorf("%s", p.Error) return @@ -369,10 +394,11 @@ func downloadPackage(p *load.Package) error { vcs *vcsCmd repo, rootPath string err error + blindRepo bool // set if the repo has unusual configuration ) security := web.Secure - if *getInsecure { + if Insecure { security = web.Insecure } @@ -389,20 +415,22 @@ func downloadPackage(p *load.Package) error { dir := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath)) remote, err := vcs.remoteRepo(vcs, dir) if err != nil { - return err + // Proceed anyway. The package is present; we likely just don't understand + // the repo configuration (e.g. unusual remote protocol). + blindRepo = true } repo = remote - if !*getF { - if rr, err := repoRootForImportPath(p.ImportPath, security); err == nil { - repo := rr.repo + if !*getF && err == nil { + if rr, err := RepoRootForImportPath(p.ImportPath, IgnoreMod, security); err == nil { + repo := rr.Repo if rr.vcs.resolveRepo != nil { resolved, err := rr.vcs.resolveRepo(rr.vcs, dir, repo) if err == nil { repo = resolved } } - if remote != repo && rr.isCustom { - return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.root, repo, dir, remote) + if remote != repo && rr.IsCustom { + return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.Root, repo, dir, remote) } } } @@ -410,13 +438,13 @@ func downloadPackage(p *load.Package) error { } else { // Analyze the import path to determine the version control system, // repository, and the import path for the root of the repository. - rr, err := repoRootForImportPath(p.ImportPath, security) + rr, err := RepoRootForImportPath(p.ImportPath, IgnoreMod, security) if err != nil { return err } - vcs, repo, rootPath = rr.vcs, rr.repo, rr.root + vcs, repo, rootPath = rr.vcs, rr.Repo, rr.Root } - if !vcs.isSecure(repo) && !*getInsecure { + if !blindRepo && !vcs.isSecure(repo) && !Insecure { return fmt.Errorf("cannot download, %v uses insecure protocol", repo) } diff --git a/libgo/go/cmd/go/internal/get/pkg_test.go b/libgo/go/cmd/go/internal/get/pkg_test.go index 1179d86..fc6a179 100644 --- a/libgo/go/cmd/go/internal/get/pkg_test.go +++ b/libgo/go/cmd/go/internal/get/pkg_test.go @@ -33,15 +33,18 @@ func TestFoldDup(t *testing.T) { var parseMetaGoImportsTests = []struct { in string + mod ModuleMode out []metaImport }{ { `<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`, + IgnoreMod, []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, }, { `<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar"> <meta name="go-import" content="baz/quux git http://github.com/rsc/baz/quux">`, + IgnoreMod, []metaImport{ {"foo/bar", "git", "https://github.com/rsc/foo/bar"}, {"baz/quux", "git", "http://github.com/rsc/baz/quux"}, @@ -50,6 +53,7 @@ var parseMetaGoImportsTests = []struct { { `<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar"> <meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">`, + IgnoreMod, []metaImport{ {"foo/bar", "git", "https://github.com/rsc/foo/bar"}, }, @@ -57,35 +61,65 @@ var parseMetaGoImportsTests = []struct { { `<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux"> <meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`, + IgnoreMod, []metaImport{ {"foo/bar", "git", "https://github.com/rsc/foo/bar"}, }, }, { + `<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux"> + <meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`, + PreferMod, + []metaImport{ + {"foo/bar", "mod", "http://github.com/rsc/baz/quux"}, + }, + }, + { `<head> <meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar"> </head>`, + IgnoreMod, []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, }, { `<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar"> <body>`, + IgnoreMod, []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, }, { `<!doctype html><meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`, + IgnoreMod, []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}}, }, { // XML doesn't like <div style=position:relative>. `<!doctype html><title>Page Not Found</title><meta name=go-import content="chitin.io/chitin git https://github.com/chitin-io/chitin"><div style=position:relative>DRAFT</div>`, + IgnoreMod, []metaImport{{"chitin.io/chitin", "git", "https://github.com/chitin-io/chitin"}}, }, + { + `<meta name="go-import" content="myitcv.io git https://github.com/myitcv/x"> + <meta name="go-import" content="myitcv.io/blah2 mod https://raw.githubusercontent.com/myitcv/pubx/master"> + `, + IgnoreMod, + []metaImport{{"myitcv.io", "git", "https://github.com/myitcv/x"}}, + }, + { + `<meta name="go-import" content="myitcv.io git https://github.com/myitcv/x"> + <meta name="go-import" content="myitcv.io/blah2 mod https://raw.githubusercontent.com/myitcv/pubx/master"> + `, + PreferMod, + []metaImport{ + {"myitcv.io/blah2", "mod", "https://raw.githubusercontent.com/myitcv/pubx/master"}, + {"myitcv.io", "git", "https://github.com/myitcv/x"}, + }, + }, } func TestParseMetaGoImports(t *testing.T) { for i, tt := range parseMetaGoImportsTests { - out, err := parseMetaGoImports(strings.NewReader(tt.in)) + out, err := parseMetaGoImports(strings.NewReader(tt.in), tt.mod) if err != nil { t.Errorf("test#%d: %v", i, err) continue diff --git a/libgo/go/cmd/go/internal/get/vcs.go b/libgo/go/cmd/go/internal/get/vcs.go index 0b2a04e..5cd164f 100644 --- a/libgo/go/cmd/go/internal/get/vcs.go +++ b/libgo/go/cmd/go/internal/get/vcs.go @@ -5,7 +5,6 @@ package get import ( - "bytes" "encoding/json" "errors" "fmt" @@ -428,19 +427,18 @@ func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) fmt.Printf("cd %s\n", dir) fmt.Printf("%s %s\n", v.cmd, strings.Join(args, " ")) } - var buf bytes.Buffer - cmd.Stdout = &buf - cmd.Stderr = &buf - err = cmd.Run() - out := buf.Bytes() + out, err := cmd.Output() if err != nil { if verbose || cfg.BuildV { fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " ")) - os.Stderr.Write(out) + if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 { + os.Stderr.Write(ee.Stderr) + } else { + fmt.Fprintf(os.Stderr, err.Error()) + } } - return out, err } - return out, nil + return out, err } // ping pings to determine scheme to use. @@ -624,27 +622,29 @@ func checkNestedVCS(vcs *vcsCmd, dir, srcRoot string) error { return nil } -// repoRoot represents a version control system, a repo, and a root of -// where to put it on disk. -type repoRoot struct { - vcs *vcsCmd - - // repo is the repository URL, including scheme - repo string +// RepoRoot describes the repository root for a tree of source code. +type RepoRoot struct { + Repo string // repository URL, including scheme + Root string // import path corresponding to root of repo + IsCustom bool // defined by served <meta> tags (as opposed to hard-coded pattern) + VCS string // vcs type ("mod", "git", ...) - // root is the import path corresponding to the root of the - // repository - root string - - // isCustom is true for custom import paths (those defined by HTML meta tags) - isCustom bool + vcs *vcsCmd // internal: vcs command access } var httpPrefixRE = regexp.MustCompile(`^https?:`) -// repoRootForImportPath analyzes importPath to determine the +// ModuleMode specifies whether to prefer modules when looking up code sources. +type ModuleMode int + +const ( + IgnoreMod ModuleMode = iota + PreferMod +) + +// RepoRootForImportPath analyzes importPath to determine the // version control system, and code repository to use. -func repoRootForImportPath(importPath string, security web.SecurityMode) (*repoRoot, error) { +func RepoRootForImportPath(importPath string, mod ModuleMode, security web.SecurityMode) (*RepoRoot, error) { rr, err := repoRootFromVCSPaths(importPath, "", security, vcsPaths) if err == errUnknownSite { // If there are wildcards, look up the thing before the wildcard, @@ -654,7 +654,7 @@ func repoRootForImportPath(importPath string, security web.SecurityMode) (*repoR if i := strings.Index(lookup, "/.../"); i >= 0 { lookup = lookup[:i] } - rr, err = repoRootForImportDynamic(lookup, security) + rr, err = repoRootForImportDynamic(lookup, mod, security) if err != nil { err = fmt.Errorf("unrecognized import path %q (%v)", importPath, err) } @@ -667,7 +667,7 @@ func repoRootForImportPath(importPath string, security web.SecurityMode) (*repoR } } - if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.root, "...") { + if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.Root, "...") { // Do not allow wildcards in the repo root. rr = nil err = fmt.Errorf("cannot expand ... in %q", importPath) @@ -680,7 +680,7 @@ var errUnknownSite = errors.New("dynamic lookup required to find mapping") // repoRootFromVCSPaths attempts to map importPath to a repoRoot // using the mappings defined in vcsPaths. // If scheme is non-empty, that scheme is forced. -func repoRootFromVCSPaths(importPath, scheme string, security web.SecurityMode, vcsPaths []*vcsPath) (*repoRoot, error) { +func repoRootFromVCSPaths(importPath, scheme string, security web.SecurityMode, vcsPaths []*vcsPath) (*RepoRoot, error) { // A common error is to use https://packagepath because that's what // hg and git require. Diagnose this helpfully. if loc := httpPrefixRE.FindStringIndex(importPath); loc != nil { @@ -733,28 +733,32 @@ func repoRootFromVCSPaths(importPath, scheme string, security web.SecurityMode, if security == web.Secure && !vcs.isSecureScheme(scheme) { continue } - if vcs.ping(scheme, match["repo"]) == nil { + if vcs.pingCmd != "" && vcs.ping(scheme, match["repo"]) == nil { match["repo"] = scheme + "://" + match["repo"] - break + goto Found } } + // No scheme found. Fall back to the first one. + match["repo"] = vcs.scheme[0] + "://" + match["repo"] + Found: } } - rr := &repoRoot{ + rr := &RepoRoot{ + Repo: match["repo"], + Root: match["root"], + VCS: vcs.cmd, vcs: vcs, - repo: match["repo"], - root: match["root"], } return rr, nil } return nil, errUnknownSite } -// repoRootForImportDynamic finds a *repoRoot for a custom domain that's not +// repoRootForImportDynamic finds a *RepoRoot for a custom domain that's not // statically known by repoRootForImportPathStatic. // // This handles custom import paths like "name.tld/pkg/foo" or just "name.tld". -func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*repoRoot, error) { +func repoRootForImportDynamic(importPath string, mod ModuleMode, security web.SecurityMode) (*RepoRoot, error) { slash := strings.Index(importPath, "/") if slash < 0 { slash = len(importPath) @@ -772,7 +776,7 @@ func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*re return nil, fmt.Errorf(msg, err) } defer body.Close() - imports, err := parseMetaGoImports(body) + imports, err := parseMetaGoImports(body, mod) if err != nil { return nil, fmt.Errorf("parsing %s: %v", importPath, err) } @@ -799,7 +803,7 @@ func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*re } urlStr0 := urlStr var imports []metaImport - urlStr, imports, err = metaImportsForPrefix(mmi.Prefix, security) + urlStr, imports, err = metaImportsForPrefix(mmi.Prefix, mod, security) if err != nil { return nil, err } @@ -809,48 +813,34 @@ func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*re } } - if err := validateRepoRootScheme(mmi.RepoRoot); err != nil { + if err := validateRepoRoot(mmi.RepoRoot); err != nil { return nil, fmt.Errorf("%s: invalid repo root %q: %v", urlStr, mmi.RepoRoot, err) } - rr := &repoRoot{ - vcs: vcsByCmd(mmi.VCS), - repo: mmi.RepoRoot, - root: mmi.Prefix, - isCustom: true, - } - if rr.vcs == nil { + vcs := vcsByCmd(mmi.VCS) + if vcs == nil && mmi.VCS != "mod" { return nil, fmt.Errorf("%s: unknown vcs %q", urlStr, mmi.VCS) } + + rr := &RepoRoot{ + Repo: mmi.RepoRoot, + Root: mmi.Prefix, + IsCustom: true, + VCS: mmi.VCS, + vcs: vcs, + } return rr, nil } -// validateRepoRootScheme returns an error if repoRoot does not seem -// to have a valid URL scheme. At this point we permit things that -// aren't valid URLs, although later, if not using -insecure, we will -// restrict repoRoots to be valid URLs. This is only because we've -// historically permitted them, and people may depend on that. -func validateRepoRootScheme(repoRoot string) error { - end := strings.Index(repoRoot, "://") - if end <= 0 { - return errors.New("no scheme") +// validateRepoRoot returns an error if repoRoot does not seem to be +// a valid URL with scheme. +func validateRepoRoot(repoRoot string) error { + url, err := url.Parse(repoRoot) + if err != nil { + return err } - - // RFC 3986 section 3.1. - for i := 0; i < end; i++ { - c := repoRoot[i] - switch { - case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z': - // OK. - case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.': - // OK except at start. - if i == 0 { - return errors.New("invalid scheme") - } - default: - return errors.New("invalid scheme") - } + if url.Scheme == "" { + return errors.New("no scheme") } - return nil } @@ -868,7 +858,7 @@ var ( // It is an error if no imports are found. // urlStr will still be valid if err != nil. // The returned urlStr will be of the form "https://golang.org/x/tools?go-get=1" -func metaImportsForPrefix(importPrefix string, security web.SecurityMode) (urlStr string, imports []metaImport, err error) { +func metaImportsForPrefix(importPrefix string, mod ModuleMode, security web.SecurityMode) (urlStr string, imports []metaImport, err error) { setCache := func(res fetchResult) (fetchResult, error) { fetchCacheMu.Lock() defer fetchCacheMu.Unlock() @@ -888,7 +878,7 @@ func metaImportsForPrefix(importPrefix string, security web.SecurityMode) (urlSt if err != nil { return setCache(fetchResult{urlStr: urlStr, err: fmt.Errorf("fetch %s: %v", urlStr, err)}) } - imports, err := parseMetaGoImports(body) + imports, err := parseMetaGoImports(body, mod) if err != nil { return setCache(fetchResult{urlStr: urlStr, err: fmt.Errorf("parsing %s: %v", urlStr, err)}) } @@ -956,7 +946,13 @@ func matchGoImport(imports []metaImport, importPath string) (metaImport, error) continue } - if match != -1 { + if match >= 0 { + if imports[match].VCS == "mod" && im.VCS != "mod" { + // All the mod entries precede all the non-mod entries. + // We have a mod entry and don't care about the rest, + // matching or not. + break + } return metaImport{}, fmt.Errorf("multiple meta tags match import path %q", importPath) } match = i @@ -1001,7 +997,7 @@ var vcsPaths = []*vcsPath{ // IBM DevOps Services (JazzHub) { prefix: "hub.jazz.net/git/", - re: `^(?P<root>hub.jazz.net/git/[a-z0-9]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`, + re: `^(?P<root>hub\.jazz\.net/git/[a-z0-9]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`, vcs: "git", repo: "https://{root}", check: noVCSSuffix, @@ -1010,7 +1006,7 @@ var vcsPaths = []*vcsPath{ // Git at Apache { prefix: "git.apache.org/", - re: `^(?P<root>git.apache.org/[a-z0-9_.\-]+\.git)(/[A-Za-z0-9_.\-]+)*$`, + re: `^(?P<root>git\.apache\.org/[a-z0-9_.\-]+\.git)(/[A-Za-z0-9_.\-]+)*$`, vcs: "git", repo: "https://{root}", }, diff --git a/libgo/go/cmd/go/internal/get/vcs_test.go b/libgo/go/cmd/go/internal/get/vcs_test.go index a6f8642..d13721b 100644 --- a/libgo/go/cmd/go/internal/get/vcs_test.go +++ b/libgo/go/cmd/go/internal/get/vcs_test.go @@ -16,43 +16,43 @@ import ( "cmd/go/internal/web" ) -// Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath. +// Test that RepoRootForImportPath determines the correct RepoRoot for a given importPath. // TODO(cmang): Add tests for SVN and BZR. func TestRepoRootForImportPath(t *testing.T) { testenv.MustHaveExternalNetwork(t) tests := []struct { path string - want *repoRoot + want *RepoRoot }{ { "github.com/golang/groupcache", - &repoRoot{ + &RepoRoot{ vcs: vcsGit, - repo: "https://github.com/golang/groupcache", + Repo: "https://github.com/golang/groupcache", }, }, // Unicode letters in directories (issue 18660). { "github.com/user/unicode/испытание", - &repoRoot{ + &RepoRoot{ vcs: vcsGit, - repo: "https://github.com/user/unicode", + Repo: "https://github.com/user/unicode", }, }, // IBM DevOps Services tests { "hub.jazz.net/git/user1/pkgname", - &repoRoot{ + &RepoRoot{ vcs: vcsGit, - repo: "https://hub.jazz.net/git/user1/pkgname", + Repo: "https://hub.jazz.net/git/user1/pkgname", }, }, { "hub.jazz.net/git/user1/pkgname/submodule/submodule/submodule", - &repoRoot{ + &RepoRoot{ vcs: vcsGit, - repo: "https://hub.jazz.net/git/user1/pkgname", + Repo: "https://hub.jazz.net/git/user1/pkgname", }, }, { @@ -60,6 +60,10 @@ func TestRepoRootForImportPath(t *testing.T) { nil, }, { + "hubajazz.net", + nil, + }, + { "hub2.jazz.net", nil, }, @@ -87,9 +91,9 @@ func TestRepoRootForImportPath(t *testing.T) { }, { "hub.jazz.net/git/user/pkg.name", - &repoRoot{ + &RepoRoot{ vcs: vcsGit, - repo: "https://hub.jazz.net/git/user/pkg.name", + Repo: "https://hub.jazz.net/git/user/pkg.name", }, }, // User names cannot have uppercase letters @@ -100,9 +104,9 @@ func TestRepoRootForImportPath(t *testing.T) { // OpenStack tests { "git.openstack.org/openstack/swift", - &repoRoot{ + &RepoRoot{ vcs: vcsGit, - repo: "https://git.openstack.org/openstack/swift", + Repo: "https://git.openstack.org/openstack/swift", }, }, // Trailing .git is less preferred but included for @@ -110,16 +114,16 @@ func TestRepoRootForImportPath(t *testing.T) { // be compilable on both old and new go { "git.openstack.org/openstack/swift.git", - &repoRoot{ + &RepoRoot{ vcs: vcsGit, - repo: "https://git.openstack.org/openstack/swift.git", + Repo: "https://git.openstack.org/openstack/swift.git", }, }, { "git.openstack.org/openstack/swift/go/hummingbird", - &repoRoot{ + &RepoRoot{ vcs: vcsGit, - repo: "https://git.openstack.org/openstack/swift", + Repo: "https://git.openstack.org/openstack/swift", }, }, { @@ -141,24 +145,28 @@ func TestRepoRootForImportPath(t *testing.T) { nil, }, { + "gitbapache.org", + nil, + }, + { "git.apache.org/package-name.git", - &repoRoot{ + &RepoRoot{ vcs: vcsGit, - repo: "https://git.apache.org/package-name.git", + Repo: "https://git.apache.org/package-name.git", }, }, { "git.apache.org/package-name_2.x.git/path/to/lib", - &repoRoot{ + &RepoRoot{ vcs: vcsGit, - repo: "https://git.apache.org/package-name_2.x.git", + Repo: "https://git.apache.org/package-name_2.x.git", }, }, { "chiselapp.com/user/kyle/repository/fossilgg", - &repoRoot{ + &RepoRoot{ vcs: vcsFossil, - repo: "https://chiselapp.com/user/kyle/repository/fossilgg", + Repo: "https://chiselapp.com/user/kyle/repository/fossilgg", }, }, { @@ -173,21 +181,21 @@ func TestRepoRootForImportPath(t *testing.T) { } for _, test := range tests { - got, err := repoRootForImportPath(test.path, web.Secure) + got, err := RepoRootForImportPath(test.path, IgnoreMod, web.Secure) want := test.want if want == nil { if err == nil { - t.Errorf("repoRootForImportPath(%q): Error expected but not received", test.path) + t.Errorf("RepoRootForImportPath(%q): Error expected but not received", test.path) } continue } if err != nil { - t.Errorf("repoRootForImportPath(%q): %v", test.path, err) + t.Errorf("RepoRootForImportPath(%q): %v", test.path, err) continue } - if got.vcs.name != want.vcs.name || got.repo != want.repo { - t.Errorf("repoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.vcs, got.repo, want.vcs, want.repo) + if got.vcs.name != want.vcs.name || got.Repo != want.Repo { + t.Errorf("RepoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.vcs, got.Repo, want.vcs, want.Repo) } } } @@ -219,18 +227,18 @@ func TestFromDir(t *testing.T) { f.Close() } - want := repoRoot{ + want := RepoRoot{ vcs: vcs, - root: path.Join("example.com", vcs.name), + Root: path.Join("example.com", vcs.name), } - var got repoRoot - got.vcs, got.root, err = vcsFromDir(dir, tempDir) + var got RepoRoot + got.vcs, got.Root, err = vcsFromDir(dir, tempDir) if err != nil { t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err) continue } - if got.vcs.name != want.vcs.name || got.root != want.root { - t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.vcs, got.root, want.vcs, want.root) + if got.vcs.name != want.vcs.name || got.Root != want.Root { + t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.vcs, got.Root, want.vcs, want.Root) } } } @@ -393,6 +401,22 @@ func TestMatchGoImport(t *testing.T) { path: "different.example.com/user/foo", err: errors.New("meta tags do not match import path"), }, + { + imports: []metaImport{ + {Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"}, + {Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"}, + }, + path: "myitcv.io/blah2/foo", + mi: metaImport{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"}, + }, + { + imports: []metaImport{ + {Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"}, + {Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"}, + }, + path: "myitcv.io/other", + mi: metaImport{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"}, + }, } for _, test := range tests { @@ -409,45 +433,46 @@ func TestMatchGoImport(t *testing.T) { } } -func TestValidateRepoRootScheme(t *testing.T) { +func TestValidateRepoRoot(t *testing.T) { tests := []struct { root string - err string + ok bool }{ { root: "", - err: "no scheme", + ok: false, }, { root: "http://", - err: "", + ok: true, }, { - root: "a://", - err: "", + root: "git+ssh://", + ok: true, }, { - root: "a#://", - err: "invalid scheme", + root: "http#://", + ok: false, + }, + { + root: "-config", + ok: false, }, { root: "-config://", - err: "invalid scheme", + ok: false, }, } for _, test := range tests { - err := validateRepoRootScheme(test.root) - if err == nil { - if test.err != "" { - t.Errorf("validateRepoRootScheme(%q) = nil, want %q", test.root, test.err) - } - } else if test.err == "" { - if err != nil { - t.Errorf("validateRepoRootScheme(%q) = %q, want nil", test.root, test.err) + err := validateRepoRoot(test.root) + ok := err == nil + if ok != test.ok { + want := "error" + if test.ok { + want = "nil" } - } else if err.Error() != test.err { - t.Errorf("validateRepoRootScheme(%q) = %q, want %q", test.root, err, test.err) + t.Errorf("validateRepoRoot(%q) = %q, want %s", test.root, err, want) } } } diff --git a/libgo/go/cmd/go/internal/help/help.go b/libgo/go/cmd/go/internal/help/help.go index b4c5217..a80afe3 100644 --- a/libgo/go/cmd/go/internal/help/help.go +++ b/libgo/go/cmd/go/internal/help/help.go @@ -21,82 +21,95 @@ import ( // Help implements the 'help' command. func Help(args []string) { - if len(args) == 0 { - PrintUsage(os.Stdout) - // not exit 2: succeeded at 'go help'. - return - } - if len(args) != 1 { - fmt.Fprintf(os.Stderr, "usage: go help command\n\nToo many arguments given.\n") - os.Exit(2) // failed at 'go help' - } - - arg := args[0] - // 'go help documentation' generates doc.go. - if arg == "documentation" { + if len(args) == 1 && args[0] == "documentation" { fmt.Println("// Copyright 2011 The Go Authors. All rights reserved.") fmt.Println("// Use of this source code is governed by a BSD-style") fmt.Println("// license that can be found in the LICENSE file.") fmt.Println() - fmt.Println("// DO NOT EDIT THIS FILE. GENERATED BY mkalldocs.sh.") + fmt.Println("// Code generated by mkalldocs.sh; DO NOT EDIT.") fmt.Println("// Edit the documentation in other files and rerun mkalldocs.sh to generate this one.") fmt.Println() buf := new(bytes.Buffer) - PrintUsage(buf) + PrintUsage(buf, base.Go) usage := &base.Command{Long: buf.String()} - tmpl(&commentWriter{W: os.Stdout}, documentationTemplate, append([]*base.Command{usage}, base.Commands...)) + cmds := []*base.Command{usage} + for _, cmd := range base.Go.Commands { + if cmd.UsageLine == "gopath-get" { + // Avoid duplication of the "get" documentation. + continue + } + cmds = append(cmds, cmd) + cmds = append(cmds, cmd.Commands...) + } + tmpl(&commentWriter{W: os.Stdout}, documentationTemplate, cmds) fmt.Println("package main") return } - for _, cmd := range base.Commands { - if cmd.Name() == arg { - tmpl(os.Stdout, helpTemplate, cmd) - // not exit 2: succeeded at 'go help cmd'. - return + cmd := base.Go +Args: + for i, arg := range args { + for _, sub := range cmd.Commands { + if sub.Name() == arg { + cmd = sub + continue Args + } + } + + // helpSuccess is the help command using as many args as possible that would succeed. + helpSuccess := "go help" + if i > 0 { + helpSuccess = " " + strings.Join(args[:i], " ") } + fmt.Fprintf(os.Stderr, "go help %s: unknown help topic. Run '%s'.\n", strings.Join(args, " "), helpSuccess) + os.Exit(2) // failed at 'go help cmd' } - fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'go help'.\n", arg) - os.Exit(2) // failed at 'go help cmd' + if len(cmd.Commands) > 0 { + PrintUsage(os.Stdout, cmd) + } else { + tmpl(os.Stdout, helpTemplate, cmd) + } + // not exit 2: succeeded at 'go help cmd'. + return } -var usageTemplate = `Go is a tool for managing Go source code. +var usageTemplate = `{{.Long | trim}} Usage: - go command [arguments] + {{.UsageLine}} <command> [arguments] The commands are: -{{range .}}{{if .Runnable}} +{{range .Commands}}{{if or (.Runnable) .Commands}} {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} -Use "go help [command]" for more information about a command. - +Use "go help{{with .LongName}} {{.}}{{end}} <command>" for more information about a command. +{{if eq (.UsageLine) "go"}} Additional help topics: -{{range .}}{{if not .Runnable}} +{{range .Commands}}{{if and (not .Runnable) (not .Commands)}} {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} -Use "go help [topic]" for more information about that topic. - +Use "go help{{with .LongName}} {{.}}{{end}} <topic>" for more information about that topic. +{{end}} ` -var helpTemplate = `{{if .Runnable}}usage: go {{.UsageLine}} +var helpTemplate = `{{if .Runnable}}usage: {{.UsageLine}} {{end}}{{.Long | trim}} ` var documentationTemplate = `{{range .}}{{if .Short}}{{.Short | capitalize}} -{{end}}{{if .Runnable}}Usage: +{{end}}{{if .Commands}}` + usageTemplate + `{{else}}{{if .Runnable}}Usage: - go {{.UsageLine}} + {{.UsageLine}} {{end}}{{.Long | trim}} -{{end}}` +{{end}}{{end}}` // commentWriter writes a Go comment to the underlying io.Writer, // using line comment form (//). @@ -171,8 +184,8 @@ func capitalize(s string) string { return string(unicode.ToTitle(r)) + s[n:] } -func PrintUsage(w io.Writer) { +func PrintUsage(w io.Writer, cmd *base.Command) { bw := bufio.NewWriter(w) - tmpl(bw, usageTemplate, base.Commands) + tmpl(bw, usageTemplate, cmd) bw.Flush() } diff --git a/libgo/go/cmd/go/internal/help/helpdoc.go b/libgo/go/cmd/go/internal/help/helpdoc.go index 6aa449a..aff4ce1 100644 --- a/libgo/go/cmd/go/internal/help/helpdoc.go +++ b/libgo/go/cmd/go/internal/help/helpdoc.go @@ -30,7 +30,7 @@ the C or C++ compiler, respectively, to use. var HelpPackages = &base.Command{ UsageLine: "packages", - Short: "package lists", + Short: "package lists and patterns", Long: ` Many commands apply to a set of packages: @@ -54,9 +54,11 @@ for packages to be built with the go tool: - "main" denotes the top-level package in a stand-alone executable. -- "all" expands to all package directories found in all the GOPATH +- "all" expands to all packages found in all the GOPATH trees. For example, 'go list all' lists all the packages on the local -system. +system. When using modules, "all" expands to all packages in +the main module and their dependencies, including dependencies +needed by tests of any of those. - "std" is like all but expands to just the packages in the standard Go library. @@ -193,6 +195,7 @@ using the named version control system, and then the path inside that repository. The supported version control systems are: Bazaar .bzr + Fossil .fossil Git .git Mercurial .hg Subversion .svn @@ -236,7 +239,7 @@ The meta tag should appear as early in the file as possible. In particular, it should appear before any raw JavaScript or CSS, to avoid confusing the go command's restricted parser. -The vcs is one of "git", "hg", "svn", etc, +The vcs is one of "bzr", "fossil", "git", "hg", "svn". The repo-root is the root of the version control system containing a scheme and not containing a .vcs qualifier. @@ -258,12 +261,22 @@ the go tool will verify that https://example.org/?go-get=1 contains the same meta tag and then git clone https://code.org/r/p/exproj into GOPATH/src/example.org. -New downloaded packages are written to the first directory listed in the GOPATH -environment variable (For more details see: 'go help gopath'). +When using GOPATH, downloaded packages are written to the first directory +listed in the GOPATH environment variable. +(See 'go help gopath-get' and 'go help gopath'.) + +When using modules, downloaded packages are stored in the module cache. +(See 'go help modules-get' and 'go help goproxy'.) + +When using modules, an additional variant of the go-import meta tag is +recognized and is preferred over those listing version control systems. +That variant uses "mod" as the vcs in the content value, as in: -The go command attempts to download the version of the -package appropriate for the Go release being used. -Run 'go help get' for more. + <meta name="go-import" content="example.org mod https://code.org/moduleproxy"> + +This tag means to fetch modules with paths beginning with example.org +from the module proxy available at the URL https://code.org/moduleproxy. +See 'go help goproxy' for details about the proxy protocol. Import path checking @@ -286,6 +299,9 @@ Import path checking is disabled for code found within vendor trees. This makes it possible to copy code into alternate locations in vendor trees without needing to update import comments. +Import path checking is also disabled when using modules. +Import path comments are obsoleted by the go.mod file's module statement. + See https://golang.org/s/go14customimport for details. `, } @@ -358,6 +374,12 @@ in the list. See https://golang.org/doc/code.html for an example. +GOPATH and Modules + +When using modules, GOPATH is no longer used for resolving imports. +However, it is still used to store downloaded source code (in GOPATH/pkg/mod) +and compiled commands (in GOPATH/bin). + Internal Directories Code in or below a directory named "internal" is importable only @@ -461,11 +483,21 @@ General-purpose environment variables: Examples are amd64, 386, arm, ppc64. GOBIN The directory where 'go install' will install a command. + GOCACHE + The directory where the go command will store cached + information for reuse in future builds. + GOFLAGS + A space-separated list of -flag=value settings to apply + to go commands by default, when the given flag is known by + the current command. Flags listed on the command-line + are applied after this list and therefore override it. GOOS The operating system for which to compile code. Examples are linux, darwin, windows, netbsd. GOPATH For more details see: 'go help gopath'. + GOPROXY + URL of Go module proxy. See 'go help goproxy'. GORACE Options for the race detector. See https://golang.org/doc/articles/race_detector.html. @@ -474,9 +506,6 @@ General-purpose environment variables: GOTMPDIR The directory where the go command will write temporary source files, packages, and binaries. - GOCACHE - The directory where the go command will store - cached information for reuse in future builds. Environment variables for use with cgo: @@ -523,6 +552,9 @@ Architecture-specific environment variables: GOMIPS For GOARCH=mips{,le}, whether to use floating point instructions. Valid values are hardfloat (default), softfloat. + GOMIPS64 + For GOARCH=mips64{,le}, whether to use floating point instructions. + Valid values are hardfloat (default), softfloat. Special-purpose environment variables: @@ -542,6 +574,20 @@ Special-purpose environment variables: Defined by Git. A colon-separated list of schemes that are allowed to be used with git fetch/clone. If set, any scheme not explicitly mentioned will be considered insecure by 'go get'. + +Additional information available from 'go env' but not read from the environment: + + GOEXE + The executable file name suffix (".exe" on Windows, "" on other systems). + GOHOSTARCH + The architecture (GOARCH) of the Go toolchain binaries. + GOHOSTOS + The operating system (GOOS) of the Go toolchain binaries. + GOMOD + The absolute path to the go.mod of the main module, + or the empty string if not using modules. + GOTOOLDIR + The directory where the go tools (compile, cover, doc, etc...) are installed. `, } @@ -651,6 +697,7 @@ The default location for cache data is a subdirectory named go-build in the standard user cache directory for the current operating system. Setting the GOCACHE environment variable overrides this default, and running 'go env GOCACHE' prints the current cache directory. +You can set the variable to 'off' to disable the cache. The go command periodically deletes cached data that has not been used recently. Running 'go clean -cache' deletes all cached data. diff --git a/libgo/go/cmd/go/internal/imports/build.go b/libgo/go/cmd/go/internal/imports/build.go new file mode 100644 index 0000000..d1adf94 --- /dev/null +++ b/libgo/go/cmd/go/internal/imports/build.go @@ -0,0 +1,211 @@ +// Copyright 2018 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. + +// Copied from Go distribution src/go/build/build.go, syslist.go + +package imports + +import ( + "bytes" + "strings" + "unicode" +) + +var slashslash = []byte("//") + +// ShouldBuild reports whether it is okay to use this file, +// The rule is that in the file's leading run of // comments +// and blank lines, which must be followed by a blank line +// (to avoid including a Go package clause doc comment), +// lines beginning with '// +build' are taken as build directives. +// +// The file is accepted only if each such line lists something +// matching the file. For example: +// +// // +build windows linux +// +// marks the file as applicable only on Windows and Linux. +// +// If tags["*"] is true, then ShouldBuild will consider every +// build tag except "ignore" to be both true and false for +// the purpose of satisfying build tags, in order to estimate +// (conservatively) whether a file could ever possibly be used +// in any build. +// +func ShouldBuild(content []byte, tags map[string]bool) bool { + // Pass 1. Identify leading run of // comments and blank lines, + // which must be followed by a blank line. + end := 0 + p := content + for len(p) > 0 { + line := p + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, p = line[:i], p[i+1:] + } else { + p = p[len(p):] + } + line = bytes.TrimSpace(line) + if len(line) == 0 { // Blank line + end = len(content) - len(p) + continue + } + if !bytes.HasPrefix(line, slashslash) { // Not comment line + break + } + } + content = content[:end] + + // Pass 2. Process each line in the run. + p = content + allok := true + for len(p) > 0 { + line := p + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, p = line[:i], p[i+1:] + } else { + p = p[len(p):] + } + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, slashslash) { + continue + } + line = bytes.TrimSpace(line[len(slashslash):]) + if len(line) > 0 && line[0] == '+' { + // Looks like a comment +line. + f := strings.Fields(string(line)) + if f[0] == "+build" { + ok := false + for _, tok := range f[1:] { + if matchTags(tok, tags) { + ok = true + } + } + if !ok { + allok = false + } + } + } + } + + return allok +} + +// matchTags reports whether the name is one of: +// +// tag (if tags[tag] is true) +// !tag (if tags[tag] is false) +// a comma-separated list of any of these +// +func matchTags(name string, tags map[string]bool) bool { + if name == "" { + return false + } + if i := strings.Index(name, ","); i >= 0 { + // comma-separated list + ok1 := matchTags(name[:i], tags) + ok2 := matchTags(name[i+1:], tags) + return ok1 && ok2 + } + if strings.HasPrefix(name, "!!") { // bad syntax, reject always + return false + } + if strings.HasPrefix(name, "!") { // negation + return len(name) > 1 && matchTag(name[1:], tags, false) + } + return matchTag(name, tags, true) +} + +// matchTag reports whether the tag name is valid and satisfied by tags[name]==want. +func matchTag(name string, tags map[string]bool, want bool) bool { + // Tags must be letters, digits, underscores or dots. + // Unlike in Go identifiers, all digits are fine (e.g., "386"). + for _, c := range name { + if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { + return false + } + } + + if tags["*"] && name != "" && name != "ignore" { + // Special case for gathering all possible imports: + // if we put * in the tags map then all tags + // except "ignore" are considered both present and not + // (so we return true no matter how 'want' is set). + return true + } + + have := tags[name] + if name == "linux" { + have = have || tags["android"] + } + return have == want +} + +// MatchFile returns false if the name contains a $GOOS or $GOARCH +// suffix which does not match the current system. +// The recognized name formats are: +// +// name_$(GOOS).* +// name_$(GOARCH).* +// name_$(GOOS)_$(GOARCH).* +// name_$(GOOS)_test.* +// name_$(GOARCH)_test.* +// name_$(GOOS)_$(GOARCH)_test.* +// +// An exception: if GOOS=android, then files with GOOS=linux are also matched. +// +// If tags["*"] is true, then MatchFile will consider all possible +// GOOS and GOARCH to be available and will consequently +// always return true. +func MatchFile(name string, tags map[string]bool) bool { + if tags["*"] { + return true + } + if dot := strings.Index(name, "."); dot != -1 { + name = name[:dot] + } + + // Before Go 1.4, a file called "linux.go" would be equivalent to having a + // build tag "linux" in that file. For Go 1.4 and beyond, we require this + // auto-tagging to apply only to files with a non-empty prefix, so + // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating + // systems, such as android, to arrive without breaking existing code with + // innocuous source code in "android.go". The easiest fix: cut everything + // in the name before the initial _. + i := strings.Index(name, "_") + if i < 0 { + return true + } + name = name[i:] // ignore everything before first _ + + l := strings.Split(name, "_") + if n := len(l); n > 0 && l[n-1] == "test" { + l = l[:n-1] + } + n := len(l) + if n >= 2 && KnownOS[l[n-2]] && KnownArch[l[n-1]] { + return tags[l[n-2]] && tags[l[n-1]] + } + if n >= 1 && KnownOS[l[n-1]] { + return tags[l[n-1]] + } + if n >= 1 && KnownArch[l[n-1]] { + return tags[l[n-1]] + } + return true +} + +var KnownOS = make(map[string]bool) +var KnownArch = make(map[string]bool) + +func init() { + for _, v := range strings.Fields(goosList) { + KnownOS[v] = true + } + for _, v := range strings.Fields(goarchList) { + KnownArch[v] = true + } +} + +const goosList = "android darwin dragonfly freebsd js linux nacl netbsd openbsd plan9 solaris windows zos " +const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc riscv riscv64 s390 s390x sparc sparc64 wasm " diff --git a/libgo/go/cmd/go/internal/imports/read.go b/libgo/go/cmd/go/internal/imports/read.go new file mode 100644 index 0000000..58c2abd --- /dev/null +++ b/libgo/go/cmd/go/internal/imports/read.go @@ -0,0 +1,249 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copied from Go distribution src/go/build/read.go. + +package imports + +import ( + "bufio" + "errors" + "io" + "unicode/utf8" +) + +type importReader struct { + b *bufio.Reader + buf []byte + peek byte + err error + eof bool + nerr int +} + +func isIdent(c byte) bool { + return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf +} + +var ( + errSyntax = errors.New("syntax error") + errNUL = errors.New("unexpected NUL in input") +) + +// syntaxError records a syntax error, but only if an I/O error has not already been recorded. +func (r *importReader) syntaxError() { + if r.err == nil { + r.err = errSyntax + } +} + +// readByte reads the next byte from the input, saves it in buf, and returns it. +// If an error occurs, readByte records the error in r.err and returns 0. +func (r *importReader) readByte() byte { + c, err := r.b.ReadByte() + if err == nil { + r.buf = append(r.buf, c) + if c == 0 { + err = errNUL + } + } + if err != nil { + if err == io.EOF { + r.eof = true + } else if r.err == nil { + r.err = err + } + c = 0 + } + return c +} + +// peekByte returns the next byte from the input reader but does not advance beyond it. +// If skipSpace is set, peekByte skips leading spaces and comments. +func (r *importReader) peekByte(skipSpace bool) byte { + if r.err != nil { + if r.nerr++; r.nerr > 10000 { + panic("go/build: import reader looping") + } + return 0 + } + + // Use r.peek as first input byte. + // Don't just return r.peek here: it might have been left by peekByte(false) + // and this might be peekByte(true). + c := r.peek + if c == 0 { + c = r.readByte() + } + for r.err == nil && !r.eof { + if skipSpace { + // For the purposes of this reader, semicolons are never necessary to + // understand the input and are treated as spaces. + switch c { + case ' ', '\f', '\t', '\r', '\n', ';': + c = r.readByte() + continue + + case '/': + c = r.readByte() + if c == '/' { + for c != '\n' && r.err == nil && !r.eof { + c = r.readByte() + } + } else if c == '*' { + var c1 byte + for (c != '*' || c1 != '/') && r.err == nil { + if r.eof { + r.syntaxError() + } + c, c1 = c1, r.readByte() + } + } else { + r.syntaxError() + } + c = r.readByte() + continue + } + } + break + } + r.peek = c + return r.peek +} + +// nextByte is like peekByte but advances beyond the returned byte. +func (r *importReader) nextByte(skipSpace bool) byte { + c := r.peekByte(skipSpace) + r.peek = 0 + return c +} + +// readKeyword reads the given keyword from the input. +// If the keyword is not present, readKeyword records a syntax error. +func (r *importReader) readKeyword(kw string) { + r.peekByte(true) + for i := 0; i < len(kw); i++ { + if r.nextByte(false) != kw[i] { + r.syntaxError() + return + } + } + if isIdent(r.peekByte(false)) { + r.syntaxError() + } +} + +// readIdent reads an identifier from the input. +// If an identifier is not present, readIdent records a syntax error. +func (r *importReader) readIdent() { + c := r.peekByte(true) + if !isIdent(c) { + r.syntaxError() + return + } + for isIdent(r.peekByte(false)) { + r.peek = 0 + } +} + +// readString reads a quoted string literal from the input. +// If an identifier is not present, readString records a syntax error. +func (r *importReader) readString(save *[]string) { + switch r.nextByte(true) { + case '`': + start := len(r.buf) - 1 + for r.err == nil { + if r.nextByte(false) == '`' { + if save != nil { + *save = append(*save, string(r.buf[start:])) + } + break + } + if r.eof { + r.syntaxError() + } + } + case '"': + start := len(r.buf) - 1 + for r.err == nil { + c := r.nextByte(false) + if c == '"' { + if save != nil { + *save = append(*save, string(r.buf[start:])) + } + break + } + if r.eof || c == '\n' { + r.syntaxError() + } + if c == '\\' { + r.nextByte(false) + } + } + default: + r.syntaxError() + } +} + +// readImport reads an import clause - optional identifier followed by quoted string - +// from the input. +func (r *importReader) readImport(imports *[]string) { + c := r.peekByte(true) + if c == '.' { + r.peek = 0 + } else if isIdent(c) { + r.readIdent() + } + r.readString(imports) +} + +// ReadComments is like ioutil.ReadAll, except that it only reads the leading +// block of comments in the file. +func ReadComments(f io.Reader) ([]byte, error) { + r := &importReader{b: bufio.NewReader(f)} + r.peekByte(true) + if r.err == nil && !r.eof { + // Didn't reach EOF, so must have found a non-space byte. Remove it. + r.buf = r.buf[:len(r.buf)-1] + } + return r.buf, r.err +} + +// ReadImports is like ioutil.ReadAll, except that it expects a Go file as input +// and stops reading the input once the imports have completed. +func ReadImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) { + r := &importReader{b: bufio.NewReader(f)} + + r.readKeyword("package") + r.readIdent() + for r.peekByte(true) == 'i' { + r.readKeyword("import") + if r.peekByte(true) == '(' { + r.nextByte(false) + for r.peekByte(true) != ')' && r.err == nil { + r.readImport(imports) + } + r.nextByte(false) + } else { + r.readImport(imports) + } + } + + // If we stopped successfully before EOF, we read a byte that told us we were done. + // Return all but that last byte, which would cause a syntax error if we let it through. + if r.err == nil && !r.eof { + return r.buf[:len(r.buf)-1], nil + } + + // If we stopped for a syntax error, consume the whole file so that + // we are sure we don't change the errors that go/parser returns. + if r.err == errSyntax && !reportSyntaxError { + r.err = nil + for r.err == nil && !r.eof { + r.readByte() + } + } + + return r.buf, r.err +} diff --git a/libgo/go/cmd/go/internal/imports/read_test.go b/libgo/go/cmd/go/internal/imports/read_test.go new file mode 100644 index 0000000..6ea356f --- /dev/null +++ b/libgo/go/cmd/go/internal/imports/read_test.go @@ -0,0 +1,228 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copied from Go distribution src/go/build/read.go. + +package imports + +import ( + "io" + "strings" + "testing" +) + +const quote = "`" + +type readTest struct { + // Test input contains ℙ where readImports should stop. + in string + err string +} + +var readImportsTests = []readTest{ + { + `package p`, + "", + }, + { + `package p; import "x"`, + "", + }, + { + `package p; import . "x"`, + "", + }, + { + `package p; import "x";ℙvar x = 1`, + "", + }, + { + `package p + + // comment + + import "x" + import _ "x" + import a "x" + + /* comment */ + + import ( + "x" /* comment */ + _ "x" + a "x" // comment + ` + quote + `x` + quote + ` + _ /*comment*/ ` + quote + `x` + quote + ` + a ` + quote + `x` + quote + ` + ) + import ( + ) + import () + import()import()import() + import();import();import() + + ℙvar x = 1 + `, + "", + }, +} + +var readCommentsTests = []readTest{ + { + `ℙpackage p`, + "", + }, + { + `ℙpackage p; import "x"`, + "", + }, + { + `ℙpackage p; import . "x"`, + "", + }, + { + `// foo + + /* bar */ + + /* quux */ // baz + + /*/ zot */ + + // asdf + ℙHello, world`, + "", + }, +} + +func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, error)) { + for i, tt := range tests { + var in, testOut string + j := strings.Index(tt.in, "ℙ") + if j < 0 { + in = tt.in + testOut = tt.in + } else { + in = tt.in[:j] + tt.in[j+len("ℙ"):] + testOut = tt.in[:j] + } + r := strings.NewReader(in) + buf, err := read(r) + if err != nil { + if tt.err == "" { + t.Errorf("#%d: err=%q, expected success (%q)", i, err, string(buf)) + continue + } + if !strings.Contains(err.Error(), tt.err) { + t.Errorf("#%d: err=%q, expected %q", i, err, tt.err) + continue + } + continue + } + if err == nil && tt.err != "" { + t.Errorf("#%d: success, expected %q", i, tt.err) + continue + } + + out := string(buf) + if out != testOut { + t.Errorf("#%d: wrong output:\nhave %q\nwant %q\n", i, out, testOut) + } + } +} + +func TestReadImports(t *testing.T) { + testRead(t, readImportsTests, func(r io.Reader) ([]byte, error) { return ReadImports(r, true, nil) }) +} + +func TestReadComments(t *testing.T) { + testRead(t, readCommentsTests, ReadComments) +} + +var readFailuresTests = []readTest{ + { + `package`, + "syntax error", + }, + { + "package p\n\x00\nimport `math`\n", + "unexpected NUL in input", + }, + { + `package p; import`, + "syntax error", + }, + { + `package p; import "`, + "syntax error", + }, + { + "package p; import ` \n\n", + "syntax error", + }, + { + `package p; import "x`, + "syntax error", + }, + { + `package p; import _`, + "syntax error", + }, + { + `package p; import _ "`, + "syntax error", + }, + { + `package p; import _ "x`, + "syntax error", + }, + { + `package p; import .`, + "syntax error", + }, + { + `package p; import . "`, + "syntax error", + }, + { + `package p; import . "x`, + "syntax error", + }, + { + `package p; import (`, + "syntax error", + }, + { + `package p; import ("`, + "syntax error", + }, + { + `package p; import ("x`, + "syntax error", + }, + { + `package p; import ("x"`, + "syntax error", + }, +} + +func TestReadFailures(t *testing.T) { + // Errors should be reported (true arg to readImports). + testRead(t, readFailuresTests, func(r io.Reader) ([]byte, error) { return ReadImports(r, true, nil) }) +} + +func TestReadFailuresIgnored(t *testing.T) { + // Syntax errors should not be reported (false arg to readImports). + // Instead, entire file should be the output and no error. + // Convert tests not to return syntax errors. + tests := make([]readTest, len(readFailuresTests)) + copy(tests, readFailuresTests) + for i := range tests { + tt := &tests[i] + if !strings.Contains(tt.err, "NUL") { + tt.err = "" + } + } + testRead(t, tests, func(r io.Reader) ([]byte, error) { return ReadImports(r, false, nil) }) +} diff --git a/libgo/go/cmd/go/internal/imports/scan.go b/libgo/go/cmd/go/internal/imports/scan.go new file mode 100644 index 0000000..d944e95 --- /dev/null +++ b/libgo/go/cmd/go/internal/imports/scan.go @@ -0,0 +1,96 @@ +// Copyright 2018 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 imports + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strconv" + "strings" +) + +func ScanDir(dir string, tags map[string]bool) ([]string, []string, error) { + infos, err := ioutil.ReadDir(dir) + if err != nil { + return nil, nil, err + } + var files []string + for _, info := range infos { + name := info.Name() + if info.Mode().IsRegular() && !strings.HasPrefix(name, "_") && strings.HasSuffix(name, ".go") && MatchFile(name, tags) { + files = append(files, filepath.Join(dir, name)) + } + } + return scanFiles(files, tags, false) +} + +func ScanFiles(files []string, tags map[string]bool) ([]string, []string, error) { + return scanFiles(files, tags, true) +} + +func scanFiles(files []string, tags map[string]bool, explicitFiles bool) ([]string, []string, error) { + imports := make(map[string]bool) + testImports := make(map[string]bool) + numFiles := 0 +Files: + for _, name := range files { + r, err := os.Open(name) + if err != nil { + return nil, nil, err + } + var list []string + data, err := ReadImports(r, false, &list) + r.Close() + if err != nil { + return nil, nil, fmt.Errorf("reading %s: %v", name, err) + } + + // import "C" is implicit requirement of cgo tag. + // When listing files on the command line (explicitFiles=true) + // we do not apply build tag filtering but we still do apply + // cgo filtering, so no explicitFiles check here. + // Why? Because we always have, and it's not worth breaking + // that behavior now. + for _, path := range list { + if path == `"C"` && !tags["cgo"] && !tags["*"] { + continue Files + } + } + + if !explicitFiles && !ShouldBuild(data, tags) { + continue + } + numFiles++ + m := imports + if strings.HasSuffix(name, "_test.go") { + m = testImports + } + for _, p := range list { + q, err := strconv.Unquote(p) + if err != nil { + continue + } + m[q] = true + } + } + if numFiles == 0 { + return nil, nil, ErrNoGo + } + return keys(imports), keys(testImports), nil +} + +var ErrNoGo = fmt.Errorf("no Go source files") + +func keys(m map[string]bool) []string { + var list []string + for k := range m { + list = append(list, k) + } + sort.Strings(list) + return list +} diff --git a/libgo/go/cmd/go/internal/imports/scan_test.go b/libgo/go/cmd/go/internal/imports/scan_test.go new file mode 100644 index 0000000..6a2ff62 --- /dev/null +++ b/libgo/go/cmd/go/internal/imports/scan_test.go @@ -0,0 +1,67 @@ +// Copyright 2018 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 imports + +import ( + "internal/testenv" + "path/filepath" + "reflect" + "runtime" + "testing" +) + +func TestScan(t *testing.T) { + testenv.MustHaveGoBuild(t) + + imports, testImports, err := ScanDir(filepath.Join(runtime.GOROOT(), "src/encoding/json"), Tags()) + if err != nil { + t.Fatal(err) + } + foundBase64 := false + for _, p := range imports { + if p == "encoding/base64" { + foundBase64 = true + } + if p == "encoding/binary" { + // A dependency but not an import + t.Errorf("json reported as importing encoding/binary but does not") + } + if p == "net/http" { + // A test import but not an import + t.Errorf("json reported as importing encoding/binary but does not") + } + } + if !foundBase64 { + t.Errorf("json missing import encoding/base64 (%q)", imports) + } + + foundHTTP := false + for _, p := range testImports { + if p == "net/http" { + foundHTTP = true + } + if p == "unicode/utf16" { + // A package import but not a test import + t.Errorf("json reported as test-importing unicode/utf16 but does not") + } + } + if !foundHTTP { + t.Errorf("json missing test import net/http (%q)", testImports) + } +} + +func TestScanStar(t *testing.T) { + testenv.MustHaveGoBuild(t) + + imports, _, err := ScanDir("testdata/import1", map[string]bool{"*": true}) + if err != nil { + t.Fatal(err) + } + + want := []string{"import1", "import2", "import3", "import4"} + if !reflect.DeepEqual(imports, want) { + t.Errorf("ScanDir testdata/import1:\nhave %v\nwant %v", imports, want) + } +} diff --git a/libgo/go/cmd/go/internal/imports/tags.go b/libgo/go/cmd/go/internal/imports/tags.go new file mode 100644 index 0000000..1c22a47 --- /dev/null +++ b/libgo/go/cmd/go/internal/imports/tags.go @@ -0,0 +1,34 @@ +// Copyright 2018 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 imports + +import "cmd/go/internal/cfg" + +var tags map[string]bool + +func Tags() map[string]bool { + if tags == nil { + tags = loadTags() + } + return tags +} + +func loadTags() map[string]bool { + tags := map[string]bool{ + cfg.BuildContext.GOOS: true, + cfg.BuildContext.GOARCH: true, + cfg.BuildContext.Compiler: true, + } + if cfg.BuildContext.CgoEnabled { + tags["cgo"] = true + } + for _, tag := range cfg.BuildContext.BuildTags { + tags[tag] = true + } + for _, tag := range cfg.BuildContext.ReleaseTags { + tags[tag] = true + } + return tags +} diff --git a/libgo/go/cmd/go/internal/imports/testdata/import1/x.go b/libgo/go/cmd/go/internal/imports/testdata/import1/x.go new file mode 100644 index 0000000..98f9191 --- /dev/null +++ b/libgo/go/cmd/go/internal/imports/testdata/import1/x.go @@ -0,0 +1,3 @@ +package x + +import "import1" diff --git a/libgo/go/cmd/go/internal/imports/testdata/import1/x1.go b/libgo/go/cmd/go/internal/imports/testdata/import1/x1.go new file mode 100644 index 0000000..6a9594a --- /dev/null +++ b/libgo/go/cmd/go/internal/imports/testdata/import1/x1.go @@ -0,0 +1,9 @@ +// +build blahblh +// +build linux +// +build !linux +// +build windows +// +build darwin + +package x + +import "import4" diff --git a/libgo/go/cmd/go/internal/imports/testdata/import1/x_darwin.go b/libgo/go/cmd/go/internal/imports/testdata/import1/x_darwin.go new file mode 100644 index 0000000..a0c3fdd --- /dev/null +++ b/libgo/go/cmd/go/internal/imports/testdata/import1/x_darwin.go @@ -0,0 +1,3 @@ +package xxxx + +import "import3" diff --git a/libgo/go/cmd/go/internal/imports/testdata/import1/x_windows.go b/libgo/go/cmd/go/internal/imports/testdata/import1/x_windows.go new file mode 100644 index 0000000..63c5082 --- /dev/null +++ b/libgo/go/cmd/go/internal/imports/testdata/import1/x_windows.go @@ -0,0 +1,3 @@ +package x + +import "import2" diff --git a/libgo/go/cmd/go/internal/list/list.go b/libgo/go/cmd/go/internal/list/list.go index 16e7f70..b3ba4ed 100644 --- a/libgo/go/cmd/go/internal/list/list.go +++ b/libgo/go/cmd/go/internal/list/list.go @@ -7,23 +7,33 @@ package list import ( "bufio" + "bytes" "encoding/json" "io" "os" + "sort" "strings" "text/template" "cmd/go/internal/base" + "cmd/go/internal/cache" "cmd/go/internal/cfg" "cmd/go/internal/load" + "cmd/go/internal/modload" + "cmd/go/internal/str" "cmd/go/internal/work" ) var CmdList = &base.Command{ - UsageLine: "list [-e] [-f format] [-json] [build flags] [packages]", - Short: "list packages", + // Note: -f -json -m are listed explicitly because they are the most common list flags. + // Do not send CLs removing them because they're covered by [list flags]. + UsageLine: "go list [-f format] [-json] [-m] [list flags] [build flags] [packages]", + Short: "list packages or modules", Long: ` -List lists the packages named by the import paths, one per line. +List lists the named packages, one per line. +The most commonly-used flags are -f and -json, which control the form +of the output printed for each package. Other list flags, documented below, +control more specific details. The default output shows the package import path: @@ -33,40 +43,46 @@ The default output shows the package import path: golang.org/x/net/html The -f flag specifies an alternate format for the list, using the -syntax of package template. The default output is equivalent to -f -'{{.ImportPath}}'. The struct being passed to the template is: +syntax of package template. The default output is equivalent +to -f '{{.ImportPath}}'. The struct being passed to the template is: type Package struct { - Dir string // directory containing package sources - ImportPath string // import path of package in dir - ImportComment string // path in import comment on package statement - Name string // package name - Doc string // package documentation string - Target string // install path - Shlib string // the shared library that contains this package (only set when -linkshared) - Goroot bool // is this package in the Go root? - Standard bool // is this package part of the standard Go library? - Stale bool // would 'go install' do anything for this package? - StaleReason string // explanation for Stale==true - Root string // Go root or Go path dir containing this package - ConflictDir string // this directory shadows Dir in $GOPATH - BinaryOnly bool // binary-only package: cannot be recompiled from sources + Dir string // directory containing package sources + ImportPath string // import path of package in dir + ImportComment string // path in import comment on package statement + Name string // package name + Doc string // package documentation string + Target string // install path + Shlib string // the shared library that contains this package (only set when -linkshared) + Goroot bool // is this package in the Go root? + Standard bool // is this package part of the standard Go library? + Stale bool // would 'go install' do anything for this package? + StaleReason string // explanation for Stale==true + Root string // Go root or Go path dir containing this package + ConflictDir string // this directory shadows Dir in $GOPATH + BinaryOnly bool // binary-only package: cannot be recompiled from sources + ForTest string // package is only for use in named test + Export string // file containing export data (when using -export) + Module *Module // info about package's containing module, if any (can be nil) + Match []string // command-line patterns matching this package + DepOnly bool // package is only a dependency, not explicitly listed // Source files - GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) - CgoFiles []string // .go sources files that import "C" - IgnoredGoFiles []string // .go sources ignored due to build constraints - CFiles []string // .c source files - CXXFiles []string // .cc, .cxx and .cpp source files - MFiles []string // .m source files - HFiles []string // .h, .hh, .hpp and .hxx source files - FFiles []string // .f, .F, .for and .f90 Fortran source files - SFiles []string // .s source files - SwigFiles []string // .swig files - SwigCXXFiles []string // .swigcxx files - SysoFiles []string // .syso object files to add to archive - TestGoFiles []string // _test.go files in package - XTestGoFiles []string // _test.go files outside package + GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string // .go source files that import "C" + CompiledGoFiles []string // .go files presented to compiler (when using -compiled) + IgnoredGoFiles []string // .go source files ignored due to build constraints + CFiles []string // .c source files + CXXFiles []string // .cc, .cxx and .cpp source files + MFiles []string // .m source files + HFiles []string // .h, .hh, .hpp and .hxx source files + FFiles []string // .f, .F, .for and .f90 Fortran source files + SFiles []string // .s source files + SwigFiles []string // .swig files + SwigCXXFiles []string // .swigcxx files + SysoFiles []string // .syso object files to add to archive + TestGoFiles []string // _test.go files in package + XTestGoFiles []string // _test.go files outside package // Cgo directives CgoCFLAGS []string // cgo: flags for C compiler @@ -77,10 +93,11 @@ syntax of package template. The default output is equivalent to -f CgoPkgConfig []string // cgo: pkg-config names // Dependency information - Imports []string // import paths used by this package - Deps []string // all (recursively) imported dependencies - TestImports []string // imports from TestGoFiles - XTestImports []string // imports from XTestGoFiles + Imports []string // import paths used by this package + ImportMap map[string]string // map from source import to ImportPath (identity entries omitted) + Deps []string // all (recursively) imported dependencies + TestImports []string // imports from TestGoFiles + XTestImports []string // imports from XTestGoFiles // Error information Incomplete bool // this package or a dependency has an error @@ -92,7 +109,7 @@ Packages stored in vendor directories report an ImportPath that includes the path to the vendor directory (for example, "d/vendor/p" instead of "p"), so that the ImportPath uniquely identifies a given copy of a package. The Imports, Deps, TestImports, and XTestImports lists also contain these -expanded imports paths. See golang.org/s/go15vendor for more about vendoring. +expanded import paths. See golang.org/s/go15vendor for more about vendoring. The error information, if any, is @@ -102,22 +119,25 @@ The error information, if any, is Err string // the error itself } +The module information is a Module struct, defined in the discussion +of list -m below. + The template function "join" calls strings.Join. The template function "context" returns the build context, defined as: - type Context struct { - GOARCH string // target architecture - GOOS string // target operating system - GOROOT string // Go root - GOPATH string // Go path - CgoEnabled bool // whether cgo can be used - UseAllFiles bool // use files regardless of +build lines, file names - Compiler string // compiler to assume when computing target paths - BuildTags []string // build constraints to match in +build lines - ReleaseTags []string // releases the current release is compatible with - InstallSuffix string // suffix to use in the name of the install dir - } + type Context struct { + GOARCH string // target architecture + GOOS string // target operating system + GOROOT string // Go root + GOPATH string // Go path + CgoEnabled bool // whether cgo can be used + UseAllFiles bool // use files regardless of +build lines, file names + Compiler string // compiler to assume when computing target paths + BuildTags []string // build constraints to match in +build lines + ReleaseTags []string // releases the current release is compatible with + InstallSuffix string // suffix to use in the name of the install dir + } For more information about the meaning of these fields see the documentation for the go/build package's Context type. @@ -125,6 +145,18 @@ for the go/build package's Context type. The -json flag causes the package data to be printed in JSON format instead of using the template format. +The -compiled flag causes list to set CompiledGoFiles to the Go source +files presented to the compiler. Typically this means that it repeats +the files listed in GoFiles and then also adds the Go code generated +by processing CgoFiles and SwigFiles. The Imports list contains the +union of all imports from both GoFiles and CompiledGoFiles. + +The -deps flag causes list to iterate over not just the named packages +but also all their dependencies. It visits them in a depth-first post-order +traversal, so that a package is listed only after all its dependencies. +Packages not explicitly listed on the command line will have the DepOnly +field set to true. + The -e flag changes the handling of erroneous packages, those that cannot be found or are malformed. By default, the list command prints an error to standard error for each erroneous package and @@ -135,9 +167,120 @@ printing. Erroneous packages will have a non-empty ImportPath and a non-nil Error field; other information may or may not be missing (zeroed). +The -export flag causes list to set the Export field to the name of a +file containing up-to-date export information for the given package. + +The -find flag causes list to identify the named packages but not +resolve their dependencies: the Imports and Deps lists will be empty. + +The -test flag causes list to report not only the named packages +but also their test binaries (for packages with tests), to convey to +source code analysis tools exactly how test binaries are constructed. +The reported import path for a test binary is the import path of +the package followed by a ".test" suffix, as in "math/rand.test". +When building a test, it is sometimes necessary to rebuild certain +dependencies specially for that test (most commonly the tested +package itself). The reported import path of a package recompiled +for a particular test binary is followed by a space and the name of +the test binary in brackets, as in "math/rand [math/rand.test]" +or "regexp [sort.test]". The ForTest field is also set to the name +of the package being tested ("math/rand" or "sort" in the previous +examples). + +The Dir, Target, Shlib, Root, ConflictDir, and Export file paths +are all absolute paths. + +By default, the lists GoFiles, CgoFiles, and so on hold names of files in Dir +(that is, paths relative to Dir, not absolute paths). +The generated files added when using the -compiled and -test flags +are absolute paths referring to cached copies of generated Go source files. +Although they are Go source files, the paths may not end in ".go". + +The -m flag causes list to list modules instead of packages. + +When listing modules, the -f flag still specifies a format template +applied to a Go struct, but now a Module struct: + + type Module struct { + Path string // module path + Version string // module version + Versions []string // available module versions (with -versions) + Replace *Module // replaced by this module + Time *time.Time // time version was created + Update *Module // available update, if any (with -u) + Main bool // is this the main module? + Indirect bool // is this module only an indirect dependency of main module? + Dir string // directory holding files for this module, if any + GoMod string // path to go.mod file for this module, if any + Error *ModuleError // error loading module + } + + type ModuleError struct { + Err string // the error itself + } + +The default output is to print the module path and then +information about the version and replacement if any. +For example, 'go list -m all' might print: + + my/main/module + golang.org/x/text v0.3.0 => /tmp/text + rsc.io/pdf v0.1.1 + +The Module struct has a String method that formats this +line of output, so that the default format is equivalent +to -f '{{.String}}'. + +Note that when a module has been replaced, its Replace field +describes the replacement module, and its Dir field is set to +the replacement's source code, if present. (That is, if Replace +is non-nil, then Dir is set to Replace.Dir, with no access to +the replaced source code.) + +The -u flag adds information about available upgrades. +When the latest version of a given module is newer than +the current one, list -u sets the Module's Update field +to information about the newer module. +The Module's String method indicates an available upgrade by +formatting the newer version in brackets after the current version. +For example, 'go list -m -u all' might print: + + my/main/module + golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text + rsc.io/pdf v0.1.1 [v0.1.2] + +(For tools, 'go list -m -u -json all' may be more convenient to parse.) + +The -versions flag causes list to set the Module's Versions field +to a list of all known versions of that module, ordered according +to semantic versioning, earliest to latest. The flag also changes +the default output format to display the module path followed by the +space-separated version list. + +The arguments to list -m are interpreted as a list of modules, not packages. +The main module is the module containing the current directory. +The active modules are the main module and its dependencies. +With no arguments, list -m shows the main module. +With arguments, list -m shows the modules specified by the arguments. +Any of the active modules can be specified by its module path. +The special pattern "all" specifies all the active modules, first the main +module and then dependencies sorted by module path. +A pattern containing "..." specifies the active modules whose +module paths match the pattern. +A query of the form path@version specifies the result of that query, +which is not limited to active modules. +See 'go help modules' for more about module queries. + +The template function "module" takes a single string argument +that must be a module path or query and returns the specified +module as a Module struct. If an error occurs, the result will +be a Module struct with a non-nil Error field. + For more about build flags, see 'go help build'. For more about specifying packages, see 'go help packages'. + +For more about modules, see 'go help modules'. `, } @@ -146,20 +289,43 @@ func init() { work.AddBuildFlags(CmdList) } -var listE = CmdList.Flag.Bool("e", false, "") -var listFmt = CmdList.Flag.String("f", "{{.ImportPath}}", "") -var listJson = CmdList.Flag.Bool("json", false, "") +var ( + listCompiled = CmdList.Flag.Bool("compiled", false, "") + listDeps = CmdList.Flag.Bool("deps", false, "") + listE = CmdList.Flag.Bool("e", false, "") + listExport = CmdList.Flag.Bool("export", false, "") + listFmt = CmdList.Flag.String("f", "", "") + listFind = CmdList.Flag.Bool("find", false, "") + listJson = CmdList.Flag.Bool("json", false, "") + listM = CmdList.Flag.Bool("m", false, "") + listU = CmdList.Flag.Bool("u", false, "") + listTest = CmdList.Flag.Bool("test", false, "") + listVersions = CmdList.Flag.Bool("versions", false, "") +) + var nl = []byte{'\n'} func runList(cmd *base.Command, args []string) { + modload.LoadTests = *listTest work.BuildInit() out := newTrackingWriter(os.Stdout) defer out.w.Flush() - var do func(*load.PackagePublic) + if *listFmt == "" { + if *listM { + *listFmt = "{{.String}}" + if *listVersions { + *listFmt = `{{.Path}}{{range .Versions}} {{.}}{{end}}` + } + } else { + *listFmt = "{{.ImportPath}}" + } + } + + var do func(interface{}) if *listJson { - do = func(p *load.PackagePublic) { - b, err := json.MarshalIndent(p, "", "\t") + do = func(x interface{}) { + b, err := json.MarshalIndent(x, "", "\t") if err != nil { out.Flush() base.Fatalf("%s", err) @@ -178,13 +344,14 @@ func runList(cmd *base.Command, args []string) { fm := template.FuncMap{ "join": strings.Join, "context": context, + "module": modload.ModuleInfo, } tmpl, err := template.New("main").Funcs(fm).Parse(*listFmt) if err != nil { base.Fatalf("%s", err) } - do = func(p *load.PackagePublic) { - if err := tmpl.Execute(out, p); err != nil { + do = func(x interface{}) { + if err := tmpl.Execute(out, x); err != nil { out.Flush() base.Fatalf("%s", err) } @@ -194,6 +361,62 @@ func runList(cmd *base.Command, args []string) { } } + if *listM { + // Module mode. + if *listCompiled { + base.Fatalf("go list -compiled cannot be used with -m") + } + if *listDeps { + // TODO(rsc): Could make this mean something with -m. + base.Fatalf("go list -deps cannot be used with -m") + } + if *listExport { + base.Fatalf("go list -export cannot be used with -m") + } + if *listFind { + base.Fatalf("go list -find cannot be used with -m") + } + if *listTest { + base.Fatalf("go list -test cannot be used with -m") + } + + if modload.Init(); !modload.Enabled() { + base.Fatalf("go list -m: not using modules") + } + modload.LoadBuildList() + + mods := modload.ListModules(args, *listU, *listVersions) + if !*listE { + for _, m := range mods { + if m.Error != nil { + base.Errorf("go list -m %s: %v", m.Path, m.Error.Err) + } + } + base.ExitIfErrors() + } + for _, m := range mods { + do(m) + } + return + } + + // Package mode (not -m). + if *listU { + base.Fatalf("go list -u can only be used with -m") + } + if *listVersions { + base.Fatalf("go list -versions can only be used with -m") + } + + // These pairings make no sense. + if *listFind && *listDeps { + base.Fatalf("go list -deps cannot be used with -find") + } + if *listFind && *listTest { + base.Fatalf("go list -test cannot be used with -find") + } + + load.IgnoreImports = *listFind var pkgs []*load.Package if *listE { pkgs = load.PackagesAndErrors(args) @@ -201,27 +424,178 @@ func runList(cmd *base.Command, args []string) { pkgs = load.Packages(args) } - // Estimate whether staleness information is needed, - // since it's a little bit of work to compute. + if cache.Default() == nil { + // These flags return file names pointing into the build cache, + // so the build cache must exist. + if *listCompiled { + base.Fatalf("go list -compiled requires build cache") + } + if *listExport { + base.Fatalf("go list -export requires build cache") + } + if *listTest { + base.Fatalf("go list -test requires build cache") + } + } + + if *listTest { + c := cache.Default() + // Add test binaries to packages to be listed. + for _, p := range pkgs { + if p.Error != nil { + continue + } + if len(p.TestGoFiles)+len(p.XTestGoFiles) > 0 { + pmain, ptest, pxtest, err := load.GetTestPackagesFor(p, nil) + if err != nil { + if *listE { + pkgs = append(pkgs, &load.Package{ + PackagePublic: load.PackagePublic{ + ImportPath: p.ImportPath + ".test", + Error: &load.PackageError{Err: err.Error()}, + }, + }) + continue + } + base.Errorf("can't load test package: %s", err) + continue + } + pkgs = append(pkgs, pmain) + if ptest != nil { + pkgs = append(pkgs, ptest) + } + if pxtest != nil { + pkgs = append(pkgs, pxtest) + } + + data := *pmain.Internal.TestmainGo + h := cache.NewHash("testmain") + h.Write([]byte("testmain\n")) + h.Write(data) + out, _, err := c.Put(h.Sum(), bytes.NewReader(data)) + if err != nil { + base.Fatalf("%s", err) + } + pmain.GoFiles[0] = c.OutputFile(out) + } + } + } + + // Remember which packages are named on the command line. + cmdline := make(map[*load.Package]bool) + for _, p := range pkgs { + cmdline[p] = true + } + + if *listDeps { + // Note: This changes the order of the listed packages + // from "as written on the command line" to + // "a depth-first post-order traversal". + // (The dependency exploration order for a given node + // is alphabetical, same as listed in .Deps.) + // Note that -deps is applied after -test, + // so that you only get descriptions of tests for the things named + // explicitly on the command line, not for all dependencies. + pkgs = load.PackageList(pkgs) + } + + // Do we need to run a build to gather information? needStale := *listJson || strings.Contains(*listFmt, ".Stale") - if needStale { + if needStale || *listExport || *listCompiled { var b work.Builder b.Init() - b.ComputeStaleOnly = true + b.IsCmdList = true + b.NeedExport = *listExport + b.NeedCompiledGoFiles = *listCompiled a := &work.Action{} // TODO: Use pkgsFilter? for _, p := range pkgs { - a.Deps = append(a.Deps, b.AutoAction(work.ModeInstall, work.ModeInstall, p)) + if len(p.GoFiles)+len(p.CgoFiles) > 0 { + a.Deps = append(a.Deps, b.AutoAction(work.ModeInstall, work.ModeInstall, p)) + } } b.Do(a) } - for _, pkg := range pkgs { + for _, p := range pkgs { // Show vendor-expanded paths in listing - pkg.TestImports = pkg.Resolve(pkg.TestImports) - pkg.XTestImports = pkg.Resolve(pkg.XTestImports) + p.TestImports = p.Resolve(p.TestImports) + p.XTestImports = p.Resolve(p.XTestImports) + p.DepOnly = !cmdline[p] + + if *listCompiled { + p.Imports = str.StringList(p.Imports, p.Internal.CompiledImports) + } + } + + if *listTest { + all := pkgs + if !*listDeps { + all = load.PackageList(pkgs) + } + // Update import paths to distinguish the real package p + // from p recompiled for q.test. + // This must happen only once the build code is done + // looking at import paths, because it will get very confused + // if it sees these. + old := make(map[string]string) + for _, p := range all { + if p.ForTest != "" { + new := p.ImportPath + " [" + p.ForTest + ".test]" + old[new] = p.ImportPath + p.ImportPath = new + } + p.DepOnly = !cmdline[p] + } + // Update import path lists to use new strings. + m := make(map[string]string) + for _, p := range all { + for _, p1 := range p.Internal.Imports { + if p1.ForTest != "" { + m[old[p1.ImportPath]] = p1.ImportPath + } + } + for i, old := range p.Imports { + if new := m[old]; new != "" { + p.Imports[i] = new + } + } + for old := range m { + delete(m, old) + } + } + // Recompute deps lists using new strings, from the leaves up. + for _, p := range all { + deps := make(map[string]bool) + for _, p1 := range p.Internal.Imports { + deps[p1.ImportPath] = true + for _, d := range p1.Deps { + deps[d] = true + } + } + p.Deps = make([]string, 0, len(deps)) + for d := range deps { + p.Deps = append(p.Deps, d) + } + sort.Strings(p.Deps) + } + } + + // Record non-identity import mappings in p.ImportMap. + for _, p := range pkgs { + for i, srcPath := range p.Internal.RawImports { + path := p.Imports[i] + if path != srcPath { + if p.ImportMap == nil { + p.ImportMap = make(map[string]string) + } + p.ImportMap[srcPath] = path + } + } + } - do(&pkg.PackagePublic) + for _, p := range pkgs { + do(&p.PackagePublic) } } diff --git a/libgo/go/cmd/go/internal/load/flag.go b/libgo/go/cmd/go/internal/load/flag.go index 7ad4208..7534e65 100644 --- a/libgo/go/cmd/go/internal/load/flag.go +++ b/libgo/go/cmd/go/internal/load/flag.go @@ -91,31 +91,3 @@ func (f *PerPackageFlag) For(p *Package) []string { } return flags } - -var cmdlineMatchers []func(*Package) bool - -// SetCmdlinePatterns records the set of patterns given on the command line, -// for use by the PerPackageFlags. -func SetCmdlinePatterns(args []string) { - setCmdlinePatterns(args, base.Cwd) -} - -func setCmdlinePatterns(args []string, cwd string) { - if len(args) == 0 { - args = []string{"."} - } - cmdlineMatchers = nil // allow reset for testing - for _, arg := range args { - cmdlineMatchers = append(cmdlineMatchers, MatchPackage(arg, cwd)) - } -} - -// isCmdlinePkg reports whether p is a package listed on the command line. -func isCmdlinePkg(p *Package) bool { - for _, m := range cmdlineMatchers { - if m(p) { - return true - } - } - return false -} diff --git a/libgo/go/cmd/go/internal/load/path.go b/libgo/go/cmd/go/internal/load/path.go index 45a9e7b..0211b28 100644 --- a/libgo/go/cmd/go/internal/load/path.go +++ b/libgo/go/cmd/go/internal/load/path.go @@ -32,22 +32,6 @@ func hasSubdir(root, dir string) (rel string, ok bool) { return filepath.ToSlash(dir[len(root):]), true } -// hasPathPrefix reports whether the path s begins with the -// elements in prefix. -func hasPathPrefix(s, prefix string) bool { - switch { - default: - return false - case len(s) == len(prefix): - return s == prefix - case len(s) > len(prefix): - if prefix != "" && prefix[len(prefix)-1] == '/' { - return strings.HasPrefix(s, prefix) - } - return s[len(prefix)] == '/' && s[:len(prefix)] == prefix - } -} - // expandPath returns the symlink-expanded form of path. func expandPath(p string) string { x, err := filepath.EvalSymlinks(p) diff --git a/libgo/go/cmd/go/internal/load/pkg.go b/libgo/go/cmd/go/internal/load/pkg.go index dfb1ff6..0579fd5 100644 --- a/libgo/go/cmd/go/internal/load/pkg.go +++ b/libgo/go/cmd/go/internal/load/pkg.go @@ -22,9 +22,26 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" + "cmd/go/internal/modinfo" + "cmd/go/internal/search" "cmd/go/internal/str" ) +var ( + // module initialization hook; never nil, no-op if module use is disabled + ModInit func() + + // module hooks; nil if module use is disabled + ModBinDir func() string // return effective bin directory + ModLookup func(path string) (dir, realPath string, err error) // lookup effective meaning of import + ModPackageModuleInfo func(path string) *modinfo.ModulePublic // return module info for Package struct + ModImportPaths func(args []string) []*search.Match // expand import paths + ModPackageBuildInfo func(main string, deps []string) string // return module info to embed in binary + ModInfoProg func(info string) []byte // wrap module info in .go code for binary + ModImportFromFiles func([]string) // update go.mod to add modules for imports in these files + ModDirImportPath func(string) string // return effective import path for directory +) + var IgnoreImports bool // control whether we ignore imports in packages // A Package describes a single package found in a directory. @@ -37,18 +54,24 @@ type PackagePublic struct { // Note: These fields are part of the go command's public API. // See list.go. It is okay to add fields, but not to change or // remove existing ones. Keep in sync with list.go - Dir string `json:",omitempty"` // directory containing package sources - ImportPath string `json:",omitempty"` // import path of package in dir - ImportComment string `json:",omitempty"` // path in import comment on package statement - Name string `json:",omitempty"` // package name - Doc string `json:",omitempty"` // package documentation string - Target string `json:",omitempty"` // installed target for this package (may be executable) - Shlib string `json:",omitempty"` // the shared library that contains this package (only set when -linkshared) - Goroot bool `json:",omitempty"` // is this package found in the Go root? - Standard bool `json:",omitempty"` // is this package part of the standard Go library? - Root string `json:",omitempty"` // Go root or Go path dir containing this package - ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory - BinaryOnly bool `json:",omitempty"` // package cannot be recompiled + Dir string `json:",omitempty"` // directory containing package sources + ImportPath string `json:",omitempty"` // import path of package in dir + ImportComment string `json:",omitempty"` // path in import comment on package statement + Name string `json:",omitempty"` // package name + Doc string `json:",omitempty"` // package documentation string + Target string `json:",omitempty"` // installed target for this package (may be executable) + Shlib string `json:",omitempty"` // the shared library that contains this package (only set when -linkshared) + Root string `json:",omitempty"` // Go root or Go path dir containing this package + ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory + ForTest string `json:",omitempty"` // package is only for use in named test + Export string `json:",omitempty"` // file containing export data (set by go list -export) + Module *modinfo.ModulePublic `json:",omitempty"` // info about package's module, if any + Match []string `json:",omitempty"` // command-line patterns matching this package + Goroot bool `json:",omitempty"` // is this package found in the Go root? + Standard bool `json:",omitempty"` // is this package part of the standard Go library? + DepOnly bool `json:",omitempty"` // package is only as a dependency, not explicitly listed + BinaryOnly bool `json:",omitempty"` // package cannot be recompiled + Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies? // Stale and StaleReason remain here *only* for the list command. // They are only initialized in preparation for list execution. @@ -59,18 +82,19 @@ type PackagePublic struct { // Source files // If you add to this list you MUST add to p.AllFiles (below) too. // Otherwise file name security lists will not apply to any new additions. - GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) - CgoFiles []string `json:",omitempty"` // .go sources files that import "C" - IgnoredGoFiles []string `json:",omitempty"` // .go sources ignored due to build constraints - CFiles []string `json:",omitempty"` // .c source files - CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files - MFiles []string `json:",omitempty"` // .m source files - HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files - FFiles []string `json:",omitempty"` // .f, .F, .for and .f90 Fortran source files - SFiles []string `json:",omitempty"` // .s source files - SwigFiles []string `json:",omitempty"` // .swig files - SwigCXXFiles []string `json:",omitempty"` // .swigcxx files - SysoFiles []string `json:",omitempty"` // .syso system object files added to package + GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string `json:",omitempty"` // .go source files that import "C" + CompiledGoFiles []string `json:",omitempty"` // .go output from running cgo on CgoFiles + IgnoredGoFiles []string `json:",omitempty"` // .go source files ignored due to build constraints + CFiles []string `json:",omitempty"` // .c source files + CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files + MFiles []string `json:",omitempty"` // .m source files + HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files + FFiles []string `json:",omitempty"` // .f, .F, .for and .f90 Fortran source files + SFiles []string `json:",omitempty"` // .s source files + SwigFiles []string `json:",omitempty"` // .swig files + SwigCXXFiles []string `json:",omitempty"` // .swigcxx files + SysoFiles []string `json:",omitempty"` // .syso system object files added to package // Cgo directives CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler @@ -81,11 +105,12 @@ type PackagePublic struct { CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names // Dependency information - Imports []string `json:",omitempty"` // import paths used by this package - Deps []string `json:",omitempty"` // all (recursively) imported dependencies + Imports []string `json:",omitempty"` // import paths used by this package + ImportMap map[string]string `json:",omitempty"` // map from source import to ImportPath (identity entries omitted) + Deps []string `json:",omitempty"` // all (recursively) imported dependencies // Error information - Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies? + // Incomplete is above, packed into the other bools Error *PackageError `json:",omitempty"` // error loading this package (not dependencies) DepsErrors []*PackageError `json:",omitempty"` // errors loading dependencies @@ -107,6 +132,7 @@ func (p *Package) AllFiles() []string { return str.StringList( p.GoFiles, p.CgoFiles, + // no p.CompiledGoFiles, because they are from GoFiles or generated by us p.IgnoredGoFiles, p.CFiles, p.CXXFiles, @@ -122,21 +148,33 @@ func (p *Package) AllFiles() []string { ) } +// Desc returns the package "description", for use in b.showOutput. +func (p *Package) Desc() string { + if p.ForTest != "" { + return p.ImportPath + " [" + p.ForTest + ".test]" + } + return p.ImportPath +} + type PackageInternal struct { // Unexported fields are not part of the public API. - Build *build.Package - Imports []*Package // this package's direct imports - RawImports []string // this package's original imports as they appear in the text of the program - ForceLibrary bool // this package is a library (even if named "main") - CmdlineFiles bool // package built from files listed on command line - CmdlinePkg bool // package listed on command line - Local bool // imported via local path (./ or ../) - LocalPrefix string // interpret ./ and ../ imports relative to this prefix - ExeName string // desired name for temporary executable - CoverMode string // preprocess Go source files with the coverage tool in this mode - CoverVars map[string]*CoverVar // variables created by coverage analysis - OmitDebug bool // tell linker not to write debug information - GobinSubdir bool // install target would be subdir of GOBIN + Build *build.Package + Imports []*Package // this package's direct imports + CompiledImports []string // additional Imports necessary when using CompiledGoFiles (all from standard library) + RawImports []string // this package's original imports as they appear in the text of the program + ForceLibrary bool // this package is a library (even if named "main") + CmdlineFiles bool // package built from files listed on command line + CmdlinePkg bool // package listed on command line + CmdlinePkgLiteral bool // package listed as literal on command line (not via wildcard) + Local bool // imported via local path (./ or ../) + LocalPrefix string // interpret ./ and ../ imports relative to this prefix + ExeName string // desired name for temporary executable + CoverMode string // preprocess Go source files with the coverage tool in this mode + CoverVars map[string]*CoverVar // variables created by coverage analysis + OmitDebug bool // tell linker not to write debug information + GobinSubdir bool // install target would be subdir of GOBIN + BuildInfo string // add this info to package main + TestmainGo *[]byte // content for _testmain.go Asmflags []string // -asmflags for this package Gcflags []string // -gcflags for this package @@ -224,7 +262,7 @@ func (p *Package) copyBuild(pp *build.Package) { // TODO? Target p.Goroot = pp.Goroot - p.Standard = p.Goroot && p.ImportPath != "" && isStandardImportPath(p.ImportPath) + p.Standard = p.Goroot && p.ImportPath != "" && search.IsStandardImportPath(p.ImportPath) p.GoFiles = pp.GoFiles p.CgoFiles = pp.CgoFiles p.IgnoredGoFiles = pp.IgnoredGoFiles @@ -253,24 +291,12 @@ func (p *Package) copyBuild(pp *build.Package) { p.XTestImports = pp.XTestImports if IgnoreImports { p.Imports = nil + p.Internal.RawImports = nil p.TestImports = nil p.XTestImports = nil } } -// isStandardImportPath reports whether $GOROOT/src/path should be considered -// part of the standard distribution. For historical reasons we allow people to add -// their own code to $GOROOT instead of using $GOPATH, but we assume that -// code will start with a domain name (dot in the first element). -func isStandardImportPath(path string) bool { - i := strings.Index(path, "/") - if i < 0 { - i = len(path) - } - elem := path[:i] - return !strings.Contains(elem, ".") -} - // A PackageError describes an error loading information about a package. type PackageError struct { ImportStack []string // shortest path from package named on command line to this one @@ -296,7 +322,9 @@ func (p *PackageError) Error() string { return "package " + strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Err } -// An ImportStack is a stack of import paths. +// An ImportStack is a stack of import paths, possibly with the suffix " (test)" appended. +// The import path of a test package is the import path of the corresponding +// non-test package with the suffix "_test" added. type ImportStack []string func (s *ImportStack) Push(p string) { @@ -349,15 +377,17 @@ func ClearPackageCachePartial(args []string) { } } -// reloadPackage is like loadPackage but makes sure +// ReloadPackageNoFlags is like LoadPackageNoFlags but makes sure // not to use the package cache. -func ReloadPackage(arg string, stk *ImportStack) *Package { +// It is only for use by GOPATH-based "go get". +// TODO(rsc): When GOPATH-based "go get" is removed, delete this function. +func ReloadPackageNoFlags(arg string, stk *ImportStack) *Package { p := packageCache[arg] if p != nil { delete(packageCache, p.Dir) delete(packageCache, p.ImportPath) } - return LoadPackage(arg, stk) + return LoadPackageNoFlags(arg, stk) } // dirToImportPath returns the pseudo-import path we use for a package @@ -406,10 +436,53 @@ const ( // but possibly a local import path (an absolute file system path or one beginning // with ./ or ../). A local relative path is interpreted relative to srcDir. // It returns a *Package describing the package found in that directory. +// LoadImport does not set tool flags and should only be used by +// this package, as part of a bigger load operation, and by GOPATH-based "go get". +// TODO(rsc): When GOPATH-based "go get" is removed, unexport this function. func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPos []token.Position, mode int) *Package { stk.Push(path) defer stk.Pop() + if strings.HasPrefix(path, "mod/") { + // Paths beginning with "mod/" might accidentally + // look in the module cache directory tree in $GOPATH/pkg/mod/. + // This prefix is owned by the Go core for possible use in the + // standard library (since it does not begin with a domain name), + // so it's OK to disallow entirely. + return &Package{ + PackagePublic: PackagePublic{ + ImportPath: path, + Error: &PackageError{ + ImportStack: stk.Copy(), + Err: fmt.Sprintf("disallowed import path %q", path), + }, + }, + } + } + + if strings.Contains(path, "@") { + var text string + if cfg.ModulesEnabled { + text = "can only use path@version syntax with 'go get'" + } else { + text = "cannot use path@version syntax in GOPATH mode" + } + return &Package{ + PackagePublic: PackagePublic{ + ImportPath: path, + Error: &PackageError{ + ImportStack: stk.Copy(), + Err: text, + }, + }, + } + } + + parentPath := "" + if parent != nil { + parentPath = parent.ImportPath + } + // Determine canonical identifier for this package. // For a local import the identifier is the pseudo-import path // we create from the full directory to the package. @@ -418,8 +491,16 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo importPath := path origPath := path isLocal := build.IsLocalImport(path) + var modDir string + var modErr error if isLocal { importPath = dirToImportPath(filepath.Join(srcDir, path)) + } else if cfg.ModulesEnabled { + var p string + modDir, p, modErr = ModLookup(path) + if modErr == nil { + importPath = p + } } else if mode&ResolveImport != 0 { // We do our own path resolution, because we want to // find out the key to use in packageCache without the @@ -444,17 +525,31 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo // Load package. // Import always returns bp != nil, even if an error occurs, // in order to return partial information. - buildMode := build.ImportComment - if mode&ResolveImport == 0 || path != origPath { - // Not vendoring, or we already found the vendored path. - buildMode |= build.IgnoreVendor + var bp *build.Package + var err error + if modDir != "" { + bp, err = cfg.BuildContext.ImportDir(modDir, 0) + } else if modErr != nil { + bp = new(build.Package) + err = fmt.Errorf("unknown import path %q: %v", importPath, modErr) + } else if cfg.ModulesEnabled && path != "unsafe" { + bp = new(build.Package) + err = fmt.Errorf("unknown import path %q: internal error: module loader did not resolve import", importPath) + } else { + buildMode := build.ImportComment + if mode&ResolveImport == 0 || path != origPath { + // Not vendoring, or we already found the vendored path. + buildMode |= build.IgnoreVendor + } + bp, err = cfg.BuildContext.Import(path, srcDir, buildMode) } - bp, err := cfg.BuildContext.Import(path, srcDir, buildMode) bp.ImportPath = importPath if cfg.GOBIN != "" { bp.BinDir = cfg.GOBIN + } else if cfg.ModulesEnabled { + bp.BinDir = ModBinDir() } - if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path && + if modDir == "" && err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path && !strings.Contains(path, "/vendor/") && !strings.HasPrefix(path, "vendor/") { err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment) } @@ -463,7 +558,7 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo p = setErrorPos(p, importPos) } - if origPath != cleanImport(origPath) { + if modDir == "" && origPath != cleanImport(origPath) { p.Error = &PackageError{ ImportStack: stk.Copy(), Err: fmt.Sprintf("non-canonical import path: %q should be %q", origPath, pathpkg.Clean(origPath)), @@ -473,11 +568,11 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo } // Checked on every import because the rules depend on the code doing the importing. - if perr := disallowInternal(srcDir, p, stk); perr != p { + if perr := disallowInternal(srcDir, parent, parentPath, p, stk); perr != p { return setErrorPos(perr, importPos) } if mode&ResolveImport != 0 { - if perr := disallowVendor(srcDir, origPath, p, stk); perr != p { + if perr := disallowVendor(srcDir, parent, parentPath, origPath, p, stk); perr != p { return setErrorPos(perr, importPos) } } @@ -539,8 +634,14 @@ func isDir(path string) bool { // There are two different resolutions applied. // First, there is Go 1.5 vendoring (golang.org/s/go15vendor). // If vendor expansion doesn't trigger, then the path is also subject to -// Go 1.11 vgo legacy conversion (golang.org/issue/25069). +// Go 1.11 module legacy conversion (golang.org/issue/25069). func ResolveImportPath(parent *Package, path string) (found string) { + if cfg.ModulesEnabled { + if _, p, e := ModLookup(path); e == nil { + return p + } + return path + } found = VendoredImportPath(parent, path) if found != path { return found @@ -828,10 +929,11 @@ func reusePackage(p *Package, stk *ImportStack) *Package { return p } -// disallowInternal checks that srcDir is allowed to import p. +// disallowInternal checks that srcDir (containing package importerPath, if non-empty) +// is allowed to import p. // If the import is allowed, disallowInternal returns the original package p. // If not, it returns a new package containing just an appropriate error. -func disallowInternal(srcDir string, p *Package, stk *ImportStack) *Package { +func disallowInternal(srcDir string, importer *Package, importerPath string, p *Package, stk *ImportStack) *Package { // golang.org/s/go14internal: // An import of a path containing the element “internal” // is disallowed if the importing code is outside the tree @@ -874,23 +976,40 @@ func disallowInternal(srcDir string, p *Package, stk *ImportStack) *Package { if i > 0 { i-- // rewind over slash in ".../internal" } - parent := p.Dir[:i+len(p.Dir)-len(p.ImportPath)] - if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) { - return p - } - // Look for symlinks before reporting error. - srcDir = expandPath(srcDir) - parent = expandPath(parent) - if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) { - return p + if p.Module == nil { + parent := p.Dir[:i+len(p.Dir)-len(p.ImportPath)] + + if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) { + return p + } + + // Look for symlinks before reporting error. + srcDir = expandPath(srcDir) + parent = expandPath(parent) + if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) { + return p + } + } else { + // p is in a module, so make it available based on the importer's import path instead + // of the file path (https://golang.org/issue/23970). + if importerPath == "." { + // The importer is a list of command-line files. + // Pretend that the import path is the import path of the + // directory containing them. + importerPath = ModDirImportPath(importer.Dir) + } + parentOfInternal := p.ImportPath[:i] + if str.HasPathPrefix(importerPath, parentOfInternal) { + return p + } } // Internal is present, and srcDir is outside parent's tree. Not allowed. perr := *p perr.Error = &PackageError{ ImportStack: stk.Copy(), - Err: "use of internal package not allowed", + Err: "use of internal package " + p.ImportPath + " not allowed", } perr.Incomplete = true return &perr @@ -915,10 +1034,11 @@ func findInternal(path string) (index int, ok bool) { return 0, false } -// disallowVendor checks that srcDir is allowed to import p as path. +// disallowVendor checks that srcDir (containing package importerPath, if non-empty) +// is allowed to import p as path. // If the import is allowed, disallowVendor returns the original package p. // If not, it returns a new package containing just an appropriate error. -func disallowVendor(srcDir, path string, p *Package, stk *ImportStack) *Package { +func disallowVendor(srcDir string, importer *Package, importerPath, path string, p *Package, stk *ImportStack) *Package { // The stack includes p.ImportPath. // If that's the only thing on the stack, we started // with a name given on the command line, not an @@ -927,6 +1047,20 @@ func disallowVendor(srcDir, path string, p *Package, stk *ImportStack) *Package return p } + // Modules must not import vendor packages in the standard library, + // but the usual vendor visibility check will not catch them + // because the module loader presents them with an ImportPath starting + // with "golang_org/" instead of "vendor/". + if p.Standard && !importer.Standard && strings.HasPrefix(p.ImportPath, "golang_org") { + perr := *p + perr.Error = &PackageError{ + ImportStack: stk.Copy(), + Err: "use of vendored package " + path + " not allowed", + } + perr.Incomplete = true + return &perr + } + if perr := disallowVendorVisibility(srcDir, p, stk); perr != p { return perr } @@ -1057,26 +1191,6 @@ var foldPath = make(map[string]string) func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { p.copyBuild(bp) - // Decide whether p was listed on the command line. - // Given that load is called while processing the command line, - // you might think we could simply pass a flag down into load - // saying whether we are loading something named on the command - // line or something to satisfy an import. But the first load of a - // package named on the command line may be as a dependency - // of an earlier package named on the command line, not when we - // get to that package during command line processing. - // For example "go test fmt reflect" will load reflect as a dependency - // of fmt before it attempts to load as a command-line argument. - // Because loads are cached, the later load will be a no-op, - // so it is important that the first load can fill in CmdlinePkg correctly. - // Hence the call to an explicit matching check here. - p.Internal.CmdlinePkg = isCmdlinePkg(p) - - p.Internal.Asmflags = BuildAsmflags.For(p) - p.Internal.Gcflags = BuildGcflags.For(p) - p.Internal.Ldflags = BuildLdflags.For(p) - p.Internal.Gccgoflags = BuildGccgoflags.For(p) - // The localPrefix is the path we interpret ./ imports relative to. // Synthesized main packages sometimes override this. if p.Internal.Local { @@ -1113,11 +1227,45 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { return } _, elem := filepath.Split(p.Dir) + if cfg.ModulesEnabled { + // NOTE(rsc): Using p.ImportPath instead of p.Dir + // makes sure we install a package in the root of a + // cached module directory as that package name + // not name@v1.2.3. + // Using p.ImportPath instead of p.Dir + // is probably correct all the time, + // even for non-module-enabled code, + // but I'm not brave enough to change the + // non-module behavior this late in the + // release cycle. Maybe for Go 1.12. + // See golang.org/issue/26869. + _, elem = pathpkg.Split(p.ImportPath) + + // If this is example.com/mycmd/v2, it's more useful to install it as mycmd than as v2. + // See golang.org/issue/24667. + isVersion := func(v string) bool { + if len(v) < 2 || v[0] != 'v' || v[1] < '1' || '9' < v[1] { + return false + } + for i := 2; i < len(v); i++ { + if c := v[i]; c < '0' || '9' < c { + return false + } + } + return true + } + if isVersion(elem) { + _, elem = pathpkg.Split(pathpkg.Dir(p.ImportPath)) + } + } full := cfg.BuildContext.GOOS + "_" + cfg.BuildContext.GOARCH + "/" + elem if cfg.BuildContext.GOOS != base.ToolGOOS || cfg.BuildContext.GOARCH != base.ToolGOARCH { // Install cross-compiled binaries to subdirectories of bin. elem = full } + if p.Internal.Build.BinDir == "" && cfg.ModulesEnabled { + p.Internal.Build.BinDir = ModBinDir() + } if p.Internal.Build.BinDir != "" { // Install to GOBIN or bin of GOPATH entry. p.Target = filepath.Join(p.Internal.Build.BinDir, elem) @@ -1165,31 +1313,37 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { // Build augmented import list to add implicit dependencies. // Be careful not to add imports twice, just to avoid confusion. importPaths := p.Imports - addImport := func(path string) { + addImport := func(path string, forCompiler bool) { for _, p := range importPaths { if path == p { return } } importPaths = append(importPaths, path) + if forCompiler { + p.Internal.CompiledImports = append(p.Internal.CompiledImports, path) + } } - // Cgo translation adds imports of "runtime/cgo" and "syscall", + // Cgo translation adds imports of "unsafe", "runtime/cgo" and "syscall", // except for certain packages, to avoid circular dependencies. + if p.UsesCgo() { + addImport("unsafe", true) + } if p.UsesCgo() && (!p.Standard || !cgoExclude[p.ImportPath]) && cfg.BuildContext.Compiler != "gccgo" { - addImport("runtime/cgo") + addImport("runtime/cgo", true) } if p.UsesCgo() && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) { - addImport("syscall") + addImport("syscall", true) } // SWIG adds imports of some standard packages. if p.UsesSwig() { if cfg.BuildContext.Compiler != "gccgo" { - addImport("runtime/cgo") + addImport("runtime/cgo", true) } - addImport("syscall") - addImport("sync") + addImport("syscall", true) + addImport("sync", true) // TODO: The .swig and .swigcxx files can use // %go_import directives to import other packages. @@ -1198,7 +1352,7 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { // The linker loads implicit dependencies. if p.Name == "main" && !p.Internal.ForceLibrary { for _, dep := range LinkerDeps(p) { - addImport(dep) + addImport(dep, false) } } @@ -1368,6 +1522,13 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { setError(fmt.Sprintf("case-insensitive import collision: %q and %q", p.ImportPath, other)) return } + + if cfg.ModulesEnabled { + p.Module = ModPackageModuleInfo(p.ImportPath) + if p.Name == "main" { + p.Internal.BuildInfo = ModPackageBuildInfo(p.ImportPath, p.Deps) + } + } } // SafeArg reports whether arg is a "safe" command-line argument, @@ -1466,7 +1627,13 @@ func (p *Package) mkAbs(list []string) []string { // InternalGoFiles returns the list of Go files being built for the package, // using absolute paths. func (p *Package) InternalGoFiles() []string { - return p.mkAbs(str.StringList(p.GoFiles, p.CgoFiles, p.TestGoFiles, p.XTestGoFiles)) + return p.mkAbs(str.StringList(p.GoFiles, p.CgoFiles, p.TestGoFiles)) +} + +// InternalXGoFiles returns the list of Go files being built for the XTest package, +// using absolute paths. +func (p *Package) InternalXGoFiles() []string { + return p.mkAbs(p.XTestGoFiles) } // InternalGoFiles returns the list of all Go files possibly relevant for the package, @@ -1492,7 +1659,7 @@ func (p *Package) UsesCgo() bool { return len(p.CgoFiles) > 0 } -// packageList returns the list of packages in the dag rooted at roots +// PackageList returns the list of packages in the dag rooted at roots // as visited in a depth-first post-order traversal. func PackageList(roots []*Package) []*Package { seen := map[*Package]bool{} @@ -1514,6 +1681,42 @@ func PackageList(roots []*Package) []*Package { return all } +// TestPackageList returns the list of packages in the dag rooted at roots +// as visited in a depth-first post-order traversal, including the test +// imports of the roots. This ignores errors in test packages. +func GetTestPackageList(roots []*Package) []*Package { + seen := map[*Package]bool{} + all := []*Package{} + var walk func(*Package) + walk = func(p *Package) { + if seen[p] { + return + } + seen[p] = true + for _, p1 := range p.Internal.Imports { + walk(p1) + } + all = append(all, p) + } + walkTest := func(root *Package, path string) { + var stk ImportStack + p1 := LoadImport(path, root.Dir, root, &stk, root.Internal.Build.TestImportPos[path], ResolveImport) + if p1.Error == nil { + walk(p1) + } + } + for _, root := range roots { + walk(root) + for _, path := range root.TestImports { + walkTest(root, path) + } + for _, path := range root.XTestImports { + walkTest(root, path) + } + } + return all +} + var cmdCache = map[string]*Package{} func ClearCmdCache() { @@ -1522,11 +1725,31 @@ func ClearCmdCache() { } } +// LoadPackage loads the package named by arg. +func LoadPackage(arg string, stk *ImportStack) *Package { + p := loadPackage(arg, stk) + setToolFlags(p) + return p +} + +// LoadPackageNoFlags is like LoadPackage +// but does not guarantee that the build tool flags are set in the result. +// It is only for use by GOPATH-based "go get" +// and is only appropriate for preliminary loading of packages. +// A real load using LoadPackage or (more likely) +// Packages, PackageAndErrors, or PackagesForBuild +// must be done before passing the package to any build +// steps, so that the tool flags can be set properly. +// TODO(rsc): When GOPATH-based "go get" is removed, delete this function. +func LoadPackageNoFlags(arg string, stk *ImportStack) *Package { + return loadPackage(arg, stk) +} + // loadPackage is like loadImport but is used for command-line arguments, // not for paths found in import statements. In addition to ordinary import paths, // loadPackage accepts pseudo-paths beginning with cmd/ to denote commands // in the Go command directory, as well as paths to those directories. -func LoadPackage(arg string, stk *ImportStack) *Package { +func loadPackage(arg string, stk *ImportStack) *Package { if build.IsLocalImport(arg) { dir := arg if !filepath.IsAbs(dir) { @@ -1573,8 +1796,12 @@ func LoadPackage(arg string, stk *ImportStack) *Package { // This lets you run go test ./ioutil in package io and be // referring to io/ioutil rather than a hypothetical import of // "./ioutil". - if build.IsLocalImport(arg) { - bp, _ := cfg.BuildContext.ImportDir(filepath.Join(base.Cwd, arg), build.FindOnly) + if build.IsLocalImport(arg) || filepath.IsAbs(arg) { + dir := arg + if !filepath.IsAbs(arg) { + dir = filepath.Join(base.Cwd, arg) + } + bp, _ := cfg.BuildContext.ImportDir(dir, build.FindOnly) if bp.ImportPath != "" && bp.ImportPath != "." { arg = bp.ImportPath } @@ -1583,7 +1810,7 @@ func LoadPackage(arg string, stk *ImportStack) *Package { return LoadImport(arg, base.Cwd, nil, stk, nil, 0) } -// packages returns the packages named by the +// Packages returns the packages named by the // command line arguments 'args'. If a named package // cannot be loaded at all (for example, if the directory does not exist), // then packages prints an error and does not include that @@ -1603,41 +1830,68 @@ func Packages(args []string) []*Package { return pkgs } -// packagesAndErrors is like 'packages' but returns a +// PackagesAndErrors is like 'packages' but returns a // *Package for every argument, even the ones that // cannot be loaded at all. // The packages that fail to load will have p.Error != nil. -func PackagesAndErrors(args []string) []*Package { - if len(args) > 0 && strings.HasSuffix(args[0], ".go") { - return []*Package{GoFilesPackage(args)} +func PackagesAndErrors(patterns []string) []*Package { + if len(patterns) > 0 && strings.HasSuffix(patterns[0], ".go") { + return []*Package{GoFilesPackage(patterns)} } - args = ImportPaths(args) + matches := ImportPaths(patterns) var ( pkgs []*Package stk ImportStack - seenArg = make(map[string]bool) seenPkg = make(map[*Package]bool) ) - for _, arg := range args { - if seenArg[arg] { - continue - } - seenArg[arg] = true - pkg := LoadPackage(arg, &stk) - if seenPkg[pkg] { - continue + for _, m := range matches { + for _, pkg := range m.Pkgs { + p := loadPackage(pkg, &stk) + p.Match = append(p.Match, m.Pattern) + p.Internal.CmdlinePkg = true + if m.Literal { + // Note: do not set = m.Literal unconditionally + // because maybe we'll see p matching both + // a literal and also a non-literal pattern. + p.Internal.CmdlinePkgLiteral = true + } + if seenPkg[p] { + continue + } + seenPkg[p] = true + pkgs = append(pkgs, p) } - seenPkg[pkg] = true - pkgs = append(pkgs, pkg) } + // Now that CmdlinePkg is set correctly, + // compute the effective flags for all loaded packages + // (not just the ones matching the patterns but also + // their dependencies). + setToolFlags(pkgs...) + return pkgs } -// packagesForBuild is like 'packages' but fails if any of -// the packages or their dependencies have errors +func setToolFlags(pkgs ...*Package) { + for _, p := range PackageList(pkgs) { + p.Internal.Asmflags = BuildAsmflags.For(p) + p.Internal.Gcflags = BuildGcflags.For(p) + p.Internal.Ldflags = BuildLdflags.For(p) + p.Internal.Gccgoflags = BuildGccgoflags.For(p) + } +} + +func ImportPaths(args []string) []*search.Match { + if ModInit(); cfg.ModulesEnabled { + return ModImportPaths(args) + } + return search.ImportPaths(args) +} + +// PackagesForBuild is like Packages but exits +// if any of the packages or their dependencies have errors // (cannot be built). func PackagesForBuild(args []string) []*Package { pkgs := PackagesAndErrors(args) @@ -1645,6 +1899,7 @@ func PackagesForBuild(args []string) []*Package { for _, pkg := range pkgs { if pkg.Error != nil { base.Errorf("can't load package: %s", pkg.Error) + printed[pkg.Error] = true } for _, err := range pkg.DepsErrors { // Since these are errors in dependencies, @@ -1682,7 +1937,8 @@ func PackagesForBuild(args []string) []*Package { // (typically named on the command line). The target is named p.a for // package p or named after the first Go file for package main. func GoFilesPackage(gofiles []string) *Package { - // TODO: Remove this restriction. + ModInit() + for _, f := range gofiles { if !strings.HasSuffix(f, ".go") { base.Fatalf("named files must be .go files") @@ -1720,6 +1976,10 @@ func GoFilesPackage(gofiles []string) *Package { } ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil } + if cfg.ModulesEnabled { + ModImportFromFiles(gofiles) + } + var err error if dir == "" { dir = base.Cwd @@ -1730,6 +1990,11 @@ func GoFilesPackage(gofiles []string) *Package { } bp, err := ctxt.ImportDir(dir, 0) + if ModDirImportPath != nil { + // Use the effective import path of the directory + // for deciding visibility during pkg.load. + bp.ImportPath = ModDirImportPath(dir) + } pkg := new(Package) pkg.Internal.Local = true pkg.Internal.CmdlineFiles = true @@ -1739,6 +2004,7 @@ func GoFilesPackage(gofiles []string) *Package { pkg.Internal.LocalPrefix = dirToImportPath(dir) pkg.ImportPath = "command-line-arguments" pkg.Target = "" + pkg.Match = gofiles if pkg.Name == "main" { _, elem := filepath.Split(gofiles[0]) @@ -1748,152 +2014,12 @@ func GoFilesPackage(gofiles []string) *Package { } if cfg.GOBIN != "" { pkg.Target = filepath.Join(cfg.GOBIN, exe) + } else if cfg.ModulesEnabled { + pkg.Target = filepath.Join(ModBinDir(), exe) } } - return pkg -} - -// GetTestPackagesFor returns package structs ptest, the package p plus -// its test files, and pxtest, the external tests of package p. -// pxtest may be nil. If there are no test files, forceTest decides -// whether this returns a new package struct or just returns p. -func GetTestPackagesFor(p *Package, forceTest bool) (ptest, pxtest *Package, err error) { - var imports, ximports []*Package - var stk ImportStack - stk.Push(p.ImportPath + " (test)") - rawTestImports := str.StringList(p.TestImports) - for i, path := range p.TestImports { - p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], ResolveImport) - if p1.Error != nil { - return nil, nil, p1.Error - } - if len(p1.DepsErrors) > 0 { - err := p1.DepsErrors[0] - err.Pos = "" // show full import stack - return nil, nil, err - } - if str.Contains(p1.Deps, p.ImportPath) || p1.ImportPath == p.ImportPath { - // Same error that loadPackage returns (via reusePackage) in pkg.go. - // Can't change that code, because that code is only for loading the - // non-test copy of a package. - err := &PackageError{ - ImportStack: testImportStack(stk[0], p1, p.ImportPath), - Err: "import cycle not allowed in test", - IsImportCycle: true, - } - return nil, nil, err - } - p.TestImports[i] = p1.ImportPath - imports = append(imports, p1) - } - stk.Pop() - stk.Push(p.ImportPath + "_test") - pxtestNeedsPtest := false - rawXTestImports := str.StringList(p.XTestImports) - for i, path := range p.XTestImports { - p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.XTestImportPos[path], ResolveImport) - if p1.Error != nil { - return nil, nil, p1.Error - } - if len(p1.DepsErrors) > 0 { - err := p1.DepsErrors[0] - err.Pos = "" // show full import stack - return nil, nil, err - } - if p1.ImportPath == p.ImportPath { - pxtestNeedsPtest = true - } else { - ximports = append(ximports, p1) - } - p.XTestImports[i] = p1.ImportPath - } - stk.Pop() - - // Test package. - if len(p.TestGoFiles) > 0 || forceTest { - ptest = new(Package) - *ptest = *p - ptest.GoFiles = nil - ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...) - ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...) - ptest.Target = "" - // Note: The preparation of the vet config requires that common - // indexes in ptest.Imports, ptest.Internal.Imports, and ptest.Internal.RawImports - // all line up (but RawImports can be shorter than the others). - // That is, for 0 ≤ i < len(RawImports), - // RawImports[i] is the import string in the program text, - // Imports[i] is the expanded import string (vendoring applied or relative path expanded away), - // and Internal.Imports[i] is the corresponding *Package. - // Any implicitly added imports appear in Imports and Internal.Imports - // but not RawImports (because they were not in the source code). - // We insert TestImports, imports, and rawTestImports at the start of - // these lists to preserve the alignment. - ptest.Imports = str.StringList(p.TestImports, p.Imports) - ptest.Internal.Imports = append(imports, p.Internal.Imports...) - ptest.Internal.RawImports = str.StringList(rawTestImports, p.Internal.RawImports) - ptest.Internal.ForceLibrary = true - ptest.Internal.Build = new(build.Package) - *ptest.Internal.Build = *p.Internal.Build - m := map[string][]token.Position{} - for k, v := range p.Internal.Build.ImportPos { - m[k] = append(m[k], v...) - } - for k, v := range p.Internal.Build.TestImportPos { - m[k] = append(m[k], v...) - } - ptest.Internal.Build.ImportPos = m - } else { - ptest = p - } - - // External test package. - if len(p.XTestGoFiles) > 0 { - pxtest = &Package{ - PackagePublic: PackagePublic{ - Name: p.Name + "_test", - ImportPath: p.ImportPath + "_test", - Root: p.Root, - Dir: p.Dir, - GoFiles: p.XTestGoFiles, - Imports: p.XTestImports, - }, - Internal: PackageInternal{ - LocalPrefix: p.Internal.LocalPrefix, - Build: &build.Package{ - ImportPos: p.Internal.Build.XTestImportPos, - }, - Imports: ximports, - RawImports: rawXTestImports, - - Asmflags: p.Internal.Asmflags, - Gcflags: p.Internal.Gcflags, - Ldflags: p.Internal.Ldflags, - Gccgoflags: p.Internal.Gccgoflags, - }, - } - if pxtestNeedsPtest { - pxtest.Internal.Imports = append(pxtest.Internal.Imports, ptest) - } - } - - return ptest, pxtest, nil -} + setToolFlags(pkg) -func testImportStack(top string, p *Package, target string) []string { - stk := []string{top, p.ImportPath} -Search: - for p.ImportPath != target { - for _, p1 := range p.Internal.Imports { - if p1.ImportPath == target || str.Contains(p1.Deps, target) { - stk = append(stk, p1.ImportPath) - p = p1 - continue Search - } - } - // Can't happen, but in case it does... - stk = append(stk, "<lost path to cycle>") - break - } - return stk + return pkg } diff --git a/libgo/go/cmd/go/internal/load/search.go b/libgo/go/cmd/go/internal/load/search.go index 595de07..cf09c7b 100644 --- a/libgo/go/cmd/go/internal/load/search.go +++ b/libgo/go/cmd/go/internal/load/search.go @@ -5,271 +5,16 @@ package load import ( - "cmd/go/internal/cfg" - "fmt" - "go/build" - "log" - "os" - "path" "path/filepath" - "regexp" "strings" -) - -// allPackages returns all the packages that can be found -// under the $GOPATH directories and $GOROOT matching pattern. -// The pattern is either "all" (all packages), "std" (standard packages), -// "cmd" (standard commands), or a path including "...". -func allPackages(pattern string) []string { - pkgs := MatchPackages(pattern) - if len(pkgs) == 0 { - fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) - } - return pkgs -} - -// allPackagesInFS is like allPackages but is passed a pattern -// beginning ./ or ../, meaning it should scan the tree rooted -// at the given directory. There are ... in the pattern too. -func allPackagesInFS(pattern string) []string { - pkgs := MatchPackagesInFS(pattern) - if len(pkgs) == 0 { - fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) - } - return pkgs -} - -// MatchPackages returns a list of package paths matching pattern -// (see go help packages for pattern syntax). -func MatchPackages(pattern string) []string { - match := func(string) bool { return true } - treeCanMatch := func(string) bool { return true } - if !IsMetaPackage(pattern) { - match = matchPattern(pattern) - treeCanMatch = treeCanMatchPattern(pattern) - } - - have := map[string]bool{ - "builtin": true, // ignore pseudo-package that exists only for documentation - } - if !cfg.BuildContext.CgoEnabled { - have["runtime/cgo"] = true // ignore during walk - } - var pkgs []string - - for _, src := range cfg.BuildContext.SrcDirs() { - if (pattern == "std" || pattern == "cmd") && src != cfg.GOROOTsrc { - continue - } - src = filepath.Clean(src) + string(filepath.Separator) - root := src - if pattern == "cmd" { - root += "cmd" + string(filepath.Separator) - } - filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { - if err != nil || path == src { - return nil - } - - want := true - // Avoid .foo, _foo, and testdata directory trees. - _, elem := filepath.Split(path) - if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { - want = false - } - - name := filepath.ToSlash(path[len(src):]) - if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") { - // The name "std" is only the standard library. - // If the name is cmd, it's the root of the command tree. - want = false - } - if !treeCanMatch(name) { - want = false - } - - if !fi.IsDir() { - if fi.Mode()&os.ModeSymlink != 0 && want { - if target, err := os.Stat(path); err == nil && target.IsDir() { - fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path) - } - } - return nil - } - if !want { - return filepath.SkipDir - } - - if have[name] { - return nil - } - have[name] = true - if !match(name) { - return nil - } - pkg, err := cfg.BuildContext.ImportDir(path, 0) - if err != nil { - if _, noGo := err.(*build.NoGoError); noGo { - return nil - } - } - - // If we are expanding "cmd", skip main - // packages under cmd/vendor. At least as of - // March, 2017, there is one there for the - // vendored pprof tool. - if pattern == "cmd" && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" { - return nil - } - - pkgs = append(pkgs, name) - return nil - }) - } - return pkgs -} - -// MatchPackagesInFS returns a list of package paths matching pattern, -// which must begin with ./ or ../ -// (see go help packages for pattern syntax). -func MatchPackagesInFS(pattern string) []string { - // Find directory to begin the scan. - // Could be smarter but this one optimization - // is enough for now, since ... is usually at the - // end of a path. - i := strings.Index(pattern, "...") - dir, _ := path.Split(pattern[:i]) - - // pattern begins with ./ or ../. - // path.Clean will discard the ./ but not the ../. - // We need to preserve the ./ for pattern matching - // and in the returned import paths. - prefix := "" - if strings.HasPrefix(pattern, "./") { - prefix = "./" - } - match := matchPattern(pattern) - - var pkgs []string - filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { - if err != nil || !fi.IsDir() { - return nil - } - if path == dir { - // filepath.Walk starts at dir and recurses. For the recursive case, - // the path is the result of filepath.Join, which calls filepath.Clean. - // The initial case is not Cleaned, though, so we do this explicitly. - // - // This converts a path like "./io/" to "io". Without this step, running - // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io - // package, because prepending the prefix "./" to the unclean path would - // result in "././io", and match("././io") returns false. - path = filepath.Clean(path) - } - - // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". - _, elem := filepath.Split(path) - dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." - if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { - return filepath.SkipDir - } - - name := prefix + filepath.ToSlash(path) - if !match(name) { - return nil - } - // We keep the directory if we can import it, or if we can't import it - // due to invalid Go source files. This means that directories containing - // parse errors will be built (and fail) instead of being silently skipped - // as not matching the pattern. Go 1.5 and earlier skipped, but that - // behavior means people miss serious mistakes. - // See golang.org/issue/11407. - if p, err := cfg.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) { - if _, noGo := err.(*build.NoGoError); !noGo { - log.Print(err) - } - return nil - } - pkgs = append(pkgs, name) - return nil - }) - return pkgs -} - -// treeCanMatchPattern(pattern)(name) reports whether -// name or children of name can possibly match pattern. -// Pattern is the same limited glob accepted by matchPattern. -func treeCanMatchPattern(pattern string) func(name string) bool { - wildCard := false - if i := strings.Index(pattern, "..."); i >= 0 { - wildCard = true - pattern = pattern[:i] - } - return func(name string) bool { - return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || - wildCard && strings.HasPrefix(name, pattern) - } -} - -// matchPattern(pattern)(name) reports whether -// name matches pattern. Pattern is a limited glob -// pattern in which '...' means 'any string' and there -// is no other special syntax. -// Unfortunately, there are two special cases. Quoting "go help packages": -// -// First, /... at the end of the pattern can match an empty string, -// so that net/... matches both net and packages in its subdirectories, like net/http. -// Second, any slash-separted pattern element containing a wildcard never -// participates in a match of the "vendor" element in the path of a vendored -// package, so that ./... does not match packages in subdirectories of -// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do. -// Note, however, that a directory named vendor that itself contains code -// is not a vendored package: cmd/vendor would be a command named vendor, -// and the pattern cmd/... matches it. -func matchPattern(pattern string) func(name string) bool { - // Convert pattern to regular expression. - // The strategy for the trailing /... is to nest it in an explicit ? expression. - // The strategy for the vendor exclusion is to change the unmatchable - // vendor strings to a disallowed code point (vendorChar) and to use - // "(anything but that codepoint)*" as the implementation of the ... wildcard. - // This is a bit complicated but the obvious alternative, - // namely a hand-written search like in most shell glob matchers, - // is too easy to make accidentally exponential. - // Using package regexp guarantees linear-time matching. - - const vendorChar = "\x00" - - if strings.Contains(pattern, vendorChar) { - return func(name string) bool { return false } - } - - re := regexp.QuoteMeta(pattern) - re = replaceVendor(re, vendorChar) - switch { - case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`): - re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)` - case re == vendorChar+`/\.\.\.`: - re = `(/vendor|/` + vendorChar + `/\.\.\.)` - case strings.HasSuffix(re, `/\.\.\.`): - re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?` - } - re = strings.Replace(re, `\.\.\.`, `[^`+vendorChar+`]*`, -1) - - reg := regexp.MustCompile(`^` + re + `$`) - - return func(name string) bool { - if strings.Contains(name, vendorChar) { - return false - } - return reg.MatchString(replaceVendor(name, vendorChar)) - } -} + "cmd/go/internal/search" +) // MatchPackage(pattern, cwd)(p) reports whether package p matches pattern in the working directory cwd. func MatchPackage(pattern, cwd string) func(*Package) bool { switch { - case strings.HasPrefix(pattern, "./") || strings.HasPrefix(pattern, "../") || pattern == "." || pattern == "..": + case search.IsRelativePath(pattern): // Split pattern into leading pattern-free directory path // (including all . and .. elements) and the final pattern. var dir string @@ -284,7 +29,7 @@ func MatchPackage(pattern, cwd string) func(*Package) bool { if pattern == "" { return func(p *Package) bool { return p.Dir == dir } } - matchPath := matchPattern(pattern) + matchPath := search.MatchPattern(pattern) return func(p *Package) bool { // Compute relative path to dir and see if it matches the pattern. rel, err := filepath.Rel(dir, p.Dir) @@ -305,81 +50,7 @@ func MatchPackage(pattern, cwd string) func(*Package) bool { case pattern == "cmd": return func(p *Package) bool { return p.Standard && strings.HasPrefix(p.ImportPath, "cmd/") } default: - matchPath := matchPattern(pattern) + matchPath := search.MatchPattern(pattern) return func(p *Package) bool { return matchPath(p.ImportPath) } } } - -// replaceVendor returns the result of replacing -// non-trailing vendor path elements in x with repl. -func replaceVendor(x, repl string) string { - if !strings.Contains(x, "vendor") { - return x - } - elem := strings.Split(x, "/") - for i := 0; i < len(elem)-1; i++ { - if elem[i] == "vendor" { - elem[i] = repl - } - } - return strings.Join(elem, "/") -} - -// ImportPaths returns the import paths to use for the given command line. -func ImportPaths(args []string) []string { - args = ImportPathsNoDotExpansion(args) - var out []string - for _, a := range args { - if strings.Contains(a, "...") { - if build.IsLocalImport(a) { - out = append(out, allPackagesInFS(a)...) - } else { - out = append(out, allPackages(a)...) - } - continue - } - out = append(out, a) - } - return out -} - -// ImportPathsNoDotExpansion returns the import paths to use for the given -// command line, but it does no ... expansion. -func ImportPathsNoDotExpansion(args []string) []string { - if cmdlineMatchers == nil { - SetCmdlinePatterns(args) - } - if len(args) == 0 { - return []string{"."} - } - var out []string - for _, a := range args { - // Arguments are supposed to be import paths, but - // as a courtesy to Windows developers, rewrite \ to / - // in command-line arguments. Handles .\... and so on. - if filepath.Separator == '\\' { - a = strings.Replace(a, `\`, `/`, -1) - } - - // Put argument in canonical form, but preserve leading ./. - if strings.HasPrefix(a, "./") { - a = "./" + path.Clean(a) - if a == "./." { - a = "." - } - } else { - a = path.Clean(a) - } - if IsMetaPackage(a) { - out = append(out, allPackages(a)...) - continue - } - out = append(out, a) - } - return out -} - -// IsMetaPackage checks if name is a reserved package name that expands to multiple packages. -func IsMetaPackage(name string) bool { - return name == "std" || name == "cmd" || name == "all" -} diff --git a/libgo/go/cmd/go/internal/load/test.go b/libgo/go/cmd/go/internal/load/test.go new file mode 100644 index 0000000..0eab21b --- /dev/null +++ b/libgo/go/cmd/go/internal/load/test.go @@ -0,0 +1,654 @@ +// Copyright 2018 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 load + +import ( + "bytes" + "cmd/go/internal/base" + "cmd/go/internal/str" + "errors" + "fmt" + "go/ast" + "go/build" + "go/doc" + "go/parser" + "go/token" + "path/filepath" + "sort" + "strings" + "text/template" + "unicode" + "unicode/utf8" +) + +var TestMainDeps = []string{ + // Dependencies for testmain. + "os", + "testing", + "testing/internal/testdeps", +} + +type TestCover struct { + Mode string + Local bool + Pkgs []*Package + Paths []string + Vars []coverInfo + DeclVars func(*Package, ...string) map[string]*CoverVar +} + +// TestPackagesFor returns three packages: +// - ptest, the package p compiled with added "package p" test files. +// - pxtest, the result of compiling any "package p_test" (external) test files. +// - pmain, the package main corresponding to the test binary (running tests in ptest and pxtest). +// +// If the package has no "package p_test" test files, pxtest will be nil. +// If the non-test compilation of package p can be reused +// (for example, if there are no "package p" test files and +// package p need not be instrumented for coverage or any other reason), +// then the returned ptest == p. +// +// The caller is expected to have checked that len(p.TestGoFiles)+len(p.XTestGoFiles) > 0, +// or else there's no point in any of this. +func GetTestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Package, err error) { + var imports, ximports []*Package + var stk ImportStack + stk.Push(p.ImportPath + " (test)") + rawTestImports := str.StringList(p.TestImports) + for i, path := range p.TestImports { + p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], ResolveImport) + if p1.Error != nil { + return nil, nil, nil, p1.Error + } + if len(p1.DepsErrors) > 0 { + err := p1.DepsErrors[0] + err.Pos = "" // show full import stack + return nil, nil, nil, err + } + if str.Contains(p1.Deps, p.ImportPath) || p1.ImportPath == p.ImportPath { + // Same error that loadPackage returns (via reusePackage) in pkg.go. + // Can't change that code, because that code is only for loading the + // non-test copy of a package. + err := &PackageError{ + ImportStack: testImportStack(stk[0], p1, p.ImportPath), + Err: "import cycle not allowed in test", + IsImportCycle: true, + } + return nil, nil, nil, err + } + p.TestImports[i] = p1.ImportPath + imports = append(imports, p1) + } + stk.Pop() + stk.Push(p.ImportPath + "_test") + pxtestNeedsPtest := false + rawXTestImports := str.StringList(p.XTestImports) + for i, path := range p.XTestImports { + p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.XTestImportPos[path], ResolveImport) + if p1.Error != nil { + return nil, nil, nil, p1.Error + } + if len(p1.DepsErrors) > 0 { + err := p1.DepsErrors[0] + err.Pos = "" // show full import stack + return nil, nil, nil, err + } + if p1.ImportPath == p.ImportPath { + pxtestNeedsPtest = true + } else { + ximports = append(ximports, p1) + } + p.XTestImports[i] = p1.ImportPath + } + stk.Pop() + + // Test package. + if len(p.TestGoFiles) > 0 || p.Name == "main" || cover != nil && cover.Local { + ptest = new(Package) + *ptest = *p + ptest.ForTest = p.ImportPath + ptest.GoFiles = nil + ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...) + ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...) + ptest.Target = "" + // Note: The preparation of the vet config requires that common + // indexes in ptest.Imports and ptest.Internal.RawImports + // all line up (but RawImports can be shorter than the others). + // That is, for 0 ≤ i < len(RawImports), + // RawImports[i] is the import string in the program text, and + // Imports[i] is the expanded import string (vendoring applied or relative path expanded away). + // Any implicitly added imports appear in Imports and Internal.Imports + // but not RawImports (because they were not in the source code). + // We insert TestImports, imports, and rawTestImports at the start of + // these lists to preserve the alignment. + // Note that p.Internal.Imports may not be aligned with p.Imports/p.Internal.RawImports, + // but we insert at the beginning there too just for consistency. + ptest.Imports = str.StringList(p.TestImports, p.Imports) + ptest.Internal.Imports = append(imports, p.Internal.Imports...) + ptest.Internal.RawImports = str.StringList(rawTestImports, p.Internal.RawImports) + ptest.Internal.ForceLibrary = true + ptest.Internal.Build = new(build.Package) + *ptest.Internal.Build = *p.Internal.Build + m := map[string][]token.Position{} + for k, v := range p.Internal.Build.ImportPos { + m[k] = append(m[k], v...) + } + for k, v := range p.Internal.Build.TestImportPos { + m[k] = append(m[k], v...) + } + ptest.Internal.Build.ImportPos = m + } else { + ptest = p + } + + // External test package. + if len(p.XTestGoFiles) > 0 { + pxtest = &Package{ + PackagePublic: PackagePublic{ + Name: p.Name + "_test", + ImportPath: p.ImportPath + "_test", + Root: p.Root, + Dir: p.Dir, + GoFiles: p.XTestGoFiles, + Imports: p.XTestImports, + ForTest: p.ImportPath, + }, + Internal: PackageInternal{ + LocalPrefix: p.Internal.LocalPrefix, + Build: &build.Package{ + ImportPos: p.Internal.Build.XTestImportPos, + }, + Imports: ximports, + RawImports: rawXTestImports, + + Asmflags: p.Internal.Asmflags, + Gcflags: p.Internal.Gcflags, + Ldflags: p.Internal.Ldflags, + Gccgoflags: p.Internal.Gccgoflags, + }, + } + if pxtestNeedsPtest { + pxtest.Internal.Imports = append(pxtest.Internal.Imports, ptest) + } + } + + // Build main package. + pmain = &Package{ + PackagePublic: PackagePublic{ + Name: "main", + Dir: p.Dir, + GoFiles: []string{"_testmain.go"}, + ImportPath: p.ImportPath + ".test", + Root: p.Root, + Imports: str.StringList(TestMainDeps), + }, + Internal: PackageInternal{ + Build: &build.Package{Name: "main"}, + Asmflags: p.Internal.Asmflags, + Gcflags: p.Internal.Gcflags, + Ldflags: p.Internal.Ldflags, + Gccgoflags: p.Internal.Gccgoflags, + }, + } + + // The generated main also imports testing, regexp, and os. + // Also the linker introduces implicit dependencies reported by LinkerDeps. + stk.Push("testmain") + deps := TestMainDeps // cap==len, so safe for append + for _, d := range LinkerDeps(p) { + deps = append(deps, d) + } + for _, dep := range deps { + if dep == ptest.ImportPath { + pmain.Internal.Imports = append(pmain.Internal.Imports, ptest) + } else { + p1 := LoadImport(dep, "", nil, &stk, nil, 0) + if p1.Error != nil { + return nil, nil, nil, p1.Error + } + pmain.Internal.Imports = append(pmain.Internal.Imports, p1) + } + } + stk.Pop() + + if cover != nil && cover.Pkgs != nil { + // Add imports, but avoid duplicates. + seen := map[*Package]bool{p: true, ptest: true} + for _, p1 := range pmain.Internal.Imports { + seen[p1] = true + } + for _, p1 := range cover.Pkgs { + if !seen[p1] { + seen[p1] = true + pmain.Internal.Imports = append(pmain.Internal.Imports, p1) + } + } + } + + // Do initial scan for metadata needed for writing _testmain.go + // Use that metadata to update the list of imports for package main. + // The list of imports is used by recompileForTest and by the loop + // afterward that gathers t.Cover information. + t, err := loadTestFuncs(ptest) + if err != nil { + return nil, nil, nil, err + } + t.Cover = cover + if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 { + pmain.Internal.Imports = append(pmain.Internal.Imports, ptest) + pmain.Imports = append(pmain.Imports, ptest.ImportPath) + t.ImportTest = true + } + if pxtest != nil { + pmain.Internal.Imports = append(pmain.Internal.Imports, pxtest) + pmain.Imports = append(pmain.Imports, pxtest.ImportPath) + t.ImportXtest = true + } + + // Sort and dedup pmain.Imports. + // Only matters for go list -test output. + sort.Strings(pmain.Imports) + w := 0 + for _, path := range pmain.Imports { + if w == 0 || path != pmain.Imports[w-1] { + pmain.Imports[w] = path + w++ + } + } + pmain.Imports = pmain.Imports[:w] + pmain.Internal.RawImports = str.StringList(pmain.Imports) + + if ptest != p { + // We have made modifications to the package p being tested + // and are rebuilding p (as ptest). + // Arrange to rebuild all packages q such that + // the test depends on q and q depends on p. + // This makes sure that q sees the modifications to p. + // Strictly speaking, the rebuild is only necessary if the + // modifications to p change its export metadata, but + // determining that is a bit tricky, so we rebuild always. + recompileForTest(pmain, p, ptest, pxtest) + } + + // Should we apply coverage analysis locally, + // only for this package and only for this test? + // Yes, if -cover is on but -coverpkg has not specified + // a list of packages for global coverage. + if cover != nil && cover.Local { + ptest.Internal.CoverMode = cover.Mode + var coverFiles []string + coverFiles = append(coverFiles, ptest.GoFiles...) + coverFiles = append(coverFiles, ptest.CgoFiles...) + ptest.Internal.CoverVars = cover.DeclVars(ptest, coverFiles...) + } + + for _, cp := range pmain.Internal.Imports { + if len(cp.Internal.CoverVars) > 0 { + t.Cover.Vars = append(t.Cover.Vars, coverInfo{cp, cp.Internal.CoverVars}) + } + } + + data, err := formatTestmain(t) + if err != nil { + return nil, nil, nil, err + } + pmain.Internal.TestmainGo = &data + + return pmain, ptest, pxtest, nil +} + +func testImportStack(top string, p *Package, target string) []string { + stk := []string{top, p.ImportPath} +Search: + for p.ImportPath != target { + for _, p1 := range p.Internal.Imports { + if p1.ImportPath == target || str.Contains(p1.Deps, target) { + stk = append(stk, p1.ImportPath) + p = p1 + continue Search + } + } + // Can't happen, but in case it does... + stk = append(stk, "<lost path to cycle>") + break + } + return stk +} + +func recompileForTest(pmain, preal, ptest, pxtest *Package) { + // The "test copy" of preal is ptest. + // For each package that depends on preal, make a "test copy" + // that depends on ptest. And so on, up the dependency tree. + testCopy := map[*Package]*Package{preal: ptest} + for _, p := range PackageList([]*Package{pmain}) { + if p == preal { + continue + } + // Copy on write. + didSplit := p == pmain || p == pxtest + split := func() { + if didSplit { + return + } + didSplit = true + if testCopy[p] != nil { + panic("recompileForTest loop") + } + p1 := new(Package) + testCopy[p] = p1 + *p1 = *p + p1.ForTest = preal.ImportPath + p1.Internal.Imports = make([]*Package, len(p.Internal.Imports)) + copy(p1.Internal.Imports, p.Internal.Imports) + p1.Imports = make([]string, len(p.Imports)) + copy(p1.Imports, p.Imports) + p = p1 + p.Target = "" + } + + // Update p.Internal.Imports to use test copies. + for i, imp := range p.Internal.Imports { + if p1 := testCopy[imp]; p1 != nil && p1 != imp { + split() + p.Internal.Imports[i] = p1 + } + } + } +} + +// isTestFunc tells whether fn has the type of a testing function. arg +// specifies the parameter type we look for: B, M or T. +func isTestFunc(fn *ast.FuncDecl, arg string) bool { + if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || + fn.Type.Params.List == nil || + len(fn.Type.Params.List) != 1 || + len(fn.Type.Params.List[0].Names) > 1 { + return false + } + ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr) + if !ok { + return false + } + // We can't easily check that the type is *testing.M + // because we don't know how testing has been imported, + // but at least check that it's *M or *something.M. + // Same applies for B and T. + if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg { + return true + } + if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg { + return true + } + return false +} + +// isTest tells whether name looks like a test (or benchmark, according to prefix). +// It is a Test (say) if there is a character after Test that is not a lower-case letter. +// We don't want TesticularCancer. +func isTest(name, prefix string) bool { + if !strings.HasPrefix(name, prefix) { + return false + } + if len(name) == len(prefix) { // "Test" is ok + return true + } + rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) + return !unicode.IsLower(rune) +} + +type coverInfo struct { + Package *Package + Vars map[string]*CoverVar +} + +// loadTestFuncs returns the testFuncs describing the tests that will be run. +func loadTestFuncs(ptest *Package) (*testFuncs, error) { + t := &testFuncs{ + Package: ptest, + } + for _, file := range ptest.TestGoFiles { + if err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); err != nil { + return nil, err + } + } + for _, file := range ptest.XTestGoFiles { + if err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); err != nil { + return nil, err + } + } + return t, nil +} + +// formatTestmain returns the content of the _testmain.go file for t. +func formatTestmain(t *testFuncs) ([]byte, error) { + var buf bytes.Buffer + if err := testmainTmpl.Execute(&buf, t); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +type testFuncs struct { + Tests []testFunc + Benchmarks []testFunc + Examples []testFunc + TestMain *testFunc + Package *Package + ImportTest bool + NeedTest bool + ImportXtest bool + NeedXtest bool + Cover *TestCover +} + +// ImportPath returns the import path of the package being tested, if it is within GOPATH. +// This is printed by the testing package when running benchmarks. +func (t *testFuncs) ImportPath() string { + pkg := t.Package.ImportPath + if strings.HasPrefix(pkg, "_/") { + return "" + } + if pkg == "command-line-arguments" { + return "" + } + return pkg +} + +// Covered returns a string describing which packages are being tested for coverage. +// If the covered package is the same as the tested package, it returns the empty string. +// Otherwise it is a comma-separated human-readable list of packages beginning with +// " in", ready for use in the coverage message. +func (t *testFuncs) Covered() string { + if t.Cover == nil || t.Cover.Paths == nil { + return "" + } + return " in " + strings.Join(t.Cover.Paths, ", ") +} + +// Tested returns the name of the package being tested. +func (t *testFuncs) Tested() string { + return t.Package.Name +} + +type testFunc struct { + Package string // imported package name (_test or _xtest) + Name string // function name + Output string // output, for examples + Unordered bool // output is allowed to be unordered. +} + +var testFileSet = token.NewFileSet() + +func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { + f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments) + if err != nil { + return base.ExpandScanner(err) + } + for _, d := range f.Decls { + n, ok := d.(*ast.FuncDecl) + if !ok { + continue + } + if n.Recv != nil { + continue + } + name := n.Name.String() + switch { + case name == "TestMain": + if isTestFunc(n, "T") { + t.Tests = append(t.Tests, testFunc{pkg, name, "", false}) + *doImport, *seen = true, true + continue + } + err := checkTestFunc(n, "M") + if err != nil { + return err + } + if t.TestMain != nil { + return errors.New("multiple definitions of TestMain") + } + t.TestMain = &testFunc{pkg, name, "", false} + *doImport, *seen = true, true + case isTest(name, "Test"): + err := checkTestFunc(n, "T") + if err != nil { + return err + } + t.Tests = append(t.Tests, testFunc{pkg, name, "", false}) + *doImport, *seen = true, true + case isTest(name, "Benchmark"): + err := checkTestFunc(n, "B") + if err != nil { + return err + } + t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false}) + *doImport, *seen = true, true + } + } + ex := doc.Examples(f) + sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order }) + for _, e := range ex { + *doImport = true // import test file whether executed or not + if e.Output == "" && !e.EmptyOutput { + // Don't run examples with no output. + continue + } + t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered}) + *seen = true + } + return nil +} + +func checkTestFunc(fn *ast.FuncDecl, arg string) error { + if !isTestFunc(fn, arg) { + name := fn.Name.String() + pos := testFileSet.Position(fn.Pos()) + return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg) + } + return nil +} + +var testmainTmpl = template.Must(template.New("main").Parse(` +package main + +import ( +{{if not .TestMain}} + "os" +{{end}} + "testing" + "testing/internal/testdeps" + +{{if .ImportTest}} + {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}} +{{end}} +{{if .ImportXtest}} + {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}} +{{end}} +{{if .Cover}} +{{range $i, $p := .Cover.Vars}} + _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}} +{{end}} +{{end}} +) + +var tests = []testing.InternalTest{ +{{range .Tests}} + {"{{.Name}}", {{.Package}}.{{.Name}}}, +{{end}} +} + +var benchmarks = []testing.InternalBenchmark{ +{{range .Benchmarks}} + {"{{.Name}}", {{.Package}}.{{.Name}}}, +{{end}} +} + +var examples = []testing.InternalExample{ +{{range .Examples}} + {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}}, +{{end}} +} + +func init() { + testdeps.ImportPath = {{.ImportPath | printf "%q"}} +} + +{{if .Cover}} + +// Only updated by init functions, so no need for atomicity. +var ( + coverCounters = make(map[string][]uint32) + coverBlocks = make(map[string][]testing.CoverBlock) +) + +func init() { + {{range $i, $p := .Cover.Vars}} + {{range $file, $cover := $p.Vars}} + coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:]) + {{end}} + {{end}} +} + +func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) { + if 3*len(counter) != len(pos) || len(counter) != len(numStmts) { + panic("coverage: mismatched sizes") + } + if coverCounters[fileName] != nil { + // Already registered. + return + } + coverCounters[fileName] = counter + block := make([]testing.CoverBlock, len(counter)) + for i := range counter { + block[i] = testing.CoverBlock{ + Line0: pos[3*i+0], + Col0: uint16(pos[3*i+2]), + Line1: pos[3*i+1], + Col1: uint16(pos[3*i+2]>>16), + Stmts: numStmts[i], + } + } + coverBlocks[fileName] = block +} +{{end}} + +func main() { +{{if .Cover}} + testing.RegisterCover(testing.Cover{ + Mode: {{printf "%q" .Cover.Mode}}, + Counters: coverCounters, + Blocks: coverBlocks, + CoveredPackages: {{printf "%q" .Covered}}, + }) +{{end}} + m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples) +{{with .TestMain}} + {{.Package}}.{{.Name}}(m) +{{else}} + os.Exit(m.Run()) +{{end}} +} + +`)) diff --git a/libgo/go/cmd/go/internal/modcmd/download.go b/libgo/go/cmd/go/internal/modcmd/download.go new file mode 100644 index 0000000..cf42eff --- /dev/null +++ b/libgo/go/cmd/go/internal/modcmd/download.go @@ -0,0 +1,133 @@ +// Copyright 2018 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 modcmd + +import ( + "cmd/go/internal/base" + "cmd/go/internal/modfetch" + "cmd/go/internal/modload" + "cmd/go/internal/module" + "cmd/go/internal/par" + "encoding/json" + "os" +) + +var cmdDownload = &base.Command{ + UsageLine: "go mod download [-dir] [-json] [modules]", + Short: "download modules to local cache", + Long: ` +Download downloads the named modules, which can be module patterns selecting +dependencies of the main module or module queries of the form path@version. +With no arguments, download applies to all dependencies of the main module. + +The go command will automatically download modules as needed during ordinary +execution. The "go mod download" command is useful mainly for pre-filling +the local cache or to compute the answers for a Go module proxy. + +By default, download reports errors to standard error but is otherwise silent. +The -json flag causes download to print a sequence of JSON objects +to standard output, describing each downloaded module (or failure), +corresponding to this Go struct: + + type Module struct { + Path string // module path + Version string // module version + Error string // error loading module + Info string // absolute path to cached .info file + GoMod string // absolute path to cached .mod file + Zip string // absolute path to cached .zip file + Dir string // absolute path to cached source root directory + Sum string // checksum for path, version (as in go.sum) + GoModSum string // checksum for go.mod (as in go.sum) + } + +See 'go help modules' for more about module queries. + `, +} + +var downloadJSON = cmdDownload.Flag.Bool("json", false, "") + +func init() { + cmdDownload.Run = runDownload // break init cycle +} + +type moduleJSON struct { + Path string `json:",omitempty"` + Version string `json:",omitempty"` + Error string `json:",omitempty"` + Info string `json:",omitempty"` + GoMod string `json:",omitempty"` + Zip string `json:",omitempty"` + Dir string `json:",omitempty"` + Sum string `json:",omitempty"` + GoModSum string `json:",omitempty"` +} + +func runDownload(cmd *base.Command, args []string) { + if len(args) == 0 { + args = []string{"all"} + } + + var mods []*moduleJSON + var work par.Work + listU := false + listVersions := false + for _, info := range modload.ListModules(args, listU, listVersions) { + if info.Replace != nil { + info = info.Replace + } + if info.Version == "" { + continue + } + m := &moduleJSON{ + Path: info.Path, + Version: info.Version, + } + mods = append(mods, m) + work.Add(m) + } + + work.Do(10, func(item interface{}) { + m := item.(*moduleJSON) + var err error + m.Info, err = modfetch.InfoFile(m.Path, m.Version) + if err != nil { + m.Error = err.Error() + return + } + m.GoMod, err = modfetch.GoModFile(m.Path, m.Version) + if err != nil { + m.Error = err.Error() + return + } + m.GoModSum, err = modfetch.GoModSum(m.Path, m.Version) + if err != nil { + m.Error = err.Error() + return + } + mod := module.Version{Path: m.Path, Version: m.Version} + m.Zip, err = modfetch.DownloadZip(mod) + if err != nil { + m.Error = err.Error() + return + } + m.Sum = modfetch.Sum(mod) + m.Dir, err = modfetch.Download(mod) + if err != nil { + m.Error = err.Error() + return + } + }) + + if *downloadJSON { + for _, m := range mods { + b, err := json.MarshalIndent(m, "", "\t") + if err != nil { + base.Fatalf("%v", err) + } + os.Stdout.Write(append(b, '\n')) + } + } +} diff --git a/libgo/go/cmd/go/internal/modcmd/edit.go b/libgo/go/cmd/go/internal/modcmd/edit.go new file mode 100644 index 0000000..5fea3e4 --- /dev/null +++ b/libgo/go/cmd/go/internal/modcmd/edit.go @@ -0,0 +1,382 @@ +// Copyright 2018 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. + +// go mod edit + +package modcmd + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "cmd/go/internal/base" + "cmd/go/internal/modfile" + "cmd/go/internal/modload" + "cmd/go/internal/module" +) + +var cmdEdit = &base.Command{ + UsageLine: "go mod edit [editing flags] [go.mod]", + Short: "edit go.mod from tools or scripts", + Long: ` +Edit provides a command-line interface for editing go.mod, +for use primarily by tools or scripts. It reads only go.mod; +it does not look up information about the modules involved. +By default, edit reads and writes the go.mod file of the main module, +but a different target file can be specified after the editing flags. + +The editing flags specify a sequence of editing operations. + +The -fmt flag reformats the go.mod file without making other changes. +This reformatting is also implied by any other modifications that use or +rewrite the go.mod file. The only time this flag is needed is if no other +flags are specified, as in 'go mod edit -fmt'. + +The -module flag changes the module's path (the go.mod file's module line). + +The -require=path@version and -droprequire=path flags +add and drop a requirement on the given module path and version. +Note that -require overrides any existing requirements on path. +These flags are mainly for tools that understand the module graph. +Users should prefer 'go get path@version' or 'go get path@none', +which make other go.mod adjustments as needed to satisfy +constraints imposed by other modules. + +The -exclude=path@version and -dropexclude=path@version flags +add and drop an exclusion for the given module path and version. +Note that -exclude=path@version is a no-op if that exclusion already exists. + +The -replace=old[@v]=new[@v] and -dropreplace=old[@v] flags +add and drop a replacement of the given module path and version pair. +If the @v in old@v is omitted, the replacement applies to all versions +with the old module path. If the @v in new@v is omitted, the new path +should be a local module root directory, not a module path. +Note that -replace overrides any existing replacements for old[@v]. + +The -require, -droprequire, -exclude, -dropexclude, -replace, +and -dropreplace editing flags may be repeated, and the changes +are applied in the order given. + +The -print flag prints the final go.mod in its text format instead of +writing it back to go.mod. + +The -json flag prints the final go.mod file in JSON format instead of +writing it back to go.mod. The JSON output corresponds to these Go types: + + type Module struct { + Path string + Version string + } + + type GoMod struct { + Module Module + Require []Require + Exclude []Module + Replace []Replace + } + + type Require struct { + Path string + Version string + Indirect bool + } + + type Replace struct { + Old Module + New Module + } + +Note that this only describes the go.mod file itself, not other modules +referred to indirectly. For the full set of modules available to a build, +use 'go list -m -json all'. + +For example, a tool can obtain the go.mod as a data structure by +parsing the output of 'go mod edit -json' and can then make changes +by invoking 'go mod edit' with -require, -exclude, and so on. + `, +} + +var ( + editFmt = cmdEdit.Flag.Bool("fmt", false, "") + // editGo = cmdEdit.Flag.String("go", "", "") + editJSON = cmdEdit.Flag.Bool("json", false, "") + editPrint = cmdEdit.Flag.Bool("print", false, "") + editModule = cmdEdit.Flag.String("module", "", "") + edits []func(*modfile.File) // edits specified in flags +) + +type flagFunc func(string) + +func (f flagFunc) String() string { return "" } +func (f flagFunc) Set(s string) error { f(s); return nil } + +func init() { + cmdEdit.Run = runEdit // break init cycle + + cmdEdit.Flag.Var(flagFunc(flagRequire), "require", "") + cmdEdit.Flag.Var(flagFunc(flagDropRequire), "droprequire", "") + cmdEdit.Flag.Var(flagFunc(flagExclude), "exclude", "") + cmdEdit.Flag.Var(flagFunc(flagDropReplace), "dropreplace", "") + cmdEdit.Flag.Var(flagFunc(flagReplace), "replace", "") + cmdEdit.Flag.Var(flagFunc(flagDropExclude), "dropexclude", "") + + base.AddBuildFlagsNX(&cmdEdit.Flag) +} + +func runEdit(cmd *base.Command, args []string) { + anyFlags := + *editModule != "" || + *editJSON || + *editPrint || + *editFmt || + len(edits) > 0 + + if !anyFlags { + base.Fatalf("go mod edit: no flags specified (see 'go help mod edit').") + } + + if *editJSON && *editPrint { + base.Fatalf("go mod edit: cannot use both -json and -print") + } + + if len(args) > 1 { + base.Fatalf("go mod edit: too many arguments") + } + var gomod string + if len(args) == 1 { + gomod = args[0] + } else { + modload.MustInit() + gomod = filepath.Join(modload.ModRoot, "go.mod") + } + + if *editModule != "" { + if err := module.CheckPath(*editModule); err != nil { + base.Fatalf("go mod: invalid -module: %v", err) + } + } + + // TODO(rsc): Implement -go= once we start advertising it. + + data, err := ioutil.ReadFile(gomod) + if err != nil { + base.Fatalf("go: %v", err) + } + + modFile, err := modfile.Parse(gomod, data, nil) + if err != nil { + base.Fatalf("go: errors parsing %s:\n%s", base.ShortPath(gomod), err) + } + + if *editModule != "" { + modFile.AddModuleStmt(modload.CmdModModule) + } + + if len(edits) > 0 { + for _, edit := range edits { + edit(modFile) + } + } + modFile.SortBlocks() + modFile.Cleanup() // clean file after edits + + if *editJSON { + editPrintJSON(modFile) + return + } + + data, err = modFile.Format() + if err != nil { + base.Fatalf("go: %v", err) + } + + if *editPrint { + os.Stdout.Write(data) + return + } + + if err := ioutil.WriteFile(gomod, data, 0666); err != nil { + base.Fatalf("go: %v", err) + } +} + +// parsePathVersion parses -flag=arg expecting arg to be path@version. +func parsePathVersion(flag, arg string) (path, version string) { + i := strings.Index(arg, "@") + if i < 0 { + base.Fatalf("go mod: -%s=%s: need path@version", flag, arg) + } + path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:]) + if err := module.CheckPath(path); err != nil { + base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err) + } + + // We don't call modfile.CheckPathVersion, because that insists + // on versions being in semver form, but here we want to allow + // versions like "master" or "1234abcdef", which the go command will resolve + // the next time it runs (or during -fix). + // Even so, we need to make sure the version is a valid token. + if modfile.MustQuote(version) { + base.Fatalf("go mod: -%s=%s: invalid version %q", flag, arg, version) + } + + return path, version +} + +// parsePath parses -flag=arg expecting arg to be path (not path@version). +func parsePath(flag, arg string) (path string) { + if strings.Contains(arg, "@") { + base.Fatalf("go mod: -%s=%s: need just path, not path@version", flag, arg) + } + path = arg + if err := module.CheckPath(path); err != nil { + base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err) + } + return path +} + +// parsePathVersionOptional parses path[@version], using adj to +// describe any errors. +func parsePathVersionOptional(adj, arg string, allowDirPath bool) (path, version string, err error) { + if i := strings.Index(arg, "@"); i < 0 { + path = arg + } else { + path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:]) + } + if err := module.CheckPath(path); err != nil { + if !allowDirPath || !modfile.IsDirectoryPath(path) { + return path, version, fmt.Errorf("invalid %s path: %v", adj, err) + } + } + if path != arg && modfile.MustQuote(version) { + return path, version, fmt.Errorf("invalid %s version: %q", adj, version) + } + return path, version, nil +} + +// flagRequire implements the -require flag. +func flagRequire(arg string) { + path, version := parsePathVersion("require", arg) + edits = append(edits, func(f *modfile.File) { + if err := f.AddRequire(path, version); err != nil { + base.Fatalf("go mod: -require=%s: %v", arg, err) + } + }) +} + +// flagDropRequire implements the -droprequire flag. +func flagDropRequire(arg string) { + path := parsePath("droprequire", arg) + edits = append(edits, func(f *modfile.File) { + if err := f.DropRequire(path); err != nil { + base.Fatalf("go mod: -droprequire=%s: %v", arg, err) + } + }) +} + +// flagExclude implements the -exclude flag. +func flagExclude(arg string) { + path, version := parsePathVersion("exclude", arg) + edits = append(edits, func(f *modfile.File) { + if err := f.AddExclude(path, version); err != nil { + base.Fatalf("go mod: -exclude=%s: %v", arg, err) + } + }) +} + +// flagDropExclude implements the -dropexclude flag. +func flagDropExclude(arg string) { + path, version := parsePathVersion("dropexclude", arg) + edits = append(edits, func(f *modfile.File) { + if err := f.DropExclude(path, version); err != nil { + base.Fatalf("go mod: -dropexclude=%s: %v", arg, err) + } + }) +} + +// flagReplace implements the -replace flag. +func flagReplace(arg string) { + var i int + if i = strings.Index(arg, "="); i < 0 { + base.Fatalf("go mod: -replace=%s: need old[@v]=new[@w] (missing =)", arg) + } + old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:]) + if strings.HasPrefix(new, ">") { + base.Fatalf("go mod: -replace=%s: separator between old and new is =, not =>", arg) + } + oldPath, oldVersion, err := parsePathVersionOptional("old", old, false) + if err != nil { + base.Fatalf("go mod: -replace=%s: %v", arg, err) + } + newPath, newVersion, err := parsePathVersionOptional("new", new, true) + if err != nil { + base.Fatalf("go mod: -replace=%s: %v", arg, err) + } + if newPath == new && !modfile.IsDirectoryPath(new) { + base.Fatalf("go mod: -replace=%s: unversioned new path must be local directory", arg) + } + + edits = append(edits, func(f *modfile.File) { + if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil { + base.Fatalf("go mod: -replace=%s: %v", arg, err) + } + }) +} + +// flagDropReplace implements the -dropreplace flag. +func flagDropReplace(arg string) { + path, version, err := parsePathVersionOptional("old", arg, true) + if err != nil { + base.Fatalf("go mod: -dropreplace=%s: %v", arg, err) + } + edits = append(edits, func(f *modfile.File) { + if err := f.DropReplace(path, version); err != nil { + base.Fatalf("go mod: -dropreplace=%s: %v", arg, err) + } + }) +} + +// fileJSON is the -json output data structure. +type fileJSON struct { + Module module.Version + Require []requireJSON + Exclude []module.Version + Replace []replaceJSON +} + +type requireJSON struct { + Path string + Version string `json:",omitempty"` + Indirect bool `json:",omitempty"` +} + +type replaceJSON struct { + Old module.Version + New module.Version +} + +// editPrintJSON prints the -json output. +func editPrintJSON(modFile *modfile.File) { + var f fileJSON + f.Module = modFile.Module.Mod + for _, r := range modFile.Require { + f.Require = append(f.Require, requireJSON{Path: r.Mod.Path, Version: r.Mod.Version, Indirect: r.Indirect}) + } + for _, x := range modFile.Exclude { + f.Exclude = append(f.Exclude, x.Mod) + } + for _, r := range modFile.Replace { + f.Replace = append(f.Replace, replaceJSON{r.Old, r.New}) + } + data, err := json.MarshalIndent(&f, "", "\t") + if err != nil { + base.Fatalf("go: internal error: %v", err) + } + data = append(data, '\n') + os.Stdout.Write(data) +} diff --git a/libgo/go/cmd/go/internal/modcmd/graph.go b/libgo/go/cmd/go/internal/modcmd/graph.go new file mode 100644 index 0000000..5825c6d --- /dev/null +++ b/libgo/go/cmd/go/internal/modcmd/graph.go @@ -0,0 +1,73 @@ +// Copyright 2018 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. + +// go mod graph + +package modcmd + +import ( + "bufio" + "os" + "sort" + + "cmd/go/internal/base" + "cmd/go/internal/modload" + "cmd/go/internal/module" + "cmd/go/internal/par" +) + +var cmdGraph = &base.Command{ + UsageLine: "go mod graph", + Short: "print module requirement graph", + Long: ` +Graph prints the module requirement graph (with replacements applied) +in text form. Each line in the output has two space-separated fields: a module +and one of its requirements. Each module is identified as a string of the form +path@version, except for the main module, which has no @version suffix. + `, + Run: runGraph, +} + +func runGraph(cmd *base.Command, args []string) { + if len(args) > 0 { + base.Fatalf("go mod graph: graph takes no arguments") + } + modload.LoadBuildList() + + reqs := modload.MinReqs() + format := func(m module.Version) string { + if m.Version == "" { + return m.Path + } + return m.Path + "@" + m.Version + } + + // Note: using par.Work only to manage work queue. + // No parallelism here, so no locking. + var out []string + var deps int // index in out where deps start + var work par.Work + work.Add(modload.Target) + work.Do(1, func(item interface{}) { + m := item.(module.Version) + list, _ := reqs.Required(m) + for _, r := range list { + work.Add(r) + out = append(out, format(m)+" "+format(r)+"\n") + } + if m == modload.Target { + deps = len(out) + } + }) + + sort.Slice(out[deps:], func(i, j int) bool { + return out[deps+i][0] < out[deps+j][0] + }) + + w := bufio.NewWriter(os.Stdout) + for _, line := range out { + w.WriteString(line) + } + w.Flush() +} diff --git a/libgo/go/cmd/go/internal/modcmd/init.go b/libgo/go/cmd/go/internal/modcmd/init.go new file mode 100644 index 0000000..f510a46 --- /dev/null +++ b/libgo/go/cmd/go/internal/modcmd/init.go @@ -0,0 +1,41 @@ +// Copyright 2018 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. + +// go mod init + +package modcmd + +import ( + "cmd/go/internal/base" + "cmd/go/internal/modload" + "os" +) + +var cmdInit = &base.Command{ + UsageLine: "go mod init [module]", + Short: "initialize new module in current directory", + Long: ` +Init initializes and writes a new go.mod to the current directory, +in effect creating a new module rooted at the current directory. +The file go.mod must not already exist. +If possible, init will guess the module path from import comments +(see 'go help importpath') or from version control configuration. +To override this guess, supply the module path as an argument. + `, + Run: runInit, +} + +func runInit(cmd *base.Command, args []string) { + modload.CmdModInit = true + if len(args) > 1 { + base.Fatalf("go mod init: too many arguments") + } + if len(args) == 1 { + modload.CmdModModule = args[0] + } + if _, err := os.Stat("go.mod"); err == nil { + base.Fatalf("go mod init: go.mod already exists") + } + modload.InitMod() // does all the hard work +} diff --git a/libgo/go/cmd/go/internal/modcmd/mod.go b/libgo/go/cmd/go/internal/modcmd/mod.go new file mode 100644 index 0000000..f150cc9 --- /dev/null +++ b/libgo/go/cmd/go/internal/modcmd/mod.go @@ -0,0 +1,31 @@ +// Copyright 2018 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 modcmd implements the ``go mod'' command. +package modcmd + +import "cmd/go/internal/base" + +var CmdMod = &base.Command{ + UsageLine: "go mod", + Short: "module maintenance", + Long: `Go mod provides access to operations on modules. + +Note that support for modules is built into all the go commands, +not just 'go mod'. For example, day-to-day adding, removing, upgrading, +and downgrading of dependencies should be done using 'go get'. +See 'go help modules' for an overview of module functionality. + `, + + Commands: []*base.Command{ + cmdDownload, + cmdEdit, + cmdGraph, + cmdInit, + cmdTidy, + cmdVendor, + cmdVerify, + cmdWhy, + }, +} diff --git a/libgo/go/cmd/go/internal/modcmd/tidy.go b/libgo/go/cmd/go/internal/modcmd/tidy.go new file mode 100644 index 0000000..f2063a9 --- /dev/null +++ b/libgo/go/cmd/go/internal/modcmd/tidy.go @@ -0,0 +1,90 @@ +// Copyright 2018 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. + +// go mod tidy + +package modcmd + +import ( + "fmt" + "os" + + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/modfetch" + "cmd/go/internal/modload" + "cmd/go/internal/module" +) + +var cmdTidy = &base.Command{ + UsageLine: "go mod tidy [-v]", + Short: "add missing and remove unused modules", + Long: ` +Tidy makes sure go.mod matches the source code in the module. +It adds any missing modules necessary to build the current module's +packages and dependencies, and it removes unused modules that +don't provide any relevant packages. It also adds any missing entries +to go.sum and removes any unnecessary ones. + +The -v flag causes tidy to print information about removed modules +to standard error. + `, +} + +func init() { + cmdTidy.Run = runTidy // break init cycle + cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "") +} + +func runTidy(cmd *base.Command, args []string) { + if len(args) > 0 { + base.Fatalf("go mod tidy: no arguments allowed") + } + + // LoadALL adds missing modules. + // Remove unused modules. + used := make(map[module.Version]bool) + for _, pkg := range modload.LoadALL() { + used[modload.PackageModule(pkg)] = true + } + used[modload.Target] = true // note: LoadALL initializes Target + + inGoMod := make(map[string]bool) + for _, r := range modload.ModFile().Require { + inGoMod[r.Mod.Path] = true + } + + var keep []module.Version + for _, m := range modload.BuildList() { + if used[m] { + keep = append(keep, m) + } else if cfg.BuildV && inGoMod[m.Path] { + fmt.Fprintf(os.Stderr, "unused %s\n", m.Path) + } + } + modload.SetBuildList(keep) + modTidyGoSum() // updates memory copy; WriteGoMod on next line flushes it out + modload.WriteGoMod() +} + +// modTidyGoSum resets the go.sum file content +// to be exactly what's needed for the current go.mod. +func modTidyGoSum() { + // Assuming go.sum already has at least enough from the successful load, + // we only have to tell modfetch what needs keeping. + reqs := modload.Reqs() + keep := make(map[module.Version]bool) + var walk func(module.Version) + walk = func(m module.Version) { + keep[m] = true + list, _ := reqs.Required(m) + for _, r := range list { + if !keep[r] { + walk(r) + } + } + } + walk(modload.Target) + modfetch.TrimGoSum(keep) +} diff --git a/libgo/go/cmd/go/internal/modcmd/vendor.go b/libgo/go/cmd/go/internal/modcmd/vendor.go new file mode 100644 index 0000000..62e7458 --- /dev/null +++ b/libgo/go/cmd/go/internal/modcmd/vendor.go @@ -0,0 +1,200 @@ +// Copyright 2018 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 modcmd + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/modload" + "cmd/go/internal/module" +) + +var cmdVendor = &base.Command{ + UsageLine: "go mod vendor [-v]", + Short: "make vendored copy of dependencies", + Long: ` +Vendor resets the main module's vendor directory to include all packages +needed to build and test all the main module's packages. +It does not include test code for vendored packages. + +The -v flag causes vendor to print the names of vendored +modules and packages to standard error. + `, + Run: runVendor, +} + +func init() { + cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "") +} + +func runVendor(cmd *base.Command, args []string) { + if len(args) != 0 { + base.Fatalf("go mod vendor: vendor takes no arguments") + } + pkgs := modload.LoadVendor() + + vdir := filepath.Join(modload.ModRoot, "vendor") + if err := os.RemoveAll(vdir); err != nil { + base.Fatalf("go vendor: %v", err) + } + + modpkgs := make(map[module.Version][]string) + for _, pkg := range pkgs { + m := modload.PackageModule(pkg) + if m == modload.Target { + continue + } + modpkgs[m] = append(modpkgs[m], pkg) + } + + var buf bytes.Buffer + for _, m := range modload.BuildList()[1:] { + if pkgs := modpkgs[m]; len(pkgs) > 0 { + repl := "" + if r := modload.Replacement(m); r.Path != "" { + repl = " => " + r.Path + if r.Version != "" { + repl += " " + r.Version + } + } + fmt.Fprintf(&buf, "# %s %s%s\n", m.Path, m.Version, repl) + if cfg.BuildV { + fmt.Fprintf(os.Stderr, "# %s %s%s\n", m.Path, m.Version, repl) + } + for _, pkg := range pkgs { + fmt.Fprintf(&buf, "%s\n", pkg) + if cfg.BuildV { + fmt.Fprintf(os.Stderr, "%s\n", pkg) + } + vendorPkg(vdir, pkg) + } + } + } + if buf.Len() == 0 { + fmt.Fprintf(os.Stderr, "go: no dependencies to vendor\n") + return + } + if err := ioutil.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil { + base.Fatalf("go vendor: %v", err) + } +} + +func vendorPkg(vdir, pkg string) { + realPath := modload.ImportMap(pkg) + if realPath != pkg && modload.ImportMap(realPath) != "" { + fmt.Fprintf(os.Stderr, "warning: %s imported as both %s and %s; making two copies.\n", realPath, realPath, pkg) + } + + dst := filepath.Join(vdir, pkg) + src := modload.PackageDir(realPath) + if src == "" { + fmt.Fprintf(os.Stderr, "internal error: no pkg for %s -> %s\n", pkg, realPath) + } + copyDir(dst, src, matchNonTest) + if m := modload.PackageModule(realPath); m.Path != "" { + copyMetadata(m.Path, realPath, dst, src) + } +} + +type metakey struct { + modPath string + dst string +} + +var copiedMetadata = make(map[metakey]bool) + +// copyMetadata copies metadata files from parents of src to parents of dst, +// stopping after processing the src parent for modPath. +func copyMetadata(modPath, pkg, dst, src string) { + for parent := 0; ; parent++ { + if copiedMetadata[metakey{modPath, dst}] { + break + } + copiedMetadata[metakey{modPath, dst}] = true + if parent > 0 { + copyDir(dst, src, matchMetadata) + } + if modPath == pkg { + break + } + pkg = filepath.Dir(pkg) + dst = filepath.Dir(dst) + src = filepath.Dir(src) + } +} + +// metaPrefixes is the list of metadata file prefixes. +// Vendoring copies metadata files from parents of copied directories. +// Note that this list could be arbitrarily extended, and it is longer +// in other tools (such as godep or dep). By using this limited set of +// prefixes and also insisting on capitalized file names, we are trying +// to nudge people toward more agreement on the naming +// and also trying to avoid false positives. +var metaPrefixes = []string{ + "AUTHORS", + "CONTRIBUTORS", + "COPYLEFT", + "COPYING", + "COPYRIGHT", + "LEGAL", + "LICENSE", + "NOTICE", + "PATENTS", +} + +// matchMetadata reports whether info is a metadata file. +func matchMetadata(info os.FileInfo) bool { + name := info.Name() + for _, p := range metaPrefixes { + if strings.HasPrefix(name, p) { + return true + } + } + return false +} + +// matchNonTest reports whether info is any non-test file (including non-Go files). +func matchNonTest(info os.FileInfo) bool { + return !strings.HasSuffix(info.Name(), "_test.go") +} + +// copyDir copies all regular files satisfying match(info) from src to dst. +func copyDir(dst, src string, match func(os.FileInfo) bool) { + files, err := ioutil.ReadDir(src) + if err != nil { + base.Fatalf("go vendor: %v", err) + } + if err := os.MkdirAll(dst, 0777); err != nil { + base.Fatalf("go vendor: %v", err) + } + for _, file := range files { + if file.IsDir() || !file.Mode().IsRegular() || !match(file) { + continue + } + r, err := os.Open(filepath.Join(src, file.Name())) + if err != nil { + base.Fatalf("go vendor: %v", err) + } + w, err := os.Create(filepath.Join(dst, file.Name())) + if err != nil { + base.Fatalf("go vendor: %v", err) + } + if _, err := io.Copy(w, r); err != nil { + base.Fatalf("go vendor: %v", err) + } + r.Close() + if err := w.Close(); err != nil { + base.Fatalf("go vendor: %v", err) + } + } +} diff --git a/libgo/go/cmd/go/internal/modcmd/verify.go b/libgo/go/cmd/go/internal/modcmd/verify.go new file mode 100644 index 0000000..381c18d --- /dev/null +++ b/libgo/go/cmd/go/internal/modcmd/verify.go @@ -0,0 +1,96 @@ +// Copyright 2018 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 modcmd + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + + "cmd/go/internal/base" + "cmd/go/internal/dirhash" + "cmd/go/internal/modfetch" + "cmd/go/internal/modload" + "cmd/go/internal/module" +) + +var cmdVerify = &base.Command{ + UsageLine: "go mod verify", + Short: "verify dependencies have expected content", + Long: ` +Verify checks that the dependencies of the current module, +which are stored in a local downloaded source cache, have not been +modified since being downloaded. If all the modules are unmodified, +verify prints "all modules verified." Otherwise it reports which +modules have been changed and causes 'go mod' to exit with a +non-zero status. + `, + Run: runVerify, +} + +func runVerify(cmd *base.Command, args []string) { + if len(args) != 0 { + // NOTE(rsc): Could take a module pattern. + base.Fatalf("go mod verify: verify takes no arguments") + } + ok := true + for _, mod := range modload.LoadBuildList()[1:] { + ok = verifyMod(mod) && ok + } + if ok { + fmt.Printf("all modules verified\n") + } +} + +func verifyMod(mod module.Version) bool { + ok := true + zip, zipErr := modfetch.CachePath(mod, "zip") + if zipErr == nil { + _, zipErr = os.Stat(zip) + } + dir, dirErr := modfetch.DownloadDir(mod) + if dirErr == nil { + _, dirErr = os.Stat(dir) + } + data, err := ioutil.ReadFile(zip + "hash") + if err != nil { + if zipErr != nil && os.IsNotExist(zipErr) && dirErr != nil && os.IsNotExist(dirErr) { + // Nothing downloaded yet. Nothing to verify. + return true + } + base.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err) + return false + } + h := string(bytes.TrimSpace(data)) + + if zipErr != nil && os.IsNotExist(zipErr) { + // ok + } else { + hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash) + if err != nil { + base.Errorf("%s %s: %v", mod.Path, mod.Version, err) + return false + } else if hZ != h { + base.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip) + ok = false + } + } + if dirErr != nil && os.IsNotExist(dirErr) { + // ok + } else { + hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash) + if err != nil { + + base.Errorf("%s %s: %v", mod.Path, mod.Version, err) + return false + } + if hD != h { + base.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir) + ok = false + } + } + return ok +} diff --git a/libgo/go/cmd/go/internal/modcmd/why.go b/libgo/go/cmd/go/internal/modcmd/why.go new file mode 100644 index 0000000..03e0a03 --- /dev/null +++ b/libgo/go/cmd/go/internal/modcmd/why.go @@ -0,0 +1,121 @@ +// Copyright 2018 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 modcmd + +import ( + "cmd/go/internal/base" + "cmd/go/internal/modload" + "cmd/go/internal/module" + "fmt" + "strings" +) + +var cmdWhy = &base.Command{ + UsageLine: "go mod why [-m] [-vendor] packages...", + Short: "explain why packages or modules are needed", + Long: ` +Why shows a shortest path in the import graph from the main module to +each of the listed packages. If the -m flag is given, why treats the +arguments as a list of modules and finds a path to any package in each +of the modules. + +By default, why queries the graph of packages matched by "go list all", +which includes tests for reachable packages. The -vendor flag causes why +to exclude tests of dependencies. + +The output is a sequence of stanzas, one for each package or module +name on the command line, separated by blank lines. Each stanza begins +with a comment line "# package" or "# module" giving the target +package or module. Subsequent lines give a path through the import +graph, one package per line. If the package or module is not +referenced from the main module, the stanza will display a single +parenthesized note indicating that fact. + +For example: + + $ go mod why golang.org/x/text/language golang.org/x/text/encoding + # golang.org/x/text/language + rsc.io/quote + rsc.io/sampler + golang.org/x/text/language + + # golang.org/x/text/encoding + (main module does not need package golang.org/x/text/encoding) + $ + `, +} + +var ( + whyM = cmdWhy.Flag.Bool("m", false, "") + whyVendor = cmdWhy.Flag.Bool("vendor", false, "") +) + +func init() { + cmdWhy.Run = runWhy // break init cycle +} + +func runWhy(cmd *base.Command, args []string) { + loadALL := modload.LoadALL + if *whyVendor { + loadALL = modload.LoadVendor + } + if *whyM { + listU := false + listVersions := false + for _, arg := range args { + if strings.Contains(arg, "@") { + base.Fatalf("go mod why: module query not allowed") + } + } + mods := modload.ListModules(args, listU, listVersions) + byModule := make(map[module.Version][]string) + for _, path := range loadALL() { + m := modload.PackageModule(path) + if m.Path != "" { + byModule[m] = append(byModule[m], path) + } + } + sep := "" + for _, m := range mods { + best := "" + bestDepth := 1000000000 + for _, path := range byModule[module.Version{Path: m.Path, Version: m.Version}] { + d := modload.WhyDepth(path) + if d > 0 && d < bestDepth { + best = path + bestDepth = d + } + } + why := modload.Why(best) + if why == "" { + vendoring := "" + if *whyVendor { + vendoring = " to vendor" + } + why = "(main module does not need" + vendoring + " module " + m.Path + ")\n" + } + fmt.Printf("%s# %s\n%s", sep, m.Path, why) + sep = "\n" + } + } else { + matches := modload.ImportPaths(args) // resolve to packages + loadALL() // rebuild graph, from main module (not from named packages) + sep := "" + for _, m := range matches { + for _, path := range m.Pkgs { + why := modload.Why(path) + if why == "" { + vendoring := "" + if *whyVendor { + vendoring = " to vendor" + } + why = "(main module does not need" + vendoring + " package " + path + ")\n" + } + fmt.Printf("%s# %s\n%s", sep, path, why) + sep = "\n" + } + } + } +} diff --git a/libgo/go/cmd/go/internal/modconv/convert.go b/libgo/go/cmd/go/internal/modconv/convert.go new file mode 100644 index 0000000..6fc6718 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/convert.go @@ -0,0 +1,90 @@ +// Copyright 2018 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 modconv + +import ( + "fmt" + "os" + "sort" + "strings" + "sync" + + "cmd/go/internal/base" + "cmd/go/internal/modfetch" + "cmd/go/internal/modfile" + "cmd/go/internal/module" + "cmd/go/internal/par" + "cmd/go/internal/semver" +) + +// ConvertLegacyConfig converts legacy config to modfile. +// The file argument is slash-delimited. +func ConvertLegacyConfig(f *modfile.File, file string, data []byte) error { + i := strings.LastIndex(file, "/") + j := -2 + if i >= 0 { + j = strings.LastIndex(file[:i], "/") + } + convert := Converters[file[i+1:]] + if convert == nil && j != -2 { + convert = Converters[file[j+1:]] + } + if convert == nil { + return fmt.Errorf("unknown legacy config file %s", file) + } + mf, err := convert(file, data) + if err != nil { + return fmt.Errorf("parsing %s: %v", file, err) + } + + // Convert requirements block, which may use raw SHA1 hashes as versions, + // to valid semver requirement list, respecting major versions. + var work par.Work + for _, r := range mf.Require { + m := r.Mod + if m.Path == "" { + continue + } + work.Add(r.Mod) + } + + var ( + mu sync.Mutex + need = make(map[string]string) + ) + work.Do(10, func(item interface{}) { + r := item.(module.Version) + repo, info, err := modfetch.ImportRepoRev(r.Path, r.Version) + if err != nil { + fmt.Fprintf(os.Stderr, "go: converting %s: stat %s@%s: %v\n", base.ShortPath(file), r.Path, r.Version, err) + return + } + mu.Lock() + path := repo.ModulePath() + // Don't use semver.Max here; need to preserve +incompatible suffix. + if v, ok := need[path]; !ok || semver.Compare(v, info.Version) < 0 { + need[path] = info.Version + } + mu.Unlock() + }) + + var paths []string + for path := range need { + paths = append(paths, path) + } + sort.Strings(paths) + for _, path := range paths { + f.AddNewRequire(path, need[path], false) + } + + for _, r := range mf.Replace { + err := f.AddReplace(r.Old.Path, r.Old.Version, r.New.Path, r.New.Version) + if err != nil { + return fmt.Errorf("add replace: %v", err) + } + } + f.Cleanup() + return nil +} diff --git a/libgo/go/cmd/go/internal/modconv/convert_test.go b/libgo/go/cmd/go/internal/modconv/convert_test.go new file mode 100644 index 0000000..ad27abb --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/convert_test.go @@ -0,0 +1,186 @@ +// Copyright 2018 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 modconv + +import ( + "bytes" + "fmt" + "internal/testenv" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + "cmd/go/internal/cfg" + "cmd/go/internal/modfetch" + "cmd/go/internal/modfetch/codehost" + "cmd/go/internal/modfile" + "cmd/go/internal/module" +) + +func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { + if _, err := exec.LookPath("git"); err != nil { + fmt.Fprintln(os.Stderr, "skipping because git binary not found") + fmt.Println("PASS") + return 0 + } + + dir, err := ioutil.TempDir("", "modconv-test-") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(dir) + modfetch.PkgMod = filepath.Join(dir, "pkg/mod") + codehost.WorkRoot = filepath.Join(dir, "codework") + + return m.Run() +} + +func TestConvertLegacyConfig(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + if testing.Verbose() { + old := cfg.BuildX + defer func() { + cfg.BuildX = old + }() + cfg.BuildX = true + } + + var tests = []struct { + path string + vers string + gomod string + }{ + /* + Different versions of git seem to find or not find + github.com/Masterminds/semver's a93e51b5a57e, + which is an unmerged pull request. + We'd rather not provide access to unmerged pull requests, + so the line is removed from the golden file here, + but some git commands still find it somehow. + + { + // Gopkg.lock parsing. + "github.com/golang/dep", "v0.4.0", + `module github.com/golang/dep + + require ( + github.com/Masterminds/vcs v1.11.1 + github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7 + github.com/boltdb/bolt v1.3.1 + github.com/go-yaml/yaml v0.0.0-20170407172122-cd8b52f8269e + github.com/golang/protobuf v0.0.0-20170901042739-5afd06f9d81a + github.com/jmank88/nuts v0.3.0 + github.com/nightlyone/lockfile v0.0.0-20170707060451-e83dc5e7bba0 + github.com/pelletier/go-toml v0.0.0-20171218135716-b8b5e7696574 + github.com/pkg/errors v0.8.0 + github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353 + golang.org/x/net v0.0.0-20170828231752-66aacef3dd8a + golang.org/x/sync v0.0.0-20170517211232-f52d1811a629 + golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea + )`, + }, + */ + + // TODO: https://github.com/docker/distribution uses vendor.conf + + { + // Godeps.json parsing. + // TODO: Should v2.0.0 work here too? + "github.com/docker/distribution", "v0.0.0-20150410205453-85de3967aa93", + `module github.com/docker/distribution + + require ( + github.com/AdRoll/goamz v0.0.0-20150130162828-d3664b76d905 + github.com/MSOpenTech/azure-sdk-for-go v0.0.0-20150323223030-d90753bcad2e + github.com/Sirupsen/logrus v0.7.3 + github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd + github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b + github.com/bugsnag/panicwrap v0.0.0-20141110184334-e5f9854865b9 + github.com/codegangsta/cli v0.0.0-20150131031259-6086d7927ec3 + github.com/docker/docker v0.0.0-20150204013315-165ea5c158cf + github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 + github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 + github.com/gorilla/context v0.0.0-20140604161150-14f550f51af5 + github.com/gorilla/handlers v0.0.0-20140825150757-0e84b7d810c1 + github.com/gorilla/mux v0.0.0-20140926153814-e444e69cbd2e + github.com/jlhawn/go-crypto v0.0.0-20150401213827-cd738dde20f0 + github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 + github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 + github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f + golang.org/x/net v0.0.0-20150202051010-1dfe7915deaf + gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789 + gopkg.in/yaml.v2 v2.0.0-20150116202057-bef53efd0c76 + )`, + }, + + { + // golang.org/issue/24585 - confusion about v2.0.0 tag in legacy non-v2 module + "github.com/fishy/gcsbucket", "v0.0.0-20150410205453-618d60fe84e0", + `module github.com/fishy/gcsbucket + + require ( + cloud.google.com/go v0.18.0 + github.com/fishy/fsdb v0.0.0-20180217030800-5527ded01371 + github.com/golang/protobuf v1.0.0 + github.com/googleapis/gax-go v2.0.0+incompatible + golang.org/x/net v0.0.0-20180216171745-136a25c244d3 + golang.org/x/oauth2 v0.0.0-20180207181906-543e37812f10 + golang.org/x/text v0.0.0-20180208041248-4e4a3210bb54 + google.golang.org/api v0.0.0-20180217000815-c7a403bb5fe1 + google.golang.org/appengine v1.0.0 + google.golang.org/genproto v0.0.0-20180206005123-2b5a72b8730b + google.golang.org/grpc v1.10.0 + )`, + }, + } + + for _, tt := range tests { + t.Run(strings.Replace(tt.path, "/", "_", -1)+"_"+tt.vers, func(t *testing.T) { + f, err := modfile.Parse("golden", []byte(tt.gomod), nil) + if err != nil { + t.Fatal(err) + } + want, err := f.Format() + if err != nil { + t.Fatal(err) + } + + dir, err := modfetch.Download(module.Version{Path: tt.path, Version: tt.vers}) + if err != nil { + t.Fatal(err) + } + + for name := range Converters { + file := filepath.Join(dir, name) + data, err := ioutil.ReadFile(file) + if err == nil { + f := new(modfile.File) + f.AddModuleStmt(tt.path) + if err := ConvertLegacyConfig(f, filepath.ToSlash(file), data); err != nil { + t.Fatal(err) + } + out, err := f.Format() + if err != nil { + t.Fatalf("format after conversion: %v", err) + } + if !bytes.Equal(out, want) { + t.Fatalf("final go.mod:\n%s\n\nwant:\n%s", out, want) + } + return + } + } + t.Fatalf("no converter found for %s@%s", tt.path, tt.vers) + }) + } +} diff --git a/libgo/go/cmd/go/internal/modconv/dep.go b/libgo/go/cmd/go/internal/modconv/dep.go new file mode 100644 index 0000000..690c206 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/dep.go @@ -0,0 +1,74 @@ +// Copyright 2018 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 modconv + +import ( + "fmt" + "strconv" + "strings" + + "cmd/go/internal/modfile" + "cmd/go/internal/module" + "cmd/go/internal/semver" +) + +func ParseGopkgLock(file string, data []byte) (*modfile.File, error) { + mf := new(modfile.File) + var list []module.Version + var r *module.Version + for lineno, line := range strings.Split(string(data), "\n") { + lineno++ + if i := strings.Index(line, "#"); i >= 0 { + line = line[:i] + } + line = strings.TrimSpace(line) + if line == "[[projects]]" { + list = append(list, module.Version{}) + r = &list[len(list)-1] + continue + } + if strings.HasPrefix(line, "[") { + r = nil + continue + } + if r == nil { + continue + } + i := strings.Index(line, "=") + if i < 0 { + continue + } + key := strings.TrimSpace(line[:i]) + val := strings.TrimSpace(line[i+1:]) + if len(val) >= 2 && val[0] == '"' && val[len(val)-1] == '"' { + q, err := strconv.Unquote(val) // Go unquoting, but close enough for now + if err != nil { + return nil, fmt.Errorf("%s:%d: invalid quoted string: %v", file, lineno, err) + } + val = q + } + switch key { + case "name": + r.Path = val + case "revision", "version": + // Note: key "version" should take priority over "revision", + // and it does, because dep writes toml keys in alphabetical order, + // so we see version (if present) second. + if key == "version" { + if !semver.IsValid(val) || semver.Canonical(val) != val { + break + } + } + r.Version = val + } + } + for _, r := range list { + if r.Path == "" || r.Version == "" { + return nil, fmt.Errorf("%s: empty [[projects]] stanza (%s)", file, r.Path) + } + mf.Require = append(mf.Require, &modfile.Require{Mod: r}) + } + return mf, nil +} diff --git a/libgo/go/cmd/go/internal/modconv/glide.go b/libgo/go/cmd/go/internal/modconv/glide.go new file mode 100644 index 0000000..3bc675f --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/glide.go @@ -0,0 +1,42 @@ +// Copyright 2018 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 modconv + +import ( + "strings" + + "cmd/go/internal/modfile" + "cmd/go/internal/module" +) + +func ParseGlideLock(file string, data []byte) (*modfile.File, error) { + mf := new(modfile.File) + imports := false + name := "" + for lineno, line := range strings.Split(string(data), "\n") { + lineno++ + if line == "" { + continue + } + if strings.HasPrefix(line, "imports:") { + imports = true + } else if line[0] != '-' && line[0] != ' ' && line[0] != '\t' { + imports = false + } + if !imports { + continue + } + if strings.HasPrefix(line, "- name:") { + name = strings.TrimSpace(line[len("- name:"):]) + } + if strings.HasPrefix(line, " version:") { + version := strings.TrimSpace(line[len(" version:"):]) + if name != "" && version != "" { + mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: name, Version: version}}) + } + } + } + return mf, nil +} diff --git a/libgo/go/cmd/go/internal/modconv/glock.go b/libgo/go/cmd/go/internal/modconv/glock.go new file mode 100644 index 0000000..1b786a9 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/glock.go @@ -0,0 +1,24 @@ +// Copyright 2018 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 modconv + +import ( + "strings" + + "cmd/go/internal/modfile" + "cmd/go/internal/module" +) + +func ParseGLOCKFILE(file string, data []byte) (*modfile.File, error) { + mf := new(modfile.File) + for lineno, line := range strings.Split(string(data), "\n") { + lineno++ + f := strings.Fields(line) + if len(f) >= 2 && f[0] != "cmd" { + mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: f[0], Version: f[1]}}) + } + } + return mf, nil +} diff --git a/libgo/go/cmd/go/internal/modconv/godeps.go b/libgo/go/cmd/go/internal/modconv/godeps.go new file mode 100644 index 0000000..6398dbe --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/godeps.go @@ -0,0 +1,30 @@ +// Copyright 2018 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 modconv + +import ( + "encoding/json" + + "cmd/go/internal/modfile" + "cmd/go/internal/module" +) + +func ParseGodepsJSON(file string, data []byte) (*modfile.File, error) { + var cfg struct { + ImportPath string + Deps []struct { + ImportPath string + Rev string + } + } + if err := json.Unmarshal(data, &cfg); err != nil { + return nil, err + } + mf := new(modfile.File) + for _, d := range cfg.Deps { + mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: d.ImportPath, Version: d.Rev}}) + } + return mf, nil +} diff --git a/libgo/go/cmd/go/internal/modconv/modconv.go b/libgo/go/cmd/go/internal/modconv/modconv.go new file mode 100644 index 0000000..a586733 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/modconv.go @@ -0,0 +1,19 @@ +// Copyright 2018 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 modconv + +import "cmd/go/internal/modfile" + +var Converters = map[string]func(string, []byte) (*modfile.File, error){ + "GLOCKFILE": ParseGLOCKFILE, + "Godeps/Godeps.json": ParseGodepsJSON, + "Gopkg.lock": ParseGopkgLock, + "dependencies.tsv": ParseDependenciesTSV, + "glide.lock": ParseGlideLock, + "vendor.conf": ParseVendorConf, + "vendor.yml": ParseVendorYML, + "vendor/manifest": ParseVendorManifest, + "vendor/vendor.json": ParseVendorJSON, +} diff --git a/libgo/go/cmd/go/internal/modconv/modconv_test.go b/libgo/go/cmd/go/internal/modconv/modconv_test.go new file mode 100644 index 0000000..353161b --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/modconv_test.go @@ -0,0 +1,66 @@ +// Copyright 2018 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 modconv + +import ( + "bytes" + "fmt" + "io/ioutil" + "path/filepath" + "testing" +) + +var extMap = map[string]string{ + ".dep": "Gopkg.lock", + ".glide": "glide.lock", + ".glock": "GLOCKFILE", + ".godeps": "Godeps/Godeps.json", + ".tsv": "dependencies.tsv", + ".vconf": "vendor.conf", + ".vjson": "vendor/vendor.json", + ".vyml": "vendor.yml", + ".vmanifest": "vendor/manifest", +} + +func Test(t *testing.T) { + tests, _ := filepath.Glob("testdata/*") + if len(tests) == 0 { + t.Fatalf("no tests found") + } + for _, test := range tests { + file := filepath.Base(test) + ext := filepath.Ext(file) + if ext == ".out" { + continue + } + t.Run(file, func(t *testing.T) { + if extMap[ext] == "" { + t.Fatal("unknown extension") + } + if Converters[extMap[ext]] == nil { + t.Fatalf("Converters[%q] == nil", extMap[ext]) + } + data, err := ioutil.ReadFile(test) + if err != nil { + t.Fatal(err) + } + out, err := Converters[extMap[ext]](test, data) + if err != nil { + t.Fatal(err) + } + want, err := ioutil.ReadFile(test[:len(test)-len(ext)] + ".out") + if err != nil { + t.Error(err) + } + var buf bytes.Buffer + for _, r := range out.Require { + fmt.Fprintf(&buf, "%s %s\n", r.Mod.Path, r.Mod.Version) + } + if !bytes.Equal(buf.Bytes(), want) { + t.Errorf("have:\n%s\nwant:\n%s", buf.Bytes(), want) + } + }) + } +} diff --git a/libgo/go/cmd/go/internal/modconv/testdata/cockroach.glock b/libgo/go/cmd/go/internal/modconv/testdata/cockroach.glock new file mode 100644 index 0000000..221c8ac --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/cockroach.glock @@ -0,0 +1,41 @@ +cmd github.com/cockroachdb/c-protobuf/cmd/protoc +cmd github.com/cockroachdb/yacc +cmd github.com/gogo/protobuf/protoc-gen-gogo +cmd github.com/golang/lint/golint +cmd github.com/jteeuwen/go-bindata/go-bindata +cmd github.com/kisielk/errcheck +cmd github.com/robfig/glock +cmd github.com/tebeka/go2xunit +cmd golang.org/x/tools/cmd/goimports +cmd golang.org/x/tools/cmd/stringer +github.com/agtorre/gocolorize f42b554bf7f006936130c9bb4f971afd2d87f671 +github.com/biogo/store e1f74b3c58befe661feed7fa4cf52436de753128 +github.com/cockroachdb/c-lz4 6e71f140a365017bbe0904710007f8725fd3f809 +github.com/cockroachdb/c-protobuf 0f9ab7b988ca7474cf76b9a961ab03c0552abcb3 +github.com/cockroachdb/c-rocksdb 7fc876fe79b96de0e25069c9ae27e6444637bd54 +github.com/cockroachdb/c-snappy 618733f9e5bab8463b9049117a335a7a1bfc9fd5 +github.com/cockroachdb/yacc 572e006f8e6b0061ebda949d13744f5108389514 +github.com/coreos/etcd 18ecc297bc913bed6fc093d66b1fa22020dba7dc +github.com/docker/docker 7374852be9def787921aea2ca831771982badecf +github.com/elazarl/go-bindata-assetfs 3dcc96556217539f50599357fb481ac0dc7439b9 +github.com/gogo/protobuf 98e73e511a62a9c232152f94999112c80142a813 +github.com/golang/lint 7b7f4364ff76043e6c3610281525fabc0d90f0e4 +github.com/google/btree cc6329d4279e3f025a53a83c397d2339b5705c45 +github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 +github.com/jteeuwen/go-bindata dce55d09e24ac40a6e725c8420902b86554f8046 +github.com/julienschmidt/httprouter 6aacfd5ab513e34f7e64ea9627ab9670371b34e7 +github.com/kisielk/errcheck 50b84cf7fa18ee2985b8c63ba3de5edd604b9259 +github.com/kisielk/gotool d678387370a2eb9b5b0a33218bc8c9d8de15b6be +github.com/lib/pq a8d8d01c4f91602f876bf5aa210274e8203a6b45 +github.com/montanaflynn/stats 44fb56da2a2a67d394dec0e18a82dd316f192529 +github.com/peterh/liner 1bb0d1c1a25ed393d8feb09bab039b2b1b1fbced +github.com/robfig/glock cb3c3ec56de988289cab7bbd284eddc04dfee6c9 +github.com/samalba/dockerclient 12570e600d71374233e5056ba315f657ced496c7 +github.com/spf13/cobra 66816bcd0378e248c613e3c443c020f544c28804 +github.com/spf13/pflag 67cbc198fd11dab704b214c1e629a97af392c085 +github.com/tebeka/go2xunit d45000af2242dd0e7b8c7b07d82a1068adc5fd40 +golang.org/x/crypto cc04154d65fb9296747569b107cfd05380b1ea3e +golang.org/x/net 8bfde94a845cb31000de3266ac83edbda58dab09 +golang.org/x/text d4cc1b1e16b49d6dafc4982403b40fe89c512cd5 +golang.org/x/tools d02228d1857b9f49cd0252788516ff5584266eb6 +gopkg.in/yaml.v1 9f9df34309c04878acc86042b16630b0f696e1de diff --git a/libgo/go/cmd/go/internal/modconv/testdata/cockroach.out b/libgo/go/cmd/go/internal/modconv/testdata/cockroach.out new file mode 100644 index 0000000..30cdbb7 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/cockroach.out @@ -0,0 +1,31 @@ +github.com/agtorre/gocolorize f42b554bf7f006936130c9bb4f971afd2d87f671 +github.com/biogo/store e1f74b3c58befe661feed7fa4cf52436de753128 +github.com/cockroachdb/c-lz4 6e71f140a365017bbe0904710007f8725fd3f809 +github.com/cockroachdb/c-protobuf 0f9ab7b988ca7474cf76b9a961ab03c0552abcb3 +github.com/cockroachdb/c-rocksdb 7fc876fe79b96de0e25069c9ae27e6444637bd54 +github.com/cockroachdb/c-snappy 618733f9e5bab8463b9049117a335a7a1bfc9fd5 +github.com/cockroachdb/yacc 572e006f8e6b0061ebda949d13744f5108389514 +github.com/coreos/etcd 18ecc297bc913bed6fc093d66b1fa22020dba7dc +github.com/docker/docker 7374852be9def787921aea2ca831771982badecf +github.com/elazarl/go-bindata-assetfs 3dcc96556217539f50599357fb481ac0dc7439b9 +github.com/gogo/protobuf 98e73e511a62a9c232152f94999112c80142a813 +github.com/golang/lint 7b7f4364ff76043e6c3610281525fabc0d90f0e4 +github.com/google/btree cc6329d4279e3f025a53a83c397d2339b5705c45 +github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 +github.com/jteeuwen/go-bindata dce55d09e24ac40a6e725c8420902b86554f8046 +github.com/julienschmidt/httprouter 6aacfd5ab513e34f7e64ea9627ab9670371b34e7 +github.com/kisielk/errcheck 50b84cf7fa18ee2985b8c63ba3de5edd604b9259 +github.com/kisielk/gotool d678387370a2eb9b5b0a33218bc8c9d8de15b6be +github.com/lib/pq a8d8d01c4f91602f876bf5aa210274e8203a6b45 +github.com/montanaflynn/stats 44fb56da2a2a67d394dec0e18a82dd316f192529 +github.com/peterh/liner 1bb0d1c1a25ed393d8feb09bab039b2b1b1fbced +github.com/robfig/glock cb3c3ec56de988289cab7bbd284eddc04dfee6c9 +github.com/samalba/dockerclient 12570e600d71374233e5056ba315f657ced496c7 +github.com/spf13/cobra 66816bcd0378e248c613e3c443c020f544c28804 +github.com/spf13/pflag 67cbc198fd11dab704b214c1e629a97af392c085 +github.com/tebeka/go2xunit d45000af2242dd0e7b8c7b07d82a1068adc5fd40 +golang.org/x/crypto cc04154d65fb9296747569b107cfd05380b1ea3e +golang.org/x/net 8bfde94a845cb31000de3266ac83edbda58dab09 +golang.org/x/text d4cc1b1e16b49d6dafc4982403b40fe89c512cd5 +golang.org/x/tools d02228d1857b9f49cd0252788516ff5584266eb6 +gopkg.in/yaml.v1 9f9df34309c04878acc86042b16630b0f696e1de diff --git a/libgo/go/cmd/go/internal/modconv/testdata/dockermachine.godeps b/libgo/go/cmd/go/internal/modconv/testdata/dockermachine.godeps new file mode 100644 index 0000000..a551002 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/dockermachine.godeps @@ -0,0 +1,159 @@ +{ + "ImportPath": "github.com/docker/machine", + "GoVersion": "go1.4.2", + "Deps": [ + { + "ImportPath": "code.google.com/p/goauth2/oauth", + "Comment": "weekly-56", + "Rev": "afe77d958c701557ec5dc56f6936fcc194d15520" + }, + { + "ImportPath": "github.com/MSOpenTech/azure-sdk-for-go", + "Comment": "v1.1-17-g515f3ec", + "Rev": "515f3ec74ce6a5b31e934cefae997c97bd0a1b1e" + }, + { + "ImportPath": "github.com/cenkalti/backoff", + "Rev": "9831e1e25c874e0a0601b6dc43641071414eec7a" + }, + { + "ImportPath": "github.com/codegangsta/cli", + "Comment": "1.2.0-64-ge1712f3", + "Rev": "e1712f381785e32046927f64a7c86fe569203196" + }, + { + "ImportPath": "github.com/digitalocean/godo", + "Comment": "v0.5.0", + "Rev": "5478aae80694de1d2d0e02c386bbedd201266234" + }, + { + "ImportPath": "github.com/docker/docker/dockerversion", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/docker/engine", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/docker/pkg/archive", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/docker/pkg/fileutils", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/docker/pkg/ioutils", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/docker/pkg/mflag", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/docker/pkg/parsers", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/docker/pkg/pools", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/docker/pkg/promise", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/docker/pkg/system", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/docker/pkg/term", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/docker/pkg/timeutils", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/docker/pkg/units", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/docker/pkg/version", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar", + "Comment": "v1.5.0", + "Rev": "a8a31eff10544860d2188dddabdee4d727545796" + }, + { + "ImportPath": "github.com/docker/libtrust", + "Rev": "c54fbb67c1f1e68d7d6f8d2ad7c9360404616a41" + }, + { + "ImportPath": "github.com/google/go-querystring/query", + "Rev": "30f7a39f4a218feb5325f3aebc60c32a572a8274" + }, + { + "ImportPath": "github.com/mitchellh/mapstructure", + "Rev": "740c764bc6149d3f1806231418adb9f52c11bcbf" + }, + { + "ImportPath": "github.com/rackspace/gophercloud", + "Comment": "v1.0.0-558-ce0f487", + "Rev": "ce0f487f6747ab43c4e4404722df25349385bebd" + }, + { + "ImportPath": "github.com/skarademir/naturalsort", + "Rev": "983d4d86054d80f91fd04dd62ec52c1d078ce403" + }, + { + "ImportPath": "github.com/smartystreets/go-aws-auth", + "Rev": "1f0db8c0ee6362470abe06a94e3385927ed72a4b" + }, + { + "ImportPath": "github.com/stretchr/testify/assert", + "Rev": "e4ec8152c15fc46bd5056ce65997a07c7d415325" + }, + { + "ImportPath": "github.com/pyr/egoscale/src/egoscale", + "Rev": "bbaa67324aeeacc90430c1fe0a9c620d3929512e" + }, + { + "ImportPath": "github.com/tent/http-link-go", + "Rev": "ac974c61c2f990f4115b119354b5e0b47550e888" + }, + { + "ImportPath": "github.com/vmware/govcloudair", + "Comment": "v0.0.2", + "Rev": "66a23eaabc61518f91769939ff541886fe1dceef" + }, + { + "ImportPath": "golang.org/x/crypto/ssh", + "Rev": "1fbbd62cfec66bd39d91e97749579579d4d3037e" + }, + { + "ImportPath": "google.golang.org/api/compute/v1", + "Rev": "aa91ac681e18e52b1a0dfe29b9d8354e88c0dcf5" + }, + { + "ImportPath": "google.golang.org/api/googleapi", + "Rev": "aa91ac681e18e52b1a0dfe29b9d8354e88c0dcf5" + } + ] +} diff --git a/libgo/go/cmd/go/internal/modconv/testdata/dockermachine.out b/libgo/go/cmd/go/internal/modconv/testdata/dockermachine.out new file mode 100644 index 0000000..0b39cea --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/dockermachine.out @@ -0,0 +1,33 @@ +code.google.com/p/goauth2/oauth afe77d958c701557ec5dc56f6936fcc194d15520 +github.com/MSOpenTech/azure-sdk-for-go 515f3ec74ce6a5b31e934cefae997c97bd0a1b1e +github.com/cenkalti/backoff 9831e1e25c874e0a0601b6dc43641071414eec7a +github.com/codegangsta/cli e1712f381785e32046927f64a7c86fe569203196 +github.com/digitalocean/godo 5478aae80694de1d2d0e02c386bbedd201266234 +github.com/docker/docker/dockerversion a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/docker/engine a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/docker/pkg/archive a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/docker/pkg/fileutils a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/docker/pkg/ioutils a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/docker/pkg/mflag a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/docker/pkg/parsers a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/docker/pkg/pools a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/docker/pkg/promise a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/docker/pkg/system a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/docker/pkg/term a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/docker/pkg/timeutils a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/docker/pkg/units a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/docker/pkg/version a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar a8a31eff10544860d2188dddabdee4d727545796 +github.com/docker/libtrust c54fbb67c1f1e68d7d6f8d2ad7c9360404616a41 +github.com/google/go-querystring/query 30f7a39f4a218feb5325f3aebc60c32a572a8274 +github.com/mitchellh/mapstructure 740c764bc6149d3f1806231418adb9f52c11bcbf +github.com/rackspace/gophercloud ce0f487f6747ab43c4e4404722df25349385bebd +github.com/skarademir/naturalsort 983d4d86054d80f91fd04dd62ec52c1d078ce403 +github.com/smartystreets/go-aws-auth 1f0db8c0ee6362470abe06a94e3385927ed72a4b +github.com/stretchr/testify/assert e4ec8152c15fc46bd5056ce65997a07c7d415325 +github.com/pyr/egoscale/src/egoscale bbaa67324aeeacc90430c1fe0a9c620d3929512e +github.com/tent/http-link-go ac974c61c2f990f4115b119354b5e0b47550e888 +github.com/vmware/govcloudair 66a23eaabc61518f91769939ff541886fe1dceef +golang.org/x/crypto/ssh 1fbbd62cfec66bd39d91e97749579579d4d3037e +google.golang.org/api/compute/v1 aa91ac681e18e52b1a0dfe29b9d8354e88c0dcf5 +google.golang.org/api/googleapi aa91ac681e18e52b1a0dfe29b9d8354e88c0dcf5 diff --git a/libgo/go/cmd/go/internal/modconv/testdata/dockerman.glide b/libgo/go/cmd/go/internal/modconv/testdata/dockerman.glide new file mode 100644 index 0000000..5ec765a --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/dockerman.glide @@ -0,0 +1,52 @@ +hash: ead3ea293a6143fe41069ebec814bf197d8c43a92cc7666b1f7e21a419b46feb +updated: 2016-06-20T21:53:35.420817456Z +imports: +- name: github.com/BurntSushi/toml + version: f0aeabca5a127c4078abb8c8d64298b147264b55 +- name: github.com/cpuguy83/go-md2man + version: a65d4d2de4d5f7c74868dfa9b202a3c8be315aaa + subpackages: + - md2man +- name: github.com/fsnotify/fsnotify + version: 30411dbcefb7a1da7e84f75530ad3abe4011b4f8 +- name: github.com/hashicorp/hcl + version: da486364306ed66c218be9b7953e19173447c18b + subpackages: + - hcl/ast + - hcl/parser + - hcl/token + - json/parser + - hcl/scanner + - hcl/strconv + - json/scanner + - json/token +- name: github.com/inconshreveable/mousetrap + version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 +- name: github.com/magiconair/properties + version: c265cfa48dda6474e208715ca93e987829f572f8 +- name: github.com/mitchellh/mapstructure + version: d2dd0262208475919e1a362f675cfc0e7c10e905 +- name: github.com/russross/blackfriday + version: 1d6b8e9301e720b08a8938b8c25c018285885438 +- name: github.com/shurcooL/sanitized_anchor_name + version: 10ef21a441db47d8b13ebcc5fd2310f636973c77 +- name: github.com/spf13/cast + version: 27b586b42e29bec072fe7379259cc719e1289da6 +- name: github.com/spf13/jwalterweatherman + version: 33c24e77fb80341fe7130ee7c594256ff08ccc46 +- name: github.com/spf13/pflag + version: dabebe21bf790f782ea4c7bbd2efc430de182afd +- name: github.com/spf13/viper + version: c1ccc378a054ea8d4e38d8c67f6938d4760b53dd +- name: golang.org/x/sys + version: 62bee037599929a6e9146f29d10dd5208c43507d + subpackages: + - unix +- name: gopkg.in/yaml.v2 + version: a83829b6f1293c91addabc89d0571c246397bbf4 +- name: github.com/spf13/cobra + repo: https://github.com/dnephin/cobra + subpackages: + - doc + version: v1.3 +devImports: [] diff --git a/libgo/go/cmd/go/internal/modconv/testdata/dockerman.out b/libgo/go/cmd/go/internal/modconv/testdata/dockerman.out new file mode 100644 index 0000000..5e6370b --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/dockerman.out @@ -0,0 +1,16 @@ +github.com/BurntSushi/toml f0aeabca5a127c4078abb8c8d64298b147264b55 +github.com/cpuguy83/go-md2man a65d4d2de4d5f7c74868dfa9b202a3c8be315aaa +github.com/fsnotify/fsnotify 30411dbcefb7a1da7e84f75530ad3abe4011b4f8 +github.com/hashicorp/hcl da486364306ed66c218be9b7953e19173447c18b +github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 +github.com/magiconair/properties c265cfa48dda6474e208715ca93e987829f572f8 +github.com/mitchellh/mapstructure d2dd0262208475919e1a362f675cfc0e7c10e905 +github.com/russross/blackfriday 1d6b8e9301e720b08a8938b8c25c018285885438 +github.com/shurcooL/sanitized_anchor_name 10ef21a441db47d8b13ebcc5fd2310f636973c77 +github.com/spf13/cast 27b586b42e29bec072fe7379259cc719e1289da6 +github.com/spf13/jwalterweatherman 33c24e77fb80341fe7130ee7c594256ff08ccc46 +github.com/spf13/pflag dabebe21bf790f782ea4c7bbd2efc430de182afd +github.com/spf13/viper c1ccc378a054ea8d4e38d8c67f6938d4760b53dd +golang.org/x/sys 62bee037599929a6e9146f29d10dd5208c43507d +gopkg.in/yaml.v2 a83829b6f1293c91addabc89d0571c246397bbf4 +github.com/spf13/cobra v1.3 diff --git a/libgo/go/cmd/go/internal/modconv/testdata/govmomi.out b/libgo/go/cmd/go/internal/modconv/testdata/govmomi.out new file mode 100644 index 0000000..188c458 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/govmomi.out @@ -0,0 +1,5 @@ +github.com/davecgh/go-xdr/xdr2 4930550ba2e22f87187498acfd78348b15f4e7a8 +github.com/google/uuid 6a5e28554805e78ea6141142aba763936c4761c0 +github.com/kr/pretty 2ee9d7453c02ef7fa518a83ae23644eb8872186a +github.com/kr/pty 95d05c1eef33a45bd58676b6ce28d105839b8d0b +github.com/vmware/vmw-guestinfo 25eff159a728be87e103a0b8045e08273f4dbec4 diff --git a/libgo/go/cmd/go/internal/modconv/testdata/govmomi.vmanifest b/libgo/go/cmd/go/internal/modconv/testdata/govmomi.vmanifest new file mode 100644 index 0000000..b89e4ab --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/govmomi.vmanifest @@ -0,0 +1,46 @@ +{ + "version": 0, + "dependencies": [ + { + "importpath": "github.com/davecgh/go-xdr/xdr2", + "repository": "https://github.com/rasky/go-xdr", + "vcs": "git", + "revision": "4930550ba2e22f87187498acfd78348b15f4e7a8", + "branch": "improvements", + "path": "/xdr2", + "notests": true + }, + { + "importpath": "github.com/google/uuid", + "repository": "https://github.com/google/uuid", + "vcs": "git", + "revision": "6a5e28554805e78ea6141142aba763936c4761c0", + "branch": "master", + "notests": true + }, + { + "importpath": "github.com/kr/pretty", + "repository": "https://github.com/dougm/pretty", + "vcs": "git", + "revision": "2ee9d7453c02ef7fa518a83ae23644eb8872186a", + "branch": "govmomi", + "notests": true + }, + { + "importpath": "github.com/kr/pty", + "repository": "https://github.com/kr/pty", + "vcs": "git", + "revision": "95d05c1eef33a45bd58676b6ce28d105839b8d0b", + "branch": "master", + "notests": true + }, + { + "importpath": "github.com/vmware/vmw-guestinfo", + "repository": "https://github.com/vmware/vmw-guestinfo", + "vcs": "git", + "revision": "25eff159a728be87e103a0b8045e08273f4dbec4", + "branch": "master", + "notests": true + } + ] +} diff --git a/libgo/go/cmd/go/internal/modconv/testdata/juju.out b/libgo/go/cmd/go/internal/modconv/testdata/juju.out new file mode 100644 index 0000000..c2430b1 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/juju.out @@ -0,0 +1,106 @@ +github.com/Azure/azure-sdk-for-go 902d95d9f311ae585ee98cfd18f418b467d60d5a +github.com/Azure/go-autorest 6f40a8acfe03270d792cb8155e2942c09d7cff95 +github.com/ajstarks/svgo 89e3ac64b5b3e403a5e7c35ea4f98d45db7b4518 +github.com/altoros/gosigma 31228935eec685587914528585da4eb9b073c76d +github.com/beorn7/perks 3ac7bf7a47d159a033b107610db8a1b6575507a4 +github.com/bmizerany/pat c068ca2f0aacee5ac3681d68e4d0a003b7d1fd2c +github.com/coreos/go-systemd 7b2428fec40033549c68f54e26e89e7ca9a9ce31 +github.com/dgrijalva/jwt-go 01aeca54ebda6e0fbfafd0a524d234159c05ec20 +github.com/dustin/go-humanize 145fabdb1ab757076a70a886d092a3af27f66f4c +github.com/godbus/dbus 32c6cc29c14570de4cf6d7e7737d68fb2d01ad15 +github.com/golang/protobuf 4bd1920723d7b7c925de087aa32e2187708897f7 +github.com/google/go-querystring 9235644dd9e52eeae6fa48efd539fdc351a0af53 +github.com/gorilla/schema 08023a0215e7fc27a9aecd8b8c50913c40019478 +github.com/gorilla/websocket 804cb600d06b10672f2fbc0a336a7bee507a428e +github.com/gosuri/uitable 36ee7e946282a3fb1cfecd476ddc9b35d8847e42 +github.com/joyent/gocommon ade826b8b54e81a779ccb29d358a45ba24b7809c +github.com/joyent/gosdc 2f11feadd2d9891e92296a1077c3e2e56939547d +github.com/joyent/gosign 0da0d5f1342065321c97812b1f4ac0c2b0bab56c +github.com/juju/ansiterm b99631de12cf04a906c1d4e4ec54fb86eae5863d +github.com/juju/blobstore 06056004b3d7b54bbb7984d830c537bad00fec21 +github.com/juju/bundlechanges 7725027b95e0d54635e0fb11efc2debdcdf19f75 +github.com/juju/cmd 9425a576247f348b9b40afe3b60085de63470de5 +github.com/juju/description d3742c23561884cd7d759ef7142340af1d22cab0 +github.com/juju/errors 1b5e39b83d1835fa480e0c2ddefb040ee82d58b3 +github.com/juju/gnuflag 4e76c56581859c14d9d87e1ddbe29e1c0f10195f +github.com/juju/go4 40d72ab9641a2a8c36a9c46a51e28367115c8e59 +github.com/juju/gojsonpointer afe8b77aa08f272b49e01b82de78510c11f61500 +github.com/juju/gojsonreference f0d24ac5ee330baa21721cdff56d45e4ee42628e +github.com/juju/gojsonschema e1ad140384f254c82f89450d9a7c8dd38a632838 +github.com/juju/gomaasapi cfbc096bd45f276c17a391efc4db710b60ae3ad7 +github.com/juju/httpprof 14bf14c307672fd2456bdbf35d19cf0ccd3cf565 +github.com/juju/httprequest 266fd1e9debf09c037a63f074d099a2da4559ece +github.com/juju/idmclient 4dc25171f675da4206b71695d3fd80e519ad05c1 +github.com/juju/jsonschema a0ef8b74ebcffeeff9fc374854deb4af388f037e +github.com/juju/loggo 21bc4c63e8b435779a080e39e592969b7b90b889 +github.com/juju/mempool 24974d6c264fe5a29716e7d56ea24c4bd904b7cc +github.com/juju/mutex 59c26ee163447c5c57f63ff71610d433862013de +github.com/juju/persistent-cookiejar 5243747bf8f2d0897f6c7a52799327dc97d585e8 +github.com/juju/pubsub 9dcaca7eb4340dbf685aa7b3ad4cc4f8691a33d4 +github.com/juju/replicaset 6b5becf2232ce76656ea765d8d915d41755a1513 +github.com/juju/retry 62c62032529169c7ec02fa48f93349604c345e1f +github.com/juju/rfc ebdbbdb950cd039a531d15cdc2ac2cbd94f068ee +github.com/juju/romulus 98d6700423d63971f10ca14afea9ecf2b9b99f0f +github.com/juju/schema 075de04f9b7d7580d60a1e12a0b3f50bb18e6998 +github.com/juju/terms-client 9b925afd677234e4146dde3cb1a11e187cbed64e +github.com/juju/testing fce9bc4ebf7a77310c262ac4884e03b778eae06a +github.com/juju/txn 28898197906200d603394d8e4ce537436529f1c5 +github.com/juju/usso 68a59c96c178fbbad65926e7f93db50a2cd14f33 +github.com/juju/utils 9f8aeb9b09e2d8c769be8317ccfa23f7eec62c26 +github.com/juju/version 1f41e27e54f21acccf9b2dddae063a782a8a7ceb +github.com/juju/webbrowser 54b8c57083b4afb7dc75da7f13e2967b2606a507 +github.com/juju/xml eb759a627588d35166bc505fceb51b88500e291e +github.com/juju/zip f6b1e93fa2e29a1d7d49b566b2b51efb060c982a +github.com/julienschmidt/httprouter 77a895ad01ebc98a4dc95d8355bc825ce80a56f6 +github.com/lestrrat/go-jspointer f4881e611bdbe9fb413a7780721ef8400a1f2341 +github.com/lestrrat/go-jsref e452c7b5801d1c6494c9e7e0cbc7498c0f88dfd1 +github.com/lestrrat/go-jsschema b09d7650b822d2ea3dc83d5091a5e2acd8330051 +github.com/lestrrat/go-jsval b1258a10419fe0693f7b35ad65cd5074bc0ba1e5 +github.com/lestrrat/go-pdebug 2e6eaaa5717f81bda41d27070d3c966f40a1e75f +github.com/lestrrat/go-structinfo f74c056fe41f860aa6264478c664a6fff8a64298 +github.com/lunixbochs/vtclean 4fbf7632a2c6d3fbdb9931439bdbbeded02cbe36 +github.com/lxc/lxd 23da0234979fa6299565b91b529a6dbeb42ee36d +github.com/masterzen/azure-sdk-for-go ee4f0065d00cd12b542f18f5bc45799e88163b12 +github.com/masterzen/simplexml 4572e39b1ab9fe03ee513ce6fc7e289e98482190 +github.com/masterzen/winrm 7a535cd943fccaeed196718896beec3fb51aff41 +github.com/masterzen/xmlpath 13f4951698adc0fa9c1dda3e275d489a24201161 +github.com/mattn/go-colorable ed8eb9e318d7a84ce5915b495b7d35e0cfe7b5a8 +github.com/mattn/go-isatty 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8 +github.com/mattn/go-runewidth d96d1bd051f2bd9e7e43d602782b37b93b1b5666 +github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c +github.com/nu7hatch/gouuid 179d4d0c4d8d407a32af483c2354df1d2c91e6c3 +github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9 +github.com/prometheus/client_golang 575f371f7862609249a1be4c9145f429fe065e32 +github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6 +github.com/prometheus/common dd586c1c5abb0be59e60f942c22af711a2008cb4 +github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5 +github.com/rogpeppe/fastuuid 6724a57986aff9bff1a1770e9347036def7c89f6 +github.com/vmware/govmomi c0c7ce63df7edd78e713257b924c89d9a2dac119 +golang.org/x/crypto 8e06e8ddd9629eb88639aba897641bff8031f1d3 +golang.org/x/net ea47fc708ee3e20177f3ca3716217c4ab75942cb +golang.org/x/oauth2 11c60b6f71a6ad48ed6f93c65fa4c6f9b1b5b46a +golang.org/x/sys 7a6e5648d140666db5d920909e082ca00a87ba2c +golang.org/x/text 2910a502d2bf9e43193af9d68ca516529614eed3 +google.golang.org/api 0d3983fb069cb6651353fc44c5cb604e263f2a93 +google.golang.org/cloud f20d6dcccb44ed49de45ae3703312cb46e627db1 +gopkg.in/amz.v3 8c3190dff075bf5442c9eedbf8f8ed6144a099e7 +gopkg.in/check.v1 4f90aeace3a26ad7021961c297b22c42160c7b25 +gopkg.in/errgo.v1 442357a80af5c6bf9b6d51ae791a39c3421004f3 +gopkg.in/goose.v1 ac43167b647feacdd9a1e34ee81e574551bc748d +gopkg.in/ini.v1 776aa739ce9373377cd16f526cdf06cb4c89b40f +gopkg.in/juju/blobstore.v2 51fa6e26128d74e445c72d3a91af555151cc3654 +gopkg.in/juju/charm.v6-unstable 83771c4919d6810bce5b7e63f46bea5fbfed0b93 +gopkg.in/juju/charmrepo.v2-unstable e79aa298df89ea887c9bffec46063c24bfb730f7 +gopkg.in/juju/charmstore.v5-unstable fd1eef3002fc6b6daff5e97efab6f5056d22dcc7 +gopkg.in/juju/environschema.v1 7359fc7857abe2b11b5b3e23811a9c64cb6b01e0 +gopkg.in/juju/jujusvg.v2 d82160011935ef79fc7aca84aba2c6f74700fe75 +gopkg.in/juju/names.v2 0847c26d322a121e52614f969fb82eae2820c715 +gopkg.in/juju/worker.v1 6965b9d826717287bb002e02d1fd4d079978083e +gopkg.in/macaroon-bakery.v1 469b44e6f1f9479e115c8ae879ef80695be624d5 +gopkg.in/macaroon.v1 ab3940c6c16510a850e1c2dd628b919f0f3f1464 +gopkg.in/mgo.v2 f2b6f6c918c452ad107eec89615f074e3bd80e33 +gopkg.in/natefinch/lumberjack.v2 514cbda263a734ae8caac038dadf05f8f3f9f738 +gopkg.in/natefinch/npipe.v2 c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6 +gopkg.in/retry.v1 c09f6b86ba4d5d2cf5bdf0665364aec9fd4815db +gopkg.in/tomb.v1 dd632973f1e7218eb1089048e0798ec9ae7dceb8 +gopkg.in/yaml.v2 a3f3340b5840cee44f372bddb5880fcbc419b46a diff --git a/libgo/go/cmd/go/internal/modconv/testdata/juju.tsv b/libgo/go/cmd/go/internal/modconv/testdata/juju.tsv new file mode 100644 index 0000000..0bddcef --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/juju.tsv @@ -0,0 +1,106 @@ +github.com/Azure/azure-sdk-for-go git 902d95d9f311ae585ee98cfd18f418b467d60d5a 2016-07-20T05:16:58Z +github.com/Azure/go-autorest git 6f40a8acfe03270d792cb8155e2942c09d7cff95 2016-07-19T23:14:56Z +github.com/ajstarks/svgo git 89e3ac64b5b3e403a5e7c35ea4f98d45db7b4518 2014-10-04T21:11:59Z +github.com/altoros/gosigma git 31228935eec685587914528585da4eb9b073c76d 2015-04-08T14:52:32Z +github.com/beorn7/perks git 3ac7bf7a47d159a033b107610db8a1b6575507a4 2016-02-29T21:34:45Z +github.com/bmizerany/pat git c068ca2f0aacee5ac3681d68e4d0a003b7d1fd2c 2016-02-17T10:32:42Z +github.com/coreos/go-systemd git 7b2428fec40033549c68f54e26e89e7ca9a9ce31 2016-02-02T21:14:25Z +github.com/dgrijalva/jwt-go git 01aeca54ebda6e0fbfafd0a524d234159c05ec20 2016-07-05T20:30:06Z +github.com/dustin/go-humanize git 145fabdb1ab757076a70a886d092a3af27f66f4c 2014-12-28T07:11:48Z +github.com/godbus/dbus git 32c6cc29c14570de4cf6d7e7737d68fb2d01ad15 2016-05-06T22:25:50Z +github.com/golang/protobuf git 4bd1920723d7b7c925de087aa32e2187708897f7 2016-11-09T07:27:36Z +github.com/google/go-querystring git 9235644dd9e52eeae6fa48efd539fdc351a0af53 2016-04-01T23:30:42Z +github.com/gorilla/schema git 08023a0215e7fc27a9aecd8b8c50913c40019478 2016-04-26T23:15:12Z +github.com/gorilla/websocket git 804cb600d06b10672f2fbc0a336a7bee507a428e 2017-02-14T17:41:18Z +github.com/gosuri/uitable git 36ee7e946282a3fb1cfecd476ddc9b35d8847e42 2016-04-04T20:39:58Z +github.com/joyent/gocommon git ade826b8b54e81a779ccb29d358a45ba24b7809c 2016-03-20T19:31:33Z +github.com/joyent/gosdc git 2f11feadd2d9891e92296a1077c3e2e56939547d 2014-05-24T00:08:15Z +github.com/joyent/gosign git 0da0d5f1342065321c97812b1f4ac0c2b0bab56c 2014-05-24T00:07:34Z +github.com/juju/ansiterm git b99631de12cf04a906c1d4e4ec54fb86eae5863d 2016-09-07T23:45:32Z +github.com/juju/blobstore git 06056004b3d7b54bbb7984d830c537bad00fec21 2015-07-29T11:18:58Z +github.com/juju/bundlechanges git 7725027b95e0d54635e0fb11efc2debdcdf19f75 2016-12-15T16:06:52Z +github.com/juju/cmd git 9425a576247f348b9b40afe3b60085de63470de5 2017-03-20T01:37:09Z +github.com/juju/description git d3742c23561884cd7d759ef7142340af1d22cab0 2017-03-20T07:46:40Z +github.com/juju/errors git 1b5e39b83d1835fa480e0c2ddefb040ee82d58b3 2015-09-16T12:56:42Z +github.com/juju/gnuflag git 4e76c56581859c14d9d87e1ddbe29e1c0f10195f 2016-08-09T16:52:14Z +github.com/juju/go4 git 40d72ab9641a2a8c36a9c46a51e28367115c8e59 2016-02-22T16:32:58Z +github.com/juju/gojsonpointer git afe8b77aa08f272b49e01b82de78510c11f61500 2015-02-04T19:46:29Z +github.com/juju/gojsonreference git f0d24ac5ee330baa21721cdff56d45e4ee42628e 2015-02-04T19:46:33Z +github.com/juju/gojsonschema git e1ad140384f254c82f89450d9a7c8dd38a632838 2015-03-12T17:00:16Z +github.com/juju/gomaasapi git cfbc096bd45f276c17a391efc4db710b60ae3ad7 2017-02-27T07:51:07Z +github.com/juju/httpprof git 14bf14c307672fd2456bdbf35d19cf0ccd3cf565 2014-12-17T16:00:36Z +github.com/juju/httprequest git 266fd1e9debf09c037a63f074d099a2da4559ece 2016-10-06T15:09:09Z +github.com/juju/idmclient git 4dc25171f675da4206b71695d3fd80e519ad05c1 2017-02-09T16:27:49Z +github.com/juju/jsonschema git a0ef8b74ebcffeeff9fc374854deb4af388f037e 2016-11-02T18:19:19Z +github.com/juju/loggo git 21bc4c63e8b435779a080e39e592969b7b90b889 2017-02-22T12:20:47Z +github.com/juju/mempool git 24974d6c264fe5a29716e7d56ea24c4bd904b7cc 2016-02-05T10:49:27Z +github.com/juju/mutex git 59c26ee163447c5c57f63ff71610d433862013de 2016-06-17T01:09:07Z +github.com/juju/persistent-cookiejar git 5243747bf8f2d0897f6c7a52799327dc97d585e8 2016-11-15T13:33:28Z +github.com/juju/pubsub git 9dcaca7eb4340dbf685aa7b3ad4cc4f8691a33d4 2016-07-28T03:00:34Z +github.com/juju/replicaset git 6b5becf2232ce76656ea765d8d915d41755a1513 2016-11-25T16:08:49Z +github.com/juju/retry git 62c62032529169c7ec02fa48f93349604c345e1f 2015-10-29T02:48:21Z +github.com/juju/rfc git ebdbbdb950cd039a531d15cdc2ac2cbd94f068ee 2016-07-11T02:42:13Z +github.com/juju/romulus git 98d6700423d63971f10ca14afea9ecf2b9b99f0f 2017-01-23T14:29:29Z +github.com/juju/schema git 075de04f9b7d7580d60a1e12a0b3f50bb18e6998 2016-04-20T04:42:03Z +github.com/juju/terms-client git 9b925afd677234e4146dde3cb1a11e187cbed64e 2016-08-09T13:19:00Z +github.com/juju/testing git fce9bc4ebf7a77310c262ac4884e03b778eae06a 2017-02-22T09:01:19Z +github.com/juju/txn git 28898197906200d603394d8e4ce537436529f1c5 2016-11-16T04:07:55Z +github.com/juju/usso git 68a59c96c178fbbad65926e7f93db50a2cd14f33 2016-04-01T10:44:24Z +github.com/juju/utils git 9f8aeb9b09e2d8c769be8317ccfa23f7eec62c26 2017-02-15T08:19:00Z +github.com/juju/version git 1f41e27e54f21acccf9b2dddae063a782a8a7ceb 2016-10-31T05:19:06Z +github.com/juju/webbrowser git 54b8c57083b4afb7dc75da7f13e2967b2606a507 2016-03-09T14:36:29Z +github.com/juju/xml git eb759a627588d35166bc505fceb51b88500e291e 2015-04-13T13:11:21Z +github.com/juju/zip git f6b1e93fa2e29a1d7d49b566b2b51efb060c982a 2016-02-05T10:52:21Z +github.com/julienschmidt/httprouter git 77a895ad01ebc98a4dc95d8355bc825ce80a56f6 2015-10-13T22:55:20Z +github.com/lestrrat/go-jspointer git f4881e611bdbe9fb413a7780721ef8400a1f2341 2016-02-29T02:13:54Z +github.com/lestrrat/go-jsref git e452c7b5801d1c6494c9e7e0cbc7498c0f88dfd1 2016-06-01T01:32:40Z +github.com/lestrrat/go-jsschema git b09d7650b822d2ea3dc83d5091a5e2acd8330051 2016-09-03T13:19:57Z +github.com/lestrrat/go-jsval git b1258a10419fe0693f7b35ad65cd5074bc0ba1e5 2016-10-12T04:57:17Z +github.com/lestrrat/go-pdebug git 2e6eaaa5717f81bda41d27070d3c966f40a1e75f 2016-08-17T06:33:33Z +github.com/lestrrat/go-structinfo git f74c056fe41f860aa6264478c664a6fff8a64298 2016-03-08T13:11:05Z +github.com/lunixbochs/vtclean git 4fbf7632a2c6d3fbdb9931439bdbbeded02cbe36 2016-01-25T03:51:06Z +github.com/lxc/lxd git 23da0234979fa6299565b91b529a6dbeb42ee36d 2017-02-16T05:29:42Z +github.com/masterzen/azure-sdk-for-go git ee4f0065d00cd12b542f18f5bc45799e88163b12 2016-10-14T13:56:28Z +github.com/masterzen/simplexml git 4572e39b1ab9fe03ee513ce6fc7e289e98482190 2016-06-08T18:30:07Z +github.com/masterzen/winrm git 7a535cd943fccaeed196718896beec3fb51aff41 2016-10-14T15:10:40Z +github.com/masterzen/xmlpath git 13f4951698adc0fa9c1dda3e275d489a24201161 2014-02-18T18:59:01Z +github.com/mattn/go-colorable git ed8eb9e318d7a84ce5915b495b7d35e0cfe7b5a8 2016-07-31T23:54:17Z +github.com/mattn/go-isatty git 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8 2016-08-06T12:27:52Z +github.com/mattn/go-runewidth git d96d1bd051f2bd9e7e43d602782b37b93b1b5666 2015-11-18T07:21:59Z +github.com/matttproud/golang_protobuf_extensions git c12348ce28de40eed0136aa2b644d0ee0650e56c 2016-04-24T11:30:07Z +github.com/nu7hatch/gouuid git 179d4d0c4d8d407a32af483c2354df1d2c91e6c3 2013-12-21T20:05:32Z +github.com/pkg/errors git 839d9e913e063e28dfd0e6c7b7512793e0a48be9 2016-10-02T05:25:12Z +github.com/prometheus/client_golang git 575f371f7862609249a1be4c9145f429fe065e32 2016-11-24T15:57:32Z +github.com/prometheus/client_model git fa8ad6fec33561be4280a8f0514318c79d7f6cb6 2015-02-12T10:17:44Z +github.com/prometheus/common git dd586c1c5abb0be59e60f942c22af711a2008cb4 2016-05-03T22:05:32Z +github.com/prometheus/procfs git abf152e5f3e97f2fafac028d2cc06c1feb87ffa5 2016-04-11T19:08:41Z +github.com/rogpeppe/fastuuid git 6724a57986aff9bff1a1770e9347036def7c89f6 2015-01-06T09:32:20Z +github.com/vmware/govmomi git c0c7ce63df7edd78e713257b924c89d9a2dac119 2016-06-30T15:37:42Z +golang.org/x/crypto git 8e06e8ddd9629eb88639aba897641bff8031f1d3 2016-09-22T17:06:29Z +golang.org/x/net git ea47fc708ee3e20177f3ca3716217c4ab75942cb 2015-08-29T23:03:18Z +golang.org/x/oauth2 git 11c60b6f71a6ad48ed6f93c65fa4c6f9b1b5b46a 2015-03-25T02:00:22Z +golang.org/x/sys git 7a6e5648d140666db5d920909e082ca00a87ba2c 2017-02-01T05:12:45Z +golang.org/x/text git 2910a502d2bf9e43193af9d68ca516529614eed3 2016-07-26T16:48:57Z +google.golang.org/api git 0d3983fb069cb6651353fc44c5cb604e263f2a93 2014-12-10T23:51:26Z +google.golang.org/cloud git f20d6dcccb44ed49de45ae3703312cb46e627db1 2015-03-19T22:36:35Z +gopkg.in/amz.v3 git 8c3190dff075bf5442c9eedbf8f8ed6144a099e7 2016-12-15T13:08:49Z +gopkg.in/check.v1 git 4f90aeace3a26ad7021961c297b22c42160c7b25 2016-01-05T16:49:36Z +gopkg.in/errgo.v1 git 442357a80af5c6bf9b6d51ae791a39c3421004f3 2016-12-22T12:58:16Z +gopkg.in/goose.v1 git ac43167b647feacdd9a1e34ee81e574551bc748d 2017-02-15T01:56:23Z +gopkg.in/ini.v1 git 776aa739ce9373377cd16f526cdf06cb4c89b40f 2016-02-22T23:24:41Z +gopkg.in/juju/blobstore.v2 git 51fa6e26128d74e445c72d3a91af555151cc3654 2016-01-25T02:37:03Z +gopkg.in/juju/charm.v6-unstable git 83771c4919d6810bce5b7e63f46bea5fbfed0b93 2016-10-03T20:31:18Z +gopkg.in/juju/charmrepo.v2-unstable git e79aa298df89ea887c9bffec46063c24bfb730f7 2016-11-17T15:25:28Z +gopkg.in/juju/charmstore.v5-unstable git fd1eef3002fc6b6daff5e97efab6f5056d22dcc7 2016-09-16T10:09:07Z +gopkg.in/juju/environschema.v1 git 7359fc7857abe2b11b5b3e23811a9c64cb6b01e0 2015-11-04T11:58:10Z +gopkg.in/juju/jujusvg.v2 git d82160011935ef79fc7aca84aba2c6f74700fe75 2016-06-09T10:52:15Z +gopkg.in/juju/names.v2 git 0847c26d322a121e52614f969fb82eae2820c715 2016-11-02T13:43:03Z +gopkg.in/juju/worker.v1 git 6965b9d826717287bb002e02d1fd4d079978083e 2017-03-08T00:24:58Z +gopkg.in/macaroon-bakery.v1 git 469b44e6f1f9479e115c8ae879ef80695be624d5 2016-06-22T12:14:21Z +gopkg.in/macaroon.v1 git ab3940c6c16510a850e1c2dd628b919f0f3f1464 2015-01-21T11:42:31Z +gopkg.in/mgo.v2 git f2b6f6c918c452ad107eec89615f074e3bd80e33 2016-08-18T01:52:18Z +gopkg.in/natefinch/lumberjack.v2 git 514cbda263a734ae8caac038dadf05f8f3f9f738 2016-01-25T11:17:49Z +gopkg.in/natefinch/npipe.v2 git c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6 2016-06-21T03:49:01Z +gopkg.in/retry.v1 git c09f6b86ba4d5d2cf5bdf0665364aec9fd4815db 2016-10-25T18:14:30Z +gopkg.in/tomb.v1 git dd632973f1e7218eb1089048e0798ec9ae7dceb8 2014-10-24T13:56:13Z +gopkg.in/yaml.v2 git a3f3340b5840cee44f372bddb5880fcbc419b46a 2017-02-08T14:18:51Z diff --git a/libgo/go/cmd/go/internal/modconv/testdata/moby.out b/libgo/go/cmd/go/internal/modconv/testdata/moby.out new file mode 100644 index 0000000..2cb2e05 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/moby.out @@ -0,0 +1,105 @@ +github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 +github.com/Microsoft/hcsshim v0.6.5 +github.com/Microsoft/go-winio v0.4.5 +github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 +github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a +github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 +github.com/gorilla/context v1.1 +github.com/gorilla/mux v1.1 +github.com/Microsoft/opengcs v0.3.4 +github.com/kr/pty 5cf931ef8f +github.com/mattn/go-shellwords v1.0.3 +github.com/sirupsen/logrus v1.0.3 +github.com/tchap/go-patricia v2.2.6 +github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 +golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 +golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5 +github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1 +github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d +golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756 +github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987 +github.com/pmezard/go-difflib v1.0.0 +github.com/gotestyourself/gotestyourself v1.1.0 +github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5 +github.com/imdario/mergo 0.2.1 +golang.org/x/sync de49d9dcd27d4f764488181bea099dfe6179bcf0 +github.com/containerd/continuity 22694c680ee48fb8f50015b44618517e2bde77e8 +github.com/moby/buildkit aaff9d591ef128560018433fe61beb802e149de8 +github.com/tonistiigi/fsutil dea3a0da73aee887fc02142d995be764106ac5e2 +github.com/docker/libnetwork 68f1039f172434709a4550fe92e3e058406c74ce +github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 +github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80 +github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec +github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b +github.com/hashicorp/memberlist v0.1.0 +github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372 +github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d +github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e +github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870 +github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef +github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25 +github.com/vishvananda/netlink bd6d5de5ccef2d66b0a26177928d0d8895d7f969 +github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060 +github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374 +github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d +github.com/coreos/etcd v3.2.1 +github.com/coreos/go-semver v0.2.0 +github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065 +github.com/hashicorp/consul v0.5.2 +github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904 +github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7 +github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c +github.com/vbatts/tar-split v0.10.1 +github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb +github.com/mistifyio/go-zfs 22c9b32c84eb0d0c6f4043b6e90fc94073de92fa +github.com/pborman/uuid v1.0 +google.golang.org/grpc v1.3.0 +github.com/opencontainers/runc 0351df1c5a66838d0c392b4ac4cf9450de844e2d +github.com/opencontainers/image-spec 372ad780f63454fbbbbcc7cf80e5b90245c13e13 +github.com/opencontainers/runtime-spec v1.0.0 +github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 +github.com/coreos/go-systemd v4 +github.com/godbus/dbus v4.0.0 +github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852 +github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4 +github.com/Graylog2/go-gelf v2 +github.com/fluent/fluent-logger-golang v1.2.1 +github.com/philhofer/fwd 98c11a7a6ec829d672b03833c3d69a7fae1ca972 +github.com/tinylib/msgp 75ee40d2601edf122ef667e2a07d600d4c44490c +github.com/fsnotify/fsnotify v1.4.2 +github.com/aws/aws-sdk-go v1.4.22 +github.com/go-ini/ini 060d7da055ba6ec5ea7a31f116332fe5efa04ce0 +github.com/jmespath/go-jmespath 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74 +github.com/bsphere/le_go 7a984a84b5492ae539b79b62fb4a10afc63c7bcf +golang.org/x/oauth2 96382aa079b72d8c014eb0c50f6c223d1e6a2de0 +google.golang.org/api 3cc2e591b550923a2c5f0ab5a803feda924d5823 +cloud.google.com/go 9d965e63e8cceb1b5d7977a202f0fcb8866d6525 +github.com/googleapis/gax-go da06d194a00e19ce00d9011a13931c3f6f6887c7 +google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 +github.com/containerd/containerd 06b9cb35161009dcb7123345749fef02f7cea8e0 +github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4 +github.com/docker/swarmkit 872861d2ae46958af7ead1d5fffb092c73afbaf0 +github.com/gogo/protobuf v0.4 +github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a +github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e +golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca +golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb +github.com/hashicorp/go-memdb cb9a474f84cc5e41b273b20c6927680b2a8776ad +github.com/hashicorp/go-immutable-radix 8e8ed81f8f0bf1bdd829593fdd5c29922c1ea990 +github.com/hashicorp/golang-lru a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4 +github.com/coreos/pkg fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8 +github.com/pivotal-golang/clock 3fd3c1944c59d9742e1cd333672181cd1a6f9fa0 +github.com/prometheus/client_golang 52437c81da6b127a9925d17eb3a382a2e5fd395e +github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 +github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6 +github.com/prometheus/common ebdfc6da46522d58825777cf1f90490a5b1ef1d8 +github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5 +github.com/matttproud/golang_protobuf_extensions v1.0.0 +github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9 +github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 +github.com/spf13/cobra v1.5.1 +github.com/spf13/pflag 9ff6c6923cfffbcd502984b8e0c80539a94968b7 +github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 +github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c +github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18 +github.com/opencontainers/selinux v1.0.0-rc1 diff --git a/libgo/go/cmd/go/internal/modconv/testdata/moby.vconf b/libgo/go/cmd/go/internal/modconv/testdata/moby.vconf new file mode 100644 index 0000000..53b90d1 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/moby.vconf @@ -0,0 +1,149 @@ +# the following lines are in sorted order, FYI +github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 +github.com/Microsoft/hcsshim v0.6.5 +github.com/Microsoft/go-winio v0.4.5 +github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 +github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a +github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git +github.com/gorilla/context v1.1 +github.com/gorilla/mux v1.1 +github.com/Microsoft/opengcs v0.3.4 +github.com/kr/pty 5cf931ef8f +github.com/mattn/go-shellwords v1.0.3 +github.com/sirupsen/logrus v1.0.3 +github.com/tchap/go-patricia v2.2.6 +github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 +golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 +golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5 +github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1 +github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d +golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756 +github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987 +github.com/pmezard/go-difflib v1.0.0 +github.com/gotestyourself/gotestyourself v1.1.0 + +github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5 +github.com/imdario/mergo 0.2.1 +golang.org/x/sync de49d9dcd27d4f764488181bea099dfe6179bcf0 + +github.com/containerd/continuity 22694c680ee48fb8f50015b44618517e2bde77e8 +github.com/moby/buildkit aaff9d591ef128560018433fe61beb802e149de8 +github.com/tonistiigi/fsutil dea3a0da73aee887fc02142d995be764106ac5e2 + +#get libnetwork packages +github.com/docker/libnetwork 68f1039f172434709a4550fe92e3e058406c74ce +github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 +github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80 +github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec +github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b +github.com/hashicorp/memberlist v0.1.0 +github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372 +github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d +github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e +github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870 +github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef +github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25 +github.com/vishvananda/netlink bd6d5de5ccef2d66b0a26177928d0d8895d7f969 +github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060 +github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374 +github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d +github.com/coreos/etcd v3.2.1 +github.com/coreos/go-semver v0.2.0 +github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065 +github.com/hashicorp/consul v0.5.2 +github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904 +github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7 + +# get graph and distribution packages +github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c +github.com/vbatts/tar-split v0.10.1 +github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb + +# get go-zfs packages +github.com/mistifyio/go-zfs 22c9b32c84eb0d0c6f4043b6e90fc94073de92fa +github.com/pborman/uuid v1.0 + +google.golang.org/grpc v1.3.0 + +# When updating, also update RUNC_COMMIT in hack/dockerfile/binaries-commits accordingly +github.com/opencontainers/runc 0351df1c5a66838d0c392b4ac4cf9450de844e2d +github.com/opencontainers/image-spec 372ad780f63454fbbbbcc7cf80e5b90245c13e13 +github.com/opencontainers/runtime-spec v1.0.0 + +github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 + +# libcontainer deps (see src/github.com/opencontainers/runc/Godeps/Godeps.json) +github.com/coreos/go-systemd v4 +github.com/godbus/dbus v4.0.0 +github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852 +github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4 + +# gelf logging driver deps +github.com/Graylog2/go-gelf v2 + +github.com/fluent/fluent-logger-golang v1.2.1 +# fluent-logger-golang deps +github.com/philhofer/fwd 98c11a7a6ec829d672b03833c3d69a7fae1ca972 +github.com/tinylib/msgp 75ee40d2601edf122ef667e2a07d600d4c44490c + +# fsnotify +github.com/fsnotify/fsnotify v1.4.2 + +# awslogs deps +github.com/aws/aws-sdk-go v1.4.22 +github.com/go-ini/ini 060d7da055ba6ec5ea7a31f116332fe5efa04ce0 +github.com/jmespath/go-jmespath 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74 + +# logentries +github.com/bsphere/le_go 7a984a84b5492ae539b79b62fb4a10afc63c7bcf + +# gcplogs deps +golang.org/x/oauth2 96382aa079b72d8c014eb0c50f6c223d1e6a2de0 +google.golang.org/api 3cc2e591b550923a2c5f0ab5a803feda924d5823 +cloud.google.com/go 9d965e63e8cceb1b5d7977a202f0fcb8866d6525 +github.com/googleapis/gax-go da06d194a00e19ce00d9011a13931c3f6f6887c7 +google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 + +# containerd +github.com/containerd/containerd 06b9cb35161009dcb7123345749fef02f7cea8e0 +github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4 + +# cluster +github.com/docker/swarmkit 872861d2ae46958af7ead1d5fffb092c73afbaf0 +github.com/gogo/protobuf v0.4 +github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a +github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e +golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca +golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb +github.com/hashicorp/go-memdb cb9a474f84cc5e41b273b20c6927680b2a8776ad +github.com/hashicorp/go-immutable-radix 8e8ed81f8f0bf1bdd829593fdd5c29922c1ea990 +github.com/hashicorp/golang-lru a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4 +github.com/coreos/pkg fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8 +github.com/pivotal-golang/clock 3fd3c1944c59d9742e1cd333672181cd1a6f9fa0 +github.com/prometheus/client_golang 52437c81da6b127a9925d17eb3a382a2e5fd395e +github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 +github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6 +github.com/prometheus/common ebdfc6da46522d58825777cf1f90490a5b1ef1d8 +github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5 +github.com/matttproud/golang_protobuf_extensions v1.0.0 +github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9 +github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 + +# cli +github.com/spf13/cobra v1.5.1 https://github.com/dnephin/cobra.git +github.com/spf13/pflag 9ff6c6923cfffbcd502984b8e0c80539a94968b7 +github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 +github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.com/ijc25/Gotty + +# metrics +github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18 + +github.com/opencontainers/selinux v1.0.0-rc1 + +# archive/tar +# mkdir -p ./vendor/archive +# git clone git://github.com/tonistiigi/go-1.git ./go +# git --git-dir ./go/.git --work-tree ./go checkout revert-prefix-ignore +# cp -a go/src/archive/tar ./vendor/archive/tar +# rm -rf ./go +# vndr diff --git a/libgo/go/cmd/go/internal/modconv/testdata/panicparse.out b/libgo/go/cmd/go/internal/modconv/testdata/panicparse.out new file mode 100644 index 0000000..8830033 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/panicparse.out @@ -0,0 +1,8 @@ +github.com/kr/pretty 737b74a46c4bf788349f72cb256fed10aea4d0ac +github.com/kr/text 7cafcd837844e784b526369c9bce262804aebc60 +github.com/maruel/ut a9c9f15ccfa6f8b90182a53df32f4745586fbae3 +github.com/mattn/go-colorable 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59 +github.com/mattn/go-isatty 56b76bdf51f7708750eac80fa38b952bb9f32639 +github.com/mgutz/ansi c286dcecd19ff979eeb73ea444e479b903f2cfcb +github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2 +golang.org/x/sys a646d33e2ee3172a661fc09bca23bb4889a41bc8 diff --git a/libgo/go/cmd/go/internal/modconv/testdata/panicparse.vyml b/libgo/go/cmd/go/internal/modconv/testdata/panicparse.vyml new file mode 100644 index 0000000..ff3d43f --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/panicparse.vyml @@ -0,0 +1,17 @@ +vendors: +- path: github.com/kr/pretty + rev: 737b74a46c4bf788349f72cb256fed10aea4d0ac +- path: github.com/kr/text + rev: 7cafcd837844e784b526369c9bce262804aebc60 +- path: github.com/maruel/ut + rev: a9c9f15ccfa6f8b90182a53df32f4745586fbae3 +- path: github.com/mattn/go-colorable + rev: 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59 +- path: github.com/mattn/go-isatty + rev: 56b76bdf51f7708750eac80fa38b952bb9f32639 +- path: github.com/mgutz/ansi + rev: c286dcecd19ff979eeb73ea444e479b903f2cfcb +- path: github.com/pmezard/go-difflib + rev: 792786c7400a136282c1664665ae0a8db921c6c2 +- path: golang.org/x/sys + rev: a646d33e2ee3172a661fc09bca23bb4889a41bc8 diff --git a/libgo/go/cmd/go/internal/modconv/testdata/prometheus.out b/libgo/go/cmd/go/internal/modconv/testdata/prometheus.out new file mode 100644 index 0000000..d11b8ec --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/prometheus.out @@ -0,0 +1,258 @@ +cloud.google.com/go/compute/metadata c589d0c9f0d81640c518354c7bcae77d99820aa3 +cloud.google.com/go/internal c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/Azure/azure-sdk-for-go/arm/compute bd73d950fa4440dae889bd9917bff7cef539f86e +github.com/Azure/azure-sdk-for-go/arm/network bd73d950fa4440dae889bd9917bff7cef539f86e +github.com/Azure/go-autorest/autorest 8a25372bbfec739b8719a9e3987400d15ef9e179 +github.com/Azure/go-autorest/autorest/azure 8a25372bbfec739b8719a9e3987400d15ef9e179 +github.com/Azure/go-autorest/autorest/date 8a25372bbfec739b8719a9e3987400d15ef9e179 +github.com/Azure/go-autorest/autorest/to 8a25372bbfec739b8719a9e3987400d15ef9e179 +github.com/Azure/go-autorest/autorest/validation 8a25372bbfec739b8719a9e3987400d15ef9e179 +github.com/PuerkitoBio/purell c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/PuerkitoBio/urlesc c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/asaskevich/govalidator 7b3beb6df3c42abd3509abfc3bcacc0fbfb7c877 +github.com/aws/aws-sdk-go/aws 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/aws/awserr 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/aws/awsutil 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/aws/client 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/aws/client/metadata 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/aws/corehandlers 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/aws/credentials 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/aws/credentials/endpointcreds 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/aws/credentials/stscreds 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/aws/defaults 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/aws/ec2metadata 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/aws/request 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/aws/session 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/aws/signer/v4 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/private/endpoints 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/private/protocol 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/private/protocol/ec2query 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/private/protocol/query 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/private/protocol/query/queryutil 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/private/protocol/rest 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/private/waiter 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/service/ec2 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/aws/aws-sdk-go/service/sts 707203bc55114ed114446bf57949c5c211d8b7c0 +github.com/beorn7/perks/quantile 3ac7bf7a47d159a033b107610db8a1b6575507a4 +github.com/blang/semver c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/coreos/go-oidc/http c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/coreos/go-oidc/jose c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/coreos/go-oidc/key c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/coreos/go-oidc/oauth2 c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/coreos/go-oidc/oidc c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/coreos/pkg/health c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/coreos/pkg/httputil c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/coreos/pkg/timeutil c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/davecgh/go-spew/spew c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/dgrijalva/jwt-go 9ed569b5d1ac936e6494082958d63a6aa4fff99a +github.com/docker/distribution/digest c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/docker/distribution/reference c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/emicklei/go-restful c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/emicklei/go-restful/log c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/emicklei/go-restful/swagger c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/ghodss/yaml c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/go-ini/ini 6e4869b434bd001f6983749881c7ead3545887d8 +github.com/go-openapi/jsonpointer c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/go-openapi/jsonreference c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/go-openapi/spec c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/go-openapi/swag c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/gogo/protobuf/proto c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/gogo/protobuf/sortkeys c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/golang/glog c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/golang/protobuf/proto 98fa357170587e470c5f27d3c3ea0947b71eb455 +github.com/golang/snappy d9eb7a3d35ec988b8585d4a0068e462c27d28380 +github.com/google/gofuzz c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/hashicorp/consul/api daacc4be8bee214e3fc4b32a6dd385f5ef1b4c36 +github.com/hashicorp/go-cleanhttp ad28ea4487f05916463e2423a55166280e8254b5 +github.com/hashicorp/serf/coordinate 1d4fa605f6ff3ed628d7ae5eda7c0e56803e72a5 +github.com/influxdb/influxdb/client 291aaeb9485b43b16875c238482b2f7d0a22a13b +github.com/influxdb/influxdb/tsdb 291aaeb9485b43b16875c238482b2f7d0a22a13b +github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d +github.com/jonboulle/clockwork c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/juju/ratelimit c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/julienschmidt/httprouter 109e267447e95ad1bb48b758e40dd7453eb7b039 +github.com/mailru/easyjson/buffer c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/mailru/easyjson/jlexer c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/mailru/easyjson/jwriter c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/matttproud/golang_protobuf_extensions/pbutil fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a +github.com/miekg/dns 58f52c57ce9df13460ac68200cef30a008b9c468 +github.com/pborman/uuid c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/pmezard/go-difflib/difflib d77da356e56a7428ad25149ca77381849a6a5232 +github.com/prometheus/client_golang/prometheus c5b7fccd204277076155f10851dad72b76a49317 +github.com/prometheus/client_model/go fa8ad6fec33561be4280a8f0514318c79d7f6cb6 +github.com/prometheus/common/expfmt 85637ea67b04b5c3bb25e671dacded2977f8f9f6 +github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg 85637ea67b04b5c3bb25e671dacded2977f8f9f6 +github.com/prometheus/common/log 85637ea67b04b5c3bb25e671dacded2977f8f9f6 +github.com/prometheus/common/model 85637ea67b04b5c3bb25e671dacded2977f8f9f6 +github.com/prometheus/common/route 85637ea67b04b5c3bb25e671dacded2977f8f9f6 +github.com/prometheus/common/version 85637ea67b04b5c3bb25e671dacded2977f8f9f6 +github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5 +github.com/samuel/go-zookeeper/zk 177002e16a0061912f02377e2dd8951a8b3551bc +github.com/spf13/pflag c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/stretchr/testify/assert d77da356e56a7428ad25149ca77381849a6a5232 +github.com/stretchr/testify/require d77da356e56a7428ad25149ca77381849a6a5232 +github.com/syndtr/goleveldb/leveldb 6b4daa5362b502898ddf367c5c11deb9e7a5c727 +github.com/syndtr/goleveldb/leveldb/cache 6b4daa5362b502898ddf367c5c11deb9e7a5c727 +github.com/syndtr/goleveldb/leveldb/comparer 6b4daa5362b502898ddf367c5c11deb9e7a5c727 +github.com/syndtr/goleveldb/leveldb/errors 6b4daa5362b502898ddf367c5c11deb9e7a5c727 +github.com/syndtr/goleveldb/leveldb/filter 6b4daa5362b502898ddf367c5c11deb9e7a5c727 +github.com/syndtr/goleveldb/leveldb/iterator 6b4daa5362b502898ddf367c5c11deb9e7a5c727 +github.com/syndtr/goleveldb/leveldb/journal 6b4daa5362b502898ddf367c5c11deb9e7a5c727 +github.com/syndtr/goleveldb/leveldb/memdb 6b4daa5362b502898ddf367c5c11deb9e7a5c727 +github.com/syndtr/goleveldb/leveldb/opt 6b4daa5362b502898ddf367c5c11deb9e7a5c727 +github.com/syndtr/goleveldb/leveldb/storage 6b4daa5362b502898ddf367c5c11deb9e7a5c727 +github.com/syndtr/goleveldb/leveldb/table 6b4daa5362b502898ddf367c5c11deb9e7a5c727 +github.com/syndtr/goleveldb/leveldb/util 6b4daa5362b502898ddf367c5c11deb9e7a5c727 +github.com/ugorji/go/codec c589d0c9f0d81640c518354c7bcae77d99820aa3 +github.com/vaughan0/go-ini a98ad7ee00ec53921f08832bc06ecf7fd600e6a1 +golang.org/x/net/context b336a971b799939dd16ae9b1df8334cb8b977c4d +golang.org/x/net/context/ctxhttp b336a971b799939dd16ae9b1df8334cb8b977c4d +golang.org/x/net/http2 c589d0c9f0d81640c518354c7bcae77d99820aa3 +golang.org/x/net/http2/hpack c589d0c9f0d81640c518354c7bcae77d99820aa3 +golang.org/x/net/idna c589d0c9f0d81640c518354c7bcae77d99820aa3 +golang.org/x/net/internal/timeseries 6250b412798208e6c90b03b7c4f226de5aa299e2 +golang.org/x/net/lex/httplex c589d0c9f0d81640c518354c7bcae77d99820aa3 +golang.org/x/net/netutil bc3663df0ac92f928d419e31e0d2af22e683a5a2 +golang.org/x/oauth2 65a8d08c6292395d47053be10b3c5e91960def76 +golang.org/x/oauth2/google 65a8d08c6292395d47053be10b3c5e91960def76 +golang.org/x/oauth2/internal 65a8d08c6292395d47053be10b3c5e91960def76 +golang.org/x/oauth2/jws 65a8d08c6292395d47053be10b3c5e91960def76 +golang.org/x/oauth2/jwt 65a8d08c6292395d47053be10b3c5e91960def76 +golang.org/x/sys/unix c200b10b5d5e122be351b67af224adc6128af5bf +golang.org/x/sys/windows c200b10b5d5e122be351b67af224adc6128af5bf +golang.org/x/sys/windows/registry c200b10b5d5e122be351b67af224adc6128af5bf +golang.org/x/sys/windows/svc/eventlog c200b10b5d5e122be351b67af224adc6128af5bf +golang.org/x/text/cases c589d0c9f0d81640c518354c7bcae77d99820aa3 +golang.org/x/text/internal/tag c589d0c9f0d81640c518354c7bcae77d99820aa3 +golang.org/x/text/language c589d0c9f0d81640c518354c7bcae77d99820aa3 +golang.org/x/text/runes c589d0c9f0d81640c518354c7bcae77d99820aa3 +golang.org/x/text/secure/bidirule c589d0c9f0d81640c518354c7bcae77d99820aa3 +golang.org/x/text/secure/precis c589d0c9f0d81640c518354c7bcae77d99820aa3 +golang.org/x/text/transform c589d0c9f0d81640c518354c7bcae77d99820aa3 +golang.org/x/text/unicode/bidi c589d0c9f0d81640c518354c7bcae77d99820aa3 +golang.org/x/text/unicode/norm c589d0c9f0d81640c518354c7bcae77d99820aa3 +golang.org/x/text/width c589d0c9f0d81640c518354c7bcae77d99820aa3 +google.golang.org/api/compute/v1 63ade871fd3aec1225809d496e81ec91ab76ea29 +google.golang.org/api/gensupport 63ade871fd3aec1225809d496e81ec91ab76ea29 +google.golang.org/api/googleapi 63ade871fd3aec1225809d496e81ec91ab76ea29 +google.golang.org/api/googleapi/internal/uritemplates 63ade871fd3aec1225809d496e81ec91ab76ea29 +google.golang.org/appengine 267c27e7492265b84fc6719503b14a1e17975d79 +google.golang.org/appengine/internal 267c27e7492265b84fc6719503b14a1e17975d79 +google.golang.org/appengine/internal/app_identity 267c27e7492265b84fc6719503b14a1e17975d79 +google.golang.org/appengine/internal/base 267c27e7492265b84fc6719503b14a1e17975d79 +google.golang.org/appengine/internal/datastore 267c27e7492265b84fc6719503b14a1e17975d79 +google.golang.org/appengine/internal/log 267c27e7492265b84fc6719503b14a1e17975d79 +google.golang.org/appengine/internal/modules 267c27e7492265b84fc6719503b14a1e17975d79 +google.golang.org/appengine/internal/remote_api 4f7eeb5305a4ba1966344836ba4af9996b7b4e05 +google.golang.org/appengine/internal/urlfetch 267c27e7492265b84fc6719503b14a1e17975d79 +google.golang.org/appengine/urlfetch 267c27e7492265b84fc6719503b14a1e17975d79 +google.golang.org/cloud/compute/metadata 0a83eba2cadb60eb22123673c8fb6fca02b03c94 +google.golang.org/cloud/internal 0a83eba2cadb60eb22123673c8fb6fca02b03c94 +gopkg.in/fsnotify.v1 30411dbcefb7a1da7e84f75530ad3abe4011b4f8 +gopkg.in/inf.v0 c589d0c9f0d81640c518354c7bcae77d99820aa3 +gopkg.in/yaml.v2 7ad95dd0798a40da1ccdff6dff35fd177b5edf40 +k8s.io/client-go/1.5/discovery c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/kubernetes c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/kubernetes/typed/apps/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/kubernetes/typed/authentication/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/kubernetes/typed/authorization/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/kubernetes/typed/autoscaling/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/kubernetes/typed/batch/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/kubernetes/typed/certificates/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/kubernetes/typed/core/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/kubernetes/typed/extensions/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/kubernetes/typed/policy/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/kubernetes/typed/rbac/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/kubernetes/typed/storage/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/api c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/api/errors c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/api/install c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/api/meta c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/api/meta/metatypes c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/api/resource c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/api/unversioned c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/api/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/api/validation/path c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apimachinery c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apimachinery/announced c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apimachinery/registered c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/apps c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/apps/install c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/apps/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/authentication c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/authentication/install c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/authentication/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/authorization c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/authorization/install c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/authorization/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/autoscaling c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/autoscaling/install c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/autoscaling/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/batch c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/batch/install c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/batch/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/batch/v2alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/certificates c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/certificates/install c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/certificates/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/extensions c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/extensions/install c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/policy c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/policy/install c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/policy/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/rbac c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/rbac/install c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/rbac/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/storage c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/storage/install c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/apis/storage/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/auth/user c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/conversion c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/conversion/queryparams c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/fields c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/genericapiserver/openapi/common c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/labels c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/runtime c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/runtime/serializer c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/runtime/serializer/json c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/runtime/serializer/protobuf c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/runtime/serializer/recognizer c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/runtime/serializer/streaming c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/runtime/serializer/versioning c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/selection c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/third_party/forked/golang/reflect c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/types c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/cert c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/clock c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/errors c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/flowcontrol c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/framer c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/integer c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/intstr c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/json c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/labels c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/net c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/parsers c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/rand c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/runtime c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/sets c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/uuid c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/validation c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/validation/field c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/wait c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/util/yaml c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/version c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/watch c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/pkg/watch/versioned c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/plugin/pkg/client/auth c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/plugin/pkg/client/auth/gcp c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/plugin/pkg/client/auth/oidc c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/rest c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/tools/cache c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/tools/clientcmd/api c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/tools/metrics c589d0c9f0d81640c518354c7bcae77d99820aa3 +k8s.io/client-go/1.5/transport c589d0c9f0d81640c518354c7bcae77d99820aa3 diff --git a/libgo/go/cmd/go/internal/modconv/testdata/prometheus.vjson b/libgo/go/cmd/go/internal/modconv/testdata/prometheus.vjson new file mode 100644 index 0000000..648bec4 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/prometheus.vjson @@ -0,0 +1,1605 @@ +{ + "comment": "", + "ignore": "test appengine", + "package": [ + { + "checksumSHA1": "Cslv4/ITyQmgjSUhNXFu8q5bqOU=", + "origin": "k8s.io/client-go/1.5/vendor/cloud.google.com/go/compute/metadata", + "path": "cloud.google.com/go/compute/metadata", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "hiJXjkFEGy+sDFf6O58Ocdy9Rnk=", + "origin": "k8s.io/client-go/1.5/vendor/cloud.google.com/go/internal", + "path": "cloud.google.com/go/internal", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "oIt4tXgFYnZJBsCac1BQLnTWALM=", + "path": "github.com/Azure/azure-sdk-for-go/arm/compute", + "revision": "bd73d950fa4440dae889bd9917bff7cef539f86e", + "revisionTime": "2016-10-28T18:31:11Z" + }, + { + "checksumSHA1": "QKi6LiSyD5GnRK8ExpMgZl4XiMI=", + "path": "github.com/Azure/azure-sdk-for-go/arm/network", + "revision": "bd73d950fa4440dae889bd9917bff7cef539f86e", + "revisionTime": "2016-10-28T18:31:11Z" + }, + { + "checksumSHA1": "eVSHe6GIHj9/ziFrQLZ1SC7Nn6k=", + "path": "github.com/Azure/go-autorest/autorest", + "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179", + "revisionTime": "2016-10-25T18:07:34Z" + }, + { + "checksumSHA1": "0sYi0JprevG/PZjtMbOh8h0pt0g=", + "path": "github.com/Azure/go-autorest/autorest/azure", + "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179", + "revisionTime": "2016-10-25T18:07:34Z" + }, + { + "checksumSHA1": "q9Qz8PAxK5FTOZwgYKe5Lj38u4c=", + "path": "github.com/Azure/go-autorest/autorest/date", + "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179", + "revisionTime": "2016-10-25T18:07:34Z" + }, + { + "checksumSHA1": "Ev8qCsbFjDlMlX0N2tYAhYQFpUc=", + "path": "github.com/Azure/go-autorest/autorest/to", + "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179", + "revisionTime": "2016-10-25T18:07:34Z" + }, + { + "checksumSHA1": "oBixceM+55gdk47iff8DSEIh3po=", + "path": "github.com/Azure/go-autorest/autorest/validation", + "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179", + "revisionTime": "2016-10-25T18:07:34Z" + }, + { + "checksumSHA1": "IatnluZB5jTVUncMN134e4VOV34=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/PuerkitoBio/purell", + "path": "github.com/PuerkitoBio/purell", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "E/Tz8z0B/gaR551g+XqPKAhcteM=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/PuerkitoBio/urlesc", + "path": "github.com/PuerkitoBio/urlesc", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "BdLdZP/C2uOO3lqk9X3NCKFpXa4=", + "path": "github.com/asaskevich/govalidator", + "revision": "7b3beb6df3c42abd3509abfc3bcacc0fbfb7c877", + "revisionTime": "2016-10-01T16:31:30Z" + }, + { + "checksumSHA1": "WNfR3yhLjRC5/uccgju/bwrdsxQ=", + "path": "github.com/aws/aws-sdk-go/aws", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "Y9W+4GimK4Fuxq+vyIskVYFRnX4=", + "path": "github.com/aws/aws-sdk-go/aws/awserr", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "+q4vdl3l1Wom8K1wfIpJ4jlFsbY=", + "path": "github.com/aws/aws-sdk-go/aws/awsutil", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "/232RBWA3KnT7U+wciPS2+wmvR0=", + "path": "github.com/aws/aws-sdk-go/aws/client", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "ieAJ+Cvp/PKv1LpUEnUXpc3OI6E=", + "path": "github.com/aws/aws-sdk-go/aws/client/metadata", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "c1N3Loy3AS9zD+m5CzpPNAED39U=", + "path": "github.com/aws/aws-sdk-go/aws/corehandlers", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "zu5C95rmCZff6NYZb62lEaT5ibE=", + "path": "github.com/aws/aws-sdk-go/aws/credentials", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "KQiUK/zr3mqnAXD7x/X55/iNme0=", + "path": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "NUJUTWlc1sV8b7WjfiYc4JZbXl0=", + "path": "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "4Ipx+5xN0gso+cENC2MHMWmQlR4=", + "path": "github.com/aws/aws-sdk-go/aws/credentials/stscreds", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "DwhFsNluCFEwqzyp3hbJR3q2Wqs=", + "path": "github.com/aws/aws-sdk-go/aws/defaults", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "8E0fEBUJY/1lJOyVxzTxMGQGInk=", + "path": "github.com/aws/aws-sdk-go/aws/ec2metadata", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "5Ac22YMTBmrX/CXaEIXzWljr8UY=", + "path": "github.com/aws/aws-sdk-go/aws/request", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "eOo6evLMAxQfo7Qkc5/h5euN1Sw=", + "path": "github.com/aws/aws-sdk-go/aws/session", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "diXvBs1LRC0RJ9WK6sllWKdzC04=", + "path": "github.com/aws/aws-sdk-go/aws/signer/v4", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "Esab5F8KswqkTdB4TtjSvZgs56k=", + "path": "github.com/aws/aws-sdk-go/private/endpoints", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "wk7EyvDaHwb5qqoOP/4d3cV0708=", + "path": "github.com/aws/aws-sdk-go/private/protocol", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "1QmQ3FqV37w0Zi44qv8pA1GeR0A=", + "path": "github.com/aws/aws-sdk-go/private/protocol/ec2query", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "ZqY5RWavBLWTo6j9xqdyBEaNFRk=", + "path": "github.com/aws/aws-sdk-go/private/protocol/query", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "5xzix1R8prUyWxgLnzUQoxTsfik=", + "path": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "TW/7U+/8ormL7acf6z2rv2hDD+s=", + "path": "github.com/aws/aws-sdk-go/private/protocol/rest", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "eUEkjyMPAuekKBE4ou+nM9tXEas=", + "path": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "Eo9yODN5U99BK0pMzoqnBm7PCrY=", + "path": "github.com/aws/aws-sdk-go/private/waiter", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "6h4tJ9wVtbYb9wG4srtUxyPoAYM=", + "path": "github.com/aws/aws-sdk-go/service/ec2", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "ouwhxcAsIYQ6oJbMRdLW/Ys/iyg=", + "path": "github.com/aws/aws-sdk-go/service/sts", + "revision": "707203bc55114ed114446bf57949c5c211d8b7c0", + "revisionTime": "2016-11-02T21:59:28Z" + }, + { + "checksumSHA1": "4QnLdmB1kG3N+KlDd1N+G9TWAGQ=", + "path": "github.com/beorn7/perks/quantile", + "revision": "3ac7bf7a47d159a033b107610db8a1b6575507a4", + "revisionTime": "2016-02-29T21:34:45Z" + }, + { + "checksumSHA1": "n+s4YwtzpMWW5Rt0dEaQa7NHDGQ=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/blang/semver", + "path": "github.com/blang/semver", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "Z2AOGSmDKKvI6nuxa+UPjQWpIeM=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/http", + "path": "github.com/coreos/go-oidc/http", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "8yvt1xKCgNwuuavJdxRnvaIjrIc=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/jose", + "path": "github.com/coreos/go-oidc/jose", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "zhXKrWBSSJLqZxVE/Xsw0M9ynFQ=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/key", + "path": "github.com/coreos/go-oidc/key", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "bkW0mnXvmHQwHprW/6wrbpP7lAk=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/oauth2", + "path": "github.com/coreos/go-oidc/oauth2", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "E1x2k5FdhJ+dzFrh3kCmC6aJfVw=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/oidc", + "path": "github.com/coreos/go-oidc/oidc", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "O0UMBRCOD9ItMayDqLQ2MJEjkVE=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/pkg/health", + "path": "github.com/coreos/pkg/health", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "74vyZz/d49FZXMbFaHOfCGvSLj0=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/pkg/httputil", + "path": "github.com/coreos/pkg/httputil", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "etBdQ0LN6ojGunfvUt6B5C3FNrQ=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/pkg/timeutil", + "path": "github.com/coreos/pkg/timeutil", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "SdSd7pyjONWWTHc5XE3AhglLo34=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/davecgh/go-spew/spew", + "path": "github.com/davecgh/go-spew/spew", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "2Fy1Y6Z3lRRX1891WF/+HT4XS2I=", + "path": "github.com/dgrijalva/jwt-go", + "revision": "9ed569b5d1ac936e6494082958d63a6aa4fff99a", + "revisionTime": "2016-11-01T19:39:35Z" + }, + { + "checksumSHA1": "f1wARLDzsF/JoyN01yoxXEwFIp8=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/docker/distribution/digest", + "path": "github.com/docker/distribution/digest", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "PzXRTLmmqWXxmDqdIXLcRYBma18=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/docker/distribution/reference", + "path": "github.com/docker/distribution/reference", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "1vQR+ZyudsjKio6RNKmWhwzGTb0=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/emicklei/go-restful", + "path": "github.com/emicklei/go-restful", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "3xWz4fZ9xW+CfADpYoPFcZCYJ4E=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/emicklei/go-restful/log", + "path": "github.com/emicklei/go-restful/log", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "J7CtF9gIs2yH9A7lPQDDrhYxiRk=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/emicklei/go-restful/swagger", + "path": "github.com/emicklei/go-restful/swagger", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "ww7LVo7jNJ1o6sfRcromEHKyY+o=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/ghodss/yaml", + "path": "github.com/ghodss/yaml", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "cVyhKIRI2gQrgpn5qrBeAqErmWM=", + "path": "github.com/go-ini/ini", + "revision": "6e4869b434bd001f6983749881c7ead3545887d8", + "revisionTime": "2016-08-27T06:11:18Z" + }, + { + "checksumSHA1": "NaZnW0tKj/b0k5WzcMD0twrLbrE=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/go-openapi/jsonpointer", + "path": "github.com/go-openapi/jsonpointer", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "3LJXjMDxPY+veIqzQtiAvK3hXnY=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/go-openapi/jsonreference", + "path": "github.com/go-openapi/jsonreference", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "faeB3fny260hQ/gEfEXa1ZQTGtk=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/go-openapi/spec", + "path": "github.com/go-openapi/spec", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "wGpZwJ5HZtReou8A3WEV1Gdxs6k=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/go-openapi/swag", + "path": "github.com/go-openapi/swag", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "BIyZQL97iG7mzZ2UMR3XpiXbZdc=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/gogo/protobuf/proto", + "path": "github.com/gogo/protobuf/proto", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "e6cMbpJj41MpihS5eP4SIliRBK4=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/gogo/protobuf/sortkeys", + "path": "github.com/gogo/protobuf/sortkeys", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "URsJa4y/sUUw/STmbeYx9EKqaYE=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/golang/glog", + "path": "github.com/golang/glog", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "yDh5kmmr0zEF1r+rvYqbZcR7iLs=", + "path": "github.com/golang/protobuf/proto", + "revision": "98fa357170587e470c5f27d3c3ea0947b71eb455", + "revisionTime": "2016-10-12T20:53:35Z" + }, + { + "checksumSHA1": "2a/SsTUBMKtcM6VtpbdPGO+c6c8=", + "path": "github.com/golang/snappy", + "revision": "d9eb7a3d35ec988b8585d4a0068e462c27d28380", + "revisionTime": "2016-05-29T05:00:41Z" + }, + { + "checksumSHA1": "/yFfUp3tGt6cK22UVzbq8SjPDCU=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/google/gofuzz", + "path": "github.com/google/gofuzz", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "LclVLJYrBi03PBjsVPpgoMbUDQ8=", + "path": "github.com/hashicorp/consul/api", + "revision": "daacc4be8bee214e3fc4b32a6dd385f5ef1b4c36", + "revisionTime": "2016-10-28T04:06:46Z" + }, + { + "checksumSHA1": "Uzyon2091lmwacNsl1hCytjhHtg=", + "path": "github.com/hashicorp/go-cleanhttp", + "revision": "ad28ea4487f05916463e2423a55166280e8254b5", + "revisionTime": "2016-04-07T17:41:26Z" + }, + { + "checksumSHA1": "E3Xcanc9ouQwL+CZGOUyA/+giLg=", + "path": "github.com/hashicorp/serf/coordinate", + "revision": "1d4fa605f6ff3ed628d7ae5eda7c0e56803e72a5", + "revisionTime": "2016-10-07T00:41:22Z" + }, + { + "path": "github.com/influxdb/influxdb/client", + "revision": "291aaeb9485b43b16875c238482b2f7d0a22a13b", + "revisionTime": "2015-09-16T14:41:53+02:00" + }, + { + "path": "github.com/influxdb/influxdb/tsdb", + "revision": "291aaeb9485b43b16875c238482b2f7d0a22a13b", + "revisionTime": "2015-09-16T14:41:53+02:00" + }, + { + "checksumSHA1": "0ZrwvB6KoGPj2PoDNSEJwxQ6Mog=", + "path": "github.com/jmespath/go-jmespath", + "revision": "bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d", + "revisionTime": "2016-08-03T19:07:31Z" + }, + { + "checksumSHA1": "9ZVOEbIXnTuYpVqce4en8rwlkPE=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/jonboulle/clockwork", + "path": "github.com/jonboulle/clockwork", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "gA95N2LM2hEJLoqrTPaFsSWDJ2Y=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/juju/ratelimit", + "path": "github.com/juju/ratelimit", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "Farach1xcmsQYrhiUfkwF2rbIaE=", + "path": "github.com/julienschmidt/httprouter", + "revision": "109e267447e95ad1bb48b758e40dd7453eb7b039", + "revisionTime": "2015-09-05T19:25:33+02:00" + }, + { + "checksumSHA1": "urY45++NYCue4nh4k8OjUFnIGfU=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/mailru/easyjson/buffer", + "path": "github.com/mailru/easyjson/buffer", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "yTDKAM4KBgOvXRsZC50zg0OChvM=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/mailru/easyjson/jlexer", + "path": "github.com/mailru/easyjson/jlexer", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "4+d+6rhM1pei6lBguhqSEW7LaXs=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/mailru/easyjson/jwriter", + "path": "github.com/mailru/easyjson/jwriter", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "Q2vw4HZBbnU8BLFt8VrzStwqSJg=", + "path": "github.com/matttproud/golang_protobuf_extensions/pbutil", + "revision": "fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a", + "revisionTime": "2015-04-06T19:39:34+02:00" + }, + { + "checksumSHA1": "Wahi4g/9XiHhSLAJ+8jskg71PCU=", + "path": "github.com/miekg/dns", + "revision": "58f52c57ce9df13460ac68200cef30a008b9c468", + "revisionTime": "2016-10-18T06:08:08Z" + }, + { + "checksumSHA1": "3YJklSuzSE1Rt8A+2dhiWSmf/fw=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/pborman/uuid", + "path": "github.com/pborman/uuid", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "zKKp5SZ3d3ycKe4EKMNT0BqAWBw=", + "origin": "github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib", + "path": "github.com/pmezard/go-difflib/difflib", + "revision": "d77da356e56a7428ad25149ca77381849a6a5232", + "revisionTime": "2016-06-15T09:26:46Z" + }, + { + "checksumSHA1": "KkB+77Ziom7N6RzSbyUwYGrmDeU=", + "path": "github.com/prometheus/client_golang/prometheus", + "revision": "c5b7fccd204277076155f10851dad72b76a49317", + "revisionTime": "2016-08-17T15:48:24Z" + }, + { + "checksumSHA1": "DvwvOlPNAgRntBzt3b3OSRMS2N4=", + "path": "github.com/prometheus/client_model/go", + "revision": "fa8ad6fec33561be4280a8f0514318c79d7f6cb6", + "revisionTime": "2015-02-12T10:17:44Z" + }, + { + "checksumSHA1": "mHyjbJ3BWOfUV6q9f5PBt0gaY1k=", + "path": "github.com/prometheus/common/expfmt", + "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6", + "revisionTime": "2016-10-02T21:02:34Z" + }, + { + "checksumSHA1": "GWlM3d2vPYyNATtTFgftS10/A9w=", + "path": "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg", + "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6", + "revisionTime": "2016-10-02T21:02:34Z" + }, + { + "checksumSHA1": "UU6hIfhVjnAYDADQEfE/3T7Ddm8=", + "path": "github.com/prometheus/common/log", + "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6", + "revisionTime": "2016-10-02T21:02:34Z" + }, + { + "checksumSHA1": "nFie+rxcX5WdIv1diZ+fu3aj6lE=", + "path": "github.com/prometheus/common/model", + "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6", + "revisionTime": "2016-10-02T21:02:34Z" + }, + { + "checksumSHA1": "QQKJYoGcY10nIHxhBEHwjwUZQzk=", + "path": "github.com/prometheus/common/route", + "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6", + "revisionTime": "2016-10-02T21:02:34Z" + }, + { + "checksumSHA1": "91KYK0SpvkaMJJA2+BcxbVnyRO0=", + "path": "github.com/prometheus/common/version", + "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6", + "revisionTime": "2016-10-02T21:02:34Z" + }, + { + "checksumSHA1": "W218eJZPXJG783fUr/z6IaAZyes=", + "path": "github.com/prometheus/procfs", + "revision": "abf152e5f3e97f2fafac028d2cc06c1feb87ffa5", + "revisionTime": "2016-04-11T19:08:41Z" + }, + { + "checksumSHA1": "+49Vr4Me28p3cR+gxX5SUQHbbas=", + "path": "github.com/samuel/go-zookeeper/zk", + "revision": "177002e16a0061912f02377e2dd8951a8b3551bc", + "revisionTime": "2015-08-17T10:50:50-07:00" + }, + { + "checksumSHA1": "YuPBOVkkE3uuBh4RcRUTF0n+frs=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/spf13/pflag", + "path": "github.com/spf13/pflag", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "iydUphwYqZRq3WhstEdGsbvBAKs=", + "path": "github.com/stretchr/testify/assert", + "revision": "d77da356e56a7428ad25149ca77381849a6a5232", + "revisionTime": "2016-06-15T09:26:46Z" + }, + { + "checksumSHA1": "P9FJpir2c4G5PA46qEkaWy3l60U=", + "path": "github.com/stretchr/testify/require", + "revision": "d77da356e56a7428ad25149ca77381849a6a5232", + "revisionTime": "2016-06-15T09:26:46Z" + }, + { + "checksumSHA1": "VhcnDY37sYAnL8WjfYQN9YYl+W4=", + "path": "github.com/syndtr/goleveldb/leveldb", + "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727", + "revisionTime": "2016-10-11T05:00:08Z" + }, + { + "checksumSHA1": "EKIow7XkgNdWvR/982ffIZxKG8Y=", + "path": "github.com/syndtr/goleveldb/leveldb/cache", + "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727", + "revisionTime": "2016-10-11T05:00:08Z" + }, + { + "checksumSHA1": "5KPgnvCPlR0ysDAqo6jApzRQ3tw=", + "path": "github.com/syndtr/goleveldb/leveldb/comparer", + "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727", + "revisionTime": "2016-10-11T05:00:08Z" + }, + { + "checksumSHA1": "1DRAxdlWzS4U0xKN/yQ/fdNN7f0=", + "path": "github.com/syndtr/goleveldb/leveldb/errors", + "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727", + "revisionTime": "2016-10-11T05:00:08Z" + }, + { + "checksumSHA1": "eqKeD6DS7eNCtxVYZEHHRKkyZrw=", + "path": "github.com/syndtr/goleveldb/leveldb/filter", + "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727", + "revisionTime": "2016-10-11T05:00:08Z" + }, + { + "checksumSHA1": "8dXuAVIsbtaMiGGuHjzGR6Ny/5c=", + "path": "github.com/syndtr/goleveldb/leveldb/iterator", + "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727", + "revisionTime": "2016-10-11T05:00:08Z" + }, + { + "checksumSHA1": "gJY7bRpELtO0PJpZXgPQ2BYFJ88=", + "path": "github.com/syndtr/goleveldb/leveldb/journal", + "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727", + "revisionTime": "2016-10-11T05:00:08Z" + }, + { + "checksumSHA1": "j+uaQ6DwJ50dkIdfMQu1TXdlQcY=", + "path": "github.com/syndtr/goleveldb/leveldb/memdb", + "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727", + "revisionTime": "2016-10-11T05:00:08Z" + }, + { + "checksumSHA1": "UmQeotV+m8/FduKEfLOhjdp18rs=", + "path": "github.com/syndtr/goleveldb/leveldb/opt", + "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727", + "revisionTime": "2016-10-11T05:00:08Z" + }, + { + "checksumSHA1": "/Wvv9HeJTN9UUjdjwUlz7X4ioIo=", + "path": "github.com/syndtr/goleveldb/leveldb/storage", + "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727", + "revisionTime": "2016-10-11T05:00:08Z" + }, + { + "checksumSHA1": "JTJA+u8zk7EXy1UUmpFPNGvtO2A=", + "path": "github.com/syndtr/goleveldb/leveldb/table", + "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727", + "revisionTime": "2016-10-11T05:00:08Z" + }, + { + "checksumSHA1": "4zil8Gwg8VPkDn1YzlgCvtukJFU=", + "path": "github.com/syndtr/goleveldb/leveldb/util", + "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727", + "revisionTime": "2016-10-11T05:00:08Z" + }, + { + "checksumSHA1": "f6Aew+ZA+HBAXCw6/xTST3mB0Lw=", + "origin": "k8s.io/client-go/1.5/vendor/github.com/ugorji/go/codec", + "path": "github.com/ugorji/go/codec", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "sFD8LpJPQtWLwGda3edjf5mNUbs=", + "path": "github.com/vaughan0/go-ini", + "revision": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1", + "revisionTime": "2013-09-23T16:52:12+02:00" + }, + { + "checksumSHA1": "9jjO5GjLa0XF/nfWihF02RoH4qc=", + "path": "golang.org/x/net/context", + "revision": "b336a971b799939dd16ae9b1df8334cb8b977c4d", + "revisionTime": "2016-10-27T19:58:04Z" + }, + { + "checksumSHA1": "WHc3uByvGaMcnSoI21fhzYgbOgg=", + "path": "golang.org/x/net/context/ctxhttp", + "revision": "b336a971b799939dd16ae9b1df8334cb8b977c4d", + "revisionTime": "2016-10-27T19:58:04Z" + }, + { + "checksumSHA1": "SPYGC6DQrH9jICccUsOfbvvhB4g=", + "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/net/http2", + "path": "golang.org/x/net/http2", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "EYNaHp7XdLWRydUCE0amEkKAtgk=", + "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/net/http2/hpack", + "path": "golang.org/x/net/http2/hpack", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "gXiSniT8fevWOVPVKopYgrdzi60=", + "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/net/idna", + "path": "golang.org/x/net/idna", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "/k7k6eJDkxXx6K9Zpo/OwNm58XM=", + "path": "golang.org/x/net/internal/timeseries", + "revision": "6250b412798208e6c90b03b7c4f226de5aa299e2", + "revisionTime": "2016-08-24T22:20:41Z" + }, + { + "checksumSHA1": "yhndhWXMs/VSEDLks4dNyFMQStA=", + "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/net/lex/httplex", + "path": "golang.org/x/net/lex/httplex", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "7WASrg0PEueWDDRHkFhEEN6Qrms=", + "path": "golang.org/x/net/netutil", + "revision": "bc3663df0ac92f928d419e31e0d2af22e683a5a2", + "revisionTime": "2016-06-21T20:48:10Z" + }, + { + "checksumSHA1": "mktBVED98G2vv+OKcSgtnFVZC1Y=", + "path": "golang.org/x/oauth2", + "revision": "65a8d08c6292395d47053be10b3c5e91960def76", + "revisionTime": "2016-06-07T03:33:14Z" + }, + { + "checksumSHA1": "2rk6lthfQa5Rfydj8j7+dilKGbo=", + "path": "golang.org/x/oauth2/google", + "revision": "65a8d08c6292395d47053be10b3c5e91960def76", + "revisionTime": "2016-06-07T03:33:14Z" + }, + { + "checksumSHA1": "W/GiDqzsagBnR7/yEvxatMhUDBs=", + "path": "golang.org/x/oauth2/internal", + "revision": "65a8d08c6292395d47053be10b3c5e91960def76", + "revisionTime": "2016-06-07T03:33:14Z" + }, + { + "checksumSHA1": "CPTYHWrVL4jA0B1IuC0hvgcE2AQ=", + "path": "golang.org/x/oauth2/jws", + "revision": "65a8d08c6292395d47053be10b3c5e91960def76", + "revisionTime": "2016-06-07T03:33:14Z" + }, + { + "checksumSHA1": "xifBSq0Pn6pIoPA/o3tyzq8X4Ds=", + "path": "golang.org/x/oauth2/jwt", + "revision": "65a8d08c6292395d47053be10b3c5e91960def76", + "revisionTime": "2016-06-07T03:33:14Z" + }, + { + "checksumSHA1": "aVgPDgwY3/t4J/JOw9H3FVMHqh0=", + "path": "golang.org/x/sys/unix", + "revision": "c200b10b5d5e122be351b67af224adc6128af5bf", + "revisionTime": "2016-10-22T18:22:21Z" + }, + { + "checksumSHA1": "fpW2dhGFC6SrVzipJx7fjg2DIH8=", + "path": "golang.org/x/sys/windows", + "revision": "c200b10b5d5e122be351b67af224adc6128af5bf", + "revisionTime": "2016-10-22T18:22:21Z" + }, + { + "checksumSHA1": "PjYlbMS0ttyZYlaevvjA/gV3g1c=", + "path": "golang.org/x/sys/windows/registry", + "revision": "c200b10b5d5e122be351b67af224adc6128af5bf", + "revisionTime": "2016-10-22T18:22:21Z" + }, + { + "checksumSHA1": "uVlUSSKplihZG7N+QJ6fzDZ4Kh8=", + "path": "golang.org/x/sys/windows/svc/eventlog", + "revision": "c200b10b5d5e122be351b67af224adc6128af5bf", + "revisionTime": "2016-10-22T18:22:21Z" + }, + { + "checksumSHA1": "QQpKbWuqvhmxVr/hfEYdWzzcXRM=", + "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/cases", + "path": "golang.org/x/text/cases", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "iAsGo/kxvnwILbJVUCd0ZcqZO/Q=", + "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/internal/tag", + "path": "golang.org/x/text/internal/tag", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "mQ6PCGHY7K0oPjKbYD8wsTjm/P8=", + "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/language", + "path": "golang.org/x/text/language", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "WpeH2TweiuiZAQVTJNO5vyZAQQA=", + "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/runes", + "path": "golang.org/x/text/runes", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "1VjEPyjdi0xOiIN/Alkqiad/B/c=", + "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/secure/bidirule", + "path": "golang.org/x/text/secure/bidirule", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "FcK7VslktIAWj5jnWVnU2SesBq0=", + "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/secure/precis", + "path": "golang.org/x/text/secure/precis", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "nwlu7UTwYbCj9l5f3a7t2ROwNzM=", + "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/transform", + "path": "golang.org/x/text/transform", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "nWJ9R1+Xw41f/mM3b7BYtv77CfI=", + "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/unicode/bidi", + "path": "golang.org/x/text/unicode/bidi", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "BAZ96wCGUj6HdY9sG60Yw09KWA4=", + "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/unicode/norm", + "path": "golang.org/x/text/unicode/norm", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "AZMILKWqLP99UilLgbGZ+uzIVrM=", + "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/width", + "path": "golang.org/x/text/width", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "AjdmRXf0fiy6Bec9mNlsGsmZi1k=", + "path": "google.golang.org/api/compute/v1", + "revision": "63ade871fd3aec1225809d496e81ec91ab76ea29", + "revisionTime": "2016-05-31T06:42:46Z" + }, + { + "checksumSHA1": "OtsMVXY89Hc/bBXdDp84atFQawM=", + "path": "google.golang.org/api/gensupport", + "revision": "63ade871fd3aec1225809d496e81ec91ab76ea29", + "revisionTime": "2016-05-31T06:42:46Z" + }, + { + "checksumSHA1": "yQREK/OWrz9PLljbr127+xFk6J0=", + "path": "google.golang.org/api/googleapi", + "revision": "63ade871fd3aec1225809d496e81ec91ab76ea29", + "revisionTime": "2016-05-31T06:42:46Z" + }, + { + "checksumSHA1": "ii4ET3JHk3vkMUEcg+9t/1RZSUU=", + "path": "google.golang.org/api/googleapi/internal/uritemplates", + "revision": "63ade871fd3aec1225809d496e81ec91ab76ea29", + "revisionTime": "2016-05-31T06:42:46Z" + }, + { + "checksumSHA1": "N3KZEuQ9O1QwJXcCJbe7Czwroo4=", + "path": "google.golang.org/appengine", + "revision": "267c27e7492265b84fc6719503b14a1e17975d79", + "revisionTime": "2016-06-21T05:59:22Z" + }, + { + "checksumSHA1": "G9Xp1ScdsfcKsw+PcWunivRRP3o=", + "path": "google.golang.org/appengine/internal", + "revision": "267c27e7492265b84fc6719503b14a1e17975d79", + "revisionTime": "2016-06-21T05:59:22Z" + }, + { + "checksumSHA1": "x6Thdfyasqd68dWZWqzWWeIfAfI=", + "path": "google.golang.org/appengine/internal/app_identity", + "revision": "267c27e7492265b84fc6719503b14a1e17975d79", + "revisionTime": "2016-06-21T05:59:22Z" + }, + { + "checksumSHA1": "TsNO8P0xUlLNyh3Ic/tzSp/fDWM=", + "path": "google.golang.org/appengine/internal/base", + "revision": "267c27e7492265b84fc6719503b14a1e17975d79", + "revisionTime": "2016-06-21T05:59:22Z" + }, + { + "checksumSHA1": "5QsV5oLGSfKZqTCVXP6NRz5T4Tw=", + "path": "google.golang.org/appengine/internal/datastore", + "revision": "267c27e7492265b84fc6719503b14a1e17975d79", + "revisionTime": "2016-06-21T05:59:22Z" + }, + { + "checksumSHA1": "Gep2T9zmVYV8qZfK2gu3zrmG6QE=", + "path": "google.golang.org/appengine/internal/log", + "revision": "267c27e7492265b84fc6719503b14a1e17975d79", + "revisionTime": "2016-06-21T05:59:22Z" + }, + { + "checksumSHA1": "eLZVX1EHLclFtQnjDIszsdyWRHo=", + "path": "google.golang.org/appengine/internal/modules", + "revision": "267c27e7492265b84fc6719503b14a1e17975d79", + "revisionTime": "2016-06-21T05:59:22Z" + }, + { + "checksumSHA1": "a1XY7rz3BieOVqVI2Et6rKiwQCk=", + "path": "google.golang.org/appengine/internal/remote_api", + "revision": "4f7eeb5305a4ba1966344836ba4af9996b7b4e05", + "revisionTime": "2016-08-19T23:33:10Z" + }, + { + "checksumSHA1": "QtAbHtHmDzcf6vOV9eqlCpKgjiw=", + "path": "google.golang.org/appengine/internal/urlfetch", + "revision": "267c27e7492265b84fc6719503b14a1e17975d79", + "revisionTime": "2016-06-21T05:59:22Z" + }, + { + "checksumSHA1": "akOV9pYnCbcPA8wJUutSQVibdyg=", + "path": "google.golang.org/appengine/urlfetch", + "revision": "267c27e7492265b84fc6719503b14a1e17975d79", + "revisionTime": "2016-06-21T05:59:22Z" + }, + { + "checksumSHA1": "Wp8g9MHRmK8SwcyGVCoGtPx+5Lo=", + "path": "google.golang.org/cloud/compute/metadata", + "revision": "0a83eba2cadb60eb22123673c8fb6fca02b03c94", + "revisionTime": "2016-06-21T15:59:29Z" + }, + { + "checksumSHA1": "U7dGDNwEHORvJFMoNSXErKE7ITg=", + "path": "google.golang.org/cloud/internal", + "revision": "0a83eba2cadb60eb22123673c8fb6fca02b03c94", + "revisionTime": "2016-06-21T15:59:29Z" + }, + { + "checksumSHA1": "JfVmsMwyeeepbdw4q4wpN07BuFg=", + "path": "gopkg.in/fsnotify.v1", + "revision": "30411dbcefb7a1da7e84f75530ad3abe4011b4f8", + "revisionTime": "2016-04-12T13:37:56Z" + }, + { + "checksumSHA1": "pfQwQtWlFezJq0Viroa/L+v+yDM=", + "origin": "k8s.io/client-go/1.5/vendor/gopkg.in/inf.v0", + "path": "gopkg.in/inf.v0", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "KgT+peLCcuh0/m2mpoOZXuxXmwc=", + "path": "gopkg.in/yaml.v2", + "revision": "7ad95dd0798a40da1ccdff6dff35fd177b5edf40", + "revisionTime": "2015-06-24T11:29:02+01:00" + }, + { + "checksumSHA1": "st0Nbu4zwLcP3mz03lDOJVZtn8Y=", + "path": "k8s.io/client-go/1.5/discovery", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "S+OzpkipMb46LGZoWuveqSLAcoM=", + "path": "k8s.io/client-go/1.5/kubernetes", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "yCBn8ig1TUMrk+ljtK0nDr7E5Vo=", + "path": "k8s.io/client-go/1.5/kubernetes/typed/apps/v1alpha1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "ZRnUz5NrpvJsXAjtnRdEv5UYhSI=", + "path": "k8s.io/client-go/1.5/kubernetes/typed/authentication/v1beta1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "TY55Np20olmPMzXgfVlIUIyqv04=", + "path": "k8s.io/client-go/1.5/kubernetes/typed/authorization/v1beta1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "FRByJsFff/6lPH20FtJPaK1NPWI=", + "path": "k8s.io/client-go/1.5/kubernetes/typed/autoscaling/v1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "3Cy2as7HnQ2FDcvpNbatpFWx0P4=", + "path": "k8s.io/client-go/1.5/kubernetes/typed/batch/v1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "RUKywApIbSLLsfkYxXzifh7HIvs=", + "path": "k8s.io/client-go/1.5/kubernetes/typed/certificates/v1alpha1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "4+Lsxu+sYgzsS2JOHP7CdrZLSKc=", + "path": "k8s.io/client-go/1.5/kubernetes/typed/core/v1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "H8jzevN03YUfmf2krJt0qj2P9sU=", + "path": "k8s.io/client-go/1.5/kubernetes/typed/extensions/v1beta1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "hrpA6xxtwj3oMcQbFxI2cDhO2ZA=", + "path": "k8s.io/client-go/1.5/kubernetes/typed/policy/v1alpha1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "B2+F12NeMwrOHvHK2ALyEcr3UGA=", + "path": "k8s.io/client-go/1.5/kubernetes/typed/rbac/v1alpha1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "h2eSNUym87RWPlez7UKujShwrUQ=", + "path": "k8s.io/client-go/1.5/kubernetes/typed/storage/v1beta1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "+oIykJ3A0wYjAWbbrGo0jNnMLXw=", + "path": "k8s.io/client-go/1.5/pkg/api", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "UsUsIdhuy5Ej2vI0hbmSsrimoaQ=", + "path": "k8s.io/client-go/1.5/pkg/api/errors", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "Eo6LLHFqG6YznIAKr2mVjuqUj6k=", + "path": "k8s.io/client-go/1.5/pkg/api/install", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "dYznkLcCEai21z1dX8kZY7uDsck=", + "path": "k8s.io/client-go/1.5/pkg/api/meta", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "b06esG4xMj/YNFD85Lqq00cx+Yo=", + "path": "k8s.io/client-go/1.5/pkg/api/meta/metatypes", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "L9svak1yut0Mx8r9VLDOwpqZzBk=", + "path": "k8s.io/client-go/1.5/pkg/api/resource", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "m7jGshKDLH9kdokfa6MwAqzxRQk=", + "path": "k8s.io/client-go/1.5/pkg/api/unversioned", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "iI6s5WAexr1PEfqrbvuscB+oVik=", + "path": "k8s.io/client-go/1.5/pkg/api/v1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "ikac34qI/IkTWHnfi8pPl9irPyo=", + "path": "k8s.io/client-go/1.5/pkg/api/validation/path", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "MJyygSPp8N6z+7SPtcROz4PEwas=", + "path": "k8s.io/client-go/1.5/pkg/apimachinery", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "EGb4IcSTQ1VXCmX0xcyG5GpWId8=", + "path": "k8s.io/client-go/1.5/pkg/apimachinery/announced", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "vhSyuINHQhCsDKTyBmvJT1HzDHI=", + "path": "k8s.io/client-go/1.5/pkg/apimachinery/registered", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "rXeBnwLg8ZFe6m5/Ki7tELVBYDk=", + "path": "k8s.io/client-go/1.5/pkg/apis/apps", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "KzHaG858KV1tBh5cuLInNcm+G5s=", + "path": "k8s.io/client-go/1.5/pkg/apis/apps/install", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "fynWdchlRbPaxuST2oGDKiKLTqE=", + "path": "k8s.io/client-go/1.5/pkg/apis/apps/v1alpha1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "hreIYssoH4Ef/+Aglpitn3GNLR4=", + "path": "k8s.io/client-go/1.5/pkg/apis/authentication", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "EgUqJH4CqB9vXVg6T8II2OEt5LE=", + "path": "k8s.io/client-go/1.5/pkg/apis/authentication/install", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "Z3DKgomzRPGcBv/8hlL6pfnIpXI=", + "path": "k8s.io/client-go/1.5/pkg/apis/authentication/v1beta1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "GpuScB2Z+NOT4WIQg1mVvVSDUts=", + "path": "k8s.io/client-go/1.5/pkg/apis/authorization", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "+u3UD+HY9lBH+PFi/2B4W564JEw=", + "path": "k8s.io/client-go/1.5/pkg/apis/authorization/install", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "zIFzgWjmlWNLHGHMpCpDCvoLtKY=", + "path": "k8s.io/client-go/1.5/pkg/apis/authorization/v1beta1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "tdpzQFQyVkt5kCLTvtKTVqT+maE=", + "path": "k8s.io/client-go/1.5/pkg/apis/autoscaling", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "nb6LbYGS5tv8H8Ovptg6M7XuDZ4=", + "path": "k8s.io/client-go/1.5/pkg/apis/autoscaling/install", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "DNb1/nl/5RDdckRrJoXBRagzJXs=", + "path": "k8s.io/client-go/1.5/pkg/apis/autoscaling/v1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "4bLhH2vNl5l4Qp6MjLhWyWVAPE0=", + "path": "k8s.io/client-go/1.5/pkg/apis/batch", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "RpAAEynmxlvOlLLZK1KEUQRnYzk=", + "path": "k8s.io/client-go/1.5/pkg/apis/batch/install", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "uWJ2BHmjL/Gq4FFlNkqiN6vvPyM=", + "path": "k8s.io/client-go/1.5/pkg/apis/batch/v1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "mHWt/p724dKeP1vqLtWQCye7zaE=", + "path": "k8s.io/client-go/1.5/pkg/apis/batch/v2alpha1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "6dJ1dGfXkB3A42TOtMaY/rvv4N8=", + "path": "k8s.io/client-go/1.5/pkg/apis/certificates", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "Bkrhm6HbFYANwtzUE8eza9SWBk0=", + "path": "k8s.io/client-go/1.5/pkg/apis/certificates/install", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "nRRPIBQ5O3Ad24kscNtK+gPC+fk=", + "path": "k8s.io/client-go/1.5/pkg/apis/certificates/v1alpha1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "KUMhoaOg9GXHN/aAVvSLO18SgqU=", + "path": "k8s.io/client-go/1.5/pkg/apis/extensions", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "eSo2VhNAYtesvmpEPqn05goW4LY=", + "path": "k8s.io/client-go/1.5/pkg/apis/extensions/install", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "DunWIPrCC5iGMWzkaaugMOxD+hg=", + "path": "k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "rVGYi2ko0E7vL5OZSMYX+NAGPYw=", + "path": "k8s.io/client-go/1.5/pkg/apis/policy", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "llJHd2H0LzABGB6BcletzIHnexo=", + "path": "k8s.io/client-go/1.5/pkg/apis/policy/install", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "j44bqyY13ldnuCtysYE8nRkMD7o=", + "path": "k8s.io/client-go/1.5/pkg/apis/policy/v1alpha1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "vT7rFxowcKMTYc55mddePqUFRgE=", + "path": "k8s.io/client-go/1.5/pkg/apis/rbac", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "r1MzUXsG+Zyn30aU8I5R5dgrJPA=", + "path": "k8s.io/client-go/1.5/pkg/apis/rbac/install", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "aNfO8xn8VDO3fM9CpVCe6EIB+GA=", + "path": "k8s.io/client-go/1.5/pkg/apis/rbac/v1alpha1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "rQCxrbisCXmj2wymlYG63kcTL9I=", + "path": "k8s.io/client-go/1.5/pkg/apis/storage", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "wZyxh5nt5Eh6kF7YNAIYukKWWy0=", + "path": "k8s.io/client-go/1.5/pkg/apis/storage/install", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "P8ANOt/I4Cs3QtjVXWmDA/gpQdg=", + "path": "k8s.io/client-go/1.5/pkg/apis/storage/v1beta1", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "qnVPwzvNLz2mmr3BXdU9qIhQXXU=", + "path": "k8s.io/client-go/1.5/pkg/auth/user", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "KrIchxhapSs242yAy8yrTS1XlZo=", + "path": "k8s.io/client-go/1.5/pkg/conversion", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "weZqKFcOhcnF47eDDHXzluCKSF0=", + "path": "k8s.io/client-go/1.5/pkg/conversion/queryparams", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "T3EMfyXZX5939/OOQ1JU+Nmbk4k=", + "path": "k8s.io/client-go/1.5/pkg/fields", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "2v11s3EBH8UBl2qfImT29tQN2kM=", + "path": "k8s.io/client-go/1.5/pkg/genericapiserver/openapi/common", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "GvBlph6PywK3zguou/T9kKNNdoQ=", + "path": "k8s.io/client-go/1.5/pkg/labels", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "Vtrgy827r0rWzIAgvIWY4flu740=", + "path": "k8s.io/client-go/1.5/pkg/runtime", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "SEcZqRATexhgHvDn+eHvMc07UJs=", + "path": "k8s.io/client-go/1.5/pkg/runtime/serializer", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "qzYKG9YZSj8l/W1QVTOrGAry/BM=", + "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/json", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "F7h+8zZ0JPLYkac4KgSVljguBE4=", + "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/protobuf", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "CvySOL8C85e3y7EWQ+Au4cwUZJM=", + "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/recognizer", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "eCitoKeIun+lJzYFhAfdSIIicSM=", + "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/streaming", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "kVWvZuLGltJ4YqQsiaCLRRLDDK0=", + "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/versioning", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "m51+LAeQ9RK1KHX+l2iGcwbVCKs=", + "path": "k8s.io/client-go/1.5/pkg/selection", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "dp4IWcC3U6a0HeOdVCDQWODWCbw=", + "path": "k8s.io/client-go/1.5/pkg/third_party/forked/golang/reflect", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "ER898XJD1ox4d71gKZD8TLtTSpM=", + "path": "k8s.io/client-go/1.5/pkg/types", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "BVdXtnLDlmBQksRPfHOIG+qdeVg=", + "path": "k8s.io/client-go/1.5/pkg/util", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "nnh8Sa4dCupxRI4bbKaozGp1d/A=", + "path": "k8s.io/client-go/1.5/pkg/util/cert", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "S32d5uduNlwouM8+mIz+ALpliUQ=", + "path": "k8s.io/client-go/1.5/pkg/util/clock", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "Y6rWC0TUw2/uUeUjJ7kazyEUzBQ=", + "path": "k8s.io/client-go/1.5/pkg/util/errors", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "C7IfEAdCOePw3/IraaZCNXuYXLw=", + "path": "k8s.io/client-go/1.5/pkg/util/flowcontrol", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "EuslQHnhBSRXaWimYqLEqhMPV48=", + "path": "k8s.io/client-go/1.5/pkg/util/framer", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "ByO18NbZwiifFr8qtLyfJAHXguA=", + "path": "k8s.io/client-go/1.5/pkg/util/integer", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "ww+RfsoIlUBDwThg2oqC5QVz33Y=", + "path": "k8s.io/client-go/1.5/pkg/util/intstr", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "7E8f8dLlXW7u6r9sggMjvB4HEiw=", + "path": "k8s.io/client-go/1.5/pkg/util/json", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "d0pFZxMJG9j95acNmaIM1l+X+QU=", + "path": "k8s.io/client-go/1.5/pkg/util/labels", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "wCN7u1lE+25neM9jXeI7aE8EAfk=", + "path": "k8s.io/client-go/1.5/pkg/util/net", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "g+kBkxcb+tYmFtRRly+VE+JAIfw=", + "path": "k8s.io/client-go/1.5/pkg/util/parsers", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "S4wUnE5VkaWWrkLbgPL/1oNLJ4g=", + "path": "k8s.io/client-go/1.5/pkg/util/rand", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "8j9c2PqTKybtnymXbStNYRexRj8=", + "path": "k8s.io/client-go/1.5/pkg/util/runtime", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "aAz4e8hLGs0+ZAz1TdA5tY/9e1A=", + "path": "k8s.io/client-go/1.5/pkg/util/sets", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "P/fwh6QZ5tsjVyHTaASDWL3WaGs=", + "path": "k8s.io/client-go/1.5/pkg/util/uuid", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "P9Bq/1qbF4SvnN9HyCTRpbUz7sQ=", + "path": "k8s.io/client-go/1.5/pkg/util/validation", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "D0JIEjlP69cuPOZEdsSKeFgsnI8=", + "path": "k8s.io/client-go/1.5/pkg/util/validation/field", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "T7ba8t8i+BtgClMgL+aMZM94fcI=", + "path": "k8s.io/client-go/1.5/pkg/util/wait", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "6RCTv/KDiw7as4KeyrgU3XrUSQI=", + "path": "k8s.io/client-go/1.5/pkg/util/yaml", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "OwKlsSeKtz1FBVC9cQ5gWRL5pKc=", + "path": "k8s.io/client-go/1.5/pkg/version", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "Oil9WGw/dODbpBopn6LWQGS3DYg=", + "path": "k8s.io/client-go/1.5/pkg/watch", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "r5alnRCbLaPsbTeJjjTVn/bt6uw=", + "path": "k8s.io/client-go/1.5/pkg/watch/versioned", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "X1+ltyfHui/XCwDupXIf39+9gWQ=", + "path": "k8s.io/client-go/1.5/plugin/pkg/client/auth", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "KYy+js37AS0ZT08g5uBr1ZoMPmE=", + "path": "k8s.io/client-go/1.5/plugin/pkg/client/auth/gcp", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "wQ9G5++lbQpejqCzGHo037N3YcY=", + "path": "k8s.io/client-go/1.5/plugin/pkg/client/auth/oidc", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "ABe8YfZVEDoRpAUqp2BKP8o1VIA=", + "path": "k8s.io/client-go/1.5/rest", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "Gbe0Vs9hkI7X5hhbXUuWdRFffSI=", + "path": "k8s.io/client-go/1.5/tools/cache", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "K/oOznXABjqSS1c2Fs407c5F8KA=", + "path": "k8s.io/client-go/1.5/tools/clientcmd/api", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "c1PQ4WJRfpA9BYcFHW2+46hu5IE=", + "path": "k8s.io/client-go/1.5/tools/metrics", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + }, + { + "checksumSHA1": "e4W2q+6wvjejv3V0UCI1mewTTro=", + "path": "k8s.io/client-go/1.5/transport", + "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3", + "revisionTime": "2016-09-30T00:14:02Z" + } + ], + "rootPath": "github.com/prometheus/prometheus" +} diff --git a/libgo/go/cmd/go/internal/modconv/testdata/upspin.dep b/libgo/go/cmd/go/internal/modconv/testdata/upspin.dep new file mode 100644 index 0000000..be77bcb --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/upspin.dep @@ -0,0 +1,57 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "bazil.org/fuse" + packages = [".","fs","fuseutil"] + revision = "371fbbdaa8987b715bdd21d6adc4c9b20155f748" + +[[projects]] + branch = "master" + name = "github.com/NYTimes/gziphandler" + packages = ["."] + revision = "97ae7fbaf81620fe97840685304a78a306a39c64" + +[[projects]] + branch = "master" + name = "github.com/golang/protobuf" + packages = ["proto"] + revision = "1643683e1b54a9e88ad26d98f81400c8c9d9f4f9" + +[[projects]] + branch = "master" + name = "github.com/russross/blackfriday" + packages = ["."] + revision = "6d1ef893fcb01b4f50cb6e57ed7df3e2e627b6b2" + +[[projects]] + branch = "master" + name = "golang.org/x/crypto" + packages = ["acme","acme/autocert","hkdf"] + revision = "13931e22f9e72ea58bb73048bc752b48c6d4d4ac" + +[[projects]] + branch = "master" + name = "golang.org/x/net" + packages = ["context"] + revision = "4b14673ba32bee7f5ac0f990a48f033919fd418b" + +[[projects]] + branch = "master" + name = "golang.org/x/text" + packages = ["cases","internal","internal/gen","internal/tag","internal/triegen","internal/ucd","language","runes","secure/bidirule","secure/precis","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable","width"] + revision = "6eab0e8f74e86c598ec3b6fad4888e0c11482d48" + +[[projects]] + branch = "v2" + name = "gopkg.in/yaml.v2" + packages = ["."] + revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "2246e647ba1c78b0b9f948f9fb072fff1467284fb138709c063e99736f646b90" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/libgo/go/cmd/go/internal/modconv/testdata/upspin.out b/libgo/go/cmd/go/internal/modconv/testdata/upspin.out new file mode 100644 index 0000000..00597db --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/testdata/upspin.out @@ -0,0 +1,8 @@ +bazil.org/fuse 371fbbdaa8987b715bdd21d6adc4c9b20155f748 +github.com/NYTimes/gziphandler 97ae7fbaf81620fe97840685304a78a306a39c64 +github.com/golang/protobuf 1643683e1b54a9e88ad26d98f81400c8c9d9f4f9 +github.com/russross/blackfriday 6d1ef893fcb01b4f50cb6e57ed7df3e2e627b6b2 +golang.org/x/crypto 13931e22f9e72ea58bb73048bc752b48c6d4d4ac +golang.org/x/net 4b14673ba32bee7f5ac0f990a48f033919fd418b +golang.org/x/text 6eab0e8f74e86c598ec3b6fad4888e0c11482d48 +gopkg.in/yaml.v2 eb3733d160e74a9c7e442f435eb3bea458e1d19f diff --git a/libgo/go/cmd/go/internal/modconv/tsv.go b/libgo/go/cmd/go/internal/modconv/tsv.go new file mode 100644 index 0000000..feba181 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/tsv.go @@ -0,0 +1,24 @@ +// Copyright 2018 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 modconv + +import ( + "strings" + + "cmd/go/internal/modfile" + "cmd/go/internal/module" +) + +func ParseDependenciesTSV(file string, data []byte) (*modfile.File, error) { + mf := new(modfile.File) + for lineno, line := range strings.Split(string(data), "\n") { + lineno++ + f := strings.Split(line, "\t") + if len(f) >= 3 { + mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: f[0], Version: f[2]}}) + } + } + return mf, nil +} diff --git a/libgo/go/cmd/go/internal/modconv/vconf.go b/libgo/go/cmd/go/internal/modconv/vconf.go new file mode 100644 index 0000000..a9a8e62 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/vconf.go @@ -0,0 +1,27 @@ +// Copyright 2018 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 modconv + +import ( + "strings" + + "cmd/go/internal/modfile" + "cmd/go/internal/module" +) + +func ParseVendorConf(file string, data []byte) (*modfile.File, error) { + mf := new(modfile.File) + for lineno, line := range strings.Split(string(data), "\n") { + lineno++ + if i := strings.Index(line, "#"); i >= 0 { + line = line[:i] + } + f := strings.Fields(line) + if len(f) >= 2 { + mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: f[0], Version: f[1]}}) + } + } + return mf, nil +} diff --git a/libgo/go/cmd/go/internal/modconv/vjson.go b/libgo/go/cmd/go/internal/modconv/vjson.go new file mode 100644 index 0000000..eec86b7 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/vjson.go @@ -0,0 +1,29 @@ +// Copyright 2018 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 modconv + +import ( + "encoding/json" + + "cmd/go/internal/modfile" + "cmd/go/internal/module" +) + +func ParseVendorJSON(file string, data []byte) (*modfile.File, error) { + var cfg struct { + Package []struct { + Path string + Revision string + } + } + if err := json.Unmarshal(data, &cfg); err != nil { + return nil, err + } + mf := new(modfile.File) + for _, d := range cfg.Package { + mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: d.Path, Version: d.Revision}}) + } + return mf, nil +} diff --git a/libgo/go/cmd/go/internal/modconv/vmanifest.go b/libgo/go/cmd/go/internal/modconv/vmanifest.go new file mode 100644 index 0000000..c0ef2a9 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/vmanifest.go @@ -0,0 +1,29 @@ +// Copyright 2018 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 modconv + +import ( + "encoding/json" + + "cmd/go/internal/modfile" + "cmd/go/internal/module" +) + +func ParseVendorManifest(file string, data []byte) (*modfile.File, error) { + var cfg struct { + Dependencies []struct { + ImportPath string + Revision string + } + } + if err := json.Unmarshal(data, &cfg); err != nil { + return nil, err + } + mf := new(modfile.File) + for _, d := range cfg.Dependencies { + mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: d.ImportPath, Version: d.Revision}}) + } + return mf, nil +} diff --git a/libgo/go/cmd/go/internal/modconv/vyml.go b/libgo/go/cmd/go/internal/modconv/vyml.go new file mode 100644 index 0000000..0f017a3 --- /dev/null +++ b/libgo/go/cmd/go/internal/modconv/vyml.go @@ -0,0 +1,42 @@ +// Copyright 2018 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 modconv + +import ( + "strings" + + "cmd/go/internal/modfile" + "cmd/go/internal/module" +) + +func ParseVendorYML(file string, data []byte) (*modfile.File, error) { + mf := new(modfile.File) + vendors := false + path := "" + for lineno, line := range strings.Split(string(data), "\n") { + lineno++ + if line == "" { + continue + } + if strings.HasPrefix(line, "vendors:") { + vendors = true + } else if line[0] != '-' && line[0] != ' ' && line[0] != '\t' { + vendors = false + } + if !vendors { + continue + } + if strings.HasPrefix(line, "- path:") { + path = strings.TrimSpace(line[len("- path:"):]) + } + if strings.HasPrefix(line, " rev:") { + rev := strings.TrimSpace(line[len(" rev:"):]) + if path != "" && rev != "" { + mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: path, Version: rev}}) + } + } + } + return mf, nil +} diff --git a/libgo/go/cmd/go/internal/modfetch/cache.go b/libgo/go/cmd/go/internal/modfetch/cache.go new file mode 100644 index 0000000..1f9cc96 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/cache.go @@ -0,0 +1,522 @@ +// Copyright 2018 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 modfetch + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "cmd/go/internal/base" + "cmd/go/internal/modfetch/codehost" + "cmd/go/internal/module" + "cmd/go/internal/par" + "cmd/go/internal/semver" +) + +var QuietLookup bool // do not print about lookups + +var PkgMod string // $GOPATH/pkg/mod; set by package modload + +func cacheDir(path string) (string, error) { + if PkgMod == "" { + return "", fmt.Errorf("internal error: modfetch.PkgMod not set") + } + enc, err := module.EncodePath(path) + if err != nil { + return "", err + } + return filepath.Join(PkgMod, "cache/download", enc, "/@v"), nil +} + +func CachePath(m module.Version, suffix string) (string, error) { + dir, err := cacheDir(m.Path) + if err != nil { + return "", err + } + if !semver.IsValid(m.Version) { + return "", fmt.Errorf("non-semver module version %q", m.Version) + } + if module.CanonicalVersion(m.Version) != m.Version { + return "", fmt.Errorf("non-canonical module version %q", m.Version) + } + encVer, err := module.EncodeVersion(m.Version) + if err != nil { + return "", err + } + return filepath.Join(dir, encVer+"."+suffix), nil +} + +func DownloadDir(m module.Version) (string, error) { + if PkgMod == "" { + return "", fmt.Errorf("internal error: modfetch.PkgMod not set") + } + enc, err := module.EncodePath(m.Path) + if err != nil { + return "", err + } + if !semver.IsValid(m.Version) { + return "", fmt.Errorf("non-semver module version %q", m.Version) + } + if module.CanonicalVersion(m.Version) != m.Version { + return "", fmt.Errorf("non-canonical module version %q", m.Version) + } + encVer, err := module.EncodeVersion(m.Version) + if err != nil { + return "", err + } + return filepath.Join(PkgMod, enc+"@"+encVer), nil +} + +// A cachingRepo is a cache around an underlying Repo, +// avoiding redundant calls to ModulePath, Versions, Stat, Latest, and GoMod (but not Zip). +// It is also safe for simultaneous use by multiple goroutines +// (so that it can be returned from Lookup multiple times). +// It serializes calls to the underlying Repo. +type cachingRepo struct { + path string + cache par.Cache // cache for all operations + r Repo +} + +func newCachingRepo(r Repo) *cachingRepo { + return &cachingRepo{ + r: r, + path: r.ModulePath(), + } +} + +func (r *cachingRepo) ModulePath() string { + return r.path +} + +func (r *cachingRepo) Versions(prefix string) ([]string, error) { + type cached struct { + list []string + err error + } + c := r.cache.Do("versions:"+prefix, func() interface{} { + list, err := r.r.Versions(prefix) + return cached{list, err} + }).(cached) + + if c.err != nil { + return nil, c.err + } + return append([]string(nil), c.list...), nil +} + +type cachedInfo struct { + info *RevInfo + err error +} + +func (r *cachingRepo) Stat(rev string) (*RevInfo, error) { + c := r.cache.Do("stat:"+rev, func() interface{} { + file, info, err := readDiskStat(r.path, rev) + if err == nil { + return cachedInfo{info, nil} + } + + if !QuietLookup { + fmt.Fprintf(os.Stderr, "go: finding %s %s\n", r.path, rev) + } + info, err = r.r.Stat(rev) + if err == nil { + if err := writeDiskStat(file, info); err != nil { + fmt.Fprintf(os.Stderr, "go: writing stat cache: %v\n", err) + } + // If we resolved, say, 1234abcde to v0.0.0-20180604122334-1234abcdef78, + // then save the information under the proper version, for future use. + if info.Version != rev { + r.cache.Do("stat:"+info.Version, func() interface{} { + return cachedInfo{info, err} + }) + } + } + return cachedInfo{info, err} + }).(cachedInfo) + + if c.err != nil { + return nil, c.err + } + info := *c.info + return &info, nil +} + +func (r *cachingRepo) Latest() (*RevInfo, error) { + c := r.cache.Do("latest:", func() interface{} { + if !QuietLookup { + fmt.Fprintf(os.Stderr, "go: finding %s latest\n", r.path) + } + info, err := r.r.Latest() + + // Save info for likely future Stat call. + if err == nil { + r.cache.Do("stat:"+info.Version, func() interface{} { + return cachedInfo{info, err} + }) + if file, _, err := readDiskStat(r.path, info.Version); err != nil { + writeDiskStat(file, info) + } + } + + return cachedInfo{info, err} + }).(cachedInfo) + + if c.err != nil { + return nil, c.err + } + info := *c.info + return &info, nil +} + +func (r *cachingRepo) GoMod(rev string) ([]byte, error) { + type cached struct { + text []byte + err error + } + c := r.cache.Do("gomod:"+rev, func() interface{} { + file, text, err := readDiskGoMod(r.path, rev) + if err == nil { + // Note: readDiskGoMod already called checkGoMod. + return cached{text, nil} + } + + // Convert rev to canonical version + // so that we use the right identifier in the go.sum check. + info, err := r.Stat(rev) + if err != nil { + return cached{nil, err} + } + rev = info.Version + + text, err = r.r.GoMod(rev) + if err == nil { + checkGoMod(r.path, rev, text) + if err := writeDiskGoMod(file, text); err != nil { + fmt.Fprintf(os.Stderr, "go: writing go.mod cache: %v\n", err) + } + } + return cached{text, err} + }).(cached) + + if c.err != nil { + return nil, c.err + } + return append([]byte(nil), c.text...), nil +} + +func (r *cachingRepo) Zip(version, tmpdir string) (string, error) { + return r.r.Zip(version, tmpdir) +} + +// Stat is like Lookup(path).Stat(rev) but avoids the +// repository path resolution in Lookup if the result is +// already cached on local disk. +func Stat(path, rev string) (*RevInfo, error) { + _, info, err := readDiskStat(path, rev) + if err == nil { + return info, nil + } + repo, err := Lookup(path) + if err != nil { + return nil, err + } + return repo.Stat(rev) +} + +// InfoFile is like Stat but returns the name of the file containing +// the cached information. +func InfoFile(path, version string) (string, error) { + if !semver.IsValid(version) { + return "", fmt.Errorf("invalid version %q", version) + } + if _, err := Stat(path, version); err != nil { + return "", err + } + // Stat should have populated the disk cache for us. + file, _, err := readDiskStat(path, version) + if err != nil { + return "", err + } + return file, nil +} + +// GoMod is like Lookup(path).GoMod(rev) but avoids the +// repository path resolution in Lookup if the result is +// already cached on local disk. +func GoMod(path, rev string) ([]byte, error) { + // Convert commit hash to pseudo-version + // to increase cache hit rate. + if !semver.IsValid(rev) { + info, err := Stat(path, rev) + if err != nil { + return nil, err + } + rev = info.Version + } + _, data, err := readDiskGoMod(path, rev) + if err == nil { + return data, nil + } + repo, err := Lookup(path) + if err != nil { + return nil, err + } + return repo.GoMod(rev) +} + +// GoModFile is like GoMod but returns the name of the file containing +// the cached information. +func GoModFile(path, version string) (string, error) { + if !semver.IsValid(version) { + return "", fmt.Errorf("invalid version %q", version) + } + if _, err := GoMod(path, version); err != nil { + return "", err + } + // GoMod should have populated the disk cache for us. + file, _, err := readDiskGoMod(path, version) + if err != nil { + return "", err + } + return file, nil +} + +// GoModSum returns the go.sum entry for the module version's go.mod file. +// (That is, it returns the entry listed in go.sum as "path version/go.mod".) +func GoModSum(path, version string) (string, error) { + if !semver.IsValid(version) { + return "", fmt.Errorf("invalid version %q", version) + } + data, err := GoMod(path, version) + if err != nil { + return "", err + } + sum, err := goModSum(data) + if err != nil { + return "", err + } + return sum, nil +} + +var errNotCached = fmt.Errorf("not in cache") + +// readDiskStat reads a cached stat result from disk, +// returning the name of the cache file and the result. +// If the read fails, the caller can use +// writeDiskStat(file, info) to write a new cache entry. +func readDiskStat(path, rev string) (file string, info *RevInfo, err error) { + file, data, err := readDiskCache(path, rev, "info") + if err != nil { + if file, info, err := readDiskStatByHash(path, rev); err == nil { + return file, info, nil + } + return file, nil, err + } + info = new(RevInfo) + if err := json.Unmarshal(data, info); err != nil { + return file, nil, errNotCached + } + // The disk might have stale .info files that have Name and Short fields set. + // We want to canonicalize to .info files with those fields omitted. + // Remarshal and update the cache file if needed. + data2, err := json.Marshal(info) + if err == nil && !bytes.Equal(data2, data) { + writeDiskCache(file, data) + } + return file, info, nil +} + +// readDiskStatByHash is a fallback for readDiskStat for the case +// where rev is a commit hash instead of a proper semantic version. +// In that case, we look for a cached pseudo-version that matches +// the commit hash. If we find one, we use it. +// This matters most for converting legacy package management +// configs, when we are often looking up commits by full hash. +// Without this check we'd be doing network I/O to the remote repo +// just to find out about a commit we already know about +// (and have cached under its pseudo-version). +func readDiskStatByHash(path, rev string) (file string, info *RevInfo, err error) { + if PkgMod == "" { + // Do not download to current directory. + return "", nil, errNotCached + } + + if !codehost.AllHex(rev) || len(rev) < 12 { + return "", nil, errNotCached + } + rev = rev[:12] + cdir, err := cacheDir(path) + if err != nil { + return "", nil, errNotCached + } + dir, err := os.Open(cdir) + if err != nil { + return "", nil, errNotCached + } + names, err := dir.Readdirnames(-1) + dir.Close() + if err != nil { + return "", nil, errNotCached + } + suffix := "-" + rev + ".info" + for _, name := range names { + if strings.HasSuffix(name, suffix) && IsPseudoVersion(strings.TrimSuffix(name, ".info")) { + return readDiskStat(path, strings.TrimSuffix(name, ".info")) + } + } + return "", nil, errNotCached +} + +// oldVgoPrefix is the prefix in the old auto-generated cached go.mod files. +// We stopped trying to auto-generate the go.mod files. Now we use a trivial +// go.mod with only a module line, and we've dropped the version prefix +// entirely. If we see a version prefix, that means we're looking at an old copy +// and should ignore it. +var oldVgoPrefix = []byte("//vgo 0.0.") + +// readDiskGoMod reads a cached stat result from disk, +// returning the name of the cache file and the result. +// If the read fails, the caller can use +// writeDiskGoMod(file, data) to write a new cache entry. +func readDiskGoMod(path, rev string) (file string, data []byte, err error) { + file, data, err = readDiskCache(path, rev, "mod") + + // If the file has an old auto-conversion prefix, pretend it's not there. + if bytes.HasPrefix(data, oldVgoPrefix) { + err = errNotCached + data = nil + } + + if err == nil { + checkGoMod(path, rev, data) + } + + return file, data, err +} + +// readDiskCache is the generic "read from a cache file" implementation. +// It takes the revision and an identifying suffix for the kind of data being cached. +// It returns the name of the cache file and the content of the file. +// If the read fails, the caller can use +// writeDiskCache(file, data) to write a new cache entry. +func readDiskCache(path, rev, suffix string) (file string, data []byte, err error) { + file, err = CachePath(module.Version{Path: path, Version: rev}, suffix) + if err != nil { + return "", nil, errNotCached + } + data, err = ioutil.ReadFile(file) + if err != nil { + return file, nil, errNotCached + } + return file, data, nil +} + +// writeDiskStat writes a stat result cache entry. +// The file name must have been returned by a previous call to readDiskStat. +func writeDiskStat(file string, info *RevInfo) error { + if file == "" { + return nil + } + js, err := json.Marshal(info) + if err != nil { + return err + } + return writeDiskCache(file, js) +} + +// writeDiskGoMod writes a go.mod cache entry. +// The file name must have been returned by a previous call to readDiskGoMod. +func writeDiskGoMod(file string, text []byte) error { + return writeDiskCache(file, text) +} + +// writeDiskCache is the generic "write to a cache file" implementation. +// The file must have been returned by a previous call to readDiskCache. +func writeDiskCache(file string, data []byte) error { + if file == "" { + return nil + } + // Make sure directory for file exists. + if err := os.MkdirAll(filepath.Dir(file), 0777); err != nil { + return err + } + // Write data to temp file next to target file. + f, err := ioutil.TempFile(filepath.Dir(file), filepath.Base(file)+".tmp-") + if err != nil { + return err + } + defer os.Remove(f.Name()) + defer f.Close() + if _, err := f.Write(data); err != nil { + return err + } + if err := f.Close(); err != nil { + return err + } + // Rename temp file onto cache file, + // so that the cache file is always a complete file. + if err := os.Rename(f.Name(), file); err != nil { + return err + } + + if strings.HasSuffix(file, ".mod") { + rewriteVersionList(filepath.Dir(file)) + } + return nil +} + +// rewriteVersionList rewrites the version list in dir +// after a new *.mod file has been written. +func rewriteVersionList(dir string) { + if filepath.Base(dir) != "@v" { + base.Fatalf("go: internal error: misuse of rewriteVersionList") + } + + // TODO(rsc): We should do some kind of directory locking here, + // to avoid lost updates. + + infos, err := ioutil.ReadDir(dir) + if err != nil { + return + } + var list []string + for _, info := range infos { + // We look for *.mod files on the theory that if we can't supply + // the .mod file then there's no point in listing that version, + // since it's unusable. (We can have *.info without *.mod.) + // We don't require *.zip files on the theory that for code only + // involved in module graph construction, many *.zip files + // will never be requested. + name := info.Name() + if strings.HasSuffix(name, ".mod") { + v := strings.TrimSuffix(name, ".mod") + if v != "" && module.CanonicalVersion(v) == v { + list = append(list, v) + } + } + } + SortVersions(list) + + var buf bytes.Buffer + for _, v := range list { + buf.WriteString(v) + buf.WriteString("\n") + } + listFile := filepath.Join(dir, "list") + old, _ := ioutil.ReadFile(listFile) + if bytes.Equal(buf.Bytes(), old) { + return + } + // TODO: Use rename to install file, + // so that readers never see an incomplete file. + ioutil.WriteFile(listFile, buf.Bytes(), 0666) +} diff --git a/libgo/go/cmd/go/internal/modfetch/cache_test.go b/libgo/go/cmd/go/internal/modfetch/cache_test.go new file mode 100644 index 0000000..241c800 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/cache_test.go @@ -0,0 +1,25 @@ +// Copyright 2018 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 modfetch + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +func TestWriteDiskCache(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "go-writeCache-test-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + err = writeDiskCache(filepath.Join(tmpdir, "file"), []byte("data")) + if err != nil { + t.Fatal(err) + } +} diff --git a/libgo/go/cmd/go/internal/modfetch/codehost/codehost.go b/libgo/go/cmd/go/internal/modfetch/codehost/codehost.go new file mode 100644 index 0000000..4103ddc --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/codehost/codehost.go @@ -0,0 +1,266 @@ +// Copyright 2018 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 codehost defines the interface implemented by a code hosting source, +// along with support code for use by implementations. +package codehost + +import ( + "bytes" + "crypto/sha256" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "sync" + "time" + + "cmd/go/internal/cfg" + "cmd/go/internal/str" +) + +// Downloaded size limits. +const ( + MaxGoMod = 16 << 20 // maximum size of go.mod file + MaxLICENSE = 16 << 20 // maximum size of LICENSE file + MaxZipFile = 500 << 20 // maximum size of downloaded zip file +) + +// A Repo represents a code hosting source. +// Typical implementations include local version control repositories, +// remote version control servers, and code hosting sites. +// A Repo must be safe for simultaneous use by multiple goroutines. +type Repo interface { + // List lists all tags with the given prefix. + Tags(prefix string) (tags []string, err error) + + // Stat returns information about the revision rev. + // A revision can be any identifier known to the underlying service: + // commit hash, branch, tag, and so on. + Stat(rev string) (*RevInfo, error) + + // Latest returns the latest revision on the default branch, + // whatever that means in the underlying implementation. + Latest() (*RevInfo, error) + + // ReadFile reads the given file in the file tree corresponding to revision rev. + // It should refuse to read more than maxSize bytes. + // + // If the requested file does not exist it should return an error for which + // os.IsNotExist(err) returns true. + ReadFile(rev, file string, maxSize int64) (data []byte, err error) + + // ReadFileRevs reads a single file at multiple versions. + // It should refuse to read more than maxSize bytes. + // The result is a map from each requested rev strings + // to the associated FileRev. The map must have a non-nil + // entry for every requested rev (unless ReadFileRevs returned an error). + // A file simply being missing or even corrupted in revs[i] + // should be reported only in files[revs[i]].Err, not in the error result + // from ReadFileRevs. + // The overall call should return an error (and no map) only + // in the case of a problem with obtaining the data, such as + // a network failure. + // Implementations may assume that revs only contain tags, + // not direct commit hashes. + ReadFileRevs(revs []string, file string, maxSize int64) (files map[string]*FileRev, err error) + + // ReadZip downloads a zip file for the subdir subdirectory + // of the given revision to a new file in a given temporary directory. + // It should refuse to read more than maxSize bytes. + // It returns a ReadCloser for a streamed copy of the zip file, + // along with the actual subdirectory (possibly shorter than subdir) + // contained in the zip file. All files in the zip file are expected to be + // nested in a single top-level directory, whose name is not specified. + ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) + + // RecentTag returns the most recent tag at or before the given rev + // with the given prefix. It should make a best-effort attempt to + // find a tag that is a valid semantic version (following the prefix), + // or else the result is not useful to the caller, but it need not + // incur great expense in doing so. For example, the git implementation + // of RecentTag limits git's search to tags matching the glob expression + // "v[0-9]*.[0-9]*.[0-9]*" (after the prefix). + RecentTag(rev, prefix string) (tag string, err error) +} + +// A Rev describes a single revision in a source code repository. +type RevInfo struct { + Name string // complete ID in underlying repository + Short string // shortened ID, for use in pseudo-version + Version string // version used in lookup + Time time.Time // commit time + Tags []string // known tags for commit +} + +// A FileRev describes the result of reading a file at a given revision. +type FileRev struct { + Rev string // requested revision + Data []byte // file data + Err error // error if any; os.IsNotExist(Err)==true if rev exists but file does not exist in that rev +} + +// AllHex reports whether the revision rev is entirely lower-case hexadecimal digits. +func AllHex(rev string) bool { + for i := 0; i < len(rev); i++ { + c := rev[i] + if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' { + continue + } + return false + } + return true +} + +// ShortenSHA1 shortens a SHA1 hash (40 hex digits) to the canonical length +// used in pseudo-versions (12 hex digits). +func ShortenSHA1(rev string) string { + if AllHex(rev) && len(rev) == 40 { + return rev[:12] + } + return rev +} + +// WorkRoot is the root of the cached work directory. +// It is set by cmd/go/internal/modload.InitMod. +var WorkRoot string + +// WorkDir returns the name of the cached work directory to use for the +// given repository type and name. +func WorkDir(typ, name string) (string, error) { + if WorkRoot == "" { + return "", fmt.Errorf("codehost.WorkRoot not set") + } + + // We name the work directory for the SHA256 hash of the type and name. + // We intentionally avoid the actual name both because of possible + // conflicts with valid file system paths and because we want to ensure + // that one checkout is never nested inside another. That nesting has + // led to security problems in the past. + if strings.Contains(typ, ":") { + return "", fmt.Errorf("codehost.WorkDir: type cannot contain colon") + } + key := typ + ":" + name + dir := filepath.Join(WorkRoot, fmt.Sprintf("%x", sha256.Sum256([]byte(key)))) + data, err := ioutil.ReadFile(dir + ".info") + info, err2 := os.Stat(dir) + if err == nil && err2 == nil && info.IsDir() { + // Info file and directory both already exist: reuse. + have := strings.TrimSuffix(string(data), "\n") + if have != key { + return "", fmt.Errorf("%s exists with wrong content (have %q want %q)", dir+".info", have, key) + } + if cfg.BuildX { + fmt.Fprintf(os.Stderr, "# %s for %s %s\n", dir, typ, name) + } + return dir, nil + } + + // Info file or directory missing. Start from scratch. + if cfg.BuildX { + fmt.Fprintf(os.Stderr, "mkdir -p %s # %s %s\n", dir, typ, name) + } + os.RemoveAll(dir) + if err := os.MkdirAll(dir, 0777); err != nil { + return "", err + } + if err := ioutil.WriteFile(dir+".info", []byte(key), 0666); err != nil { + os.RemoveAll(dir) + return "", err + } + return dir, nil +} + +type RunError struct { + Cmd string + Err error + Stderr []byte +} + +func (e *RunError) Error() string { + text := e.Cmd + ": " + e.Err.Error() + stderr := bytes.TrimRight(e.Stderr, "\n") + if len(stderr) > 0 { + text += ":\n\t" + strings.Replace(string(stderr), "\n", "\n\t", -1) + } + return text +} + +var dirLock sync.Map + +// Run runs the command line in the given directory +// (an empty dir means the current directory). +// It returns the standard output and, for a non-zero exit, +// a *RunError indicating the command, exit status, and standard error. +// Standard error is unavailable for commands that exit successfully. +func Run(dir string, cmdline ...interface{}) ([]byte, error) { + return RunWithStdin(dir, nil, cmdline...) +} + +// bashQuoter escapes characters that have special meaning in double-quoted strings in the bash shell. +// See https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html. +var bashQuoter = strings.NewReplacer(`"`, `\"`, `$`, `\$`, "`", "\\`", `\`, `\\`) + +func RunWithStdin(dir string, stdin io.Reader, cmdline ...interface{}) ([]byte, error) { + if dir != "" { + muIface, ok := dirLock.Load(dir) + if !ok { + muIface, _ = dirLock.LoadOrStore(dir, new(sync.Mutex)) + } + mu := muIface.(*sync.Mutex) + mu.Lock() + defer mu.Unlock() + } + + cmd := str.StringList(cmdline...) + if cfg.BuildX { + text := new(strings.Builder) + if dir != "" { + text.WriteString("cd ") + text.WriteString(dir) + text.WriteString("; ") + } + for i, arg := range cmd { + if i > 0 { + text.WriteByte(' ') + } + switch { + case strings.ContainsAny(arg, "'"): + // Quote args that could be mistaken for quoted args. + text.WriteByte('"') + text.WriteString(bashQuoter.Replace(arg)) + text.WriteByte('"') + case strings.ContainsAny(arg, "$`\\*?[\"\t\n\v\f\r \u0085\u00a0"): + // Quote args that contain special characters, glob patterns, or spaces. + text.WriteByte('\'') + text.WriteString(arg) + text.WriteByte('\'') + default: + text.WriteString(arg) + } + } + fmt.Fprintf(os.Stderr, "%s\n", text) + start := time.Now() + defer func() { + fmt.Fprintf(os.Stderr, "%.3fs # %s\n", time.Since(start).Seconds(), text) + }() + } + // TODO: Impose limits on command output size. + // TODO: Set environment to get English error messages. + var stderr bytes.Buffer + var stdout bytes.Buffer + c := exec.Command(cmd[0], cmd[1:]...) + c.Dir = dir + c.Stdin = stdin + c.Stderr = &stderr + c.Stdout = &stdout + err := c.Run() + if err != nil { + err = &RunError{Cmd: strings.Join(cmd, " ") + " in " + dir, Stderr: stderr.Bytes(), Err: err} + } + return stdout.Bytes(), err +} diff --git a/libgo/go/cmd/go/internal/modfetch/codehost/git.go b/libgo/go/cmd/go/internal/modfetch/codehost/git.go new file mode 100644 index 0000000..87940a8 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/codehost/git.go @@ -0,0 +1,711 @@ +// Copyright 2018 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 codehost + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "sync" + "time" + + "cmd/go/internal/par" +) + +// GitRepo returns the code repository at the given Git remote reference. +func GitRepo(remote string) (Repo, error) { + return newGitRepoCached(remote, false) +} + +// LocalGitRepo is like Repo but accepts both Git remote references +// and paths to repositories on the local file system. +func LocalGitRepo(remote string) (Repo, error) { + return newGitRepoCached(remote, true) +} + +const gitWorkDirType = "git2" + +var gitRepoCache par.Cache + +func newGitRepoCached(remote string, localOK bool) (Repo, error) { + type key struct { + remote string + localOK bool + } + type cached struct { + repo Repo + err error + } + + c := gitRepoCache.Do(key{remote, localOK}, func() interface{} { + repo, err := newGitRepo(remote, localOK) + return cached{repo, err} + }).(cached) + + return c.repo, c.err +} + +func newGitRepo(remote string, localOK bool) (Repo, error) { + r := &gitRepo{remote: remote} + if strings.Contains(remote, "://") { + // This is a remote path. + dir, err := WorkDir(gitWorkDirType, r.remote) + if err != nil { + return nil, err + } + r.dir = dir + if _, err := os.Stat(filepath.Join(dir, "objects")); err != nil { + if _, err := Run(dir, "git", "init", "--bare"); err != nil { + os.RemoveAll(dir) + return nil, err + } + // We could just say git fetch https://whatever later, + // but this lets us say git fetch origin instead, which + // is a little nicer. More importantly, using a named remote + // avoids a problem with Git LFS. See golang.org/issue/25605. + if _, err := Run(dir, "git", "remote", "add", "origin", r.remote); err != nil { + os.RemoveAll(dir) + return nil, err + } + r.remote = "origin" + } + } else { + // Local path. + // Disallow colon (not in ://) because sometimes + // that's rcp-style host:path syntax and sometimes it's not (c:\work). + // The go command has always insisted on URL syntax for ssh. + if strings.Contains(remote, ":") { + return nil, fmt.Errorf("git remote cannot use host:path syntax") + } + if !localOK { + return nil, fmt.Errorf("git remote must not be local directory") + } + r.local = true + info, err := os.Stat(remote) + if err != nil { + return nil, err + } + if !info.IsDir() { + return nil, fmt.Errorf("%s exists but is not a directory", remote) + } + r.dir = remote + } + return r, nil +} + +type gitRepo struct { + remote string + local bool + dir string + + mu sync.Mutex // protects fetchLevel, some git repo state + fetchLevel int + + statCache par.Cache + + refsOnce sync.Once + refs map[string]string + refsErr error + + localTagsOnce sync.Once + localTags map[string]bool +} + +const ( + // How much have we fetched into the git repo (in this process)? + fetchNone = iota // nothing yet + fetchSome // shallow fetches of individual hashes + fetchAll // "fetch -t origin": get all remote branches and tags +) + +// loadLocalTags loads tag references from the local git cache +// into the map r.localTags. +// Should only be called as r.localTagsOnce.Do(r.loadLocalTags). +func (r *gitRepo) loadLocalTags() { + // The git protocol sends all known refs and ls-remote filters them on the client side, + // so we might as well record both heads and tags in one shot. + // Most of the time we only care about tags but sometimes we care about heads too. + out, err := Run(r.dir, "git", "tag", "-l") + if err != nil { + return + } + + r.localTags = make(map[string]bool) + for _, line := range strings.Split(string(out), "\n") { + if line != "" { + r.localTags[line] = true + } + } +} + +// loadRefs loads heads and tags references from the remote into the map r.refs. +// Should only be called as r.refsOnce.Do(r.loadRefs). +func (r *gitRepo) loadRefs() { + // The git protocol sends all known refs and ls-remote filters them on the client side, + // so we might as well record both heads and tags in one shot. + // Most of the time we only care about tags but sometimes we care about heads too. + out, err := Run(r.dir, "git", "ls-remote", "-q", r.remote) + if err != nil { + r.refsErr = err + return + } + + r.refs = make(map[string]string) + for _, line := range strings.Split(string(out), "\n") { + f := strings.Fields(line) + if len(f) != 2 { + continue + } + if f[1] == "HEAD" || strings.HasPrefix(f[1], "refs/heads/") || strings.HasPrefix(f[1], "refs/tags/") { + r.refs[f[1]] = f[0] + } + } + for ref, hash := range r.refs { + if strings.HasSuffix(ref, "^{}") { // record unwrapped annotated tag as value of tag + r.refs[strings.TrimSuffix(ref, "^{}")] = hash + delete(r.refs, ref) + } + } +} + +func (r *gitRepo) Tags(prefix string) ([]string, error) { + r.refsOnce.Do(r.loadRefs) + if r.refsErr != nil { + return nil, r.refsErr + } + + tags := []string{} + for ref := range r.refs { + if !strings.HasPrefix(ref, "refs/tags/") { + continue + } + tag := ref[len("refs/tags/"):] + if !strings.HasPrefix(tag, prefix) { + continue + } + tags = append(tags, tag) + } + sort.Strings(tags) + return tags, nil +} + +func (r *gitRepo) Latest() (*RevInfo, error) { + r.refsOnce.Do(r.loadRefs) + if r.refsErr != nil { + return nil, r.refsErr + } + if r.refs["HEAD"] == "" { + return nil, fmt.Errorf("no commits") + } + return r.Stat(r.refs["HEAD"]) +} + +// findRef finds some ref name for the given hash, +// for use when the server requires giving a ref instead of a hash. +// There may be multiple ref names for a given hash, +// in which case this returns some name - it doesn't matter which. +func (r *gitRepo) findRef(hash string) (ref string, ok bool) { + r.refsOnce.Do(r.loadRefs) + for ref, h := range r.refs { + if h == hash { + return ref, true + } + } + return "", false +} + +func unshallow(gitDir string) []string { + if _, err := os.Stat(filepath.Join(gitDir, "shallow")); err == nil { + return []string{"--unshallow"} + } + return []string{} +} + +// minHashDigits is the minimum number of digits to require +// before accepting a hex digit sequence as potentially identifying +// a specific commit in a git repo. (Of course, users can always +// specify more digits, and many will paste in all 40 digits, +// but many of git's commands default to printing short hashes +// as 7 digits.) +const minHashDigits = 7 + +// stat stats the given rev in the local repository, +// or else it fetches more info from the remote repository and tries again. +func (r *gitRepo) stat(rev string) (*RevInfo, error) { + if r.local { + return r.statLocal(rev, rev) + } + + // Fast path: maybe rev is a hash we already have locally. + didStatLocal := false + if len(rev) >= minHashDigits && len(rev) <= 40 && AllHex(rev) { + if info, err := r.statLocal(rev, rev); err == nil { + return info, nil + } + didStatLocal = true + } + + // Maybe rev is a tag we already have locally. + // (Note that we're excluding branches, which can be stale.) + r.localTagsOnce.Do(r.loadLocalTags) + if r.localTags[rev] { + return r.statLocal(rev, "refs/tags/"+rev) + } + + // Maybe rev is the name of a tag or branch on the remote server. + // Or maybe it's the prefix of a hash of a named ref. + // Try to resolve to both a ref (git name) and full (40-hex-digit) commit hash. + r.refsOnce.Do(r.loadRefs) + var ref, hash string + if r.refs["refs/tags/"+rev] != "" { + ref = "refs/tags/" + rev + hash = r.refs[ref] + // Keep rev as is: tags are assumed not to change meaning. + } else if r.refs["refs/heads/"+rev] != "" { + ref = "refs/heads/" + rev + hash = r.refs[ref] + rev = hash // Replace rev, because meaning of refs/heads/foo can change. + } else if rev == "HEAD" && r.refs["HEAD"] != "" { + ref = "HEAD" + hash = r.refs[ref] + rev = hash // Replace rev, because meaning of HEAD can change. + } else if len(rev) >= minHashDigits && len(rev) <= 40 && AllHex(rev) { + // At the least, we have a hash prefix we can look up after the fetch below. + // Maybe we can map it to a full hash using the known refs. + prefix := rev + // Check whether rev is prefix of known ref hash. + for k, h := range r.refs { + if strings.HasPrefix(h, prefix) { + if hash != "" && hash != h { + // Hash is an ambiguous hash prefix. + // More information will not change that. + return nil, fmt.Errorf("ambiguous revision %s", rev) + } + if ref == "" || ref > k { // Break ties deterministically when multiple refs point at same hash. + ref = k + } + rev = h + hash = h + } + } + if hash == "" && len(rev) == 40 { // Didn't find a ref, but rev is a full hash. + hash = rev + } + } else { + return nil, fmt.Errorf("unknown revision %s", rev) + } + + // Protect r.fetchLevel and the "fetch more and more" sequence. + // TODO(rsc): Add LockDir and use it for protecting that + // sequence, so that multiple processes don't collide in their + // git commands. + r.mu.Lock() + defer r.mu.Unlock() + + // Perhaps r.localTags did not have the ref when we loaded local tags, + // but we've since done fetches that pulled down the hash we need + // (or already have the hash we need, just without its tag). + // Either way, try a local stat before falling back to network I/O. + if !didStatLocal { + if info, err := r.statLocal(rev, hash); err == nil { + if strings.HasPrefix(ref, "refs/tags/") { + // Make sure tag exists, so it will be in localTags next time the go command is run. + Run(r.dir, "git", "tag", strings.TrimPrefix(ref, "refs/tags/"), hash) + } + return info, nil + } + } + + // If we know a specific commit we need, fetch it. + if r.fetchLevel <= fetchSome && hash != "" && !r.local { + r.fetchLevel = fetchSome + var refspec string + if ref != "" && ref != "HEAD" { + // If we do know the ref name, save the mapping locally + // so that (if it is a tag) it can show up in localTags + // on a future call. Also, some servers refuse to allow + // full hashes in ref specs, so prefer a ref name if known. + refspec = ref + ":" + ref + } else { + // Fetch the hash but give it a local name (refs/dummy), + // because that triggers the fetch behavior of creating any + // other known remote tags for the hash. We never use + // refs/dummy (it's not refs/tags/dummy) and it will be + // overwritten in the next command, and that's fine. + ref = hash + refspec = hash + ":refs/dummy" + } + _, err := Run(r.dir, "git", "fetch", "-f", "--depth=1", r.remote, refspec) + if err == nil { + return r.statLocal(rev, ref) + } + // Don't try to be smart about parsing the error. + // It's too complex and varies too much by git version. + // No matter what went wrong, fall back to a complete fetch. + } + + // Last resort. + // Fetch all heads and tags and hope the hash we want is in the history. + if r.fetchLevel < fetchAll { + // TODO(bcmills): should we wait to upgrade fetchLevel until after we check + // err? If there is a temporary server error, we want subsequent fetches to + // try again instead of proceeding with an incomplete repo. + r.fetchLevel = fetchAll + if err := r.fetchUnshallow("refs/heads/*:refs/heads/*", "refs/tags/*:refs/tags/*"); err != nil { + return nil, err + } + } + + return r.statLocal(rev, rev) +} + +func (r *gitRepo) fetchUnshallow(refSpecs ...string) error { + // To work around a protocol version 2 bug that breaks --unshallow, + // add -c protocol.version=0. + // TODO(rsc): The bug is believed to be server-side, meaning only + // on Google's Git servers. Once the servers are fixed, drop the + // protocol.version=0. See Google-internal bug b/110495752. + var protoFlag []string + unshallowFlag := unshallow(r.dir) + if len(unshallowFlag) > 0 { + protoFlag = []string{"-c", "protocol.version=0"} + } + _, err := Run(r.dir, "git", protoFlag, "fetch", unshallowFlag, "-f", r.remote, refSpecs) + return err +} + +// statLocal returns a RevInfo describing rev in the local git repository. +// It uses version as info.Version. +func (r *gitRepo) statLocal(version, rev string) (*RevInfo, error) { + out, err := Run(r.dir, "git", "-c", "log.showsignature=false", "log", "-n1", "--format=format:%H %ct %D", rev) + if err != nil { + return nil, fmt.Errorf("unknown revision %s", rev) + } + f := strings.Fields(string(out)) + if len(f) < 2 { + return nil, fmt.Errorf("unexpected response from git log: %q", out) + } + hash := f[0] + if strings.HasPrefix(hash, version) { + version = hash // extend to full hash + } + t, err := strconv.ParseInt(f[1], 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid time from git log: %q", out) + } + + info := &RevInfo{ + Name: hash, + Short: ShortenSHA1(hash), + Time: time.Unix(t, 0).UTC(), + Version: hash, + } + + // Add tags. Output looks like: + // ede458df7cd0fdca520df19a33158086a8a68e81 1523994202 HEAD -> master, tag: v1.2.4-annotated, tag: v1.2.3, origin/master, origin/HEAD + for i := 2; i < len(f); i++ { + if f[i] == "tag:" { + i++ + if i < len(f) { + info.Tags = append(info.Tags, strings.TrimSuffix(f[i], ",")) + } + } + } + sort.Strings(info.Tags) + + // Used hash as info.Version above. + // Use caller's suggested version if it appears in the tag list + // (filters out branch names, HEAD). + for _, tag := range info.Tags { + if version == tag { + info.Version = version + } + } + + return info, nil +} + +func (r *gitRepo) Stat(rev string) (*RevInfo, error) { + if rev == "latest" { + return r.Latest() + } + type cached struct { + info *RevInfo + err error + } + c := r.statCache.Do(rev, func() interface{} { + info, err := r.stat(rev) + return cached{info, err} + }).(cached) + return c.info, c.err +} + +func (r *gitRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) { + // TODO: Could use git cat-file --batch. + info, err := r.Stat(rev) // download rev into local git repo + if err != nil { + return nil, err + } + out, err := Run(r.dir, "git", "cat-file", "blob", info.Name+":"+file) + if err != nil { + return nil, os.ErrNotExist + } + return out, nil +} + +func (r *gitRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[string]*FileRev, error) { + // Create space to hold results. + files := make(map[string]*FileRev) + for _, rev := range revs { + f := &FileRev{Rev: rev} + files[rev] = f + } + + // Collect locally-known revs. + need, err := r.readFileRevs(revs, file, files) + if err != nil { + return nil, err + } + if len(need) == 0 { + return files, nil + } + + // Build list of known remote refs that might help. + var redo []string + r.refsOnce.Do(r.loadRefs) + if r.refsErr != nil { + return nil, r.refsErr + } + for _, tag := range need { + if r.refs["refs/tags/"+tag] != "" { + redo = append(redo, tag) + } + } + if len(redo) == 0 { + return files, nil + } + + // Protect r.fetchLevel and the "fetch more and more" sequence. + // See stat method above. + r.mu.Lock() + defer r.mu.Unlock() + + var refs []string + var protoFlag []string + var unshallowFlag []string + for _, tag := range redo { + refs = append(refs, "refs/tags/"+tag+":refs/tags/"+tag) + } + if len(refs) > 1 { + unshallowFlag = unshallow(r.dir) + if len(unshallowFlag) > 0 { + // To work around a protocol version 2 bug that breaks --unshallow, + // add -c protocol.version=0. + // TODO(rsc): The bug is believed to be server-side, meaning only + // on Google's Git servers. Once the servers are fixed, drop the + // protocol.version=0. See Google-internal bug b/110495752. + protoFlag = []string{"-c", "protocol.version=0"} + } + } + if _, err := Run(r.dir, "git", protoFlag, "fetch", unshallowFlag, "-f", r.remote, refs); err != nil { + return nil, err + } + + // TODO(bcmills): after the 1.11 freeze, replace the block above with: + // if r.fetchLevel <= fetchSome { + // r.fetchLevel = fetchSome + // var refs []string + // for _, tag := range redo { + // refs = append(refs, "refs/tags/"+tag+":refs/tags/"+tag) + // } + // if _, err := Run(r.dir, "git", "fetch", "--update-shallow", "-f", r.remote, refs); err != nil { + // return nil, err + // } + // } + + if _, err := r.readFileRevs(redo, file, files); err != nil { + return nil, err + } + + return files, nil +} + +func (r *gitRepo) readFileRevs(tags []string, file string, fileMap map[string]*FileRev) (missing []string, err error) { + var stdin bytes.Buffer + for _, tag := range tags { + fmt.Fprintf(&stdin, "refs/tags/%s\n", tag) + fmt.Fprintf(&stdin, "refs/tags/%s:%s\n", tag, file) + } + + data, err := RunWithStdin(r.dir, &stdin, "git", "cat-file", "--batch") + if err != nil { + return nil, err + } + + next := func() (typ string, body []byte, ok bool) { + var line string + i := bytes.IndexByte(data, '\n') + if i < 0 { + return "", nil, false + } + line, data = string(bytes.TrimSpace(data[:i])), data[i+1:] + if strings.HasSuffix(line, " missing") { + return "missing", nil, true + } + f := strings.Fields(line) + if len(f) != 3 { + return "", nil, false + } + n, err := strconv.Atoi(f[2]) + if err != nil || n > len(data) { + return "", nil, false + } + body, data = data[:n], data[n:] + if len(data) > 0 && data[0] == '\r' { + data = data[1:] + } + if len(data) > 0 && data[0] == '\n' { + data = data[1:] + } + return f[1], body, true + } + + badGit := func() ([]string, error) { + return nil, fmt.Errorf("malformed output from git cat-file --batch") + } + + for _, tag := range tags { + commitType, _, ok := next() + if !ok { + return badGit() + } + fileType, fileData, ok := next() + if !ok { + return badGit() + } + f := fileMap[tag] + f.Data = nil + f.Err = nil + switch commitType { + default: + f.Err = fmt.Errorf("unexpected non-commit type %q for rev %s", commitType, tag) + + case "missing": + // Note: f.Err must not satisfy os.IsNotExist. That's reserved for the file not existing in a valid commit. + f.Err = fmt.Errorf("no such rev %s", tag) + missing = append(missing, tag) + + case "tag", "commit": + switch fileType { + default: + f.Err = &os.PathError{Path: tag + ":" + file, Op: "read", Err: fmt.Errorf("unexpected non-blob type %q", fileType)} + case "missing": + f.Err = &os.PathError{Path: tag + ":" + file, Op: "read", Err: os.ErrNotExist} + case "blob": + f.Data = fileData + } + } + } + if len(bytes.TrimSpace(data)) != 0 { + return badGit() + } + + return missing, nil +} + +func (r *gitRepo) RecentTag(rev, prefix string) (tag string, err error) { + info, err := r.Stat(rev) + if err != nil { + return "", err + } + rev = info.Name // expand hash prefixes + + // describe sets tag and err using 'git describe' and reports whether the + // result is definitive. + describe := func() (definitive bool) { + var out []byte + out, err = Run(r.dir, "git", "describe", "--first-parent", "--always", "--abbrev=0", "--match", prefix+"v[0-9]*.[0-9]*.[0-9]*", "--tags", rev) + if err != nil { + return true // Because we use "--always", describe should never fail. + } + + tag = string(bytes.TrimSpace(out)) + return tag != "" && !AllHex(tag) + } + + if describe() { + return tag, err + } + + // Git didn't find a version tag preceding the requested rev. + // See whether any plausible tag exists. + tags, err := r.Tags(prefix + "v") + if err != nil { + return "", err + } + if len(tags) == 0 { + return "", nil + } + + // There are plausible tags, but we don't know if rev is a descendent of any of them. + // Fetch the history to find out. + + r.mu.Lock() + defer r.mu.Unlock() + + if r.fetchLevel < fetchAll { + // Fetch all heads and tags and see if that gives us enough history. + if err := r.fetchUnshallow("refs/heads/*:refs/heads/*", "refs/tags/*:refs/tags/*"); err != nil { + return "", err + } + r.fetchLevel = fetchAll + } + + // If we've reached this point, we have all of the commits that are reachable + // from all heads and tags. + // + // The only refs we should be missing are those that are no longer reachable + // (or never were reachable) from any branch or tag, including the master + // branch, and we don't want to resolve them anyway (they're probably + // unreachable for a reason). + // + // Try one last time in case some other goroutine fetched rev while we were + // waiting on r.mu. + describe() + return tag, err +} + +func (r *gitRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) { + // TODO: Use maxSize or drop it. + args := []string{} + if subdir != "" { + args = append(args, "--", subdir) + } + info, err := r.Stat(rev) // download rev into local git repo + if err != nil { + return nil, "", err + } + + // Incredibly, git produces different archives depending on whether + // it is running on a Windows system or not, in an attempt to normalize + // text file line endings. Setting -c core.autocrlf=input means only + // translate files on the way into the repo, not on the way out (archive). + // The -c core.eol=lf should be unnecessary but set it anyway. + archive, err := Run(r.dir, "git", "-c", "core.autocrlf=input", "-c", "core.eol=lf", "archive", "--format=zip", "--prefix=prefix/", info.Name, args) + if err != nil { + if bytes.Contains(err.(*RunError).Stderr, []byte("did not match any files")) { + return nil, "", os.ErrNotExist + } + return nil, "", err + } + + return ioutil.NopCloser(bytes.NewReader(archive)), "", nil +} diff --git a/libgo/go/cmd/go/internal/modfetch/codehost/git_test.go b/libgo/go/cmd/go/internal/modfetch/codehost/git_test.go new file mode 100644 index 0000000..da9e705 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/codehost/git_test.go @@ -0,0 +1,635 @@ +// Copyright 2018 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 codehost + +import ( + "archive/zip" + "bytes" + "flag" + "fmt" + "internal/testenv" + "io/ioutil" + "log" + "os" + "os/exec" + "path" + "path/filepath" + "reflect" + "strings" + "testing" + "time" +) + +func TestMain(m *testing.M) { + // needed for initializing the test environment variables as testing.Short + // and HasExternalNetwork + flag.Parse() + os.Exit(testMain(m)) +} + +const ( + gitrepo1 = "https://vcs-test.golang.org/git/gitrepo1" + hgrepo1 = "https://vcs-test.golang.org/hg/hgrepo1" +) + +var altRepos = []string{ + "localGitRepo", + hgrepo1, +} + +// TODO: Convert gitrepo1 to svn, bzr, fossil and add tests. +// For now, at least the hgrepo1 tests check the general vcs.go logic. + +// localGitRepo is like gitrepo1 but allows archive access. +var localGitRepo string + +func testMain(m *testing.M) int { + if _, err := exec.LookPath("git"); err != nil { + fmt.Fprintln(os.Stderr, "skipping because git binary not found") + fmt.Println("PASS") + return 0 + } + + dir, err := ioutil.TempDir("", "gitrepo-test-") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(dir) + WorkRoot = dir + + if testenv.HasExternalNetwork() && testenv.HasExec() { + // Clone gitrepo1 into a local directory. + // If we use a file:// URL to access the local directory, + // then git starts up all the usual protocol machinery, + // which will let us test remote git archive invocations. + localGitRepo = filepath.Join(dir, "gitrepo2") + if _, err := Run("", "git", "clone", "--mirror", gitrepo1, localGitRepo); err != nil { + log.Fatal(err) + } + if _, err := Run(localGitRepo, "git", "config", "daemon.uploadarch", "true"); err != nil { + log.Fatal(err) + } + } + + return m.Run() +} + +func testRepo(remote string) (Repo, error) { + if remote == "localGitRepo" { + return LocalGitRepo(filepath.ToSlash(localGitRepo)) + } + kind := "git" + for _, k := range []string{"hg"} { + if strings.Contains(remote, "/"+k+"/") { + kind = k + } + } + return NewRepo(kind, remote) +} + +var tagsTests = []struct { + repo string + prefix string + tags []string +}{ + {gitrepo1, "xxx", []string{}}, + {gitrepo1, "", []string{"v1.2.3", "v1.2.4-annotated", "v2.0.1", "v2.0.2", "v2.3"}}, + {gitrepo1, "v", []string{"v1.2.3", "v1.2.4-annotated", "v2.0.1", "v2.0.2", "v2.3"}}, + {gitrepo1, "v1", []string{"v1.2.3", "v1.2.4-annotated"}}, + {gitrepo1, "2", []string{}}, +} + +func TestTags(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + testenv.MustHaveExec(t) + + for _, tt := range tagsTests { + f := func(t *testing.T) { + r, err := testRepo(tt.repo) + if err != nil { + t.Fatal(err) + } + tags, err := r.Tags(tt.prefix) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(tags, tt.tags) { + t.Errorf("Tags: incorrect tags\nhave %v\nwant %v", tags, tt.tags) + } + } + t.Run(path.Base(tt.repo)+"/"+tt.prefix, f) + if tt.repo == gitrepo1 { + for _, tt.repo = range altRepos { + t.Run(path.Base(tt.repo)+"/"+tt.prefix, f) + } + } + } +} + +var latestTests = []struct { + repo string + info *RevInfo +}{ + { + gitrepo1, + &RevInfo{ + Name: "ede458df7cd0fdca520df19a33158086a8a68e81", + Short: "ede458df7cd0", + Version: "ede458df7cd0fdca520df19a33158086a8a68e81", + Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC), + Tags: []string{"v1.2.3", "v1.2.4-annotated"}, + }, + }, + { + hgrepo1, + &RevInfo{ + Name: "18518c07eb8ed5c80221e997e518cccaa8c0c287", + Short: "18518c07eb8e", + Version: "18518c07eb8ed5c80221e997e518cccaa8c0c287", + Time: time.Date(2018, 6, 27, 16, 16, 30, 0, time.UTC), + }, + }, +} + +func TestLatest(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + testenv.MustHaveExec(t) + + for _, tt := range latestTests { + f := func(t *testing.T) { + r, err := testRepo(tt.repo) + if err != nil { + t.Fatal(err) + } + info, err := r.Latest() + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(info, tt.info) { + t.Errorf("Latest: incorrect info\nhave %+v\nwant %+v", *info, *tt.info) + } + } + t.Run(path.Base(tt.repo), f) + if tt.repo == gitrepo1 { + tt.repo = "localGitRepo" + t.Run(path.Base(tt.repo), f) + } + } +} + +var readFileTests = []struct { + repo string + rev string + file string + err string + data string +}{ + { + repo: gitrepo1, + rev: "latest", + file: "README", + data: "", + }, + { + repo: gitrepo1, + rev: "v2", + file: "another.txt", + data: "another\n", + }, + { + repo: gitrepo1, + rev: "v2.3.4", + file: "another.txt", + err: os.ErrNotExist.Error(), + }, +} + +func TestReadFile(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + testenv.MustHaveExec(t) + + for _, tt := range readFileTests { + f := func(t *testing.T) { + r, err := testRepo(tt.repo) + if err != nil { + t.Fatal(err) + } + data, err := r.ReadFile(tt.rev, tt.file, 100) + if err != nil { + if tt.err == "" { + t.Fatalf("ReadFile: unexpected error %v", err) + } + if !strings.Contains(err.Error(), tt.err) { + t.Fatalf("ReadFile: wrong error %q, want %q", err, tt.err) + } + if len(data) != 0 { + t.Errorf("ReadFile: non-empty data %q with error %v", data, err) + } + return + } + if tt.err != "" { + t.Fatalf("ReadFile: no error, wanted %v", tt.err) + } + if string(data) != tt.data { + t.Errorf("ReadFile: incorrect data\nhave %q\nwant %q", data, tt.data) + } + } + t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.file, f) + if tt.repo == gitrepo1 { + for _, tt.repo = range altRepos { + t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.file, f) + } + } + } +} + +var readZipTests = []struct { + repo string + rev string + subdir string + actualSubdir string + err string + files map[string]uint64 +}{ + { + repo: gitrepo1, + rev: "v2.3.4", + subdir: "", + files: map[string]uint64{ + "prefix/": 0, + "prefix/README": 0, + "prefix/v2": 3, + }, + }, + { + repo: hgrepo1, + rev: "v2.3.4", + subdir: "", + files: map[string]uint64{ + "prefix/.hg_archival.txt": ^uint64(0), + "prefix/README": 0, + "prefix/v2": 3, + }, + }, + + { + repo: gitrepo1, + rev: "v2", + subdir: "", + files: map[string]uint64{ + "prefix/": 0, + "prefix/README": 0, + "prefix/v2": 3, + "prefix/another.txt": 8, + "prefix/foo.txt": 13, + }, + }, + { + repo: hgrepo1, + rev: "v2", + subdir: "", + files: map[string]uint64{ + "prefix/.hg_archival.txt": ^uint64(0), + "prefix/README": 0, + "prefix/v2": 3, + "prefix/another.txt": 8, + "prefix/foo.txt": 13, + }, + }, + + { + repo: gitrepo1, + rev: "v3", + subdir: "", + files: map[string]uint64{ + "prefix/": 0, + "prefix/v3/": 0, + "prefix/v3/sub/": 0, + "prefix/v3/sub/dir/": 0, + "prefix/v3/sub/dir/file.txt": 16, + "prefix/README": 0, + }, + }, + { + repo: hgrepo1, + rev: "v3", + subdir: "", + files: map[string]uint64{ + "prefix/.hg_archival.txt": ^uint64(0), + "prefix/.hgtags": 405, + "prefix/v3/sub/dir/file.txt": 16, + "prefix/README": 0, + }, + }, + + { + repo: gitrepo1, + rev: "v3", + subdir: "v3/sub/dir", + files: map[string]uint64{ + "prefix/": 0, + "prefix/v3/": 0, + "prefix/v3/sub/": 0, + "prefix/v3/sub/dir/": 0, + "prefix/v3/sub/dir/file.txt": 16, + }, + }, + { + repo: hgrepo1, + rev: "v3", + subdir: "v3/sub/dir", + files: map[string]uint64{ + "prefix/v3/sub/dir/file.txt": 16, + }, + }, + + { + repo: gitrepo1, + rev: "v3", + subdir: "v3/sub", + files: map[string]uint64{ + "prefix/": 0, + "prefix/v3/": 0, + "prefix/v3/sub/": 0, + "prefix/v3/sub/dir/": 0, + "prefix/v3/sub/dir/file.txt": 16, + }, + }, + { + repo: hgrepo1, + rev: "v3", + subdir: "v3/sub", + files: map[string]uint64{ + "prefix/v3/sub/dir/file.txt": 16, + }, + }, + + { + repo: gitrepo1, + rev: "aaaaaaaaab", + subdir: "", + err: "unknown revision", + }, + { + repo: hgrepo1, + rev: "aaaaaaaaab", + subdir: "", + err: "unknown revision", + }, + + { + repo: "https://github.com/rsc/vgotest1", + rev: "submod/v1.0.4", + subdir: "submod", + files: map[string]uint64{ + "prefix/": 0, + "prefix/submod/": 0, + "prefix/submod/go.mod": 53, + "prefix/submod/pkg/": 0, + "prefix/submod/pkg/p.go": 31, + }, + }, +} + +type zipFile struct { + name string + size int64 +} + +func TestReadZip(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + testenv.MustHaveExec(t) + + for _, tt := range readZipTests { + f := func(t *testing.T) { + r, err := testRepo(tt.repo) + if err != nil { + t.Fatal(err) + } + rc, actualSubdir, err := r.ReadZip(tt.rev, tt.subdir, 100000) + if err != nil { + if tt.err == "" { + t.Fatalf("ReadZip: unexpected error %v", err) + } + if !strings.Contains(err.Error(), tt.err) { + t.Fatalf("ReadZip: wrong error %q, want %q", err, tt.err) + } + if rc != nil { + t.Errorf("ReadZip: non-nil io.ReadCloser with error %v", err) + } + return + } + defer rc.Close() + if tt.err != "" { + t.Fatalf("ReadZip: no error, wanted %v", tt.err) + } + if actualSubdir != tt.actualSubdir { + t.Fatalf("ReadZip: actualSubdir = %q, want %q", actualSubdir, tt.actualSubdir) + } + zipdata, err := ioutil.ReadAll(rc) + if err != nil { + t.Fatal(err) + } + z, err := zip.NewReader(bytes.NewReader(zipdata), int64(len(zipdata))) + if err != nil { + t.Fatalf("ReadZip: cannot read zip file: %v", err) + } + have := make(map[string]bool) + for _, f := range z.File { + size, ok := tt.files[f.Name] + if !ok { + t.Errorf("ReadZip: unexpected file %s", f.Name) + continue + } + have[f.Name] = true + if size != ^uint64(0) && f.UncompressedSize64 != size { + t.Errorf("ReadZip: file %s has unexpected size %d != %d", f.Name, f.UncompressedSize64, size) + } + } + for name := range tt.files { + if !have[name] { + t.Errorf("ReadZip: missing file %s", name) + } + } + } + t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.subdir, f) + if tt.repo == gitrepo1 { + tt.repo = "localGitRepo" + t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.subdir, f) + } + } +} + +var hgmap = map[string]string{ + "HEAD": "41964ddce1180313bdc01d0a39a2813344d6261d", // not tip due to bad hgrepo1 conversion + "9d02800338b8a55be062c838d1f02e0c5780b9eb": "8f49ee7a6ddcdec6f0112d9dca48d4a2e4c3c09e", + "76a00fb249b7f93091bc2c89a789dab1fc1bc26f": "88fde824ec8b41a76baa16b7e84212cee9f3edd0", + "ede458df7cd0fdca520df19a33158086a8a68e81": "41964ddce1180313bdc01d0a39a2813344d6261d", + "97f6aa59c81c623494825b43d39e445566e429a4": "c0cbbfb24c7c3c50c35c7b88e7db777da4ff625d", +} + +var statTests = []struct { + repo string + rev string + err string + info *RevInfo +}{ + { + repo: gitrepo1, + rev: "HEAD", + info: &RevInfo{ + Name: "ede458df7cd0fdca520df19a33158086a8a68e81", + Short: "ede458df7cd0", + Version: "ede458df7cd0fdca520df19a33158086a8a68e81", + Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC), + Tags: []string{"v1.2.3", "v1.2.4-annotated"}, + }, + }, + { + repo: gitrepo1, + rev: "v2", // branch + info: &RevInfo{ + Name: "9d02800338b8a55be062c838d1f02e0c5780b9eb", + Short: "9d02800338b8", + Version: "9d02800338b8a55be062c838d1f02e0c5780b9eb", + Time: time.Date(2018, 4, 17, 20, 00, 32, 0, time.UTC), + Tags: []string{"v2.0.2"}, + }, + }, + { + repo: gitrepo1, + rev: "v2.3.4", // badly-named branch (semver should be a tag) + info: &RevInfo{ + Name: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f", + Short: "76a00fb249b7", + Version: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f", + Time: time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC), + Tags: []string{"v2.0.1", "v2.3"}, + }, + }, + { + repo: gitrepo1, + rev: "v2.3", // badly-named tag (we only respect full semver v2.3.0) + info: &RevInfo{ + Name: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f", + Short: "76a00fb249b7", + Version: "v2.3", + Time: time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC), + Tags: []string{"v2.0.1", "v2.3"}, + }, + }, + { + repo: gitrepo1, + rev: "v1.2.3", // tag + info: &RevInfo{ + Name: "ede458df7cd0fdca520df19a33158086a8a68e81", + Short: "ede458df7cd0", + Version: "v1.2.3", + Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC), + Tags: []string{"v1.2.3", "v1.2.4-annotated"}, + }, + }, + { + repo: gitrepo1, + rev: "ede458df", // hash prefix in refs + info: &RevInfo{ + Name: "ede458df7cd0fdca520df19a33158086a8a68e81", + Short: "ede458df7cd0", + Version: "ede458df7cd0fdca520df19a33158086a8a68e81", + Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC), + Tags: []string{"v1.2.3", "v1.2.4-annotated"}, + }, + }, + { + repo: gitrepo1, + rev: "97f6aa59", // hash prefix not in refs + info: &RevInfo{ + Name: "97f6aa59c81c623494825b43d39e445566e429a4", + Short: "97f6aa59c81c", + Version: "97f6aa59c81c623494825b43d39e445566e429a4", + Time: time.Date(2018, 4, 17, 20, 0, 19, 0, time.UTC), + }, + }, + { + repo: gitrepo1, + rev: "v1.2.4-annotated", // annotated tag uses unwrapped commit hash + info: &RevInfo{ + Name: "ede458df7cd0fdca520df19a33158086a8a68e81", + Short: "ede458df7cd0", + Version: "v1.2.4-annotated", + Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC), + Tags: []string{"v1.2.3", "v1.2.4-annotated"}, + }, + }, + { + repo: gitrepo1, + rev: "aaaaaaaaab", + err: "unknown revision", + }, +} + +func TestStat(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + testenv.MustHaveExec(t) + + for _, tt := range statTests { + f := func(t *testing.T) { + r, err := testRepo(tt.repo) + if err != nil { + t.Fatal(err) + } + info, err := r.Stat(tt.rev) + if err != nil { + if tt.err == "" { + t.Fatalf("Stat: unexpected error %v", err) + } + if !strings.Contains(err.Error(), tt.err) { + t.Fatalf("Stat: wrong error %q, want %q", err, tt.err) + } + if info != nil { + t.Errorf("Stat: non-nil info with error %q", err) + } + return + } + if !reflect.DeepEqual(info, tt.info) { + t.Errorf("Stat: incorrect info\nhave %+v\nwant %+v", *info, *tt.info) + } + } + t.Run(path.Base(tt.repo)+"/"+tt.rev, f) + if tt.repo == gitrepo1 { + for _, tt.repo = range altRepos { + old := tt + var m map[string]string + if tt.repo == hgrepo1 { + m = hgmap + } + if tt.info != nil { + info := *tt.info + tt.info = &info + tt.info.Name = remap(tt.info.Name, m) + tt.info.Version = remap(tt.info.Version, m) + tt.info.Short = remap(tt.info.Short, m) + } + tt.rev = remap(tt.rev, m) + t.Run(path.Base(tt.repo)+"/"+tt.rev, f) + tt = old + } + } + } +} + +func remap(name string, m map[string]string) string { + if m[name] != "" { + return m[name] + } + if AllHex(name) { + for k, v := range m { + if strings.HasPrefix(k, name) { + return v[:len(name)] + } + } + } + return name +} diff --git a/libgo/go/cmd/go/internal/modfetch/codehost/shell.go b/libgo/go/cmd/go/internal/modfetch/codehost/shell.go new file mode 100644 index 0000000..7b813c3 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/codehost/shell.go @@ -0,0 +1,140 @@ +// Copyright 2018 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 ignore + +// Interactive debugging shell for codehost.Repo implementations. + +package main + +import ( + "archive/zip" + "bufio" + "bytes" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "strings" + "time" + + "cmd/go/internal/modfetch/codehost" +) + +func usage() { + fmt.Fprintf(os.Stderr, "usage: go run shell.go vcs remote\n") + os.Exit(2) +} + +func main() { + codehost.WorkRoot = "/tmp/vcswork" + log.SetFlags(0) + log.SetPrefix("shell: ") + flag.Usage = usage + flag.Parse() + if flag.NArg() != 2 { + usage() + } + + repo, err := codehost.NewRepo(flag.Arg(0), flag.Arg(1)) + if err != nil { + log.Fatal(err) + } + + b := bufio.NewReader(os.Stdin) + for { + fmt.Fprintf(os.Stderr, ">>> ") + line, err := b.ReadString('\n') + if err != nil { + log.Fatal(err) + } + f := strings.Fields(line) + if len(f) == 0 { + continue + } + switch f[0] { + default: + fmt.Fprintf(os.Stderr, "?unknown command\n") + continue + case "tags": + prefix := "" + if len(f) == 2 { + prefix = f[1] + } + if len(f) > 2 { + fmt.Fprintf(os.Stderr, "?usage: tags [prefix]\n") + continue + } + tags, err := repo.Tags(prefix) + if err != nil { + fmt.Fprintf(os.Stderr, "?%s\n", err) + continue + } + for _, tag := range tags { + fmt.Printf("%s\n", tag) + } + + case "stat": + if len(f) != 2 { + fmt.Fprintf(os.Stderr, "?usage: stat rev\n") + continue + } + info, err := repo.Stat(f[1]) + if err != nil { + fmt.Fprintf(os.Stderr, "?%s\n", err) + continue + } + fmt.Printf("name=%s short=%s version=%s time=%s\n", info.Name, info.Short, info.Version, info.Time.UTC().Format(time.RFC3339)) + + case "read": + if len(f) != 3 { + fmt.Fprintf(os.Stderr, "?usage: read rev file\n") + continue + } + data, err := repo.ReadFile(f[1], f[2], 10<<20) + if err != nil { + fmt.Fprintf(os.Stderr, "?%s\n", err) + continue + } + os.Stdout.Write(data) + + case "zip": + if len(f) != 4 { + fmt.Fprintf(os.Stderr, "?usage: zip rev subdir output\n") + continue + } + subdir := f[2] + if subdir == "-" { + subdir = "" + } + rc, _, err := repo.ReadZip(f[1], subdir, 10<<20) + if err != nil { + fmt.Fprintf(os.Stderr, "?%s\n", err) + continue + } + data, err := ioutil.ReadAll(rc) + rc.Close() + if err != nil { + fmt.Fprintf(os.Stderr, "?%s\n", err) + continue + } + + if f[3] != "-" { + if err := ioutil.WriteFile(f[3], data, 0666); err != nil { + fmt.Fprintf(os.Stderr, "?%s\n", err) + continue + } + } + z, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) + if err != nil { + fmt.Fprintf(os.Stderr, "?%s\n", err) + continue + } + for _, f := range z.File { + fmt.Printf("%s %d\n", f.Name, f.UncompressedSize64) + } + } + } +} diff --git a/libgo/go/cmd/go/internal/modfetch/codehost/vcs.go b/libgo/go/cmd/go/internal/modfetch/codehost/vcs.go new file mode 100644 index 0000000..9e862a0 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/codehost/vcs.go @@ -0,0 +1,528 @@ +// Copyright 2018 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 codehost + +import ( + "encoding/xml" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "sync" + "time" + + "cmd/go/internal/par" + "cmd/go/internal/str" +) + +// A VCSError indicates an error using a version control system. +// The implication of a VCSError is that we know definitively where +// to get the code, but we can't access it due to the error. +// The caller should report this error instead of continuing to probe +// other possible module paths. +type VCSError struct { + Err error +} + +func (e *VCSError) Error() string { return e.Err.Error() } + +func NewRepo(vcs, remote string) (Repo, error) { + type key struct { + vcs string + remote string + } + type cached struct { + repo Repo + err error + } + c := vcsRepoCache.Do(key{vcs, remote}, func() interface{} { + repo, err := newVCSRepo(vcs, remote) + if err != nil { + err = &VCSError{err} + } + return cached{repo, err} + }).(cached) + + return c.repo, c.err +} + +var vcsRepoCache par.Cache + +type vcsRepo struct { + remote string + cmd *vcsCmd + dir string + + tagsOnce sync.Once + tags map[string]bool + + branchesOnce sync.Once + branches map[string]bool + + fetchOnce sync.Once + fetchErr error +} + +func newVCSRepo(vcs, remote string) (Repo, error) { + if vcs == "git" { + return newGitRepo(remote, false) + } + cmd := vcsCmds[vcs] + if cmd == nil { + return nil, fmt.Errorf("unknown vcs: %s %s", vcs, remote) + } + if !strings.Contains(remote, "://") { + return nil, fmt.Errorf("invalid vcs remote: %s %s", vcs, remote) + } + r := &vcsRepo{remote: remote, cmd: cmd} + if cmd.init == nil { + return r, nil + } + dir, err := WorkDir(vcsWorkDirType+vcs, r.remote) + if err != nil { + return nil, err + } + r.dir = dir + if _, err := os.Stat(filepath.Join(dir, "."+vcs)); err != nil { + if _, err := Run(dir, cmd.init(r.remote)); err != nil { + os.RemoveAll(dir) + return nil, err + } + } + return r, nil +} + +const vcsWorkDirType = "vcs1." + +type vcsCmd struct { + vcs string // vcs name "hg" + init func(remote string) []string // cmd to init repo to track remote + tags func(remote string) []string // cmd to list local tags + tagRE *regexp.Regexp // regexp to extract tag names from output of tags cmd + branches func(remote string) []string // cmd to list local branches + branchRE *regexp.Regexp // regexp to extract branch names from output of tags cmd + badLocalRevRE *regexp.Regexp // regexp of names that must not be served out of local cache without doing fetch first + statLocal func(rev, remote string) []string // cmd to stat local rev + parseStat func(rev, out string) (*RevInfo, error) // cmd to parse output of statLocal + fetch []string // cmd to fetch everything from remote + latest string // name of latest commit on remote (tip, HEAD, etc) + readFile func(rev, file, remote string) []string // cmd to read rev's file + readZip func(rev, subdir, remote, target string) []string // cmd to read rev's subdir as zip file +} + +var re = regexp.MustCompile + +var vcsCmds = map[string]*vcsCmd{ + "hg": { + vcs: "hg", + init: func(remote string) []string { + return []string{"hg", "clone", "-U", remote, "."} + }, + tags: func(remote string) []string { + return []string{"hg", "tags", "-q"} + }, + tagRE: re(`(?m)^[^\n]+$`), + branches: func(remote string) []string { + return []string{"hg", "branches", "-c", "-q"} + }, + branchRE: re(`(?m)^[^\n]+$`), + badLocalRevRE: re(`(?m)^(tip)$`), + statLocal: func(rev, remote string) []string { + return []string{"hg", "log", "-l1", "-r", rev, "--template", "{node} {date|hgdate} {tags}"} + }, + parseStat: hgParseStat, + fetch: []string{"hg", "pull", "-f"}, + latest: "tip", + readFile: func(rev, file, remote string) []string { + return []string{"hg", "cat", "-r", rev, file} + }, + readZip: func(rev, subdir, remote, target string) []string { + pattern := []string{} + if subdir != "" { + pattern = []string{"-I", subdir + "/**"} + } + return str.StringList("hg", "archive", "-t", "zip", "--no-decode", "-r", rev, "--prefix=prefix/", pattern, target) + }, + }, + + "svn": { + vcs: "svn", + init: nil, // no local checkout + tags: func(remote string) []string { + return []string{"svn", "list", strings.TrimSuffix(remote, "/trunk") + "/tags"} + }, + tagRE: re(`(?m)^(.*?)/?$`), + statLocal: func(rev, remote string) []string { + suffix := "@" + rev + if rev == "latest" { + suffix = "" + } + return []string{"svn", "log", "-l1", "--xml", remote + suffix} + }, + parseStat: svnParseStat, + latest: "latest", + readFile: func(rev, file, remote string) []string { + return []string{"svn", "cat", remote + "/" + file + "@" + rev} + }, + // TODO: zip + }, + + "bzr": { + vcs: "bzr", + init: func(remote string) []string { + return []string{"bzr", "branch", "--use-existing-dir", remote, "."} + }, + fetch: []string{ + "bzr", "pull", "--overwrite-tags", + }, + tags: func(remote string) []string { + return []string{"bzr", "tags"} + }, + tagRE: re(`(?m)^\S+`), + badLocalRevRE: re(`^revno:-`), + statLocal: func(rev, remote string) []string { + return []string{"bzr", "log", "-l1", "--long", "--show-ids", "-r", rev} + }, + parseStat: bzrParseStat, + latest: "revno:-1", + readFile: func(rev, file, remote string) []string { + return []string{"bzr", "cat", "-r", rev, file} + }, + readZip: func(rev, subdir, remote, target string) []string { + extra := []string{} + if subdir != "" { + extra = []string{"./" + subdir} + } + return str.StringList("bzr", "export", "--format=zip", "-r", rev, "--root=prefix/", target, extra) + }, + }, + + "fossil": { + vcs: "fossil", + init: func(remote string) []string { + return []string{"fossil", "clone", remote, ".fossil"} + }, + fetch: []string{"fossil", "pull", "-R", ".fossil"}, + tags: func(remote string) []string { + return []string{"fossil", "tag", "-R", ".fossil", "list"} + }, + tagRE: re(`XXXTODO`), + statLocal: func(rev, remote string) []string { + return []string{"fossil", "info", "-R", ".fossil", rev} + }, + parseStat: fossilParseStat, + latest: "trunk", + readFile: func(rev, file, remote string) []string { + return []string{"fossil", "cat", "-R", ".fossil", "-r", rev, file} + }, + readZip: func(rev, subdir, remote, target string) []string { + extra := []string{} + if subdir != "" && !strings.ContainsAny(subdir, "*?[],") { + extra = []string{"--include", subdir} + } + // Note that vcsRepo.ReadZip below rewrites this command + // to run in a different directory, to work around a fossil bug. + return str.StringList("fossil", "zip", "-R", ".fossil", "--name", "prefix", extra, rev, target) + }, + }, +} + +func (r *vcsRepo) loadTags() { + out, err := Run(r.dir, r.cmd.tags(r.remote)) + if err != nil { + return + } + + // Run tag-listing command and extract tags. + r.tags = make(map[string]bool) + for _, tag := range r.cmd.tagRE.FindAllString(string(out), -1) { + if r.cmd.badLocalRevRE != nil && r.cmd.badLocalRevRE.MatchString(tag) { + continue + } + r.tags[tag] = true + } +} + +func (r *vcsRepo) loadBranches() { + if r.cmd.branches == nil { + return + } + + out, err := Run(r.dir, r.cmd.branches(r.remote)) + if err != nil { + return + } + + r.branches = make(map[string]bool) + for _, branch := range r.cmd.branchRE.FindAllString(string(out), -1) { + if r.cmd.badLocalRevRE != nil && r.cmd.badLocalRevRE.MatchString(branch) { + continue + } + r.branches[branch] = true + } +} + +func (r *vcsRepo) Tags(prefix string) ([]string, error) { + r.tagsOnce.Do(r.loadTags) + + tags := []string{} + for tag := range r.tags { + if strings.HasPrefix(tag, prefix) { + tags = append(tags, tag) + } + } + sort.Strings(tags) + return tags, nil +} + +func (r *vcsRepo) Stat(rev string) (*RevInfo, error) { + if rev == "latest" { + rev = r.cmd.latest + } + r.branchesOnce.Do(r.loadBranches) + revOK := (r.cmd.badLocalRevRE == nil || !r.cmd.badLocalRevRE.MatchString(rev)) && !r.branches[rev] + if revOK { + if info, err := r.statLocal(rev); err == nil { + return info, nil + } + } + + r.fetchOnce.Do(r.fetch) + if r.fetchErr != nil { + return nil, r.fetchErr + } + info, err := r.statLocal(rev) + if err != nil { + return nil, err + } + if !revOK { + info.Version = info.Name + } + return info, nil +} + +func (r *vcsRepo) fetch() { + _, r.fetchErr = Run(r.dir, r.cmd.fetch) +} + +func (r *vcsRepo) statLocal(rev string) (*RevInfo, error) { + out, err := Run(r.dir, r.cmd.statLocal(rev, r.remote)) + if err != nil { + return nil, fmt.Errorf("unknown revision %s", rev) + } + return r.cmd.parseStat(rev, string(out)) +} + +func (r *vcsRepo) Latest() (*RevInfo, error) { + return r.Stat("latest") +} + +func (r *vcsRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) { + if rev == "latest" { + rev = r.cmd.latest + } + _, err := r.Stat(rev) // download rev into local repo + if err != nil { + return nil, err + } + out, err := Run(r.dir, r.cmd.readFile(rev, file, r.remote)) + if err != nil { + return nil, os.ErrNotExist + } + return out, nil +} + +func (r *vcsRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[string]*FileRev, error) { + return nil, fmt.Errorf("ReadFileRevs not implemented") +} + +func (r *vcsRepo) RecentTag(rev, prefix string) (tag string, err error) { + return "", fmt.Errorf("RecentTags not implemented") +} + +func (r *vcsRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) { + if rev == "latest" { + rev = r.cmd.latest + } + f, err := ioutil.TempFile("", "go-readzip-*.zip") + if err != nil { + return nil, "", err + } + if r.cmd.vcs == "fossil" { + // If you run + // fossil zip -R .fossil --name prefix trunk /tmp/x.zip + // fossil fails with "unable to create directory /tmp" [sic]. + // Change the command to run in /tmp instead, + // replacing the -R argument with an absolute path. + args := r.cmd.readZip(rev, subdir, r.remote, filepath.Base(f.Name())) + for i := range args { + if args[i] == ".fossil" { + args[i] = filepath.Join(r.dir, ".fossil") + } + } + _, err = Run(filepath.Dir(f.Name()), args) + } else { + _, err = Run(r.dir, r.cmd.readZip(rev, subdir, r.remote, f.Name())) + } + if err != nil { + f.Close() + os.Remove(f.Name()) + return nil, "", err + } + return &deleteCloser{f}, "", nil +} + +// deleteCloser is a file that gets deleted on Close. +type deleteCloser struct { + *os.File +} + +func (d *deleteCloser) Close() error { + defer os.Remove(d.File.Name()) + return d.File.Close() +} + +func hgParseStat(rev, out string) (*RevInfo, error) { + f := strings.Fields(string(out)) + if len(f) < 3 { + return nil, fmt.Errorf("unexpected response from hg log: %q", out) + } + hash := f[0] + version := rev + if strings.HasPrefix(hash, version) { + version = hash // extend to full hash + } + t, err := strconv.ParseInt(f[1], 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid time from hg log: %q", out) + } + + var tags []string + for _, tag := range f[3:] { + if tag != "tip" { + tags = append(tags, tag) + } + } + sort.Strings(tags) + + info := &RevInfo{ + Name: hash, + Short: ShortenSHA1(hash), + Time: time.Unix(t, 0).UTC(), + Version: version, + Tags: tags, + } + return info, nil +} + +func svnParseStat(rev, out string) (*RevInfo, error) { + var log struct { + Logentry struct { + Revision int64 `xml:"revision,attr"` + Date string `xml:"date"` + } `xml:"logentry"` + } + if err := xml.Unmarshal([]byte(out), &log); err != nil { + return nil, fmt.Errorf("unexpected response from svn log --xml: %v\n%s", err, out) + } + + t, err := time.Parse(time.RFC3339, log.Logentry.Date) + if err != nil { + return nil, fmt.Errorf("unexpected response from svn log --xml: %v\n%s", err, out) + } + + info := &RevInfo{ + Name: fmt.Sprintf("%d", log.Logentry.Revision), + Short: fmt.Sprintf("%012d", log.Logentry.Revision), + Time: t.UTC(), + Version: rev, + } + return info, nil +} + +func bzrParseStat(rev, out string) (*RevInfo, error) { + var revno int64 + var tm time.Time + for _, line := range strings.Split(out, "\n") { + if line == "" || line[0] == ' ' || line[0] == '\t' { + // End of header, start of commit message. + break + } + if line[0] == '-' { + continue + } + i := strings.Index(line, ":") + if i < 0 { + // End of header, start of commit message. + break + } + key, val := line[:i], strings.TrimSpace(line[i+1:]) + switch key { + case "revno": + if j := strings.Index(val, " "); j >= 0 { + val = val[:j] + } + i, err := strconv.ParseInt(val, 10, 64) + if err != nil { + return nil, fmt.Errorf("unexpected revno from bzr log: %q", line) + } + revno = i + case "timestamp": + j := strings.Index(val, " ") + if j < 0 { + return nil, fmt.Errorf("unexpected timestamp from bzr log: %q", line) + } + t, err := time.Parse("2006-01-02 15:04:05 -0700", val[j+1:]) + if err != nil { + return nil, fmt.Errorf("unexpected timestamp from bzr log: %q", line) + } + tm = t.UTC() + } + } + if revno == 0 || tm.IsZero() { + return nil, fmt.Errorf("unexpected response from bzr log: %q", out) + } + + info := &RevInfo{ + Name: fmt.Sprintf("%d", revno), + Short: fmt.Sprintf("%012d", revno), + Time: tm, + Version: rev, + } + return info, nil +} + +func fossilParseStat(rev, out string) (*RevInfo, error) { + for _, line := range strings.Split(out, "\n") { + if strings.HasPrefix(line, "uuid:") { + f := strings.Fields(line) + if len(f) != 5 || len(f[1]) != 40 || f[4] != "UTC" { + return nil, fmt.Errorf("unexpected response from fossil info: %q", line) + } + t, err := time.Parse("2006-01-02 15:04:05", f[2]+" "+f[3]) + if err != nil { + return nil, fmt.Errorf("unexpected response from fossil info: %q", line) + } + hash := f[1] + version := rev + if strings.HasPrefix(hash, version) { + version = hash // extend to full hash + } + info := &RevInfo{ + Name: hash, + Short: ShortenSHA1(hash), + Time: t, + Version: version, + } + return info, nil + } + } + return nil, fmt.Errorf("unexpected response from fossil info: %q", out) +} diff --git a/libgo/go/cmd/go/internal/modfetch/coderepo.go b/libgo/go/cmd/go/internal/modfetch/coderepo.go new file mode 100644 index 0000000..9cf0e91 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/coderepo.go @@ -0,0 +1,605 @@ +// Copyright 2018 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 modfetch + +import ( + "archive/zip" + "fmt" + "io" + "io/ioutil" + "os" + "path" + "strings" + + "cmd/go/internal/modfetch/codehost" + "cmd/go/internal/modfile" + "cmd/go/internal/module" + "cmd/go/internal/semver" +) + +// A codeRepo implements modfetch.Repo using an underlying codehost.Repo. +type codeRepo struct { + modPath string + + code codehost.Repo + codeRoot string + codeDir string + + path string + pathPrefix string + pathMajor string + pseudoMajor string +} + +func newCodeRepo(code codehost.Repo, root, path string) (Repo, error) { + if !hasPathPrefix(path, root) { + return nil, fmt.Errorf("mismatched repo: found %s for %s", root, path) + } + pathPrefix, pathMajor, ok := module.SplitPathVersion(path) + if !ok { + return nil, fmt.Errorf("invalid module path %q", path) + } + pseudoMajor := "v0" + if pathMajor != "" { + pseudoMajor = pathMajor[1:] + } + + // At this point we might have: + // codeRoot = github.com/rsc/foo + // path = github.com/rsc/foo/bar/v2 + // pathPrefix = github.com/rsc/foo/bar + // pathMajor = /v2 + // pseudoMajor = v2 + // + // Compute codeDir = bar, the subdirectory within the repo + // corresponding to the module root. + codeDir := strings.Trim(strings.TrimPrefix(pathPrefix, root), "/") + if strings.HasPrefix(path, "gopkg.in/") { + // But gopkg.in is a special legacy case, in which pathPrefix does not start with codeRoot. + // For example we might have: + // codeRoot = gopkg.in/yaml.v2 + // pathPrefix = gopkg.in/yaml + // pathMajor = .v2 + // pseudoMajor = v2 + // codeDir = pathPrefix (because codeRoot is not a prefix of pathPrefix) + // Clear codeDir - the module root is the repo root for gopkg.in repos. + codeDir = "" + } + + r := &codeRepo{ + modPath: path, + code: code, + codeRoot: root, + codeDir: codeDir, + pathPrefix: pathPrefix, + pathMajor: pathMajor, + pseudoMajor: pseudoMajor, + } + + return r, nil +} + +func (r *codeRepo) ModulePath() string { + return r.modPath +} + +func (r *codeRepo) Versions(prefix string) ([]string, error) { + // Special case: gopkg.in/macaroon-bakery.v2-unstable + // does not use the v2 tags (those are for macaroon-bakery.v2). + // It has no possible tags at all. + if strings.HasPrefix(r.modPath, "gopkg.in/") && strings.HasSuffix(r.modPath, "-unstable") { + return nil, nil + } + + p := prefix + if r.codeDir != "" { + p = r.codeDir + "/" + p + } + tags, err := r.code.Tags(p) + if err != nil { + return nil, err + } + + list := []string{} + var incompatible []string + for _, tag := range tags { + if !strings.HasPrefix(tag, p) { + continue + } + v := tag + if r.codeDir != "" { + v = v[len(r.codeDir)+1:] + } + if v == "" || v != module.CanonicalVersion(v) || IsPseudoVersion(v) { + continue + } + if !module.MatchPathMajor(v, r.pathMajor) { + if r.codeDir == "" && r.pathMajor == "" && semver.Major(v) > "v1" { + incompatible = append(incompatible, v) + } + continue + } + list = append(list, v) + } + + if len(incompatible) > 0 { + // Check for later versions that were created not following semantic import versioning, + // as indicated by the absence of a go.mod file. Those versions can be addressed + // by referring to them with a +incompatible suffix, as in v17.0.0+incompatible. + files, err := r.code.ReadFileRevs(incompatible, "go.mod", codehost.MaxGoMod) + if err != nil { + return nil, err + } + for _, rev := range incompatible { + f := files[rev] + if os.IsNotExist(f.Err) { + list = append(list, rev+"+incompatible") + } + } + } + + SortVersions(list) + return list, nil +} + +func (r *codeRepo) Stat(rev string) (*RevInfo, error) { + if rev == "latest" { + return r.Latest() + } + codeRev := r.revToRev(rev) + if semver.IsValid(codeRev) && r.codeDir != "" { + codeRev = r.codeDir + "/" + codeRev + } + info, err := r.code.Stat(codeRev) + if err != nil { + return nil, err + } + return r.convert(info, rev) +} + +func (r *codeRepo) Latest() (*RevInfo, error) { + info, err := r.code.Latest() + if err != nil { + return nil, err + } + return r.convert(info, "") +} + +func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, error) { + info2 := &RevInfo{ + Name: info.Name, + Short: info.Short, + Time: info.Time, + } + + // Determine version. + if module.CanonicalVersion(statVers) == statVers && module.MatchPathMajor(statVers, r.pathMajor) { + // The original call was repo.Stat(statVers), and requestedVersion is OK, so use it. + info2.Version = statVers + } else { + // Otherwise derive a version from a code repo tag. + // Tag must have a prefix matching codeDir. + p := "" + if r.codeDir != "" { + p = r.codeDir + "/" + } + + // If this is a plain tag (no dir/ prefix) + // and the module path is unversioned, + // and if the underlying file tree has no go.mod, + // then allow using the tag with a +incompatible suffix. + canUseIncompatible := false + if r.codeDir == "" && r.pathMajor == "" { + _, errGoMod := r.code.ReadFile(info.Name, "go.mod", codehost.MaxGoMod) + if errGoMod != nil { + canUseIncompatible = true + } + } + + tagToVersion := func(v string) string { + if !strings.HasPrefix(v, p) { + return "" + } + v = v[len(p):] + if module.CanonicalVersion(v) != v || IsPseudoVersion(v) { + return "" + } + if module.MatchPathMajor(v, r.pathMajor) { + return v + } + if canUseIncompatible { + return v + "+incompatible" + } + return "" + } + + // If info.Version is OK, use it. + if v := tagToVersion(info.Version); v != "" { + info2.Version = v + } else { + // Otherwise look through all known tags for latest in semver ordering. + for _, tag := range info.Tags { + if v := tagToVersion(tag); v != "" && semver.Compare(info2.Version, v) < 0 { + info2.Version = v + } + } + // Otherwise make a pseudo-version. + if info2.Version == "" { + tag, _ := r.code.RecentTag(statVers, p) + v = tagToVersion(tag) + // TODO: Check that v is OK for r.pseudoMajor or else is OK for incompatible. + info2.Version = PseudoVersion(r.pseudoMajor, v, info.Time, info.Short) + } + } + } + + // Do not allow a successful stat of a pseudo-version for a subdirectory + // unless the subdirectory actually does have a go.mod. + if IsPseudoVersion(info2.Version) && r.codeDir != "" { + _, _, _, err := r.findDir(info2.Version) + if err != nil { + // TODO: It would be nice to return an error like "not a module". + // Right now we return "missing go.mod", which is a little confusing. + return nil, err + } + } + + return info2, nil +} + +func (r *codeRepo) revToRev(rev string) string { + if semver.IsValid(rev) { + if IsPseudoVersion(rev) { + r, _ := PseudoVersionRev(rev) + return r + } + if semver.Build(rev) == "+incompatible" { + rev = rev[:len(rev)-len("+incompatible")] + } + if r.codeDir == "" { + return rev + } + return r.codeDir + "/" + rev + } + return rev +} + +func (r *codeRepo) versionToRev(version string) (rev string, err error) { + if !semver.IsValid(version) { + return "", fmt.Errorf("malformed semantic version %q", version) + } + return r.revToRev(version), nil +} + +func (r *codeRepo) findDir(version string) (rev, dir string, gomod []byte, err error) { + rev, err = r.versionToRev(version) + if err != nil { + return "", "", nil, err + } + + // Load info about go.mod but delay consideration + // (except I/O error) until we rule out v2/go.mod. + file1 := path.Join(r.codeDir, "go.mod") + gomod1, err1 := r.code.ReadFile(rev, file1, codehost.MaxGoMod) + if err1 != nil && !os.IsNotExist(err1) { + return "", "", nil, fmt.Errorf("reading %s/%s at revision %s: %v", r.pathPrefix, file1, rev, err1) + } + mpath1 := modfile.ModulePath(gomod1) + found1 := err1 == nil && isMajor(mpath1, r.pathMajor) + + var file2 string + if r.pathMajor != "" && !strings.HasPrefix(r.pathMajor, ".") { + // Suppose pathMajor is "/v2". + // Either go.mod should claim v2 and v2/go.mod should not exist, + // or v2/go.mod should exist and claim v2. Not both. + // Note that we don't check the full path, just the major suffix, + // because of replacement modules. This might be a fork of + // the real module, found at a different path, usable only in + // a replace directive. + dir2 := path.Join(r.codeDir, r.pathMajor[1:]) + file2 = path.Join(dir2, "go.mod") + gomod2, err2 := r.code.ReadFile(rev, file2, codehost.MaxGoMod) + if err2 != nil && !os.IsNotExist(err2) { + return "", "", nil, fmt.Errorf("reading %s/%s at revision %s: %v", r.pathPrefix, file2, rev, err2) + } + mpath2 := modfile.ModulePath(gomod2) + found2 := err2 == nil && isMajor(mpath2, r.pathMajor) + + if found1 && found2 { + return "", "", nil, fmt.Errorf("%s/%s and ...%s/go.mod both have ...%s module paths at revision %s", r.pathPrefix, file1, r.pathMajor, r.pathMajor, rev) + } + if found2 { + return rev, dir2, gomod2, nil + } + if err2 == nil { + if mpath2 == "" { + return "", "", nil, fmt.Errorf("%s/%s is missing module path at revision %s", r.pathPrefix, file2, rev) + } + return "", "", nil, fmt.Errorf("%s/%s has non-...%s module path %q at revision %s", r.pathPrefix, file2, r.pathMajor, mpath2, rev) + } + } + + // Not v2/go.mod, so it's either go.mod or nothing. Which is it? + if found1 { + // Explicit go.mod with matching module path OK. + return rev, r.codeDir, gomod1, nil + } + if err1 == nil { + // Explicit go.mod with non-matching module path disallowed. + suffix := "" + if file2 != "" { + suffix = fmt.Sprintf(" (and ...%s/go.mod does not exist)", r.pathMajor) + } + if mpath1 == "" { + return "", "", nil, fmt.Errorf("%s is missing module path%s at revision %s", file1, suffix, rev) + } + if r.pathMajor != "" { // ".v1", ".v2" for gopkg.in + return "", "", nil, fmt.Errorf("%s has non-...%s module path %q%s at revision %s", file1, r.pathMajor, mpath1, suffix, rev) + } + return "", "", nil, fmt.Errorf("%s has post-%s module path %q%s at revision %s", file1, semver.Major(version), mpath1, suffix, rev) + } + + if r.codeDir == "" && (r.pathMajor == "" || strings.HasPrefix(r.pathMajor, ".")) { + // Implicit go.mod at root of repo OK for v0/v1 and for gopkg.in. + return rev, "", nil, nil + } + + // Implicit go.mod below root of repo or at v2+ disallowed. + // Be clear about possibility of using either location for v2+. + if file2 != "" { + return "", "", nil, fmt.Errorf("missing %s/go.mod and ...%s/go.mod at revision %s", r.pathPrefix, r.pathMajor, rev) + } + return "", "", nil, fmt.Errorf("missing %s/go.mod at revision %s", r.pathPrefix, rev) +} + +func isMajor(mpath, pathMajor string) bool { + if mpath == "" { + return false + } + if pathMajor == "" { + // mpath must NOT have version suffix. + i := len(mpath) + for i > 0 && '0' <= mpath[i-1] && mpath[i-1] <= '9' { + i-- + } + if i < len(mpath) && i >= 2 && mpath[i-1] == 'v' && mpath[i-2] == '/' { + // Found valid suffix. + return false + } + return true + } + // Otherwise pathMajor is ".v1", ".v2" (gopkg.in), or "/v2", "/v3" etc. + return strings.HasSuffix(mpath, pathMajor) +} + +func (r *codeRepo) GoMod(version string) (data []byte, err error) { + rev, dir, gomod, err := r.findDir(version) + if err != nil { + return nil, err + } + if gomod != nil { + return gomod, nil + } + data, err = r.code.ReadFile(rev, path.Join(dir, "go.mod"), codehost.MaxGoMod) + if err != nil { + if os.IsNotExist(err) { + return r.legacyGoMod(rev, dir), nil + } + return nil, err + } + return data, nil +} + +func (r *codeRepo) legacyGoMod(rev, dir string) []byte { + // We used to try to build a go.mod reflecting pre-existing + // package management metadata files, but the conversion + // was inherently imperfect (because those files don't have + // exactly the same semantics as go.mod) and, when done + // for dependencies in the middle of a build, impossible to + // correct. So we stopped. + // Return a fake go.mod that simply declares the module path. + return []byte(fmt.Sprintf("module %s\n", modfile.AutoQuote(r.modPath))) +} + +func (r *codeRepo) modPrefix(rev string) string { + return r.modPath + "@" + rev +} + +func (r *codeRepo) Zip(version string, tmpdir string) (tmpfile string, err error) { + rev, dir, _, err := r.findDir(version) + if err != nil { + return "", err + } + dl, actualDir, err := r.code.ReadZip(rev, dir, codehost.MaxZipFile) + if err != nil { + return "", err + } + if actualDir != "" && !hasPathPrefix(dir, actualDir) { + return "", fmt.Errorf("internal error: downloading %v %v: dir=%q but actualDir=%q", r.path, rev, dir, actualDir) + } + subdir := strings.Trim(strings.TrimPrefix(dir, actualDir), "/") + + // Spool to local file. + f, err := ioutil.TempFile(tmpdir, "go-codehost-") + if err != nil { + dl.Close() + return "", err + } + defer os.Remove(f.Name()) + defer f.Close() + maxSize := int64(codehost.MaxZipFile) + lr := &io.LimitedReader{R: dl, N: maxSize + 1} + if _, err := io.Copy(f, lr); err != nil { + dl.Close() + return "", err + } + dl.Close() + if lr.N <= 0 { + return "", fmt.Errorf("downloaded zip file too large") + } + size := (maxSize + 1) - lr.N + if _, err := f.Seek(0, 0); err != nil { + return "", err + } + + // Translate from zip file we have to zip file we want. + zr, err := zip.NewReader(f, size) + if err != nil { + return "", err + } + f2, err := ioutil.TempFile(tmpdir, "go-codezip-") + if err != nil { + return "", err + } + + zw := zip.NewWriter(f2) + newName := f2.Name() + defer func() { + f2.Close() + if err != nil { + os.Remove(newName) + } + }() + if subdir != "" { + subdir += "/" + } + haveLICENSE := false + topPrefix := "" + haveGoMod := make(map[string]bool) + for _, zf := range zr.File { + if topPrefix == "" { + i := strings.Index(zf.Name, "/") + if i < 0 { + return "", fmt.Errorf("missing top-level directory prefix") + } + topPrefix = zf.Name[:i+1] + } + if !strings.HasPrefix(zf.Name, topPrefix) { + return "", fmt.Errorf("zip file contains more than one top-level directory") + } + dir, file := path.Split(zf.Name) + if file == "go.mod" { + haveGoMod[dir] = true + } + } + root := topPrefix + subdir + inSubmodule := func(name string) bool { + for { + dir, _ := path.Split(name) + if len(dir) <= len(root) { + return false + } + if haveGoMod[dir] { + return true + } + name = dir[:len(dir)-1] + } + } + for _, zf := range zr.File { + if topPrefix == "" { + i := strings.Index(zf.Name, "/") + if i < 0 { + return "", fmt.Errorf("missing top-level directory prefix") + } + topPrefix = zf.Name[:i+1] + } + if strings.HasSuffix(zf.Name, "/") { // drop directory dummy entries + continue + } + if !strings.HasPrefix(zf.Name, topPrefix) { + return "", fmt.Errorf("zip file contains more than one top-level directory") + } + name := strings.TrimPrefix(zf.Name, topPrefix) + if !strings.HasPrefix(name, subdir) { + continue + } + if name == ".hg_archival.txt" { + // Inserted by hg archive. + // Not correct to drop from other version control systems, but too bad. + continue + } + name = strings.TrimPrefix(name, subdir) + if isVendoredPackage(name) { + continue + } + if inSubmodule(zf.Name) { + continue + } + base := path.Base(name) + if strings.ToLower(base) == "go.mod" && base != "go.mod" { + return "", fmt.Errorf("zip file contains %s, want all lower-case go.mod", zf.Name) + } + if name == "LICENSE" { + haveLICENSE = true + } + size := int64(zf.UncompressedSize) + if size < 0 || maxSize < size { + return "", fmt.Errorf("module source tree too big") + } + maxSize -= size + + rc, err := zf.Open() + if err != nil { + return "", err + } + w, err := zw.Create(r.modPrefix(version) + "/" + name) + lr := &io.LimitedReader{R: rc, N: size + 1} + if _, err := io.Copy(w, lr); err != nil { + return "", err + } + if lr.N <= 0 { + return "", fmt.Errorf("individual file too large") + } + } + + if !haveLICENSE && subdir != "" { + data, err := r.code.ReadFile(rev, "LICENSE", codehost.MaxLICENSE) + if err == nil { + w, err := zw.Create(r.modPrefix(version) + "/LICENSE") + if err != nil { + return "", err + } + if _, err := w.Write(data); err != nil { + return "", err + } + } + } + if err := zw.Close(); err != nil { + return "", err + } + if err := f2.Close(); err != nil { + return "", err + } + + return f2.Name(), nil +} + +// hasPathPrefix reports whether the path s begins with the +// elements in prefix. +func hasPathPrefix(s, prefix string) bool { + switch { + default: + return false + case len(s) == len(prefix): + return s == prefix + case len(s) > len(prefix): + if prefix != "" && prefix[len(prefix)-1] == '/' { + return strings.HasPrefix(s, prefix) + } + return s[len(prefix)] == '/' && s[:len(prefix)] == prefix + } +} + +func isVendoredPackage(name string) bool { + var i int + if strings.HasPrefix(name, "vendor/") { + i += len("vendor/") + } else if j := strings.Index(name, "/vendor/"); j >= 0 { + i += len("/vendor/") + } else { + return false + } + return strings.Contains(name[i:], "/") +} diff --git a/libgo/go/cmd/go/internal/modfetch/coderepo_test.go b/libgo/go/cmd/go/internal/modfetch/coderepo_test.go new file mode 100644 index 0000000..79b8278 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/coderepo_test.go @@ -0,0 +1,643 @@ +// Copyright 2018 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 modfetch + +import ( + "archive/zip" + "internal/testenv" + "io" + "io/ioutil" + "log" + "os" + "reflect" + "strings" + "testing" + "time" + + "cmd/go/internal/modfetch/codehost" +) + +func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { + dir, err := ioutil.TempDir("", "gitrepo-test-") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(dir) + + codehost.WorkRoot = dir + return m.Run() +} + +const ( + vgotest1git = "github.com/rsc/vgotest1" + vgotest1hg = "vcs-test.golang.org/hg/vgotest1.hg" +) + +var altVgotests = []string{ + vgotest1hg, +} + +var codeRepoTests = []struct { + path string + lookerr string + mpath string + rev string + err string + version string + name string + short string + time time.Time + gomod string + gomoderr string + zip []string + ziperr string +}{ + { + path: "github.com/rsc/vgotest1", + rev: "v0.0.0", + version: "v0.0.0", + name: "80d85c5d4d17598a0e9055e7c175a32b415d6128", + short: "80d85c5d4d17", + time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC), + zip: []string{ + "LICENSE", + "README.md", + "pkg/p.go", + }, + }, + { + path: "github.com/rsc/vgotest1", + rev: "v1.0.0", + version: "v1.0.0", + name: "80d85c5d4d17598a0e9055e7c175a32b415d6128", + short: "80d85c5d4d17", + time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC), + zip: []string{ + "LICENSE", + "README.md", + "pkg/p.go", + }, + }, + { + path: "github.com/rsc/vgotest1/v2", + rev: "v2.0.0", + version: "v2.0.0", + name: "45f53230a74ad275c7127e117ac46914c8126160", + short: "45f53230a74a", + time: time.Date(2018, 7, 19, 1, 21, 27, 0, time.UTC), + ziperr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0", + }, + { + path: "github.com/rsc/vgotest1", + rev: "80d85c5", + version: "v1.0.0", + name: "80d85c5d4d17598a0e9055e7c175a32b415d6128", + short: "80d85c5d4d17", + time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC), + zip: []string{ + "LICENSE", + "README.md", + "pkg/p.go", + }, + }, + { + path: "github.com/rsc/vgotest1", + rev: "mytag", + version: "v1.0.0", + name: "80d85c5d4d17598a0e9055e7c175a32b415d6128", + short: "80d85c5d4d17", + time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC), + zip: []string{ + "LICENSE", + "README.md", + "pkg/p.go", + }, + }, + { + path: "github.com/rsc/vgotest1/v2", + rev: "45f53230a", + version: "v2.0.0", + name: "45f53230a74ad275c7127e117ac46914c8126160", + short: "45f53230a74a", + time: time.Date(2018, 7, 19, 1, 21, 27, 0, time.UTC), + gomoderr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0", + ziperr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0", + }, + { + path: "github.com/rsc/vgotest1/v54321", + rev: "80d85c5", + version: "v54321.0.0-20180219231006-80d85c5d4d17", + name: "80d85c5d4d17598a0e9055e7c175a32b415d6128", + short: "80d85c5d4d17", + time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC), + ziperr: "missing github.com/rsc/vgotest1/go.mod and .../v54321/go.mod at revision 80d85c5d4d17", + }, + { + path: "github.com/rsc/vgotest1/submod", + rev: "v1.0.0", + err: "unknown revision submod/v1.0.0", + }, + { + path: "github.com/rsc/vgotest1/submod", + rev: "v1.0.3", + err: "unknown revision submod/v1.0.3", + }, + { + path: "github.com/rsc/vgotest1/submod", + rev: "v1.0.4", + version: "v1.0.4", + name: "8afe2b2efed96e0880ecd2a69b98a53b8c2738b6", + short: "8afe2b2efed9", + time: time.Date(2018, 2, 19, 23, 12, 7, 0, time.UTC), + gomod: "module \"github.com/vgotest1/submod\" // submod/go.mod\n", + zip: []string{ + "go.mod", + "pkg/p.go", + "LICENSE", + }, + }, + { + path: "github.com/rsc/vgotest1", + rev: "v1.1.0", + version: "v1.1.0", + name: "b769f2de407a4db81af9c5de0a06016d60d2ea09", + short: "b769f2de407a", + time: time.Date(2018, 2, 19, 23, 13, 36, 0, time.UTC), + gomod: "module \"github.com/rsc/vgotest1\" // root go.mod\nrequire \"github.com/rsc/vgotest1/submod\" v1.0.5\n", + zip: []string{ + "LICENSE", + "README.md", + "go.mod", + "pkg/p.go", + }, + }, + { + path: "github.com/rsc/vgotest1/v2", + rev: "v2.0.1", + version: "v2.0.1", + name: "ea65f87c8f52c15ea68f3bdd9925ef17e20d91e9", + short: "ea65f87c8f52", + time: time.Date(2018, 2, 19, 23, 14, 23, 0, time.UTC), + gomod: "module \"github.com/rsc/vgotest1/v2\" // root go.mod\n", + }, + { + path: "github.com/rsc/vgotest1/v2", + rev: "v2.0.3", + version: "v2.0.3", + name: "f18795870fb14388a21ef3ebc1d75911c8694f31", + short: "f18795870fb1", + time: time.Date(2018, 2, 19, 23, 16, 4, 0, time.UTC), + gomoderr: "github.com/rsc/vgotest1/v2/go.mod has non-.../v2 module path \"github.com/rsc/vgotest\" at revision v2.0.3", + }, + { + path: "github.com/rsc/vgotest1/v2", + rev: "v2.0.4", + version: "v2.0.4", + name: "1f863feb76bc7029b78b21c5375644838962f88d", + short: "1f863feb76bc", + time: time.Date(2018, 2, 20, 0, 3, 38, 0, time.UTC), + gomoderr: "github.com/rsc/vgotest1/go.mod and .../v2/go.mod both have .../v2 module paths at revision v2.0.4", + }, + { + path: "github.com/rsc/vgotest1/v2", + rev: "v2.0.5", + version: "v2.0.5", + name: "2f615117ce481c8efef46e0cc0b4b4dccfac8fea", + short: "2f615117ce48", + time: time.Date(2018, 2, 20, 0, 3, 59, 0, time.UTC), + gomod: "module \"github.com/rsc/vgotest1/v2\" // v2/go.mod\n", + }, + { + // redirect to github + path: "rsc.io/quote", + rev: "v1.0.0", + version: "v1.0.0", + name: "f488df80bcdbd3e5bafdc24ad7d1e79e83edd7e6", + short: "f488df80bcdb", + time: time.Date(2018, 2, 14, 0, 45, 20, 0, time.UTC), + gomod: "module \"rsc.io/quote\"\n", + }, + { + // redirect to static hosting proxy + path: "swtch.com/testmod", + rev: "v1.0.0", + version: "v1.0.0", + // NO name or short - we intentionally ignore those in the proxy protocol + time: time.Date(1972, 7, 18, 12, 34, 56, 0, time.UTC), + gomod: "module \"swtch.com/testmod\"\n", + }, + { + // redirect to googlesource + path: "golang.org/x/text", + rev: "4e4a3210bb", + version: "v0.3.1-0.20180208041248-4e4a3210bb54", + name: "4e4a3210bb54bb31f6ab2cdca2edcc0b50c420c1", + short: "4e4a3210bb54", + time: time.Date(2018, 2, 8, 4, 12, 48, 0, time.UTC), + }, + { + path: "github.com/pkg/errors", + rev: "v0.8.0", + version: "v0.8.0", + name: "645ef00459ed84a119197bfb8d8205042c6df63d", + short: "645ef00459ed", + time: time.Date(2016, 9, 29, 1, 48, 1, 0, time.UTC), + }, + { + // package in subdirectory - custom domain + // In general we can't reject these definitively in Lookup, + // but gopkg.in is special. + path: "gopkg.in/yaml.v2/abc", + lookerr: "invalid module path \"gopkg.in/yaml.v2/abc\"", + }, + { + // package in subdirectory - github + // Because it's a package, Stat should fail entirely. + path: "github.com/rsc/quote/buggy", + rev: "c4d4236f", + err: "missing github.com/rsc/quote/buggy/go.mod at revision c4d4236f9242", + }, + { + path: "gopkg.in/yaml.v2", + rev: "d670f940", + version: "v2.0.0", + name: "d670f9405373e636a5a2765eea47fac0c9bc91a4", + short: "d670f9405373", + time: time.Date(2018, 1, 9, 11, 43, 31, 0, time.UTC), + gomod: "module gopkg.in/yaml.v2\n", + }, + { + path: "gopkg.in/check.v1", + rev: "20d25e280405", + version: "v1.0.0-20161208181325-20d25e280405", + name: "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec", + short: "20d25e280405", + time: time.Date(2016, 12, 8, 18, 13, 25, 0, time.UTC), + gomod: "module gopkg.in/check.v1\n", + }, + { + path: "gopkg.in/yaml.v2", + rev: "v2", + version: "v2.2.1", + name: "5420a8b6744d3b0345ab293f6fcba19c978f1183", + short: "5420a8b6744d", + time: time.Date(2018, 3, 28, 19, 50, 20, 0, time.UTC), + gomod: "module \"gopkg.in/yaml.v2\"\n\nrequire (\n\t\"gopkg.in/check.v1\" v0.0.0-20161208181325-20d25e280405\n)\n", + }, + { + path: "vcs-test.golang.org/go/mod/gitrepo1", + rev: "master", + version: "v1.2.4-annotated", + name: "ede458df7cd0fdca520df19a33158086a8a68e81", + short: "ede458df7cd0", + time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC), + gomod: "module vcs-test.golang.org/go/mod/gitrepo1\n", + }, + { + path: "gopkg.in/natefinch/lumberjack.v2", + rev: "latest", + version: "v2.0.0-20170531160350-a96e63847dc3", + name: "a96e63847dc3c67d17befa69c303767e2f84e54f", + short: "a96e63847dc3", + time: time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC), + gomod: "module gopkg.in/natefinch/lumberjack.v2\n", + }, + { + path: "gopkg.in/natefinch/lumberjack.v2", + // This repo has a v2.1 tag. + // We only allow semver references to tags that are fully qualified, as in v2.1.0. + // Because we can't record v2.1.0 (the actual tag is v2.1), we record a pseudo-version + // instead, same as if the tag were any other non-version-looking string. + // We use a v2 pseudo-version here because of the .v2 in the path, not because + // of the v2 in the rev. + rev: "v2.1", // non-canonical semantic version turns into pseudo-version + version: "v2.0.0-20170531160350-a96e63847dc3", + name: "a96e63847dc3c67d17befa69c303767e2f84e54f", + short: "a96e63847dc3", + time: time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC), + gomod: "module gopkg.in/natefinch/lumberjack.v2\n", + }, +} + +func TestCodeRepo(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + for _, tt := range codeRepoTests { + f := func(t *testing.T) { + repo, err := Lookup(tt.path) + if tt.lookerr != "" { + if err != nil && err.Error() == tt.lookerr { + return + } + t.Errorf("Lookup(%q): %v, want error %q", tt.path, err, tt.lookerr) + } + if err != nil { + t.Fatalf("Lookup(%q): %v", tt.path, err) + } + if tt.mpath == "" { + tt.mpath = tt.path + } + if mpath := repo.ModulePath(); mpath != tt.mpath { + t.Errorf("repo.ModulePath() = %q, want %q", mpath, tt.mpath) + } + info, err := repo.Stat(tt.rev) + if err != nil { + if tt.err != "" { + if !strings.Contains(err.Error(), tt.err) { + t.Fatalf("repoStat(%q): %v, wanted %q", tt.rev, err, tt.err) + } + return + } + t.Fatalf("repo.Stat(%q): %v", tt.rev, err) + } + if tt.err != "" { + t.Errorf("repo.Stat(%q): success, wanted error", tt.rev) + } + if info.Version != tt.version { + t.Errorf("info.Version = %q, want %q", info.Version, tt.version) + } + if info.Name != tt.name { + t.Errorf("info.Name = %q, want %q", info.Name, tt.name) + } + if info.Short != tt.short { + t.Errorf("info.Short = %q, want %q", info.Short, tt.short) + } + if !info.Time.Equal(tt.time) { + t.Errorf("info.Time = %v, want %v", info.Time, tt.time) + } + if tt.gomod != "" || tt.gomoderr != "" { + data, err := repo.GoMod(tt.version) + if err != nil && tt.gomoderr == "" { + t.Errorf("repo.GoMod(%q): %v", tt.version, err) + } else if err != nil && tt.gomoderr != "" { + if err.Error() != tt.gomoderr { + t.Errorf("repo.GoMod(%q): %v, want %q", tt.version, err, tt.gomoderr) + } + } else if tt.gomoderr != "" { + t.Errorf("repo.GoMod(%q) = %q, want error %q", tt.version, data, tt.gomoderr) + } else if string(data) != tt.gomod { + t.Errorf("repo.GoMod(%q) = %q, want %q", tt.version, data, tt.gomod) + } + } + if tt.zip != nil || tt.ziperr != "" { + zipfile, err := repo.Zip(tt.version, tmpdir) + if err != nil { + if tt.ziperr != "" { + if err.Error() == tt.ziperr { + return + } + t.Fatalf("repo.Zip(%q): %v, want error %q", tt.version, err, tt.ziperr) + } + t.Fatalf("repo.Zip(%q): %v", tt.version, err) + } + if tt.ziperr != "" { + t.Errorf("repo.Zip(%q): success, want error %q", tt.version, tt.ziperr) + } + prefix := tt.path + "@" + tt.version + "/" + z, err := zip.OpenReader(zipfile) + if err != nil { + t.Fatalf("open zip %s: %v", zipfile, err) + } + var names []string + for _, file := range z.File { + if !strings.HasPrefix(file.Name, prefix) { + t.Errorf("zip entry %v does not start with prefix %v", file.Name, prefix) + continue + } + names = append(names, file.Name[len(prefix):]) + } + z.Close() + if !reflect.DeepEqual(names, tt.zip) { + t.Fatalf("zip = %v\nwant %v\n", names, tt.zip) + } + } + } + t.Run(strings.Replace(tt.path, "/", "_", -1)+"/"+tt.rev, f) + if strings.HasPrefix(tt.path, vgotest1git) { + for _, alt := range altVgotests { + // Note: Communicating with f through tt; should be cleaned up. + old := tt + tt.path = alt + strings.TrimPrefix(tt.path, vgotest1git) + if strings.HasPrefix(tt.mpath, vgotest1git) { + tt.mpath = alt + strings.TrimPrefix(tt.mpath, vgotest1git) + } + var m map[string]string + if alt == vgotest1hg { + m = hgmap + } + tt.version = remap(tt.version, m) + tt.name = remap(tt.name, m) + tt.short = remap(tt.short, m) + tt.rev = remap(tt.rev, m) + tt.gomoderr = remap(tt.gomoderr, m) + tt.ziperr = remap(tt.ziperr, m) + t.Run(strings.Replace(tt.path, "/", "_", -1)+"/"+tt.rev, f) + tt = old + } + } + } +} + +var hgmap = map[string]string{ + "github.com/rsc/vgotest1/": "vcs-test.golang.org/hg/vgotest1.hg/", + "f18795870fb14388a21ef3ebc1d75911c8694f31": "a9ad6d1d14eb544f459f446210c7eb3b009807c6", + "ea65f87c8f52c15ea68f3bdd9925ef17e20d91e9": "f1fc0f22021b638d073d31c752847e7bf385def7", + "b769f2de407a4db81af9c5de0a06016d60d2ea09": "92c7eb888b4fac17f1c6bd2e1060a1b881a3b832", + "8afe2b2efed96e0880ecd2a69b98a53b8c2738b6": "4e58084d459ae7e79c8c2264d0e8e9a92eb5cd44", + "2f615117ce481c8efef46e0cc0b4b4dccfac8fea": "879ea98f7743c8eff54f59a918f3a24123d1cf46", + "80d85c5d4d17598a0e9055e7c175a32b415d6128": "e125018e286a4b09061079a81e7b537070b7ff71", + "1f863feb76bc7029b78b21c5375644838962f88d": "bf63880162304a9337477f3858f5b7e255c75459", + "45f53230a74ad275c7127e117ac46914c8126160": "814fce58e83abd5bf2a13892e0b0e1198abefcd4", +} + +func remap(name string, m map[string]string) string { + if m[name] != "" { + return m[name] + } + if codehost.AllHex(name) { + for k, v := range m { + if strings.HasPrefix(k, name) { + return v[:len(name)] + } + } + } + for k, v := range m { + name = strings.Replace(name, k, v, -1) + if codehost.AllHex(k) { + name = strings.Replace(name, k[:12], v[:12], -1) + } + } + return name +} + +var codeRepoVersionsTests = []struct { + path string + prefix string + versions []string +}{ + { + path: "github.com/rsc/vgotest1", + versions: []string{"v0.0.0", "v0.0.1", "v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3", "v1.1.0", "v2.0.0+incompatible"}, + }, + { + path: "github.com/rsc/vgotest1", + prefix: "v1.0", + versions: []string{"v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3"}, + }, + { + path: "github.com/rsc/vgotest1/v2", + versions: []string{"v2.0.0", "v2.0.1", "v2.0.2", "v2.0.3", "v2.0.4", "v2.0.5", "v2.0.6"}, + }, + { + path: "swtch.com/testmod", + versions: []string{"v1.0.0", "v1.1.1"}, + }, + { + path: "gopkg.in/russross/blackfriday.v2", + versions: []string{"v2.0.0"}, + }, + { + path: "gopkg.in/natefinch/lumberjack.v2", + versions: nil, + }, +} + +func TestCodeRepoVersions(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + for _, tt := range codeRepoVersionsTests { + t.Run(strings.Replace(tt.path, "/", "_", -1), func(t *testing.T) { + repo, err := Lookup(tt.path) + if err != nil { + t.Fatalf("Lookup(%q): %v", tt.path, err) + } + list, err := repo.Versions(tt.prefix) + if err != nil { + t.Fatalf("Versions(%q): %v", tt.prefix, err) + } + if !reflect.DeepEqual(list, tt.versions) { + t.Fatalf("Versions(%q):\nhave %v\nwant %v", tt.prefix, list, tt.versions) + } + }) + } +} + +var latestTests = []struct { + path string + version string + err string +}{ + { + path: "github.com/rsc/empty", + err: "no commits", + }, + { + path: "github.com/rsc/vgotest1", + version: "v0.0.0-20180219223237-a08abb797a67", + }, + { + path: "github.com/rsc/vgotest1/subdir", + err: "missing github.com/rsc/vgotest1/subdir/go.mod at revision a08abb797a67", + }, + { + path: "swtch.com/testmod", + version: "v1.1.1", + }, +} + +func TestLatest(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + for _, tt := range latestTests { + name := strings.Replace(tt.path, "/", "_", -1) + t.Run(name, func(t *testing.T) { + repo, err := Lookup(tt.path) + if err != nil { + t.Fatalf("Lookup(%q): %v", tt.path, err) + } + info, err := repo.Latest() + if err != nil { + if tt.err != "" { + if err.Error() == tt.err { + return + } + t.Fatalf("Latest(): %v, want %q", err, tt.err) + } + t.Fatalf("Latest(): %v", err) + } + if tt.err != "" { + t.Fatalf("Latest() = %v, want error %q", info.Version, tt.err) + } + if info.Version != tt.version { + t.Fatalf("Latest() = %v, want %v", info.Version, tt.version) + } + }) + } +} + +// fixedTagsRepo is a fake codehost.Repo that returns a fixed list of tags +type fixedTagsRepo struct { + tags []string +} + +func (ch *fixedTagsRepo) Tags(string) ([]string, error) { return ch.tags, nil } +func (ch *fixedTagsRepo) Latest() (*codehost.RevInfo, error) { panic("not impl") } +func (ch *fixedTagsRepo) ReadFile(string, string, int64) ([]byte, error) { panic("not impl") } +func (ch *fixedTagsRepo) ReadFileRevs([]string, string, int64) (map[string]*codehost.FileRev, error) { + panic("not impl") +} +func (ch *fixedTagsRepo) ReadZip(string, string, int64) (io.ReadCloser, string, error) { + panic("not impl") +} +func (ch *fixedTagsRepo) RecentTag(string, string) (string, error) { + panic("not impl") +} +func (ch *fixedTagsRepo) Stat(string) (*codehost.RevInfo, error) { panic("not impl") } + +func TestNonCanonicalSemver(t *testing.T) { + root := "golang.org/x/issue24476" + ch := &fixedTagsRepo{ + tags: []string{ + "", "huh?", "1.0.1", + // what about "version 1 dot dogcow"? + "v1.🐕.🐄", + "v1", "v0.1", + // and one normal one that should pass through + "v1.0.1", + }, + } + + cr, err := newCodeRepo(ch, root, root) + if err != nil { + t.Fatal(err) + } + + v, err := cr.Versions("") + if err != nil { + t.Fatal(err) + } + if len(v) != 1 || v[0] != "v1.0.1" { + t.Fatal("unexpected versions returned:", v) + } +} diff --git a/libgo/go/cmd/go/internal/modfetch/fetch.go b/libgo/go/cmd/go/internal/modfetch/fetch.go new file mode 100644 index 0000000..2e26bac --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/fetch.go @@ -0,0 +1,365 @@ +// Copyright 2018 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 modfetch + +import ( + "archive/zip" + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + "sync" + + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/dirhash" + "cmd/go/internal/module" + "cmd/go/internal/par" +) + +var downloadCache par.Cache + +// Download downloads the specific module version to the +// local download cache and returns the name of the directory +// corresponding to the root of the module's file tree. +func Download(mod module.Version) (dir string, err error) { + if PkgMod == "" { + // Do not download to current directory. + return "", fmt.Errorf("missing modfetch.PkgMod") + } + + // The par.Cache here avoids duplicate work but also + // avoids conflicts from simultaneous calls by multiple goroutines + // for the same version. + type cached struct { + dir string + err error + } + c := downloadCache.Do(mod, func() interface{} { + dir, err := DownloadDir(mod) + if err != nil { + return cached{"", err} + } + if files, _ := ioutil.ReadDir(dir); len(files) == 0 { + zipfile, err := DownloadZip(mod) + if err != nil { + return cached{"", err} + } + modpath := mod.Path + "@" + mod.Version + if err := Unzip(dir, zipfile, modpath, 0); err != nil { + fmt.Fprintf(os.Stderr, "-> %s\n", err) + return cached{"", err} + } + } + checkSum(mod) + return cached{dir, nil} + }).(cached) + return c.dir, c.err +} + +var downloadZipCache par.Cache + +// DownloadZip downloads the specific module version to the +// local zip cache and returns the name of the zip file. +func DownloadZip(mod module.Version) (zipfile string, err error) { + // The par.Cache here avoids duplicate work but also + // avoids conflicts from simultaneous calls by multiple goroutines + // for the same version. + type cached struct { + zipfile string + err error + } + c := downloadZipCache.Do(mod, func() interface{} { + zipfile, err := CachePath(mod, "zip") + if err != nil { + return cached{"", err} + } + if _, err := os.Stat(zipfile); err == nil { + // Use it. + // This should only happen if the mod/cache directory is preinitialized + // or if pkg/mod/path was removed but not pkg/mod/cache/download. + if cfg.CmdName != "mod download" { + fmt.Fprintf(os.Stderr, "go: extracting %s %s\n", mod.Path, mod.Version) + } + } else { + if err := os.MkdirAll(filepath.Dir(zipfile), 0777); err != nil { + return cached{"", err} + } + if cfg.CmdName != "mod download" { + fmt.Fprintf(os.Stderr, "go: downloading %s %s\n", mod.Path, mod.Version) + } + if err := downloadZip(mod, zipfile); err != nil { + return cached{"", err} + } + } + return cached{zipfile, nil} + }).(cached) + return c.zipfile, c.err +} + +func downloadZip(mod module.Version, target string) error { + repo, err := Lookup(mod.Path) + if err != nil { + return err + } + tmpfile, err := repo.Zip(mod.Version, os.TempDir()) + if err != nil { + return err + } + defer os.Remove(tmpfile) + + // Double-check zip file looks OK. + z, err := zip.OpenReader(tmpfile) + if err != nil { + return err + } + prefix := mod.Path + "@" + mod.Version + for _, f := range z.File { + if !strings.HasPrefix(f.Name, prefix) { + z.Close() + return fmt.Errorf("zip for %s has unexpected file %s", prefix[:len(prefix)-1], f.Name) + } + } + z.Close() + + hash, err := dirhash.HashZip(tmpfile, dirhash.DefaultHash) + if err != nil { + return err + } + checkOneSum(mod, hash) // check before installing the zip file + r, err := os.Open(tmpfile) + if err != nil { + return err + } + defer r.Close() + w, err := os.Create(target) + if err != nil { + return err + } + if _, err := io.Copy(w, r); err != nil { + w.Close() + return fmt.Errorf("copying: %v", err) + } + if err := w.Close(); err != nil { + return err + } + return ioutil.WriteFile(target+"hash", []byte(hash), 0666) +} + +var GoSumFile string // path to go.sum; set by package modload + +var goSum struct { + mu sync.Mutex + m map[module.Version][]string // content of go.sum file (+ go.modverify if present) + enabled bool // whether to use go.sum at all + modverify string // path to go.modverify, to be deleted +} + +// initGoSum initializes the go.sum data. +// It reports whether use of go.sum is now enabled. +// The goSum lock must be held. +func initGoSum() bool { + if GoSumFile == "" { + return false + } + if goSum.m != nil { + return true + } + + goSum.m = make(map[module.Version][]string) + data, err := ioutil.ReadFile(GoSumFile) + if err != nil && !os.IsNotExist(err) { + base.Fatalf("go: %v", err) + } + goSum.enabled = true + readGoSum(GoSumFile, data) + + // Add old go.modverify file. + // We'll delete go.modverify in WriteGoSum. + alt := strings.TrimSuffix(GoSumFile, ".sum") + ".modverify" + if data, err := ioutil.ReadFile(alt); err == nil { + readGoSum(alt, data) + goSum.modverify = alt + } + return true +} + +// emptyGoModHash is the hash of a 1-file tree containing a 0-length go.mod. +// A bug caused us to write these into go.sum files for non-modules. +// We detect and remove them. +const emptyGoModHash = "h1:G7mAYYxgmS0lVkHyy2hEOLQCFB0DlQFTMLWggykrydY=" + +// readGoSum parses data, which is the content of file, +// and adds it to goSum.m. The goSum lock must be held. +func readGoSum(file string, data []byte) { + lineno := 0 + for len(data) > 0 { + var line []byte + lineno++ + i := bytes.IndexByte(data, '\n') + if i < 0 { + line, data = data, nil + } else { + line, data = data[:i], data[i+1:] + } + f := strings.Fields(string(line)) + if len(f) == 0 { + // blank line; skip it + continue + } + if len(f) != 3 { + base.Fatalf("go: malformed go.sum:\n%s:%d: wrong number of fields %v", file, lineno, len(f)) + } + if f[2] == emptyGoModHash { + // Old bug; drop it. + continue + } + mod := module.Version{Path: f[0], Version: f[1]} + goSum.m[mod] = append(goSum.m[mod], f[2]) + } +} + +// checkSum checks the given module's checksum. +func checkSum(mod module.Version) { + if PkgMod == "" { + // Do not use current directory. + return + } + + // Do the file I/O before acquiring the go.sum lock. + ziphash, err := CachePath(mod, "ziphash") + if err != nil { + base.Fatalf("go: verifying %s@%s: %v", mod.Path, mod.Version, err) + } + data, err := ioutil.ReadFile(ziphash) + if err != nil { + if os.IsNotExist(err) { + // This can happen if someone does rm -rf GOPATH/src/cache/download. So it goes. + return + } + base.Fatalf("go: verifying %s@%s: %v", mod.Path, mod.Version, err) + } + h := strings.TrimSpace(string(data)) + if !strings.HasPrefix(h, "h1:") { + base.Fatalf("go: verifying %s@%s: unexpected ziphash: %q", mod.Path, mod.Version, h) + } + + checkOneSum(mod, h) +} + +// goModSum returns the checksum for the go.mod contents. +func goModSum(data []byte) (string, error) { + return dirhash.Hash1([]string{"go.mod"}, func(string) (io.ReadCloser, error) { + return ioutil.NopCloser(bytes.NewReader(data)), nil + }) +} + +// checkGoMod checks the given module's go.mod checksum; +// data is the go.mod content. +func checkGoMod(path, version string, data []byte) { + h, err := goModSum(data) + if err != nil { + base.Fatalf("go: verifying %s %s go.mod: %v", path, version, err) + } + + checkOneSum(module.Version{Path: path, Version: version + "/go.mod"}, h) +} + +// checkOneSum checks that the recorded hash for mod is h. +func checkOneSum(mod module.Version, h string) { + goSum.mu.Lock() + defer goSum.mu.Unlock() + if !initGoSum() { + return + } + + for _, vh := range goSum.m[mod] { + if h == vh { + return + } + if strings.HasPrefix(vh, "h1:") { + base.Fatalf("go: verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\tgo.sum: %v", mod.Path, mod.Version, h, vh) + } + } + if len(goSum.m[mod]) > 0 { + fmt.Fprintf(os.Stderr, "warning: verifying %s@%s: unknown hashes in go.sum: %v; adding %v", mod.Path, mod.Version, strings.Join(goSum.m[mod], ", "), h) + } + goSum.m[mod] = append(goSum.m[mod], h) +} + +// Sum returns the checksum for the downloaded copy of the given module, +// if present in the download cache. +func Sum(mod module.Version) string { + if PkgMod == "" { + // Do not use current directory. + return "" + } + + ziphash, err := CachePath(mod, "ziphash") + if err != nil { + return "" + } + data, err := ioutil.ReadFile(ziphash) + if err != nil { + return "" + } + return strings.TrimSpace(string(data)) +} + +// WriteGoSum writes the go.sum file if it needs to be updated. +func WriteGoSum() { + goSum.mu.Lock() + defer goSum.mu.Unlock() + if !initGoSum() { + return + } + + var mods []module.Version + for m := range goSum.m { + mods = append(mods, m) + } + module.Sort(mods) + var buf bytes.Buffer + for _, m := range mods { + list := goSum.m[m] + sort.Strings(list) + for _, h := range list { + fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h) + } + } + + data, _ := ioutil.ReadFile(GoSumFile) + if !bytes.Equal(data, buf.Bytes()) { + if err := ioutil.WriteFile(GoSumFile, buf.Bytes(), 0666); err != nil { + base.Fatalf("go: writing go.sum: %v", err) + } + } + + if goSum.modverify != "" { + os.Remove(goSum.modverify) + } +} + +// TrimGoSum trims go.sum to contain only the modules for which keep[m] is true. +func TrimGoSum(keep map[module.Version]bool) { + goSum.mu.Lock() + defer goSum.mu.Unlock() + if !initGoSum() { + return + } + + for m := range goSum.m { + // If we're keeping x@v we also keep x@v/go.mod. + // Map x@v/go.mod back to x@v for the keep lookup. + noGoMod := module.Version{Path: m.Path, Version: strings.TrimSuffix(m.Version, "/go.mod")} + if !keep[m] && !keep[noGoMod] { + delete(goSum.m, m) + } + } +} diff --git a/libgo/go/cmd/go/internal/modfetch/noweb.go b/libgo/go/cmd/go/internal/modfetch/noweb.go new file mode 100644 index 0000000..9d713dc --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/noweb.go @@ -0,0 +1,24 @@ +// Copyright 2018 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 cmd_go_bootstrap + +package modfetch + +import ( + "fmt" + "io" +) + +func webGetGoGet(url string, body *io.ReadCloser) error { + return fmt.Errorf("no network in go_bootstrap") +} + +func webGetBytes(url string, body *[]byte) error { + return fmt.Errorf("no network in go_bootstrap") +} + +func webGetBody(url string, body *io.ReadCloser) error { + return fmt.Errorf("no network in go_bootstrap") +} diff --git a/libgo/go/cmd/go/internal/modfetch/proxy.go b/libgo/go/cmd/go/internal/modfetch/proxy.go new file mode 100644 index 0000000..5f856b8 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/proxy.go @@ -0,0 +1,252 @@ +// Copyright 2018 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 modfetch + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/url" + "os" + "strings" + "time" + + "cmd/go/internal/base" + "cmd/go/internal/modfetch/codehost" + "cmd/go/internal/module" + "cmd/go/internal/semver" +) + +var HelpGoproxy = &base.Command{ + UsageLine: "goproxy", + Short: "module proxy protocol", + Long: ` +The go command by default downloads modules from version control systems +directly, just as 'go get' always has. The GOPROXY environment variable allows +further control over the download source. If GOPROXY is unset, is the empty string, +or is the string "direct", downloads use the default direct connection to version +control systems. Setting GOPROXY to "off" disallows downloading modules from +any source. Otherwise, GOPROXY is expected to be the URL of a module proxy, +in which case the go command will fetch all modules from that proxy. +No matter the source of the modules, downloaded modules must match existing +entries in go.sum (see 'go help modules' for discussion of verification). + +A Go module proxy is any web server that can respond to GET requests for +URLs of a specified form. The requests have no query parameters, so even +a site serving from a fixed file system (including a file:/// URL) +can be a module proxy. + +The GET requests sent to a Go module proxy are: + +GET $GOPROXY/<module>/@v/list returns a list of all known versions of the +given module, one per line. + +GET $GOPROXY/<module>/@v/<version>.info returns JSON-formatted metadata +about that version of the given module. + +GET $GOPROXY/<module>/@v/<version>.mod returns the go.mod file +for that version of the given module. + +GET $GOPROXY/<module>/@v/<version>.zip returns the zip archive +for that version of the given module. + +To avoid problems when serving from case-sensitive file systems, +the <module> and <version> elements are case-encoded, replacing every +uppercase letter with an exclamation mark followed by the corresponding +lower-case letter: github.com/Azure encodes as github.com/!azure. + +The JSON-formatted metadata about a given module corresponds to +this Go data structure, which may be expanded in the future: + + type Info struct { + Version string // version string + Time time.Time // commit time + } + +The zip archive for a specific version of a given module is a +standard zip file that contains the file tree corresponding +to the module's source code and related files. The archive uses +slash-separated paths, and every file path in the archive must +begin with <module>@<version>/, where the module and version are +substituted directly, not case-encoded. The root of the module +file tree corresponds to the <module>@<version>/ prefix in the +archive. + +Even when downloading directly from version control systems, +the go command synthesizes explicit info, mod, and zip files +and stores them in its local cache, $GOPATH/pkg/mod/cache/download, +the same as if it had downloaded them directly from a proxy. +The cache layout is the same as the proxy URL space, so +serving $GOPATH/pkg/mod/cache/download at (or copying it to) +https://example.com/proxy would let other users access those +cached module versions with GOPROXY=https://example.com/proxy. +`, +} + +var proxyURL = os.Getenv("GOPROXY") + +func lookupProxy(path string) (Repo, error) { + if strings.Contains(proxyURL, ",") { + return nil, fmt.Errorf("invalid $GOPROXY setting: cannot have comma") + } + u, err := url.Parse(proxyURL) + if err != nil || u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "file" { + // Don't echo $GOPROXY back in case it has user:password in it (sigh). + return nil, fmt.Errorf("invalid $GOPROXY setting: malformed URL or invalid scheme (must be http, https, file)") + } + return newProxyRepo(u.String(), path) +} + +type proxyRepo struct { + url string + path string +} + +func newProxyRepo(baseURL, path string) (Repo, error) { + enc, err := module.EncodePath(path) + if err != nil { + return nil, err + } + return &proxyRepo{strings.TrimSuffix(baseURL, "/") + "/" + pathEscape(enc), path}, nil +} + +func (p *proxyRepo) ModulePath() string { + return p.path +} + +func (p *proxyRepo) Versions(prefix string) ([]string, error) { + var data []byte + err := webGetBytes(p.url+"/@v/list", &data) + if err != nil { + return nil, err + } + var list []string + for _, line := range strings.Split(string(data), "\n") { + f := strings.Fields(line) + if len(f) >= 1 && semver.IsValid(f[0]) && strings.HasPrefix(f[0], prefix) { + list = append(list, f[0]) + } + } + SortVersions(list) + return list, nil +} + +func (p *proxyRepo) latest() (*RevInfo, error) { + var data []byte + err := webGetBytes(p.url+"/@v/list", &data) + if err != nil { + return nil, err + } + var best time.Time + var bestVersion string + for _, line := range strings.Split(string(data), "\n") { + f := strings.Fields(line) + if len(f) >= 2 && semver.IsValid(f[0]) { + ft, err := time.Parse(time.RFC3339, f[1]) + if err == nil && best.Before(ft) { + best = ft + bestVersion = f[0] + } + } + } + if bestVersion == "" { + return nil, fmt.Errorf("no commits") + } + info := &RevInfo{ + Version: bestVersion, + Name: bestVersion, + Short: bestVersion, + Time: best, + } + return info, nil +} + +func (p *proxyRepo) Stat(rev string) (*RevInfo, error) { + var data []byte + encRev, err := module.EncodeVersion(rev) + if err != nil { + return nil, err + } + err = webGetBytes(p.url+"/@v/"+pathEscape(encRev)+".info", &data) + if err != nil { + return nil, err + } + info := new(RevInfo) + if err := json.Unmarshal(data, info); err != nil { + return nil, err + } + return info, nil +} + +func (p *proxyRepo) Latest() (*RevInfo, error) { + var data []byte + u := p.url + "/@latest" + err := webGetBytes(u, &data) + if err != nil { + // TODO return err if not 404 + return p.latest() + } + info := new(RevInfo) + if err := json.Unmarshal(data, info); err != nil { + return nil, err + } + return info, nil +} + +func (p *proxyRepo) GoMod(version string) ([]byte, error) { + var data []byte + encVer, err := module.EncodeVersion(version) + if err != nil { + return nil, err + } + err = webGetBytes(p.url+"/@v/"+pathEscape(encVer)+".mod", &data) + if err != nil { + return nil, err + } + return data, nil +} + +func (p *proxyRepo) Zip(version string, tmpdir string) (tmpfile string, err error) { + var body io.ReadCloser + encVer, err := module.EncodeVersion(version) + if err != nil { + return "", err + } + err = webGetBody(p.url+"/@v/"+pathEscape(encVer)+".zip", &body) + if err != nil { + return "", err + } + defer body.Close() + + // Spool to local file. + f, err := ioutil.TempFile(tmpdir, "go-proxy-download-") + if err != nil { + return "", err + } + defer f.Close() + maxSize := int64(codehost.MaxZipFile) + lr := &io.LimitedReader{R: body, N: maxSize + 1} + if _, err := io.Copy(f, lr); err != nil { + os.Remove(f.Name()) + return "", err + } + if lr.N <= 0 { + os.Remove(f.Name()) + return "", fmt.Errorf("downloaded zip file too large") + } + if err := f.Close(); err != nil { + os.Remove(f.Name()) + return "", err + } + return f.Name(), nil +} + +// pathEscape escapes s so it can be used in a path. +// That is, it escapes things like ? and # (which really shouldn't appear anyway). +// It does not escape / to %2F: our REST API is designed so that / can be left as is. +func pathEscape(s string) string { + return strings.Replace(url.PathEscape(s), "%2F", "/", -1) +} diff --git a/libgo/go/cmd/go/internal/modfetch/pseudo.go b/libgo/go/cmd/go/internal/modfetch/pseudo.go new file mode 100644 index 0000000..32c7bf8 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/pseudo.go @@ -0,0 +1,129 @@ +// Copyright 2018 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. + +// Pseudo-versions +// +// Code authors are expected to tag the revisions they want users to use, +// including prereleases. However, not all authors tag versions at all, +// and not all commits a user might want to try will have tags. +// A pseudo-version is a version with a special form that allows us to +// address an untagged commit and order that version with respect to +// other versions we might encounter. +// +// A pseudo-version takes one of the general forms: +// +// (1) vX.0.0-yyyymmddhhmmss-abcdef123456 +// (2) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456 +// (3) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456+incompatible +// (4) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456 +// (5) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456+incompatible +// +// If there is no recently tagged version with the right major version vX, +// then form (1) is used, creating a space of pseudo-versions at the bottom +// of the vX version range, less than any tagged version, including the unlikely v0.0.0. +// +// If the most recent tagged version before the target commit is vX.Y.Z or vX.Y.Z+incompatible, +// then the pseudo-version uses form (2) or (3), making it a prerelease for the next +// possible semantic version after vX.Y.Z. The leading 0 segment in the prerelease string +// ensures that the pseudo-version compares less than possible future explicit prereleases +// like vX.Y.(Z+1)-rc1 or vX.Y.(Z+1)-1. +// +// If the most recent tagged version before the target commit is vX.Y.Z-pre or vX.Y.Z-pre+incompatible, +// then the pseudo-version uses form (4) or (5), making it a slightly later prerelease. + +package modfetch + +import ( + "cmd/go/internal/semver" + "fmt" + "regexp" + "strings" + "time" +) + +// PseudoVersion returns a pseudo-version for the given major version ("v1") +// preexisting older tagged version ("" or "v1.2.3" or "v1.2.3-pre"), revision time, +// and revision identifier (usually a 12-byte commit hash prefix). +func PseudoVersion(major, older string, t time.Time, rev string) string { + if major == "" { + major = "v0" + } + major = strings.TrimSuffix(major, "-unstable") // make gopkg.in/macaroon-bakery.v2-unstable use "v2" + segment := fmt.Sprintf("%s-%s", t.UTC().Format("20060102150405"), rev) + build := semver.Build(older) + older = semver.Canonical(older) + if older == "" { + return major + ".0.0-" + segment // form (1) + } + if semver.Prerelease(older) != "" { + return older + ".0." + segment + build // form (4), (5) + } + + // Form (2), (3). + // Extract patch from vMAJOR.MINOR.PATCH + v := older[:len(older)] + i := strings.LastIndex(v, ".") + 1 + v, patch := v[:i], v[i:] + + // Increment PATCH by adding 1 to decimal: + // scan right to left turning 9s to 0s until you find a digit to increment. + // (Number might exceed int64, but math/big is overkill.) + digits := []byte(patch) + for i = len(digits) - 1; i >= 0 && digits[i] == '9'; i-- { + digits[i] = '0' + } + if i >= 0 { + digits[i]++ + } else { + // digits is all zeros + digits[0] = '1' + digits = append(digits, '0') + } + patch = string(digits) + + // Reassemble. + return v + patch + "-0." + segment + build +} + +var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+incompatible)?$`) + +// IsPseudoVersion reports whether v is a pseudo-version. +func IsPseudoVersion(v string) bool { + return strings.Count(v, "-") >= 2 && semver.IsValid(v) && pseudoVersionRE.MatchString(v) +} + +// PseudoVersionTime returns the time stamp of the pseudo-version v. +// It returns an error if v is not a pseudo-version or if the time stamp +// embedded in the pseudo-version is not a valid time. +func PseudoVersionTime(v string) (time.Time, error) { + timestamp, _, err := parsePseudoVersion(v) + t, err := time.Parse("20060102150405", timestamp) + if err != nil { + return time.Time{}, fmt.Errorf("pseudo-version with malformed time %s: %q", timestamp, v) + } + return t, nil +} + +// PseudoVersionRev returns the revision identifier of the pseudo-version v. +// It returns an error if v is not a pseudo-version. +func PseudoVersionRev(v string) (rev string, err error) { + _, rev, err = parsePseudoVersion(v) + return +} + +func parsePseudoVersion(v string) (timestamp, rev string, err error) { + if !IsPseudoVersion(v) { + return "", "", fmt.Errorf("malformed pseudo-version %q", v) + } + v = strings.TrimSuffix(v, "+incompatible") + j := strings.LastIndex(v, "-") + v, rev = v[:j], v[j+1:] + i := strings.LastIndex(v, "-") + if j := strings.LastIndex(v, "."); j > i { + timestamp = v[j+1:] + } else { + timestamp = v[i+1:] + } + return timestamp, rev, nil +} diff --git a/libgo/go/cmd/go/internal/modfetch/pseudo_test.go b/libgo/go/cmd/go/internal/modfetch/pseudo_test.go new file mode 100644 index 0000000..3c2fa51 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/pseudo_test.go @@ -0,0 +1,74 @@ +// Copyright 2018 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 modfetch + +import ( + "testing" + "time" +) + +var pseudoTests = []struct { + major string + older string + version string +}{ + {"", "", "v0.0.0-20060102150405-hash"}, + {"v0", "", "v0.0.0-20060102150405-hash"}, + {"v1", "", "v1.0.0-20060102150405-hash"}, + {"v2", "", "v2.0.0-20060102150405-hash"}, + {"unused", "v0.0.0", "v0.0.1-0.20060102150405-hash"}, + {"unused", "v1.2.3", "v1.2.4-0.20060102150405-hash"}, + {"unused", "v1.2.99999999999999999", "v1.2.100000000000000000-0.20060102150405-hash"}, + {"unused", "v1.2.3-pre", "v1.2.3-pre.0.20060102150405-hash"}, + {"unused", "v1.3.0-pre", "v1.3.0-pre.0.20060102150405-hash"}, +} + +var pseudoTime = time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC) + +func TestPseudoVersion(t *testing.T) { + for _, tt := range pseudoTests { + v := PseudoVersion(tt.major, tt.older, pseudoTime, "hash") + if v != tt.version { + t.Errorf("PseudoVersion(%q, %q, ...) = %v, want %v", tt.major, tt.older, v, tt.version) + } + } +} + +func TestIsPseudoVersion(t *testing.T) { + for _, tt := range pseudoTests { + if !IsPseudoVersion(tt.version) { + t.Errorf("IsPseudoVersion(%q) = false, want true", tt.version) + } + if IsPseudoVersion(tt.older) { + t.Errorf("IsPseudoVersion(%q) = true, want false", tt.older) + } + } +} + +func TestPseudoVersionTime(t *testing.T) { + for _, tt := range pseudoTests { + tm, err := PseudoVersionTime(tt.version) + if tm != pseudoTime || err != nil { + t.Errorf("PseudoVersionTime(%q) = %v, %v, want %v, nil", tt.version, tm.Format(time.RFC3339), err, pseudoTime.Format(time.RFC3339)) + } + tm, err = PseudoVersionTime(tt.older) + if tm != (time.Time{}) || err == nil { + t.Errorf("PseudoVersionTime(%q) = %v, %v, want %v, error", tt.older, tm.Format(time.RFC3339), err, time.Time{}.Format(time.RFC3339)) + } + } +} + +func TestPseudoVersionRev(t *testing.T) { + for _, tt := range pseudoTests { + rev, err := PseudoVersionRev(tt.version) + if rev != "hash" || err != nil { + t.Errorf("PseudoVersionRev(%q) = %q, %v, want %q, nil", tt.older, rev, err, "hash") + } + rev, err = PseudoVersionRev(tt.older) + if rev != "" || err == nil { + t.Errorf("PseudoVersionRev(%q) = %q, %v, want %q, error", tt.older, rev, err, "") + } + } +} diff --git a/libgo/go/cmd/go/internal/modfetch/repo.go b/libgo/go/cmd/go/internal/modfetch/repo.go new file mode 100644 index 0000000..0ea8c1f --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/repo.go @@ -0,0 +1,363 @@ +// Copyright 2018 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 modfetch + +import ( + "fmt" + "os" + "sort" + "time" + + "cmd/go/internal/cfg" + "cmd/go/internal/get" + "cmd/go/internal/modfetch/codehost" + "cmd/go/internal/par" + "cmd/go/internal/semver" + web "cmd/go/internal/web" +) + +const traceRepo = false // trace all repo actions, for debugging + +// A Repo represents a repository storing all versions of a single module. +// It must be safe for simultaneous use by multiple goroutines. +type Repo interface { + // ModulePath returns the module path. + ModulePath() string + + // Versions lists all known versions with the given prefix. + // Pseudo-versions are not included. + // Versions should be returned sorted in semver order + // (implementations can use SortVersions). + Versions(prefix string) (tags []string, err error) + + // Stat returns information about the revision rev. + // A revision can be any identifier known to the underlying service: + // commit hash, branch, tag, and so on. + Stat(rev string) (*RevInfo, error) + + // Latest returns the latest revision on the default branch, + // whatever that means in the underlying source code repository. + // It is only used when there are no tagged versions. + Latest() (*RevInfo, error) + + // GoMod returns the go.mod file for the given version. + GoMod(version string) (data []byte, err error) + + // Zip downloads a zip file for the given version + // to a new file in a given temporary directory. + // It returns the name of the new file. + // The caller should remove the file when finished with it. + Zip(version, tmpdir string) (tmpfile string, err error) +} + +// A Rev describes a single revision in a module repository. +type RevInfo struct { + Version string // version string + Time time.Time // commit time + + // These fields are used for Stat of arbitrary rev, + // but they are not recorded when talking about module versions. + Name string `json:"-"` // complete ID in underlying repository + Short string `json:"-"` // shortened ID, for use in pseudo-version +} + +// Re: module paths, import paths, repository roots, and lookups +// +// A module is a collection of Go packages stored in a file tree +// with a go.mod file at the root of the tree. +// The go.mod defines the module path, which is the import path +// corresponding to the root of the file tree. +// The import path of a directory within that file tree is the module path +// joined with the name of the subdirectory relative to the root. +// +// For example, the module with path rsc.io/qr corresponds to the +// file tree in the repository https://github.com/rsc/qr. +// That file tree has a go.mod that says "module rsc.io/qr". +// The package in the root directory has import path "rsc.io/qr". +// The package in the gf256 subdirectory has import path "rsc.io/qr/gf256". +// In this example, "rsc.io/qr" is both a module path and an import path. +// But "rsc.io/qr/gf256" is only an import path, not a module path: +// it names an importable package, but not a module. +// +// As a special case to incorporate code written before modules were +// introduced, if a path p resolves using the pre-module "go get" lookup +// to the root of a source code repository without a go.mod file, +// that repository is treated as if it had a go.mod in its root directory +// declaring module path p. (The go.mod is further considered to +// contain requirements corresponding to any legacy version +// tracking format such as Gopkg.lock, vendor/vendor.conf, and so on.) +// +// The presentation so far ignores the fact that a source code repository +// has many different versions of a file tree, and those versions may +// differ in whether a particular go.mod exists and what it contains. +// In fact there is a well-defined mapping only from a module path, version +// pair - often written path@version - to a particular file tree. +// For example rsc.io/qr@v0.1.0 depends on the "implicit go.mod at root of +// repository" rule, while rsc.io/qr@v0.2.0 has an explicit go.mod. +// Because the "go get" import paths rsc.io/qr and github.com/rsc/qr +// both redirect to the Git repository https://github.com/rsc/qr, +// github.com/rsc/qr@v0.1.0 is the same file tree as rsc.io/qr@v0.1.0 +// but a different module (a different name). In contrast, since v0.2.0 +// of that repository has an explicit go.mod that declares path rsc.io/qr, +// github.com/rsc/qr@v0.2.0 is an invalid module path, version pair. +// Before modules, import comments would have had the same effect. +// +// The set of import paths associated with a given module path is +// clearly not fixed: at the least, new directories with new import paths +// can always be added. But another potential operation is to split a +// subtree out of a module into its own module. If done carefully, +// this operation can be done while preserving compatibility for clients. +// For example, suppose that we want to split rsc.io/qr/gf256 into its +// own module, so that there would be two modules rsc.io/qr and rsc.io/qr/gf256. +// Then we can simultaneously issue rsc.io/qr v0.3.0 (dropping the gf256 subdirectory) +// and rsc.io/qr/gf256 v0.1.0, including in their respective go.mod +// cyclic requirements pointing at each other: rsc.io/qr v0.3.0 requires +// rsc.io/qr/gf256 v0.1.0 and vice versa. Then a build can be +// using an older rsc.io/qr module that includes the gf256 package, but if +// it adds a requirement on either the newer rsc.io/qr or the newer +// rsc.io/qr/gf256 module, it will automatically add the requirement +// on the complementary half, ensuring both that rsc.io/qr/gf256 is +// available for importing by the build and also that it is only defined +// by a single module. The gf256 package could move back into the +// original by another simultaneous release of rsc.io/qr v0.4.0 including +// the gf256 subdirectory and an rsc.io/qr/gf256 v0.2.0 with no code +// in its root directory, along with a new requirement cycle. +// The ability to shift module boundaries in this way is expected to be +// important in large-scale program refactorings, similar to the ones +// described in https://talks.golang.org/2016/refactor.article. +// +// The possibility of shifting module boundaries reemphasizes +// that you must know both the module path and its version +// to determine the set of packages provided directly by that module. +// +// On top of all this, it is possible for a single code repository +// to contain multiple modules, either in branches or subdirectories, +// as a limited kind of monorepo. For example rsc.io/qr/v2, +// the v2.x.x continuation of rsc.io/qr, is expected to be found +// in v2-tagged commits in https://github.com/rsc/qr, either +// in the root or in a v2 subdirectory, disambiguated by go.mod. +// Again the precise file tree corresponding to a module +// depends on which version we are considering. +// +// It is also possible for the underlying repository to change over time, +// without changing the module path. If I copy the github repo over +// to https://bitbucket.org/rsc/qr and update https://rsc.io/qr?go-get=1, +// then clients of all versions should start fetching from bitbucket +// instead of github. That is, in contrast to the exact file tree, +// the location of the source code repository associated with a module path +// does not depend on the module version. (This is by design, as the whole +// point of these redirects is to allow package authors to establish a stable +// name that can be updated as code moves from one service to another.) +// +// All of this is important background for the lookup APIs defined in this +// file. +// +// The Lookup function takes a module path and returns a Repo representing +// that module path. Lookup can do only a little with the path alone. +// It can check that the path is well-formed (see semver.CheckPath) +// and it can check that the path can be resolved to a target repository. +// To avoid version control access except when absolutely necessary, +// Lookup does not attempt to connect to the repository itself. +// +// The Import function takes an import path found in source code and +// determines which module to add to the requirement list to satisfy +// that import. It checks successive truncations of the import path +// to determine possible modules and stops when it finds a module +// in which the latest version satisfies the import path. +// +// The ImportRepoRev function is a variant of Import which is limited +// to code in a source code repository at a particular revision identifier +// (usually a commit hash or source code repository tag, not necessarily +// a module version). +// ImportRepoRev is used when converting legacy dependency requirements +// from older systems into go.mod files. Those older systems worked +// at either package or repository granularity, and most of the time they +// recorded commit hashes, not tagged versions. + +var lookupCache par.Cache + +// Lookup returns the module with the given module path. +// A successful return does not guarantee that the module +// has any defined versions. +func Lookup(path string) (Repo, error) { + if traceRepo { + defer logCall("Lookup(%q)", path)() + } + + type cached struct { + r Repo + err error + } + c := lookupCache.Do(path, func() interface{} { + r, err := lookup(path) + if err == nil { + if traceRepo { + r = newLoggingRepo(r) + } + r = newCachingRepo(r) + } + return cached{r, err} + }).(cached) + + return c.r, c.err +} + +// lookup returns the module with the given module path. +func lookup(path string) (r Repo, err error) { + if cfg.BuildMod == "vendor" { + return nil, fmt.Errorf("module lookup disabled by -mod=%s", cfg.BuildMod) + } + if proxyURL == "off" { + return nil, fmt.Errorf("module lookup disabled by GOPROXY=%s", proxyURL) + } + if proxyURL != "" && proxyURL != "direct" { + return lookupProxy(path) + } + + security := web.Secure + if get.Insecure { + security = web.Insecure + } + rr, err := get.RepoRootForImportPath(path, get.PreferMod, security) + if err != nil { + // We don't know where to find code for a module with this path. + return nil, err + } + + if rr.VCS == "mod" { + // Fetch module from proxy with base URL rr.Repo. + return newProxyRepo(rr.Repo, path) + } + + code, err := lookupCodeRepo(rr) + if err != nil { + return nil, err + } + return newCodeRepo(code, rr.Root, path) +} + +func lookupCodeRepo(rr *get.RepoRoot) (codehost.Repo, error) { + code, err := codehost.NewRepo(rr.VCS, rr.Repo) + if err != nil { + if _, ok := err.(*codehost.VCSError); ok { + return nil, err + } + return nil, fmt.Errorf("lookup %s: %v", rr.Root, err) + } + return code, nil +} + +// ImportRepoRev returns the module and version to use to access +// the given import path loaded from the source code repository that +// the original "go get" would have used, at the specific repository revision +// (typically a commit hash, but possibly also a source control tag). +func ImportRepoRev(path, rev string) (Repo, *RevInfo, error) { + if cfg.BuildMod == "vendor" || cfg.BuildMod == "readonly" { + return nil, nil, fmt.Errorf("repo version lookup disabled by -mod=%s", cfg.BuildMod) + } + + // Note: Because we are converting a code reference from a legacy + // version control system, we ignore meta tags about modules + // and use only direct source control entries (get.IgnoreMod). + security := web.Secure + if get.Insecure { + security = web.Insecure + } + rr, err := get.RepoRootForImportPath(path, get.IgnoreMod, security) + if err != nil { + return nil, nil, err + } + + code, err := lookupCodeRepo(rr) + if err != nil { + return nil, nil, err + } + + revInfo, err := code.Stat(rev) + if err != nil { + return nil, nil, err + } + + // TODO: Look in repo to find path, check for go.mod files. + // For now we're just assuming rr.Root is the module path, + // which is true in the absence of go.mod files. + + repo, err := newCodeRepo(code, rr.Root, rr.Root) + if err != nil { + return nil, nil, err + } + + info, err := repo.(*codeRepo).convert(revInfo, "") + if err != nil { + return nil, nil, err + } + return repo, info, nil +} + +func SortVersions(list []string) { + sort.Slice(list, func(i, j int) bool { + cmp := semver.Compare(list[i], list[j]) + if cmp != 0 { + return cmp < 0 + } + return list[i] < list[j] + }) +} + +// A loggingRepo is a wrapper around an underlying Repo +// that prints a log message at the start and end of each call. +// It can be inserted when debugging. +type loggingRepo struct { + r Repo +} + +func newLoggingRepo(r Repo) *loggingRepo { + return &loggingRepo{r} +} + +// logCall prints a log message using format and args and then +// also returns a function that will print the same message again, +// along with the elapsed time. +// Typical usage is: +// +// defer logCall("hello %s", arg)() +// +// Note the final (). +func logCall(format string, args ...interface{}) func() { + start := time.Now() + fmt.Fprintf(os.Stderr, "+++ %s\n", fmt.Sprintf(format, args...)) + return func() { + fmt.Fprintf(os.Stderr, "%.3fs %s\n", time.Since(start).Seconds(), fmt.Sprintf(format, args...)) + } +} + +func (l *loggingRepo) ModulePath() string { + return l.r.ModulePath() +} + +func (l *loggingRepo) Versions(prefix string) (tags []string, err error) { + defer logCall("Repo[%s]: Versions(%q)", l.r.ModulePath(), prefix)() + return l.r.Versions(prefix) +} + +func (l *loggingRepo) Stat(rev string) (*RevInfo, error) { + defer logCall("Repo[%s]: Stat(%q)", l.r.ModulePath(), rev)() + return l.r.Stat(rev) +} + +func (l *loggingRepo) Latest() (*RevInfo, error) { + defer logCall("Repo[%s]: Latest()", l.r.ModulePath())() + return l.r.Latest() +} + +func (l *loggingRepo) GoMod(version string) ([]byte, error) { + defer logCall("Repo[%s]: GoMod(%q)", l.r.ModulePath(), version)() + return l.r.GoMod(version) +} + +func (l *loggingRepo) Zip(version, tmpdir string) (string, error) { + defer logCall("Repo[%s]: Zip(%q, %q)", l.r.ModulePath(), version, tmpdir)() + return l.r.Zip(version, tmpdir) +} diff --git a/libgo/go/cmd/go/internal/modfetch/unzip.go b/libgo/go/cmd/go/internal/modfetch/unzip.go new file mode 100644 index 0000000..a50431f --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/unzip.go @@ -0,0 +1,153 @@ +// Copyright 2018 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 modfetch + +import ( + "archive/zip" + "fmt" + "io" + "io/ioutil" + "os" + "path" + "path/filepath" + "sort" + "strings" + + "cmd/go/internal/modfetch/codehost" + "cmd/go/internal/module" + "cmd/go/internal/str" +) + +func Unzip(dir, zipfile, prefix string, maxSize int64) error { + if maxSize == 0 { + maxSize = codehost.MaxZipFile + } + + // Directory can exist, but must be empty. + // except maybe + files, _ := ioutil.ReadDir(dir) + if len(files) > 0 { + return fmt.Errorf("target directory %v exists and is not empty", dir) + } + if err := os.MkdirAll(dir, 0777); err != nil { + return err + } + + f, err := os.Open(zipfile) + if err != nil { + return err + } + defer f.Close() + info, err := f.Stat() + if err != nil { + return err + } + + z, err := zip.NewReader(f, info.Size()) + if err != nil { + return fmt.Errorf("unzip %v: %s", zipfile, err) + } + + foldPath := make(map[string]string) + var checkFold func(string) error + checkFold = func(name string) error { + fold := str.ToFold(name) + if foldPath[fold] == name { + return nil + } + dir := path.Dir(name) + if dir != "." { + if err := checkFold(dir); err != nil { + return err + } + } + if foldPath[fold] == "" { + foldPath[fold] = name + return nil + } + other := foldPath[fold] + return fmt.Errorf("unzip %v: case-insensitive file name collision: %q and %q", zipfile, other, name) + } + + // Check total size, valid file names. + var size int64 + for _, zf := range z.File { + if !str.HasPathPrefix(zf.Name, prefix) { + return fmt.Errorf("unzip %v: unexpected file name %s", zipfile, zf.Name) + } + if zf.Name == prefix || strings.HasSuffix(zf.Name, "/") { + continue + } + name := zf.Name[len(prefix)+1:] + if err := module.CheckFilePath(name); err != nil { + return fmt.Errorf("unzip %v: %v", zipfile, err) + } + if err := checkFold(name); err != nil { + return err + } + if path.Clean(zf.Name) != zf.Name || strings.HasPrefix(zf.Name[len(prefix)+1:], "/") { + return fmt.Errorf("unzip %v: invalid file name %s", zipfile, zf.Name) + } + s := int64(zf.UncompressedSize64) + if s < 0 || maxSize-size < s { + return fmt.Errorf("unzip %v: content too large", zipfile) + } + size += s + } + + // Unzip, enforcing sizes checked earlier. + dirs := map[string]bool{dir: true} + for _, zf := range z.File { + if zf.Name == prefix || strings.HasSuffix(zf.Name, "/") { + continue + } + name := zf.Name[len(prefix):] + dst := filepath.Join(dir, name) + parent := filepath.Dir(dst) + for parent != dir { + dirs[parent] = true + parent = filepath.Dir(parent) + } + if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil { + return err + } + w, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0444) + if err != nil { + return fmt.Errorf("unzip %v: %v", zipfile, err) + } + r, err := zf.Open() + if err != nil { + w.Close() + return fmt.Errorf("unzip %v: %v", zipfile, err) + } + lr := &io.LimitedReader{R: r, N: int64(zf.UncompressedSize64) + 1} + _, err = io.Copy(w, lr) + r.Close() + if err != nil { + w.Close() + return fmt.Errorf("unzip %v: %v", zipfile, err) + } + if err := w.Close(); err != nil { + return fmt.Errorf("unzip %v: %v", zipfile, err) + } + if lr.N <= 0 { + return fmt.Errorf("unzip %v: content too large", zipfile) + } + } + + // Mark directories unwritable, best effort. + var dirlist []string + for dir := range dirs { + dirlist = append(dirlist, dir) + } + sort.Strings(dirlist) + + // Run over list backward to chmod children before parents. + for i := len(dirlist) - 1; i >= 0; i-- { + os.Chmod(dirlist[i], 0555) + } + + return nil +} diff --git a/libgo/go/cmd/go/internal/modfetch/web.go b/libgo/go/cmd/go/internal/modfetch/web.go new file mode 100644 index 0000000..b327bf2 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfetch/web.go @@ -0,0 +1,31 @@ +// Copyright 2018 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 !cmd_go_bootstrap + +package modfetch + +import ( + "io" + + web "cmd/go/internal/web2" +) + +// webGetGoGet fetches a go-get=1 URL and returns the body in *body. +// It allows non-200 responses, as usual for these URLs. +func webGetGoGet(url string, body *io.ReadCloser) error { + return web.Get(url, web.Non200OK(), web.Body(body)) +} + +// webGetBytes returns the body returned by an HTTP GET, as a []byte. +// It insists on a 200 response. +func webGetBytes(url string, body *[]byte) error { + return web.Get(url, web.ReadAllBody(body)) +} + +// webGetBody returns the body returned by an HTTP GET, as a io.ReadCloser. +// It insists on a 200 response. +func webGetBody(url string, body *io.ReadCloser) error { + return web.Get(url, web.Body(body)) +} diff --git a/libgo/go/cmd/go/internal/modfile/gopkgin.go b/libgo/go/cmd/go/internal/modfile/gopkgin.go new file mode 100644 index 0000000..c94b384 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/gopkgin.go @@ -0,0 +1,47 @@ +// Copyright 2018 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. + +// TODO: Figure out what gopkg.in should do. + +package modfile + +import "strings" + +// ParseGopkgIn splits gopkg.in import paths into their constituent parts +func ParseGopkgIn(path string) (root, repo, major, subdir string, ok bool) { + if !strings.HasPrefix(path, "gopkg.in/") { + return + } + f := strings.Split(path, "/") + if len(f) >= 2 { + if elem, v, ok := dotV(f[1]); ok { + root = strings.Join(f[:2], "/") + repo = "github.com/go-" + elem + "/" + elem + major = v + subdir = strings.Join(f[2:], "/") + return root, repo, major, subdir, true + } + } + if len(f) >= 3 { + if elem, v, ok := dotV(f[2]); ok { + root = strings.Join(f[:3], "/") + repo = "github.com/" + f[1] + "/" + elem + major = v + subdir = strings.Join(f[3:], "/") + return root, repo, major, subdir, true + } + } + return +} + +func dotV(name string) (elem, v string, ok bool) { + i := len(name) - 1 + for i >= 0 && '0' <= name[i] && name[i] <= '9' { + i-- + } + if i <= 2 || i+1 >= len(name) || name[i-1] != '.' || name[i] != 'v' || name[i+1] == '0' && len(name) != i+2 { + return "", "", false + } + return name[:i-1], name[i:], true +} diff --git a/libgo/go/cmd/go/internal/modfile/print.go b/libgo/go/cmd/go/internal/modfile/print.go new file mode 100644 index 0000000..cefc43b --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/print.go @@ -0,0 +1,164 @@ +// Copyright 2018 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. + +// Module file printer. + +package modfile + +import ( + "bytes" + "fmt" + "strings" +) + +func Format(f *FileSyntax) []byte { + pr := &printer{} + pr.file(f) + return pr.Bytes() +} + +// A printer collects the state during printing of a file or expression. +type printer struct { + bytes.Buffer // output buffer + comment []Comment // pending end-of-line comments + margin int // left margin (indent), a number of tabs +} + +// printf prints to the buffer. +func (p *printer) printf(format string, args ...interface{}) { + fmt.Fprintf(p, format, args...) +} + +// indent returns the position on the current line, in bytes, 0-indexed. +func (p *printer) indent() int { + b := p.Bytes() + n := 0 + for n < len(b) && b[len(b)-1-n] != '\n' { + n++ + } + return n +} + +// newline ends the current line, flushing end-of-line comments. +func (p *printer) newline() { + if len(p.comment) > 0 { + p.printf(" ") + for i, com := range p.comment { + if i > 0 { + p.trim() + p.printf("\n") + for i := 0; i < p.margin; i++ { + p.printf("\t") + } + } + p.printf("%s", strings.TrimSpace(com.Token)) + } + p.comment = p.comment[:0] + } + + p.trim() + p.printf("\n") + for i := 0; i < p.margin; i++ { + p.printf("\t") + } +} + +// trim removes trailing spaces and tabs from the current line. +func (p *printer) trim() { + // Remove trailing spaces and tabs from line we're about to end. + b := p.Bytes() + n := len(b) + for n > 0 && (b[n-1] == '\t' || b[n-1] == ' ') { + n-- + } + p.Truncate(n) +} + +// file formats the given file into the print buffer. +func (p *printer) file(f *FileSyntax) { + for _, com := range f.Before { + p.printf("%s", strings.TrimSpace(com.Token)) + p.newline() + } + + for i, stmt := range f.Stmt { + switch x := stmt.(type) { + case *CommentBlock: + // comments already handled + p.expr(x) + + default: + p.expr(x) + p.newline() + } + + for _, com := range stmt.Comment().After { + p.printf("%s", strings.TrimSpace(com.Token)) + p.newline() + } + + if i+1 < len(f.Stmt) { + p.newline() + } + } +} + +func (p *printer) expr(x Expr) { + // Emit line-comments preceding this expression. + if before := x.Comment().Before; len(before) > 0 { + // Want to print a line comment. + // Line comments must be at the current margin. + p.trim() + if p.indent() > 0 { + // There's other text on the line. Start a new line. + p.printf("\n") + } + // Re-indent to margin. + for i := 0; i < p.margin; i++ { + p.printf("\t") + } + for _, com := range before { + p.printf("%s", strings.TrimSpace(com.Token)) + p.newline() + } + } + + switch x := x.(type) { + default: + panic(fmt.Errorf("printer: unexpected type %T", x)) + + case *CommentBlock: + // done + + case *LParen: + p.printf("(") + case *RParen: + p.printf(")") + + case *Line: + sep := "" + for _, tok := range x.Token { + p.printf("%s%s", sep, tok) + sep = " " + } + + case *LineBlock: + for _, tok := range x.Token { + p.printf("%s ", tok) + } + p.expr(&x.LParen) + p.margin++ + for _, l := range x.Line { + p.newline() + p.expr(l) + } + p.margin-- + p.newline() + p.expr(&x.RParen) + } + + // Queue end-of-line comments for printing when we + // reach the end of the line. + p.comment = append(p.comment, x.Comment().Suffix...) +} diff --git a/libgo/go/cmd/go/internal/modfile/read.go b/libgo/go/cmd/go/internal/modfile/read.go new file mode 100644 index 0000000..1d81ff1 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/read.go @@ -0,0 +1,869 @@ +// Copyright 2018 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. + +// Module file parser. +// This is a simplified copy of Google's buildifier parser. + +package modfile + +import ( + "bytes" + "fmt" + "os" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +// A Position describes the position between two bytes of input. +type Position struct { + Line int // line in input (starting at 1) + LineRune int // rune in line (starting at 1) + Byte int // byte in input (starting at 0) +} + +// add returns the position at the end of s, assuming it starts at p. +func (p Position) add(s string) Position { + p.Byte += len(s) + if n := strings.Count(s, "\n"); n > 0 { + p.Line += n + s = s[strings.LastIndex(s, "\n")+1:] + p.LineRune = 1 + } + p.LineRune += utf8.RuneCountInString(s) + return p +} + +// An Expr represents an input element. +type Expr interface { + // Span returns the start and end position of the expression, + // excluding leading or trailing comments. + Span() (start, end Position) + + // Comment returns the comments attached to the expression. + // This method would normally be named 'Comments' but that + // would interfere with embedding a type of the same name. + Comment() *Comments +} + +// A Comment represents a single // comment. +type Comment struct { + Start Position + Token string // without trailing newline + Suffix bool // an end of line (not whole line) comment +} + +// Comments collects the comments associated with an expression. +type Comments struct { + Before []Comment // whole-line comments before this expression + Suffix []Comment // end-of-line comments after this expression + + // For top-level expressions only, After lists whole-line + // comments following the expression. + After []Comment +} + +// Comment returns the receiver. This isn't useful by itself, but +// a Comments struct is embedded into all the expression +// implementation types, and this gives each of those a Comment +// method to satisfy the Expr interface. +func (c *Comments) Comment() *Comments { + return c +} + +// A FileSyntax represents an entire go.mod file. +type FileSyntax struct { + Name string // file path + Comments + Stmt []Expr +} + +func (x *FileSyntax) Span() (start, end Position) { + if len(x.Stmt) == 0 { + return + } + start, _ = x.Stmt[0].Span() + _, end = x.Stmt[len(x.Stmt)-1].Span() + return start, end +} + +func (x *FileSyntax) addLine(hint Expr, tokens ...string) *Line { + if hint == nil { + // If no hint given, add to the last statement of the given type. + Loop: + for i := len(x.Stmt) - 1; i >= 0; i-- { + stmt := x.Stmt[i] + switch stmt := stmt.(type) { + case *Line: + if stmt.Token != nil && stmt.Token[0] == tokens[0] { + hint = stmt + break Loop + } + case *LineBlock: + if stmt.Token[0] == tokens[0] { + hint = stmt + break Loop + } + } + } + } + + if hint != nil { + for i, stmt := range x.Stmt { + switch stmt := stmt.(type) { + case *Line: + if stmt == hint { + // Convert line to line block. + stmt.InBlock = true + block := &LineBlock{Token: stmt.Token[:1], Line: []*Line{stmt}} + stmt.Token = stmt.Token[1:] + x.Stmt[i] = block + new := &Line{Token: tokens[1:], InBlock: true} + block.Line = append(block.Line, new) + return new + } + case *LineBlock: + if stmt == hint { + new := &Line{Token: tokens[1:], InBlock: true} + stmt.Line = append(stmt.Line, new) + return new + } + for j, line := range stmt.Line { + if line == hint { + // Add new line after hint. + stmt.Line = append(stmt.Line, nil) + copy(stmt.Line[j+2:], stmt.Line[j+1:]) + new := &Line{Token: tokens[1:], InBlock: true} + stmt.Line[j+1] = new + return new + } + } + } + } + } + + new := &Line{Token: tokens} + x.Stmt = append(x.Stmt, new) + return new +} + +func (x *FileSyntax) updateLine(line *Line, tokens ...string) { + if line.InBlock { + tokens = tokens[1:] + } + line.Token = tokens +} + +func (x *FileSyntax) removeLine(line *Line) { + line.Token = nil +} + +// Cleanup cleans up the file syntax x after any edit operations. +// To avoid quadratic behavior, removeLine marks the line as dead +// by setting line.Token = nil but does not remove it from the slice +// in which it appears. After edits have all been indicated, +// calling Cleanup cleans out the dead lines. +func (x *FileSyntax) Cleanup() { + w := 0 + for _, stmt := range x.Stmt { + switch stmt := stmt.(type) { + case *Line: + if stmt.Token == nil { + continue + } + case *LineBlock: + ww := 0 + for _, line := range stmt.Line { + if line.Token != nil { + stmt.Line[ww] = line + ww++ + } + } + if ww == 0 { + continue + } + if ww == 1 { + // Collapse block into single line. + line := &Line{ + Comments: Comments{ + Before: commentsAdd(stmt.Before, stmt.Line[0].Before), + Suffix: commentsAdd(stmt.Line[0].Suffix, stmt.Suffix), + After: commentsAdd(stmt.Line[0].After, stmt.After), + }, + Token: stringsAdd(stmt.Token, stmt.Line[0].Token), + } + x.Stmt[w] = line + w++ + continue + } + stmt.Line = stmt.Line[:ww] + } + x.Stmt[w] = stmt + w++ + } + x.Stmt = x.Stmt[:w] +} + +func commentsAdd(x, y []Comment) []Comment { + return append(x[:len(x):len(x)], y...) +} + +func stringsAdd(x, y []string) []string { + return append(x[:len(x):len(x)], y...) +} + +// A CommentBlock represents a top-level block of comments separate +// from any rule. +type CommentBlock struct { + Comments + Start Position +} + +func (x *CommentBlock) Span() (start, end Position) { + return x.Start, x.Start +} + +// A Line is a single line of tokens. +type Line struct { + Comments + Start Position + Token []string + InBlock bool + End Position +} + +func (x *Line) Span() (start, end Position) { + return x.Start, x.End +} + +// A LineBlock is a factored block of lines, like +// +// require ( +// "x" +// "y" +// ) +// +type LineBlock struct { + Comments + Start Position + LParen LParen + Token []string + Line []*Line + RParen RParen +} + +func (x *LineBlock) Span() (start, end Position) { + return x.Start, x.RParen.Pos.add(")") +} + +// An LParen represents the beginning of a parenthesized line block. +// It is a place to store suffix comments. +type LParen struct { + Comments + Pos Position +} + +func (x *LParen) Span() (start, end Position) { + return x.Pos, x.Pos.add(")") +} + +// An RParen represents the end of a parenthesized line block. +// It is a place to store whole-line (before) comments. +type RParen struct { + Comments + Pos Position +} + +func (x *RParen) Span() (start, end Position) { + return x.Pos, x.Pos.add(")") +} + +// An input represents a single input file being parsed. +type input struct { + // Lexing state. + filename string // name of input file, for errors + complete []byte // entire input + remaining []byte // remaining input + token []byte // token being scanned + lastToken string // most recently returned token, for error messages + pos Position // current input position + comments []Comment // accumulated comments + endRule int // position of end of current rule + + // Parser state. + file *FileSyntax // returned top-level syntax tree + parseError error // error encountered during parsing + + // Comment assignment state. + pre []Expr // all expressions, in preorder traversal + post []Expr // all expressions, in postorder traversal +} + +func newInput(filename string, data []byte) *input { + return &input{ + filename: filename, + complete: data, + remaining: data, + pos: Position{Line: 1, LineRune: 1, Byte: 0}, + } +} + +// parse parses the input file. +func parse(file string, data []byte) (f *FileSyntax, err error) { + in := newInput(file, data) + // The parser panics for both routine errors like syntax errors + // and for programmer bugs like array index errors. + // Turn both into error returns. Catching bug panics is + // especially important when processing many files. + defer func() { + if e := recover(); e != nil { + if e == in.parseError { + err = in.parseError + } else { + err = fmt.Errorf("%s:%d:%d: internal error: %v", in.filename, in.pos.Line, in.pos.LineRune, e) + } + } + }() + + // Invoke the parser. + in.parseFile() + if in.parseError != nil { + return nil, in.parseError + } + in.file.Name = in.filename + + // Assign comments to nearby syntax. + in.assignComments() + + return in.file, nil +} + +// Error is called to report an error. +// The reason s is often "syntax error". +// Error does not return: it panics. +func (in *input) Error(s string) { + if s == "syntax error" && in.lastToken != "" { + s += " near " + in.lastToken + } + in.parseError = fmt.Errorf("%s:%d:%d: %v", in.filename, in.pos.Line, in.pos.LineRune, s) + panic(in.parseError) +} + +// eof reports whether the input has reached end of file. +func (in *input) eof() bool { + return len(in.remaining) == 0 +} + +// peekRune returns the next rune in the input without consuming it. +func (in *input) peekRune() int { + if len(in.remaining) == 0 { + return 0 + } + r, _ := utf8.DecodeRune(in.remaining) + return int(r) +} + +// peekPrefix reports whether the remaining input begins with the given prefix. +func (in *input) peekPrefix(prefix string) bool { + // This is like bytes.HasPrefix(in.remaining, []byte(prefix)) + // but without the allocation of the []byte copy of prefix. + for i := 0; i < len(prefix); i++ { + if i >= len(in.remaining) || in.remaining[i] != prefix[i] { + return false + } + } + return true +} + +// readRune consumes and returns the next rune in the input. +func (in *input) readRune() int { + if len(in.remaining) == 0 { + in.Error("internal lexer error: readRune at EOF") + } + r, size := utf8.DecodeRune(in.remaining) + in.remaining = in.remaining[size:] + if r == '\n' { + in.pos.Line++ + in.pos.LineRune = 1 + } else { + in.pos.LineRune++ + } + in.pos.Byte += size + return int(r) +} + +type symType struct { + pos Position + endPos Position + text string +} + +// startToken marks the beginning of the next input token. +// It must be followed by a call to endToken, once the token has +// been consumed using readRune. +func (in *input) startToken(sym *symType) { + in.token = in.remaining + sym.text = "" + sym.pos = in.pos +} + +// endToken marks the end of an input token. +// It records the actual token string in sym.text if the caller +// has not done that already. +func (in *input) endToken(sym *symType) { + if sym.text == "" { + tok := string(in.token[:len(in.token)-len(in.remaining)]) + sym.text = tok + in.lastToken = sym.text + } + sym.endPos = in.pos +} + +// lex is called from the parser to obtain the next input token. +// It returns the token value (either a rune like '+' or a symbolic token _FOR) +// and sets val to the data associated with the token. +// For all our input tokens, the associated data is +// val.Pos (the position where the token begins) +// and val.Token (the input string corresponding to the token). +func (in *input) lex(sym *symType) int { + // Skip past spaces, stopping at non-space or EOF. + countNL := 0 // number of newlines we've skipped past + for !in.eof() { + // Skip over spaces. Count newlines so we can give the parser + // information about where top-level blank lines are, + // for top-level comment assignment. + c := in.peekRune() + if c == ' ' || c == '\t' || c == '\r' { + in.readRune() + continue + } + + // Comment runs to end of line. + if in.peekPrefix("//") { + in.startToken(sym) + + // Is this comment the only thing on its line? + // Find the last \n before this // and see if it's all + // spaces from there to here. + i := bytes.LastIndex(in.complete[:in.pos.Byte], []byte("\n")) + suffix := len(bytes.TrimSpace(in.complete[i+1:in.pos.Byte])) > 0 + in.readRune() + in.readRune() + + // Consume comment. + for len(in.remaining) > 0 && in.readRune() != '\n' { + } + in.endToken(sym) + + sym.text = strings.TrimRight(sym.text, "\n") + in.lastToken = "comment" + + // If we are at top level (not in a statement), hand the comment to + // the parser as a _COMMENT token. The grammar is written + // to handle top-level comments itself. + if !suffix { + // Not in a statement. Tell parser about top-level comment. + return _COMMENT + } + + // Otherwise, save comment for later attachment to syntax tree. + if countNL > 1 { + in.comments = append(in.comments, Comment{sym.pos, "", false}) + } + in.comments = append(in.comments, Comment{sym.pos, sym.text, suffix}) + countNL = 1 + return _EOL + } + + if in.peekPrefix("/*") { + in.Error(fmt.Sprintf("mod files must use // comments (not /* */ comments)")) + } + + // Found non-space non-comment. + break + } + + // Found the beginning of the next token. + in.startToken(sym) + defer in.endToken(sym) + + // End of file. + if in.eof() { + in.lastToken = "EOF" + return _EOF + } + + // Punctuation tokens. + switch c := in.peekRune(); c { + case '\n': + in.readRune() + return c + + case '(': + in.readRune() + return c + + case ')': + in.readRune() + return c + + case '"', '`': // quoted string + quote := c + in.readRune() + for { + if in.eof() { + in.pos = sym.pos + in.Error("unexpected EOF in string") + } + if in.peekRune() == '\n' { + in.Error("unexpected newline in string") + } + c := in.readRune() + if c == quote { + break + } + if c == '\\' && quote != '`' { + if in.eof() { + in.pos = sym.pos + in.Error("unexpected EOF in string") + } + in.readRune() + } + } + in.endToken(sym) + return _STRING + } + + // Checked all punctuation. Must be identifier token. + if c := in.peekRune(); !isIdent(c) { + in.Error(fmt.Sprintf("unexpected input character %#q", c)) + } + + // Scan over identifier. + for isIdent(in.peekRune()) { + if in.peekPrefix("//") { + break + } + if in.peekPrefix("/*") { + in.Error(fmt.Sprintf("mod files must use // comments (not /* */ comments)")) + } + in.readRune() + } + return _IDENT +} + +// isIdent reports whether c is an identifier rune. +// We treat nearly all runes as identifier runes. +func isIdent(c int) bool { + return c != 0 && !unicode.IsSpace(rune(c)) +} + +// Comment assignment. +// We build two lists of all subexpressions, preorder and postorder. +// The preorder list is ordered by start location, with outer expressions first. +// The postorder list is ordered by end location, with outer expressions last. +// We use the preorder list to assign each whole-line comment to the syntax +// immediately following it, and we use the postorder list to assign each +// end-of-line comment to the syntax immediately preceding it. + +// order walks the expression adding it and its subexpressions to the +// preorder and postorder lists. +func (in *input) order(x Expr) { + if x != nil { + in.pre = append(in.pre, x) + } + switch x := x.(type) { + default: + panic(fmt.Errorf("order: unexpected type %T", x)) + case nil: + // nothing + case *LParen, *RParen: + // nothing + case *CommentBlock: + // nothing + case *Line: + // nothing + case *FileSyntax: + for _, stmt := range x.Stmt { + in.order(stmt) + } + case *LineBlock: + in.order(&x.LParen) + for _, l := range x.Line { + in.order(l) + } + in.order(&x.RParen) + } + if x != nil { + in.post = append(in.post, x) + } +} + +// assignComments attaches comments to nearby syntax. +func (in *input) assignComments() { + const debug = false + + // Generate preorder and postorder lists. + in.order(in.file) + + // Split into whole-line comments and suffix comments. + var line, suffix []Comment + for _, com := range in.comments { + if com.Suffix { + suffix = append(suffix, com) + } else { + line = append(line, com) + } + } + + if debug { + for _, c := range line { + fmt.Fprintf(os.Stderr, "LINE %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte) + } + } + + // Assign line comments to syntax immediately following. + for _, x := range in.pre { + start, _ := x.Span() + if debug { + fmt.Printf("pre %T :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte) + } + xcom := x.Comment() + for len(line) > 0 && start.Byte >= line[0].Start.Byte { + if debug { + fmt.Fprintf(os.Stderr, "ASSIGN LINE %q #%d\n", line[0].Token, line[0].Start.Byte) + } + xcom.Before = append(xcom.Before, line[0]) + line = line[1:] + } + } + + // Remaining line comments go at end of file. + in.file.After = append(in.file.After, line...) + + if debug { + for _, c := range suffix { + fmt.Fprintf(os.Stderr, "SUFFIX %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte) + } + } + + // Assign suffix comments to syntax immediately before. + for i := len(in.post) - 1; i >= 0; i-- { + x := in.post[i] + + start, end := x.Span() + if debug { + fmt.Printf("post %T :%d:%d #%d :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte, end.Line, end.LineRune, end.Byte) + } + + // Do not assign suffix comments to end of line block or whole file. + // Instead assign them to the last element inside. + switch x.(type) { + case *FileSyntax: + continue + } + + // Do not assign suffix comments to something that starts + // on an earlier line, so that in + // + // x ( y + // z ) // comment + // + // we assign the comment to z and not to x ( ... ). + if start.Line != end.Line { + continue + } + xcom := x.Comment() + for len(suffix) > 0 && end.Byte <= suffix[len(suffix)-1].Start.Byte { + if debug { + fmt.Fprintf(os.Stderr, "ASSIGN SUFFIX %q #%d\n", suffix[len(suffix)-1].Token, suffix[len(suffix)-1].Start.Byte) + } + xcom.Suffix = append(xcom.Suffix, suffix[len(suffix)-1]) + suffix = suffix[:len(suffix)-1] + } + } + + // We assigned suffix comments in reverse. + // If multiple suffix comments were appended to the same + // expression node, they are now in reverse. Fix that. + for _, x := range in.post { + reverseComments(x.Comment().Suffix) + } + + // Remaining suffix comments go at beginning of file. + in.file.Before = append(in.file.Before, suffix...) +} + +// reverseComments reverses the []Comment list. +func reverseComments(list []Comment) { + for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 { + list[i], list[j] = list[j], list[i] + } +} + +func (in *input) parseFile() { + in.file = new(FileSyntax) + var sym symType + var cb *CommentBlock + for { + tok := in.lex(&sym) + switch tok { + case '\n': + if cb != nil { + in.file.Stmt = append(in.file.Stmt, cb) + cb = nil + } + case _COMMENT: + if cb == nil { + cb = &CommentBlock{Start: sym.pos} + } + com := cb.Comment() + com.Before = append(com.Before, Comment{Start: sym.pos, Token: sym.text}) + case _EOF: + if cb != nil { + in.file.Stmt = append(in.file.Stmt, cb) + } + return + default: + in.parseStmt(&sym) + if cb != nil { + in.file.Stmt[len(in.file.Stmt)-1].Comment().Before = cb.Before + cb = nil + } + } + } +} + +func (in *input) parseStmt(sym *symType) { + start := sym.pos + end := sym.endPos + token := []string{sym.text} + for { + tok := in.lex(sym) + switch tok { + case '\n', _EOF, _EOL: + in.file.Stmt = append(in.file.Stmt, &Line{ + Start: start, + Token: token, + End: end, + }) + return + case '(': + in.file.Stmt = append(in.file.Stmt, in.parseLineBlock(start, token, sym)) + return + default: + token = append(token, sym.text) + end = sym.endPos + } + } +} + +func (in *input) parseLineBlock(start Position, token []string, sym *symType) *LineBlock { + x := &LineBlock{ + Start: start, + Token: token, + LParen: LParen{Pos: sym.pos}, + } + var comments []Comment + for { + tok := in.lex(sym) + switch tok { + case _EOL: + // ignore + case '\n': + if len(comments) == 0 && len(x.Line) > 0 || len(comments) > 0 && comments[len(comments)-1].Token != "" { + comments = append(comments, Comment{}) + } + case _COMMENT: + comments = append(comments, Comment{Start: sym.pos, Token: sym.text}) + case _EOF: + in.Error(fmt.Sprintf("syntax error (unterminated block started at %s:%d:%d)", in.filename, x.Start.Line, x.Start.LineRune)) + case ')': + x.RParen.Before = comments + x.RParen.Pos = sym.pos + tok = in.lex(sym) + if tok != '\n' && tok != _EOF && tok != _EOL { + in.Error("syntax error (expected newline after closing paren)") + } + return x + default: + l := in.parseLine(sym) + x.Line = append(x.Line, l) + l.Comment().Before = comments + comments = nil + } + } +} + +func (in *input) parseLine(sym *symType) *Line { + start := sym.pos + end := sym.endPos + token := []string{sym.text} + for { + tok := in.lex(sym) + switch tok { + case '\n', _EOF, _EOL: + return &Line{ + Start: start, + Token: token, + End: end, + InBlock: true, + } + default: + token = append(token, sym.text) + end = sym.endPos + } + } +} + +const ( + _EOF = -(1 + iota) + _EOL + _IDENT + _STRING + _COMMENT +) + +var ( + slashSlash = []byte("//") + moduleStr = []byte("module") +) + +// ModulePath returns the module path from the gomod file text. +// If it cannot find a module path, it returns an empty string. +// It is tolerant of unrelated problems in the go.mod file. +func ModulePath(mod []byte) string { + for len(mod) > 0 { + line := mod + mod = nil + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, mod = line[:i], line[i+1:] + } + if i := bytes.Index(line, slashSlash); i >= 0 { + line = line[:i] + } + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, moduleStr) { + continue + } + line = line[len(moduleStr):] + n := len(line) + line = bytes.TrimSpace(line) + if len(line) == n || len(line) == 0 { + continue + } + + if line[0] == '"' || line[0] == '`' { + p, err := strconv.Unquote(string(line)) + if err != nil { + return "" // malformed quoted string or multiline module path + } + return p + } + + return string(line) + } + return "" // missing module path +} diff --git a/libgo/go/cmd/go/internal/modfile/read_test.go b/libgo/go/cmd/go/internal/modfile/read_test.go new file mode 100644 index 0000000..8cb1a39 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/read_test.go @@ -0,0 +1,365 @@ +// Copyright 2018 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 modfile + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "reflect" + "strings" + "testing" +) + +// exists reports whether the named file exists. +func exists(name string) bool { + _, err := os.Stat(name) + return err == nil +} + +// Test that reading and then writing the golden files +// does not change their output. +func TestPrintGolden(t *testing.T) { + outs, err := filepath.Glob("testdata/*.golden") + if err != nil { + t.Fatal(err) + } + for _, out := range outs { + testPrint(t, out, out) + } +} + +// testPrint is a helper for testing the printer. +// It reads the file named in, reformats it, and compares +// the result to the file named out. +func testPrint(t *testing.T, in, out string) { + data, err := ioutil.ReadFile(in) + if err != nil { + t.Error(err) + return + } + + golden, err := ioutil.ReadFile(out) + if err != nil { + t.Error(err) + return + } + + base := "testdata/" + filepath.Base(in) + f, err := parse(in, data) + if err != nil { + t.Error(err) + return + } + + ndata := Format(f) + + if !bytes.Equal(ndata, golden) { + t.Errorf("formatted %s incorrectly: diff shows -golden, +ours", base) + tdiff(t, string(golden), string(ndata)) + return + } +} + +func TestParseLax(t *testing.T) { + badFile := []byte(`module m + surprise attack + x y ( + z + ) + exclude v1.2.3 + replace <-!!! + `) + _, err := ParseLax("file", badFile, nil) + if err != nil { + t.Fatalf("ParseLax did not ignore irrelevant errors: %v", err) + } +} + +// Test that when files in the testdata directory are parsed +// and printed and parsed again, we get the same parse tree +// both times. +func TestPrintParse(t *testing.T) { + outs, err := filepath.Glob("testdata/*") + if err != nil { + t.Fatal(err) + } + for _, out := range outs { + data, err := ioutil.ReadFile(out) + if err != nil { + t.Error(err) + continue + } + + base := "testdata/" + filepath.Base(out) + f, err := parse(base, data) + if err != nil { + t.Errorf("parsing original: %v", err) + continue + } + + ndata := Format(f) + f2, err := parse(base, ndata) + if err != nil { + t.Errorf("parsing reformatted: %v", err) + continue + } + + eq := eqchecker{file: base} + if err := eq.check(f, f2); err != nil { + t.Errorf("not equal (parse/Format/parse): %v", err) + } + + pf1, err := Parse(base, data, nil) + if err != nil { + switch base { + case "testdata/replace2.in", "testdata/gopkg.in.golden": + t.Errorf("should parse %v: %v", base, err) + } + } + if err == nil { + pf2, err := Parse(base, ndata, nil) + if err != nil { + t.Errorf("Parsing reformatted: %v", err) + continue + } + eq := eqchecker{file: base} + if err := eq.check(pf1, pf2); err != nil { + t.Errorf("not equal (parse/Format/Parse): %v", err) + } + + ndata2, err := pf1.Format() + if err != nil { + t.Errorf("reformat: %v", err) + } + pf3, err := Parse(base, ndata2, nil) + if err != nil { + t.Errorf("Parsing reformatted2: %v", err) + continue + } + eq = eqchecker{file: base} + if err := eq.check(pf1, pf3); err != nil { + t.Errorf("not equal (Parse/Format/Parse): %v", err) + } + ndata = ndata2 + } + + if strings.HasSuffix(out, ".in") { + golden, err := ioutil.ReadFile(strings.TrimSuffix(out, ".in") + ".golden") + if err != nil { + t.Error(err) + continue + } + if !bytes.Equal(ndata, golden) { + t.Errorf("formatted %s incorrectly: diff shows -golden, +ours", base) + tdiff(t, string(golden), string(ndata)) + return + } + } + } +} + +// An eqchecker holds state for checking the equality of two parse trees. +type eqchecker struct { + file string + pos Position +} + +// errorf returns an error described by the printf-style format and arguments, +// inserting the current file position before the error text. +func (eq *eqchecker) errorf(format string, args ...interface{}) error { + return fmt.Errorf("%s:%d: %s", eq.file, eq.pos.Line, + fmt.Sprintf(format, args...)) +} + +// check checks that v and w represent the same parse tree. +// If not, it returns an error describing the first difference. +func (eq *eqchecker) check(v, w interface{}) error { + return eq.checkValue(reflect.ValueOf(v), reflect.ValueOf(w)) +} + +var ( + posType = reflect.TypeOf(Position{}) + commentsType = reflect.TypeOf(Comments{}) +) + +// checkValue checks that v and w represent the same parse tree. +// If not, it returns an error describing the first difference. +func (eq *eqchecker) checkValue(v, w reflect.Value) error { + // inner returns the innermost expression for v. + // if v is a non-nil interface value, it returns the concrete + // value in the interface. + inner := func(v reflect.Value) reflect.Value { + for { + if v.Kind() == reflect.Interface && !v.IsNil() { + v = v.Elem() + continue + } + break + } + return v + } + + v = inner(v) + w = inner(w) + if v.Kind() == reflect.Invalid && w.Kind() == reflect.Invalid { + return nil + } + if v.Kind() == reflect.Invalid { + return eq.errorf("nil interface became %s", w.Type()) + } + if w.Kind() == reflect.Invalid { + return eq.errorf("%s became nil interface", v.Type()) + } + + if v.Type() != w.Type() { + return eq.errorf("%s became %s", v.Type(), w.Type()) + } + + if p, ok := v.Interface().(Expr); ok { + eq.pos, _ = p.Span() + } + + switch v.Kind() { + default: + return eq.errorf("unexpected type %s", v.Type()) + + case reflect.Bool, reflect.Int, reflect.String: + vi := v.Interface() + wi := w.Interface() + if vi != wi { + return eq.errorf("%v became %v", vi, wi) + } + + case reflect.Slice: + vl := v.Len() + wl := w.Len() + for i := 0; i < vl || i < wl; i++ { + if i >= vl { + return eq.errorf("unexpected %s", w.Index(i).Type()) + } + if i >= wl { + return eq.errorf("missing %s", v.Index(i).Type()) + } + if err := eq.checkValue(v.Index(i), w.Index(i)); err != nil { + return err + } + } + + case reflect.Struct: + // Fields in struct must match. + t := v.Type() + n := t.NumField() + for i := 0; i < n; i++ { + tf := t.Field(i) + switch { + default: + if err := eq.checkValue(v.Field(i), w.Field(i)); err != nil { + return err + } + + case tf.Type == posType: // ignore positions + case tf.Type == commentsType: // ignore comment assignment + } + } + + case reflect.Ptr, reflect.Interface: + if v.IsNil() != w.IsNil() { + if v.IsNil() { + return eq.errorf("unexpected %s", w.Elem().Type()) + } + return eq.errorf("missing %s", v.Elem().Type()) + } + if err := eq.checkValue(v.Elem(), w.Elem()); err != nil { + return err + } + } + return nil +} + +// diff returns the output of running diff on b1 and b2. +func diff(b1, b2 []byte) (data []byte, err error) { + f1, err := ioutil.TempFile("", "testdiff") + if err != nil { + return nil, err + } + defer os.Remove(f1.Name()) + defer f1.Close() + + f2, err := ioutil.TempFile("", "testdiff") + if err != nil { + return nil, err + } + defer os.Remove(f2.Name()) + defer f2.Close() + + f1.Write(b1) + f2.Write(b2) + + data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() + if len(data) > 0 { + // diff exits with a non-zero status when the files don't match. + // Ignore that failure as long as we get output. + err = nil + } + return +} + +// tdiff logs the diff output to t.Error. +func tdiff(t *testing.T, a, b string) { + data, err := diff([]byte(a), []byte(b)) + if err != nil { + t.Error(err) + return + } + t.Error(string(data)) +} + +var modulePathTests = []struct { + input []byte + expected string +}{ + {input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"}, + {input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"}, + {input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"}, + {input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"}, + {input: []byte("module `github.com/rsc/vgotest`"), expected: "github.com/rsc/vgotest"}, + {input: []byte("module \"github.com/rsc/vgotest/v2\""), expected: "github.com/rsc/vgotest/v2"}, + {input: []byte("module github.com/rsc/vgotest/v2"), expected: "github.com/rsc/vgotest/v2"}, + {input: []byte("module \"gopkg.in/yaml.v2\""), expected: "gopkg.in/yaml.v2"}, + {input: []byte("module gopkg.in/yaml.v2"), expected: "gopkg.in/yaml.v2"}, + {input: []byte("module \"gopkg.in/check.v1\"\n"), expected: "gopkg.in/check.v1"}, + {input: []byte("module \"gopkg.in/check.v1\n\""), expected: ""}, + {input: []byte("module gopkg.in/check.v1\n"), expected: "gopkg.in/check.v1"}, + {input: []byte("module \"gopkg.in/check.v1\"\r\n"), expected: "gopkg.in/check.v1"}, + {input: []byte("module gopkg.in/check.v1\r\n"), expected: "gopkg.in/check.v1"}, + {input: []byte("module \"gopkg.in/check.v1\"\n\n"), expected: "gopkg.in/check.v1"}, + {input: []byte("module gopkg.in/check.v1\n\n"), expected: "gopkg.in/check.v1"}, + {input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""}, + {input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""}, + {input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""}, + {input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""}, + {input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""}, + {input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""}, + {input: []byte("module \nmodule a/b/c "), expected: "a/b/c"}, + {input: []byte("module \" \""), expected: " "}, + {input: []byte("module "), expected: ""}, + {input: []byte("module \" a/b/c \""), expected: " a/b/c "}, + {input: []byte("module \"github.com/rsc/vgotest1\" // with a comment"), expected: "github.com/rsc/vgotest1"}, +} + +func TestModulePath(t *testing.T) { + for _, test := range modulePathTests { + t.Run(string(test.input), func(t *testing.T) { + result := ModulePath(test.input) + if result != test.expected { + t.Fatalf("ModulePath(%q): %s, want %s", string(test.input), result, test.expected) + } + }) + } +} diff --git a/libgo/go/cmd/go/internal/modfile/rule.go b/libgo/go/cmd/go/internal/modfile/rule.go new file mode 100644 index 0000000..e11f0a6 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/rule.go @@ -0,0 +1,724 @@ +// Copyright 2018 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 modfile + +import ( + "bytes" + "errors" + "fmt" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "unicode" + + "cmd/go/internal/module" + "cmd/go/internal/semver" +) + +// A File is the parsed, interpreted form of a go.mod file. +type File struct { + Module *Module + Go *Go + Require []*Require + Exclude []*Exclude + Replace []*Replace + + Syntax *FileSyntax +} + +// A Module is the module statement. +type Module struct { + Mod module.Version + Syntax *Line +} + +// A Go is the go statement. +type Go struct { + Version string // "1.23" + Syntax *Line +} + +// A Require is a single require statement. +type Require struct { + Mod module.Version + Indirect bool // has "// indirect" comment + Syntax *Line +} + +// An Exclude is a single exclude statement. +type Exclude struct { + Mod module.Version + Syntax *Line +} + +// A Replace is a single replace statement. +type Replace struct { + Old module.Version + New module.Version + Syntax *Line +} + +func (f *File) AddModuleStmt(path string) error { + if f.Syntax == nil { + f.Syntax = new(FileSyntax) + } + if f.Module == nil { + f.Module = &Module{ + Mod: module.Version{Path: path}, + Syntax: f.Syntax.addLine(nil, "module", AutoQuote(path)), + } + } else { + f.Module.Mod.Path = path + f.Syntax.updateLine(f.Module.Syntax, "module", AutoQuote(path)) + } + return nil +} + +func (f *File) AddComment(text string) { + if f.Syntax == nil { + f.Syntax = new(FileSyntax) + } + f.Syntax.Stmt = append(f.Syntax.Stmt, &CommentBlock{ + Comments: Comments{ + Before: []Comment{ + { + Token: text, + }, + }, + }, + }) +} + +type VersionFixer func(path, version string) (string, error) + +// Parse parses the data, reported in errors as being from file, +// into a File struct. It applies fix, if non-nil, to canonicalize all module versions found. +func Parse(file string, data []byte, fix VersionFixer) (*File, error) { + return parseToFile(file, data, fix, true) +} + +// ParseLax is like Parse but ignores unknown statements. +// It is used when parsing go.mod files other than the main module, +// under the theory that most statement types we add in the future will +// only apply in the main module, like exclude and replace, +// and so we get better gradual deployments if old go commands +// simply ignore those statements when found in go.mod files +// in dependencies. +func ParseLax(file string, data []byte, fix VersionFixer) (*File, error) { + return parseToFile(file, data, fix, false) +} + +func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (*File, error) { + fs, err := parse(file, data) + if err != nil { + return nil, err + } + f := &File{ + Syntax: fs, + } + + var errs bytes.Buffer + for _, x := range fs.Stmt { + switch x := x.(type) { + case *Line: + f.add(&errs, x, x.Token[0], x.Token[1:], fix, strict) + + case *LineBlock: + if len(x.Token) > 1 { + if strict { + fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " ")) + } + continue + } + switch x.Token[0] { + default: + if strict { + fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " ")) + } + continue + case "module", "require", "exclude", "replace": + for _, l := range x.Line { + f.add(&errs, l, x.Token[0], l.Token, fix, strict) + } + } + } + } + + if errs.Len() > 0 { + return nil, errors.New(strings.TrimRight(errs.String(), "\n")) + } + return f, nil +} + +var goVersionRE = regexp.MustCompile(`([1-9][0-9]*)\.(0|[1-9][0-9]*)`) + +func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, fix VersionFixer, strict bool) { + // If strict is false, this module is a dependency. + // We ignore all unknown directives as well as main-module-only + // directives like replace and exclude. It will work better for + // forward compatibility if we can depend on modules that have unknown + // statements (presumed relevant only when acting as the main module) + // and simply ignore those statements. + if !strict { + switch verb { + case "module", "require", "go": + // want these even for dependency go.mods + default: + return + } + } + + switch verb { + default: + fmt.Fprintf(errs, "%s:%d: unknown directive: %s\n", f.Syntax.Name, line.Start.Line, verb) + + case "go": + if f.Go != nil { + fmt.Fprintf(errs, "%s:%d: repeated go statement\n", f.Syntax.Name, line.Start.Line) + return + } + if len(args) != 1 || !goVersionRE.MatchString(args[0]) { + fmt.Fprintf(errs, "%s:%d: usage: go 1.23\n", f.Syntax.Name, line.Start.Line) + return + } + f.Go = &Go{Syntax: line} + f.Go.Version = args[0] + case "module": + if f.Module != nil { + fmt.Fprintf(errs, "%s:%d: repeated module statement\n", f.Syntax.Name, line.Start.Line) + return + } + f.Module = &Module{Syntax: line} + if len(args) != 1 { + + fmt.Fprintf(errs, "%s:%d: usage: module module/path [version]\n", f.Syntax.Name, line.Start.Line) + return + } + s, err := parseString(&args[0]) + if err != nil { + fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err) + return + } + f.Module.Mod = module.Version{Path: s} + case "require", "exclude": + if len(args) != 2 { + fmt.Fprintf(errs, "%s:%d: usage: %s module/path v1.2.3\n", f.Syntax.Name, line.Start.Line, verb) + return + } + s, err := parseString(&args[0]) + if err != nil { + fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err) + return + } + old := args[1] + v, err := parseVersion(s, &args[1], fix) + if err != nil { + fmt.Fprintf(errs, "%s:%d: invalid module version %q: %v\n", f.Syntax.Name, line.Start.Line, old, err) + return + } + pathMajor, err := modulePathMajor(s) + if err != nil { + fmt.Fprintf(errs, "%s:%d: %v\n", f.Syntax.Name, line.Start.Line, err) + return + } + if !module.MatchPathMajor(v, pathMajor) { + if pathMajor == "" { + pathMajor = "v0 or v1" + } + fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, pathMajor, semver.Major(v), v) + return + } + if verb == "require" { + f.Require = append(f.Require, &Require{ + Mod: module.Version{Path: s, Version: v}, + Syntax: line, + Indirect: isIndirect(line), + }) + } else { + f.Exclude = append(f.Exclude, &Exclude{ + Mod: module.Version{Path: s, Version: v}, + Syntax: line, + }) + } + case "replace": + arrow := 2 + if len(args) >= 2 && args[1] == "=>" { + arrow = 1 + } + if len(args) < arrow+2 || len(args) > arrow+3 || args[arrow] != "=>" { + fmt.Fprintf(errs, "%s:%d: usage: %s module/path [v1.2.3] => other/module v1.4\n\t or %s module/path [v1.2.3] => ../local/directory\n", f.Syntax.Name, line.Start.Line, verb, verb) + return + } + s, err := parseString(&args[0]) + if err != nil { + fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err) + return + } + pathMajor, err := modulePathMajor(s) + if err != nil { + fmt.Fprintf(errs, "%s:%d: %v\n", f.Syntax.Name, line.Start.Line, err) + return + } + var v string + if arrow == 2 { + old := args[1] + v, err = parseVersion(s, &args[1], fix) + if err != nil { + fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err) + return + } + if !module.MatchPathMajor(v, pathMajor) { + if pathMajor == "" { + pathMajor = "v0 or v1" + } + fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, pathMajor, semver.Major(v), v) + return + } + } + ns, err := parseString(&args[arrow+1]) + if err != nil { + fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err) + return + } + nv := "" + if len(args) == arrow+2 { + if !IsDirectoryPath(ns) { + fmt.Fprintf(errs, "%s:%d: replacement module without version must be directory path (rooted or starting with ./ or ../)\n", f.Syntax.Name, line.Start.Line) + return + } + if filepath.Separator == '/' && strings.Contains(ns, `\`) { + fmt.Fprintf(errs, "%s:%d: replacement directory appears to be Windows path (on a non-windows system)\n", f.Syntax.Name, line.Start.Line) + return + } + } + if len(args) == arrow+3 { + old := args[arrow+1] + nv, err = parseVersion(ns, &args[arrow+2], fix) + if err != nil { + fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err) + return + } + if IsDirectoryPath(ns) { + fmt.Fprintf(errs, "%s:%d: replacement module directory path %q cannot have version\n", f.Syntax.Name, line.Start.Line, ns) + return + } + } + f.Replace = append(f.Replace, &Replace{ + Old: module.Version{Path: s, Version: v}, + New: module.Version{Path: ns, Version: nv}, + Syntax: line, + }) + } +} + +// isIndirect reports whether line has a "// indirect" comment, +// meaning it is in go.mod only for its effect on indirect dependencies, +// so that it can be dropped entirely once the effective version of the +// indirect dependency reaches the given minimum version. +func isIndirect(line *Line) bool { + if len(line.Suffix) == 0 { + return false + } + f := strings.Fields(line.Suffix[0].Token) + return (len(f) == 2 && f[1] == "indirect" || len(f) > 2 && f[1] == "indirect;") && f[0] == "//" +} + +// setIndirect sets line to have (or not have) a "// indirect" comment. +func setIndirect(line *Line, indirect bool) { + if isIndirect(line) == indirect { + return + } + if indirect { + // Adding comment. + if len(line.Suffix) == 0 { + // New comment. + line.Suffix = []Comment{{Token: "// indirect", Suffix: true}} + return + } + // Insert at beginning of existing comment. + com := &line.Suffix[0] + space := " " + if len(com.Token) > 2 && com.Token[2] == ' ' || com.Token[2] == '\t' { + space = "" + } + com.Token = "// indirect;" + space + com.Token[2:] + return + } + + // Removing comment. + f := strings.Fields(line.Suffix[0].Token) + if len(f) == 2 { + // Remove whole comment. + line.Suffix = nil + return + } + + // Remove comment prefix. + com := &line.Suffix[0] + i := strings.Index(com.Token, "indirect;") + com.Token = "//" + com.Token[i+len("indirect;"):] +} + +// IsDirectoryPath reports whether the given path should be interpreted +// as a directory path. Just like on the go command line, relative paths +// and rooted paths are directory paths; the rest are module paths. +func IsDirectoryPath(ns string) bool { + // Because go.mod files can move from one system to another, + // we check all known path syntaxes, both Unix and Windows. + return strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, "/") || + strings.HasPrefix(ns, `.\`) || strings.HasPrefix(ns, `..\`) || strings.HasPrefix(ns, `\`) || + len(ns) >= 2 && ('A' <= ns[0] && ns[0] <= 'Z' || 'a' <= ns[0] && ns[0] <= 'z') && ns[1] == ':' +} + +// MustQuote reports whether s must be quoted in order to appear as +// a single token in a go.mod line. +func MustQuote(s string) bool { + for _, r := range s { + if !unicode.IsPrint(r) || r == ' ' || r == '"' || r == '\'' || r == '`' { + return true + } + } + return s == "" || strings.Contains(s, "//") || strings.Contains(s, "/*") +} + +// AutoQuote returns s or, if quoting is required for s to appear in a go.mod, +// the quotation of s. +func AutoQuote(s string) string { + if MustQuote(s) { + return strconv.Quote(s) + } + return s +} + +func parseString(s *string) (string, error) { + t := *s + if strings.HasPrefix(t, `"`) { + var err error + if t, err = strconv.Unquote(t); err != nil { + return "", err + } + } else if strings.ContainsAny(t, "\"'`") { + // Other quotes are reserved both for possible future expansion + // and to avoid confusion. For example if someone types 'x' + // we want that to be a syntax error and not a literal x in literal quotation marks. + return "", fmt.Errorf("unquoted string cannot contain quote") + } + *s = AutoQuote(t) + return t, nil +} + +func parseVersion(path string, s *string, fix VersionFixer) (string, error) { + t, err := parseString(s) + if err != nil { + return "", err + } + if fix != nil { + var err error + t, err = fix(path, t) + if err != nil { + return "", err + } + } + if v := module.CanonicalVersion(t); v != "" { + *s = v + return *s, nil + } + return "", fmt.Errorf("version must be of the form v1.2.3") +} + +func modulePathMajor(path string) (string, error) { + _, major, ok := module.SplitPathVersion(path) + if !ok { + return "", fmt.Errorf("invalid module path") + } + return major, nil +} + +func (f *File) Format() ([]byte, error) { + return Format(f.Syntax), nil +} + +// Cleanup cleans up the file f after any edit operations. +// To avoid quadratic behavior, modifications like DropRequire +// clear the entry but do not remove it from the slice. +// Cleanup cleans out all the cleared entries. +func (f *File) Cleanup() { + w := 0 + for _, r := range f.Require { + if r.Mod.Path != "" { + f.Require[w] = r + w++ + } + } + f.Require = f.Require[:w] + + w = 0 + for _, x := range f.Exclude { + if x.Mod.Path != "" { + f.Exclude[w] = x + w++ + } + } + f.Exclude = f.Exclude[:w] + + w = 0 + for _, r := range f.Replace { + if r.Old.Path != "" { + f.Replace[w] = r + w++ + } + } + f.Replace = f.Replace[:w] + + f.Syntax.Cleanup() +} + +func (f *File) AddRequire(path, vers string) error { + need := true + for _, r := range f.Require { + if r.Mod.Path == path { + if need { + r.Mod.Version = vers + f.Syntax.updateLine(r.Syntax, "require", AutoQuote(path), vers) + need = false + } else { + f.Syntax.removeLine(r.Syntax) + *r = Require{} + } + } + } + + if need { + f.AddNewRequire(path, vers, false) + } + return nil +} + +func (f *File) AddNewRequire(path, vers string, indirect bool) { + line := f.Syntax.addLine(nil, "require", AutoQuote(path), vers) + setIndirect(line, indirect) + f.Require = append(f.Require, &Require{module.Version{Path: path, Version: vers}, indirect, line}) +} + +func (f *File) SetRequire(req []*Require) { + need := make(map[string]string) + indirect := make(map[string]bool) + for _, r := range req { + need[r.Mod.Path] = r.Mod.Version + indirect[r.Mod.Path] = r.Indirect + } + + for _, r := range f.Require { + if v, ok := need[r.Mod.Path]; ok { + r.Mod.Version = v + r.Indirect = indirect[r.Mod.Path] + } + } + + var newStmts []Expr + for _, stmt := range f.Syntax.Stmt { + switch stmt := stmt.(type) { + case *LineBlock: + if len(stmt.Token) > 0 && stmt.Token[0] == "require" { + var newLines []*Line + for _, line := range stmt.Line { + if p, err := parseString(&line.Token[0]); err == nil && need[p] != "" { + line.Token[1] = need[p] + delete(need, p) + setIndirect(line, indirect[p]) + newLines = append(newLines, line) + } + } + if len(newLines) == 0 { + continue // drop stmt + } + stmt.Line = newLines + } + + case *Line: + if len(stmt.Token) > 0 && stmt.Token[0] == "require" { + if p, err := parseString(&stmt.Token[1]); err == nil && need[p] != "" { + stmt.Token[2] = need[p] + delete(need, p) + setIndirect(stmt, indirect[p]) + } else { + continue // drop stmt + } + } + } + newStmts = append(newStmts, stmt) + } + f.Syntax.Stmt = newStmts + + for path, vers := range need { + f.AddNewRequire(path, vers, indirect[path]) + } + f.SortBlocks() +} + +func (f *File) DropRequire(path string) error { + for _, r := range f.Require { + if r.Mod.Path == path { + f.Syntax.removeLine(r.Syntax) + *r = Require{} + } + } + return nil +} + +func (f *File) AddExclude(path, vers string) error { + var hint *Line + for _, x := range f.Exclude { + if x.Mod.Path == path && x.Mod.Version == vers { + return nil + } + if x.Mod.Path == path { + hint = x.Syntax + } + } + + f.Exclude = append(f.Exclude, &Exclude{Mod: module.Version{Path: path, Version: vers}, Syntax: f.Syntax.addLine(hint, "exclude", AutoQuote(path), vers)}) + return nil +} + +func (f *File) DropExclude(path, vers string) error { + for _, x := range f.Exclude { + if x.Mod.Path == path && x.Mod.Version == vers { + f.Syntax.removeLine(x.Syntax) + *x = Exclude{} + } + } + return nil +} + +func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error { + need := true + old := module.Version{Path: oldPath, Version: oldVers} + new := module.Version{Path: newPath, Version: newVers} + tokens := []string{"replace", AutoQuote(oldPath)} + if oldVers != "" { + tokens = append(tokens, oldVers) + } + tokens = append(tokens, "=>", AutoQuote(newPath)) + if newVers != "" { + tokens = append(tokens, newVers) + } + + var hint *Line + for _, r := range f.Replace { + if r.Old.Path == oldPath && (oldVers == "" || r.Old.Version == oldVers) { + if need { + // Found replacement for old; update to use new. + r.New = new + f.Syntax.updateLine(r.Syntax, tokens...) + need = false + continue + } + // Already added; delete other replacements for same. + f.Syntax.removeLine(r.Syntax) + *r = Replace{} + } + if r.Old.Path == oldPath { + hint = r.Syntax + } + } + if need { + f.Replace = append(f.Replace, &Replace{Old: old, New: new, Syntax: f.Syntax.addLine(hint, tokens...)}) + } + return nil +} + +func (f *File) DropReplace(oldPath, oldVers string) error { + for _, r := range f.Replace { + if r.Old.Path == oldPath && r.Old.Version == oldVers { + f.Syntax.removeLine(r.Syntax) + *r = Replace{} + } + } + return nil +} + +func (f *File) SortBlocks() { + f.removeDups() // otherwise sorting is unsafe + + for _, stmt := range f.Syntax.Stmt { + block, ok := stmt.(*LineBlock) + if !ok { + continue + } + sort.Slice(block.Line, func(i, j int) bool { + li := block.Line[i] + lj := block.Line[j] + for k := 0; k < len(li.Token) && k < len(lj.Token); k++ { + if li.Token[k] != lj.Token[k] { + return li.Token[k] < lj.Token[k] + } + } + return len(li.Token) < len(lj.Token) + }) + } +} + +func (f *File) removeDups() { + have := make(map[module.Version]bool) + kill := make(map[*Line]bool) + for _, x := range f.Exclude { + if have[x.Mod] { + kill[x.Syntax] = true + continue + } + have[x.Mod] = true + } + var excl []*Exclude + for _, x := range f.Exclude { + if !kill[x.Syntax] { + excl = append(excl, x) + } + } + f.Exclude = excl + + have = make(map[module.Version]bool) + // Later replacements take priority over earlier ones. + for i := len(f.Replace) - 1; i >= 0; i-- { + x := f.Replace[i] + if have[x.Old] { + kill[x.Syntax] = true + continue + } + have[x.Old] = true + } + var repl []*Replace + for _, x := range f.Replace { + if !kill[x.Syntax] { + repl = append(repl, x) + } + } + f.Replace = repl + + var stmts []Expr + for _, stmt := range f.Syntax.Stmt { + switch stmt := stmt.(type) { + case *Line: + if kill[stmt] { + continue + } + case *LineBlock: + var lines []*Line + for _, line := range stmt.Line { + if !kill[line] { + lines = append(lines, line) + } + } + stmt.Line = lines + if len(lines) == 0 { + continue + } + } + stmts = append(stmts, stmt) + } + f.Syntax.Stmt = stmts +} diff --git a/libgo/go/cmd/go/internal/modfile/rule_test.go b/libgo/go/cmd/go/internal/modfile/rule_test.go new file mode 100644 index 0000000..b88ad62 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/rule_test.go @@ -0,0 +1,90 @@ +// Copyright 2018 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 modfile + +import ( + "bytes" + "fmt" + "testing" +) + +var addRequireTests = []struct { + in string + path string + vers string + out string +}{ + { + ` + module m + require x.y/z v1.2.3 + `, + "x.y/z", "v1.5.6", + ` + module m + require x.y/z v1.5.6 + `, + }, + { + ` + module m + require x.y/z v1.2.3 + `, + "x.y/w", "v1.5.6", + ` + module m + require ( + x.y/z v1.2.3 + x.y/w v1.5.6 + ) + `, + }, + { + ` + module m + require x.y/z v1.2.3 + require x.y/q/v2 v2.3.4 + `, + "x.y/w", "v1.5.6", + ` + module m + require x.y/z v1.2.3 + require ( + x.y/q/v2 v2.3.4 + x.y/w v1.5.6 + ) + `, + }, +} + +func TestAddRequire(t *testing.T) { + for i, tt := range addRequireTests { + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + f, err := Parse("in", []byte(tt.in), nil) + if err != nil { + t.Fatal(err) + } + g, err := Parse("out", []byte(tt.out), nil) + if err != nil { + t.Fatal(err) + } + golden, err := g.Format() + if err != nil { + t.Fatal(err) + } + + if err := f.AddRequire(tt.path, tt.vers); err != nil { + t.Fatal(err) + } + out, err := f.Format() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(out, golden) { + t.Errorf("have:\n%s\nwant:\n%s", out, golden) + } + }) + } +} diff --git a/libgo/go/cmd/go/internal/modfile/testdata/block.golden b/libgo/go/cmd/go/internal/modfile/testdata/block.golden new file mode 100644 index 0000000..4aa2d63 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/testdata/block.golden @@ -0,0 +1,29 @@ +// comment +x "y" z + +// block +block ( // block-eol + // x-before-line + + "x" ( y // x-eol + "x1" + "x2" + // line + "x3" + "x4" + + "x5" + + // y-line + "y" // y-eol + + "z" // z-eol +) // block-eol2 + +block2 ( + x + y + z +) + +// eof diff --git a/libgo/go/cmd/go/internal/modfile/testdata/block.in b/libgo/go/cmd/go/internal/modfile/testdata/block.in new file mode 100644 index 0000000..1dfae65 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/testdata/block.in @@ -0,0 +1,29 @@ +// comment +x "y" z + +// block +block ( // block-eol + // x-before-line + + "x" ( y // x-eol + "x1" + "x2" + // line + "x3" + "x4" + + "x5" + + // y-line + "y" // y-eol + + "z" // z-eol +) // block-eol2 + + +block2 (x + y + z +) + +// eof diff --git a/libgo/go/cmd/go/internal/modfile/testdata/comment.golden b/libgo/go/cmd/go/internal/modfile/testdata/comment.golden new file mode 100644 index 0000000..75f3b84 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/testdata/comment.golden @@ -0,0 +1,10 @@ +// comment +module "x" // eol + +// mid comment + +// comment 2 +// comment 2 line 2 +module "y" // eoy + +// comment 3 diff --git a/libgo/go/cmd/go/internal/modfile/testdata/comment.in b/libgo/go/cmd/go/internal/modfile/testdata/comment.in new file mode 100644 index 0000000..bfc2492 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/testdata/comment.in @@ -0,0 +1,8 @@ +// comment +module "x" // eol +// mid comment + +// comment 2 +// comment 2 line 2 +module "y" // eoy +// comment 3 diff --git a/libgo/go/cmd/go/internal/modfile/testdata/empty.golden b/libgo/go/cmd/go/internal/modfile/testdata/empty.golden new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/testdata/empty.golden diff --git a/libgo/go/cmd/go/internal/modfile/testdata/empty.in b/libgo/go/cmd/go/internal/modfile/testdata/empty.in new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/testdata/empty.in diff --git a/libgo/go/cmd/go/internal/modfile/testdata/gopkg.in.golden b/libgo/go/cmd/go/internal/modfile/testdata/gopkg.in.golden new file mode 100644 index 0000000..41669b3 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/testdata/gopkg.in.golden @@ -0,0 +1,6 @@ +module x + +require ( + gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528 + gopkg.in/yaml.v2 v2.2.1 +) diff --git a/libgo/go/cmd/go/internal/modfile/testdata/module.golden b/libgo/go/cmd/go/internal/modfile/testdata/module.golden new file mode 100644 index 0000000..78ba943 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/testdata/module.golden @@ -0,0 +1 @@ +module abc diff --git a/libgo/go/cmd/go/internal/modfile/testdata/module.in b/libgo/go/cmd/go/internal/modfile/testdata/module.in new file mode 100644 index 0000000..08f3836 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/testdata/module.in @@ -0,0 +1 @@ +module "abc" diff --git a/libgo/go/cmd/go/internal/modfile/testdata/replace.golden b/libgo/go/cmd/go/internal/modfile/testdata/replace.golden new file mode 100644 index 0000000..5d6abcf --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/testdata/replace.golden @@ -0,0 +1,5 @@ +module abc + +replace xyz v1.2.3 => /tmp/z + +replace xyz v1.3.4 => my/xyz v1.3.4-me diff --git a/libgo/go/cmd/go/internal/modfile/testdata/replace.in b/libgo/go/cmd/go/internal/modfile/testdata/replace.in new file mode 100644 index 0000000..685249946 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/testdata/replace.in @@ -0,0 +1,5 @@ +module "abc" + +replace "xyz" v1.2.3 => "/tmp/z" + +replace "xyz" v1.3.4 => "my/xyz" v1.3.4-me diff --git a/libgo/go/cmd/go/internal/modfile/testdata/replace2.golden b/libgo/go/cmd/go/internal/modfile/testdata/replace2.golden new file mode 100644 index 0000000..e1d9c72 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/testdata/replace2.golden @@ -0,0 +1,10 @@ +module abc + +replace ( + xyz v1.2.3 => /tmp/z + xyz v1.3.4 => my/xyz v1.3.4-me + xyz v1.4.5 => "/tmp/my dir" + xyz v1.5.6 => my/xyz v1.5.6 + + xyz => my/other/xyz v1.5.4 +) diff --git a/libgo/go/cmd/go/internal/modfile/testdata/replace2.in b/libgo/go/cmd/go/internal/modfile/testdata/replace2.in new file mode 100644 index 0000000..7864698 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/testdata/replace2.in @@ -0,0 +1,10 @@ +module "abc" + +replace ( + "xyz" v1.2.3 => "/tmp/z" + "xyz" v1.3.4 => "my/xyz" "v1.3.4-me" + xyz "v1.4.5" => "/tmp/my dir" + xyz v1.5.6 => my/xyz v1.5.6 + + xyz => my/other/xyz v1.5.4 +) diff --git a/libgo/go/cmd/go/internal/modfile/testdata/rule1.golden b/libgo/go/cmd/go/internal/modfile/testdata/rule1.golden new file mode 100644 index 0000000..8a5c725 --- /dev/null +++ b/libgo/go/cmd/go/internal/modfile/testdata/rule1.golden @@ -0,0 +1,7 @@ +module "x" + +module "y" + +require "x" + +require x diff --git a/libgo/go/cmd/go/internal/modget/get.go b/libgo/go/cmd/go/internal/modget/get.go new file mode 100644 index 0000000..90a5bd8 --- /dev/null +++ b/libgo/go/cmd/go/internal/modget/get.go @@ -0,0 +1,656 @@ +// Copyright 2018 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 modget implements the module-aware ``go get'' command. +package modget + +import ( + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/get" + "cmd/go/internal/load" + "cmd/go/internal/modfetch" + "cmd/go/internal/modload" + "cmd/go/internal/module" + "cmd/go/internal/mvs" + "cmd/go/internal/par" + "cmd/go/internal/search" + "cmd/go/internal/semver" + "cmd/go/internal/str" + "cmd/go/internal/work" + "fmt" + "os" + pathpkg "path" + "path/filepath" + "strings" +) + +var CmdGet = &base.Command{ + // Note: -d -m -u are listed explicitly because they are the most common get flags. + // Do not send CLs removing them because they're covered by [get flags]. + UsageLine: "go get [-d] [-m] [-u] [-v] [-insecure] [build flags] [packages]", + Short: "add dependencies to current module and install them", + Long: ` +Get resolves and adds dependencies to the current development module +and then builds and installs them. + +The first step is to resolve which dependencies to add. + +For each named package or package pattern, get must decide which version of +the corresponding module to use. By default, get chooses the latest tagged +release version, such as v0.4.5 or v1.2.3. If there are no tagged release +versions, get chooses the latest tagged prerelease version, such as +v0.0.1-pre1. If there are no tagged versions at all, get chooses the latest +known commit. + +This default version selection can be overridden by adding an @version +suffix to the package argument, as in 'go get golang.org/x/text@v0.3.0'. +For modules stored in source control repositories, the version suffix can +also be a commit hash, branch identifier, or other syntax known to the +source control system, as in 'go get golang.org/x/text@master'. +The version suffix @latest explicitly requests the default behavior +described above. + +If a module under consideration is already a dependency of the current +development module, then get will update the required version. +Specifying a version earlier than the current required version is valid and +downgrades the dependency. The version suffix @none indicates that the +dependency should be removed entirely. + +Although get defaults to using the latest version of the module containing +a named package, it does not use the latest version of that module's +dependencies. Instead it prefers to use the specific dependency versions +requested by that module. For example, if the latest A requires module +B v1.2.3, while B v1.2.4 and v1.3.1 are also available, then 'go get A' +will use the latest A but then use B v1.2.3, as requested by A. (If there +are competing requirements for a particular module, then 'go get' resolves +those requirements by taking the maximum requested version.) + +The -u flag instructs get to update dependencies to use newer minor or +patch releases when available. Continuing the previous example, +'go get -u A' will use the latest A with B v1.3.1 (not B v1.2.3). + +The -u=patch flag (not -u patch) instructs get to update dependencies +to use newer patch releases when available. Continuing the previous example, +'go get -u=patch A' will use the latest A with B v1.2.4 (not B v1.2.3). + +In general, adding a new dependency may require upgrading +existing dependencies to keep a working build, and 'go get' does +this automatically. Similarly, downgrading one dependency may +require downgrading other dependenceis, and 'go get' does +this automatically as well. + +The -m flag instructs get to stop here, after resolving, upgrading, +and downgrading modules and updating go.mod. When using -m, +each specified package path must be a module path as well, +not the import path of a package below the module root. + +The -insecure flag permits fetching from repositories and resolving +custom domains using insecure schemes such as HTTP. Use with caution. + +The second step is to download (if needed), build, and install +the named packages. + +If an argument names a module but not a package (because there is no +Go source code in the module's root directory), then the install step +is skipped for that argument, instead of causing a build failure. +For example 'go get golang.org/x/perf' succeeds even though there +is no code corresponding to that import path. + +Note that package patterns are allowed and are expanded after resolving +the module versions. For example, 'go get golang.org/x/perf/cmd/...' +adds the latest golang.org/x/perf and then installs the commands in that +latest version. + +The -d flag instructs get to download the source code needed to build +the named packages, including downloading necessary dependencies, +but not to build and install them. + +With no package arguments, 'go get' applies to the main module, +and to the Go package in the current directory, if any. In particular, +'go get -u' and 'go get -u=patch' update all the dependencies of the +main module. With no package arguments and also without -u, +'go get' is not much more than 'go install', and 'go get -d' not much +more than 'go list'. + +For more about modules, see 'go help modules'. + +For more about specifying packages, see 'go help packages'. + +This text describes the behavior of get using modules to manage source +code and dependencies. If instead the go command is running in GOPATH +mode, the details of get's flags and effects change, as does 'go help get'. +See 'go help modules' and 'go help gopath-get'. + +See also: go build, go install, go clean, go mod. + `, +} + +// Note that this help text is a stopgap to make the module-aware get help text +// available even in non-module settings. It should be deleted when the old get +// is deleted. It should NOT be considered to set a precedent of having hierarchical +// help names with dashes. +var HelpModuleGet = &base.Command{ + UsageLine: "module-get", + Short: "module-aware go get", + Long: ` +The 'go get' command changes behavior depending on whether the +go command is running in module-aware mode or legacy GOPATH mode. +This help text, accessible as 'go help module-get' even in legacy GOPATH mode, +describes 'go get' as it operates in module-aware mode. + +Usage: ` + CmdGet.UsageLine + ` +` + CmdGet.Long, +} + +var ( + getD = CmdGet.Flag.Bool("d", false, "") + getF = CmdGet.Flag.Bool("f", false, "") + getFix = CmdGet.Flag.Bool("fix", false, "") + getM = CmdGet.Flag.Bool("m", false, "") + getT = CmdGet.Flag.Bool("t", false, "") + getU upgradeFlag + // -insecure is get.Insecure + // -v is cfg.BuildV +) + +// upgradeFlag is a custom flag.Value for -u. +type upgradeFlag string + +func (*upgradeFlag) IsBoolFlag() bool { return true } // allow -u + +func (v *upgradeFlag) Set(s string) error { + if s == "false" { + s = "" + } + *v = upgradeFlag(s) + return nil +} + +func (v *upgradeFlag) String() string { return "" } + +func init() { + work.AddBuildFlags(CmdGet) + CmdGet.Run = runGet // break init loop + CmdGet.Flag.BoolVar(&get.Insecure, "insecure", get.Insecure, "") + CmdGet.Flag.Var(&getU, "u", "") +} + +// A task holds the state for processing a single get argument (path@vers). +type task struct { + arg string // original argument + index int + path string // package path part of arg + forceModulePath bool // path must be interpreted as a module path + vers string // version part of arg + m module.Version // module version indicated by argument + req []module.Version // m's requirement list (not upgraded) +} + +func runGet(cmd *base.Command, args []string) { + // -mod=readonly has no effect on "go get". + if cfg.BuildMod == "readonly" { + cfg.BuildMod = "" + } + + switch getU { + case "", "patch", "true": + // ok + default: + base.Fatalf("go get: unknown upgrade flag -u=%s", getU) + } + if *getF { + fmt.Fprintf(os.Stderr, "go get: -f flag is a no-op when using modules\n") + } + if *getFix { + fmt.Fprintf(os.Stderr, "go get: -fix flag is a no-op when using modules\n") + } + if *getT { + fmt.Fprintf(os.Stderr, "go get: -t flag is a no-op when using modules\n") + } + + if cfg.BuildMod == "vendor" { + base.Fatalf("go get: disabled by -mod=%s", cfg.BuildMod) + } + + modload.LoadBuildList() + + // Do not allow any updating of go.mod until we've applied + // all the requested changes and checked that the result matches + // what was requested. + modload.DisallowWriteGoMod() + + // Build task and install lists. + // The command-line arguments are of the form path@version + // or simply path, with implicit @latest. path@none is "downgrade away". + // At the end of the loop, we've resolved the list of arguments into + // a list of tasks (a path@vers that needs further processing) + // and a list of install targets (for the "go install" at the end). + var tasks []*task + var install []string + for _, arg := range search.CleanPatterns(args) { + // Argument is module query path@vers, or else path with implicit @latest. + path := arg + vers := "" + if i := strings.Index(arg, "@"); i >= 0 { + path, vers = arg[:i], arg[i+1:] + } + if strings.Contains(vers, "@") || arg != path && vers == "" { + base.Errorf("go get %s: invalid module version syntax", arg) + continue + } + if vers != "none" { + install = append(install, path) + } + + // Deciding which module to upgrade/downgrade for a particular argument is difficult. + // Patterns only make it more difficult. + // We impose restrictions to avoid needing to interlace pattern expansion, + // like in in modload.ImportPaths. + // Specifically, these patterns are supported: + // + // - Relative paths like ../../foo or ../../foo... are restricted to matching directories + // in the current module and therefore map to the current module. + // It's possible that the pattern matches no packages, but we will still treat it + // as mapping to the current module. + // TODO: In followup, could just expand the full list and remove the discrepancy. + // - The pattern "all" has its usual package meaning and maps to the list of modules + // from which the matched packages are drawn. This is potentially a subset of the + // module pattern "all". If module A requires B requires C but A does not import + // the parts of B that import C, the packages matched by "all" are only from A and B, + // so only A and B end up on the tasks list. + // TODO: Even in -m mode? + // - The patterns "std" and "cmd" expand to packages in the standard library, + // which aren't upgradable, so we skip over those. + // In -m mode they expand to non-module-paths, so they are disallowed. + // - Import path patterns like foo/bar... are matched against the module list, + // assuming any package match would imply a module pattern match. + // TODO: What about -m mode? + // - Import paths without patterns are left as is, for resolution by getQuery (eventually modload.Import). + // + if search.IsRelativePath(path) { + // Check that this relative pattern only matches directories in the current module, + // and then record the current module as the target. + dir := path + if i := strings.Index(path, "..."); i >= 0 { + dir, _ = pathpkg.Split(path[:i]) + } + abs, err := filepath.Abs(dir) + if err != nil { + base.Errorf("go get %s: %v", arg, err) + continue + } + if !str.HasFilePathPrefix(abs, modload.ModRoot) { + base.Errorf("go get %s: directory %s is outside module root %s", arg, abs, modload.ModRoot) + continue + } + // TODO: Check if abs is inside a nested module. + tasks = append(tasks, &task{arg: arg, path: modload.Target.Path, vers: ""}) + continue + } + if path == "all" { + // TODO: If *getM, should this be the module pattern "all"? + + // This is the package pattern "all" not the module pattern "all": + // enumerate all the modules actually needed by builds of the packages + // in the main module, not incidental modules that happen to be + // in the package graph (and therefore build list). + // Note that LoadALL may add new modules to the build list to + // satisfy new imports, but vers == "latest" implicitly anyway, + // so we'll assume that's OK. + seen := make(map[module.Version]bool) + pkgs := modload.LoadALL() + for _, pkg := range pkgs { + m := modload.PackageModule(pkg) + if m.Path != "" && !seen[m] { + seen[m] = true + tasks = append(tasks, &task{arg: arg, path: m.Path, vers: "latest", forceModulePath: true}) + } + } + continue + } + if search.IsMetaPackage(path) { + // Already handled "all", so this must be "std" or "cmd", + // which are entirely in the standard library. + if path != arg { + base.Errorf("go get %s: cannot use pattern %q with explicit version", arg, arg) + } + if *getM { + base.Errorf("go get %s: cannot use pattern %q with -m", arg, arg) + continue + } + continue + } + if strings.Contains(path, "...") { + // Apply to modules in build list matched by pattern (golang.org/x/...), if any. + match := search.MatchPattern(path) + matched := false + for _, m := range modload.BuildList() { + if match(m.Path) || str.HasPathPrefix(path, m.Path) { + tasks = append(tasks, &task{arg: arg, path: m.Path, vers: vers, forceModulePath: true}) + matched = true + } + } + // If matched, we're done. + // Otherwise assume pattern is inside a single module + // (golang.org/x/text/unicode/...) and leave for usual lookup. + // Unless we're using -m. + if matched { + continue + } + if *getM { + base.Errorf("go get %s: pattern matches no modules in build list", arg) + continue + } + } + tasks = append(tasks, &task{arg: arg, path: path, vers: vers}) + } + base.ExitIfErrors() + + // Now we've reduced the upgrade/downgrade work to a list of path@vers pairs (tasks). + // Resolve each one in parallel. + reqs := modload.Reqs() + var lookup par.Work + for _, t := range tasks { + lookup.Add(t) + } + lookup.Do(10, func(item interface{}) { + t := item.(*task) + if t.vers == "none" { + // Wait for downgrade step. + t.m = module.Version{Path: t.path, Version: "none"} + return + } + m, err := getQuery(t.path, t.vers, t.forceModulePath) + if err != nil { + base.Errorf("go get %v: %v", t.arg, err) + return + } + t.m = m + }) + base.ExitIfErrors() + + // Now we know the specific version of each path@vers. + // The final build list will be the union of three build lists: + // 1. the original build list + // 2. the modules named on the command line (other than @none) + // 3. the upgraded requirements of those modules (if upgrading) + // Start building those lists. + // This loop collects (2). + // Also, because the list of paths might have named multiple packages in a single module + // (or even the same package multiple times), now that we know the module for each + // package, this loop deduplicates multiple references to a given module. + // (If a module is mentioned multiple times, the listed target version must be the same each time.) + var named []module.Version + byPath := make(map[string]*task) + for _, t := range tasks { + prev, ok := byPath[t.m.Path] + if prev != nil && prev.m != t.m { + base.Errorf("go get: conflicting versions for module %s: %s and %s", t.m.Path, prev.m.Version, t.m.Version) + byPath[t.m.Path] = nil // sentinel to stop errors + continue + } + if ok { + continue // already added + } + byPath[t.m.Path] = t + if t.m.Version != "none" { + named = append(named, t.m) + } + } + base.ExitIfErrors() + + // If the modules named on the command line have any dependencies + // and we're supposed to upgrade dependencies, + // chase down the full list of upgraded dependencies. + // This turns required from a not-yet-upgraded (3) to the final (3). + // (See list above.) + var required []module.Version + if getU != "" { + upgraded, err := mvs.UpgradeAll(upgradeTarget, &upgrader{ + Reqs: modload.Reqs(), + targets: named, + patch: getU == "patch", + tasks: byPath, + }) + if err != nil { + base.Fatalf("go get: %v", err) + } + required = upgraded[1:] // slice off upgradeTarget + base.ExitIfErrors() + } + + // Put together the final build list as described above (1) (2) (3). + // If we're not using -u, then len(required) == 0 and ReloadBuildList + // chases down the dependencies of all the named module versions + // in one operation. + var list []module.Version + list = append(list, modload.BuildList()...) + list = append(list, named...) + list = append(list, required...) + modload.SetBuildList(list) + modload.ReloadBuildList() // note: does not update go.mod + base.ExitIfErrors() + + // Scan for and apply any needed downgrades. + var down []module.Version + for _, m := range modload.BuildList() { + t := byPath[m.Path] + if t != nil && semver.Compare(m.Version, t.m.Version) > 0 { + down = append(down, module.Version{Path: m.Path, Version: t.m.Version}) + } + } + if len(down) > 0 { + list, err := mvs.Downgrade(modload.Target, modload.Reqs(), down...) + if err != nil { + base.Fatalf("go get: %v", err) + } + modload.SetBuildList(list) + modload.ReloadBuildList() // note: does not update go.mod + } + base.ExitIfErrors() + + // Scan for any upgrades lost by the downgrades. + lost := make(map[string]string) + for _, m := range modload.BuildList() { + t := byPath[m.Path] + if t != nil && semver.Compare(m.Version, t.m.Version) != 0 { + lost[m.Path] = m.Version + } + } + if len(lost) > 0 { + desc := func(m module.Version) string { + s := m.Path + "@" + m.Version + t := byPath[m.Path] + if t != nil && t.arg != s { + s += " from " + t.arg + } + return s + } + downByPath := make(map[string]module.Version) + for _, d := range down { + downByPath[d.Path] = d + } + var buf strings.Builder + fmt.Fprintf(&buf, "go get: inconsistent versions:") + for _, t := range tasks { + if lost[t.m.Path] == "" { + continue + } + // We lost t because its build list requires a newer version of something in down. + // Figure out exactly what. + // Repeatedly constructing the build list is inefficient + // if there are MANY command-line arguments, + // but at least all the necessary requirement lists are cached at this point. + list, err := mvs.BuildList(t.m, reqs) + if err != nil { + base.Fatalf("go get: %v", err) + } + + fmt.Fprintf(&buf, "\n\t%s", desc(t.m)) + sep := " requires" + for _, m := range list { + if down, ok := downByPath[m.Path]; ok && semver.Compare(down.Version, m.Version) < 0 { + fmt.Fprintf(&buf, "%s %s@%s (not %s)", sep, m.Path, m.Version, desc(down)) + sep = "," + } + } + if sep != "," { + // We have no idea why this happened. + // At least report the problem. + fmt.Fprintf(&buf, " ended up at %v unexpectedly (please report at golang.org/issue/new)", lost[t.m.Path]) + } + } + base.Fatalf("%v", buf.String()) + } + + // Everything succeeded. Update go.mod. + modload.AllowWriteGoMod() + modload.WriteGoMod() + + // If -m was specified, we're done after the module work. No download, no build. + if *getM { + return + } + + if len(install) > 0 { + // All requested versions were explicitly @none. + // Note that 'go get -u' without any arguments results in len(install) == 1: + // search.CleanImportPaths returns "." for empty args. + work.BuildInit() + pkgs := load.PackagesAndErrors(install) + var todo []*load.Package + for _, p := range pkgs { + // Ignore "no Go source files" errors for 'go get' operations on modules. + if p.Error != nil { + if len(args) == 0 && getU != "" && strings.HasPrefix(p.Error.Err, "no Go files") { + // Upgrading modules: skip the implicitly-requested package at the + // current directory, even if it is not tho module root. + continue + } + if strings.Contains(p.Error.Err, "cannot find module providing") && modload.ModuleInfo(p.ImportPath) != nil { + // Explicitly-requested module, but it doesn't contain a package at the + // module root. + continue + } + } + todo = append(todo, p) + } + + // If -d was specified, we're done after the download: no build. + // (The load.PackagesAndErrors is what did the download + // of the named packages and their dependencies.) + if len(todo) > 0 && !*getD { + work.InstallPackages(install, todo) + } + } +} + +// getQuery evaluates the given package path, version pair +// to determine the underlying module version being requested. +// If forceModulePath is set, getQuery must interpret path +// as a module path. +func getQuery(path, vers string, forceModulePath bool) (module.Version, error) { + if vers == "" { + vers = "latest" + } + + // First choice is always to assume path is a module path. + // If that works out, we're done. + info, err := modload.Query(path, vers, modload.Allowed) + if err == nil { + return module.Version{Path: path, Version: info.Version}, nil + } + + // Even if the query fails, if the path must be a real module, then report the query error. + if forceModulePath || *getM { + return module.Version{}, err + } + + // Otherwise, try a package path. + m, _, err := modload.QueryPackage(path, vers, modload.Allowed) + return m, err +} + +// An upgrader adapts an underlying mvs.Reqs to apply an +// upgrade policy to a list of targets and their dependencies. +// If patch=false, the upgrader implements "get -u". +// If patch=true, the upgrader implements "get -u=patch". +type upgrader struct { + mvs.Reqs + targets []module.Version + patch bool + tasks map[string]*task +} + +// upgradeTarget is a fake "target" requiring all the modules to be upgraded. +var upgradeTarget = module.Version{Path: "upgrade target", Version: ""} + +// Required returns the requirement list for m. +// Other than the upgradeTarget, we defer to u.Reqs. +func (u *upgrader) Required(m module.Version) ([]module.Version, error) { + if m == upgradeTarget { + return u.targets, nil + } + return u.Reqs.Required(m) +} + +// Upgrade returns the desired upgrade for m. +// If m is a tagged version, then Upgrade returns the latest tagged version. +// If m is a pseudo-version, then Upgrade returns the latest tagged version +// when that version has a time-stamp newer than m. +// Otherwise Upgrade returns m (preserving the pseudo-version). +// This special case prevents accidental downgrades +// when already using a pseudo-version newer than the latest tagged version. +func (u *upgrader) Upgrade(m module.Version) (module.Version, error) { + // Allow pkg@vers on the command line to override the upgrade choice v. + // If t's version is < v, then we're going to downgrade anyway, + // and it's cleaner to avoid moving back and forth and picking up + // extraneous other newer dependencies. + // If t's version is > v, then we're going to upgrade past v anyway, + // and again it's cleaner to avoid moving back and forth picking up + // extraneous other newer dependencies. + if t := u.tasks[m.Path]; t != nil { + return t.m, nil + } + + // Note that query "latest" is not the same as + // using repo.Latest. + // The query only falls back to untagged versions + // if nothing is tagged. The Latest method + // only ever returns untagged versions, + // which is not what we want. + query := "latest" + if u.patch { + // For patch upgrade, query "v1.2". + query = semver.MajorMinor(m.Version) + } + info, err := modload.Query(m.Path, query, modload.Allowed) + if err != nil { + // Report error but return m, to let version selection continue. + // (Reporting the error will fail the command at the next base.ExitIfErrors.) + // Special case: if the error is "no matching versions" then don't + // even report the error. Because Query does not consider pseudo-versions, + // it may happen that we have a pseudo-version but during -u=patch + // the query v0.0 matches no versions (not even the one we're using). + if !strings.Contains(err.Error(), "no matching versions") { + base.Errorf("go get: upgrading %s@%s: %v", m.Path, m.Version, err) + } + return m, nil + } + + // If we're on a later prerelease, keep using it, + // even though normally an Upgrade will ignore prereleases. + if semver.Compare(info.Version, m.Version) < 0 { + return m, nil + } + + // If we're on a pseudo-version chronologically after the latest tagged version, keep using it. + // This avoids some accidental downgrades. + if mTime, err := modfetch.PseudoVersionTime(m.Version); err == nil && info.Time.Before(mTime) { + return m, nil + } + + return module.Version{Path: m.Path, Version: info.Version}, nil +} diff --git a/libgo/go/cmd/go/internal/modinfo/info.go b/libgo/go/cmd/go/internal/modinfo/info.go new file mode 100644 index 0000000..7341ce4 --- /dev/null +++ b/libgo/go/cmd/go/internal/modinfo/info.go @@ -0,0 +1,49 @@ +// Copyright 2018 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 modinfo + +import "time" + +// Note that these structs are publicly visible (part of go list's API) +// and the fields are documented in the help text in ../list/list.go + +type ModulePublic struct { + Path string `json:",omitempty"` // module path + Version string `json:",omitempty"` // module version + Versions []string `json:",omitempty"` // available module versions + Replace *ModulePublic `json:",omitempty"` // replaced by this module + Time *time.Time `json:",omitempty"` // time version was created + Update *ModulePublic `json:",omitempty"` // available update (with -u) + Main bool `json:",omitempty"` // is this the main module? + Indirect bool `json:",omitempty"` // module is only indirectly needed by main module + Dir string `json:",omitempty"` // directory holding local copy of files, if any + GoMod string `json:",omitempty"` // path to go.mod file describing module, if any + Error *ModuleError `json:",omitempty"` // error loading module + GoVersion string `json:",omitempty"` // go version used in module +} + +type ModuleError struct { + Err string // error text +} + +func (m *ModulePublic) String() string { + s := m.Path + if m.Version != "" { + s += " " + m.Version + if m.Update != nil { + s += " [" + m.Update.Version + "]" + } + } + if m.Replace != nil { + s += " => " + m.Replace.Path + if m.Replace.Version != "" { + s += " " + m.Replace.Version + if m.Replace.Update != nil { + s += " [" + m.Replace.Update.Version + "]" + } + } + } + return s +} diff --git a/libgo/go/cmd/go/internal/modload/build.go b/libgo/go/cmd/go/internal/modload/build.go new file mode 100644 index 0000000..558401d --- /dev/null +++ b/libgo/go/cmd/go/internal/modload/build.go @@ -0,0 +1,243 @@ +// Copyright 2018 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 modload + +import ( + "bytes" + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/modfetch" + "cmd/go/internal/modinfo" + "cmd/go/internal/module" + "cmd/go/internal/search" + "encoding/hex" + "fmt" + "internal/goroot" + "os" + "path/filepath" + "strings" +) + +var ( + infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6") + infoEnd, _ = hex.DecodeString("f932433186182072008242104116d8f2") +) + +func isStandardImportPath(path string) bool { + return findStandardImportPath(path) != "" +} + +func findStandardImportPath(path string) string { + if search.IsStandardImportPath(path) { + if goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { + return filepath.Join(cfg.GOROOT, "src", path) + } + if goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, "vendor/"+path) { + return filepath.Join(cfg.GOROOT, "src/vendor", path) + } + } + return "" +} + +func PackageModuleInfo(pkgpath string) *modinfo.ModulePublic { + if isStandardImportPath(pkgpath) || !Enabled() { + return nil + } + return moduleInfo(findModule(pkgpath, pkgpath), true) +} + +func ModuleInfo(path string) *modinfo.ModulePublic { + if !Enabled() { + return nil + } + + if i := strings.Index(path, "@"); i >= 0 { + return moduleInfo(module.Version{Path: path[:i], Version: path[i+1:]}, false) + } + + for _, m := range BuildList() { + if m.Path == path { + return moduleInfo(m, true) + } + } + + return &modinfo.ModulePublic{ + Path: path, + Error: &modinfo.ModuleError{ + Err: "module not in current build", + }, + } +} + +// addUpdate fills in m.Update if an updated version is available. +func addUpdate(m *modinfo.ModulePublic) { + if m.Version != "" { + if info, err := Query(m.Path, "latest", Allowed); err == nil && info.Version != m.Version { + m.Update = &modinfo.ModulePublic{ + Path: m.Path, + Version: info.Version, + Time: &info.Time, + } + } + } +} + +// addVersions fills in m.Versions with the list of known versions. +func addVersions(m *modinfo.ModulePublic) { + m.Versions, _ = versions(m.Path) +} + +func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic { + if m == Target { + info := &modinfo.ModulePublic{ + Path: m.Path, + Version: m.Version, + Main: true, + Dir: ModRoot, + GoMod: filepath.Join(ModRoot, "go.mod"), + } + if modFile.Go != nil { + info.GoVersion = modFile.Go.Version + } + return info + } + + info := &modinfo.ModulePublic{ + Path: m.Path, + Version: m.Version, + Indirect: fromBuildList && loaded != nil && !loaded.direct[m.Path], + } + if loaded != nil { + info.GoVersion = loaded.goVersion[m.Path] + } + + if cfg.BuildMod == "vendor" { + info.Dir = filepath.Join(ModRoot, "vendor", m.Path) + return info + } + + // complete fills in the extra fields in m. + complete := func(m *modinfo.ModulePublic) { + if m.Version != "" { + if q, err := Query(m.Path, m.Version, nil); err != nil { + m.Error = &modinfo.ModuleError{Err: err.Error()} + } else { + m.Version = q.Version + m.Time = &q.Time + } + + mod := module.Version{Path: m.Path, Version: m.Version} + gomod, err := modfetch.CachePath(mod, "mod") + if err == nil { + if info, err := os.Stat(gomod); err == nil && info.Mode().IsRegular() { + m.GoMod = gomod + } + } + dir, err := modfetch.DownloadDir(mod) + if err == nil { + if info, err := os.Stat(dir); err == nil && info.IsDir() { + m.Dir = dir + } + } + } + if cfg.BuildMod == "vendor" { + m.Dir = filepath.Join(ModRoot, "vendor", m.Path) + } + } + + complete(info) + + if fromBuildList { + if r := Replacement(m); r.Path != "" { + info.Replace = &modinfo.ModulePublic{ + Path: r.Path, + Version: r.Version, + GoVersion: info.GoVersion, + } + if r.Version == "" { + if filepath.IsAbs(r.Path) { + info.Replace.Dir = r.Path + } else { + info.Replace.Dir = filepath.Join(ModRoot, r.Path) + } + } + complete(info.Replace) + info.Dir = info.Replace.Dir + info.GoMod = filepath.Join(info.Dir, "go.mod") + info.Error = nil // ignore error loading original module version (it has been replaced) + } + } + + return info +} + +func PackageBuildInfo(path string, deps []string) string { + if isStandardImportPath(path) || !Enabled() { + return "" + } + target := findModule(path, path) + mdeps := make(map[module.Version]bool) + for _, dep := range deps { + if !isStandardImportPath(dep) { + mdeps[findModule(path, dep)] = true + } + } + var mods []module.Version + delete(mdeps, target) + for mod := range mdeps { + mods = append(mods, mod) + } + module.Sort(mods) + + var buf bytes.Buffer + fmt.Fprintf(&buf, "path\t%s\n", path) + tv := target.Version + if tv == "" { + tv = "(devel)" + } + fmt.Fprintf(&buf, "mod\t%s\t%s\t%s\n", target.Path, tv, modfetch.Sum(target)) + for _, mod := range mods { + mv := mod.Version + if mv == "" { + mv = "(devel)" + } + r := Replacement(mod) + h := "" + if r.Path == "" { + h = "\t" + modfetch.Sum(mod) + } + fmt.Fprintf(&buf, "dep\t%s\t%s%s\n", mod.Path, mod.Version, h) + if r.Path != "" { + fmt.Fprintf(&buf, "=>\t%s\t%s\t%s\n", r.Path, r.Version, modfetch.Sum(r)) + } + } + return buf.String() +} + +func findModule(target, path string) module.Version { + // TODO: This should use loaded. + if path == "." { + return buildList[0] + } + for _, mod := range buildList { + if maybeInModule(path, mod.Path) { + return mod + } + } + base.Fatalf("build %v: cannot find module for path %v", target, path) + panic("unreachable") +} + +func ModInfoProg(info string) []byte { + return []byte(fmt.Sprintf(` + package main + import _ "unsafe" + //go:linkname __debug_modinfo__ runtime/debug.modinfo + var __debug_modinfo__ string + func init() { + __debug_modinfo__ = %q + } + `, string(infoStart)+info+string(infoEnd))) +} diff --git a/libgo/go/cmd/go/internal/modload/help.go b/libgo/go/cmd/go/internal/modload/help.go new file mode 100644 index 0000000..f2f3419 --- /dev/null +++ b/libgo/go/cmd/go/internal/modload/help.go @@ -0,0 +1,462 @@ +// Copyright 2018 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 modload + +import "cmd/go/internal/base" + +// TODO(rsc): The "module code layout" section needs to be written. + +var HelpModules = &base.Command{ + UsageLine: "modules", + Short: "modules, module versions, and more", + Long: ` +A module is a collection of related Go packages. +Modules are the unit of source code interchange and versioning. +The go command has direct support for working with modules, +including recording and resolving dependencies on other modules. +Modules replace the old GOPATH-based approach to specifying +which source files are used in a given build. + +Preliminary module support + +Go 1.11 includes preliminary support for Go modules, +including a new module-aware 'go get' command. +We intend to keep revising this support, while preserving compatibility, +until it can be declared official (no longer preliminary), +and then at a later point we may remove support for work +in GOPATH and the old 'go get' command. + +The quickest way to take advantage of the new Go 1.11 module support +is to check out your repository into a directory outside GOPATH/src, +create a go.mod file (described in the next section) there, and run +go commands from within that file tree. + +For more fine-grained control, the module support in Go 1.11 respects +a temporary environment variable, GO111MODULE, which can be set to one +of three string values: off, on, or auto (the default). +If GO111MODULE=off, then the go command never uses the +new module support. Instead it looks in vendor directories and GOPATH +to find dependencies; we now refer to this as "GOPATH mode." +If GO111MODULE=on, then the go command requires the use of modules, +never consulting GOPATH. We refer to this as the command being +module-aware or running in "module-aware mode". +If GO111MODULE=auto or is unset, then the go command enables or +disables module support based on the current directory. +Module support is enabled only when the current directory is outside +GOPATH/src and itself contains a go.mod file or is below a directory +containing a go.mod file. + +In module-aware mode, GOPATH no longer defines the meaning of imports +during a build, but it still stores downloaded dependencies (in GOPATH/pkg/mod) +and installed commands (in GOPATH/bin, unless GOBIN is set). + +Defining a module + +A module is defined by a tree of Go source files with a go.mod file +in the tree's root directory. The directory containing the go.mod file +is called the module root. Typically the module root will also correspond +to a source code repository root (but in general it need not). +The module is the set of all Go packages in the module root and its +subdirectories, but excluding subtrees with their own go.mod files. + +The "module path" is the import path prefix corresponding to the module root. +The go.mod file defines the module path and lists the specific versions +of other modules that should be used when resolving imports during a build, +by giving their module paths and versions. + +For example, this go.mod declares that the directory containing it is the root +of the module with path example.com/m, and it also declares that the module +depends on specific versions of golang.org/x/text and gopkg.in/yaml.v2: + + module example.com/m + + require ( + golang.org/x/text v0.3.0 + gopkg.in/yaml.v2 v2.1.0 + ) + +The go.mod file can also specify replacements and excluded versions +that only apply when building the module directly; they are ignored +when the module is incorporated into a larger build. +For more about the go.mod file, see 'go help go.mod'. + +To start a new module, simply create a go.mod file in the root of the +module's directory tree, containing only a module statement. +The 'go mod init' command can be used to do this: + + go mod init example.com/m + +In a project already using an existing dependency management tool like +godep, glide, or dep, 'go mod init' will also add require statements +matching the existing configuration. + +Once the go.mod file exists, no additional steps are required: +go commands like 'go build', 'go test', or even 'go list' will automatically +add new dependencies as needed to satisfy imports. + +The main module and the build list + +The "main module" is the module containing the directory where the go command +is run. The go command finds the module root by looking for a go.mod in the +current directory, or else the current directory's parent directory, +or else the parent's parent directory, and so on. + +The main module's go.mod file defines the precise set of packages available +for use by the go command, through require, replace, and exclude statements. +Dependency modules, found by following require statements, also contribute +to the definition of that set of packages, but only through their go.mod +files' require statements: any replace and exclude statements in dependency +modules are ignored. The replace and exclude statements therefore allow the +main module complete control over its own build, without also being subject +to complete control by dependencies. + +The set of modules providing packages to builds is called the "build list". +The build list initially contains only the main module. Then the go command +adds to the list the exact module versions required by modules already +on the list, recursively, until there is nothing left to add to the list. +If multiple versions of a particular module are added to the list, +then at the end only the latest version (according to semantic version +ordering) is kept for use in the build. + +The 'go list' command provides information about the main module +and the build list. For example: + + go list -m # print path of main module + go list -m -f={{.Dir}} # print root directory of main module + go list -m all # print build list + +Maintaining module requirements + +The go.mod file is meant to be readable and editable by both +programmers and tools. The go command itself automatically updates the go.mod file +to maintain a standard formatting and the accuracy of require statements. + +Any go command that finds an unfamiliar import will look up the module +containing that import and add the latest version of that module +to go.mod automatically. In most cases, therefore, it suffices to +add an import to source code and run 'go build', 'go test', or even 'go list': +as part of analyzing the package, the go command will discover +and resolve the import and update the go.mod file. + +Any go command can determine that a module requirement is +missing and must be added, even when considering only a single +package from the module. On the other hand, determining that a module requirement +is no longer necessary and can be deleted requires a full view of +all packages in the module, across all possible build configurations +(architectures, operating systems, build tags, and so on). +The 'go mod tidy' command builds that view and then +adds any missing module requirements and removes unnecessary ones. + +As part of maintaining the require statements in go.mod, the go command +tracks which ones provide packages imported directly by the current module +and which ones provide packages only used indirectly by other module +dependencies. Requirements needed only for indirect uses are marked with a +"// indirect" comment in the go.mod file. Indirect requirements are +automatically removed from the go.mod file once they are implied by other +direct requirements. Indirect requirements only arise when using modules +that fail to state some of their own dependencies or when explicitly +upgrading a module's dependencies ahead of its own stated requirements. + +Because of this automatic maintenance, the information in go.mod is an +up-to-date, readable description of the build. + +The 'go get' command updates go.mod to change the module versions used in a +build. An upgrade of one module may imply upgrading others, and similarly a +downgrade of one module may imply downgrading others. The 'go get' command +makes these implied changes as well. If go.mod is edited directly, commands +like 'go build' or 'go list' will assume that an upgrade is intended and +automatically make any implied upgrades and update go.mod to reflect them. + +The 'go mod' command provides other functionality for use in maintaining +and understanding modules and go.mod files. See 'go help mod'. + +The -mod build flag provides additional control over updating and use of go.mod. + +If invoked with -mod=readonly, the go command is disallowed from the implicit +automatic updating of go.mod described above. Instead, it fails when any changes +to go.mod are needed. This setting is most useful to check that go.mod does +not need updates, such as in a continuous integration and testing system. +The "go get" command remains permitted to update go.mod even with -mod=readonly, +and the "go mod" commands do not take the -mod flag (or any other build flags). + +If invoked with -mod=vendor, the go command assumes that the vendor +directory holds the correct copies of dependencies and ignores +the dependency descriptions in go.mod. + +Pseudo-versions + +The go.mod file and the go command more generally use semantic versions as +the standard form for describing module versions, so that versions can be +compared to determine which should be considered earlier or later than another. +A module version like v1.2.3 is introduced by tagging a revision in the +underlying source repository. Untagged revisions can be referred to +using a "pseudo-version" like v0.0.0-yyyymmddhhmmss-abcdefabcdef, +where the time is the commit time in UTC and the final suffix is the prefix +of the commit hash. The time portion ensures that two pseudo-versions can +be compared to determine which happened later, the commit hash identifes +the underlying commit, and the prefix (v0.0.0- in this example) is derived from +the most recent tagged version in the commit graph before this commit. + +There are three pseudo-version forms: + +vX.0.0-yyyymmddhhmmss-abcdefabcdef is used when there is no earlier +versioned commit with an appropriate major version before the target commit. +(This was originally the only form, so some older go.mod files use this form +even for commits that do follow tags.) + +vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef is used when the most +recent versioned commit before the target commit is vX.Y.Z-pre. + +vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef is used when the most +recent versioned commit before the target commit is vX.Y.Z. + +Pseudo-versions never need to be typed by hand: the go command will accept +the plain commit hash and translate it into a pseudo-version (or a tagged +version if available) automatically. This conversion is an example of a +module query. + +Module queries + +The go command accepts a "module query" in place of a module version +both on the command line and in the main module's go.mod file. +(After evaluating a query found in the main module's go.mod file, +the go command updates the file to replace the query with its result.) + +A fully-specified semantic version, such as "v1.2.3", +evaluates to that specific version. + +A semantic version prefix, such as "v1" or "v1.2", +evaluates to the latest available tagged version with that prefix. + +A semantic version comparison, such as "<v1.2.3" or ">=v1.5.6", +evaluates to the available tagged version nearest to the comparison target +(the latest version for < and <=, the earliest version for > and >=). + +The string "latest" matches the latest available tagged version, +or else the underlying source repository's latest untagged revision. + +A revision identifier for the underlying source repository, +such as a commit hash prefix, revision tag, or branch name, +selects that specific code revision. If the revision is +also tagged with a semantic version, the query evaluates to +that semantic version. Otherwise the query evaluates to a +pseudo-version for the commit. + +All queries prefer release versions to pre-release versions. +For example, "<v1.2.3" will prefer to return "v1.2.2" +instead of "v1.2.3-pre1", even though "v1.2.3-pre1" is nearer +to the comparison target. + +Module versions disallowed by exclude statements in the +main module's go.mod are considered unavailable and cannot +be returned by queries. + +For example, these commands are all valid: + + go get github.com/gorilla/mux@latest # same (@latest is default for 'go get') + go get github.com/gorilla/mux@v1.6.2 # records v1.6.2 + go get github.com/gorilla/mux@e3702bed2 # records v1.6.2 + go get github.com/gorilla/mux@c856192 # records v0.0.0-20180517173623-c85619274f5d + go get github.com/gorilla/mux@master # records current meaning of master + +Module compatibility and semantic versioning + +The go command requires that modules use semantic versions and expects that +the versions accurately describe compatibility: it assumes that v1.5.4 is a +backwards-compatible replacement for v1.5.3, v1.4.0, and even v1.0.0. +More generally the go command expects that packages follow the +"import compatibility rule", which says: + +"If an old package and a new package have the same import path, +the new package must be backwards compatible with the old package." + +Because the go command assumes the import compatibility rule, +a module definition can only set the minimum required version of one +of its dependencies: it cannot set a maximum or exclude selected versions. +Still, the import compatibility rule is not a guarantee: it may be that +v1.5.4 is buggy and not a backwards-compatible replacement for v1.5.3. +Because of this, the go command never updates from an older version +to a newer version of a module unasked. + +In semantic versioning, changing the major version number indicates a lack +of backwards compatibility with earlier versions. To preserve import +compatibility, the go command requires that modules with major version v2 +or later use a module path with that major version as the final element. +For example, version v2.0.0 of example.com/m must instead use module path +example.com/m/v2, and packages in that module would use that path as +their import path prefix, as in example.com/m/v2/sub/pkg. Including the +major version number in the module path and import paths in this way is +called "semantic import versioning". Pseudo-versions for modules with major +version v2 and later begin with that major version instead of v0, as in +v2.0.0-20180326061214-4fc5987536ef. + +As a special case, module paths beginning with gopkg.in/ continue to use the +conventions established on that system: the major version is always present, +and it is preceded by a dot instead of a slash: gopkg.in/yaml.v1 +and gopkg.in/yaml.v2, not gopkg.in/yaml and gopkg.in/yaml/v2. + +The go command treats modules with different module paths as unrelated: +it makes no connection between example.com/m and example.com/m/v2. +Modules with different major versions can be used together in a build +and are kept separate by the fact that their packages use different +import paths. + +In semantic versioning, major version v0 is for initial development, +indicating no expectations of stability or backwards compatibility. +Major version v0 does not appear in the module path, because those +versions are preparation for v1.0.0, and v1 does not appear in the +module path either. + +Code written before the semantic import versioning convention +was introduced may use major versions v2 and later to describe +the same set of unversioned import paths as used in v0 and v1. +To accommodate such code, if a source code repository has a +v2.0.0 or later tag for a file tree with no go.mod, the version is +considered to be part of the v1 module's available versions +and is given an +incompatible suffix when converted to a module +version, as in v2.0.0+incompatible. The +incompatible tag is also +applied to pseudo-versions derived from such versions, as in +v2.0.1-0.yyyymmddhhmmss-abcdefabcdef+incompatible. + +In general, having a dependency in the build list (as reported by 'go list -m all') +on a v0 version, pre-release version, pseudo-version, or +incompatible version +is an indication that problems are more likely when upgrading that +dependency, since there is no expectation of compatibility for those. + +See https://research.swtch.com/vgo-import for more information about +semantic import versioning, and see https://semver.org/ for more about +semantic versioning. + +Module code layout + +For now, see https://research.swtch.com/vgo-module for information +about how source code in version control systems is mapped to +module file trees. + +Module downloading and verification + +The go command maintains, in the main module's root directory alongside +go.mod, a file named go.sum containing the expected cryptographic checksums +of the content of specific module versions. Each time a dependency is +used, its checksum is added to go.sum if missing or else required to match +the existing entry in go.sum. + +The go command maintains a cache of downloaded packages and computes +and records the cryptographic checksum of each package at download time. +In normal operation, the go command checks these pre-computed checksums +against the main module's go.sum file, instead of recomputing them on +each command invocation. The 'go mod verify' command checks that +the cached copies of module downloads still match both their recorded +checksums and the entries in go.sum. + +The go command can fetch modules from a proxy instead of connecting +to source control systems directly, according to the setting of the GOPROXY +environment variable. + +See 'go help goproxy' for details about the proxy and also the format of +the cached downloaded packages. + +Modules and vendoring + +When using modules, the go command completely ignores vendor directories. + +By default, the go command satisfies dependencies by downloading modules +from their sources and using those downloaded copies (after verification, +as described in the previous section). To allow interoperation with older +versions of Go, or to ensure that all files used for a build are stored +together in a single file tree, 'go mod vendor' creates a directory named +vendor in the root directory of the main module and stores there all the +packages from dependency modules that are needed to support builds and +tests of packages in the main module. + +To build using the main module's top-level vendor directory to satisfy +dependencies (disabling use of the usual network sources and local +caches), use 'go build -mod=vendor'. Note that only the main module's +top-level vendor directory is used; vendor directories in other locations +are still ignored. + `, +} + +var HelpGoMod = &base.Command{ + UsageLine: "go.mod", + Short: "the go.mod file", + Long: ` +A module version is defined by a tree of source files, with a go.mod +file in its root. When the go command is run, it looks in the current +directory and then successive parent directories to find the go.mod +marking the root of the main (current) module. + +The go.mod file itself is line-oriented, with // comments but +no /* */ comments. Each line holds a single directive, made up of a +verb followed by arguments. For example: + + module my/thing + require other/thing v1.0.2 + require new/thing v2.3.4 + exclude old/thing v1.2.3 + replace bad/thing v1.4.5 => good/thing v1.4.5 + +The verbs are module, to define the module path; require, to require +a particular module at a given version or later; exclude, to exclude +a particular module version from use; and replace, to replace a module +version with a different module version. Exclude and replace apply only +in the main module's go.mod and are ignored in dependencies. +See https://research.swtch.com/vgo-mvs for details. + +The leading verb can be factored out of adjacent lines to create a block, +like in Go imports: + + require ( + new/thing v2.3.4 + old/thing v1.2.3 + ) + +The go.mod file is designed both to be edited directly and to be +easily updated by tools. The 'go mod edit' command can be used to +parse and edit the go.mod file from programs and tools. +See 'go help mod edit'. + +The go command automatically updates go.mod each time it uses the +module graph, to make sure go.mod always accurately reflects reality +and is properly formatted. For example, consider this go.mod file: + + module M + + require ( + A v1 + B v1.0.0 + C v1.0.0 + D v1.2.3 + E dev + ) + + exclude D v1.2.3 + +The update rewrites non-canonical version identifiers to semver form, +so A's v1 becomes v1.0.0 and E's dev becomes the pseudo-version for the +latest commit on the dev branch, perhaps v0.0.0-20180523231146-b3f5c0f6e5f1. + +The update modifies requirements to respect exclusions, so the +requirement on the excluded D v1.2.3 is updated to use the next +available version of D, perhaps D v1.2.4 or D v1.3.0. + +The update removes redundant or misleading requirements. +For example, if A v1.0.0 itself requires B v1.2.0 and C v1.0.0, +then go.mod's requirement of B v1.0.0 is misleading (superseded by +A's need for v1.2.0), and its requirement of C v1.0.0 is redundant +(implied by A's need for the same version), so both will be removed. +If module M contains packages that directly import packages from B or +C, then the requirements will be kept but updated to the actual +versions being used. + +Finally, the update reformats the go.mod in a canonical formatting, so +that future mechanical changes will result in minimal diffs. + +Because the module graph defines the meaning of import statements, any +commands that load packages also use and therefore update go.mod, +including go build, go get, go install, go list, go test, go mod graph, +go mod tidy, and go mod why. + `, +} diff --git a/libgo/go/cmd/go/internal/modload/import.go b/libgo/go/cmd/go/internal/modload/import.go new file mode 100644 index 0000000..44c2a23 --- /dev/null +++ b/libgo/go/cmd/go/internal/modload/import.go @@ -0,0 +1,228 @@ +// Copyright 2018 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 modload + +import ( + "bytes" + "errors" + "fmt" + "go/build" + "internal/goroot" + "os" + "path/filepath" + "strings" + + "cmd/go/internal/cfg" + "cmd/go/internal/modfetch/codehost" + "cmd/go/internal/module" + "cmd/go/internal/par" + "cmd/go/internal/search" +) + +type ImportMissingError struct { + ImportPath string + Module module.Version +} + +func (e *ImportMissingError) Error() string { + if e.Module.Path == "" { + return "cannot find module providing package " + e.ImportPath + } + return "missing module for import: " + e.Module.Path + "@" + e.Module.Version + " provides " + e.ImportPath +} + +// Import finds the module and directory in the build list +// containing the package with the given import path. +// The answer must be unique: Import returns an error +// if multiple modules attempt to provide the same package. +// Import can return a module with an empty m.Path, for packages in the standard library. +// Import can return an empty directory string, for fake packages like "C" and "unsafe". +// +// If the package cannot be found in the current build list, +// Import returns an ImportMissingError as the error. +// If Import can identify a module that could be added to supply the package, +// the ImportMissingError records that module. +func Import(path string) (m module.Version, dir string, err error) { + if strings.Contains(path, "@") { + return module.Version{}, "", fmt.Errorf("import path should not have @version") + } + if build.IsLocalImport(path) { + return module.Version{}, "", fmt.Errorf("relative import not supported") + } + if path == "C" || path == "unsafe" { + // There's no directory for import "C" or import "unsafe". + return module.Version{}, "", nil + } + + // Is the package in the standard library? + if search.IsStandardImportPath(path) { + if strings.HasPrefix(path, "golang_org/") { + return module.Version{}, filepath.Join(cfg.GOROOT, "src/vendor", path), nil + } + if goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { + dir := filepath.Join(cfg.GOROOT, "src", path) + return module.Version{}, dir, nil + } + } + + // -mod=vendor is special. + // Everything must be in the main module or the main module's vendor directory. + if cfg.BuildMod == "vendor" { + mainDir, mainOK := dirInModule(path, Target.Path, ModRoot, true) + vendorDir, vendorOK := dirInModule(path, "", filepath.Join(ModRoot, "vendor"), false) + if mainOK && vendorOK { + return module.Version{}, "", fmt.Errorf("ambiguous import: found %s in multiple directories:\n\t%s\n\t%s", path, mainDir, vendorDir) + } + // Prefer to return main directory if there is one, + // Note that we're not checking that the package exists. + // We'll leave that for load. + if !vendorOK && mainDir != "" { + return Target, mainDir, nil + } + readVendorList() + return vendorMap[path], vendorDir, nil + } + + // Check each module on the build list. + var dirs []string + var mods []module.Version + for _, m := range buildList { + if !maybeInModule(path, m.Path) { + // Avoid possibly downloading irrelevant modules. + continue + } + root, isLocal, err := fetch(m) + if err != nil { + // Report fetch error. + // Note that we don't know for sure this module is necessary, + // but it certainly _could_ provide the package, and even if we + // continue the loop and find the package in some other module, + // we need to look at this module to make sure the import is + // not ambiguous. + return module.Version{}, "", err + } + dir, ok := dirInModule(path, m.Path, root, isLocal) + if ok { + mods = append(mods, m) + dirs = append(dirs, dir) + } + } + if len(mods) == 1 { + return mods[0], dirs[0], nil + } + if len(mods) > 0 { + var buf bytes.Buffer + fmt.Fprintf(&buf, "ambiguous import: found %s in multiple modules:", path) + for i, m := range mods { + fmt.Fprintf(&buf, "\n\t%s", m.Path) + if m.Version != "" { + fmt.Fprintf(&buf, " %s", m.Version) + } + fmt.Fprintf(&buf, " (%s)", dirs[i]) + } + return module.Version{}, "", errors.New(buf.String()) + } + + // Not on build list. + + // Look up module containing the package, for addition to the build list. + // Goal is to determine the module, download it to dir, and return m, dir, ErrMissing. + if cfg.BuildMod == "readonly" { + return module.Version{}, "", fmt.Errorf("import lookup disabled by -mod=%s", cfg.BuildMod) + } + + m, _, err = QueryPackage(path, "latest", Allowed) + if err != nil { + if _, ok := err.(*codehost.VCSError); ok { + return module.Version{}, "", err + } + return module.Version{}, "", &ImportMissingError{ImportPath: path} + } + return m, "", &ImportMissingError{ImportPath: path, Module: m} +} + +// maybeInModule reports whether, syntactically, +// a package with the given import path could be supplied +// by a module with the given module path (mpath). +func maybeInModule(path, mpath string) bool { + return mpath == path || + len(path) > len(mpath) && path[len(mpath)] == '/' && path[:len(mpath)] == mpath +} + +var haveGoModCache, haveGoFilesCache par.Cache + +// dirInModule locates the directory that would hold the package named by the given path, +// if it were in the module with module path mpath and root mdir. +// If path is syntactically not within mpath, +// or if mdir is a local file tree (isLocal == true) and the directory +// that would hold path is in a sub-module (covered by a go.mod below mdir), +// dirInModule returns "", false. +// +// Otherwise, dirInModule returns the name of the directory where +// Go source files would be expected, along with a boolean indicating +// whether there are in fact Go source files in that directory. +func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFiles bool) { + // Determine where to expect the package. + if path == mpath { + dir = mdir + } else if mpath == "" { // vendor directory + dir = filepath.Join(mdir, path) + } else if len(path) > len(mpath) && path[len(mpath)] == '/' && path[:len(mpath)] == mpath { + dir = filepath.Join(mdir, path[len(mpath)+1:]) + } else { + return "", false + } + + // Check that there aren't other modules in the way. + // This check is unnecessary inside the module cache + // and important to skip in the vendor directory, + // where all the module trees have been overlaid. + // So we only check local module trees + // (the main module, and any directory trees pointed at by replace directives). + if isLocal { + for d := dir; d != mdir && len(d) > len(mdir); { + haveGoMod := haveGoModCache.Do(d, func() interface{} { + _, err := os.Stat(filepath.Join(d, "go.mod")) + return err == nil + }).(bool) + + if haveGoMod { + return "", false + } + parent := filepath.Dir(d) + if parent == d { + // Break the loop, as otherwise we'd loop + // forever if d=="." and mdir=="". + break + } + d = parent + } + } + + // Now committed to returning dir (not ""). + + // Are there Go source files in the directory? + // We don't care about build tags, not even "+build ignore". + // We're just looking for a plausible directory. + haveGoFiles = haveGoFilesCache.Do(dir, func() interface{} { + f, err := os.Open(dir) + if err != nil { + return false + } + defer f.Close() + names, _ := f.Readdirnames(-1) + for _, name := range names { + if strings.HasSuffix(name, ".go") { + info, err := os.Stat(filepath.Join(dir, name)) + if err == nil && info.Mode().IsRegular() { + return true + } + } + } + return false + }).(bool) + + return dir, haveGoFiles +} diff --git a/libgo/go/cmd/go/internal/modload/import_test.go b/libgo/go/cmd/go/internal/modload/import_test.go new file mode 100644 index 0000000..3f4ddab --- /dev/null +++ b/libgo/go/cmd/go/internal/modload/import_test.go @@ -0,0 +1,59 @@ +// Copyright 2018 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 modload + +import ( + "internal/testenv" + "regexp" + "strings" + "testing" +) + +var importTests = []struct { + path string + err string +}{ + { + path: "golang.org/x/net/context", + err: "missing module for import: golang.org/x/net@.* provides golang.org/x/net/context", + }, + { + path: "golang.org/x/net", + err: "cannot find module providing package golang.org/x/net", + }, + { + path: "golang.org/x/text", + err: "missing module for import: golang.org/x/text@.* provides golang.org/x/text", + }, + { + path: "github.com/rsc/quote/buggy", + err: "missing module for import: github.com/rsc/quote@v1.5.2 provides github.com/rsc/quote/buggy", + }, + { + path: "github.com/rsc/quote", + err: "missing module for import: github.com/rsc/quote@v1.5.2 provides github.com/rsc/quote", + }, + { + path: "golang.org/x/foo/bar", + err: "cannot find module providing package golang.org/x/foo/bar", + }, +} + +func TestImport(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + for _, tt := range importTests { + t.Run(strings.Replace(tt.path, "/", "_", -1), func(t *testing.T) { + // Note that there is no build list, so Import should always fail. + m, dir, err := Import(tt.path) + if err == nil { + t.Fatalf("Import(%q) = %v, %v, nil; expected error", tt.path, m, dir) + } + if !regexp.MustCompile(tt.err).MatchString(err.Error()) { + t.Fatalf("Import(%q): error %q, want error matching %#q", tt.path, err, tt.err) + } + }) + } +} diff --git a/libgo/go/cmd/go/internal/modload/init.go b/libgo/go/cmd/go/internal/modload/init.go new file mode 100644 index 0000000..f995bad --- /dev/null +++ b/libgo/go/cmd/go/internal/modload/init.go @@ -0,0 +1,600 @@ +// Copyright 2018 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 modload + +import ( + "bytes" + "cmd/go/internal/base" + "cmd/go/internal/cache" + "cmd/go/internal/cfg" + "cmd/go/internal/load" + "cmd/go/internal/modconv" + "cmd/go/internal/modfetch" + "cmd/go/internal/modfetch/codehost" + "cmd/go/internal/modfile" + "cmd/go/internal/module" + "cmd/go/internal/mvs" + "cmd/go/internal/search" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "regexp" + "strconv" + "strings" +) + +var ( + cwd string + MustUseModules = mustUseModules() + initialized bool + + ModRoot string + modFile *modfile.File + excluded map[module.Version]bool + Target module.Version + + gopath string + + CmdModInit bool // running 'go mod init' + CmdModModule string // module argument for 'go mod init' +) + +// ModFile returns the parsed go.mod file. +// +// Note that after calling ImportPaths or LoadBuildList, +// the require statements in the modfile.File are no longer +// the source of truth and will be ignored: edits made directly +// will be lost at the next call to WriteGoMod. +// To make permanent changes to the require statements +// in go.mod, edit it before calling ImportPaths or LoadBuildList. +func ModFile() *modfile.File { + return modFile +} + +func BinDir() string { + MustInit() + return filepath.Join(gopath, "bin") +} + +// mustUseModules reports whether we are invoked as vgo +// (as opposed to go). +// If so, we only support builds with go.mod files. +func mustUseModules() bool { + name := os.Args[0] + name = name[strings.LastIndex(name, "/")+1:] + name = name[strings.LastIndex(name, `\`)+1:] + return strings.HasPrefix(name, "vgo") +} + +var inGOPATH bool // running in GOPATH/src + +func Init() { + if initialized { + return + } + initialized = true + + env := os.Getenv("GO111MODULE") + switch env { + default: + base.Fatalf("go: unknown environment setting GO111MODULE=%s", env) + case "", "auto": + // leave MustUseModules alone + case "on": + MustUseModules = true + case "off": + if !MustUseModules { + return + } + } + + // Disable any prompting for passwords by Git. + // Only has an effect for 2.3.0 or later, but avoiding + // the prompt in earlier versions is just too hard. + // If user has explicitly set GIT_TERMINAL_PROMPT=1, keep + // prompting. + // See golang.org/issue/9341 and golang.org/issue/12706. + if os.Getenv("GIT_TERMINAL_PROMPT") == "" { + os.Setenv("GIT_TERMINAL_PROMPT", "0") + } + + // Disable any ssh connection pooling by Git. + // If a Git subprocess forks a child into the background to cache a new connection, + // that child keeps stdout/stderr open. After the Git subprocess exits, + // os /exec expects to be able to read from the stdout/stderr pipe + // until EOF to get all the data that the Git subprocess wrote before exiting. + // The EOF doesn't come until the child exits too, because the child + // is holding the write end of the pipe. + // This is unfortunate, but it has come up at least twice + // (see golang.org/issue/13453 and golang.org/issue/16104) + // and confuses users when it does. + // If the user has explicitly set GIT_SSH or GIT_SSH_COMMAND, + // assume they know what they are doing and don't step on it. + // But default to turning off ControlMaster. + if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" { + os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no") + } + + var err error + cwd, err = os.Getwd() + if err != nil { + base.Fatalf("go: %v", err) + } + + inGOPATH = false + for _, gopath := range filepath.SplitList(cfg.BuildContext.GOPATH) { + if gopath == "" { + continue + } + if search.InDir(cwd, filepath.Join(gopath, "src")) != "" { + inGOPATH = true + break + } + } + + if inGOPATH && !MustUseModules { + // No automatic enabling in GOPATH. + if root, _ := FindModuleRoot(cwd, "", false); root != "" { + cfg.GoModInGOPATH = filepath.Join(root, "go.mod") + } + return + } + + if CmdModInit { + // Running 'go mod init': go.mod will be created in current directory. + ModRoot = cwd + } else { + ModRoot, _ = FindModuleRoot(cwd, "", MustUseModules) + if !MustUseModules { + if ModRoot == "" { + return + } + if search.InDir(ModRoot, os.TempDir()) == "." { + // If you create /tmp/go.mod for experimenting, + // then any tests that create work directories under /tmp + // will find it and get modules when they're not expecting them. + // It's a bit of a peculiar thing to disallow but quite mysterious + // when it happens. See golang.org/issue/26708. + ModRoot = "" + fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir()) + return + } + } + } + + cfg.ModulesEnabled = true + load.ModBinDir = BinDir + load.ModLookup = Lookup + load.ModPackageModuleInfo = PackageModuleInfo + load.ModImportPaths = ImportPaths + load.ModPackageBuildInfo = PackageBuildInfo + load.ModInfoProg = ModInfoProg + load.ModImportFromFiles = ImportFromFiles + load.ModDirImportPath = DirImportPath + + search.SetModRoot(ModRoot) +} + +func init() { + load.ModInit = Init + + // Set modfetch.PkgMod unconditionally, so that go clean -modcache can run even without modules enabled. + if list := filepath.SplitList(cfg.BuildContext.GOPATH); len(list) > 0 && list[0] != "" { + modfetch.PkgMod = filepath.Join(list[0], "pkg/mod") + } +} + +// Enabled reports whether modules are (or must be) enabled. +// If modules must be enabled but are not, Enabled returns true +// and then the first use of module information will call die +// (usually through InitMod and MustInit). +func Enabled() bool { + if !initialized { + panic("go: Enabled called before Init") + } + return ModRoot != "" || MustUseModules +} + +// MustInit calls Init if needed and checks that +// modules are enabled and the main module has been found. +// If not, MustInit calls base.Fatalf with an appropriate message. +func MustInit() { + if Init(); ModRoot == "" { + die() + } + if c := cache.Default(); c == nil { + // With modules, there are no install locations for packages + // other than the build cache. + base.Fatalf("go: cannot use modules with build cache disabled") + } +} + +// Failed reports whether module loading failed. +// If Failed returns true, then any use of module information will call die. +func Failed() bool { + Init() + return cfg.ModulesEnabled && ModRoot == "" +} + +func die() { + if os.Getenv("GO111MODULE") == "off" { + base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'") + } + if inGOPATH && !MustUseModules { + base.Fatalf("go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'") + } + base.Fatalf("go: cannot find main module; see 'go help modules'") +} + +func InitMod() { + MustInit() + if modFile != nil { + return + } + + list := filepath.SplitList(cfg.BuildContext.GOPATH) + if len(list) == 0 || list[0] == "" { + base.Fatalf("missing $GOPATH") + } + gopath = list[0] + if _, err := os.Stat(filepath.Join(gopath, "go.mod")); err == nil { + base.Fatalf("$GOPATH/go.mod exists but should not") + } + + oldSrcMod := filepath.Join(list[0], "src/mod") + pkgMod := filepath.Join(list[0], "pkg/mod") + infoOld, errOld := os.Stat(oldSrcMod) + _, errMod := os.Stat(pkgMod) + if errOld == nil && infoOld.IsDir() && errMod != nil && os.IsNotExist(errMod) { + os.Rename(oldSrcMod, pkgMod) + } + + modfetch.PkgMod = pkgMod + modfetch.GoSumFile = filepath.Join(ModRoot, "go.sum") + codehost.WorkRoot = filepath.Join(pkgMod, "cache/vcs") + + if CmdModInit { + // Running go mod init: do legacy module conversion + legacyModInit() + modFileToBuildList() + WriteGoMod() + return + } + + gomod := filepath.Join(ModRoot, "go.mod") + data, err := ioutil.ReadFile(gomod) + if err != nil { + if os.IsNotExist(err) { + legacyModInit() + modFileToBuildList() + WriteGoMod() + return + } + base.Fatalf("go: %v", err) + } + + f, err := modfile.Parse(gomod, data, fixVersion) + if err != nil { + // Errors returned by modfile.Parse begin with file:line. + base.Fatalf("go: errors parsing go.mod:\n%s\n", err) + } + modFile = f + + if len(f.Syntax.Stmt) == 0 || f.Module == nil { + // Empty mod file. Must add module path. + path, err := FindModulePath(ModRoot) + if err != nil { + base.Fatalf("go: %v", err) + } + f.AddModuleStmt(path) + } + + if len(f.Syntax.Stmt) == 1 && f.Module != nil { + // Entire file is just a module statement. + // Populate require if possible. + legacyModInit() + } + + excluded = make(map[module.Version]bool) + for _, x := range f.Exclude { + excluded[x.Mod] = true + } + modFileToBuildList() + WriteGoMod() +} + +// modFileToBuildList initializes buildList from the modFile. +func modFileToBuildList() { + Target = modFile.Module.Mod + list := []module.Version{Target} + for _, r := range modFile.Require { + list = append(list, r.Mod) + } + buildList = list +} + +// Allowed reports whether module m is allowed (not excluded) by the main module's go.mod. +func Allowed(m module.Version) bool { + return !excluded[m] +} + +func legacyModInit() { + if modFile == nil { + path, err := FindModulePath(ModRoot) + if err != nil { + base.Fatalf("go: %v", err) + } + fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", path) + modFile = new(modfile.File) + modFile.AddModuleStmt(path) + } + + for _, name := range altConfigs { + cfg := filepath.Join(ModRoot, name) + data, err := ioutil.ReadFile(cfg) + if err == nil { + convert := modconv.Converters[name] + if convert == nil { + return + } + fmt.Fprintf(os.Stderr, "go: copying requirements from %s\n", base.ShortPath(cfg)) + cfg = filepath.ToSlash(cfg) + if err := modconv.ConvertLegacyConfig(modFile, cfg, data); err != nil { + base.Fatalf("go: %v", err) + } + if len(modFile.Syntax.Stmt) == 1 { + // Add comment to avoid re-converting every time it runs. + modFile.AddComment("// go: no requirements found in " + name) + } + return + } + } +} + +var altConfigs = []string{ + "Gopkg.lock", + + "GLOCKFILE", + "Godeps/Godeps.json", + "dependencies.tsv", + "glide.lock", + "vendor.conf", + "vendor.yml", + "vendor/manifest", + "vendor/vendor.json", + + ".git/config", +} + +// Exported only for testing. +func FindModuleRoot(dir, limit string, legacyConfigOK bool) (root, file string) { + dir = filepath.Clean(dir) + dir1 := dir + limit = filepath.Clean(limit) + + // Look for enclosing go.mod. + for { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + return dir, "go.mod" + } + if dir == limit { + break + } + d := filepath.Dir(dir) + if d == dir { + break + } + dir = d + } + + // Failing that, look for enclosing alternate version config. + if legacyConfigOK { + dir = dir1 + for { + for _, name := range altConfigs { + if _, err := os.Stat(filepath.Join(dir, name)); err == nil { + return dir, name + } + } + if dir == limit { + break + } + d := filepath.Dir(dir) + if d == dir { + break + } + dir = d + } + } + + return "", "" +} + +// Exported only for testing. +func FindModulePath(dir string) (string, error) { + if CmdModModule != "" { + // Running go mod init x/y/z; return x/y/z. + return CmdModModule, nil + } + + // Cast about for import comments, + // first in top-level directory, then in subdirectories. + list, _ := ioutil.ReadDir(dir) + for _, info := range list { + if info.Mode().IsRegular() && strings.HasSuffix(info.Name(), ".go") { + if com := findImportComment(filepath.Join(dir, info.Name())); com != "" { + return com, nil + } + } + } + for _, info1 := range list { + if info1.IsDir() { + files, _ := ioutil.ReadDir(filepath.Join(dir, info1.Name())) + for _, info2 := range files { + if info2.Mode().IsRegular() && strings.HasSuffix(info2.Name(), ".go") { + if com := findImportComment(filepath.Join(dir, info1.Name(), info2.Name())); com != "" { + return path.Dir(com), nil + } + } + } + } + } + + // Look for Godeps.json declaring import path. + data, _ := ioutil.ReadFile(filepath.Join(dir, "Godeps/Godeps.json")) + var cfg1 struct{ ImportPath string } + json.Unmarshal(data, &cfg1) + if cfg1.ImportPath != "" { + return cfg1.ImportPath, nil + } + + // Look for vendor.json declaring import path. + data, _ = ioutil.ReadFile(filepath.Join(dir, "vendor/vendor.json")) + var cfg2 struct{ RootPath string } + json.Unmarshal(data, &cfg2) + if cfg2.RootPath != "" { + return cfg2.RootPath, nil + } + + // Look for path in GOPATH. + for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) { + if gpdir == "" { + continue + } + if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." { + return filepath.ToSlash(rel), nil + } + } + + // Look for .git/config with github origin as last resort. + data, _ = ioutil.ReadFile(filepath.Join(dir, ".git/config")) + if m := gitOriginRE.FindSubmatch(data); m != nil { + return "github.com/" + string(m[1]), nil + } + + return "", fmt.Errorf("cannot determine module path for source directory %s (outside GOPATH, no import comments)", dir) +} + +var ( + gitOriginRE = regexp.MustCompile(`(?m)^\[remote "origin"\]\r?\n\turl = (?:https://github.com/|git@github.com:|gh:)([^/]+/[^/]+?)(\.git)?\r?\n`) + importCommentRE = regexp.MustCompile(`(?m)^package[ \t]+[^ \t\r\n/]+[ \t]+//[ \t]+import[ \t]+(\"[^"]+\")[ \t]*\r?\n`) +) + +func findImportComment(file string) string { + data, err := ioutil.ReadFile(file) + if err != nil { + return "" + } + m := importCommentRE.FindSubmatch(data) + if m == nil { + return "" + } + path, err := strconv.Unquote(string(m[1])) + if err != nil { + return "" + } + return path +} + +var allowWriteGoMod = true + +// DisallowWriteGoMod causes future calls to WriteGoMod to do nothing at all. +func DisallowWriteGoMod() { + allowWriteGoMod = false +} + +// AllowWriteGoMod undoes the effect of DisallowWriteGoMod: +// future calls to WriteGoMod will update go.mod if needed. +// Note that any past calls have been discarded, so typically +// a call to AlowWriteGoMod should be followed by a call to WriteGoMod. +func AllowWriteGoMod() { + allowWriteGoMod = true +} + +// MinReqs returns a Reqs with minimal dependencies of Target, +// as will be written to go.mod. +func MinReqs() mvs.Reqs { + var direct []string + for _, m := range buildList[1:] { + if loaded.direct[m.Path] { + direct = append(direct, m.Path) + } + } + min, err := mvs.Req(Target, buildList, direct, Reqs()) + if err != nil { + base.Fatalf("go: %v", err) + } + return &mvsReqs{buildList: append([]module.Version{Target}, min...)} +} + +// WriteGoMod writes the current build list back to go.mod. +func WriteGoMod() { + // If we're using -mod=vendor we basically ignored + // go.mod, so definitely don't try to write back our + // incomplete view of the world. + if !allowWriteGoMod || cfg.BuildMod == "vendor" { + return + } + + if loaded != nil { + reqs := MinReqs() + min, err := reqs.Required(Target) + if err != nil { + base.Fatalf("go: %v", err) + } + var list []*modfile.Require + for _, m := range min { + list = append(list, &modfile.Require{ + Mod: m, + Indirect: !loaded.direct[m.Path], + }) + } + modFile.SetRequire(list) + } + + file := filepath.Join(ModRoot, "go.mod") + old, _ := ioutil.ReadFile(file) + modFile.Cleanup() // clean file after edits + new, err := modFile.Format() + if err != nil { + base.Fatalf("go: %v", err) + } + if !bytes.Equal(old, new) { + if cfg.BuildMod == "readonly" { + base.Fatalf("go: updates to go.mod needed, disabled by -mod=readonly") + } + if err := ioutil.WriteFile(file, new, 0666); err != nil { + base.Fatalf("go: %v", err) + } + } + modfetch.WriteGoSum() +} + +func fixVersion(path, vers string) (string, error) { + // Special case: remove the old -gopkgin- hack. + if strings.HasPrefix(path, "gopkg.in/") && strings.Contains(vers, "-gopkgin-") { + vers = vers[strings.Index(vers, "-gopkgin-")+len("-gopkgin-"):] + } + + // fixVersion is called speculatively on every + // module, version pair from every go.mod file. + // Avoid the query if it looks OK. + _, pathMajor, ok := module.SplitPathVersion(path) + if !ok { + return "", fmt.Errorf("malformed module path: %s", path) + } + if vers != "" && module.CanonicalVersion(vers) == vers && module.MatchPathMajor(vers, pathMajor) { + return vers, nil + } + + info, err := Query(path, vers, nil) + if err != nil { + return "", err + } + return info.Version, nil +} diff --git a/libgo/go/cmd/go/internal/modload/list.go b/libgo/go/cmd/go/internal/modload/list.go new file mode 100644 index 0000000..69a832d --- /dev/null +++ b/libgo/go/cmd/go/internal/modload/list.go @@ -0,0 +1,109 @@ +// Copyright 2018 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 modload + +import ( + "fmt" + "os" + "strings" + + "cmd/go/internal/base" + "cmd/go/internal/modinfo" + "cmd/go/internal/module" + "cmd/go/internal/par" + "cmd/go/internal/search" +) + +func ListModules(args []string, listU, listVersions bool) []*modinfo.ModulePublic { + mods := listModules(args) + if listU || listVersions { + var work par.Work + for _, m := range mods { + work.Add(m) + if m.Replace != nil { + work.Add(m.Replace) + } + } + work.Do(10, func(item interface{}) { + m := item.(*modinfo.ModulePublic) + if listU { + addUpdate(m) + } + if listVersions { + addVersions(m) + } + }) + } + return mods +} + +func listModules(args []string) []*modinfo.ModulePublic { + LoadBuildList() + if len(args) == 0 { + return []*modinfo.ModulePublic{moduleInfo(buildList[0], true)} + } + + var mods []*modinfo.ModulePublic + matchedBuildList := make([]bool, len(buildList)) + for _, arg := range args { + if strings.Contains(arg, `\`) { + base.Fatalf("go: module paths never use backslash") + } + if search.IsRelativePath(arg) { + base.Fatalf("go: cannot use relative path %s to specify module", arg) + } + if i := strings.Index(arg, "@"); i >= 0 { + info, err := Query(arg[:i], arg[i+1:], nil) + if err != nil { + mods = append(mods, &modinfo.ModulePublic{ + Path: arg[:i], + Version: arg[i+1:], + Error: &modinfo.ModuleError{ + Err: err.Error(), + }, + }) + continue + } + mods = append(mods, moduleInfo(module.Version{Path: arg[:i], Version: info.Version}, false)) + continue + } + + // Module path or pattern. + var match func(string) bool + var literal bool + if arg == "all" { + match = func(string) bool { return true } + } else if strings.Contains(arg, "...") { + match = search.MatchPattern(arg) + } else { + match = func(p string) bool { return arg == p } + literal = true + } + matched := false + for i, m := range buildList { + if match(m.Path) { + matched = true + if !matchedBuildList[i] { + matchedBuildList[i] = true + mods = append(mods, moduleInfo(m, true)) + } + } + } + if !matched { + if literal { + mods = append(mods, &modinfo.ModulePublic{ + Path: arg, + Error: &modinfo.ModuleError{ + Err: fmt.Sprintf("module %q is not a known dependency", arg), + }, + }) + } else { + fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies\n", arg) + } + } + } + + return mods +} diff --git a/libgo/go/cmd/go/internal/modload/load.go b/libgo/go/cmd/go/internal/modload/load.go new file mode 100644 index 0000000..5bf6c9b --- /dev/null +++ b/libgo/go/cmd/go/internal/modload/load.go @@ -0,0 +1,1071 @@ +// Copyright 2018 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 modload + +import ( + "bytes" + "errors" + "fmt" + "go/build" + "io/ioutil" + "os" + "path" + "path/filepath" + "sort" + "strings" + "sync" + + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/imports" + "cmd/go/internal/modfetch" + "cmd/go/internal/modfile" + "cmd/go/internal/module" + "cmd/go/internal/mvs" + "cmd/go/internal/par" + "cmd/go/internal/search" + "cmd/go/internal/semver" + "cmd/go/internal/str" +) + +// buildList is the list of modules to use for building packages. +// It is initialized by calling ImportPaths, ImportFromFiles, +// LoadALL, or LoadBuildList, each of which uses loaded.load. +// +// Ideally, exactly ONE of those functions would be called, +// and exactly once. Most of the time, that's true. +// During "go get" it may not be. TODO(rsc): Figure out if +// that restriction can be established, or else document why not. +// +var buildList []module.Version + +// loaded is the most recently-used package loader. +// It holds details about individual packages. +// +// Note that loaded.buildList is only valid during a load operation; +// afterward, it is copied back into the global buildList, +// which should be used instead. +var loaded *loader + +// ImportPaths returns the set of packages matching the args (patterns), +// adding modules to the build list as needed to satisfy new imports. +func ImportPaths(patterns []string) []*search.Match { + InitMod() + + var matches []*search.Match + for _, pattern := range search.CleanPatterns(patterns) { + m := &search.Match{ + Pattern: pattern, + Literal: !strings.Contains(pattern, "...") && !search.IsMetaPackage(pattern), + } + if m.Literal { + m.Pkgs = []string{pattern} + } + matches = append(matches, m) + } + + fsDirs := make([][]string, len(matches)) + loaded = newLoader() + updateMatches := func(iterating bool) { + for i, m := range matches { + switch { + case build.IsLocalImport(m.Pattern) || filepath.IsAbs(m.Pattern): + // Evaluate list of file system directories on first iteration. + if fsDirs[i] == nil { + var dirs []string + if m.Literal { + dirs = []string{m.Pattern} + } else { + dirs = search.MatchPackagesInFS(m.Pattern).Pkgs + } + fsDirs[i] = dirs + } + + // Make a copy of the directory list and translate to import paths. + // Note that whether a directory corresponds to an import path + // changes as the build list is updated, and a directory can change + // from not being in the build list to being in it and back as + // the exact version of a particular module increases during + // the loader iterations. + m.Pkgs = str.StringList(fsDirs[i]) + for i, pkg := range m.Pkgs { + dir := pkg + if !filepath.IsAbs(dir) { + dir = filepath.Join(cwd, pkg) + } else { + dir = filepath.Clean(dir) + } + + // Note: The checks for @ here are just to avoid misinterpreting + // the module cache directories (formerly GOPATH/src/mod/foo@v1.5.2/bar). + // It's not strictly necessary but helpful to keep the checks. + if dir == ModRoot { + pkg = Target.Path + } else if strings.HasPrefix(dir, ModRoot+string(filepath.Separator)) && !strings.Contains(dir[len(ModRoot):], "@") { + suffix := filepath.ToSlash(dir[len(ModRoot):]) + if strings.HasPrefix(suffix, "/vendor/") { + // TODO getmode vendor check + pkg = strings.TrimPrefix(suffix, "/vendor/") + } else { + pkg = Target.Path + suffix + } + } else if sub := search.InDir(dir, cfg.GOROOTsrc); sub != "" && !strings.Contains(sub, "@") { + pkg = filepath.ToSlash(sub) + } else if path := pathInModuleCache(dir); path != "" { + pkg = path + } else { + pkg = "" + if !iterating { + base.Errorf("go: directory %s outside available modules", base.ShortPath(dir)) + } + } + info, err := os.Stat(dir) + if err != nil || !info.IsDir() { + // If the directory does not exist, + // don't turn it into an import path + // that will trigger a lookup. + pkg = "" + if !iterating { + if err != nil { + base.Errorf("go: no such directory %v", m.Pattern) + } else { + base.Errorf("go: %s is not a directory", m.Pattern) + } + } + } + m.Pkgs[i] = pkg + } + + case strings.Contains(m.Pattern, "..."): + m.Pkgs = matchPackages(m.Pattern, loaded.tags, true, buildList) + + case m.Pattern == "all": + loaded.testAll = true + if iterating { + // Enumerate the packages in the main module. + // We'll load the dependencies as we find them. + m.Pkgs = matchPackages("...", loaded.tags, false, []module.Version{Target}) + } else { + // Starting with the packages in the main module, + // enumerate the full list of "all". + m.Pkgs = loaded.computePatternAll(m.Pkgs) + } + + case search.IsMetaPackage(m.Pattern): // std, cmd + if len(m.Pkgs) == 0 { + m.Pkgs = search.MatchPackages(m.Pattern).Pkgs + } + } + } + } + + loaded.load(func() []string { + var roots []string + updateMatches(true) + for _, m := range matches { + for _, pkg := range m.Pkgs { + if pkg != "" { + roots = append(roots, pkg) + } + } + } + return roots + }) + + // One last pass to finalize wildcards. + updateMatches(false) + + // A given module path may be used as itself or as a replacement for another + // module, but not both at the same time. Otherwise, the aliasing behavior is + // too subtle (see https://golang.org/issue/26607), and we don't want to + // commit to a specific behavior at this point. + firstPath := make(map[module.Version]string, len(buildList)) + for _, mod := range buildList { + src := mod + if rep := Replacement(mod); rep.Path != "" { + src = rep + } + if prev, ok := firstPath[src]; !ok { + firstPath[src] = mod.Path + } else if prev != mod.Path { + base.Errorf("go: %s@%s used for two different module paths (%s and %s)", src.Path, src.Version, prev, mod.Path) + } + } + base.ExitIfErrors() + WriteGoMod() + + search.WarnUnmatched(matches) + return matches +} + +// pathInModuleCache returns the import path of the directory dir, +// if dir is in the module cache copy of a module in our build list. +func pathInModuleCache(dir string) string { + for _, m := range buildList[1:] { + root, err := modfetch.DownloadDir(m) + if err != nil { + continue + } + if sub := search.InDir(dir, root); sub != "" { + sub = filepath.ToSlash(sub) + if !strings.Contains(sub, "/vendor/") && !strings.HasPrefix(sub, "vendor/") && !strings.Contains(sub, "@") { + return path.Join(m.Path, filepath.ToSlash(sub)) + } + } + } + return "" +} + +// warnPattern returns list, the result of matching pattern, +// but if list is empty then first it prints a warning about +// the pattern not matching any packages. +func warnPattern(pattern string, list []string) []string { + if len(list) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return list +} + +// ImportFromFiles adds modules to the build list as needed +// to satisfy the imports in the named Go source files. +func ImportFromFiles(gofiles []string) { + InitMod() + + imports, testImports, err := imports.ScanFiles(gofiles, imports.Tags()) + if err != nil { + base.Fatalf("go: %v", err) + } + + loaded = newLoader() + loaded.load(func() []string { + var roots []string + roots = append(roots, imports...) + roots = append(roots, testImports...) + return roots + }) + WriteGoMod() +} + +// DirImportPath returns the effective import path for dir, +// provided it is within the main module, or else returns ".". +func DirImportPath(dir string) string { + if !filepath.IsAbs(dir) { + dir = filepath.Join(cwd, dir) + } else { + dir = filepath.Clean(dir) + } + + if dir == ModRoot { + return Target.Path + } + if strings.HasPrefix(dir, ModRoot+string(filepath.Separator)) { + suffix := filepath.ToSlash(dir[len(ModRoot):]) + if strings.HasPrefix(suffix, "/vendor/") { + return strings.TrimPrefix(suffix, "/vendor/") + } + return Target.Path + suffix + } + return "." +} + +// LoadBuildList loads and returns the build list from go.mod. +// The loading of the build list happens automatically in ImportPaths: +// LoadBuildList need only be called if ImportPaths is not +// (typically in commands that care about the module but +// no particular package). +func LoadBuildList() []module.Version { + InitMod() + ReloadBuildList() + WriteGoMod() + return buildList +} + +func ReloadBuildList() []module.Version { + loaded = newLoader() + loaded.load(func() []string { return nil }) + return buildList +} + +// LoadALL returns the set of all packages in the current module +// and their dependencies in any other modules, without filtering +// due to build tags, except "+build ignore". +// It adds modules to the build list as needed to satisfy new imports. +// This set is useful for deciding whether a particular import is needed +// anywhere in a module. +func LoadALL() []string { + return loadAll(true) +} + +// LoadVendor is like LoadALL but only follows test dependencies +// for tests in the main module. Tests in dependency modules are +// ignored completely. +// This set is useful for identifying the which packages to include in a vendor directory. +func LoadVendor() []string { + return loadAll(false) +} + +func loadAll(testAll bool) []string { + InitMod() + + loaded = newLoader() + loaded.isALL = true + loaded.tags = anyTags + loaded.testAll = testAll + if !testAll { + loaded.testRoots = true + } + all := TargetPackages() + loaded.load(func() []string { return all }) + WriteGoMod() + + var paths []string + for _, pkg := range loaded.pkgs { + if e, ok := pkg.err.(*ImportMissingError); ok && e.Module.Path == "" { + continue // Package doesn't actually exist. + } + paths = append(paths, pkg.path) + } + return paths +} + +// anyTags is a special tags map that satisfies nearly all build tag expressions. +// Only "ignore" and malformed build tag requirements are considered false. +var anyTags = map[string]bool{"*": true} + +// TargetPackages returns the list of packages in the target (top-level) module, +// under all build tag settings. +func TargetPackages() []string { + return matchPackages("...", anyTags, false, []module.Version{Target}) +} + +// BuildList returns the module build list, +// typically constructed by a previous call to +// LoadBuildList or ImportPaths. +// The caller must not modify the returned list. +func BuildList() []module.Version { + return buildList +} + +// SetBuildList sets the module build list. +// The caller is responsible for ensuring that the list is valid. +// SetBuildList does not retain a reference to the original list. +func SetBuildList(list []module.Version) { + buildList = append([]module.Version{}, list...) +} + +// ImportMap returns the actual package import path +// for an import path found in source code. +// If the given import path does not appear in the source code +// for the packages that have been loaded, ImportMap returns the empty string. +func ImportMap(path string) string { + pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) + if !ok { + return "" + } + return pkg.path +} + +// PackageDir returns the directory containing the source code +// for the package named by the import path. +func PackageDir(path string) string { + pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) + if !ok { + return "" + } + return pkg.dir +} + +// PackageModule returns the module providing the package named by the import path. +func PackageModule(path string) module.Version { + pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) + if !ok { + return module.Version{} + } + return pkg.mod +} + +// ModuleUsedDirectly reports whether the main module directly imports +// some package in the module with the given path. +func ModuleUsedDirectly(path string) bool { + return loaded.direct[path] +} + +// Lookup returns the source directory, import path, and any loading error for +// the package at path. +// Lookup requires that one of the Load functions in this package has already +// been called. +func Lookup(path string) (dir, realPath string, err error) { + pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) + if !ok { + // The loader should have found all the relevant paths. + // There are a few exceptions, though: + // - during go list without -test, the p.Resolve calls to process p.TestImports and p.XTestImports + // end up here to canonicalize the import paths. + // - during any load, non-loaded packages like "unsafe" end up here. + // - during any load, build-injected dependencies like "runtime/cgo" end up here. + // - because we ignore appengine/* in the module loader, + // the dependencies of any actual appengine/* library end up here. + dir := findStandardImportPath(path) + if dir != "" { + return dir, path, nil + } + return "", "", errMissing + } + return pkg.dir, pkg.path, pkg.err +} + +// A loader manages the process of loading information about +// the required packages for a particular build, +// checking that the packages are available in the module set, +// and updating the module set if needed. +// Loading is an iterative process: try to load all the needed packages, +// but if imports are missing, try to resolve those imports, and repeat. +// +// Although most of the loading state is maintained in the loader struct, +// one key piece - the build list - is a global, so that it can be modified +// separate from the loading operation, such as during "go get" +// upgrades/downgrades or in "go mod" operations. +// TODO(rsc): It might be nice to make the loader take and return +// a buildList rather than hard-coding use of the global. +type loader struct { + tags map[string]bool // tags for scanDir + testRoots bool // include tests for roots + isALL bool // created with LoadALL + testAll bool // include tests for all packages + + // reset on each iteration + roots []*loadPkg + pkgs []*loadPkg + work *par.Work // current work queue + pkgCache *par.Cache // map from string to *loadPkg + + // computed at end of iterations + direct map[string]bool // imported directly by main module + goVersion map[string]string // go version recorded in each module +} + +// LoadTests controls whether the loaders load tests of the root packages. +var LoadTests bool + +func newLoader() *loader { + ld := new(loader) + ld.tags = imports.Tags() + ld.testRoots = LoadTests + return ld +} + +func (ld *loader) reset() { + ld.roots = nil + ld.pkgs = nil + ld.work = new(par.Work) + ld.pkgCache = new(par.Cache) +} + +// A loadPkg records information about a single loaded package. +type loadPkg struct { + path string // import path + mod module.Version // module providing package + dir string // directory containing source code + imports []*loadPkg // packages imported by this one + err error // error loading package + stack *loadPkg // package importing this one in minimal import stack for this pkg + test *loadPkg // package with test imports, if we need test + testOf *loadPkg + testImports []string // test-only imports, saved for use by pkg.test. +} + +var errMissing = errors.New("cannot find package") + +// load attempts to load the build graph needed to process a set of root packages. +// The set of root packages is defined by the addRoots function, +// which must call add(path) with the import path of each root package. +func (ld *loader) load(roots func() []string) { + var err error + reqs := Reqs() + buildList, err = mvs.BuildList(Target, reqs) + if err != nil { + base.Fatalf("go: %v", err) + } + + added := make(map[string]bool) + for { + ld.reset() + if roots != nil { + // Note: the returned roots can change on each iteration, + // since the expansion of package patterns depends on the + // build list we're using. + for _, path := range roots() { + ld.work.Add(ld.pkg(path, true)) + } + } + ld.work.Do(10, ld.doPkg) + ld.buildStacks() + numAdded := 0 + haveMod := make(map[module.Version]bool) + for _, m := range buildList { + haveMod[m] = true + } + for _, pkg := range ld.pkgs { + if err, ok := pkg.err.(*ImportMissingError); ok && err.Module.Path != "" { + if added[pkg.path] { + base.Fatalf("go: %s: looping trying to add package", pkg.stackText()) + } + added[pkg.path] = true + numAdded++ + if !haveMod[err.Module] { + haveMod[err.Module] = true + buildList = append(buildList, err.Module) + } + continue + } + // Leave other errors for Import or load.Packages to report. + } + base.ExitIfErrors() + if numAdded == 0 { + break + } + + // Recompute buildList with all our additions. + reqs = Reqs() + buildList, err = mvs.BuildList(Target, reqs) + if err != nil { + base.Fatalf("go: %v", err) + } + } + base.ExitIfErrors() + + // Compute directly referenced dependency modules. + ld.direct = make(map[string]bool) + for _, pkg := range ld.pkgs { + if pkg.mod == Target { + for _, dep := range pkg.imports { + if dep.mod.Path != "" { + ld.direct[dep.mod.Path] = true + } + } + } + } + + // Add Go versions, computed during walk. + ld.goVersion = make(map[string]string) + for _, m := range buildList { + v, _ := reqs.(*mvsReqs).versions.Load(m) + ld.goVersion[m.Path], _ = v.(string) + } + + // Mix in direct markings (really, lack of indirect markings) + // from go.mod, unless we scanned the whole module + // and can therefore be sure we know better than go.mod. + if !ld.isALL && modFile != nil { + for _, r := range modFile.Require { + if !r.Indirect { + ld.direct[r.Mod.Path] = true + } + } + } +} + +// pkg returns the *loadPkg for path, creating and queuing it if needed. +// If the package should be tested, its test is created but not queued +// (the test is queued after processing pkg). +// If isRoot is true, the pkg is being queued as one of the roots of the work graph. +func (ld *loader) pkg(path string, isRoot bool) *loadPkg { + return ld.pkgCache.Do(path, func() interface{} { + pkg := &loadPkg{ + path: path, + } + if ld.testRoots && isRoot || ld.testAll { + test := &loadPkg{ + path: path, + testOf: pkg, + } + pkg.test = test + } + if isRoot { + ld.roots = append(ld.roots, pkg) + } + ld.work.Add(pkg) + return pkg + }).(*loadPkg) +} + +// doPkg processes a package on the work queue. +func (ld *loader) doPkg(item interface{}) { + // TODO: what about replacements? + pkg := item.(*loadPkg) + var imports []string + if pkg.testOf != nil { + pkg.dir = pkg.testOf.dir + pkg.mod = pkg.testOf.mod + imports = pkg.testOf.testImports + } else { + if strings.Contains(pkg.path, "@") { + // Leave for error during load. + return + } + if build.IsLocalImport(pkg.path) { + // Leave for error during load. + // (Module mode does not allow local imports.) + return + } + + pkg.mod, pkg.dir, pkg.err = Import(pkg.path) + if pkg.dir == "" { + return + } + if cfg.BuildContext.Compiler == "gccgo" && pkg.mod.Path == "" { + return + } + + var testImports []string + var err error + imports, testImports, err = scanDir(pkg.dir, ld.tags) + if err != nil { + pkg.err = err + return + } + if pkg.test != nil { + pkg.testImports = testImports + } + } + + for _, path := range imports { + pkg.imports = append(pkg.imports, ld.pkg(path, false)) + } + + // Now that pkg.dir, pkg.mod, pkg.testImports are set, we can queue pkg.test. + // TODO: All that's left is creating new imports. Why not just do it now? + if pkg.test != nil { + ld.work.Add(pkg.test) + } +} + +// computePatternAll returns the list of packages matching pattern "all", +// starting with a list of the import paths for the packages in the main module. +func (ld *loader) computePatternAll(paths []string) []string { + seen := make(map[*loadPkg]bool) + var all []string + var walk func(*loadPkg) + walk = func(pkg *loadPkg) { + if seen[pkg] { + return + } + seen[pkg] = true + if pkg.testOf == nil { + all = append(all, pkg.path) + } + for _, p := range pkg.imports { + walk(p) + } + if p := pkg.test; p != nil { + walk(p) + } + } + for _, path := range paths { + walk(ld.pkg(path, false)) + } + sort.Strings(all) + + return all +} + +// scanDir is like imports.ScanDir but elides known magic imports from the list, +// so that we do not go looking for packages that don't really exist. +// +// The standard magic import is "C", for cgo. +// +// The only other known magic imports are appengine and appengine/*. +// These are so old that they predate "go get" and did not use URL-like paths. +// Most code today now uses google.golang.org/appengine instead, +// but not all code has been so updated. When we mostly ignore build tags +// during "go vendor", we look into "// +build appengine" files and +// may see these legacy imports. We drop them so that the module +// search does not look for modules to try to satisfy them. +func scanDir(dir string, tags map[string]bool) (imports_, testImports []string, err error) { + imports_, testImports, err = imports.ScanDir(dir, tags) + + filter := func(x []string) []string { + w := 0 + for _, pkg := range x { + if pkg != "C" && pkg != "appengine" && !strings.HasPrefix(pkg, "appengine/") && + pkg != "appengine_internal" && !strings.HasPrefix(pkg, "appengine_internal/") { + x[w] = pkg + w++ + } + } + return x[:w] + } + + return filter(imports_), filter(testImports), err +} + +// buildStacks computes minimal import stacks for each package, +// for use in error messages. When it completes, packages that +// are part of the original root set have pkg.stack == nil, +// and other packages have pkg.stack pointing at the next +// package up the import stack in their minimal chain. +// As a side effect, buildStacks also constructs ld.pkgs, +// the list of all packages loaded. +func (ld *loader) buildStacks() { + if len(ld.pkgs) > 0 { + panic("buildStacks") + } + for _, pkg := range ld.roots { + pkg.stack = pkg // sentinel to avoid processing in next loop + ld.pkgs = append(ld.pkgs, pkg) + } + for i := 0; i < len(ld.pkgs); i++ { // not range: appending to ld.pkgs in loop + pkg := ld.pkgs[i] + for _, next := range pkg.imports { + if next.stack == nil { + next.stack = pkg + ld.pkgs = append(ld.pkgs, next) + } + } + if next := pkg.test; next != nil && next.stack == nil { + next.stack = pkg + ld.pkgs = append(ld.pkgs, next) + } + } + for _, pkg := range ld.roots { + pkg.stack = nil + } +} + +// stackText builds the import stack text to use when +// reporting an error in pkg. It has the general form +// +// import root -> +// import other -> +// import other2 -> +// import pkg +// +func (pkg *loadPkg) stackText() string { + var stack []*loadPkg + for p := pkg.stack; p != nil; p = p.stack { + stack = append(stack, p) + } + + var buf bytes.Buffer + for i := len(stack) - 1; i >= 0; i-- { + p := stack[i] + if p.testOf != nil { + fmt.Fprintf(&buf, "test ->\n\t") + } else { + fmt.Fprintf(&buf, "import %q ->\n\t", p.path) + } + } + fmt.Fprintf(&buf, "import %q", pkg.path) + return buf.String() +} + +// why returns the text to use in "go mod why" output about the given package. +// It is less ornate than the stackText but conatins the same information. +func (pkg *loadPkg) why() string { + var buf strings.Builder + var stack []*loadPkg + for p := pkg; p != nil; p = p.stack { + stack = append(stack, p) + } + + for i := len(stack) - 1; i >= 0; i-- { + p := stack[i] + if p.testOf != nil { + fmt.Fprintf(&buf, "%s.test\n", p.testOf.path) + } else { + fmt.Fprintf(&buf, "%s\n", p.path) + } + } + return buf.String() +} + +// Why returns the "go mod why" output stanza for the given package, +// without the leading # comment. +// The package graph must have been loaded already, usually by LoadALL. +// If there is no reason for the package to be in the current build, +// Why returns an empty string. +func Why(path string) string { + pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) + if !ok { + return "" + } + return pkg.why() +} + +// WhyDepth returns the number of steps in the Why listing. +// If there is no reason for the package to be in the current build, +// WhyDepth returns 0. +func WhyDepth(path string) int { + n := 0 + pkg, _ := loaded.pkgCache.Get(path).(*loadPkg) + for p := pkg; p != nil; p = p.stack { + n++ + } + return n +} + +// Replacement returns the replacement for mod, if any, from go.mod. +// If there is no replacement for mod, Replacement returns +// a module.Version with Path == "". +func Replacement(mod module.Version) module.Version { + if modFile == nil { + // Happens during testing. + return module.Version{} + } + + var found *modfile.Replace + for _, r := range modFile.Replace { + if r.Old.Path == mod.Path && (r.Old.Version == "" || r.Old.Version == mod.Version) { + found = r // keep going + } + } + if found == nil { + return module.Version{} + } + return found.New +} + +// mvsReqs implements mvs.Reqs for module semantic versions, +// with any exclusions or replacements applied internally. +type mvsReqs struct { + buildList []module.Version + cache par.Cache + versions sync.Map +} + +// Reqs returns the current module requirement graph. +// Future calls to SetBuildList do not affect the operation +// of the returned Reqs. +func Reqs() mvs.Reqs { + r := &mvsReqs{ + buildList: buildList, + } + return r +} + +func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) { + type cached struct { + list []module.Version + err error + } + + c := r.cache.Do(mod, func() interface{} { + list, err := r.required(mod) + if err != nil { + return cached{nil, err} + } + for i, mv := range list { + for excluded[mv] { + mv1, err := r.next(mv) + if err != nil { + return cached{nil, err} + } + if mv1.Version == "none" { + return cached{nil, fmt.Errorf("%s(%s) depends on excluded %s(%s) with no newer version available", mod.Path, mod.Version, mv.Path, mv.Version)} + } + mv = mv1 + } + list[i] = mv + } + + return cached{list, nil} + }).(cached) + + return c.list, c.err +} + +var vendorOnce sync.Once + +var ( + vendorList []module.Version + vendorMap map[string]module.Version +) + +// readVendorList reads the list of vendored modules from vendor/modules.txt. +func readVendorList() { + vendorOnce.Do(func() { + vendorList = nil + vendorMap = make(map[string]module.Version) + data, _ := ioutil.ReadFile(filepath.Join(ModRoot, "vendor/modules.txt")) + var m module.Version + for _, line := range strings.Split(string(data), "\n") { + if strings.HasPrefix(line, "# ") { + f := strings.Fields(line) + m = module.Version{} + if len(f) == 3 && semver.IsValid(f[2]) { + m = module.Version{Path: f[1], Version: f[2]} + vendorList = append(vendorList, m) + } + } else if m.Path != "" { + f := strings.Fields(line) + if len(f) == 1 { + vendorMap[f[0]] = m + } + } + } + }) +} + +func (r *mvsReqs) modFileToList(f *modfile.File) []module.Version { + var list []module.Version + for _, r := range f.Require { + list = append(list, r.Mod) + } + return list +} + +func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) { + if mod == Target { + if modFile.Go != nil { + r.versions.LoadOrStore(mod, modFile.Go.Version) + } + var list []module.Version + return append(list, r.buildList[1:]...), nil + } + + if cfg.BuildMod == "vendor" { + // For every module other than the target, + // return the full list of modules from modules.txt. + readVendorList() + return vendorList, nil + } + + origPath := mod.Path + if repl := Replacement(mod); repl.Path != "" { + if repl.Version == "" { + // TODO: need to slip the new version into the tags list etc. + dir := repl.Path + if !filepath.IsAbs(dir) { + dir = filepath.Join(ModRoot, dir) + } + gomod := filepath.Join(dir, "go.mod") + data, err := ioutil.ReadFile(gomod) + if err != nil { + base.Errorf("go: parsing %s: %v", base.ShortPath(gomod), err) + return nil, ErrRequire + } + f, err := modfile.ParseLax(gomod, data, nil) + if err != nil { + base.Errorf("go: parsing %s: %v", base.ShortPath(gomod), err) + return nil, ErrRequire + } + if f.Go != nil { + r.versions.LoadOrStore(mod, f.Go.Version) + } + return r.modFileToList(f), nil + } + mod = repl + } + + if mod.Version == "none" { + return nil, nil + } + + if !semver.IsValid(mod.Version) { + // Disallow the broader queries supported by fetch.Lookup. + base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", mod.Path, mod.Version) + } + + data, err := modfetch.GoMod(mod.Path, mod.Version) + if err != nil { + base.Errorf("go: %s@%s: %v\n", mod.Path, mod.Version, err) + return nil, ErrRequire + } + f, err := modfile.ParseLax("go.mod", data, nil) + if err != nil { + base.Errorf("go: %s@%s: parsing go.mod: %v", mod.Path, mod.Version, err) + return nil, ErrRequire + } + + if f.Module == nil { + base.Errorf("go: %s@%s: parsing go.mod: missing module line", mod.Path, mod.Version) + return nil, ErrRequire + } + if mpath := f.Module.Mod.Path; mpath != origPath && mpath != mod.Path { + base.Errorf("go: %s@%s: parsing go.mod: unexpected module path %q", mod.Path, mod.Version, mpath) + return nil, ErrRequire + } + if f.Go != nil { + r.versions.LoadOrStore(mod, f.Go.Version) + } + + return r.modFileToList(f), nil +} + +// ErrRequire is the sentinel error returned when Require encounters problems. +// It prints the problems directly to standard error, so that multiple errors +// can be displayed easily. +var ErrRequire = errors.New("error loading module requirements") + +func (*mvsReqs) Max(v1, v2 string) string { + if v1 != "" && semver.Compare(v1, v2) == -1 { + return v2 + } + return v1 +} + +// Upgrade is a no-op, here to implement mvs.Reqs. +// The upgrade logic for go get -u is in ../modget/get.go. +func (*mvsReqs) Upgrade(m module.Version) (module.Version, error) { + return m, nil +} + +func versions(path string) ([]string, error) { + // Note: modfetch.Lookup and repo.Versions are cached, + // so there's no need for us to add extra caching here. + repo, err := modfetch.Lookup(path) + if err != nil { + return nil, err + } + return repo.Versions("") +} + +// Previous returns the tagged version of m.Path immediately prior to +// m.Version, or version "none" if no prior version is tagged. +func (*mvsReqs) Previous(m module.Version) (module.Version, error) { + list, err := versions(m.Path) + if err != nil { + return module.Version{}, err + } + i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) >= 0 }) + if i > 0 { + return module.Version{Path: m.Path, Version: list[i-1]}, nil + } + return module.Version{Path: m.Path, Version: "none"}, nil +} + +// next returns the next version of m.Path after m.Version. +// It is only used by the exclusion processing in the Required method, +// not called directly by MVS. +func (*mvsReqs) next(m module.Version) (module.Version, error) { + list, err := versions(m.Path) + if err != nil { + return module.Version{}, err + } + i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) > 0 }) + if i < len(list) { + return module.Version{Path: m.Path, Version: list[i]}, nil + } + return module.Version{Path: m.Path, Version: "none"}, nil +} + +func fetch(mod module.Version) (dir string, isLocal bool, err error) { + if mod == Target { + return ModRoot, true, nil + } + if r := Replacement(mod); r.Path != "" { + if r.Version == "" { + dir = r.Path + if !filepath.IsAbs(dir) { + dir = filepath.Join(ModRoot, dir) + } + return dir, true, nil + } + mod = r + } + + dir, err = modfetch.Download(mod) + return dir, false, err +} diff --git a/libgo/go/cmd/go/internal/modload/query.go b/libgo/go/cmd/go/internal/modload/query.go new file mode 100644 index 0000000..3b550f1 --- /dev/null +++ b/libgo/go/cmd/go/internal/modload/query.go @@ -0,0 +1,249 @@ +// Copyright 2018 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 modload + +import ( + "cmd/go/internal/modfetch" + "cmd/go/internal/modfetch/codehost" + "cmd/go/internal/module" + "cmd/go/internal/semver" + "fmt" + pathpkg "path" + "strings" +) + +// Query looks up a revision of a given module given a version query string. +// The module must be a complete module path. +// The version must take one of the following forms: +// +// - the literal string "latest", denoting the latest available, allowed tagged version, +// with non-prereleases preferred over prereleases. +// If there are no tagged versions in the repo, latest returns the most recent commit. +// - v1, denoting the latest available tagged version v1.x.x. +// - v1.2, denoting the latest available tagged version v1.2.x. +// - v1.2.3, a semantic version string denoting that tagged version. +// - <v1.2.3, <=v1.2.3, >v1.2.3, >=v1.2.3, +// denoting the version closest to the target and satisfying the given operator, +// with non-prereleases preferred over prereleases. +// - a repository commit identifier, denoting that commit. +// +// If the allowed function is non-nil, Query excludes any versions for which allowed returns false. +// +// If path is the path of the main module and the query is "latest", +// Query returns Target.Version as the version. +func Query(path, query string, allowed func(module.Version) bool) (*modfetch.RevInfo, error) { + if allowed == nil { + allowed = func(module.Version) bool { return true } + } + + // Parse query to detect parse errors (and possibly handle query) + // before any network I/O. + badVersion := func(v string) (*modfetch.RevInfo, error) { + return nil, fmt.Errorf("invalid semantic version %q in range %q", v, query) + } + var ok func(module.Version) bool + var prefix string + var preferOlder bool + switch { + case query == "latest": + ok = allowed + + case strings.HasPrefix(query, "<="): + v := query[len("<="):] + if !semver.IsValid(v) { + return badVersion(v) + } + if isSemverPrefix(v) { + // Refuse to say whether <=v1.2 allows v1.2.3 (remember, @v1.2 might mean v1.2.3). + return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query) + } + ok = func(m module.Version) bool { + return semver.Compare(m.Version, v) <= 0 && allowed(m) + } + + case strings.HasPrefix(query, "<"): + v := query[len("<"):] + if !semver.IsValid(v) { + return badVersion(v) + } + ok = func(m module.Version) bool { + return semver.Compare(m.Version, v) < 0 && allowed(m) + } + + case strings.HasPrefix(query, ">="): + v := query[len(">="):] + if !semver.IsValid(v) { + return badVersion(v) + } + ok = func(m module.Version) bool { + return semver.Compare(m.Version, v) >= 0 && allowed(m) + } + preferOlder = true + + case strings.HasPrefix(query, ">"): + v := query[len(">"):] + if !semver.IsValid(v) { + return badVersion(v) + } + if isSemverPrefix(v) { + // Refuse to say whether >v1.2 allows v1.2.3 (remember, @v1.2 might mean v1.2.3). + return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query) + } + ok = func(m module.Version) bool { + return semver.Compare(m.Version, v) > 0 && allowed(m) + } + preferOlder = true + + case semver.IsValid(query) && isSemverPrefix(query): + ok = func(m module.Version) bool { + return matchSemverPrefix(query, m.Version) && allowed(m) + } + prefix = query + "." + + case semver.IsValid(query): + vers := module.CanonicalVersion(query) + if !allowed(module.Version{Path: path, Version: vers}) { + return nil, fmt.Errorf("%s@%s excluded", path, vers) + } + return modfetch.Stat(path, vers) + + default: + // Direct lookup of semantic version or commit identifier. + info, err := modfetch.Stat(path, query) + if err != nil { + return nil, err + } + if !allowed(module.Version{Path: path, Version: info.Version}) { + return nil, fmt.Errorf("%s@%s excluded", path, info.Version) + } + return info, nil + } + + if path == Target.Path { + if query != "latest" { + return nil, fmt.Errorf("can't query specific version (%q) for the main module (%s)", query, path) + } + if !allowed(Target) { + return nil, fmt.Errorf("internal error: main module version is not allowed") + } + return &modfetch.RevInfo{Version: Target.Version}, nil + } + + // Load versions and execute query. + repo, err := modfetch.Lookup(path) + if err != nil { + return nil, err + } + versions, err := repo.Versions(prefix) + if err != nil { + return nil, err + } + + if preferOlder { + for _, v := range versions { + if semver.Prerelease(v) == "" && ok(module.Version{Path: path, Version: v}) { + return repo.Stat(v) + } + } + for _, v := range versions { + if semver.Prerelease(v) != "" && ok(module.Version{Path: path, Version: v}) { + return repo.Stat(v) + } + } + } else { + for i := len(versions) - 1; i >= 0; i-- { + v := versions[i] + if semver.Prerelease(v) == "" && ok(module.Version{Path: path, Version: v}) { + return repo.Stat(v) + } + } + for i := len(versions) - 1; i >= 0; i-- { + v := versions[i] + if semver.Prerelease(v) != "" && ok(module.Version{Path: path, Version: v}) { + return repo.Stat(v) + } + } + } + + if query == "latest" { + // Special case for "latest": if no tags match, use latest commit in repo, + // provided it is not excluded. + if info, err := repo.Latest(); err == nil && allowed(module.Version{Path: path, Version: info.Version}) { + return info, nil + } + } + + return nil, fmt.Errorf("no matching versions for query %q", query) +} + +// isSemverPrefix reports whether v is a semantic version prefix: v1 or v1.2 (not v1.2.3). +// The caller is assumed to have checked that semver.IsValid(v) is true. +func isSemverPrefix(v string) bool { + dots := 0 + for i := 0; i < len(v); i++ { + switch v[i] { + case '-', '+': + return false + case '.': + dots++ + if dots >= 2 { + return false + } + } + } + return true +} + +// matchSemverPrefix reports whether the shortened semantic version p +// matches the full-width (non-shortened) semantic version v. +func matchSemverPrefix(p, v string) bool { + return len(v) > len(p) && v[len(p)] == '.' && v[:len(p)] == p +} + +// QueryPackage looks up a revision of a module containing path. +// +// If multiple modules with revisions matching the query provide the requested +// package, QueryPackage picks the one with the longest module path. +// +// If the path is in the the main module and the query is "latest", +// QueryPackage returns Target as the version. +func QueryPackage(path, query string, allowed func(module.Version) bool) (module.Version, *modfetch.RevInfo, error) { + if _, ok := dirInModule(path, Target.Path, ModRoot, true); ok { + if query != "latest" { + return module.Version{}, nil, fmt.Errorf("can't query specific version (%q) for package %s in the main module (%s)", query, path, Target.Path) + } + if !allowed(Target) { + return module.Version{}, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed", path, Target.Path) + } + return Target, &modfetch.RevInfo{Version: Target.Version}, nil + } + + finalErr := errMissing + for p := path; p != "."; p = pathpkg.Dir(p) { + info, err := Query(p, query, allowed) + if err != nil { + if _, ok := err.(*codehost.VCSError); ok { + // A VCSError means we know where to find the code, + // we just can't. Abort search. + return module.Version{}, nil, err + } + if finalErr == errMissing { + finalErr = err + } + continue + } + m := module.Version{Path: p, Version: info.Version} + root, isLocal, err := fetch(m) + if err != nil { + return module.Version{}, nil, err + } + _, ok := dirInModule(path, m.Path, root, isLocal) + if ok { + return m, info, nil + } + } + + return module.Version{}, nil, finalErr +} diff --git a/libgo/go/cmd/go/internal/modload/query_test.go b/libgo/go/cmd/go/internal/modload/query_test.go new file mode 100644 index 0000000..7f3ffab --- /dev/null +++ b/libgo/go/cmd/go/internal/modload/query_test.go @@ -0,0 +1,151 @@ +// Copyright 2018 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 modload + +import ( + "internal/testenv" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "strings" + "testing" + + "cmd/go/internal/modfetch" + "cmd/go/internal/modfetch/codehost" + "cmd/go/internal/module" +) + +func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { + dir, err := ioutil.TempDir("", "modload-test-") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(dir) + modfetch.PkgMod = filepath.Join(dir, "pkg/mod") + codehost.WorkRoot = filepath.Join(dir, "codework") + return m.Run() +} + +var ( + queryRepo = "vcs-test.golang.org/git/querytest.git" + queryRepoV2 = queryRepo + "/v2" + queryRepoV3 = queryRepo + "/v3" + + // Empty version list (no semver tags), not actually empty. + emptyRepo = "vcs-test.golang.org/git/emptytest.git" +) + +var queryTests = []struct { + path string + query string + allow string + vers string + err string +}{ + /* + git init + echo module vcs-test.golang.org/git/querytest.git >go.mod + git add go.mod + git commit -m v1 go.mod + git tag start + for i in v0.0.0-pre1 v0.0.0 v0.0.1 v0.0.2 v0.0.3 v0.1.0 v0.1.1 v0.1.2 v0.3.0 v1.0.0 v1.1.0 v1.9.0 v1.9.9 v1.9.10-pre1; do + echo before $i >status + git add status + git commit -m "before $i" status + echo at $i >status + git commit -m "at $i" status + git tag $i + done + git tag favorite v0.0.3 + + git branch v2 start + git checkout v2 + echo module vcs-test.golang.org/git/querytest.git/v2 >go.mod + git commit -m v2 go.mod + for i in v2.0.0 v2.1.0 v2.2.0 v2.5.5; do + echo before $i >status + git add status + git commit -m "before $i" status + echo at $i >status + git commit -m "at $i" status + git tag $i + done + echo after v2.5.5 >status + git commit -m 'after v2.5.5' status + git checkout master + zip -r ../querytest.zip + gsutil cp ../querytest.zip gs://vcs-test/git/querytest.zip + curl 'https://vcs-test.golang.org/git/querytest?go-get=1' + */ + {path: queryRepo, query: "<v0.0.0", vers: "v0.0.0-pre1"}, + {path: queryRepo, query: "<v0.0.0-pre1", err: `no matching versions for query "<v0.0.0-pre1"`}, + {path: queryRepo, query: "<=v0.0.0", vers: "v0.0.0"}, + {path: queryRepo, query: ">v0.0.0", vers: "v0.0.1"}, + {path: queryRepo, query: ">=v0.0.0", vers: "v0.0.0"}, + {path: queryRepo, query: "v0.0.1", vers: "v0.0.1"}, + {path: queryRepo, query: "v0.0.1+foo", vers: "v0.0.1"}, + {path: queryRepo, query: "v0.0.99", err: `unknown revision v0.0.99`}, + {path: queryRepo, query: "v0", vers: "v0.3.0"}, + {path: queryRepo, query: "v0.1", vers: "v0.1.2"}, + {path: queryRepo, query: "v0.2", err: `no matching versions for query "v0.2"`}, + {path: queryRepo, query: "v0.0", vers: "v0.0.3"}, + {path: queryRepo, query: "latest", vers: "v1.9.9"}, + {path: queryRepo, query: "latest", allow: "NOMATCH", err: `no matching versions for query "latest"`}, + {path: queryRepo, query: ">v1.9.9", vers: "v1.9.10-pre1"}, + {path: queryRepo, query: ">v1.10.0", err: `no matching versions for query ">v1.10.0"`}, + {path: queryRepo, query: ">=v1.10.0", err: `no matching versions for query ">=v1.10.0"`}, + {path: queryRepo, query: "6cf84eb", vers: "v0.0.2-0.20180704023347-6cf84ebaea54"}, + {path: queryRepo, query: "start", vers: "v0.0.0-20180704023101-5e9e31667ddf"}, + {path: queryRepo, query: "7a1b6bf", vers: "v0.1.0"}, + + {path: queryRepoV2, query: "<v0.0.0", err: `no matching versions for query "<v0.0.0"`}, + {path: queryRepoV2, query: "<=v0.0.0", err: `no matching versions for query "<=v0.0.0"`}, + {path: queryRepoV2, query: ">v0.0.0", vers: "v2.0.0"}, + {path: queryRepoV2, query: ">=v0.0.0", vers: "v2.0.0"}, + {path: queryRepoV2, query: "v0.0.1+foo", vers: "v2.0.0-20180704023347-179bc86b1be3"}, + {path: queryRepoV2, query: "latest", vers: "v2.5.5"}, + + {path: queryRepoV3, query: "latest", vers: "v3.0.0-20180704024501-e0cf3de987e6"}, + + {path: emptyRepo, query: "latest", vers: "v0.0.0-20180704023549-7bb914627242"}, + {path: emptyRepo, query: ">v0.0.0", err: `no matching versions for query ">v0.0.0"`}, + {path: emptyRepo, query: "<v10.0.0", err: `no matching versions for query "<v10.0.0"`}, +} + +func TestQuery(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + for _, tt := range queryTests { + allow := tt.allow + if allow == "" { + allow = "*" + } + allowed := func(m module.Version) bool { + ok, _ := path.Match(allow, m.Version) + return ok + } + t.Run(strings.Replace(tt.path, "/", "_", -1)+"/"+tt.query+"/"+allow, func(t *testing.T) { + info, err := Query(tt.path, tt.query, allowed) + if tt.err != "" { + if err != nil && err.Error() == tt.err { + return + } + t.Fatalf("Query(%q, %q, %v): %v, want error %q", tt.path, tt.query, allow, err, tt.err) + } + if err != nil { + t.Fatalf("Query(%q, %q, %v): %v", tt.path, tt.query, allow, err) + } + if info.Version != tt.vers { + t.Errorf("Query(%q, %q, %v) = %v, want %v", tt.path, tt.query, allow, info.Version, tt.vers) + } + }) + } +} diff --git a/libgo/go/cmd/go/internal/modload/search.go b/libgo/go/cmd/go/internal/modload/search.go new file mode 100644 index 0000000..24825cc --- /dev/null +++ b/libgo/go/cmd/go/internal/modload/search.go @@ -0,0 +1,134 @@ +// Copyright 2018 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 modload + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/imports" + "cmd/go/internal/module" + "cmd/go/internal/search" +) + +// matchPackages returns a list of packages in the list of modules +// matching the pattern. Package loading assumes the given set of tags. +func matchPackages(pattern string, tags map[string]bool, useStd bool, modules []module.Version) []string { + match := func(string) bool { return true } + treeCanMatch := func(string) bool { return true } + if !search.IsMetaPackage(pattern) { + match = search.MatchPattern(pattern) + treeCanMatch = search.TreeCanMatchPattern(pattern) + } + + have := map[string]bool{ + "builtin": true, // ignore pseudo-package that exists only for documentation + } + if !cfg.BuildContext.CgoEnabled { + have["runtime/cgo"] = true // ignore during walk + } + var pkgs []string + + walkPkgs := func(root, importPathRoot string) { + root = filepath.Clean(root) + var cmd string + if root == cfg.GOROOTsrc { + cmd = filepath.Join(root, "cmd") + } + filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return nil + } + + // Don't use GOROOT/src but do walk down into it. + if path == root && importPathRoot == "" { + return nil + } + + // GOROOT/src/cmd makes use of GOROOT/src/cmd/vendor, + // which module mode can't deal with. Eventually we'll stop using + // that vendor directory, and then we can remove this exclusion. + // golang.org/issue/26924. + if path == cmd { + return filepath.SkipDir + } + + want := true + // Avoid .foo, _foo, and testdata directory trees. + _, elem := filepath.Split(path) + if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { + want = false + } + + name := importPathRoot + filepath.ToSlash(path[len(root):]) + if importPathRoot == "" { + name = name[1:] // cut leading slash + } + if !treeCanMatch(name) { + want = false + } + + if !fi.IsDir() { + if fi.Mode()&os.ModeSymlink != 0 && want { + if target, err := os.Stat(path); err == nil && target.IsDir() { + fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path) + } + } + return nil + } + + if !want { + return filepath.SkipDir + } + if path != root { + if _, err := os.Stat(filepath.Join(path, "go.mod")); err == nil { + return filepath.SkipDir + } + } + + if !have[name] { + have[name] = true + if match(name) { + if _, _, err := scanDir(path, tags); err != imports.ErrNoGo { + pkgs = append(pkgs, name) + } + } + } + + if elem == "vendor" { + return filepath.SkipDir + } + return nil + }) + } + + if useStd { + walkPkgs(cfg.GOROOTsrc, "") + } + + for _, mod := range modules { + if !treeCanMatch(mod.Path) { + continue + } + var root string + if mod.Version == "" { + root = ModRoot + } else { + var err error + root, _, err = fetch(mod) + if err != nil { + base.Errorf("go: %v", err) + continue + } + } + walkPkgs(root, mod.Path) + } + + return pkgs +} diff --git a/libgo/go/cmd/go/internal/module/module.go b/libgo/go/cmd/go/internal/module/module.go new file mode 100644 index 0000000..1dbb0f5 --- /dev/null +++ b/libgo/go/cmd/go/internal/module/module.go @@ -0,0 +1,540 @@ +// Copyright 2018 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 module defines the module.Version type +// along with support code. +package module + +// IMPORTANT NOTE +// +// This file essentially defines the set of valid import paths for the go command. +// There are many subtle considerations, including Unicode ambiguity, +// security, network, and file system representations. +// +// This file also defines the set of valid module path and version combinations, +// another topic with many subtle considerations. +// +// Changes to the semantics in this file require approval from rsc. + +import ( + "fmt" + "sort" + "strings" + "unicode" + "unicode/utf8" + + "cmd/go/internal/semver" +) + +// A Version is defined by a module path and version pair. +type Version struct { + Path string + + // Version is usually a semantic version in canonical form. + // There are two exceptions to this general rule. + // First, the top-level target of a build has no specific version + // and uses Version = "". + // Second, during MVS calculations the version "none" is used + // to represent the decision to take no version of a given module. + Version string `json:",omitempty"` +} + +// Check checks that a given module path, version pair is valid. +// In addition to the path being a valid module path +// and the version being a valid semantic version, +// the two must correspond. +// For example, the path "yaml/v2" only corresponds to +// semantic versions beginning with "v2.". +func Check(path, version string) error { + if err := CheckPath(path); err != nil { + return err + } + if !semver.IsValid(version) { + return fmt.Errorf("malformed semantic version %v", version) + } + _, pathMajor, _ := SplitPathVersion(path) + if !MatchPathMajor(version, pathMajor) { + if pathMajor == "" { + pathMajor = "v0 or v1" + } + if pathMajor[0] == '.' { // .v1 + pathMajor = pathMajor[1:] + } + return fmt.Errorf("mismatched module path %v and version %v (want %v)", path, version, pathMajor) + } + return nil +} + +// firstPathOK reports whether r can appear in the first element of a module path. +// The first element of the path must be an LDH domain name, at least for now. +// To avoid case ambiguity, the domain name must be entirely lower case. +func firstPathOK(r rune) bool { + return r == '-' || r == '.' || + '0' <= r && r <= '9' || + 'a' <= r && r <= 'z' +} + +// pathOK reports whether r can appear in an import path element. +// Paths can be ASCII letters, ASCII digits, and limited ASCII punctuation: + - . _ and ~. +// This matches what "go get" has historically recognized in import paths. +// TODO(rsc): We would like to allow Unicode letters, but that requires additional +// care in the safe encoding (see note below). +func pathOK(r rune) bool { + if r < utf8.RuneSelf { + return r == '+' || r == '-' || r == '.' || r == '_' || r == '~' || + '0' <= r && r <= '9' || + 'A' <= r && r <= 'Z' || + 'a' <= r && r <= 'z' + } + return false +} + +// fileNameOK reports whether r can appear in a file name. +// For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters. +// If we expand the set of allowed characters here, we have to +// work harder at detecting potential case-folding and normalization collisions. +// See note about "safe encoding" below. +func fileNameOK(r rune) bool { + if r < utf8.RuneSelf { + // Entire set of ASCII punctuation, from which we remove characters: + // ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ + // We disallow some shell special characters: " ' * < > ? ` | + // (Note that some of those are disallowed by the Windows file system as well.) + // We also disallow path separators / : and \ (fileNameOK is only called on path element characters). + // We allow spaces (U+0020) in file names. + const allowed = "!#$%&()+,-.=@[]^_{}~ " + if '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' { + return true + } + for i := 0; i < len(allowed); i++ { + if rune(allowed[i]) == r { + return true + } + } + return false + } + // It may be OK to add more ASCII punctuation here, but only carefully. + // For example Windows disallows < > \, and macOS disallows :, so we must not allow those. + return unicode.IsLetter(r) +} + +// CheckPath checks that a module path is valid. +func CheckPath(path string) error { + if err := checkPath(path, false); err != nil { + return fmt.Errorf("malformed module path %q: %v", path, err) + } + i := strings.Index(path, "/") + if i < 0 { + i = len(path) + } + if i == 0 { + return fmt.Errorf("malformed module path %q: leading slash", path) + } + if !strings.Contains(path[:i], ".") { + return fmt.Errorf("malformed module path %q: missing dot in first path element", path) + } + if path[0] == '-' { + return fmt.Errorf("malformed module path %q: leading dash in first path element", path) + } + for _, r := range path[:i] { + if !firstPathOK(r) { + return fmt.Errorf("malformed module path %q: invalid char %q in first path element", path, r) + } + } + if _, _, ok := SplitPathVersion(path); !ok { + return fmt.Errorf("malformed module path %q: invalid version", path) + } + return nil +} + +// CheckImportPath checks that an import path is valid. +func CheckImportPath(path string) error { + if err := checkPath(path, false); err != nil { + return fmt.Errorf("malformed import path %q: %v", path, err) + } + return nil +} + +// checkPath checks that a general path is valid. +// It returns an error describing why but not mentioning path. +// Because these checks apply to both module paths and import paths, +// the caller is expected to add the "malformed ___ path %q: " prefix. +// fileName indicates whether the final element of the path is a file name +// (as opposed to a directory name). +func checkPath(path string, fileName bool) error { + if !utf8.ValidString(path) { + return fmt.Errorf("invalid UTF-8") + } + if path == "" { + return fmt.Errorf("empty string") + } + if strings.Contains(path, "..") { + return fmt.Errorf("double dot") + } + if strings.Contains(path, "//") { + return fmt.Errorf("double slash") + } + if path[len(path)-1] == '/' { + return fmt.Errorf("trailing slash") + } + elemStart := 0 + for i, r := range path { + if r == '/' { + if err := checkElem(path[elemStart:i], fileName); err != nil { + return err + } + elemStart = i + 1 + } + } + if err := checkElem(path[elemStart:], fileName); err != nil { + return err + } + return nil +} + +// checkElem checks whether an individual path element is valid. +// fileName indicates whether the element is a file name (not a directory name). +func checkElem(elem string, fileName bool) error { + if elem == "" { + return fmt.Errorf("empty path element") + } + if strings.Count(elem, ".") == len(elem) { + return fmt.Errorf("invalid path element %q", elem) + } + if elem[0] == '.' && !fileName { + return fmt.Errorf("leading dot in path element") + } + if elem[len(elem)-1] == '.' { + return fmt.Errorf("trailing dot in path element") + } + charOK := pathOK + if fileName { + charOK = fileNameOK + } + for _, r := range elem { + if !charOK(r) { + return fmt.Errorf("invalid char %q", r) + } + } + + // Windows disallows a bunch of path elements, sadly. + // See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file + short := elem + if i := strings.Index(short, "."); i >= 0 { + short = short[:i] + } + for _, bad := range badWindowsNames { + if strings.EqualFold(bad, short) { + return fmt.Errorf("disallowed path element %q", elem) + } + } + return nil +} + +// CheckFilePath checks whether a slash-separated file path is valid. +func CheckFilePath(path string) error { + if err := checkPath(path, true); err != nil { + return fmt.Errorf("malformed file path %q: %v", path, err) + } + return nil +} + +// badWindowsNames are the reserved file path elements on Windows. +// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file +var badWindowsNames = []string{ + "CON", + "PRN", + "AUX", + "NUL", + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9", + "LPT1", + "LPT2", + "LPT3", + "LPT4", + "LPT5", + "LPT6", + "LPT7", + "LPT8", + "LPT9", +} + +// SplitPathVersion returns prefix and major version such that prefix+pathMajor == path +// and version is either empty or "/vN" for N >= 2. +// As a special case, gopkg.in paths are recognized directly; +// they require ".vN" instead of "/vN", and for all N, not just N >= 2. +func SplitPathVersion(path string) (prefix, pathMajor string, ok bool) { + if strings.HasPrefix(path, "gopkg.in/") { + return splitGopkgIn(path) + } + + i := len(path) + dot := false + for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') { + if path[i-1] == '.' { + dot = true + } + i-- + } + if i <= 1 || path[i-1] != 'v' || path[i-2] != '/' { + return path, "", true + } + prefix, pathMajor = path[:i-2], path[i-2:] + if dot || len(pathMajor) <= 2 || pathMajor[2] == '0' || pathMajor == "/v1" { + return path, "", false + } + return prefix, pathMajor, true +} + +// splitGopkgIn is like SplitPathVersion but only for gopkg.in paths. +func splitGopkgIn(path string) (prefix, pathMajor string, ok bool) { + if !strings.HasPrefix(path, "gopkg.in/") { + return path, "", false + } + i := len(path) + if strings.HasSuffix(path, "-unstable") { + i -= len("-unstable") + } + for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9') { + i-- + } + if i <= 1 || path[i-1] != 'v' || path[i-2] != '.' { + // All gopkg.in paths must end in vN for some N. + return path, "", false + } + prefix, pathMajor = path[:i-2], path[i-2:] + if len(pathMajor) <= 2 || pathMajor[2] == '0' && pathMajor != ".v0" { + return path, "", false + } + return prefix, pathMajor, true +} + +// MatchPathMajor reports whether the semantic version v +// matches the path major version pathMajor. +func MatchPathMajor(v, pathMajor string) bool { + if strings.HasPrefix(pathMajor, ".v") && strings.HasSuffix(pathMajor, "-unstable") { + pathMajor = strings.TrimSuffix(pathMajor, "-unstable") + } + if strings.HasPrefix(v, "v0.0.0-") && pathMajor == ".v1" { + // Allow old bug in pseudo-versions that generated v0.0.0- pseudoversion for gopkg .v1. + // For example, gopkg.in/yaml.v2@v2.2.1's go.mod requires gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405. + return true + } + m := semver.Major(v) + if pathMajor == "" { + return m == "v0" || m == "v1" || semver.Build(v) == "+incompatible" + } + return (pathMajor[0] == '/' || pathMajor[0] == '.') && m == pathMajor[1:] +} + +// CanonicalVersion returns the canonical form of the version string v. +// It is the same as semver.Canonical(v) except that it preserves the special build suffix "+incompatible". +func CanonicalVersion(v string) string { + cv := semver.Canonical(v) + if semver.Build(v) == "+incompatible" { + cv += "+incompatible" + } + return cv +} + +// Sort sorts the list by Path, breaking ties by comparing Versions. +func Sort(list []Version) { + sort.Slice(list, func(i, j int) bool { + mi := list[i] + mj := list[j] + if mi.Path != mj.Path { + return mi.Path < mj.Path + } + // To help go.sum formatting, allow version/file. + // Compare semver prefix by semver rules, + // file by string order. + vi := mi.Version + vj := mj.Version + var fi, fj string + if k := strings.Index(vi, "/"); k >= 0 { + vi, fi = vi[:k], vi[k:] + } + if k := strings.Index(vj, "/"); k >= 0 { + vj, fj = vj[:k], vj[k:] + } + if vi != vj { + return semver.Compare(vi, vj) < 0 + } + return fi < fj + }) +} + +// Safe encodings +// +// Module paths appear as substrings of file system paths +// (in the download cache) and of web server URLs in the proxy protocol. +// In general we cannot rely on file systems to be case-sensitive, +// nor can we rely on web servers, since they read from file systems. +// That is, we cannot rely on the file system to keep rsc.io/QUOTE +// and rsc.io/quote separate. Windows and macOS don't. +// Instead, we must never require two different casings of a file path. +// Because we want the download cache to match the proxy protocol, +// and because we want the proxy protocol to be possible to serve +// from a tree of static files (which might be stored on a case-insensitive +// file system), the proxy protocol must never require two different casings +// of a URL path either. +// +// One possibility would be to make the safe encoding be the lowercase +// hexadecimal encoding of the actual path bytes. This would avoid ever +// needing different casings of a file path, but it would be fairly illegible +// to most programmers when those paths appeared in the file system +// (including in file paths in compiler errors and stack traces) +// in web server logs, and so on. Instead, we want a safe encoding that +// leaves most paths unaltered. +// +// The safe encoding is this: +// replace every uppercase letter with an exclamation mark +// followed by the letter's lowercase equivalent. +// +// For example, +// github.com/Azure/azure-sdk-for-go -> github.com/!azure/azure-sdk-for-go. +// github.com/GoogleCloudPlatform/cloudsql-proxy -> github.com/!google!cloud!platform/cloudsql-proxy +// github.com/Sirupsen/logrus -> github.com/!sirupsen/logrus. +// +// Import paths that avoid upper-case letters are left unchanged. +// Note that because import paths are ASCII-only and avoid various +// problematic punctuation (like : < and >), the safe encoding is also ASCII-only +// and avoids the same problematic punctuation. +// +// Import paths have never allowed exclamation marks, so there is no +// need to define how to encode a literal !. +// +// Although paths are disallowed from using Unicode (see pathOK above), +// the eventual plan is to allow Unicode letters as well, to assume that +// file systems and URLs are Unicode-safe (storing UTF-8), and apply +// the !-for-uppercase convention. Note however that not all runes that +// are different but case-fold equivalent are an upper/lower pair. +// For example, U+004B ('K'), U+006B ('k'), and U+212A ('K' for Kelvin) +// are considered to case-fold to each other. When we do add Unicode +// letters, we must not assume that upper/lower are the only case-equivalent pairs. +// Perhaps the Kelvin symbol would be disallowed entirely, for example. +// Or perhaps it would encode as "!!k", or perhaps as "(212A)". +// +// Also, it would be nice to allow Unicode marks as well as letters, +// but marks include combining marks, and then we must deal not +// only with case folding but also normalization: both U+00E9 ('é') +// and U+0065 U+0301 ('e' followed by combining acute accent) +// look the same on the page and are treated by some file systems +// as the same path. If we do allow Unicode marks in paths, there +// must be some kind of normalization to allow only one canonical +// encoding of any character used in an import path. + +// EncodePath returns the safe encoding of the given module path. +// It fails if the module path is invalid. +func EncodePath(path string) (encoding string, err error) { + if err := CheckPath(path); err != nil { + return "", err + } + + return encodeString(path) +} + +// EncodeVersion returns the safe encoding of the given module version. +// Versions are allowed to be in non-semver form but must be valid file names +// and not contain exclamation marks. +func EncodeVersion(v string) (encoding string, err error) { + if err := checkElem(v, true); err != nil || strings.Contains(v, "!") { + return "", fmt.Errorf("disallowed version string %q", v) + } + return encodeString(v) +} + +func encodeString(s string) (encoding string, err error) { + haveUpper := false + for _, r := range s { + if r == '!' || r >= utf8.RuneSelf { + // This should be disallowed by CheckPath, but diagnose anyway. + // The correctness of the encoding loop below depends on it. + return "", fmt.Errorf("internal error: inconsistency in EncodePath") + } + if 'A' <= r && r <= 'Z' { + haveUpper = true + } + } + + if !haveUpper { + return s, nil + } + + var buf []byte + for _, r := range s { + if 'A' <= r && r <= 'Z' { + buf = append(buf, '!', byte(r+'a'-'A')) + } else { + buf = append(buf, byte(r)) + } + } + return string(buf), nil +} + +// DecodePath returns the module path of the given safe encoding. +// It fails if the encoding is invalid or encodes an invalid path. +func DecodePath(encoding string) (path string, err error) { + path, ok := decodeString(encoding) + if !ok { + return "", fmt.Errorf("invalid module path encoding %q", encoding) + } + if err := CheckPath(path); err != nil { + return "", fmt.Errorf("invalid module path encoding %q: %v", encoding, err) + } + return path, nil +} + +// DecodeVersion returns the version string for the given safe encoding. +// It fails if the encoding is invalid or encodes an invalid version. +// Versions are allowed to be in non-semver form but must be valid file names +// and not contain exclamation marks. +func DecodeVersion(encoding string) (v string, err error) { + v, ok := decodeString(encoding) + if !ok { + return "", fmt.Errorf("invalid version encoding %q", encoding) + } + if err := checkElem(v, true); err != nil { + return "", fmt.Errorf("disallowed version string %q", v) + } + return v, nil +} + +func decodeString(encoding string) (string, bool) { + var buf []byte + + bang := false + for _, r := range encoding { + if r >= utf8.RuneSelf { + return "", false + } + if bang { + bang = false + if r < 'a' || 'z' < r { + return "", false + } + buf = append(buf, byte(r+'A'-'a')) + continue + } + if r == '!' { + bang = true + continue + } + if 'A' <= r && r <= 'Z' { + return "", false + } + buf = append(buf, byte(r)) + } + if bang { + return "", false + } + return string(buf), true +} diff --git a/libgo/go/cmd/go/internal/module/module_test.go b/libgo/go/cmd/go/internal/module/module_test.go new file mode 100644 index 0000000..f21d620 --- /dev/null +++ b/libgo/go/cmd/go/internal/module/module_test.go @@ -0,0 +1,318 @@ +// Copyright 2018 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 module + +import "testing" + +var checkTests = []struct { + path string + version string + ok bool +}{ + {"rsc.io/quote", "0.1.0", false}, + {"rsc io/quote", "v1.0.0", false}, + + {"github.com/go-yaml/yaml", "v0.8.0", true}, + {"github.com/go-yaml/yaml", "v1.0.0", true}, + {"github.com/go-yaml/yaml", "v2.0.0", false}, + {"github.com/go-yaml/yaml", "v2.1.5", false}, + {"github.com/go-yaml/yaml", "v3.0.0", false}, + + {"github.com/go-yaml/yaml/v2", "v1.0.0", false}, + {"github.com/go-yaml/yaml/v2", "v2.0.0", true}, + {"github.com/go-yaml/yaml/v2", "v2.1.5", true}, + {"github.com/go-yaml/yaml/v2", "v3.0.0", false}, + + {"gopkg.in/yaml.v0", "v0.8.0", true}, + {"gopkg.in/yaml.v0", "v1.0.0", false}, + {"gopkg.in/yaml.v0", "v2.0.0", false}, + {"gopkg.in/yaml.v0", "v2.1.5", false}, + {"gopkg.in/yaml.v0", "v3.0.0", false}, + + {"gopkg.in/yaml.v1", "v0.8.0", false}, + {"gopkg.in/yaml.v1", "v1.0.0", true}, + {"gopkg.in/yaml.v1", "v2.0.0", false}, + {"gopkg.in/yaml.v1", "v2.1.5", false}, + {"gopkg.in/yaml.v1", "v3.0.0", false}, + + // For gopkg.in, .v1 means v1 only (not v0). + // But early versions of vgo still generated v0 pseudo-versions for it. + // Even though now we'd generate those as v1 pseudo-versions, + // we accept the old pseudo-versions to avoid breaking existing go.mod files. + // For example gopkg.in/yaml.v2@v2.2.1's go.mod requires check.v1 at a v0 pseudo-version. + {"gopkg.in/check.v1", "v0.0.0", false}, + {"gopkg.in/check.v1", "v0.0.0-20160102150405-abcdef123456", true}, + + {"gopkg.in/yaml.v2", "v1.0.0", false}, + {"gopkg.in/yaml.v2", "v2.0.0", true}, + {"gopkg.in/yaml.v2", "v2.1.5", true}, + {"gopkg.in/yaml.v2", "v3.0.0", false}, + + {"rsc.io/quote", "v17.0.0", false}, + {"rsc.io/quote", "v17.0.0+incompatible", true}, +} + +func TestCheck(t *testing.T) { + for _, tt := range checkTests { + err := Check(tt.path, tt.version) + if tt.ok && err != nil { + t.Errorf("Check(%q, %q) = %v, wanted nil error", tt.path, tt.version, err) + } else if !tt.ok && err == nil { + t.Errorf("Check(%q, %q) succeeded, wanted error", tt.path, tt.version) + } + } +} + +var checkPathTests = []struct { + path string + ok bool + importOK bool + fileOK bool +}{ + {"x.y/z", true, true, true}, + {"x.y", true, true, true}, + + {"", false, false, false}, + {"x.y/\xFFz", false, false, false}, + {"/x.y/z", false, false, false}, + {"x./z", false, false, false}, + {".x/z", false, false, true}, + {"-x/z", false, true, true}, + {"x..y/z", false, false, false}, + {"x.y/z/../../w", false, false, false}, + {"x.y//z", false, false, false}, + {"x.y/z//w", false, false, false}, + {"x.y/z/", false, false, false}, + + {"x.y/z/v0", false, true, true}, + {"x.y/z/v1", false, true, true}, + {"x.y/z/v2", true, true, true}, + {"x.y/z/v2.0", false, true, true}, + {"X.y/z", false, true, true}, + + {"!x.y/z", false, false, true}, + {"_x.y/z", false, true, true}, + {"x.y!/z", false, false, true}, + {"x.y\"/z", false, false, false}, + {"x.y#/z", false, false, true}, + {"x.y$/z", false, false, true}, + {"x.y%/z", false, false, true}, + {"x.y&/z", false, false, true}, + {"x.y'/z", false, false, false}, + {"x.y(/z", false, false, true}, + {"x.y)/z", false, false, true}, + {"x.y*/z", false, false, false}, + {"x.y+/z", false, true, true}, + {"x.y,/z", false, false, true}, + {"x.y-/z", true, true, true}, + {"x.y./zt", false, false, false}, + {"x.y:/z", false, false, false}, + {"x.y;/z", false, false, false}, + {"x.y</z", false, false, false}, + {"x.y=/z", false, false, true}, + {"x.y>/z", false, false, false}, + {"x.y?/z", false, false, false}, + {"x.y@/z", false, false, true}, + {"x.y[/z", false, false, true}, + {"x.y\\/z", false, false, false}, + {"x.y]/z", false, false, true}, + {"x.y^/z", false, false, true}, + {"x.y_/z", false, true, true}, + {"x.y`/z", false, false, false}, + {"x.y{/z", false, false, true}, + {"x.y}/z", false, false, true}, + {"x.y~/z", false, true, true}, + {"x.y/z!", false, false, true}, + {"x.y/z\"", false, false, false}, + {"x.y/z#", false, false, true}, + {"x.y/z$", false, false, true}, + {"x.y/z%", false, false, true}, + {"x.y/z&", false, false, true}, + {"x.y/z'", false, false, false}, + {"x.y/z(", false, false, true}, + {"x.y/z)", false, false, true}, + {"x.y/z*", false, false, false}, + {"x.y/z+", true, true, true}, + {"x.y/z,", false, false, true}, + {"x.y/z-", true, true, true}, + {"x.y/z.t", true, true, true}, + {"x.y/z/t", true, true, true}, + {"x.y/z:", false, false, false}, + {"x.y/z;", false, false, false}, + {"x.y/z<", false, false, false}, + {"x.y/z=", false, false, true}, + {"x.y/z>", false, false, false}, + {"x.y/z?", false, false, false}, + {"x.y/z@", false, false, true}, + {"x.y/z[", false, false, true}, + {"x.y/z\\", false, false, false}, + {"x.y/z]", false, false, true}, + {"x.y/z^", false, false, true}, + {"x.y/z_", true, true, true}, + {"x.y/z`", false, false, false}, + {"x.y/z{", false, false, true}, + {"x.y/z}", false, false, true}, + {"x.y/z~", true, true, true}, + {"x.y/x.foo", true, true, true}, + {"x.y/aux.foo", false, false, false}, + {"x.y/prn", false, false, false}, + {"x.y/prn2", true, true, true}, + {"x.y/com", true, true, true}, + {"x.y/com1", false, false, false}, + {"x.y/com1.txt", false, false, false}, + {"x.y/calm1", true, true, true}, + {"github.com/!123/logrus", false, false, true}, + + // TODO: CL 41822 allowed Unicode letters in old "go get" + // without due consideration of the implications, and only on github.com (!). + // For now, we disallow non-ASCII characters in module mode, + // in both module paths and general import paths, + // until we can get the implications right. + // When we do, we'll enable them everywhere, not just for GitHub. + {"github.com/user/unicode/испытание", false, false, true}, + + {"../x", false, false, false}, + {"./y", false, false, false}, + {"x:y", false, false, false}, + {`\temp\foo`, false, false, false}, + {".gitignore", false, false, true}, + {".github/ISSUE_TEMPLATE", false, false, true}, + {"x☺y", false, false, false}, +} + +func TestCheckPath(t *testing.T) { + for _, tt := range checkPathTests { + err := CheckPath(tt.path) + if tt.ok && err != nil { + t.Errorf("CheckPath(%q) = %v, wanted nil error", tt.path, err) + } else if !tt.ok && err == nil { + t.Errorf("CheckPath(%q) succeeded, wanted error", tt.path) + } + + err = CheckImportPath(tt.path) + if tt.importOK && err != nil { + t.Errorf("CheckImportPath(%q) = %v, wanted nil error", tt.path, err) + } else if !tt.importOK && err == nil { + t.Errorf("CheckImportPath(%q) succeeded, wanted error", tt.path) + } + + err = CheckFilePath(tt.path) + if tt.fileOK && err != nil { + t.Errorf("CheckFilePath(%q) = %v, wanted nil error", tt.path, err) + } else if !tt.fileOK && err == nil { + t.Errorf("CheckFilePath(%q) succeeded, wanted error", tt.path) + } + } +} + +var splitPathVersionTests = []struct { + pathPrefix string + version string +}{ + {"x.y/z", ""}, + {"x.y/z", "/v2"}, + {"x.y/z", "/v3"}, + {"gopkg.in/yaml", ".v0"}, + {"gopkg.in/yaml", ".v1"}, + {"gopkg.in/yaml", ".v2"}, + {"gopkg.in/yaml", ".v3"}, +} + +func TestSplitPathVersion(t *testing.T) { + for _, tt := range splitPathVersionTests { + pathPrefix, version, ok := SplitPathVersion(tt.pathPrefix + tt.version) + if pathPrefix != tt.pathPrefix || version != tt.version || !ok { + t.Errorf("SplitPathVersion(%q) = %q, %q, %v, want %q, %q, true", tt.pathPrefix+tt.version, pathPrefix, version, ok, tt.pathPrefix, tt.version) + } + } + + for _, tt := range checkPathTests { + pathPrefix, version, ok := SplitPathVersion(tt.path) + if pathPrefix+version != tt.path { + t.Errorf("SplitPathVersion(%q) = %q, %q, %v, doesn't add to input", tt.path, pathPrefix, version, ok) + } + } +} + +var encodeTests = []struct { + path string + enc string // empty means same as path +}{ + {path: "ascii.com/abcdefghijklmnopqrstuvwxyz.-+/~_0123456789"}, + {path: "github.com/GoogleCloudPlatform/omega", enc: "github.com/!google!cloud!platform/omega"}, +} + +func TestEncodePath(t *testing.T) { + // Check invalid paths. + for _, tt := range checkPathTests { + if !tt.ok { + _, err := EncodePath(tt.path) + if err == nil { + t.Errorf("EncodePath(%q): succeeded, want error (invalid path)", tt.path) + } + } + } + + // Check encodings. + for _, tt := range encodeTests { + enc, err := EncodePath(tt.path) + if err != nil { + t.Errorf("EncodePath(%q): unexpected error: %v", tt.path, err) + continue + } + want := tt.enc + if want == "" { + want = tt.path + } + if enc != want { + t.Errorf("EncodePath(%q) = %q, want %q", tt.path, enc, want) + } + } +} + +var badDecode = []string{ + "github.com/GoogleCloudPlatform/omega", + "github.com/!google!cloud!platform!/omega", + "github.com/!0google!cloud!platform/omega", + "github.com/!_google!cloud!platform/omega", + "github.com/!!google!cloud!platform/omega", + "", +} + +func TestDecodePath(t *testing.T) { + // Check invalid decodings. + for _, bad := range badDecode { + _, err := DecodePath(bad) + if err == nil { + t.Errorf("DecodePath(%q): succeeded, want error (invalid decoding)", bad) + } + } + + // Check invalid paths (or maybe decodings). + for _, tt := range checkPathTests { + if !tt.ok { + path, err := DecodePath(tt.path) + if err == nil { + t.Errorf("DecodePath(%q) = %q, want error (invalid path)", tt.path, path) + } + } + } + + // Check encodings. + for _, tt := range encodeTests { + enc := tt.enc + if enc == "" { + enc = tt.path + } + path, err := DecodePath(enc) + if err != nil { + t.Errorf("DecodePath(%q): unexpected error: %v", enc, err) + continue + } + if path != tt.path { + t.Errorf("DecodePath(%q) = %q, want %q", enc, path, tt.path) + } + } +} diff --git a/libgo/go/cmd/go/internal/mvs/mvs.go b/libgo/go/cmd/go/internal/mvs/mvs.go new file mode 100644 index 0000000..8ec9162 --- /dev/null +++ b/libgo/go/cmd/go/internal/mvs/mvs.go @@ -0,0 +1,368 @@ +// Copyright 2018 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 mvs implements Minimal Version Selection. +// See https://research.swtch.com/vgo-mvs. +package mvs + +import ( + "fmt" + "sort" + "sync" + + "cmd/go/internal/base" + "cmd/go/internal/module" + "cmd/go/internal/par" +) + +// A Reqs is the requirement graph on which Minimal Version Selection (MVS) operates. +// +// The version strings are opaque except for the special version "none" +// (see the documentation for module.Version). In particular, MVS does not +// assume that the version strings are semantic versions; instead, the Max method +// gives access to the comparison operation. +// +// It must be safe to call methods on a Reqs from multiple goroutines simultaneously. +// Because a Reqs may read the underlying graph from the network on demand, +// the MVS algorithms parallelize the traversal to overlap network delays. +type Reqs interface { + // Required returns the module versions explicitly required by m itself. + // The caller must not modify the returned list. + Required(m module.Version) ([]module.Version, error) + + // Max returns the maximum of v1 and v2 (it returns either v1 or v2). + // + // For all versions v, Max(v, "none") must be v, + // and for the tanget passed as the first argument to MVS functions, + // Max(target, v) must be target. + // + // Note that v1 < v2 can be written Max(v1, v2) != v1 + // and similarly v1 <= v2 can be written Max(v1, v2) == v2. + Max(v1, v2 string) string + + // Upgrade returns the upgraded version of m, + // for use during an UpgradeAll operation. + // If m should be kept as is, Upgrade returns m. + // If m is not yet used in the build, then m.Version will be "none". + // More typically, m.Version will be the version required + // by some other module in the build. + // + // If no module version is available for the given path, + // Upgrade returns a non-nil error. + // TODO(rsc): Upgrade must be able to return errors, + // but should "no latest version" just return m instead? + Upgrade(m module.Version) (module.Version, error) + + // Previous returns the version of m.Path immediately prior to m.Version, + // or "none" if no such version is known. + Previous(m module.Version) (module.Version, error) +} + +type MissingModuleError struct { + Module module.Version +} + +func (e *MissingModuleError) Error() string { + return fmt.Sprintf("missing module: %v", e.Module) +} + +// BuildList returns the build list for the target module. +func BuildList(target module.Version, reqs Reqs) ([]module.Version, error) { + return buildList(target, reqs, nil) +} + +func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) module.Version) ([]module.Version, error) { + // Explore work graph in parallel in case reqs.Required + // does high-latency network operations. + var work par.Work + work.Add(target) + var ( + mu sync.Mutex + min = map[string]string{target.Path: target.Version} + firstErr error + ) + work.Do(10, func(item interface{}) { + m := item.(module.Version) + required, err := reqs.Required(m) + + mu.Lock() + if err != nil && firstErr == nil { + firstErr = err + } + if firstErr != nil { + mu.Unlock() + return + } + if v, ok := min[m.Path]; !ok || reqs.Max(v, m.Version) != v { + min[m.Path] = m.Version + } + mu.Unlock() + + for _, r := range required { + if r.Path == "" { + base.Errorf("Required(%v) returned zero module in list", m) + continue + } + work.Add(r) + } + + if upgrade != nil { + u := upgrade(m) + if u.Path == "" { + base.Errorf("Upgrade(%v) returned zero module", m) + return + } + work.Add(u) + } + }) + + if firstErr != nil { + return nil, firstErr + } + if v := min[target.Path]; v != target.Version { + panic(fmt.Sprintf("mistake: chose version %q instead of target %+v", v, target)) // TODO: Don't panic. + } + + list := []module.Version{target} + listed := map[string]bool{target.Path: true} + for i := 0; i < len(list); i++ { + m := list[i] + required, err := reqs.Required(m) + if err != nil { + return nil, err + } + for _, r := range required { + v := min[r.Path] + if r.Path != target.Path && reqs.Max(v, r.Version) != v { + panic(fmt.Sprintf("mistake: version %q does not satisfy requirement %+v", v, r)) // TODO: Don't panic. + } + if !listed[r.Path] { + list = append(list, module.Version{Path: r.Path, Version: v}) + listed[r.Path] = true + } + } + } + + tail := list[1:] + sort.Slice(tail, func(i, j int) bool { + return tail[i].Path < tail[j].Path + }) + return list, nil +} + +// Req returns the minimal requirement list for the target module +// that results in the given build list, with the constraint that all +// module paths listed in base must appear in the returned list. +func Req(target module.Version, list []module.Version, base []string, reqs Reqs) ([]module.Version, error) { + // Note: Not running in parallel because we assume + // that list came from a previous operation that paged + // in all the requirements, so there's no I/O to overlap now. + + // Compute postorder, cache requirements. + var postorder []module.Version + reqCache := map[module.Version][]module.Version{} + reqCache[target] = nil + var walk func(module.Version) error + walk = func(m module.Version) error { + _, ok := reqCache[m] + if ok { + return nil + } + required, err := reqs.Required(m) + if err != nil { + return err + } + reqCache[m] = required + for _, m1 := range required { + if err := walk(m1); err != nil { + return err + } + } + postorder = append(postorder, m) + return nil + } + for _, m := range list { + if err := walk(m); err != nil { + return nil, err + } + } + + // Walk modules in reverse post-order, only adding those not implied already. + have := map[string]string{} + walk = func(m module.Version) error { + if v, ok := have[m.Path]; ok && reqs.Max(m.Version, v) == v { + return nil + } + have[m.Path] = m.Version + for _, m1 := range reqCache[m] { + walk(m1) + } + return nil + } + max := map[string]string{} + for _, m := range list { + if v, ok := max[m.Path]; ok { + max[m.Path] = reqs.Max(m.Version, v) + } else { + max[m.Path] = m.Version + } + } + // First walk the base modules that must be listed. + var min []module.Version + for _, path := range base { + m := module.Version{Path: path, Version: max[path]} + min = append(min, m) + walk(m) + } + // Now the reverse postorder to bring in anything else. + for i := len(postorder) - 1; i >= 0; i-- { + m := postorder[i] + if max[m.Path] != m.Version { + // Older version. + continue + } + if have[m.Path] != m.Version { + min = append(min, m) + walk(m) + } + } + sort.Slice(min, func(i, j int) bool { + return min[i].Path < min[j].Path + }) + return min, nil +} + +// UpgradeAll returns a build list for the target module +// in which every module is upgraded to its latest version. +func UpgradeAll(target module.Version, reqs Reqs) ([]module.Version, error) { + return buildList(target, reqs, func(m module.Version) module.Version { + if m.Path == target.Path { + return target + } + + latest, err := reqs.Upgrade(m) + if err != nil { + panic(err) // TODO + } + m.Version = latest.Version + return m + }) +} + +// Upgrade returns a build list for the target module +// in which the given additional modules are upgraded. +func Upgrade(target module.Version, reqs Reqs, upgrade ...module.Version) ([]module.Version, error) { + list, err := reqs.Required(target) + if err != nil { + panic(err) // TODO + } + // TODO: Maybe if an error is given, + // rerun with BuildList(upgrade[0], reqs) etc + // to find which ones are the buggy ones. + list = append([]module.Version(nil), list...) + list = append(list, upgrade...) + return BuildList(target, &override{target, list, reqs}) +} + +// Downgrade returns a build list for the target module +// in which the given additional modules are downgraded. +// +// The versions to be downgraded may be unreachable from reqs.Latest and +// reqs.Previous, but the methods of reqs must otherwise handle such versions +// correctly. +func Downgrade(target module.Version, reqs Reqs, downgrade ...module.Version) ([]module.Version, error) { + list, err := reqs.Required(target) + if err != nil { + panic(err) // TODO + } + max := make(map[string]string) + for _, r := range list { + max[r.Path] = r.Version + } + for _, d := range downgrade { + if v, ok := max[d.Path]; !ok || reqs.Max(v, d.Version) != d.Version { + max[d.Path] = d.Version + } + } + + var ( + added = make(map[module.Version]bool) + rdeps = make(map[module.Version][]module.Version) + excluded = make(map[module.Version]bool) + ) + var exclude func(module.Version) + exclude = func(m module.Version) { + if excluded[m] { + return + } + excluded[m] = true + for _, p := range rdeps[m] { + exclude(p) + } + } + var add func(module.Version) + add = func(m module.Version) { + if added[m] { + return + } + added[m] = true + if v, ok := max[m.Path]; ok && reqs.Max(m.Version, v) != v { + exclude(m) + return + } + list, err := reqs.Required(m) + if err != nil { + panic(err) // TODO + } + for _, r := range list { + add(r) + if excluded[r] { + exclude(m) + return + } + rdeps[r] = append(rdeps[r], m) + } + } + + var out []module.Version + out = append(out, target) +List: + for _, r := range list { + add(r) + for excluded[r] { + p, err := reqs.Previous(r) + if err != nil { + return nil, err // TODO + } + // If the target version is a pseudo-version, it may not be + // included when iterating over prior versions using reqs.Previous. + // Insert it into the right place in the iteration. + // If v is excluded, p should be returned again by reqs.Previous on the next iteration. + if v := max[r.Path]; reqs.Max(v, r.Version) != v && reqs.Max(p.Version, v) != p.Version { + p.Version = v + } + if p.Version == "none" { + continue List + } + add(p) + r = p + } + out = append(out, r) + } + + return out, nil +} + +type override struct { + target module.Version + list []module.Version + Reqs +} + +func (r *override) Required(m module.Version) ([]module.Version, error) { + if m == r.target { + return r.list, nil + } + return r.Reqs.Required(m) +} diff --git a/libgo/go/cmd/go/internal/mvs/mvs_test.go b/libgo/go/cmd/go/internal/mvs/mvs_test.go new file mode 100644 index 0000000..2a27dfb --- /dev/null +++ b/libgo/go/cmd/go/internal/mvs/mvs_test.go @@ -0,0 +1,473 @@ +// Copyright 2018 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 mvs + +import ( + "reflect" + "strings" + "testing" + + "cmd/go/internal/module" +) + +var tests = ` +# Scenario from blog. +name: blog +A: B1 C2 +B1: D3 +C1: D2 +C2: D4 +C3: D5 +C4: G1 +D2: E1 +D3: E2 +D4: E2 F1 +D5: E2 +G1: C4 +A2: B1 C4 D4 +build A: A B1 C2 D4 E2 F1 +upgrade* A: A B1 C4 D5 E2 G1 +upgrade A C4: A B1 C4 D4 E2 F1 G1 +downgrade A2 D2: A2 C4 D2 + +name: trim +A: B1 C2 +B1: D3 +C2: B2 +B2: +build A: A B2 C2 + +# Cross-dependency between D and E. +# No matter how it arises, should get result of merging all build lists via max, +# which leads to including both D2 and E2. + +name: cross1 +A: B C +B: D1 +C: D2 +D1: E2 +D2: E1 +build A: A B C D2 E2 + +name: cross1V +A: B2 C D2 E1 +B1: +B2: D1 +C: D2 +D1: E2 +D2: E1 +build A: A B2 C D2 E2 + +name: cross1U +A: B1 C +B1: +B2: D1 +C: D2 +D1: E2 +D2: E1 +build A: A B1 C D2 E1 +upgrade A B2: A B2 C D2 E2 + +name: cross1R +A: B C +B: D2 +C: D1 +D1: E2 +D2: E1 +build A: A B C D2 E2 + +name: cross1X +A: B C +B: D1 E2 +C: D2 +D1: E2 +D2: E1 +build A: A B C D2 E2 + +name: cross2 +A: B D2 +B: D1 +D1: E2 +D2: E1 +build A: A B D2 E2 + +name: cross2X +A: B D2 +B: D1 E2 +C: D2 +D1: E2 +D2: E1 +build A: A B D2 E2 + +name: cross3 +A: B D2 E1 +B: D1 +D1: E2 +D2: E1 +build A: A B D2 E2 + +name: cross3X +A: B D2 E1 +B: D1 E2 +D1: E2 +D2: E1 +build A: A B D2 E2 + +# Should not get E2 here, because B has been updated +# not to depend on D1 anymore. +name: cross4 +A1: B1 D2 +A2: B2 D2 +B1: D1 +B2: D2 +D1: E2 +D2: E1 +build A1: A1 B1 D2 E2 +build A2: A2 B2 D2 E1 + +# But the upgrade from A1 preserves the E2 dep explicitly. +upgrade A1 B2: A1 B2 D2 E2 +upgradereq A1 B2: B2 E2 + +name: cross5 +A: D1 +D1: E2 +D2: E1 +build A: A D1 E2 +upgrade* A: A D2 E2 +upgrade A D2: A D2 E2 +upgradereq A D2: D2 E2 + +name: cross6 +A: D2 +D1: E2 +D2: E1 +build A: A D2 E1 +upgrade* A: A D2 E2 +upgrade A E2: A D2 E2 + +name: cross7 +A: B C +B: D1 +C: E1 +D1: E2 +E1: D2 +build A: A B C D2 E2 + +# Upgrade from B1 to B2 should drop the transitive dep on D. +name: drop +A: B1 C1 +B1: D1 +B2: +C2: +D2: +build A: A B1 C1 D1 +upgrade* A: A B2 C2 + +name: simplify +A: B1 C1 +B1: C2 +C1: D1 +C2: +build A: A B1 C2 + +name: up1 +A: B1 C1 +B1: +B2: +B3: +B4: +B5.hidden: +C2: +C3: +build A: A B1 C1 +upgrade* A: A B4 C3 + +name: up2 +A: B5.hidden C1 +B1: +B2: +B3: +B4: +B5.hidden: +C2: +C3: +build A: A B5.hidden C1 +upgrade* A: A B5.hidden C3 + +name: down1 +A: B2 +B1: C1 +B2: C2 +build A: A B2 C2 +downgrade A C1: A B1 + +name: down2 +A: B2 E2 +B1: +B2: C2 F2 +C1: +D1: +C2: D2 E2 +D2: B2 +E2: D2 +E1: +F1: +downgrade A F1: A B1 E1 + +name: down3 +A: + +# golang.org/issue/25542. +name: noprev1 +A: B4 C2 +B2.hidden: +C2: +downgrade A B2.hidden: A B2.hidden C2 + +name: noprev2 +A: B4 C2 +B2.hidden: +B1: +C2: +downgrade A B2.hidden: A B2.hidden C2 + +name: noprev3 +A: B4 C2 +B3: +B2.hidden: +C2: +downgrade A B2.hidden: A B2.hidden C2 + +# Cycles involving the target. + +# The target must be the newest version of itself. +name: cycle1 +A: B1 +B1: A1 +B2: A2 +B3: A3 +build A: A B1 +upgrade A B2: A B2 +upgrade* A: A B3 + +# Requirements of older versions of the target +# must not be carried over. +name: cycle2 +A: B1 +A1: C1 +A2: D1 +B1: A1 +B2: A2 +C1: A2 +C2: +D2: +build A: A B1 +upgrade* A: A B2 + +# Requirement minimization. + +name: req1 +A: B1 C1 D1 E1 F1 +B1: C1 E1 F1 +req A: B1 D1 +req A C: B1 C1 D1 + +name: req2 +A: G1 H1 +G1: H1 +H1: G1 +req A: G1 +req A G: G1 +req A H: H1 +` + +func Test(t *testing.T) { + var ( + name string + reqs reqsMap + fns []func(*testing.T) + ) + flush := func() { + if name != "" { + t.Run(name, func(t *testing.T) { + for _, fn := range fns { + fn(t) + } + }) + } + } + m := func(s string) module.Version { + return module.Version{Path: s[:1], Version: s[1:]} + } + ms := func(list []string) []module.Version { + var mlist []module.Version + for _, s := range list { + mlist = append(mlist, m(s)) + } + return mlist + } + checkList := func(t *testing.T, desc string, list []module.Version, err error, val string) { + if err != nil { + t.Fatalf("%s: %v", desc, err) + } + vs := ms(strings.Fields(val)) + if !reflect.DeepEqual(list, vs) { + t.Errorf("%s = %v, want %v", desc, list, vs) + } + } + + for _, line := range strings.Split(tests, "\n") { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "#") || line == "" { + continue + } + i := strings.Index(line, ":") + if i < 0 { + t.Fatalf("missing colon: %q", line) + } + key := strings.TrimSpace(line[:i]) + val := strings.TrimSpace(line[i+1:]) + if key == "" { + t.Fatalf("missing key: %q", line) + } + kf := strings.Fields(key) + switch kf[0] { + case "name": + if len(kf) != 1 { + t.Fatalf("name takes no arguments: %q", line) + } + flush() + reqs = make(reqsMap) + fns = nil + name = val + continue + case "build": + if len(kf) != 2 { + t.Fatalf("build takes one argument: %q", line) + } + fns = append(fns, func(t *testing.T) { + list, err := BuildList(m(kf[1]), reqs) + checkList(t, key, list, err, val) + }) + continue + case "upgrade*": + if len(kf) != 2 { + t.Fatalf("upgrade* takes one argument: %q", line) + } + fns = append(fns, func(t *testing.T) { + list, err := UpgradeAll(m(kf[1]), reqs) + checkList(t, key, list, err, val) + }) + continue + case "upgradereq": + if len(kf) < 2 { + t.Fatalf("upgrade takes at least one argument: %q", line) + } + fns = append(fns, func(t *testing.T) { + list, err := Upgrade(m(kf[1]), reqs, ms(kf[2:])...) + if err == nil { + list, err = Req(m(kf[1]), list, nil, reqs) + } + checkList(t, key, list, err, val) + }) + continue + case "upgrade": + if len(kf) < 2 { + t.Fatalf("upgrade takes at least one argument: %q", line) + } + fns = append(fns, func(t *testing.T) { + list, err := Upgrade(m(kf[1]), reqs, ms(kf[2:])...) + checkList(t, key, list, err, val) + }) + continue + case "downgrade": + if len(kf) < 2 { + t.Fatalf("downgrade takes at least one argument: %q", line) + } + fns = append(fns, func(t *testing.T) { + list, err := Downgrade(m(kf[1]), reqs, ms(kf[1:])...) + checkList(t, key, list, err, val) + }) + continue + case "req": + if len(kf) < 2 { + t.Fatalf("req takes at least one argument: %q", line) + } + fns = append(fns, func(t *testing.T) { + list, err := BuildList(m(kf[1]), reqs) + if err != nil { + t.Fatal(err) + } + list, err = Req(m(kf[1]), list, kf[2:], reqs) + checkList(t, key, list, err, val) + }) + continue + } + if len(kf) == 1 && 'A' <= key[0] && key[0] <= 'Z' { + var rs []module.Version + for _, f := range strings.Fields(val) { + r := m(f) + if reqs[r] == nil { + reqs[r] = []module.Version{} + } + rs = append(rs, r) + } + reqs[m(key)] = rs + continue + } + t.Fatalf("bad line: %q", line) + } + flush() +} + +type reqsMap map[module.Version][]module.Version + +func (r reqsMap) Max(v1, v2 string) string { + if v1 == "none" || v2 == "" { + return v2 + } + if v2 == "none" || v1 == "" { + return v1 + } + if v1 < v2 { + return v2 + } + return v1 +} + +func (r reqsMap) Upgrade(m module.Version) (module.Version, error) { + var u module.Version + for k := range r { + if k.Path == m.Path && u.Version < k.Version && !strings.HasSuffix(k.Version, ".hidden") { + u = k + } + } + if u.Path == "" { + return module.Version{}, &MissingModuleError{module.Version{Path: m.Path, Version: ""}} + } + return u, nil +} + +func (r reqsMap) Previous(m module.Version) (module.Version, error) { + var p module.Version + for k := range r { + if k.Path == m.Path && p.Version < k.Version && k.Version < m.Version && !strings.HasSuffix(k.Version, ".hidden") { + p = k + } + } + if p.Path == "" { + return module.Version{Path: m.Path, Version: "none"}, nil + } + return p, nil +} + +func (r reqsMap) Required(m module.Version) ([]module.Version, error) { + rr, ok := r[m] + if !ok { + return nil, &MissingModuleError{m} + } + return rr, nil +} diff --git a/libgo/go/cmd/go/internal/par/work.go b/libgo/go/cmd/go/internal/par/work.go new file mode 100644 index 0000000..a568c86 --- /dev/null +++ b/libgo/go/cmd/go/internal/par/work.go @@ -0,0 +1,149 @@ +// Copyright 2018 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 par implements parallel execution helpers. +package par + +import ( + "math/rand" + "sync" + "sync/atomic" +) + +// Work manages a set of work items to be executed in parallel, at most once each. +// The items in the set must all be valid map keys. +type Work struct { + f func(interface{}) // function to run for each item + running int // total number of runners + + mu sync.Mutex + added map[interface{}]bool // items added to set + todo []interface{} // items yet to be run + wait sync.Cond // wait when todo is empty + waiting int // number of runners waiting for todo +} + +func (w *Work) init() { + if w.added == nil { + w.added = make(map[interface{}]bool) + } +} + +// Add adds item to the work set, if it hasn't already been added. +func (w *Work) Add(item interface{}) { + w.mu.Lock() + w.init() + if !w.added[item] { + w.added[item] = true + w.todo = append(w.todo, item) + if w.waiting > 0 { + w.wait.Signal() + } + } + w.mu.Unlock() +} + +// Do runs f in parallel on items from the work set, +// with at most n invocations of f running at a time. +// It returns when everything added to the work set has been processed. +// At least one item should have been added to the work set +// before calling Do (or else Do returns immediately), +// but it is allowed for f(item) to add new items to the set. +// Do should only be used once on a given Work. +func (w *Work) Do(n int, f func(item interface{})) { + if n < 1 { + panic("par.Work.Do: n < 1") + } + if w.running >= 1 { + panic("par.Work.Do: already called Do") + } + + w.running = n + w.f = f + w.wait.L = &w.mu + + for i := 0; i < n-1; i++ { + go w.runner() + } + w.runner() +} + +// runner executes work in w until both nothing is left to do +// and all the runners are waiting for work. +// (Then all the runners return.) +func (w *Work) runner() { + for { + // Wait for something to do. + w.mu.Lock() + for len(w.todo) == 0 { + w.waiting++ + if w.waiting == w.running { + // All done. + w.wait.Broadcast() + w.mu.Unlock() + return + } + w.wait.Wait() + w.waiting-- + } + + // Pick something to do at random, + // to eliminate pathological contention + // in case items added at about the same time + // are most likely to contend. + i := rand.Intn(len(w.todo)) + item := w.todo[i] + w.todo[i] = w.todo[len(w.todo)-1] + w.todo = w.todo[:len(w.todo)-1] + w.mu.Unlock() + + w.f(item) + } +} + +// Cache runs an action once per key and caches the result. +type Cache struct { + m sync.Map +} + +type cacheEntry struct { + done uint32 + mu sync.Mutex + result interface{} +} + +// Do calls the function f if and only if Do is being called for the first time with this key. +// No call to Do with a given key returns until the one call to f returns. +// Do returns the value returned by the one call to f. +func (c *Cache) Do(key interface{}, f func() interface{}) interface{} { + entryIface, ok := c.m.Load(key) + if !ok { + entryIface, _ = c.m.LoadOrStore(key, new(cacheEntry)) + } + e := entryIface.(*cacheEntry) + if atomic.LoadUint32(&e.done) == 0 { + e.mu.Lock() + if atomic.LoadUint32(&e.done) == 0 { + e.result = f() + atomic.StoreUint32(&e.done, 1) + } + e.mu.Unlock() + } + return e.result +} + +// Get returns the cached result associated with key. +// It returns nil if there is no such result. +// If the result for key is being computed, Get does not wait for the computation to finish. +func (c *Cache) Get(key interface{}) interface{} { + entryIface, ok := c.m.Load(key) + if !ok { + return nil + } + e := entryIface.(*cacheEntry) + if atomic.LoadUint32(&e.done) == 0 { + return nil + } + return e.result +} diff --git a/libgo/go/cmd/go/internal/par/work_test.go b/libgo/go/cmd/go/internal/par/work_test.go new file mode 100644 index 0000000..f104bc4 --- /dev/null +++ b/libgo/go/cmd/go/internal/par/work_test.go @@ -0,0 +1,77 @@ +// Copyright 2018 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 par + +import ( + "sync/atomic" + "testing" + "time" +) + +func TestWork(t *testing.T) { + var w Work + + const N = 10000 + n := int32(0) + w.Add(N) + w.Do(100, func(x interface{}) { + atomic.AddInt32(&n, 1) + i := x.(int) + if i >= 2 { + w.Add(i - 1) + w.Add(i - 2) + } + w.Add(i >> 1) + w.Add((i >> 1) ^ 1) + }) + if n != N+1 { + t.Fatalf("ran %d items, expected %d", n, N+1) + } +} + +func TestWorkParallel(t *testing.T) { + for tries := 0; tries < 10; tries++ { + var w Work + const N = 100 + for i := 0; i < N; i++ { + w.Add(i) + } + start := time.Now() + var n int32 + w.Do(N, func(x interface{}) { + time.Sleep(1 * time.Millisecond) + atomic.AddInt32(&n, +1) + }) + if n != N { + t.Fatalf("par.Work.Do did not do all the work") + } + if time.Since(start) < N/2*time.Millisecond { + return + } + } + t.Fatalf("par.Work.Do does not seem to be parallel") +} + +func TestCache(t *testing.T) { + var cache Cache + + n := 1 + v := cache.Do(1, func() interface{} { n++; return n }) + if v != 2 { + t.Fatalf("cache.Do(1) did not run f") + } + v = cache.Do(1, func() interface{} { n++; return n }) + if v != 2 { + t.Fatalf("cache.Do(1) ran f again!") + } + v = cache.Do(2, func() interface{} { n++; return n }) + if v != 3 { + t.Fatalf("cache.Do(2) did not run f") + } + v = cache.Do(1, func() interface{} { n++; return n }) + if v != 2 { + t.Fatalf("cache.Do(1) did not returned saved value from original cache.Do(1)") + } +} diff --git a/libgo/go/cmd/go/internal/run/run.go b/libgo/go/cmd/go/internal/run/run.go index ce24748..303e684 100644 --- a/libgo/go/cmd/go/internal/run/run.go +++ b/libgo/go/cmd/go/internal/run/run.go @@ -18,11 +18,13 @@ import ( ) var CmdRun = &base.Command{ - UsageLine: "run [build flags] [-exec xprog] gofiles... [arguments...]", + UsageLine: "go run [build flags] [-exec xprog] package [arguments...]", Short: "compile and run Go program", Long: ` -Run compiles and runs the main package comprising the named Go source files. -A Go source file is defined to be a file ending in a literal ".go" suffix. +Run compiles and runs the named main Go package. +Typically the package is specified as a list of .go source files, +but it may also be an import path, file system path, or pattern +matching a single known package, as in 'go run .' or 'go run my/cmd'. By default, 'go run' runs the compiled binary directly: 'a.out arguments...'. If the -exec flag is given, 'go run' invokes the binary using xprog: @@ -34,7 +36,10 @@ for example 'go_nacl_386_exec a.out arguments...'. This allows execution of cross-compiled programs when a simulator or other execution method is available. +The exit status of Run is not the exit status of the compiled binary. + For more about build flags, see 'go help build'. +For more about specifying packages, see 'go help packages'. See also: go build. `, @@ -60,18 +65,33 @@ func runRun(cmd *base.Command, args []string) { for i < len(args) && strings.HasSuffix(args[i], ".go") { i++ } - files, cmdArgs := args[:i], args[i:] - if len(files) == 0 { - base.Fatalf("go run: no go files listed") - } - for _, file := range files { - if strings.HasSuffix(file, "_test.go") { - // GoFilesPackage is going to assign this to TestGoFiles. - // Reject since it won't be part of the build. - base.Fatalf("go run: cannot run *_test.go files (%s)", file) + var p *load.Package + if i > 0 { + files := args[:i] + for _, file := range files { + if strings.HasSuffix(file, "_test.go") { + // GoFilesPackage is going to assign this to TestGoFiles. + // Reject since it won't be part of the build. + base.Fatalf("go run: cannot run *_test.go files (%s)", file) + } + } + p = load.GoFilesPackage(files) + } else if len(args) > 0 && !strings.HasPrefix(args[0], "-") { + pkgs := load.PackagesAndErrors(args[:1]) + if len(pkgs) > 1 { + var names []string + for _, p := range pkgs { + names = append(names, p.ImportPath) + } + base.Fatalf("go run: pattern %s matches multiple packages:\n\t%s", args[0], strings.Join(names, "\n\t")) } + p = pkgs[0] + i++ + } else { + base.Fatalf("go run: no go files listed") } - p := load.GoFilesPackage(files) + cmdArgs := args[i:] + if p.Error != nil { base.Fatalf("%s", p.Error) } diff --git a/libgo/go/cmd/go/internal/search/search.go b/libgo/go/cmd/go/internal/search/search.go new file mode 100644 index 0000000..60ae736 --- /dev/null +++ b/libgo/go/cmd/go/internal/search/search.go @@ -0,0 +1,505 @@ +// Copyright 2017 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 search + +import ( + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "fmt" + "go/build" + "log" + "os" + "path" + "path/filepath" + "regexp" + "strings" +) + +// A Match represents the result of matching a single package pattern. +type Match struct { + Pattern string // the pattern itself + Literal bool // whether it is a literal (no wildcards) + Pkgs []string // matching packages (dirs or import paths) +} + +// MatchPackages returns all the packages that can be found +// under the $GOPATH directories and $GOROOT matching pattern. +// The pattern is either "all" (all packages), "std" (standard packages), +// "cmd" (standard commands), or a path including "...". +func MatchPackages(pattern string) *Match { + m := &Match{ + Pattern: pattern, + Literal: false, + } + match := func(string) bool { return true } + treeCanMatch := func(string) bool { return true } + if !IsMetaPackage(pattern) { + match = MatchPattern(pattern) + treeCanMatch = TreeCanMatchPattern(pattern) + } + + have := map[string]bool{ + "builtin": true, // ignore pseudo-package that exists only for documentation + } + if !cfg.BuildContext.CgoEnabled { + have["runtime/cgo"] = true // ignore during walk + } + + for _, src := range cfg.BuildContext.SrcDirs() { + if (pattern == "std" || pattern == "cmd") && src != cfg.GOROOTsrc { + continue + } + src = filepath.Clean(src) + string(filepath.Separator) + root := src + if pattern == "cmd" { + root += "cmd" + string(filepath.Separator) + } + filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { + if err != nil || path == src { + return nil + } + + want := true + // Avoid .foo, _foo, and testdata directory trees. + _, elem := filepath.Split(path) + if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { + want = false + } + + name := filepath.ToSlash(path[len(src):]) + if pattern == "std" && (!IsStandardImportPath(name) || name == "cmd") { + // The name "std" is only the standard library. + // If the name is cmd, it's the root of the command tree. + want = false + } + if !treeCanMatch(name) { + want = false + } + + if !fi.IsDir() { + if fi.Mode()&os.ModeSymlink != 0 && want { + if target, err := os.Stat(path); err == nil && target.IsDir() { + fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path) + } + } + return nil + } + if !want { + return filepath.SkipDir + } + + if have[name] { + return nil + } + have[name] = true + if !match(name) { + return nil + } + pkg, err := cfg.BuildContext.ImportDir(path, 0) + if err != nil { + if _, noGo := err.(*build.NoGoError); noGo { + return nil + } + } + + // If we are expanding "cmd", skip main + // packages under cmd/vendor. At least as of + // March, 2017, there is one there for the + // vendored pprof tool. + if pattern == "cmd" && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" { + return nil + } + + m.Pkgs = append(m.Pkgs, name) + return nil + }) + } + return m +} + +var modRoot string + +func SetModRoot(dir string) { + modRoot = dir +} + +// MatchPackagesInFS is like allPackages but is passed a pattern +// beginning ./ or ../, meaning it should scan the tree rooted +// at the given directory. There are ... in the pattern too. +// (See go help packages for pattern syntax.) +func MatchPackagesInFS(pattern string) *Match { + m := &Match{ + Pattern: pattern, + Literal: false, + } + + // Find directory to begin the scan. + // Could be smarter but this one optimization + // is enough for now, since ... is usually at the + // end of a path. + i := strings.Index(pattern, "...") + dir, _ := path.Split(pattern[:i]) + + // pattern begins with ./ or ../. + // path.Clean will discard the ./ but not the ../. + // We need to preserve the ./ for pattern matching + // and in the returned import paths. + prefix := "" + if strings.HasPrefix(pattern, "./") { + prefix = "./" + } + match := MatchPattern(pattern) + + if modRoot != "" { + abs, err := filepath.Abs(dir) + if err != nil { + base.Fatalf("go: %v", err) + } + if !hasFilepathPrefix(abs, modRoot) { + base.Fatalf("go: pattern %s refers to dir %s, outside module root %s", pattern, abs, modRoot) + return nil + } + } + + filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() { + return nil + } + top := false + if path == dir { + // filepath.Walk starts at dir and recurses. For the recursive case, + // the path is the result of filepath.Join, which calls filepath.Clean. + // The initial case is not Cleaned, though, so we do this explicitly. + // + // This converts a path like "./io/" to "io". Without this step, running + // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io + // package, because prepending the prefix "./" to the unclean path would + // result in "././io", and match("././io") returns false. + top = true + path = filepath.Clean(path) + } + + // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". + _, elem := filepath.Split(path) + dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." + if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { + return filepath.SkipDir + } + + if !top && cfg.ModulesEnabled { + // Ignore other modules found in subdirectories. + if _, err := os.Stat(filepath.Join(path, "go.mod")); err == nil { + return filepath.SkipDir + } + } + + name := prefix + filepath.ToSlash(path) + if !match(name) { + return nil + } + + // We keep the directory if we can import it, or if we can't import it + // due to invalid Go source files. This means that directories containing + // parse errors will be built (and fail) instead of being silently skipped + // as not matching the pattern. Go 1.5 and earlier skipped, but that + // behavior means people miss serious mistakes. + // See golang.org/issue/11407. + if p, err := cfg.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) { + if _, noGo := err.(*build.NoGoError); !noGo { + log.Print(err) + } + return nil + } + m.Pkgs = append(m.Pkgs, name) + return nil + }) + return m +} + +// TreeCanMatchPattern(pattern)(name) reports whether +// name or children of name can possibly match pattern. +// Pattern is the same limited glob accepted by matchPattern. +func TreeCanMatchPattern(pattern string) func(name string) bool { + wildCard := false + if i := strings.Index(pattern, "..."); i >= 0 { + wildCard = true + pattern = pattern[:i] + } + return func(name string) bool { + return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || + wildCard && strings.HasPrefix(name, pattern) + } +} + +// MatchPattern(pattern)(name) reports whether +// name matches pattern. Pattern is a limited glob +// pattern in which '...' means 'any string' and there +// is no other special syntax. +// Unfortunately, there are two special cases. Quoting "go help packages": +// +// First, /... at the end of the pattern can match an empty string, +// so that net/... matches both net and packages in its subdirectories, like net/http. +// Second, any slash-separted pattern element containing a wildcard never +// participates in a match of the "vendor" element in the path of a vendored +// package, so that ./... does not match packages in subdirectories of +// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do. +// Note, however, that a directory named vendor that itself contains code +// is not a vendored package: cmd/vendor would be a command named vendor, +// and the pattern cmd/... matches it. +func MatchPattern(pattern string) func(name string) bool { + // Convert pattern to regular expression. + // The strategy for the trailing /... is to nest it in an explicit ? expression. + // The strategy for the vendor exclusion is to change the unmatchable + // vendor strings to a disallowed code point (vendorChar) and to use + // "(anything but that codepoint)*" as the implementation of the ... wildcard. + // This is a bit complicated but the obvious alternative, + // namely a hand-written search like in most shell glob matchers, + // is too easy to make accidentally exponential. + // Using package regexp guarantees linear-time matching. + + const vendorChar = "\x00" + + if strings.Contains(pattern, vendorChar) { + return func(name string) bool { return false } + } + + re := regexp.QuoteMeta(pattern) + re = replaceVendor(re, vendorChar) + switch { + case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`): + re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)` + case re == vendorChar+`/\.\.\.`: + re = `(/vendor|/` + vendorChar + `/\.\.\.)` + case strings.HasSuffix(re, `/\.\.\.`): + re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?` + } + re = strings.Replace(re, `\.\.\.`, `[^`+vendorChar+`]*`, -1) + + reg := regexp.MustCompile(`^` + re + `$`) + + return func(name string) bool { + if strings.Contains(name, vendorChar) { + return false + } + return reg.MatchString(replaceVendor(name, vendorChar)) + } +} + +// replaceVendor returns the result of replacing +// non-trailing vendor path elements in x with repl. +func replaceVendor(x, repl string) string { + if !strings.Contains(x, "vendor") { + return x + } + elem := strings.Split(x, "/") + for i := 0; i < len(elem)-1; i++ { + if elem[i] == "vendor" { + elem[i] = repl + } + } + return strings.Join(elem, "/") +} + +// WarnUnmatched warns about patterns that didn't match any packages. +func WarnUnmatched(matches []*Match) { + for _, m := range matches { + if len(m.Pkgs) == 0 { + fmt.Fprintf(os.Stderr, "go: warning: %q matched no packages\n", m.Pattern) + } + } +} + +// ImportPaths returns the matching paths to use for the given command line. +// It calls ImportPathsQuiet and then WarnUnmatched. +func ImportPaths(patterns []string) []*Match { + matches := ImportPathsQuiet(patterns) + WarnUnmatched(matches) + return matches +} + +// ImportPathsQuiet is like ImportPaths but does not warn about patterns with no matches. +func ImportPathsQuiet(patterns []string) []*Match { + var out []*Match + for _, a := range CleanPatterns(patterns) { + if IsMetaPackage(a) { + out = append(out, MatchPackages(a)) + continue + } + if strings.Contains(a, "...") { + if build.IsLocalImport(a) { + out = append(out, MatchPackagesInFS(a)) + } else { + out = append(out, MatchPackages(a)) + } + continue + } + out = append(out, &Match{Pattern: a, Literal: true, Pkgs: []string{a}}) + } + return out +} + +// CleanPatterns returns the patterns to use for the given +// command line. It canonicalizes the patterns but does not +// evaluate any matches. +func CleanPatterns(patterns []string) []string { + if len(patterns) == 0 { + return []string{"."} + } + var out []string + for _, a := range patterns { + // Arguments are supposed to be import paths, but + // as a courtesy to Windows developers, rewrite \ to / + // in command-line arguments. Handles .\... and so on. + if filepath.Separator == '\\' { + a = strings.Replace(a, `\`, `/`, -1) + } + + // Put argument in canonical form, but preserve leading ./. + if strings.HasPrefix(a, "./") { + a = "./" + path.Clean(a) + if a == "./." { + a = "." + } + } else { + a = path.Clean(a) + } + out = append(out, a) + } + return out +} + +// IsMetaPackage checks if name is a reserved package name that expands to multiple packages. +func IsMetaPackage(name string) bool { + return name == "std" || name == "cmd" || name == "all" +} + +// hasPathPrefix reports whether the path s begins with the +// elements in prefix. +func hasPathPrefix(s, prefix string) bool { + switch { + default: + return false + case len(s) == len(prefix): + return s == prefix + case len(s) > len(prefix): + if prefix != "" && prefix[len(prefix)-1] == '/' { + return strings.HasPrefix(s, prefix) + } + return s[len(prefix)] == '/' && s[:len(prefix)] == prefix + } +} + +// hasFilepathPrefix reports whether the path s begins with the +// elements in prefix. +func hasFilepathPrefix(s, prefix string) bool { + switch { + default: + return false + case len(s) == len(prefix): + return s == prefix + case len(s) > len(prefix): + if prefix != "" && prefix[len(prefix)-1] == filepath.Separator { + return strings.HasPrefix(s, prefix) + } + return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix + } +} + +// IsStandardImportPath reports whether $GOROOT/src/path should be considered +// part of the standard distribution. For historical reasons we allow people to add +// their own code to $GOROOT instead of using $GOPATH, but we assume that +// code will start with a domain name (dot in the first element). +// +// Note that this function is meant to evaluate whether a directory found in GOROOT +// should be treated as part of the standard library. It should not be used to decide +// that a directory found in GOPATH should be rejected: directories in GOPATH +// need not have dots in the first element, and they just take their chances +// with future collisions in the standard library. +func IsStandardImportPath(path string) bool { + i := strings.Index(path, "/") + if i < 0 { + i = len(path) + } + elem := path[:i] + return !strings.Contains(elem, ".") +} + +// IsRelativePath reports whether pattern should be interpreted as a directory +// path relative to the current directory, as opposed to a pattern matching +// import paths. +func IsRelativePath(pattern string) bool { + return strings.HasPrefix(pattern, "./") || strings.HasPrefix(pattern, "../") || pattern == "." || pattern == ".." +} + +// InDir checks whether path is in the file tree rooted at dir. +// If so, InDir returns an equivalent path relative to dir. +// If not, InDir returns an empty string. +// InDir makes some effort to succeed even in the presence of symbolic links. +// TODO(rsc): Replace internal/test.inDir with a call to this function for Go 1.12. +func InDir(path, dir string) string { + if rel := inDirLex(path, dir); rel != "" { + return rel + } + xpath, err := filepath.EvalSymlinks(path) + if err != nil || xpath == path { + xpath = "" + } else { + if rel := inDirLex(xpath, dir); rel != "" { + return rel + } + } + + xdir, err := filepath.EvalSymlinks(dir) + if err == nil && xdir != dir { + if rel := inDirLex(path, xdir); rel != "" { + return rel + } + if xpath != "" { + if rel := inDirLex(xpath, xdir); rel != "" { + return rel + } + } + } + return "" +} + +// inDirLex is like inDir but only checks the lexical form of the file names. +// It does not consider symbolic links. +// TODO(rsc): This is a copy of str.HasFilePathPrefix, modified to +// return the suffix. Most uses of str.HasFilePathPrefix should probably +// be calling InDir instead. +func inDirLex(path, dir string) string { + pv := strings.ToUpper(filepath.VolumeName(path)) + dv := strings.ToUpper(filepath.VolumeName(dir)) + path = path[len(pv):] + dir = dir[len(dv):] + switch { + default: + return "" + case pv != dv: + return "" + case len(path) == len(dir): + if path == dir { + return "." + } + return "" + case dir == "": + return path + case len(path) > len(dir): + if dir[len(dir)-1] == filepath.Separator { + if path[:len(dir)] == dir { + return path[len(dir):] + } + return "" + } + if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir { + if len(path) == len(dir)+1 { + return "." + } + return path[len(dir)+1:] + } + return "" + } +} diff --git a/libgo/go/cmd/go/internal/load/match_test.go b/libgo/go/cmd/go/internal/search/search_test.go index b8d67da..0bef765 100644 --- a/libgo/go/cmd/go/internal/load/match_test.go +++ b/libgo/go/cmd/go/internal/search/search_test.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 load +package search import ( "strings" @@ -65,8 +65,8 @@ var matchPatternTests = ` ` func TestMatchPattern(t *testing.T) { - testPatterns(t, "matchPattern", matchPatternTests, func(pattern, name string) bool { - return matchPattern(pattern)(name) + testPatterns(t, "MatchPattern", matchPatternTests, func(pattern, name string) bool { + return MatchPattern(pattern)(name) }) } @@ -106,8 +106,8 @@ var treeCanMatchPatternTests = ` ` func TestTreeCanMatchPattern(t *testing.T) { - testPatterns(t, "treeCanMatchPattern", treeCanMatchPatternTests, func(pattern, name string) bool { - return treeCanMatchPattern(pattern)(name) + testPatterns(t, "TreeCanMatchPattern", treeCanMatchPatternTests, func(pattern, name string) bool { + return TreeCanMatchPattern(pattern)(name) }) } diff --git a/libgo/go/cmd/go/internal/semver/semver.go b/libgo/go/cmd/go/internal/semver/semver.go new file mode 100644 index 0000000..4af7118 --- /dev/null +++ b/libgo/go/cmd/go/internal/semver/semver.go @@ -0,0 +1,388 @@ +// Copyright 2018 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 semver implements comparison of semantic version strings. +// In this package, semantic version strings must begin with a leading "v", +// as in "v1.0.0". +// +// The general form of a semantic version string accepted by this package is +// +// vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]] +// +// where square brackets indicate optional parts of the syntax; +// MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros; +// PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers +// using only alphanumeric characters and hyphens; and +// all-numeric PRERELEASE identifiers must not have leading zeros. +// +// This package follows Semantic Versioning 2.0.0 (see semver.org) +// with two exceptions. First, it requires the "v" prefix. Second, it recognizes +// vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes) +// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0. +package semver + +// parsed returns the parsed form of a semantic version string. +type parsed struct { + major string + minor string + patch string + short string + prerelease string + build string + err string +} + +// IsValid reports whether v is a valid semantic version string. +func IsValid(v string) bool { + _, ok := parse(v) + return ok +} + +// Canonical returns the canonical formatting of the semantic version v. +// It fills in any missing .MINOR or .PATCH and discards build metadata. +// Two semantic versions compare equal only if their canonical formattings +// are identical strings. +// The canonical invalid semantic version is the empty string. +func Canonical(v string) string { + p, ok := parse(v) + if !ok { + return "" + } + if p.build != "" { + return v[:len(v)-len(p.build)] + } + if p.short != "" { + return v + p.short + } + return v +} + +// Major returns the major version prefix of the semantic version v. +// For example, Major("v2.1.0") == "v2". +// If v is an invalid semantic version string, Major returns the empty string. +func Major(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + return v[:1+len(pv.major)] +} + +// MajorMinor returns the major.minor version prefix of the semantic version v. +// For example, MajorMinor("v2.1.0") == "v2.1". +// If v is an invalid semantic version string, MajorMinor returns the empty string. +func MajorMinor(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + i := 1 + len(pv.major) + if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor { + return v[:j] + } + return v[:i] + "." + pv.minor +} + +// Prerelease returns the prerelease suffix of the semantic version v. +// For example, Prerelease("v2.1.0-pre+meta") == "-pre". +// If v is an invalid semantic version string, Prerelease returns the empty string. +func Prerelease(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + return pv.prerelease +} + +// Build returns the build suffix of the semantic version v. +// For example, Build("v2.1.0+meta") == "+meta". +// If v is an invalid semantic version string, Build returns the empty string. +func Build(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + return pv.build +} + +// Compare returns an integer comparing two versions according to +// according to semantic version precedence. +// The result will be 0 if v == w, -1 if v < w, or +1 if v > w. +// +// An invalid semantic version string is considered less than a valid one. +// All invalid semantic version strings compare equal to each other. +func Compare(v, w string) int { + pv, ok1 := parse(v) + pw, ok2 := parse(w) + if !ok1 && !ok2 { + return 0 + } + if !ok1 { + return -1 + } + if !ok2 { + return +1 + } + if c := compareInt(pv.major, pw.major); c != 0 { + return c + } + if c := compareInt(pv.minor, pw.minor); c != 0 { + return c + } + if c := compareInt(pv.patch, pw.patch); c != 0 { + return c + } + return comparePrerelease(pv.prerelease, pw.prerelease) +} + +// Max canonicalizes its arguments and then returns the version string +// that compares greater. +func Max(v, w string) string { + v = Canonical(v) + w = Canonical(w) + if Compare(v, w) > 0 { + return v + } + return w +} + +func parse(v string) (p parsed, ok bool) { + if v == "" || v[0] != 'v' { + p.err = "missing v prefix" + return + } + p.major, v, ok = parseInt(v[1:]) + if !ok { + p.err = "bad major version" + return + } + if v == "" { + p.minor = "0" + p.patch = "0" + p.short = ".0.0" + return + } + if v[0] != '.' { + p.err = "bad minor prefix" + ok = false + return + } + p.minor, v, ok = parseInt(v[1:]) + if !ok { + p.err = "bad minor version" + return + } + if v == "" { + p.patch = "0" + p.short = ".0" + return + } + if v[0] != '.' { + p.err = "bad patch prefix" + ok = false + return + } + p.patch, v, ok = parseInt(v[1:]) + if !ok { + p.err = "bad patch version" + return + } + if len(v) > 0 && v[0] == '-' { + p.prerelease, v, ok = parsePrerelease(v) + if !ok { + p.err = "bad prerelease" + return + } + } + if len(v) > 0 && v[0] == '+' { + p.build, v, ok = parseBuild(v) + if !ok { + p.err = "bad build" + return + } + } + if v != "" { + p.err = "junk on end" + ok = false + return + } + ok = true + return +} + +func parseInt(v string) (t, rest string, ok bool) { + if v == "" { + return + } + if v[0] < '0' || '9' < v[0] { + return + } + i := 1 + for i < len(v) && '0' <= v[i] && v[i] <= '9' { + i++ + } + if v[0] == '0' && i != 1 { + return + } + return v[:i], v[i:], true +} + +func parsePrerelease(v string) (t, rest string, ok bool) { + // "A pre-release version MAY be denoted by appending a hyphen and + // a series of dot separated identifiers immediately following the patch version. + // Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. + // Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes." + if v == "" || v[0] != '-' { + return + } + i := 1 + start := 1 + for i < len(v) && v[i] != '+' { + if !isIdentChar(v[i]) && v[i] != '.' { + return + } + if v[i] == '.' { + if start == i || isBadNum(v[start:i]) { + return + } + start = i + 1 + } + i++ + } + if start == i || isBadNum(v[start:i]) { + return + } + return v[:i], v[i:], true +} + +func parseBuild(v string) (t, rest string, ok bool) { + if v == "" || v[0] != '+' { + return + } + i := 1 + start := 1 + for i < len(v) { + if !isIdentChar(v[i]) { + return + } + if v[i] == '.' { + if start == i { + return + } + start = i + 1 + } + i++ + } + if start == i { + return + } + return v[:i], v[i:], true +} + +func isIdentChar(c byte) bool { + return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' +} + +func isBadNum(v string) bool { + i := 0 + for i < len(v) && '0' <= v[i] && v[i] <= '9' { + i++ + } + return i == len(v) && i > 1 && v[0] == '0' +} + +func isNum(v string) bool { + i := 0 + for i < len(v) && '0' <= v[i] && v[i] <= '9' { + i++ + } + return i == len(v) +} + +func compareInt(x, y string) int { + if x == y { + return 0 + } + if len(x) < len(y) { + return -1 + } + if len(x) > len(y) { + return +1 + } + if x < y { + return -1 + } else { + return +1 + } +} + +func comparePrerelease(x, y string) int { + // "When major, minor, and patch are equal, a pre-release version has + // lower precedence than a normal version. + // Example: 1.0.0-alpha < 1.0.0. + // Precedence for two pre-release versions with the same major, minor, + // and patch version MUST be determined by comparing each dot separated + // identifier from left to right until a difference is found as follows: + // identifiers consisting of only digits are compared numerically and + // identifiers with letters or hyphens are compared lexically in ASCII + // sort order. Numeric identifiers always have lower precedence than + // non-numeric identifiers. A larger set of pre-release fields has a + // higher precedence than a smaller set, if all of the preceding + // identifiers are equal. + // Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < + // 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0." + if x == y { + return 0 + } + if x == "" { + return +1 + } + if y == "" { + return -1 + } + for x != "" && y != "" { + x = x[1:] // skip - or . + y = y[1:] // skip - or . + var dx, dy string + dx, x = nextIdent(x) + dy, y = nextIdent(y) + if dx != dy { + ix := isNum(dx) + iy := isNum(dy) + if ix != iy { + if ix { + return -1 + } else { + return +1 + } + } + if ix { + if len(dx) < len(dy) { + return -1 + } + if len(dx) > len(dy) { + return +1 + } + } + if dx < dy { + return -1 + } else { + return +1 + } + } + } + if x == "" { + return -1 + } else { + return +1 + } +} + +func nextIdent(x string) (dx, rest string) { + i := 0 + for i < len(x) && x[i] != '.' { + i++ + } + return x[:i], x[i:] +} diff --git a/libgo/go/cmd/go/internal/semver/semver_test.go b/libgo/go/cmd/go/internal/semver/semver_test.go new file mode 100644 index 0000000..96b64a5 --- /dev/null +++ b/libgo/go/cmd/go/internal/semver/semver_test.go @@ -0,0 +1,182 @@ +// Copyright 2018 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 semver + +import ( + "strings" + "testing" +) + +var tests = []struct { + in string + out string +}{ + {"bad", ""}, + {"v1-alpha.beta.gamma", ""}, + {"v1-pre", ""}, + {"v1+meta", ""}, + {"v1-pre+meta", ""}, + {"v1.2-pre", ""}, + {"v1.2+meta", ""}, + {"v1.2-pre+meta", ""}, + {"v1.0.0-alpha", "v1.0.0-alpha"}, + {"v1.0.0-alpha.1", "v1.0.0-alpha.1"}, + {"v1.0.0-alpha.beta", "v1.0.0-alpha.beta"}, + {"v1.0.0-beta", "v1.0.0-beta"}, + {"v1.0.0-beta.2", "v1.0.0-beta.2"}, + {"v1.0.0-beta.11", "v1.0.0-beta.11"}, + {"v1.0.0-rc.1", "v1.0.0-rc.1"}, + {"v1", "v1.0.0"}, + {"v1.0", "v1.0.0"}, + {"v1.0.0", "v1.0.0"}, + {"v1.2", "v1.2.0"}, + {"v1.2.0", "v1.2.0"}, + {"v1.2.3-456", "v1.2.3-456"}, + {"v1.2.3-456.789", "v1.2.3-456.789"}, + {"v1.2.3-456-789", "v1.2.3-456-789"}, + {"v1.2.3-456a", "v1.2.3-456a"}, + {"v1.2.3-pre", "v1.2.3-pre"}, + {"v1.2.3-pre+meta", "v1.2.3-pre"}, + {"v1.2.3-pre.1", "v1.2.3-pre.1"}, + {"v1.2.3-zzz", "v1.2.3-zzz"}, + {"v1.2.3", "v1.2.3"}, + {"v1.2.3+meta", "v1.2.3"}, + {"v1.2.3+meta-pre", "v1.2.3"}, +} + +func TestIsValid(t *testing.T) { + for _, tt := range tests { + ok := IsValid(tt.in) + if ok != (tt.out != "") { + t.Errorf("IsValid(%q) = %v, want %v", tt.in, ok, !ok) + } + } +} + +func TestCanonical(t *testing.T) { + for _, tt := range tests { + out := Canonical(tt.in) + if out != tt.out { + t.Errorf("Canonical(%q) = %q, want %q", tt.in, out, tt.out) + } + } +} + +func TestMajor(t *testing.T) { + for _, tt := range tests { + out := Major(tt.in) + want := "" + if i := strings.Index(tt.out, "."); i >= 0 { + want = tt.out[:i] + } + if out != want { + t.Errorf("Major(%q) = %q, want %q", tt.in, out, want) + } + } +} + +func TestMajorMinor(t *testing.T) { + for _, tt := range tests { + out := MajorMinor(tt.in) + var want string + if tt.out != "" { + want = tt.in + if i := strings.Index(want, "+"); i >= 0 { + want = want[:i] + } + if i := strings.Index(want, "-"); i >= 0 { + want = want[:i] + } + switch strings.Count(want, ".") { + case 0: + want += ".0" + case 1: + // ok + case 2: + want = want[:strings.LastIndex(want, ".")] + } + } + if out != want { + t.Errorf("MajorMinor(%q) = %q, want %q", tt.in, out, want) + } + } +} + +func TestPrerelease(t *testing.T) { + for _, tt := range tests { + pre := Prerelease(tt.in) + var want string + if tt.out != "" { + if i := strings.Index(tt.out, "-"); i >= 0 { + want = tt.out[i:] + } + } + if pre != want { + t.Errorf("Prerelease(%q) = %q, want %q", tt.in, pre, want) + } + } +} + +func TestBuild(t *testing.T) { + for _, tt := range tests { + build := Build(tt.in) + var want string + if tt.out != "" { + if i := strings.Index(tt.in, "+"); i >= 0 { + want = tt.in[i:] + } + } + if build != want { + t.Errorf("Build(%q) = %q, want %q", tt.in, build, want) + } + } +} + +func TestCompare(t *testing.T) { + for i, ti := range tests { + for j, tj := range tests { + cmp := Compare(ti.in, tj.in) + var want int + if ti.out == tj.out { + want = 0 + } else if i < j { + want = -1 + } else { + want = +1 + } + if cmp != want { + t.Errorf("Compare(%q, %q) = %d, want %d", ti.in, tj.in, cmp, want) + } + } + } +} + +func TestMax(t *testing.T) { + for i, ti := range tests { + for j, tj := range tests { + max := Max(ti.in, tj.in) + want := Canonical(ti.in) + if i < j { + want = Canonical(tj.in) + } + if max != want { + t.Errorf("Max(%q, %q) = %q, want %q", ti.in, tj.in, max, want) + } + } + } +} + +var ( + v1 = "v1.0.0+metadata-dash" + v2 = "v1.0.0+metadata-dash1" +) + +func BenchmarkCompare(b *testing.B) { + for i := 0; i < b.N; i++ { + if Compare(v1, v2) != 0 { + b.Fatalf("bad compare") + } + } +} diff --git a/libgo/go/cmd/go/internal/str/path.go b/libgo/go/cmd/go/internal/str/path.go index 84ca9d5..a9b4d75 100644 --- a/libgo/go/cmd/go/internal/str/path.go +++ b/libgo/go/cmd/go/internal/str/path.go @@ -9,8 +9,25 @@ import ( "strings" ) -// HasFilePathPrefix reports whether the filesystem path s begins with the -// elements in prefix. +// HasPath reports whether the slash-separated path s +// begins with the elements in prefix. +func HasPathPrefix(s, prefix string) bool { + if len(s) == len(prefix) { + return s == prefix + } + if prefix == "" { + return true + } + if len(s) > len(prefix) { + if prefix[len(prefix)-1] == '/' || s[len(prefix)] == '/' { + return s[:len(prefix)] == prefix + } + } + return false +} + +// HasFilePathPrefix reports whether the filesystem path s +// begins with the elements in prefix. func HasFilePathPrefix(s, prefix string) bool { sv := strings.ToUpper(filepath.VolumeName(s)) pv := strings.ToUpper(filepath.VolumeName(prefix)) @@ -23,8 +40,10 @@ func HasFilePathPrefix(s, prefix string) bool { return false case len(s) == len(prefix): return s == prefix + case prefix == "": + return true case len(s) > len(prefix): - if prefix != "" && prefix[len(prefix)-1] == filepath.Separator { + if prefix[len(prefix)-1] == filepath.Separator { return strings.HasPrefix(s, prefix) } return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix diff --git a/libgo/go/cmd/go/internal/test/cover.go b/libgo/go/cmd/go/internal/test/cover.go index 12538b4..9841791 100644 --- a/libgo/go/cmd/go/internal/test/cover.go +++ b/libgo/go/cmd/go/internal/test/cover.go @@ -23,7 +23,7 @@ var coverMerge struct { // Using this function clears the profile in case it existed from a previous run, // or in case it doesn't exist and the test is going to fail to create it (or not run). func initCoverProfile() { - if testCoverProfile == "" { + if testCoverProfile == "" || testC { return } if !filepath.IsAbs(testCoverProfile) && testOutputDir != "" { diff --git a/libgo/go/cmd/go/internal/test/test.go b/libgo/go/cmd/go/internal/test/test.go index 5adb7df..a089f1b 100644 --- a/libgo/go/cmd/go/internal/test/test.go +++ b/libgo/go/cmd/go/internal/test/test.go @@ -9,11 +9,7 @@ import ( "crypto/sha256" "errors" "fmt" - "go/ast" "go/build" - "go/doc" - "go/parser" - "go/token" "io" "io/ioutil" "os" @@ -25,15 +21,13 @@ import ( "strconv" "strings" "sync" - "text/template" "time" - "unicode" - "unicode/utf8" "cmd/go/internal/base" "cmd/go/internal/cache" "cmd/go/internal/cfg" "cmd/go/internal/load" + "cmd/go/internal/modload" "cmd/go/internal/str" "cmd/go/internal/work" "cmd/internal/test2json" @@ -44,7 +38,7 @@ func init() { CmdTest.Run = runTest } -const testUsage = "test [build/test flags] [packages] [build/test flags & test binary flags]" +const testUsage = "go test [build/test flags] [packages] [build/test flags & test binary flags]" var CmdTest = &base.Command{ CustomFlags: true, @@ -76,9 +70,12 @@ to hold ancillary data needed by the tests. As part of building a test binary, go test runs go vet on the package and its test source files to identify significant problems. If go vet -finds any problems, go test reports those and does not run the test binary. -Only a high-confidence subset of the default go vet checks are used. -To disable the running of go vet, use the -vet=off flag. +finds any problems, go test reports those and does not run the test +binary. Only a high-confidence subset of the default go vet checks are +used. That subset is: 'atomic', 'bool', 'buildtags', 'nilfunc', and +'printf'. You can see the documentation for these and other vet tests +via "go doc cmd/vet". To disable the running of go vet, use the +-vet=off flag. All test output and summary lines are printed to the go command's standard output, even if the test printed them to its own standard @@ -172,7 +169,7 @@ flags are also accessible by 'go test'. // Usage prints the usage message for 'go test -h' and exits. func Usage() { - os.Stderr.WriteString(testUsage + "\n\n" + + os.Stderr.WriteString("usage: " + testUsage + "\n\n" + strings.TrimSpace(testFlag1) + "\n\n\t" + strings.TrimSpace(testFlag2) + "\n") os.Exit(2) @@ -328,14 +325,13 @@ profile the tests during execution: Writes test binary as -c would. -memprofile mem.out - Write a memory profile to the file after all tests have passed. + Write an allocation profile to the file after all tests have passed. Writes test binary as -c would. -memprofilerate n - Enable more precise (and expensive) memory profiles by setting - runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'. - To profile all memory allocations, use -test.memprofilerate=1 - and pass --alloc_space flag to the pprof tool. + Enable more precise (and expensive) memory allocation profiles by + setting runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'. + To profile all memory allocations, use -test.memprofilerate=1. -mutexprofile mutex.out Write a mutex contention profile to the specified file @@ -385,6 +381,12 @@ flag not known to the go test command. Continuing the example above, the package list would have to appear before -myflag, but could appear on either side of -v. +When 'go test' runs in package list mode, 'go test' caches successful +package test results to avoid unnecessary repeated running of tests. To +disable test caching, use any test flag or argument other than the +cacheable flags. The idiomatic way to disable test caching explicitly +is to use -count=1. + To keep an argument for a test binary from being interpreted as a known flag or a package name, use -args (see 'go help test') which passes the remainder of the command line through to the test binary @@ -499,13 +501,6 @@ var ( testCacheExpire time.Time // ignore cached test results before this time ) -var testMainDeps = []string{ - // Dependencies for testmain. - "os", - "testing", - "testing/internal/testdeps", -} - // testVetFlags is the list of flags to pass to vet when invoked automatically during go test. var testVetFlags = []string{ // TODO(rsc): Decide which tests are enabled by default. @@ -533,6 +528,8 @@ var testVetFlags = []string{ } func runTest(cmd *base.Command, args []string) { + modload.LoadTests = true + pkgArgs, testArgs = testFlags(args) work.FindExecCmd() // initialize cached result @@ -597,7 +594,7 @@ func runTest(cmd *base.Command, args []string) { cfg.BuildV = testV deps := make(map[string]bool) - for _, dep := range testMainDeps { + for _, dep := range load.TestMainDeps { deps[dep] = true } @@ -656,7 +653,7 @@ func runTest(cmd *base.Command, args []string) { } // Select for coverage all dependencies matching the testCoverPaths patterns. - for _, p := range load.PackageList(pkgs) { + for _, p := range load.GetTestPackageList(pkgs) { haveMatch := false for i := range testCoverPaths { if match[i](p) { @@ -704,7 +701,7 @@ func runTest(cmd *base.Command, args []string) { coverFiles = append(coverFiles, p.GoFiles...) coverFiles = append(coverFiles, p.CgoFiles...) coverFiles = append(coverFiles, p.TestGoFiles...) - p.Internal.CoverVars = declareCoverVars(p.ImportPath, coverFiles...) + p.Internal.CoverVars = declareCoverVars(p, coverFiles...) if testCover && testCoverMode == "atomic" { ensureImport(p, "sync/atomic") } @@ -721,16 +718,13 @@ func runTest(cmd *base.Command, args []string) { buildTest, runTest, printTest, err := builderTest(&b, p) if err != nil { str := err.Error() - if strings.HasPrefix(str, "\n") { - str = str[1:] - } - failed := fmt.Sprintf("FAIL\t%s [setup failed]\n", p.ImportPath) - + str = strings.TrimPrefix(str, "\n") if p.ImportPath != "" { - base.Errorf("# %s\n%s\n%s", p.ImportPath, str, failed) + base.Errorf("# %s\n%s", p.ImportPath, str) } else { - base.Errorf("%s\n%s", str, failed) + base.Errorf("%s", str) } + fmt.Printf("FAIL\t%s [setup failed]\n", p.ImportPath) continue } builds = append(builds, buildTest) @@ -798,14 +792,20 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin } // Build Package structs describing: + // pmain - pkg.test binary // ptest - package + test files // pxtest - package of external test files - // pmain - pkg.test binary - var ptest, pxtest, pmain *load.Package - - localCover := testCover && testCoverPaths == nil - - ptest, pxtest, err = load.GetTestPackagesFor(p, localCover || p.Name == "main") + var cover *load.TestCover + if testCover { + cover = &load.TestCover{ + Mode: testCoverMode, + Local: testCover && testCoverPaths == nil, + Pkgs: testCoverPkgs, + Paths: testCoverPaths, + DeclVars: declareCoverVars, + } + } + pmain, ptest, pxtest, err := load.GetTestPackagesFor(p, cover) if err != nil { return nil, nil, nil, err } @@ -822,116 +822,18 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin } testBinary := elem + ".test" - // Should we apply coverage analysis locally, - // only for this package and only for this test? - // Yes, if -cover is on but -coverpkg has not specified - // a list of packages for global coverage. - if localCover { - ptest.Internal.CoverMode = testCoverMode - var coverFiles []string - coverFiles = append(coverFiles, ptest.GoFiles...) - coverFiles = append(coverFiles, ptest.CgoFiles...) - ptest.Internal.CoverVars = declareCoverVars(ptest.ImportPath, coverFiles...) - } - testDir := b.NewObjdir() if err := b.Mkdir(testDir); err != nil { return nil, nil, nil, err } - // Action for building pkg.test. - pmain = &load.Package{ - PackagePublic: load.PackagePublic{ - Name: "main", - Dir: testDir, - GoFiles: []string{"_testmain.go"}, - ImportPath: p.ImportPath + " (testmain)", - Root: p.Root, - }, - Internal: load.PackageInternal{ - Build: &build.Package{Name: "main"}, - OmitDebug: !testC && !testNeedBinary, - - Asmflags: p.Internal.Asmflags, - Gcflags: p.Internal.Gcflags, - Ldflags: p.Internal.Ldflags, - Gccgoflags: p.Internal.Gccgoflags, - }, - } - - // The generated main also imports testing, regexp, and os. - // Also the linker introduces implicit dependencies reported by LinkerDeps. - var stk load.ImportStack - stk.Push("testmain") - deps := testMainDeps // cap==len, so safe for append - for _, d := range load.LinkerDeps(p) { - deps = append(deps, d) - } - for _, dep := range deps { - if dep == ptest.ImportPath { - pmain.Internal.Imports = append(pmain.Internal.Imports, ptest) - } else { - p1 := load.LoadImport(dep, "", nil, &stk, nil, 0) - if p1.Error != nil { - return nil, nil, nil, p1.Error - } - pmain.Internal.Imports = append(pmain.Internal.Imports, p1) - } - } - - if testCoverPkgs != nil { - // Add imports, but avoid duplicates. - seen := map[*load.Package]bool{p: true, ptest: true} - for _, p1 := range pmain.Internal.Imports { - seen[p1] = true - } - for _, p1 := range testCoverPkgs { - if !seen[p1] { - seen[p1] = true - pmain.Internal.Imports = append(pmain.Internal.Imports, p1) - } - } - } - - // Do initial scan for metadata needed for writing _testmain.go - // Use that metadata to update the list of imports for package main. - // The list of imports is used by recompileForTest and by the loop - // afterward that gathers t.Cover information. - t, err := loadTestFuncs(ptest) - if err != nil { - return nil, nil, nil, err - } - if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 { - pmain.Internal.Imports = append(pmain.Internal.Imports, ptest) - t.ImportTest = true - } - if pxtest != nil { - pmain.Internal.Imports = append(pmain.Internal.Imports, pxtest) - t.ImportXtest = true - } - - if ptest != p { - // We have made modifications to the package p being tested - // and are rebuilding p (as ptest). - // Arrange to rebuild all packages q such that - // the test depends on q and q depends on p. - // This makes sure that q sees the modifications to p. - // Strictly speaking, the rebuild is only necessary if the - // modifications to p change its export metadata, but - // determining that is a bit tricky, so we rebuild always. - recompileForTest(pmain, p, ptest, pxtest) - } - - for _, cp := range pmain.Internal.Imports { - if len(cp.Internal.CoverVars) > 0 { - t.Cover = append(t.Cover, coverInfo{cp, cp.Internal.CoverVars}) - } - } + pmain.Dir = testDir + pmain.Internal.OmitDebug = !testC && !testNeedBinary if !cfg.BuildN { // writeTestmain writes _testmain.go, // using the test description gathered in t. - if err := writeTestmain(testDir+"_testmain.go", t); err != nil { + if err := ioutil.WriteFile(testDir+"_testmain.go", *pmain.Internal.TestmainGo, 0666); err != nil { return nil, nil, nil, err } } @@ -993,8 +895,10 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin } runAction = installAction // make sure runAction != nil even if not running test } + var vetRunAction *work.Action if testC { printAction = &work.Action{Mode: "test print (nop)", Package: p, Deps: []*work.Action{runAction}} // nop + vetRunAction = printAction } else { // run test c := new(runCache) @@ -1007,12 +911,7 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin TryCache: c.tryCache, Objdir: testDir, } - if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 { - addTestVet(b, ptest, runAction, installAction) - } - if pxtest != nil { - addTestVet(b, pxtest, runAction, installAction) - } + vetRunAction = runAction cleanAction = &work.Action{ Mode: "test clean", Func: builderCleanTest, @@ -1029,6 +928,14 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin IgnoreFail: true, // print even if test failed } } + + if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 { + addTestVet(b, ptest, vetRunAction, installAction) + } + if pxtest != nil { + addTestVet(b, pxtest, vetRunAction, installAction) + } + if installAction != nil { if runAction != installAction { installAction.Deps = append(installAction.Deps, runAction) @@ -1057,44 +964,6 @@ func addTestVet(b *work.Builder, p *load.Package, runAction, installAction *work } } -func recompileForTest(pmain, preal, ptest, pxtest *load.Package) { - // The "test copy" of preal is ptest. - // For each package that depends on preal, make a "test copy" - // that depends on ptest. And so on, up the dependency tree. - testCopy := map[*load.Package]*load.Package{preal: ptest} - for _, p := range load.PackageList([]*load.Package{pmain}) { - if p == preal { - continue - } - // Copy on write. - didSplit := p == pmain || p == pxtest - split := func() { - if didSplit { - return - } - didSplit = true - if testCopy[p] != nil { - panic("recompileForTest loop") - } - p1 := new(load.Package) - testCopy[p] = p1 - *p1 = *p - p1.Internal.Imports = make([]*load.Package, len(p.Internal.Imports)) - copy(p1.Internal.Imports, p.Internal.Imports) - p = p1 - p.Target = "" - } - - // Update p.Internal.Imports to use test copies. - for i, imp := range p.Internal.Imports { - if p1 := testCopy[imp]; p1 != nil && p1 != imp { - split() - p.Internal.Imports[i] = p1 - } - } - } -} - // isTestFile reports whether the source file is a set of tests and should therefore // be excluded from coverage analysis. func isTestFile(file string) bool { @@ -1104,7 +973,7 @@ func isTestFile(file string) bool { // declareCoverVars attaches the required cover variables names // to the files, to be used when annotating the files. -func declareCoverVars(importPath string, files ...string) map[string]*load.CoverVar { +func declareCoverVars(p *load.Package, files ...string) map[string]*load.CoverVar { coverVars := make(map[string]*load.CoverVar) coverIndex := 0 // We create the cover counters as new top-level variables in the package. @@ -1113,14 +982,25 @@ func declareCoverVars(importPath string, files ...string) map[string]*load.Cover // so we append 12 hex digits from the SHA-256 of the import path. // The point is only to avoid accidents, not to defeat users determined to // break things. - sum := sha256.Sum256([]byte(importPath)) + sum := sha256.Sum256([]byte(p.ImportPath)) h := fmt.Sprintf("%x", sum[:6]) for _, file := range files { if isTestFile(file) { continue } + // For a package that is "local" (imported via ./ import or command line, outside GOPATH), + // we record the full path to the file name. + // Otherwise we record the import path, then a forward slash, then the file name. + // This makes profiles within GOPATH file system-independent. + // These names appear in the cmd/cover HTML interface. + var longFile string + if p.Internal.Local { + longFile = filepath.Join(p.Dir, file) + } else { + longFile = path.Join(p.ImportPath, file) + } coverVars[file] = &load.CoverVar{ - File: filepath.Join(importPath, file), + File: longFile, Var: fmt.Sprintf("GoCover_%d_%x", coverIndex, h), } coverIndex++ @@ -1538,7 +1418,7 @@ func computeTestInputsID(a *work.Action, testlog []byte) (cache.ActionID, error) fmt.Fprintf(h, "env %s %x\n", name, hashGetenv(name)) case "chdir": pwd = name // always absolute - fmt.Fprintf(h, "cbdir %s %x\n", name, hashStat(name)) + fmt.Fprintf(h, "chdir %s %x\n", name, hashStat(name)) case "stat": if !filepath.IsAbs(name) { name = filepath.Join(pwd, name) @@ -1746,310 +1626,3 @@ func builderNoTest(b *work.Builder, a *work.Action) error { fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", a.Package.ImportPath) return nil } - -// isTestFunc tells whether fn has the type of a testing function. arg -// specifies the parameter type we look for: B, M or T. -func isTestFunc(fn *ast.FuncDecl, arg string) bool { - if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || - fn.Type.Params.List == nil || - len(fn.Type.Params.List) != 1 || - len(fn.Type.Params.List[0].Names) > 1 { - return false - } - ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr) - if !ok { - return false - } - // We can't easily check that the type is *testing.M - // because we don't know how testing has been imported, - // but at least check that it's *M or *something.M. - // Same applies for B and T. - if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg { - return true - } - if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg { - return true - } - return false -} - -// isTest tells whether name looks like a test (or benchmark, according to prefix). -// It is a Test (say) if there is a character after Test that is not a lower-case letter. -// We don't want TesticularCancer. -func isTest(name, prefix string) bool { - if !strings.HasPrefix(name, prefix) { - return false - } - if len(name) == len(prefix) { // "Test" is ok - return true - } - rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) - return !unicode.IsLower(rune) -} - -type coverInfo struct { - Package *load.Package - Vars map[string]*load.CoverVar -} - -// loadTestFuncs returns the testFuncs describing the tests that will be run. -func loadTestFuncs(ptest *load.Package) (*testFuncs, error) { - t := &testFuncs{ - Package: ptest, - } - for _, file := range ptest.TestGoFiles { - if err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); err != nil { - return nil, err - } - } - for _, file := range ptest.XTestGoFiles { - if err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); err != nil { - return nil, err - } - } - return t, nil -} - -// writeTestmain writes the _testmain.go file for t to the file named out. -func writeTestmain(out string, t *testFuncs) error { - f, err := os.Create(out) - if err != nil { - return err - } - defer f.Close() - - if err := testmainTmpl.Execute(f, t); err != nil { - return err - } - - return nil -} - -type testFuncs struct { - Tests []testFunc - Benchmarks []testFunc - Examples []testFunc - TestMain *testFunc - Package *load.Package - ImportTest bool - NeedTest bool - ImportXtest bool - NeedXtest bool - Cover []coverInfo -} - -func (t *testFuncs) CoverMode() string { - return testCoverMode -} - -func (t *testFuncs) CoverEnabled() bool { - return testCover -} - -// ImportPath returns the import path of the package being tested, if it is within GOPATH. -// This is printed by the testing package when running benchmarks. -func (t *testFuncs) ImportPath() string { - pkg := t.Package.ImportPath - if strings.HasPrefix(pkg, "_/") { - return "" - } - if pkg == "command-line-arguments" { - return "" - } - return pkg -} - -// Covered returns a string describing which packages are being tested for coverage. -// If the covered package is the same as the tested package, it returns the empty string. -// Otherwise it is a comma-separated human-readable list of packages beginning with -// " in", ready for use in the coverage message. -func (t *testFuncs) Covered() string { - if testCoverPaths == nil { - return "" - } - return " in " + strings.Join(testCoverPaths, ", ") -} - -// Tested returns the name of the package being tested. -func (t *testFuncs) Tested() string { - return t.Package.Name -} - -type testFunc struct { - Package string // imported package name (_test or _xtest) - Name string // function name - Output string // output, for examples - Unordered bool // output is allowed to be unordered. -} - -var testFileSet = token.NewFileSet() - -func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { - f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments) - if err != nil { - return base.ExpandScanner(err) - } - for _, d := range f.Decls { - n, ok := d.(*ast.FuncDecl) - if !ok { - continue - } - if n.Recv != nil { - continue - } - name := n.Name.String() - switch { - case name == "TestMain": - if isTestFunc(n, "T") { - t.Tests = append(t.Tests, testFunc{pkg, name, "", false}) - *doImport, *seen = true, true - continue - } - err := checkTestFunc(n, "M") - if err != nil { - return err - } - if t.TestMain != nil { - return errors.New("multiple definitions of TestMain") - } - t.TestMain = &testFunc{pkg, name, "", false} - *doImport, *seen = true, true - case isTest(name, "Test"): - err := checkTestFunc(n, "T") - if err != nil { - return err - } - t.Tests = append(t.Tests, testFunc{pkg, name, "", false}) - *doImport, *seen = true, true - case isTest(name, "Benchmark"): - err := checkTestFunc(n, "B") - if err != nil { - return err - } - t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false}) - *doImport, *seen = true, true - } - } - ex := doc.Examples(f) - sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order }) - for _, e := range ex { - *doImport = true // import test file whether executed or not - if e.Output == "" && !e.EmptyOutput { - // Don't run examples with no output. - continue - } - t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered}) - *seen = true - } - return nil -} - -func checkTestFunc(fn *ast.FuncDecl, arg string) error { - if !isTestFunc(fn, arg) { - name := fn.Name.String() - pos := testFileSet.Position(fn.Pos()) - return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg) - } - return nil -} - -var testmainTmpl = template.Must(template.New("main").Parse(` -package main - -import ( -{{if not .TestMain}} - "os" -{{end}} - "testing" - "testing/internal/testdeps" - -{{if .ImportTest}} - {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}} -{{end}} -{{if .ImportXtest}} - {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}} -{{end}} -{{range $i, $p := .Cover}} - _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}} -{{end}} -) - -var tests = []testing.InternalTest{ -{{range .Tests}} - {"{{.Name}}", {{.Package}}.{{.Name}}}, -{{end}} -} - -var benchmarks = []testing.InternalBenchmark{ -{{range .Benchmarks}} - {"{{.Name}}", {{.Package}}.{{.Name}}}, -{{end}} -} - -var examples = []testing.InternalExample{ -{{range .Examples}} - {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}}, -{{end}} -} - -func init() { - testdeps.ImportPath = {{.ImportPath | printf "%q"}} -} - -{{if .CoverEnabled}} - -// Only updated by init functions, so no need for atomicity. -var ( - coverCounters = make(map[string][]uint32) - coverBlocks = make(map[string][]testing.CoverBlock) -) - -func init() { - {{range $i, $p := .Cover}} - {{range $file, $cover := $p.Vars}} - coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:]) - {{end}} - {{end}} -} - -func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) { - if 3*len(counter) != len(pos) || len(counter) != len(numStmts) { - panic("coverage: mismatched sizes") - } - if coverCounters[fileName] != nil { - // Already registered. - return - } - coverCounters[fileName] = counter - block := make([]testing.CoverBlock, len(counter)) - for i := range counter { - block[i] = testing.CoverBlock{ - Line0: pos[3*i+0], - Col0: uint16(pos[3*i+2]), - Line1: pos[3*i+1], - Col1: uint16(pos[3*i+2]>>16), - Stmts: numStmts[i], - } - } - coverBlocks[fileName] = block -} -{{end}} - -func main() { -{{if .CoverEnabled}} - testing.RegisterCover(testing.Cover{ - Mode: {{printf "%q" .CoverMode}}, - Counters: coverCounters, - Blocks: coverBlocks, - CoveredPackages: {{printf "%q" .Covered}}, - }) -{{end}} - m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples) -{{with .TestMain}} - {{.Package}}.{{.Name}}(m) -{{else}} - os.Exit(m.Run()) -{{end}} -} - -`)) diff --git a/libgo/go/cmd/go/internal/test/testflag.go b/libgo/go/cmd/go/internal/test/testflag.go index 8a686b7..73f8c69 100644 --- a/libgo/go/cmd/go/internal/test/testflag.go +++ b/libgo/go/cmd/go/internal/test/testflag.go @@ -63,6 +63,7 @@ var testFlagDefn = []*cmdflag.Defn{ // add build flags to testFlagDefn func init() { + cmdflag.AddKnownFlags("test", testFlagDefn) var cmd base.Command work.AddBuildFlags(&cmd) cmd.Flag.VisitAll(func(f *flag.Flag) { @@ -87,6 +88,7 @@ func init() { // go test fmt -custom-flag-for-fmt-test // go test -x math func testFlags(args []string) (packageNames, passToTest []string) { + args = str.StringList(cmdflag.FindGOFLAGS(testFlagDefn), args) inPkg := false var explicitArgs []string for i := 0; i < len(args); i++ { diff --git a/libgo/go/cmd/go/internal/tool/tool.go b/libgo/go/cmd/go/internal/tool/tool.go index 4c7d089..edcf935 100644 --- a/libgo/go/cmd/go/internal/tool/tool.go +++ b/libgo/go/cmd/go/internal/tool/tool.go @@ -18,7 +18,7 @@ import ( var CmdTool = &base.Command{ Run: runTool, - UsageLine: "tool [-n] command [args...]", + UsageLine: "go tool [-n] command [args...]", Short: "run specified go tool", Long: ` Tool runs the go tool command identified by the arguments. diff --git a/libgo/go/cmd/go/internal/txtar/archive.go b/libgo/go/cmd/go/internal/txtar/archive.go new file mode 100644 index 0000000..c384f33 --- /dev/null +++ b/libgo/go/cmd/go/internal/txtar/archive.go @@ -0,0 +1,140 @@ +// Copyright 2018 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 txtar implements a trivial text-based file archive format. +// +// The goals for the format are: +// +// - be trivial enough to create and edit by hand. +// - be able to store trees of text files describing go command test cases. +// - diff nicely in git history and code reviews. +// +// Non-goals include being a completely general archive format, +// storing binary data, storing file modes, storing special files like +// symbolic links, and so on. +// +// Txtar format +// +// A txtar archive is zero or more comment lines and then a sequence of file entries. +// Each file entry begins with a file marker line of the form "-- FILENAME --" +// and is followed by zero or more file content lines making up the file data. +// The comment or file content ends at the next file marker line. +// The file marker line must begin with the three-byte sequence "-- " +// and end with the three-byte sequence " --", but the enclosed +// file name can be surrounding by additional white space, +// all of which is stripped. +// +// If the txtar file is missing a trailing newline on the final line, +// parsers should consider a final newline to be present anyway. +// +// There are no possible syntax errors in a txtar archive. +package txtar + +import ( + "bytes" + "fmt" + "io/ioutil" + "strings" +) + +// An Archive is a collection of files. +type Archive struct { + Comment []byte + Files []File +} + +// A File is a single file in an archive. +type File struct { + Name string // name of file ("foo/bar.txt") + Data []byte // text content of file +} + +// Format returns the serialized form of an Archive. +// It is assumed that the Archive data structure is well-formed: +// a.Comment and all a.File[i].Data contain no file marker lines, +// and all a.File[i].Name is non-empty. +func Format(a *Archive) []byte { + var buf bytes.Buffer + buf.Write(fixNL(a.Comment)) + for _, f := range a.Files { + fmt.Fprintf(&buf, "-- %s --\n", f.Name) + buf.Write(fixNL(f.Data)) + } + return buf.Bytes() +} + +// ParseFile parses the named file as an archive. +func ParseFile(file string) (*Archive, error) { + data, err := ioutil.ReadFile(file) + if err != nil { + return nil, err + } + return Parse(data), nil +} + +// Parse parses the serialized form of an Archive. +// The returned Archive holds slices of data. +func Parse(data []byte) *Archive { + a := new(Archive) + var name string + a.Comment, name, data = findFileMarker(data) + for name != "" { + f := File{name, nil} + f.Data, name, data = findFileMarker(data) + a.Files = append(a.Files, f) + } + return a +} + +var ( + newlineMarker = []byte("\n-- ") + marker = []byte("-- ") + markerEnd = []byte(" --") +) + +// findFileMarker finds the next file marker in data, +// extracts the file name, and returns the data before the marker, +// the file name, and the data after the marker. +// If there is no next marker, findFileMarker returns before = fixNL(data), name = "", after = nil. +func findFileMarker(data []byte) (before []byte, name string, after []byte) { + var i int + for { + if name, after = isMarker(data[i:]); name != "" { + return data[:i], name, after + } + j := bytes.Index(data[i:], newlineMarker) + if j < 0 { + return fixNL(data), "", nil + } + i += j + 1 // positioned at start of new possible marker + } +} + +// isMarker checks whether data begins with a file marker line. +// If so, it returns the name from the line and the data after the line. +// Otherwise it returns name == "" with an unspecified after. +func isMarker(data []byte) (name string, after []byte) { + if !bytes.HasPrefix(data, marker) { + return "", nil + } + if i := bytes.IndexByte(data, '\n'); i >= 0 { + data, after = data[:i], data[i+1:] + } + if !bytes.HasSuffix(data, markerEnd) { + return "", nil + } + return strings.TrimSpace(string(data[len(marker) : len(data)-len(markerEnd)])), after +} + +// If data is empty or ends in \n, fixNL returns data. +// Otherwise fixNL returns a new slice consisting of data with a final \n added. +func fixNL(data []byte) []byte { + if len(data) == 0 || data[len(data)-1] == '\n' { + return data + } + d := make([]byte, len(data)+1) + copy(d, data) + d[len(data)] = '\n' + return d +} diff --git a/libgo/go/cmd/go/internal/txtar/archive_test.go b/libgo/go/cmd/go/internal/txtar/archive_test.go new file mode 100644 index 0000000..3f734f67 --- /dev/null +++ b/libgo/go/cmd/go/internal/txtar/archive_test.go @@ -0,0 +1,67 @@ +// Copyright 2018 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 txtar + +import ( + "bytes" + "fmt" + "reflect" + "testing" +) + +var tests = []struct { + name string + text string + parsed *Archive +}{ + { + name: "basic", + text: `comment1 +comment2 +-- file1 -- +File 1 text. +-- foo --- +More file 1 text. +-- file 2 -- +File 2 text. +-- empty -- +-- noNL -- +hello world`, + parsed: &Archive{ + Comment: []byte("comment1\ncomment2\n"), + Files: []File{ + {"file1", []byte("File 1 text.\n-- foo ---\nMore file 1 text.\n")}, + {"file 2", []byte("File 2 text.\n")}, + {"empty", []byte{}}, + {"noNL", []byte("hello world\n")}, + }, + }, + }, +} + +func Test(t *testing.T) { + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := Parse([]byte(tt.text)) + if !reflect.DeepEqual(a, tt.parsed) { + t.Fatalf("Parse: wrong output:\nhave:\n%s\nwant:\n%s", shortArchive(a), shortArchive(tt.parsed)) + } + text := Format(a) + a = Parse(text) + if !reflect.DeepEqual(a, tt.parsed) { + t.Fatalf("Parse after Format: wrong output:\nhave:\n%s\nwant:\n%s", shortArchive(a), shortArchive(tt.parsed)) + } + }) + } +} + +func shortArchive(a *Archive) string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "comment: %q\n", a.Comment) + for _, f := range a.Files { + fmt.Fprintf(&buf, "file %q: %q\n", f.Name, f.Data) + } + return buf.String() +} diff --git a/libgo/go/cmd/go/internal/version/version.go b/libgo/go/cmd/go/internal/version/version.go index c3f7d73..9344a28 100644 --- a/libgo/go/cmd/go/internal/version/version.go +++ b/libgo/go/cmd/go/internal/version/version.go @@ -14,7 +14,7 @@ import ( var CmdVersion = &base.Command{ Run: runVersion, - UsageLine: "version", + UsageLine: "go version", Short: "print Go version", Long: `Version prints the Go version, as reported by runtime.Version.`, } diff --git a/libgo/go/cmd/go/internal/vet/vet.go b/libgo/go/cmd/go/internal/vet/vet.go index a737ebd..ea85fb8 100644 --- a/libgo/go/cmd/go/internal/vet/vet.go +++ b/libgo/go/cmd/go/internal/vet/vet.go @@ -8,6 +8,7 @@ package vet import ( "cmd/go/internal/base" "cmd/go/internal/load" + "cmd/go/internal/modload" "cmd/go/internal/work" "path/filepath" ) @@ -15,7 +16,7 @@ import ( var CmdVet = &base.Command{ Run: runVet, CustomFlags: true, - UsageLine: "vet [-n] [-x] [build flags] [vet flags] [packages]", + UsageLine: "go vet [-n] [-x] [build flags] [vet flags] [packages]", Short: "report likely mistakes in packages", Long: ` Vet runs the Go vet command on the packages named by the import paths. @@ -35,6 +36,8 @@ See also: go fmt, go fix. } func runVet(cmd *base.Command, args []string) { + modload.LoadTests = true + vetFlags, pkgArgs := vetFlags(args) work.BuildInit() @@ -57,7 +60,7 @@ func runVet(cmd *base.Command, args []string) { root := &work.Action{Mode: "go vet"} for _, p := range pkgs { - ptest, pxtest, err := load.GetTestPackagesFor(p, false) + _, ptest, pxtest, err := load.GetTestPackagesFor(p, nil) if err != nil { base.Errorf("%v", err) continue diff --git a/libgo/go/cmd/go/internal/vet/vetflag.go b/libgo/go/cmd/go/internal/vet/vetflag.go index 03770ea..50eac42 100644 --- a/libgo/go/cmd/go/internal/vet/vetflag.go +++ b/libgo/go/cmd/go/internal/vet/vetflag.go @@ -12,6 +12,7 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cmdflag" + "cmd/go/internal/str" "cmd/go/internal/work" ) @@ -26,39 +27,40 @@ var vetFlagDefn = []*cmdflag.Defn{ // to vet. We handle them in vetFlags. // local. - {Name: "all", BoolVar: new(bool)}, - {Name: "asmdecl", BoolVar: new(bool)}, - {Name: "assign", BoolVar: new(bool)}, - {Name: "atomic", BoolVar: new(bool)}, - {Name: "bool", BoolVar: new(bool)}, - {Name: "buildtags", BoolVar: new(bool)}, - {Name: "cgocall", BoolVar: new(bool)}, - {Name: "composites", BoolVar: new(bool)}, - {Name: "copylocks", BoolVar: new(bool)}, - {Name: "httpresponse", BoolVar: new(bool)}, - {Name: "lostcancel", BoolVar: new(bool)}, - {Name: "methods", BoolVar: new(bool)}, - {Name: "nilfunc", BoolVar: new(bool)}, - {Name: "printf", BoolVar: new(bool)}, - {Name: "printfuncs"}, - {Name: "rangeloops", BoolVar: new(bool)}, - {Name: "shadow", BoolVar: new(bool)}, - {Name: "shadowstrict", BoolVar: new(bool)}, - {Name: "shift", BoolVar: new(bool)}, - {Name: "source", BoolVar: new(bool)}, - {Name: "structtags", BoolVar: new(bool)}, - {Name: "tests", BoolVar: new(bool)}, - {Name: "unreachable", BoolVar: new(bool)}, - {Name: "unsafeptr", BoolVar: new(bool)}, - {Name: "unusedfuncs"}, - {Name: "unusedresult", BoolVar: new(bool)}, - {Name: "unusedstringmethods"}, + {Name: "all", BoolVar: new(bool), PassToTest: true}, + {Name: "asmdecl", BoolVar: new(bool), PassToTest: true}, + {Name: "assign", BoolVar: new(bool), PassToTest: true}, + {Name: "atomic", BoolVar: new(bool), PassToTest: true}, + {Name: "bool", BoolVar: new(bool), PassToTest: true}, + {Name: "buildtags", BoolVar: new(bool), PassToTest: true}, + {Name: "cgocall", BoolVar: new(bool), PassToTest: true}, + {Name: "composites", BoolVar: new(bool), PassToTest: true}, + {Name: "copylocks", BoolVar: new(bool), PassToTest: true}, + {Name: "httpresponse", BoolVar: new(bool), PassToTest: true}, + {Name: "lostcancel", BoolVar: new(bool), PassToTest: true}, + {Name: "methods", BoolVar: new(bool), PassToTest: true}, + {Name: "nilfunc", BoolVar: new(bool), PassToTest: true}, + {Name: "printf", BoolVar: new(bool), PassToTest: true}, + {Name: "printfuncs", PassToTest: true}, + {Name: "rangeloops", BoolVar: new(bool), PassToTest: true}, + {Name: "shadow", BoolVar: new(bool), PassToTest: true}, + {Name: "shadowstrict", BoolVar: new(bool), PassToTest: true}, + {Name: "shift", BoolVar: new(bool), PassToTest: true}, + {Name: "source", BoolVar: new(bool), PassToTest: true}, + {Name: "structtags", BoolVar: new(bool), PassToTest: true}, + {Name: "tests", BoolVar: new(bool), PassToTest: true}, + {Name: "unreachable", BoolVar: new(bool), PassToTest: true}, + {Name: "unsafeptr", BoolVar: new(bool), PassToTest: true}, + {Name: "unusedfuncs", PassToTest: true}, + {Name: "unusedresult", BoolVar: new(bool), PassToTest: true}, + {Name: "unusedstringmethods", PassToTest: true}, } var vetTool string // add build flags to vetFlagDefn. func init() { + cmdflag.AddKnownFlags("vet", vetFlagDefn) var cmd base.Command work.AddBuildFlags(&cmd) cmd.Flag.StringVar(&vetTool, "vettool", "", "path to vet tool binary") // for cmd/vet tests; undocumented for now @@ -73,6 +75,7 @@ func init() { // vetFlags processes the command line, splitting it at the first non-flag // into the list of flags and list of packages. func vetFlags(args []string) (passToVet, packageNames []string) { + args = str.StringList(cmdflag.FindGOFLAGS(vetFlagDefn), args) for i := 0; i < len(args); i++ { if !strings.HasPrefix(args[i], "-") { return args[:i], args[i:] @@ -88,9 +91,17 @@ func vetFlags(args []string) (passToVet, packageNames []string) { if err := f.Value.Set(value); err != nil { base.Fatalf("invalid flag argument for -%s: %v", f.Name, err) } - switch f.Name { - // Flags known to the build but not to vet, so must be dropped. - case "x", "n", "vettool", "compiler": + keep := f.PassToTest + if !keep { + // A build flag, probably one we don't want to pass to vet. + // Can whitelist. + switch f.Name { + case "tags", "v": + keep = true + } + } + if !keep { + // Flags known to the build but not to vet, so must be dropped. if extraWord { args = append(args[:i], args[i+2:]...) extraWord = false diff --git a/libgo/go/cmd/go/internal/web2/web.go b/libgo/go/cmd/go/internal/web2/web.go new file mode 100644 index 0000000..f390037 --- /dev/null +++ b/libgo/go/cmd/go/internal/web2/web.go @@ -0,0 +1,297 @@ +// Copyright 2018 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 web2 + +import ( + "bytes" + "cmd/go/internal/base" + "encoding/json" + "flag" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "runtime" + "runtime/debug" + "strings" + "sync" +) + +var TraceGET = false +var webstack = false + +func init() { + flag.BoolVar(&TraceGET, "webtrace", TraceGET, "trace GET requests") + flag.BoolVar(&webstack, "webstack", webstack, "print stack for GET requests") +} + +type netrcLine struct { + machine string + login string + password string +} + +var netrcOnce sync.Once +var netrc []netrcLine + +func parseNetrc(data string) []netrcLine { + var nrc []netrcLine + var l netrcLine + for _, line := range strings.Split(data, "\n") { + f := strings.Fields(line) + for i := 0; i < len(f)-1; i += 2 { + switch f[i] { + case "machine": + l.machine = f[i+1] + case "login": + l.login = f[i+1] + case "password": + l.password = f[i+1] + } + } + if l.machine != "" && l.login != "" && l.password != "" { + nrc = append(nrc, l) + l = netrcLine{} + } + } + return nrc +} + +func havePassword(machine string) bool { + netrcOnce.Do(readNetrc) + for _, line := range netrc { + if line.machine == machine { + return true + } + } + return false +} + +func netrcPath() string { + switch runtime.GOOS { + case "windows": + return filepath.Join(os.Getenv("USERPROFILE"), "_netrc") + case "plan9": + return filepath.Join(os.Getenv("home"), ".netrc") + default: + return filepath.Join(os.Getenv("HOME"), ".netrc") + } +} + +func readNetrc() { + data, err := ioutil.ReadFile(netrcPath()) + if err != nil { + return + } + netrc = parseNetrc(string(data)) +} + +type getState struct { + req *http.Request + resp *http.Response + body io.ReadCloser + non200ok bool +} + +type Option interface { + option(*getState) error +} + +func Non200OK() Option { + return optionFunc(func(g *getState) error { + g.non200ok = true + return nil + }) +} + +type optionFunc func(*getState) error + +func (f optionFunc) option(g *getState) error { + return f(g) +} + +func DecodeJSON(dst interface{}) Option { + return optionFunc(func(g *getState) error { + if g.resp != nil { + return json.NewDecoder(g.body).Decode(dst) + } + return nil + }) +} + +func ReadAllBody(body *[]byte) Option { + return optionFunc(func(g *getState) error { + if g.resp != nil { + var err error + *body, err = ioutil.ReadAll(g.body) + return err + } + return nil + }) +} + +func Body(body *io.ReadCloser) Option { + return optionFunc(func(g *getState) error { + if g.resp != nil { + *body = g.body + g.body = nil + } + return nil + }) +} + +func Header(hdr *http.Header) Option { + return optionFunc(func(g *getState) error { + if g.resp != nil { + *hdr = CopyHeader(g.resp.Header) + } + return nil + }) +} + +func CopyHeader(hdr http.Header) http.Header { + if hdr == nil { + return nil + } + h2 := make(http.Header) + for k, v := range hdr { + v2 := make([]string, len(v)) + copy(v2, v) + h2[k] = v2 + } + return h2 +} + +var cache struct { + mu sync.Mutex + byURL map[string]*cacheEntry +} + +type cacheEntry struct { + mu sync.Mutex + resp *http.Response + body []byte +} + +var httpDo = http.DefaultClient.Do + +func SetHTTPDoForTesting(do func(*http.Request) (*http.Response, error)) { + if do == nil { + do = http.DefaultClient.Do + } + httpDo = do +} + +func Get(url string, options ...Option) error { + if TraceGET || webstack { + println("GET", url) + if webstack { + println(string(debug.Stack())) + } + } + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return err + } + + netrcOnce.Do(readNetrc) + for _, l := range netrc { + if l.machine == req.URL.Host { + req.SetBasicAuth(l.login, l.password) + break + } + } + + g := &getState{req: req} + for _, o := range options { + if err := o.option(g); err != nil { + return err + } + } + + cache.mu.Lock() + e := cache.byURL[url] + if e == nil { + e = new(cacheEntry) + if !strings.HasPrefix(url, "file:") { + if cache.byURL == nil { + cache.byURL = make(map[string]*cacheEntry) + } + cache.byURL[url] = e + } + } + cache.mu.Unlock() + + e.mu.Lock() + if strings.HasPrefix(url, "file:") { + body, err := ioutil.ReadFile(req.URL.Path) + if err != nil { + e.mu.Unlock() + return err + } + e.body = body + e.resp = &http.Response{ + StatusCode: 200, + } + } else if e.resp == nil { + resp, err := httpDo(req) + if err != nil { + e.mu.Unlock() + return err + } + e.resp = resp + // TODO: Spool to temp file. + body, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + resp.Body = nil + if err != nil { + e.mu.Unlock() + return err + } + e.body = body + } + g.resp = e.resp + g.body = ioutil.NopCloser(bytes.NewReader(e.body)) + e.mu.Unlock() + + defer func() { + if g.body != nil { + g.body.Close() + } + }() + + if g.resp.StatusCode == 403 && req.URL.Host == "api.github.com" && !havePassword("api.github.com") { + base.Errorf("%s", githubMessage) + } + if !g.non200ok && g.resp.StatusCode != 200 { + return fmt.Errorf("unexpected status (%s): %v", url, g.resp.Status) + } + + for _, o := range options { + if err := o.option(g); err != nil { + return err + } + } + return err +} + +var githubMessage = `go: 403 response from api.github.com + +GitHub applies fairly small rate limits to unauthenticated users, and +you appear to be hitting them. To authenticate, please visit +https://github.com/settings/tokens and click "Generate New Token" to +create a Personal Access Token. The token only needs "public_repo" +scope, but you can add "repo" if you want to access private +repositories too. + +Add the token to your $HOME/.netrc (%USERPROFILE%\_netrc on Windows): + + machine api.github.com login YOU password TOKEN + +Sorry for the interruption. +` diff --git a/libgo/go/cmd/go/internal/web2/web_test.go b/libgo/go/cmd/go/internal/web2/web_test.go new file mode 100644 index 0000000..c6f6b1e --- /dev/null +++ b/libgo/go/cmd/go/internal/web2/web_test.go @@ -0,0 +1,35 @@ +// Copyright 2018 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 web2 + +import ( + "reflect" + "testing" +) + +var testNetrc = ` +machine api.github.com + login user + password pwd + +machine incomlete.host + login justlogin + +machine test.host +login user2 +password pwd2 +` + +func TestReadNetrc(t *testing.T) { + lines := parseNetrc(testNetrc) + want := []netrcLine{ + {"api.github.com", "user", "pwd"}, + {"test.host", "user2", "pwd2"}, + } + + if !reflect.DeepEqual(lines, want) { + t.Errorf("parseNetrc:\nhave %q\nwant %q", lines, want) + } +} diff --git a/libgo/go/cmd/go/internal/webtest/test.go b/libgo/go/cmd/go/internal/webtest/test.go new file mode 100644 index 0000000..94b20a3 --- /dev/null +++ b/libgo/go/cmd/go/internal/webtest/test.go @@ -0,0 +1,314 @@ +// Copyright 2018 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 webtest + +import ( + "bufio" + "bytes" + "encoding/hex" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "os" + "sort" + "strconv" + "strings" + "sync" + "unicode/utf8" + + web "cmd/go/internal/web2" +) + +var mode = flag.String("webtest", "replay", "set webtest `mode` - record, replay, bypass") + +func Hook() { + if *mode == "bypass" { + return + } + web.SetHTTPDoForTesting(Do) +} + +func Unhook() { + web.SetHTTPDoForTesting(nil) +} + +func Print() { + web.SetHTTPDoForTesting(DoPrint) +} + +var responses struct { + mu sync.Mutex + byURL map[string]*respEntry +} + +type respEntry struct { + status string + code int + hdr http.Header + body []byte +} + +func Serve(url string, status string, hdr http.Header, body []byte) { + if status == "" { + status = "200 OK" + } + code, err := strconv.Atoi(strings.Fields(status)[0]) + if err != nil { + panic("bad Serve status - " + status + " - " + err.Error()) + } + + responses.mu.Lock() + defer responses.mu.Unlock() + + if responses.byURL == nil { + responses.byURL = make(map[string]*respEntry) + } + responses.byURL[url] = &respEntry{status: status, code: code, hdr: web.CopyHeader(hdr), body: body} +} + +func Do(req *http.Request) (*http.Response, error) { + if req.Method != "GET" { + return nil, fmt.Errorf("bad method - must be GET") + } + + responses.mu.Lock() + e := responses.byURL[req.URL.String()] + responses.mu.Unlock() + + if e == nil { + if *mode == "record" { + loaded.mu.Lock() + if len(loaded.did) != 1 { + loaded.mu.Unlock() + return nil, fmt.Errorf("cannot use -webtest=record with multiple loaded response files") + } + var file string + for file = range loaded.did { + break + } + loaded.mu.Unlock() + return doSave(file, req) + } + e = &respEntry{code: 599, status: "599 unexpected request (no canned response)"} + } + resp := &http.Response{ + Status: e.status, + StatusCode: e.code, + Header: web.CopyHeader(e.hdr), + Body: ioutil.NopCloser(bytes.NewReader(e.body)), + } + return resp, nil +} + +func DoPrint(req *http.Request) (*http.Response, error) { + return doSave("", req) +} + +func doSave(file string, req *http.Request) (*http.Response, error) { + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + data, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + return nil, err + } + resp.Body = ioutil.NopCloser(bytes.NewReader(data)) + + var f *os.File + if file == "" { + f = os.Stderr + } else { + f, err = os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + log.Fatal(err) + } + defer f.Close() + } + + fmt.Fprintf(f, "GET %s\n", req.URL.String()) + fmt.Fprintf(f, "%s\n", resp.Status) + var keys []string + for k := range resp.Header { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + if k == "Set-Cookie" { + continue + } + for _, v := range resp.Header[k] { + fmt.Fprintf(f, "%s: %s\n", k, v) + } + } + fmt.Fprintf(f, "\n") + if utf8.Valid(data) && !bytes.Contains(data, []byte("\nGET")) && !isHexDump(data) { + fmt.Fprintf(f, "%s\n\n", data) + } else { + fmt.Fprintf(f, "%s\n", hex.Dump(data)) + } + return resp, err +} + +var loaded struct { + mu sync.Mutex + did map[string]bool +} + +func LoadOnce(file string) { + loaded.mu.Lock() + if loaded.did[file] { + loaded.mu.Unlock() + return + } + if loaded.did == nil { + loaded.did = make(map[string]bool) + } + loaded.did[file] = true + loaded.mu.Unlock() + + f, err := os.Open(file) + if err != nil { + log.Fatal(err) + } + defer f.Close() + + b := bufio.NewReader(f) + var ungetLine string + nextLine := func() string { + if ungetLine != "" { + l := ungetLine + ungetLine = "" + return l + } + line, err := b.ReadString('\n') + if err != nil { + if err == io.EOF { + return "" + } + log.Fatalf("%s: unexpected read error: %v", file, err) + } + return line + } + + for { + line := nextLine() + if line == "" { // EOF + break + } + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "#") || line == "" { + continue + } + if !strings.HasPrefix(line, "GET ") { + log.Fatalf("%s: malformed GET line: %s", file, line) + } + url := line[len("GET "):] + status := nextLine() + if _, err := strconv.Atoi(strings.Fields(status)[0]); err != nil { + log.Fatalf("%s: malformed status line (after GET %s): %s", file, url, status) + } + hdr := make(http.Header) + for { + kv := strings.TrimSpace(nextLine()) + if kv == "" { + break + } + i := strings.Index(kv, ":") + if i < 0 { + log.Fatalf("%s: malformed header line (after GET %s): %s", file, url, kv) + } + k, v := kv[:i], strings.TrimSpace(kv[i+1:]) + hdr[k] = append(hdr[k], v) + } + + var body []byte + Body: + for n := 0; ; n++ { + line := nextLine() + if n == 0 && isHexDump([]byte(line)) { + ungetLine = line + b, err := parseHexDump(nextLine) + if err != nil { + log.Fatalf("%s: malformed hex dump (after GET %s): %v", file, url, err) + } + body = b + break + } + if line == "" { // EOF + for i := 0; i < 2; i++ { + if len(body) > 0 && body[len(body)-1] == '\n' { + body = body[:len(body)-1] + } + } + break + } + body = append(body, line...) + for line == "\n" { + line = nextLine() + if strings.HasPrefix(line, "GET ") { + ungetLine = line + body = body[:len(body)-1] + if len(body) > 0 { + body = body[:len(body)-1] + } + break Body + } + body = append(body, line...) + } + } + + Serve(url, status, hdr, body) + } +} + +func isHexDump(data []byte) bool { + return bytes.HasPrefix(data, []byte("00000000 ")) || bytes.HasPrefix(data, []byte("0000000 ")) +} + +// parseHexDump parses the hex dump in text, which should be the +// output of "hexdump -C" or Plan 9's "xd -b" or Go's hex.Dump +// and returns the original data used to produce the dump. +// It is meant to enable storing golden binary files as text, so that +// changes to the golden files can be seen during code reviews. +func parseHexDump(nextLine func() string) ([]byte, error) { + var out []byte + for { + line := nextLine() + if line == "" || line == "\n" { + break + } + if i := strings.Index(line, "|"); i >= 0 { // remove text dump + line = line[:i] + } + f := strings.Fields(line) + if len(f) > 1+16 { + return nil, fmt.Errorf("parsing hex dump: too many fields on line %q", line) + } + if len(f) == 0 || len(f) == 1 && f[0] == "*" { // all zeros block omitted + continue + } + addr64, err := strconv.ParseUint(f[0], 16, 0) + if err != nil { + return nil, fmt.Errorf("parsing hex dump: invalid address %q", f[0]) + } + addr := int(addr64) + if len(out) < addr { + out = append(out, make([]byte, addr-len(out))...) + } + for _, x := range f[1:] { + val, err := strconv.ParseUint(x, 16, 8) + if err != nil { + return nil, fmt.Errorf("parsing hexdump: invalid hex byte %q", x) + } + out = append(out, byte(val)) + } + } + return out, nil +} diff --git a/libgo/go/cmd/go/internal/work/action.go b/libgo/go/cmd/go/internal/work/action.go index 803a6bd..82cf228 100644 --- a/libgo/go/cmd/go/internal/work/action.go +++ b/libgo/go/cmd/go/internal/work/action.go @@ -37,7 +37,10 @@ type Builder struct { flagCache map[[2]string]bool // a cache of supported compiler flags Print func(args ...interface{}) (int, error) - ComputeStaleOnly bool // compute staleness for go list; no actual build + IsCmdList bool // running as part of go list; set p.Stale and additional fields below + NeedError bool // list needs p.Error + NeedExport bool // list needs p.Export + NeedCompiledGoFiles bool // list needs p.CompiledGoFIles objdirSeq int // counter for NewObjdir pkgSeq int @@ -80,9 +83,10 @@ type Action struct { actionID cache.ActionID // cache ID of action input buildID string // build ID of action output - needVet bool // Mode=="build": need to fill in vet config - vetCfg *vetConfig // vet config - output []byte // output redirect buffer (nil means use b.Print) + VetxOnly bool // Mode=="vet": only being called to supply info about dependencies + needVet bool // Mode=="build": need to fill in vet config + vetCfg *vetConfig // vet config + output []byte // output redirect buffer (nil means use b.Print) // Execution state. pending int // number of deps yet to complete @@ -139,6 +143,7 @@ type actionJSON struct { Priority int `json:",omitempty"` Failed bool `json:",omitempty"` Built string `json:",omitempty"` + VetxOnly bool `json:",omitempty"` } // cacheKey is the key for the action cache. @@ -178,6 +183,7 @@ func actionGraphJSON(a *Action) string { Failed: a.Failed, Priority: a.priority, Built: a.built, + VetxOnly: a.VetxOnly, } if a.Package != nil { // TODO(rsc): Make this a unique key for a.Package somehow. @@ -208,7 +214,6 @@ const ( ) func (b *Builder) Init() { - var err error b.Print = func(a ...interface{}) (int, error) { return fmt.Fprint(os.Stderr, a...) } @@ -220,10 +225,19 @@ func (b *Builder) Init() { if cfg.BuildN { b.WorkDir = "$WORK" } else { - b.WorkDir, err = ioutil.TempDir(os.Getenv("GOTMPDIR"), "go-build") + tmp, err := ioutil.TempDir(os.Getenv("GOTMPDIR"), "go-build") if err != nil { - base.Fatalf("%s", err) + base.Fatalf("go: creating work dir: %v", err) } + if !filepath.IsAbs(tmp) { + abs, err := filepath.Abs(tmp) + if err != nil { + os.RemoveAll(tmp) + base.Fatalf("go: creating work dir: %v", err) + } + tmp = abs + } + b.WorkDir = tmp if cfg.BuildX || cfg.BuildWork { fmt.Fprintf(os.Stderr, "WORK=%s\n", b.WorkDir) } @@ -322,8 +336,8 @@ func (b *Builder) AutoAction(mode, depMode BuildMode, p *load.Package) *Action { // depMode is the action (build or install) to use when building dependencies. // To turn package main into an executable, call b.Link instead. func (b *Builder) CompileAction(mode, depMode BuildMode, p *load.Package) *Action { - if mode != ModeBuild && p.Internal.Local && p.Target == "" { - // Imported via local path. No permanent target. + if mode != ModeBuild && (p.Internal.Local || p.Module != nil) && p.Target == "" { + // Imported via local path or using modules. No permanent target. mode = ModeBuild } if mode != ModeBuild && p.Name == "main" { @@ -340,8 +354,10 @@ func (b *Builder) CompileAction(mode, depMode BuildMode, p *load.Package) *Actio Objdir: b.NewObjdir(), } - for _, p1 := range p.Internal.Imports { - a.Deps = append(a.Deps, b.CompileAction(depMode, depMode, p1)) + if p.Error == nil || !p.Error.IsImportCycle { + for _, p1 := range p.Internal.Imports { + a.Deps = append(a.Deps, b.CompileAction(depMode, depMode, p1)) + } } if p.Standard { @@ -379,6 +395,12 @@ func (b *Builder) CompileAction(mode, depMode BuildMode, p *load.Package) *Actio // If the caller may be causing p to be installed, it is up to the caller // to make sure that the install depends on (runs after) vet. func (b *Builder) VetAction(mode, depMode BuildMode, p *load.Package) *Action { + a := b.vetAction(mode, depMode, p) + a.VetxOnly = false + return a +} + +func (b *Builder) vetAction(mode, depMode BuildMode, p *load.Package) *Action { // Construct vet action. a := b.cacheAction("vet", p, func() *Action { a1 := b.CompileAction(mode, depMode, p) @@ -390,19 +412,34 @@ func (b *Builder) VetAction(mode, depMode BuildMode, p *load.Package) *Action { stk.Pop() aFmt := b.CompileAction(ModeBuild, depMode, p1) + var deps []*Action + if a1.buggyInstall { + // (*Builder).vet expects deps[0] to be the package + // and deps[1] to be "fmt". If we see buggyInstall + // here then a1 is an install of a shared library, + // and the real package is a1.Deps[0]. + deps = []*Action{a1.Deps[0], aFmt, a1} + } else { + deps = []*Action{a1, aFmt} + } + for _, p1 := range load.PackageList(p.Internal.Imports) { + deps = append(deps, b.vetAction(mode, depMode, p1)) + } + a := &Action{ - Mode: "vet", - Package: p, - Deps: []*Action{a1, aFmt}, - Objdir: a1.Objdir, + Mode: "vet", + Package: p, + Deps: deps, + Objdir: a1.Objdir, + VetxOnly: true, + IgnoreFail: true, // it's OK if vet of dependencies "fails" (reports problems) } if a1.Func == nil { // Built-in packages like unsafe. return a } - a1.needVet = true + deps[0].needVet = true a.Func = (*Builder).vet - return a }) return a diff --git a/libgo/go/cmd/go/internal/work/build.go b/libgo/go/cmd/go/internal/work/build.go index 25dfe58..ed41ce5 100644 --- a/libgo/go/cmd/go/internal/work/build.go +++ b/libgo/go/cmd/go/internal/work/build.go @@ -18,10 +18,11 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/load" + "cmd/go/internal/search" ) var CmdBuild = &base.Command{ - UsageLine: "build [-o output] [-i] [build flags] [packages]", + UsageLine: "go build [-o output] [-i] [build flags] [packages]", Short: "compile packages and dependencies", Long: ` Build compiles the packages named by the import paths, @@ -65,7 +66,7 @@ and test commands: Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64. -msan enable interoperation with memory sanitizer. - Supported only on linux/amd64, + Supported only on linux/amd64, linux/arm64 and only with Clang/LLVM as the host C compiler. -v print the names of packages as they are compiled. @@ -97,6 +98,9 @@ and test commands: -linkshared link against shared libraries previously created with -buildmode=shared. + -mod mode + module download mode to use: readonly, release, or vendor. + See 'go help modules' for more. -pkgdir dir install and load all packages from dir instead of the usual locations. For example, when building with a non-standard configuration, @@ -217,6 +221,7 @@ func AddBuildFlags(cmd *base.Command) { cmd.Flag.StringVar(&cfg.BuildBuildmode, "buildmode", "default", "") cmd.Flag.Var(&load.BuildGcflags, "gcflags", "") cmd.Flag.Var(&load.BuildGccgoflags, "gccgoflags", "") + cmd.Flag.StringVar(&cfg.BuildMod, "mod", "", "") cmd.Flag.StringVar(&cfg.BuildContext.InstallSuffix, "installsuffix", "", "") cmd.Flag.Var(&load.BuildLdflags, "ldflags", "") cmd.Flag.BoolVar(&cfg.BuildLinkshared, "linkshared", false, "") @@ -284,11 +289,6 @@ func runBuild(cmd *base.Command, args []string) { cfg.BuildO += cfg.ExeSuffix } - // Special case -o /dev/null by not writing at all. - if cfg.BuildO == os.DevNull { - cfg.BuildO = "" - } - // sanity check some often mis-used options switch cfg.BuildContext.Compiler { case "gccgo": @@ -309,7 +309,12 @@ func runBuild(cmd *base.Command, args []string) { depMode = ModeInstall } - pkgs = pkgsFilter(load.Packages(args)) + pkgs = omitTestOnly(pkgsFilter(load.Packages(args))) + + // Special case -o /dev/null by not writing at all. + if cfg.BuildO == os.DevNull { + cfg.BuildO = "" + } if cfg.BuildO != "" { if len(pkgs) > 1 { @@ -337,7 +342,7 @@ func runBuild(cmd *base.Command, args []string) { } var CmdInstall = &base.Command{ - UsageLine: "install [-i] [build flags] [packages]", + UsageLine: "go install [-i] [build flags] [packages]", Short: "compile and install packages and dependencies", Long: ` Install compiles and installs the packages named by the import paths. @@ -376,7 +381,7 @@ func libname(args []string, pkgs []*load.Package) (string, error) { } var haveNonMeta bool for _, arg := range args { - if load.IsMetaPackage(arg) { + if search.IsMetaPackage(arg) { appendName(arg) } else { haveNonMeta = true @@ -409,19 +414,44 @@ func libname(args []string, pkgs []*load.Package) (string, error) { func runInstall(cmd *base.Command, args []string) { BuildInit() - InstallPackages(args, false) + InstallPackages(args, load.PackagesForBuild(args)) } -func InstallPackages(args []string, forGet bool) { +// omitTestOnly returns pkgs with test-only packages removed. +func omitTestOnly(pkgs []*load.Package) []*load.Package { + var list []*load.Package + for _, p := range pkgs { + if len(p.GoFiles)+len(p.CgoFiles) == 0 && !p.Internal.CmdlinePkgLiteral { + // Package has no source files, + // perhaps due to build tags or perhaps due to only having *_test.go files. + // Also, it is only being processed as the result of a wildcard match + // like ./..., not because it was listed as a literal path on the command line. + // Ignore it. + continue + } + list = append(list, p) + } + return list +} + +func InstallPackages(patterns []string, pkgs []*load.Package) { if cfg.GOBIN != "" && !filepath.IsAbs(cfg.GOBIN) { base.Fatalf("cannot install, GOBIN must be an absolute path") } - pkgs := pkgsFilter(load.PackagesForBuild(args)) - + pkgs = omitTestOnly(pkgsFilter(pkgs)) for _, p := range pkgs { - if p.Target == "" && (!p.Standard || p.ImportPath != "unsafe") { + if p.Target == "" { switch { + case p.Standard && p.ImportPath == "unsafe": + // unsafe is a built-in package, has no target + case p.Name != "main" && p.Internal.Local && p.ConflictDir == "": + // Non-executables outside GOPATH need not have a target: + // we can use the cache to hold the built package archive for use in future builds. + // The ones inside GOPATH should have a target (in GOPATH/pkg) + // or else something is wrong and worth reporting (like a ConflictDir). + case p.Name != "main" && p.Module != nil: + // Non-executables have no target (except the cache) when building with modules. case p.Internal.GobinSubdir: base.Errorf("go %s: cannot install cross-compiled binaries when GOBIN is set", cfg.CmdName) case p.Internal.CmdlineFiles: @@ -445,11 +475,6 @@ func InstallPackages(args []string, forGet bool) { a := &Action{Mode: "go install"} var tools []*Action for _, p := range pkgs { - // During 'go get', don't attempt (and fail) to install packages with only tests. - // TODO(rsc): It's not clear why 'go get' should be different from 'go install' here. See #20760. - if forGet && len(p.GoFiles)+len(p.CgoFiles) == 0 && len(p.TestGoFiles)+len(p.XTestGoFiles) > 0 { - continue - } // If p is a tool, delay the installation until the end of the build. // This avoids installing assemblers/compilers that are being executed // by other steps in the build. @@ -475,7 +500,7 @@ func InstallPackages(args []string, forGet bool) { // tools above did not apply, and a is just a simple Action // with a list of Deps, one per package named in pkgs, // the same as in runBuild. - a = b.buildmodeShared(ModeInstall, ModeInstall, args, pkgs, a) + a = b.buildmodeShared(ModeInstall, ModeInstall, patterns, pkgs, a) } b.Do(a) @@ -490,7 +515,7 @@ func InstallPackages(args []string, forGet bool) { // One way to view this behavior is that it is as if 'go install' first // runs 'go build' and the moves the generated file to the install dir. // See issue 9645. - if len(args) == 0 && len(pkgs) == 1 && pkgs[0].Name == "main" { + if len(patterns) == 0 && len(pkgs) == 1 && pkgs[0].Name == "main" { // Compute file 'go build' would have created. // If it exists and is an executable file, remove it. _, targ := filepath.Split(pkgs[0].ImportPath) diff --git a/libgo/go/cmd/go/internal/work/build_test.go b/libgo/go/cmd/go/internal/work/build_test.go index 3f5ba37..010e17e 100644 --- a/libgo/go/cmd/go/internal/work/build_test.go +++ b/libgo/go/cmd/go/internal/work/build_test.go @@ -42,15 +42,60 @@ func TestSplitPkgConfigOutput(t *testing.T) { }{ {[]byte(`-r:foo -L/usr/white\ space/lib -lfoo\ bar -lbar\ baz`), []string{"-r:foo", "-L/usr/white space/lib", "-lfoo bar", "-lbar baz"}}, {[]byte(`-lextra\ fun\ arg\\`), []string{`-lextra fun arg\`}}, - {[]byte(`broken flag\`), []string{"broken", "flag"}}, {[]byte("\textra whitespace\r\n"), []string{"extra", "whitespace"}}, {[]byte(" \r\n "), nil}, + {[]byte(`"-r:foo" "-L/usr/white space/lib" "-lfoo bar" "-lbar baz"`), []string{"-r:foo", "-L/usr/white space/lib", "-lfoo bar", "-lbar baz"}}, + {[]byte(`"-lextra fun arg\\"`), []string{`-lextra fun arg\`}}, + {[]byte(`" \r\n\ "`), []string{` \r\n\ `}}, + {[]byte(`""`), nil}, + {[]byte(``), nil}, + {[]byte(`"\\"`), []string{`\`}}, + {[]byte(`"\x"`), []string{`\x`}}, + {[]byte(`"\\x"`), []string{`\x`}}, + {[]byte(`'\\'`), []string{`\`}}, + {[]byte(`'\x'`), []string{`\x`}}, + {[]byte(`"\\x"`), []string{`\x`}}, + {[]byte(`-fPIC -I/test/include/foo -DQUOTED='"/test/share/doc"'`), []string{"-fPIC", "-I/test/include/foo", `-DQUOTED="/test/share/doc"`}}, + {[]byte(`-fPIC -I/test/include/foo -DQUOTED="/test/share/doc"`), []string{"-fPIC", "-I/test/include/foo", "-DQUOTED=/test/share/doc"}}, + {[]byte(`-fPIC -I/test/include/foo -DQUOTED=\"/test/share/doc\"`), []string{"-fPIC", "-I/test/include/foo", `-DQUOTED="/test/share/doc"`}}, + {[]byte(`-fPIC -I/test/include/foo -DQUOTED='/test/share/doc'`), []string{"-fPIC", "-I/test/include/foo", "-DQUOTED=/test/share/doc"}}, + {[]byte(`-DQUOTED='/te\st/share/d\oc'`), []string{`-DQUOTED=/te\st/share/d\oc`}}, + {[]byte(`-Dhello=10 -Dworld=+32 -DDEFINED_FROM_PKG_CONFIG=hello\ world`), []string{"-Dhello=10", "-Dworld=+32", "-DDEFINED_FROM_PKG_CONFIG=hello world"}}, + {[]byte(`"broken\"" \\\a "a"`), []string{"broken\"", "\\a", "a"}}, } { - got := splitPkgConfigOutput(test.in) + got, err := splitPkgConfigOutput(test.in) + if err != nil { + t.Errorf("splitPkgConfigOutput on %v failed with error %v", test.in, err) + continue + } + if !reflect.DeepEqual(got, test.want) { + t.Errorf("splitPkgConfigOutput(%v) = %v; want %v", test.in, got, test.want) + } + } + + for _, test := range []struct { + in []byte + want []string + }{ + // broken quotation + {[]byte(`" \r\n `), nil}, + {[]byte(`"-r:foo" "-L/usr/white space/lib "-lfoo bar" "-lbar baz"`), nil}, + {[]byte(`"-lextra fun arg\\`), nil}, + // broken char escaping + {[]byte(`broken flag\`), nil}, + {[]byte(`extra broken flag \`), nil}, + {[]byte(`\`), nil}, + {[]byte(`"broken\"" "extra" \`), nil}, + } { + got, err := splitPkgConfigOutput(test.in) + if err == nil { + t.Errorf("splitPkgConfigOutput(%v) = %v; haven't failed with error as expected.", test.in, got) + } if !reflect.DeepEqual(got, test.want) { t.Errorf("splitPkgConfigOutput(%v) = %v; want %v", test.in, got, test.want) } } + } func TestSharedLibName(t *testing.T) { @@ -220,7 +265,7 @@ func TestRespectSetgidDir(t *testing.T) { defer pkgfile.Close() dirGIDFile := filepath.Join(setgiddir, "setgid") - if err := b.moveOrCopyFile(nil, dirGIDFile, pkgfile.Name(), 0666, true); err != nil { + if err := b.moveOrCopyFile(dirGIDFile, pkgfile.Name(), 0666, true); err != nil { t.Fatalf("moveOrCopyFile: %v", err) } diff --git a/libgo/go/cmd/go/internal/work/buildid.go b/libgo/go/cmd/go/internal/work/buildid.go index c7c07ed..fb57e91 100644 --- a/libgo/go/cmd/go/internal/work/buildid.go +++ b/libgo/go/cmd/go/internal/work/buildid.go @@ -18,6 +18,7 @@ import ( "cmd/go/internal/load" "cmd/go/internal/str" "cmd/internal/buildid" + "cmd/internal/objabi" ) // Build IDs @@ -174,20 +175,29 @@ func (b *Builder) toolID(name string) string { return id } - cmdline := str.StringList(cfg.BuildToolexec, base.Tool(name), "-V=full") + path := base.Tool(name) + desc := "go tool " + name + + // Special case: undocumented -vettool overrides usual vet, for testing vet. + if name == "vet" && VetTool != "" { + path = VetTool + desc = VetTool + } + + cmdline := str.StringList(cfg.BuildToolexec, path, "-V=full") cmd := exec.Command(cmdline[0], cmdline[1:]...) cmd.Env = base.EnvForDir(cmd.Dir, os.Environ()) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr if err := cmd.Run(); err != nil { - base.Fatalf("go tool %s: %v\n%s%s", name, err, stdout.Bytes(), stderr.Bytes()) + base.Fatalf("%s: %v\n%s%s", desc, err, stdout.Bytes(), stderr.Bytes()) } line := stdout.String() f := strings.Fields(line) - if len(f) < 3 || f[0] != name || f[1] != "version" || f[2] == "devel" && !strings.HasPrefix(f[len(f)-1], "buildID=") { - base.Fatalf("go tool %s -V=full: unexpected output:\n\t%s", name, line) + if len(f) < 3 || f[0] != name && path != VetTool || f[1] != "version" || f[2] == "devel" && !strings.HasPrefix(f[len(f)-1], "buildID=") { + base.Fatalf("%s -V=full: unexpected output:\n\t%s", desc, line) } if f[2] == "devel" { // On the development branch, use the content ID part of the build ID. @@ -197,6 +207,11 @@ func (b *Builder) toolID(name string) string { id = f[2] } + // For the compiler, add any experiments. + if name == "compile" { + id += " " + objabi.Expstring() + } + b.id.Lock() b.toolIDCache[name] = id b.id.Unlock() @@ -301,11 +316,7 @@ func assemblerIsGas() bool { if err == nil { cmd := exec.Command(strings.TrimSpace(string(assembler)), "--version") out, err := cmd.Output() - if err == nil && strings.Contains(string(out), "GNU") { - return true - } else { - return false - } + return err == nil && strings.Contains(string(out), "GNU") } else { return false } @@ -480,7 +491,7 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID // already up-to-date, then to avoid a rebuild, report the package // as up-to-date as well. See "Build IDs" comment above. // TODO(rsc): Rewrite this code to use a TryCache func on the link action. - if target != "" && !cfg.BuildA && a.Mode == "build" && len(a.triggers) == 1 && a.triggers[0].Mode == "link" { + if target != "" && !cfg.BuildA && !b.NeedExport && a.Mode == "build" && len(a.triggers) == 1 && a.triggers[0].Mode == "link" { buildID, err := buildid.ReadFile(target) if err == nil { id := strings.Split(buildID, buildIDSeparator) @@ -499,6 +510,14 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID a.buildID = id[1] + buildIDSeparator + id[2] linkID := hashToString(b.linkActionID(a.triggers[0])) if id[0] == linkID { + // Best effort attempt to display output from the compile and link steps. + // If it doesn't work, it doesn't work: reusing the cached binary is more + // important than reprinting diagnostic information. + if c := cache.Default(); c != nil { + showStdout(b, c, a.actionID, "stdout") // compile output + showStdout(b, c, a.actionID, "link-stdout") // link output + } + // Poison a.Target to catch uses later in the build. a.Target = "DO NOT USE - main build pseudo-cache Target" a.built = "DO NOT USE - main build pseudo-cache built" @@ -516,13 +535,22 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID // We avoid the nested build ID problem in the previous special case // by recording the test results in the cache under the action ID half. if !cfg.BuildA && len(a.triggers) == 1 && a.triggers[0].TryCache != nil && a.triggers[0].TryCache(b, a.triggers[0]) { + // Best effort attempt to display output from the compile and link steps. + // If it doesn't work, it doesn't work: reusing the test result is more + // important than reprinting diagnostic information. + if c := cache.Default(); c != nil { + showStdout(b, c, a.Deps[0].actionID, "stdout") // compile output + showStdout(b, c, a.Deps[0].actionID, "link-stdout") // link output + } + + // Poison a.Target to catch uses later in the build. a.Target = "DO NOT USE - pseudo-cache Target" a.built = "DO NOT USE - pseudo-cache built" return true } - if b.ComputeStaleOnly { - // Invoked during go list only to compute and record staleness. + if b.IsCmdList { + // Invoked during go list to compute and record staleness. if p := a.Package; p != nil && !p.Stale { p.Stale = true if cfg.BuildA { @@ -554,22 +582,9 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID // but we're still happy to use results from the build artifact cache. if c := cache.Default(); c != nil { if !cfg.BuildA { - entry, err := c.Get(actionHash) - if err == nil { - file := c.OutputFile(entry.OutputID) - info, err1 := os.Stat(file) - buildID, err2 := buildid.ReadFile(file) - if err1 == nil && err2 == nil && info.Size() == entry.Size { - stdout, stdoutEntry, err := c.GetBytes(cache.Subkey(a.actionID, "stdout")) - if err == nil { - if len(stdout) > 0 { - if cfg.BuildX || cfg.BuildN { - b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList("cat", c.OutputFile(stdoutEntry.OutputID)))) - } - if !cfg.BuildN { - b.Print(string(stdout)) - } - } + if file, _, err := c.GetFile(actionHash); err == nil { + if buildID, err := buildid.ReadFile(file); err == nil { + if err := showStdout(b, c, a.actionID, "stdout"); err == nil { a.built = file a.Target = "DO NOT USE - using cache" a.buildID = buildID @@ -587,11 +602,24 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID a.output = []byte{} } - if b.ComputeStaleOnly { - return true + return false +} + +func showStdout(b *Builder, c *cache.Cache, actionID cache.ActionID, key string) error { + stdout, stdoutEntry, err := c.GetBytes(cache.Subkey(actionID, key)) + if err != nil { + return err } - return false + if len(stdout) > 0 { + if cfg.BuildX || cfg.BuildN { + b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList("cat", c.OutputFile(stdoutEntry.OutputID)))) + } + if !cfg.BuildN { + b.Print(string(stdout)) + } + } + return nil } // flushOutput flushes the output being queued in a. @@ -618,6 +646,26 @@ func (b *Builder) updateBuildID(a *Action, target string, rewrite bool) error { } } + // Cache output from compile/link, even if we don't do the rest. + if c := cache.Default(); c != nil { + switch a.Mode { + case "build": + c.PutBytes(cache.Subkey(a.actionID, "stdout"), a.output) + case "link": + // Even though we don't cache the binary, cache the linker text output. + // We might notice that an installed binary is up-to-date but still + // want to pretend to have run the linker. + // Store it under the main package's action ID + // to make it easier to find when that's all we have. + for _, a1 := range a.Deps { + if p1 := a1.Package; p1 != nil && p1.Name == "main" { + c.PutBytes(cache.Subkey(a1.actionID, "link-stdout"), a.output) + break + } + } + } + } + // Find occurrences of old ID and compute new content-based ID. r, err := os.Open(target) if err != nil { @@ -675,11 +723,16 @@ func (b *Builder) updateBuildID(a *Action, target string, rewrite bool) error { panic("internal error: a.output not set") } outputID, _, err := c.Put(a.actionID, r) + r.Close() if err == nil && cfg.BuildX { b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList("cp", target, c.OutputFile(outputID)))) } - c.PutBytes(cache.Subkey(a.actionID, "stdout"), a.output) - r.Close() + if b.NeedExport { + if err != nil { + return err + } + a.Package.Export = c.OutputFile(outputID) + } } } diff --git a/libgo/go/cmd/go/internal/work/exec.go b/libgo/go/cmd/go/internal/work/exec.go index 5994dbc..84870e5 100644 --- a/libgo/go/cmd/go/internal/work/exec.go +++ b/libgo/go/cmd/go/internal/work/exec.go @@ -14,6 +14,7 @@ import ( "io" "io/ioutil" "log" + "math/rand" "os" "os/exec" "path/filepath" @@ -53,7 +54,7 @@ func actionList(root *Action) []*Action { // do runs the action graph rooted at root. func (b *Builder) Do(root *Action) { - if c := cache.Default(); c != nil && !b.ComputeStaleOnly { + if c := cache.Default(); c != nil && !b.IsCmdList { // If we're doing real work, take time at the end to trim the cache. defer c.Trim() } @@ -195,10 +196,13 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { fmt.Fprintf(h, "goos %s goarch %s\n", cfg.Goos, cfg.Goarch) fmt.Fprintf(h, "import %q\n", p.ImportPath) fmt.Fprintf(h, "omitdebug %v standard %v local %v prefix %q\n", p.Internal.OmitDebug, p.Standard, p.Internal.Local, p.Internal.LocalPrefix) + if p.Internal.ForceLibrary { + fmt.Fprintf(h, "forcelibrary\n") + } if len(p.CgoFiles)+len(p.SwigFiles) > 0 { fmt.Fprintf(h, "cgo %q\n", b.toolID("cgo")) - cppflags, cflags, cxxflags, fflags, _, _ := b.CFlags(p) - fmt.Fprintf(h, "CC=%q %q %q\n", b.ccExe(), cppflags, cflags) + cppflags, cflags, cxxflags, fflags, ldflags, _ := b.CFlags(p) + fmt.Fprintf(h, "CC=%q %q %q %q\n", b.ccExe(), cppflags, cflags, ldflags) if len(p.CXXFiles)+len(p.SwigFiles) > 0 { fmt.Fprintf(h, "CXX=%q %q\n", b.cxxExe(), cxxflags) } @@ -295,40 +299,93 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { return h.Sum() } +// needCgoHdr reports whether the actions triggered by this one +// expect to be able to access the cgo-generated header file. +func (b *Builder) needCgoHdr(a *Action) bool { + // If this build triggers a header install, run cgo to get the header. + if !b.IsCmdList && (a.Package.UsesCgo() || a.Package.UsesSwig()) && (cfg.BuildBuildmode == "c-archive" || cfg.BuildBuildmode == "c-shared") { + for _, t1 := range a.triggers { + if t1.Mode == "install header" { + return true + } + } + for _, t1 := range a.triggers { + for _, t2 := range t1.triggers { + if t2.Mode == "install header" { + return true + } + } + } + } + return false +} + +// allowedVersion reports whether the version v is an allowed version of go +// (one that we can compile). +// v is known to be of the form "1.23". +func allowedVersion(v string) bool { + // Special case: no requirement. + if v == "" { + return true + } + // Special case "1.0" means "go1", which is OK. + if v == "1.0" { + return true + } + // Otherwise look through release tags of form "go1.23" for one that matches. + for _, tag := range cfg.BuildContext.ReleaseTags { + if strings.HasPrefix(tag, "go") && tag[2:] == v { + return true + } + } + return false +} + +const ( + needBuild uint32 = 1 << iota + needCgoHdr + needVet + needCompiledGoFiles + needStale +) + // build is the action for building a single package. // Note that any new influence on this logic must be reported in b.buildActionID above as well. func (b *Builder) build(a *Action) (err error) { p := a.Package + + bit := func(x uint32, b bool) uint32 { + if b { + return x + } + return 0 + } + cached := false + need := bit(needBuild, !b.IsCmdList || b.NeedExport) | + bit(needCgoHdr, b.needCgoHdr(a)) | + bit(needVet, a.needVet) | + bit(needCompiledGoFiles, b.NeedCompiledGoFiles) + if !p.BinaryOnly { if b.useCache(a, p, b.buildActionID(a), p.Target) { - // If this build triggers a header install, run cgo to get the header. - // TODO(rsc): Once we can cache multiple file outputs from an action, - // the header should be cached, and then this awful test can be deleted. - // Need to look for install header actions depending on this action, - // or depending on a link that depends on this action. - needHeader := false - if (a.Package.UsesCgo() || a.Package.UsesSwig()) && (cfg.BuildBuildmode == "c-archive" || cfg.BuildBuildmode == "c-shared") { - for _, t1 := range a.triggers { - if t1.Mode == "install header" { - needHeader = true - goto CheckedHeader - } - } - for _, t1 := range a.triggers { - for _, t2 := range t1.triggers { - if t2.Mode == "install header" { - needHeader = true - goto CheckedHeader - } - } - } + // We found the main output in the cache. + // If we don't need any other outputs, we can stop. + need &^= needBuild + if b.NeedExport { + p.Export = a.built } - CheckedHeader: - if b.ComputeStaleOnly || !a.needVet && !needHeader { - return nil + if need&needCompiledGoFiles != 0 && b.loadCachedGoFiles(a) { + need &^= needCompiledGoFiles } + // Otherwise, we need to write files to a.Objdir (needVet, needCgoHdr). + // Remember that we might have them in cache + // and check again after we create a.Objdir. cached = true + a.output = []byte{} // start saving output in case we miss any cache results + } + if need == 0 { + return nil } defer b.flushOutput(a) } @@ -337,6 +394,9 @@ func (b *Builder) build(a *Action) (err error) { if err != nil && err != errPrintedOutput { err = fmt.Errorf("go build %s: %v", a.Package.ImportPath, err) } + if err != nil && b.IsCmdList && b.NeedError && p.Error == nil { + p.Error = &load.PackageError{Err: err.Error()} + } }() if cfg.BuildN { // In -n mode, print a banner between packages. @@ -356,17 +416,24 @@ func (b *Builder) build(a *Action) (err error) { if err == nil { a.built = a.Package.Target a.Target = a.Package.Target + if b.NeedExport { + a.Package.Export = a.Package.Target + } a.buildID = b.fileHash(a.Package.Target) a.Package.Stale = false a.Package.StaleReason = "binary-only package" return nil } - if b.ComputeStaleOnly { - a.Package.Stale = true - a.Package.StaleReason = "missing or invalid binary-only package" + a.Package.Stale = true + a.Package.StaleReason = "missing or invalid binary-only package" + if b.IsCmdList { return nil } - return fmt.Errorf("missing or invalid binary-only package") + return fmt.Errorf("missing or invalid binary-only package; expected file %q", a.Package.Target) + } + + if p.Module != nil && !allowedVersion(p.Module.GoVersion) { + return fmt.Errorf("module requires Go %s", p.Module.GoVersion) } if err := b.Mkdir(a.Objdir); err != nil { @@ -374,6 +441,23 @@ func (b *Builder) build(a *Action) (err error) { } objdir := a.Objdir + if cached { + if need&needCgoHdr != 0 && b.loadCachedCgoHdr(a) { + need &^= needCgoHdr + } + + // Load cached vet config, but only if that's all we have left + // (need == needVet, not testing just the one bit). + // If we are going to do a full build anyway, + // we're going to regenerate the files below anyway. + if need == needVet && b.loadCachedVet(a) { + need &^= needVet + } + if need == 0 { + return nil + } + } + // make target directory dir, _ := filepath.Split(a.Target) if dir != "" { @@ -382,13 +466,12 @@ func (b *Builder) build(a *Action) (err error) { } } - var gofiles, cgofiles, cfiles, sfiles, cxxfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string - - gofiles = append(gofiles, a.Package.GoFiles...) - cgofiles = append(cgofiles, a.Package.CgoFiles...) - cfiles = append(cfiles, a.Package.CFiles...) - sfiles = append(sfiles, a.Package.SFiles...) - cxxfiles = append(cxxfiles, a.Package.CXXFiles...) + gofiles := str.StringList(a.Package.GoFiles) + cgofiles := str.StringList(a.Package.CgoFiles) + cfiles := str.StringList(a.Package.CFiles) + sfiles := str.StringList(a.Package.SFiles) + cxxfiles := str.StringList(a.Package.CXXFiles) + var objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string if a.Package.UsesCgo() || a.Package.UsesSwig() { if pcCFLAGS, pcLDFLAGS, err = b.getPkgConfigFlags(a.Package); err != nil { @@ -432,7 +515,7 @@ func (b *Builder) build(a *Action) (err error) { // Not covering this file. continue } - if err := b.cover(a, coverFile, sourceFile, 0666, cover.Var); err != nil { + if err := b.cover(a, coverFile, sourceFile, cover.Var); err != nil { return err } if i < len(gofiles) { @@ -488,10 +571,16 @@ func (b *Builder) build(a *Action) (err error) { } cgoObjects = append(cgoObjects, outObj...) gofiles = append(gofiles, outGo...) + + switch cfg.BuildBuildmode { + case "c-archive", "c-shared": + b.cacheCgoHdr(a) + } } - if cached && !a.needVet { - return nil - } + b.cacheGofiles(a, gofiles) + + // Running cgo generated the cgo header. + need &^= needCgoHdr // Sanity check only, since Package.load already checked as well. if len(gofiles) == 0 { @@ -499,26 +588,19 @@ func (b *Builder) build(a *Action) (err error) { } // Prepare Go vet config if needed. - var vcfg *vetConfig - if a.needVet { - // Pass list of absolute paths to vet, - // so that vet's error messages will use absolute paths, - // so that we can reformat them relative to the directory - // in which the go command is invoked. - vcfg = &vetConfig{ - Compiler: cfg.BuildToolchainName, - Dir: a.Package.Dir, - GoFiles: mkAbsFiles(a.Package.Dir, gofiles), - ImportPath: a.Package.ImportPath, - ImportMap: make(map[string]string), - PackageFile: make(map[string]string), - Standard: make(map[string]bool), - } - a.vetCfg = vcfg - for i, raw := range a.Package.Internal.RawImports { - final := a.Package.Imports[i] - vcfg.ImportMap[raw] = final + if need&needVet != 0 { + buildVetConfig(a, gofiles) + need &^= needVet + } + if need&needCompiledGoFiles != 0 { + if !b.loadCachedGoFiles(a) { + return fmt.Errorf("failed to cache compiled Go files") } + need &^= needCompiledGoFiles + } + if need == 0 { + // Nothing left to do. + return nil } // Prepare Go import config. @@ -529,24 +611,12 @@ func (b *Builder) build(a *Action) (err error) { // except when it doesn't. var icfg bytes.Buffer fmt.Fprintf(&icfg, "# import config\n") - for i, raw := range a.Package.Internal.RawImports { final := a.Package.Imports[i] if final != raw { fmt.Fprintf(&icfg, "importmap %s=%s\n", raw, final) } } - - // Compute the list of mapped imports in the vet config - // so that we can add any missing mappings below. - var vcfgMapped map[string]bool - if vcfg != nil { - vcfgMapped = make(map[string]bool) - for _, p := range vcfg.ImportMap { - vcfgMapped[p] = true - } - } - for _, a1 := range a.Deps { p1 := a1.Package if p1 == nil || p1.ImportPath == "" { @@ -555,33 +625,20 @@ func (b *Builder) build(a *Action) (err error) { if a1.built != "" { fmt.Fprintf(&icfg, "packagefile %s=%s\n", p1.ImportPath, a1.built) } - if vcfg != nil { - // Add import mapping if needed - // (for imports like "runtime/cgo" that appear only in generated code). - if !vcfgMapped[p1.ImportPath] { - vcfg.ImportMap[p1.ImportPath] = p1.ImportPath - } - if a1.built != "" { - vcfg.PackageFile[p1.ImportPath] = a1.built - } - if p1.Standard { - vcfg.Standard[p1.ImportPath] = true - } - } } - if cached { - // The cached package file is OK, so we don't need to run the compile. - // We've only going through the motions to prepare the vet configuration, - // which is now complete. - return nil + if p.Internal.BuildInfo != "" && cfg.ModulesEnabled { + if err := b.writeFile(objdir+"_gomod_.go", load.ModInfoProg(p.Internal.BuildInfo)); err != nil { + return err + } + gofiles = append(gofiles, objdir+"_gomod_.go") } // Compile Go. objpkg := objdir + "_pkg_.a" ofile, out, err := BuildToolchain.gc(b, a, objpkg, icfg.Bytes(), len(sfiles) > 0, gofiles) if len(out) > 0 { - b.showOutput(a, a.Package.Dir, a.Package.ImportPath, b.processOutput(out)) + b.showOutput(a, a.Package.Dir, a.Package.Desc(), b.processOutput(out)) if err != nil { return errPrintedOutput } @@ -604,17 +661,17 @@ func (b *Builder) build(a *Action) (err error) { switch { case strings.HasSuffix(name, _goos_goarch): targ := file[:len(name)-len(_goos_goarch)] + "_GOOS_GOARCH." + ext - if err := b.copyFile(a, objdir+targ, filepath.Join(a.Package.Dir, file), 0666, true); err != nil { + if err := b.copyFile(objdir+targ, filepath.Join(a.Package.Dir, file), 0666, true); err != nil { return err } case strings.HasSuffix(name, _goarch): targ := file[:len(name)-len(_goarch)] + "_GOARCH." + ext - if err := b.copyFile(a, objdir+targ, filepath.Join(a.Package.Dir, file), 0666, true); err != nil { + if err := b.copyFile(objdir+targ, filepath.Join(a.Package.Dir, file), 0666, true); err != nil { return err } case strings.HasSuffix(name, _goos): targ := file[:len(name)-len(_goos)] + "_GOOS." + ext - if err := b.copyFile(a, objdir+targ, filepath.Join(a.Package.Dir, file), 0666, true); err != nil { + if err := b.copyFile(objdir+targ, filepath.Join(a.Package.Dir, file), 0666, true); err != nil { return err } } @@ -638,7 +695,7 @@ func (b *Builder) build(a *Action) (err error) { } // For gccgo on ELF systems, we write the build ID as an assembler file. - // This lets us set the the SHF_EXCLUDE flag. + // This lets us set the SHF_EXCLUDE flag. // This is read by readGccgoArchive in cmd/internal/buildid/buildid.go. if a.buildID != "" && cfg.BuildToolchainName == "gccgo" { switch cfg.Goos { @@ -695,16 +752,189 @@ func (b *Builder) build(a *Action) (err error) { return nil } +func (b *Builder) cacheObjdirFile(a *Action, c *cache.Cache, name string) error { + f, err := os.Open(a.Objdir + name) + if err != nil { + return err + } + defer f.Close() + _, _, err = c.Put(cache.Subkey(a.actionID, name), f) + return err +} + +func (b *Builder) findCachedObjdirFile(a *Action, c *cache.Cache, name string) (string, error) { + file, _, err := c.GetFile(cache.Subkey(a.actionID, name)) + if err != nil { + return "", err + } + return file, nil +} + +func (b *Builder) loadCachedObjdirFile(a *Action, c *cache.Cache, name string) error { + cached, err := b.findCachedObjdirFile(a, c, name) + if err != nil { + return err + } + return b.copyFile(a.Objdir+name, cached, 0666, true) +} + +func (b *Builder) cacheCgoHdr(a *Action) { + c := cache.Default() + if c == nil { + return + } + b.cacheObjdirFile(a, c, "_cgo_install.h") +} + +func (b *Builder) loadCachedCgoHdr(a *Action) bool { + c := cache.Default() + if c == nil { + return false + } + err := b.loadCachedObjdirFile(a, c, "_cgo_install.h") + return err == nil +} + +func (b *Builder) cacheGofiles(a *Action, gofiles []string) { + c := cache.Default() + if c == nil { + return + } + var buf bytes.Buffer + for _, file := range gofiles { + if !strings.HasPrefix(file, a.Objdir) { + // not generated + buf.WriteString("./") + buf.WriteString(file) + buf.WriteString("\n") + continue + } + name := file[len(a.Objdir):] + buf.WriteString(name) + buf.WriteString("\n") + if err := b.cacheObjdirFile(a, c, name); err != nil { + return + } + } + c.PutBytes(cache.Subkey(a.actionID, "gofiles"), buf.Bytes()) +} + +func (b *Builder) loadCachedVet(a *Action) bool { + c := cache.Default() + if c == nil { + return false + } + list, _, err := c.GetBytes(cache.Subkey(a.actionID, "gofiles")) + if err != nil { + return false + } + var gofiles []string + for _, name := range strings.Split(string(list), "\n") { + if name == "" { // end of list + continue + } + if strings.HasPrefix(name, "./") { + gofiles = append(gofiles, name[2:]) + continue + } + if err := b.loadCachedObjdirFile(a, c, name); err != nil { + return false + } + gofiles = append(gofiles, a.Objdir+name) + } + buildVetConfig(a, gofiles) + return true +} + +func (b *Builder) loadCachedGoFiles(a *Action) bool { + c := cache.Default() + if c == nil { + return false + } + list, _, err := c.GetBytes(cache.Subkey(a.actionID, "gofiles")) + if err != nil { + return false + } + var files []string + for _, name := range strings.Split(string(list), "\n") { + if name == "" { // end of list + continue + } + if strings.HasPrefix(name, "./") { + files = append(files, name[len("./"):]) + continue + } + file, err := b.findCachedObjdirFile(a, c, name) + if err != nil { + return false + } + files = append(files, file) + } + a.Package.CompiledGoFiles = files + return true +} + +// vetConfig is the configuration passed to vet describing a single package. type vetConfig struct { - Compiler string - Dir string - GoFiles []string - ImportMap map[string]string - PackageFile map[string]string - Standard map[string]bool - ImportPath string + Compiler string // compiler name (gc, gccgo) + Dir string // directory containing package + ImportPath string // canonical import path ("package path") + GoFiles []string // absolute paths to package source files + + ImportMap map[string]string // map import path in source code to package path + PackageFile map[string]string // map package path to .a file with export data + Standard map[string]bool // map package path to whether it's in the standard library + PackageVetx map[string]string // map package path to vetx data from earlier vet run + VetxOnly bool // only compute vetx data; don't report detected problems + VetxOutput string // write vetx data to this output file + + SucceedOnTypecheckFailure bool // awful hack; see #18395 and below +} + +func buildVetConfig(a *Action, gofiles []string) { + // Pass list of absolute paths to vet, + // so that vet's error messages will use absolute paths, + // so that we can reformat them relative to the directory + // in which the go command is invoked. + vcfg := &vetConfig{ + Compiler: cfg.BuildToolchainName, + Dir: a.Package.Dir, + GoFiles: mkAbsFiles(a.Package.Dir, gofiles), + ImportPath: a.Package.ImportPath, + ImportMap: make(map[string]string), + PackageFile: make(map[string]string), + Standard: make(map[string]bool), + } + a.vetCfg = vcfg + for i, raw := range a.Package.Internal.RawImports { + final := a.Package.Imports[i] + vcfg.ImportMap[raw] = final + } - SucceedOnTypecheckFailure bool + // Compute the list of mapped imports in the vet config + // so that we can add any missing mappings below. + vcfgMapped := make(map[string]bool) + for _, p := range vcfg.ImportMap { + vcfgMapped[p] = true + } + + for _, a1 := range a.Deps { + p1 := a1.Package + if p1 == nil || p1.ImportPath == "" { + continue + } + // Add import mapping if needed + // (for imports like "runtime/cgo" that appear only in generated code). + if !vcfgMapped[p1.ImportPath] { + vcfg.ImportMap[p1.ImportPath] = p1.ImportPath + } + if a1.built != "" { + vcfg.PackageFile[p1.ImportPath] = a1.built + } + if p1.Standard { + vcfg.Standard[p1.ImportPath] = true + } + } } // VetTool is the path to an alternate vet tool binary. @@ -719,13 +949,51 @@ func (b *Builder) vet(a *Action) error { // a.Deps[0] is the build of the package being vetted. // a.Deps[1] is the build of the "fmt" package. + a.Failed = false // vet of dependency may have failed but we can still succeed + + if a.Deps[0].Failed { + // The build of the package has failed. Skip vet check. + // Vet could return export data for non-typecheck errors, + // but we ignore it because the package cannot be compiled. + return nil + } + vcfg := a.Deps[0].vetCfg if vcfg == nil { // Vet config should only be missing if the build failed. - if !a.Deps[0].Failed { - return fmt.Errorf("vet config not found") + return fmt.Errorf("vet config not found") + } + + vcfg.VetxOnly = a.VetxOnly + vcfg.VetxOutput = a.Objdir + "vet.out" + vcfg.PackageVetx = make(map[string]string) + + h := cache.NewHash("vet " + a.Package.ImportPath) + fmt.Fprintf(h, "vet %q\n", b.toolID("vet")) + + // Note: We could decide that vet should compute export data for + // all analyses, in which case we don't need to include the flags here. + // But that would mean that if an analysis causes problems like + // unexpected crashes there would be no way to turn it off. + // It seems better to let the flags disable export analysis too. + fmt.Fprintf(h, "vetflags %q\n", VetFlags) + + fmt.Fprintf(h, "pkg %q\n", a.Deps[0].actionID) + for _, a1 := range a.Deps { + if a1.Mode == "vet" && a1.built != "" { + fmt.Fprintf(h, "vetout %q %s\n", a1.Package.ImportPath, b.fileHash(a1.built)) + vcfg.PackageVetx[a1.Package.ImportPath] = a1.built + } + } + key := cache.ActionID(h.Sum()) + + if vcfg.VetxOnly { + if c := cache.Default(); c != nil && !cfg.BuildA { + if file, _, err := c.GetFile(key); err == nil { + a.built = file + return nil + } } - return nil } if vcfg.ImportMap["fmt"] == "" { @@ -742,7 +1010,9 @@ func (b *Builder) vet(a *Action) error { // so at least for now assume the bug is in vet. // We know of at least #18395. // TODO(rsc,gri): Try to remove this for Go 1.11. - vcfg.SucceedOnTypecheckFailure = cfg.CmdName == "test" + // + // Disabled 2018-04-20. Let's see if we can do without it. + // vcfg.SucceedOnTypecheckFailure = cfg.CmdName == "test" js, err := json.MarshalIndent(vcfg, "", "\t") if err != nil { @@ -753,7 +1023,7 @@ func (b *Builder) vet(a *Action) error { return err } - var env []string + env := b.cCompilerEnv() if cfg.BuildToolchainName == "gccgo" { env = append(env, "GCCGO="+BuildToolchain.compiler()) } @@ -763,7 +1033,18 @@ func (b *Builder) vet(a *Action) error { if tool == "" { tool = base.Tool("vet") } - return b.run(a, p.Dir, p.ImportPath, env, cfg.BuildToolexec, tool, VetFlags, a.Objdir+"vet.cfg") + runErr := b.run(a, p.Dir, p.ImportPath, env, cfg.BuildToolexec, tool, VetFlags, a.Objdir+"vet.cfg") + + // If vet wrote export data, save it for input to future vets. + if f, err := os.Open(vcfg.VetxOutput); err == nil { + a.built = vcfg.VetxOutput + if c := cache.Default(); c != nil { + c.Put(key, f) + } + f.Close() + } + + return runErr } // linkActionID computes the action ID for a link action. @@ -849,7 +1130,7 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) { // link is the action for linking a single command. // Note that any new influence on this logic must be reported in b.linkActionID above as well. func (b *Builder) link(a *Action) (err error) { - if b.useCache(a, a.Package, b.linkActionID(a), a.Package.Target) { + if b.useCache(a, a.Package, b.linkActionID(a), a.Package.Target) || b.IsCmdList { return nil } defer b.flushOutput(a) @@ -886,11 +1167,11 @@ func (b *Builder) link(a *Action) (err error) { // We still call updateBuildID to update a.buildID, which is important // for test result caching, but passing rewrite=false (final arg) // means we don't actually rewrite the binary, nor store the - // result into the cache. - // Not calling updateBuildID means we also don't insert these - // binaries into the build object cache. That's probably a net win: + // result into the cache. That's probably a net win: // less cache space wasted on large binaries we are not likely to // need again. (On the other hand it does make repeated go test slower.) + // It also makes repeated go run slower, which is a win in itself: + // we don't want people to treat go run like a scripting environment. if err := b.updateBuildID(a, a.Target, !a.Package.Internal.OmitDebug); err != nil { return err } @@ -922,36 +1203,64 @@ func (b *Builder) PkgconfigCmd() string { } // splitPkgConfigOutput parses the pkg-config output into a slice of -// flags. pkg-config always uses \ to escape special characters. -func splitPkgConfigOutput(out []byte) []string { +// flags. This implements the algorithm from pkgconf/libpkgconf/argvsplit.c. +func splitPkgConfigOutput(out []byte) ([]string, error) { if len(out) == 0 { - return nil + return nil, nil } var flags []string - flag := make([]byte, len(out)) - r, w := 0, 0 - for r < len(out) { - switch out[r] { - case ' ', '\t', '\r', '\n': - if w > 0 { - flags = append(flags, string(flag[:w])) + flag := make([]byte, 0, len(out)) + escaped := false + quote := byte(0) + + for _, c := range out { + if escaped { + if quote != 0 { + switch c { + case '$', '`', '"', '\\': + default: + flag = append(flag, '\\') + } + flag = append(flag, c) + } else { + flag = append(flag, c) } - w = 0 - case '\\': - r++ - fallthrough - default: - if r < len(out) { - flag[w] = out[r] - w++ + escaped = false + } else if quote != 0 { + if c == quote { + quote = 0 + } else { + switch c { + case '\\': + escaped = true + default: + flag = append(flag, c) + } + } + } else if strings.IndexByte(" \t\n\v\f\r", c) < 0 { + switch c { + case '\\': + escaped = true + case '\'', '"': + quote = c + default: + flag = append(flag, c) } + } else if len(flag) != 0 { + flags = append(flags, string(flag)) + flag = flag[:0] } - r++ } - if w > 0 { - flags = append(flags, string(flag[:w])) + if escaped { + return nil, errors.New("broken character escaping in pkgconf output ") + } + if quote != 0 { + return nil, errors.New("unterminated quoted string in pkgconf output ") + } else if len(flag) != 0 { + flags = append(flags, string(flag)) } - return flags + + return flags, nil } // Calls pkg-config if needed and returns the cflags/ldflags needed to build the package. @@ -976,21 +1285,24 @@ func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string, } } var out []byte - out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--cflags", pcflags, "--", pkgs) + out, err = b.runOut(p.Dir, nil, b.PkgconfigCmd(), "--cflags", pcflags, "--", pkgs) if err != nil { - b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --cflags "+strings.Join(pcflags, " ")+strings.Join(pkgs, " "), string(out)) + b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --cflags "+strings.Join(pcflags, " ")+" -- "+strings.Join(pkgs, " "), string(out)) b.Print(err.Error() + "\n") return nil, nil, errPrintedOutput } if len(out) > 0 { - cflags = splitPkgConfigOutput(out) + cflags, err = splitPkgConfigOutput(out) + if err != nil { + return nil, nil, err + } if err := checkCompilerFlags("CFLAGS", "pkg-config --cflags", cflags); err != nil { return nil, nil, err } } - out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--libs", pcflags, "--", pkgs) + out, err = b.runOut(p.Dir, nil, b.PkgconfigCmd(), "--libs", pcflags, "--", pkgs) if err != nil { - b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --libs "+strings.Join(pcflags, " ")+strings.Join(pkgs, " "), string(out)) + b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --libs "+strings.Join(pcflags, " ")+" -- "+strings.Join(pkgs, " "), string(out)) b.Print(err.Error() + "\n") return nil, nil, errPrintedOutput } @@ -1051,7 +1363,7 @@ func (b *Builder) linkSharedActionID(a *Action) cache.ActionID { } func (b *Builder) linkShared(a *Action) (err error) { - if b.useCache(a, nil, b.linkSharedActionID(a), a.Target) { + if b.useCache(a, nil, b.linkSharedActionID(a), a.Target) || b.IsCmdList { return nil } defer b.flushOutput(a) @@ -1096,7 +1408,9 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) { // so the built target is not in the a1.Objdir tree that b.cleanup(a1) removes. if a1.built == a.Target { a.built = a.Target - b.cleanup(a1) + if !a.buggyInstall { + b.cleanup(a1) + } // Whether we're smart enough to avoid a complete rebuild // depends on exactly what the staleness and rebuild algorithms // are, as well as potentially the state of the Go build cache. @@ -1115,13 +1429,17 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) { // We want to hide that awful detail as much as possible, so don't // advertise it by touching the mtimes (usually the libraries are up // to date). - if !a.buggyInstall && !b.ComputeStaleOnly { + if !a.buggyInstall && !b.IsCmdList { now := time.Now() os.Chtimes(a.Target, now, now) } return nil } - if b.ComputeStaleOnly { + + // If we're building for go list -export, + // never install anything; just keep the cache reference. + if b.IsCmdList { + a.built = a1.built return nil } @@ -1146,9 +1464,11 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) { } } - defer b.cleanup(a1) + if !a.buggyInstall { + defer b.cleanup(a1) + } - return b.moveOrCopyFile(a, a.Target, a1.built, perm, false) + return b.moveOrCopyFile(a.Target, a1.built, perm, false) } // cleanup removes a's object dir to keep the amount of @@ -1158,14 +1478,18 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) { func (b *Builder) cleanup(a *Action) { if !cfg.BuildWork { if cfg.BuildX { - b.Showcmd("", "rm -r %s", a.Objdir) + // Don't say we are removing the directory if + // we never created it. + if _, err := os.Stat(a.Objdir); err == nil || cfg.BuildN { + b.Showcmd("", "rm -r %s", a.Objdir) + } } os.RemoveAll(a.Objdir) } } // moveOrCopyFile is like 'mv src dst' or 'cp src dst'. -func (b *Builder) moveOrCopyFile(a *Action, dst, src string, perm os.FileMode, force bool) error { +func (b *Builder) moveOrCopyFile(dst, src string, perm os.FileMode, force bool) error { if cfg.BuildN { b.Showcmd("", "mv %s %s", src, dst) return nil @@ -1176,7 +1500,7 @@ func (b *Builder) moveOrCopyFile(a *Action, dst, src string, perm os.FileMode, f // If the source is in the build cache, we need to copy it. if strings.HasPrefix(src, cache.DefaultDir()) { - return b.copyFile(a, dst, src, perm, force) + return b.copyFile(dst, src, perm, force) } // On Windows, always copy the file, so that we respect the NTFS @@ -1184,7 +1508,7 @@ func (b *Builder) moveOrCopyFile(a *Action, dst, src string, perm os.FileMode, f // What matters here is not cfg.Goos (the system we are building // for) but runtime.GOOS (the system we are building on). if runtime.GOOS == "windows" { - return b.copyFile(a, dst, src, perm, force) + return b.copyFile(dst, src, perm, force) } // If the destination directory has the group sticky bit set, @@ -1192,7 +1516,7 @@ func (b *Builder) moveOrCopyFile(a *Action, dst, src string, perm os.FileMode, f // https://golang.org/issue/18878 if fi, err := os.Stat(filepath.Dir(dst)); err == nil { if fi.IsDir() && (fi.Mode()&os.ModeSetgid) != 0 { - return b.copyFile(a, dst, src, perm, force) + return b.copyFile(dst, src, perm, force) } } @@ -1222,11 +1546,11 @@ func (b *Builder) moveOrCopyFile(a *Action, dst, src string, perm os.FileMode, f } } - return b.copyFile(a, dst, src, perm, force) + return b.copyFile(dst, src, perm, force) } // copyFile is like 'cp src dst'. -func (b *Builder) copyFile(a *Action, dst, src string, perm os.FileMode, force bool) error { +func (b *Builder) copyFile(dst, src string, perm os.FileMode, force bool) error { if cfg.BuildN || cfg.BuildX { b.Showcmd("", "cp %s %s", src, dst) if cfg.BuildN { @@ -1317,12 +1641,12 @@ func (b *Builder) installHeader(a *Action) error { } } - return b.moveOrCopyFile(a, a.Target, src, 0666, true) + return b.moveOrCopyFile(a.Target, src, 0666, true) } // cover runs, in effect, // go tool cover -mode=b.coverMode -var="varName" -o dst.go src.go -func (b *Builder) cover(a *Action, dst, src string, perm os.FileMode, varName string) error { +func (b *Builder) cover(a *Action, dst, src string, varName string) error { return b.run(a, a.Objdir, "cover "+a.Package.ImportPath, nil, cfg.BuildToolexec, base.Tool("cover"), @@ -1344,6 +1668,7 @@ var objectMagic = [][]byte{ {0x00, 0x00, 0x01, 0xEB}, // Plan 9 i386 {0x00, 0x00, 0x8a, 0x97}, // Plan 9 amd64 {0x00, 0x00, 0x06, 0x47}, // Plan 9 arm + {0x00, 0x61, 0x73, 0x6D}, // WASM {0x01, 0xDF}, // XCOFF32 {0x01, 0xF7}, // XCOFF64 } @@ -1389,11 +1714,11 @@ func mayberemovefile(s string) { func (b *Builder) fmtcmd(dir string, format string, args ...interface{}) string { cmd := fmt.Sprintf(format, args...) if dir != "" && dir != "/" { - to := "." + dot := " ." if dir[len(dir)-1] == filepath.Separator { - to += string(filepath.Separator) + dot += string(filepath.Separator) } - cmd = strings.Replace(" "+cmd, " "+dir, " "+to, -1)[1:] + cmd = strings.Replace(" "+cmd, " "+dir, dot, -1)[1:] if b.scriptDir != dir { b.scriptDir = dir cmd = "cd " + dir + "\n" + cmd @@ -1472,7 +1797,7 @@ var cgoTypeSigRe = regexp.MustCompile(`\b_C2?(type|func|var|macro)_\B`) // If the command fails, run prints information about the failure // and returns a non-nil error. func (b *Builder) run(a *Action, dir string, desc string, env []string, cmdargs ...interface{}) error { - out, err := b.runOut(dir, desc, env, cmdargs...) + out, err := b.runOut(dir, env, cmdargs...) if len(out) > 0 { if desc == "" { desc = b.fmtcmd(dir, "%s", strings.Join(str.StringList(cmdargs...), " ")) @@ -1504,7 +1829,7 @@ func (b *Builder) processOutput(out []byte) string { // runOut runs the command given by cmdline in the directory dir. // It returns the command output and any errors that occurred. -func (b *Builder) runOut(dir string, desc string, env []string, cmdargs ...interface{}) ([]byte, error) { +func (b *Builder) runOut(dir string, env []string, cmdargs ...interface{}) ([]byte, error) { cmdline := str.StringList(cmdargs...) for _, arg := range cmdline { @@ -1540,6 +1865,8 @@ func (b *Builder) runOut(dir string, desc string, env []string, cmdargs ...inter cmd := exec.Command(cmdline[0], cmdline[1:]...) cmd.Stdout = &buf cmd.Stderr = &buf + cleanup := passLongArgsInResponseFiles(cmd) + defer cleanup() cmd.Dir = dir cmd.Env = base.MergeEnvLists(env, base.EnvForDir(cmd.Dir, os.Environ())) err := cmd.Run() @@ -1576,6 +1903,13 @@ func joinUnambiguously(a []string) string { return buf.String() } +// cCompilerEnv returns environment variables to set when running the +// C compiler. This is needed to disable escape codes in clang error +// messages that confuse tools like cgo. +func (b *Builder) cCompilerEnv() []string { + return []string{"TERM=dumb"} +} + // mkdir makes the named directory. func (b *Builder) Mkdir(dir string) error { // Make Mkdir(a.Objdir) a no-op instead of an error when a.Objdir == "". @@ -1723,7 +2057,7 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s if !filepath.IsAbs(outfile) { outfile = filepath.Join(p.Dir, outfile) } - output, err := b.runOut(filepath.Dir(file), desc, nil, compiler, flags, "-o", outfile, "-c", filepath.Base(file)) + output, err := b.runOut(filepath.Dir(file), b.cCompilerEnv(), compiler, flags, "-o", outfile, "-c", filepath.Base(file)) if len(output) > 0 { // On FreeBSD 11, when we pass -g to clang 3.8 it // invokes its internal assembler with -dwarf-version=2. @@ -1763,7 +2097,7 @@ func (b *Builder) gccld(p *load.Package, objdir, out string, flags []string, obj } else { cmd = b.GccCmd(p.Dir, objdir) } - return b.run(nil, p.Dir, p.ImportPath, nil, cmd, "-o", out, objs, flags) + return b.run(nil, p.Dir, p.ImportPath, b.cCompilerEnv(), cmd, "-o", out, objs, flags) } // Grab these before main helpfully overwrites them. @@ -2068,7 +2402,7 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo // along to the host linker. At this point in the code, cgoLDFLAGS // consists of the original $CGO_LDFLAGS (unchecked) and all the // flags put together from source code (checked). - var cgoenv []string + cgoenv := b.cCompilerEnv() if len(cgoLDFLAGS) > 0 { flags := make([]string, len(cgoLDFLAGS)) for i, f := range cgoLDFLAGS { @@ -2195,7 +2529,15 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe // we need to use -pie for Linux/ARM to get accurate imported sym ldflags := cgoLDFLAGS if (cfg.Goarch == "arm" && cfg.Goos == "linux") || cfg.Goos == "android" { - ldflags = append(ldflags, "-pie") + // -static -pie doesn't make sense, and causes link errors. + // Issue 26197. + n := make([]string, 0, len(ldflags)) + for _, flag := range ldflags { + if flag != "-static" { + n = append(n, flag) + } + } + ldflags = append(n, "-pie") } if err := b.gccld(p, objdir, dynobj, ldflags, linkobj); err != nil { return err @@ -2206,7 +2548,7 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe if p.Standard && p.ImportPath == "runtime/cgo" { cgoflags = []string{"-dynlinker"} // record path to dynamic linker } - return b.run(a, p.Dir, p.ImportPath, nil, cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags) + return b.run(a, p.Dir, p.ImportPath, b.cCompilerEnv(), cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags) } // Run SWIG on all SWIG input files. @@ -2256,7 +2598,7 @@ var ( ) func (b *Builder) swigDoVersionCheck() error { - out, err := b.runOut("", "", nil, "swig", "-version") + out, err := b.runOut("", nil, "swig", "-version") if err != nil { return err } @@ -2411,19 +2753,19 @@ func (b *Builder) swigOne(a *Action, p *load.Package, file, objdir string, pcCFL args = append(args, "-c++") } - out, err := b.runOut(p.Dir, p.ImportPath, nil, "swig", args, file) + out, err := b.runOut(p.Dir, nil, "swig", args, file) if err != nil { if len(out) > 0 { if bytes.Contains(out, []byte("-intgosize")) || bytes.Contains(out, []byte("-cgo")) { return "", "", errors.New("must have SWIG version >= 3.0.6") } - b.showOutput(a, p.Dir, p.ImportPath, b.processOutput(out)) // swig error + b.showOutput(a, p.Dir, p.Desc(), b.processOutput(out)) // swig error return "", "", errPrintedOutput } return "", "", err } if len(out) > 0 { - b.showOutput(a, p.Dir, p.ImportPath, b.processOutput(out)) // swig warning + b.showOutput(a, p.Dir, p.Desc(), b.processOutput(out)) // swig warning } // If the input was x.swig, the output is x.go in the objdir. @@ -2473,3 +2815,78 @@ func mkAbsFiles(dir string, files []string) []string { } return abs } + +// passLongArgsInResponseFiles modifies cmd on Windows such that, for +// certain programs, long arguments are passed in "response files", a +// file on disk with the arguments, with one arg per line. An actual +// argument starting with '@' means that the rest of the argument is +// a filename of arguments to expand. +// +// See Issue 18468. +func passLongArgsInResponseFiles(cmd *exec.Cmd) (cleanup func()) { + cleanup = func() {} // no cleanup by default + + var argLen int + for _, arg := range cmd.Args { + argLen += len(arg) + } + + // If we're not approaching 32KB of args, just pass args normally. + // (use 30KB instead to be conservative; not sure how accounting is done) + if !useResponseFile(cmd.Path, argLen) { + return + } + + tf, err := ioutil.TempFile("", "args") + if err != nil { + log.Fatalf("error writing long arguments to response file: %v", err) + } + cleanup = func() { os.Remove(tf.Name()) } + var buf bytes.Buffer + for _, arg := range cmd.Args[1:] { + fmt.Fprintf(&buf, "%s\n", arg) + } + if _, err := tf.Write(buf.Bytes()); err != nil { + tf.Close() + cleanup() + log.Fatalf("error writing long arguments to response file: %v", err) + } + if err := tf.Close(); err != nil { + cleanup() + log.Fatalf("error writing long arguments to response file: %v", err) + } + cmd.Args = []string{cmd.Args[0], "@" + tf.Name()} + return cleanup +} + +func useResponseFile(path string, argLen int) bool { + // Unless we're on Windows, don't use response files. + if runtime.GOOS != "windows" { + return false + } + + // Unless the program uses objabi.Flagparse, which understands + // response files, don't use response files. + // TODO: do we need more commands? asm? cgo? For now, no. + prog := strings.TrimSuffix(filepath.Base(path), ".exe") + switch prog { + case "compile", "link": + default: + return false + } + + // Windows has a limit of 32 KB arguments. To be conservative and not + // worry about whether that includes spaces or not, just use 30 KB. + if argLen > (30 << 10) { + return true + } + + // On the Go build system, use response files about 10% of the + // time, just to excercise this codepath. + isBuilder := os.Getenv("GO_BUILDER_NAME") != "" + if isBuilder && rand.Intn(10) == 0 { + return true + } + + return false +} diff --git a/libgo/go/cmd/go/internal/work/gc.go b/libgo/go/cmd/go/internal/work/gc.go index 71b5337..6e5333c 100644 --- a/libgo/go/cmd/go/internal/work/gc.go +++ b/libgo/go/cmd/go/internal/work/gc.go @@ -57,9 +57,14 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, a gcargs = append(gcargs, "-std") } compilingRuntime := p.Standard && (p.ImportPath == "runtime" || strings.HasPrefix(p.ImportPath, "runtime/internal")) + // The runtime package imports a couple of general internal packages. + if p.Standard && (p.ImportPath == "internal/cpu" || p.ImportPath == "internal/bytealg") { + compilingRuntime = true + } if compilingRuntime { - // runtime compiles with a special gc flag to emit - // additional reflect type data. + // runtime compiles with a special gc flag to check for + // memory allocations that are invalid in the runtime package, + // and to implement some special compiler pragmas. gcargs = append(gcargs, "-+") } @@ -70,7 +75,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, a extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.FFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) if p.Standard { switch p.ImportPath { - case "bytes", "internal/poll", "net", "os", "runtime/pprof", "sync", "syscall", "time": + case "bytes", "internal/poll", "net", "os", "runtime/pprof", "runtime/trace", "sync", "syscall", "time": extFiles++ } } @@ -84,7 +89,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, a gcargs = append(gcargs, "-buildid", a.buildID) } platform := cfg.Goos + "/" + cfg.Goarch - if p.Internal.OmitDebug || platform == "nacl/amd64p32" || platform == "darwin/arm" || platform == "darwin/arm64" || cfg.Goos == "plan9" { + if p.Internal.OmitDebug || platform == "nacl/amd64p32" || cfg.Goos == "plan9" || cfg.Goarch == "wasm" { gcargs = append(gcargs, "-dwarf=false") } if strings.HasPrefix(runtimeVersion, "go1") && !strings.Contains(os.Args[0], "go_bootstrap") { @@ -128,7 +133,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, a args = append(args, mkAbs(p.Dir, f)) } - output, err = b.runOut(p.Dir, p.ImportPath, nil, args...) + output, err = b.runOut(p.Dir, nil, args...) return ofile, output, err } @@ -228,6 +233,11 @@ func (gcToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) args = append(args, "-D", "GOMIPS_"+cfg.GOMIPS) } + if cfg.Goarch == "mips64" || cfg.Goarch == "mips64le" { + // Define GOMIPS64_value from cfg.GOMIPS64. + args = append(args, "-D", "GOMIPS64_"+cfg.GOMIPS64) + } + var ofiles []string for _, sfile := range sfiles { ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o" @@ -289,14 +299,14 @@ func (gcToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) er if cfg.BuildN { return nil } - if err := packInternal(b, absAfile, absOfiles); err != nil { - b.showOutput(a, p.Dir, p.ImportPath, err.Error()+"\n") + if err := packInternal(absAfile, absOfiles); err != nil { + b.showOutput(a, p.Dir, p.Desc(), err.Error()+"\n") return errPrintedOutput } return nil } -func packInternal(b *Builder, afile string, ofiles []string) error { +func packInternal(afile string, ofiles []string) error { dst, err := os.OpenFile(afile, os.O_WRONLY|os.O_APPEND, 0) if err != nil { return err diff --git a/libgo/go/cmd/go/internal/work/gccgo.go b/libgo/go/cmd/go/internal/work/gccgo.go index e8dab19..7255748 100644 --- a/libgo/go/cmd/go/internal/work/gccgo.go +++ b/libgo/go/cmd/go/internal/work/gccgo.go @@ -87,7 +87,7 @@ func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg args = append(args, mkAbs(p.Dir, f)) } - output, err = b.runOut(p.Dir, p.ImportPath, nil, args) + output, err = b.runOut(p.Dir, nil, args) return ofile, output, err } @@ -270,7 +270,7 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string readAndRemoveCgoFlags := func(archive string) (string, error) { newID++ newArchive := root.Objdir + fmt.Sprintf("_pkg%d_.a", newID) - if err := b.copyFile(root, newArchive, archive, 0666, false); err != nil { + if err := b.copyFile(newArchive, archive, 0666, false); err != nil { return "", err } if cfg.BuildN { diff --git a/libgo/go/cmd/go/internal/work/init.go b/libgo/go/cmd/go/internal/work/init.go index c2beb3b..de0cd42 100644 --- a/libgo/go/cmd/go/internal/work/init.go +++ b/libgo/go/cmd/go/internal/work/init.go @@ -9,13 +9,16 @@ package work import ( "cmd/go/internal/base" "cmd/go/internal/cfg" + "cmd/go/internal/load" "flag" "fmt" "os" "path/filepath" + "strings" ) func BuildInit() { + load.ModInit() instrumentInit() buildModeInit() @@ -39,15 +42,20 @@ func instrumentInit() { fmt.Fprintf(os.Stderr, "go %s: may not use -race and -msan simultaneously\n", flag.Args()[0]) os.Exit(2) } - if cfg.BuildMSan && (cfg.Goos != "linux" || cfg.Goarch != "amd64") { + if cfg.BuildMSan && (cfg.Goos != "linux" || cfg.Goarch != "amd64" && cfg.Goarch != "arm64") { fmt.Fprintf(os.Stderr, "-msan is not supported on %s/%s\n", cfg.Goos, cfg.Goarch) os.Exit(2) } - if cfg.Goarch != "amd64" || cfg.Goos != "linux" && cfg.Goos != "freebsd" && cfg.Goos != "darwin" && cfg.Goos != "windows" { - fmt.Fprintf(os.Stderr, "go %s: -race and -msan are only supported on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0]) - os.Exit(2) + if cfg.BuildRace { + platform := cfg.Goos + "/" + cfg.Goarch + switch platform { + default: + fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, linux/ppc64le, freebsd/amd64, netbsd/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0]) + os.Exit(2) + case "linux/amd64", "linux/ppc64le", "freebsd/amd64", "netbsd/amd64", "darwin/amd64", "windows/amd64": + // race supported on these platforms + } } - mode := "race" if cfg.BuildMSan { mode = "msan" @@ -83,6 +91,9 @@ func buildModeInit() { default: switch cfg.Goos { case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris": + if platform == "linux/ppc64" { + base.Fatalf("-buildmode=c-archive not supported on %s\n", platform) + } // Use -shared so that the result is // suitable for inclusion in a PIE or // shared library. @@ -101,7 +112,8 @@ func buildModeInit() { } else { switch platform { case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/s390x", - "android/amd64", "android/arm", "android/arm64", "android/386": + "android/amd64", "android/arm", "android/arm64", "android/386", + "freebsd/amd64": codegenArg = "-shared" case "darwin/amd64", "darwin/386": case "windows/amd64", "windows/386": @@ -144,7 +156,8 @@ func buildModeInit() { } else { switch platform { case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", - "android/amd64", "android/arm", "android/arm64", "android/386": + "android/amd64", "android/arm", "android/arm64", "android/386", + "freebsd/amd64": codegenArg = "-shared" case "darwin/amd64": codegenArg = "-shared" @@ -220,4 +233,31 @@ func buildModeInit() { cfg.BuildContext.InstallSuffix += codegenArg[1:] } } + + switch cfg.BuildMod { + case "": + // ok + case "readonly", "vendor": + if load.ModLookup == nil && !inGOFLAGS("-mod") { + base.Fatalf("build flag -mod=%s only valid when using modules", cfg.BuildMod) + } + default: + base.Fatalf("-mod=%s not supported (can be '', 'readonly', or 'vendor')", cfg.BuildMod) + } +} + +func inGOFLAGS(flag string) bool { + for _, goflag := range base.GOFLAGS() { + name := goflag + if strings.HasPrefix(name, "--") { + name = name[1:] + } + if i := strings.Index(name, "="); i >= 0 { + name = name[:i] + } + if name == flag { + return true + } + } + return false } diff --git a/libgo/go/cmd/go/internal/work/security.go b/libgo/go/cmd/go/internal/work/security.go index 1b82af9..d5d1261 100644 --- a/libgo/go/cmd/go/internal/work/security.go +++ b/libgo/go/cmd/go/internal/work/security.go @@ -112,6 +112,7 @@ var validCompilerFlags = []*regexp.Regexp{ re(`--sysroot=([^@\-].*)`), re(`-w`), re(`-x([^@\-].*)`), + re(`-v`), } var validCompilerFlagsWithNextArg = []string{ @@ -135,6 +136,7 @@ var validLinkerFlags = []*regexp.Regexp{ re(`-f(no-)?(pic|PIC|pie|PIE)`), re(`-f(no-)?openmp(-simd)?`), re(`-fsanitize=([^@\-].*)`), + re(`-flat_namespace`), re(`-g([^@\-].*)?`), re(`-headerpad_max_install_names`), re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`), @@ -150,6 +152,7 @@ var validLinkerFlags = []*regexp.Regexp{ re(`-shared`), re(`-?-static([-a-z0-9+]*)`), re(`-?-stdlib=([^@\-].*)`), + re(`-v`), // Note that any wildcards in -Wl need to exclude comma, // since -Wl splits its argument at commas and passes diff --git a/libgo/go/cmd/go/internal/work/security_test.go b/libgo/go/cmd/go/internal/work/security_test.go index c3a61b8..d23b6ea 100644 --- a/libgo/go/cmd/go/internal/work/security_test.go +++ b/libgo/go/cmd/go/internal/work/security_test.go @@ -58,6 +58,7 @@ var goodCompilerFlags = [][]string{ {"-I", "世界"}, {"-framework", "Chocolate"}, {"-x", "c"}, + {"-v"}, } var badCompilerFlags = [][]string{ @@ -136,6 +137,7 @@ var goodLinkerFlags = [][]string{ {"-l", "世界"}, {"-L", "framework"}, {"-framework", "Chocolate"}, + {"-v"}, {"-Wl,-framework", "-Wl,Chocolate"}, {"-Wl,-framework,Chocolate"}, {"-Wl,-unresolved-symbols=ignore-all"}, diff --git a/libgo/go/cmd/go/main.go b/libgo/go/cmd/go/main.go index 7558e08..14b4352 100644 --- a/libgo/go/cmd/go/main.go +++ b/libgo/go/cmd/go/main.go @@ -27,6 +27,10 @@ import ( "cmd/go/internal/get" "cmd/go/internal/help" "cmd/go/internal/list" + "cmd/go/internal/modcmd" + "cmd/go/internal/modfetch" + "cmd/go/internal/modget" + "cmd/go/internal/modload" "cmd/go/internal/run" "cmd/go/internal/test" "cmd/go/internal/tool" @@ -36,31 +40,37 @@ import ( ) func init() { - base.Commands = []*base.Command{ + base.Go.Commands = []*base.Command{ + bug.CmdBug, work.CmdBuild, clean.CmdClean, doc.CmdDoc, envcmd.CmdEnv, - bug.CmdBug, fix.CmdFix, fmtcmd.CmdFmt, generate.CmdGenerate, get.CmdGet, work.CmdInstall, list.CmdList, + modcmd.CmdMod, run.CmdRun, test.CmdTest, tool.CmdTool, version.CmdVersion, vet.CmdVet, - help.HelpC, help.HelpBuildmode, + help.HelpC, help.HelpCache, + help.HelpEnvironment, help.HelpFileType, + modload.HelpGoMod, help.HelpGopath, - help.HelpEnvironment, + get.HelpGopathGet, + modfetch.HelpGoproxy, help.HelpImportPath, + modload.HelpModules, + modget.HelpModuleGet, help.HelpPackages, test.HelpTestflag, test.HelpTestfunc, @@ -78,6 +88,11 @@ func main() { base.Usage() } + if modload.MustUseModules { + // If running with modules force-enabled, change get now to change help message. + *get.CmdGet = *modget.CmdGet + } + cfg.CmdName = args[0] // for error messages if args[0] == "help" { help.Help(args[1:]) @@ -118,6 +133,46 @@ func main() { os.Exit(2) } + // TODO(rsc): Remove all these helper prints in Go 1.12. + switch args[0] { + case "mod": + if len(args) >= 2 { + flag := args[1] + if strings.HasPrefix(flag, "--") { + flag = flag[1:] + } + if i := strings.Index(flag, "="); i >= 0 { + flag = flag[:i] + } + switch flag { + case "-sync": + fmt.Fprintf(os.Stderr, "go: go mod -sync is now go mod tidy\n") + os.Exit(2) + case "-init", "-fix", "-graph", "-vendor", "-verify": + fmt.Fprintf(os.Stderr, "go: go mod %s is now go mod %s\n", flag, flag[1:]) + os.Exit(2) + case "-fmt", "-json", "-module", "-require", "-droprequire", "-replace", "-dropreplace", "-exclude", "-dropexclude": + fmt.Fprintf(os.Stderr, "go: go mod %s is now go mod edit %s\n", flag, flag) + os.Exit(2) + } + } + case "vendor": + fmt.Fprintf(os.Stderr, "go: vgo vendor is now go mod vendor\n") + os.Exit(2) + case "verify": + fmt.Fprintf(os.Stderr, "go: vgo verify is now go mod verify\n") + os.Exit(2) + } + + if args[0] == "get" { + // Replace get with module-aware get if appropriate. + // Note that if MustUseModules is true, this happened already above, + // but no harm in doing it again. + if modload.Init(); modload.Enabled() { + *get.CmdGet = *modget.CmdGet + } + } + // Set environment (GOOS, GOARCH, etc) explicitly. // In theory all the commands we invoke should have // the same default computation of these as we do, @@ -131,12 +186,36 @@ func main() { } } - for _, cmd := range base.Commands { - if cmd.Name() == args[0] && cmd.Runnable() { +BigCmdLoop: + for bigCmd := base.Go; ; { + for _, cmd := range bigCmd.Commands { + if cmd.Name() != args[0] { + continue + } + if len(cmd.Commands) > 0 { + bigCmd = cmd + args = args[1:] + if len(args) == 0 { + help.PrintUsage(os.Stderr, bigCmd) + base.SetExitStatus(2) + base.Exit() + } + if args[0] == "help" { + // Accept 'go mod help' and 'go mod help foo' for 'go help mod' and 'go help mod foo'. + help.Help(append(strings.Split(cfg.CmdName, " "), args[1:]...)) + return + } + cfg.CmdName += " " + args[0] + continue BigCmdLoop + } + if !cmd.Runnable() { + continue + } cmd.Flag.Usage = func() { cmd.Usage() } if cmd.CustomFlags { args = args[1:] } else { + base.SetFromGOFLAGS(cmd.Flag) cmd.Flag.Parse(args[1:]) args = cmd.Flag.Args() } @@ -144,11 +223,14 @@ func main() { base.Exit() return } + helpArg := "" + if i := strings.LastIndex(cfg.CmdName, " "); i >= 0 { + helpArg = " " + cfg.CmdName[:i] + } + fmt.Fprintf(os.Stderr, "go %s: unknown command\nRun 'go help%s' for usage.\n", cfg.CmdName, helpArg) + base.SetExitStatus(2) + base.Exit() } - - fmt.Fprintf(os.Stderr, "go: unknown subcommand %q\nRun 'go help' for usage.\n", args[0]) - base.SetExitStatus(2) - base.Exit() } func init() { @@ -160,6 +242,6 @@ func mainUsage() { if len(os.Args) > 1 && os.Args[1] == "test" { test.Usage() } - help.PrintUsage(os.Stderr) + help.PrintUsage(os.Stderr, base.Go) os.Exit(2) } diff --git a/libgo/go/cmd/go/proxy_test.go b/libgo/go/cmd/go/proxy_test.go new file mode 100644 index 0000000..212e5aa --- /dev/null +++ b/libgo/go/cmd/go/proxy_test.go @@ -0,0 +1,272 @@ +// Copyright 2018 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 main_test + +import ( + "archive/zip" + "bytes" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "net" + "net/http" + "os" + "path/filepath" + "strings" + "sync" + "testing" + + "cmd/go/internal/modfetch" + "cmd/go/internal/modfetch/codehost" + "cmd/go/internal/module" + "cmd/go/internal/par" + "cmd/go/internal/semver" + "cmd/go/internal/txtar" +) + +var ( + proxyAddr = flag.String("proxy", "", "run proxy on this network address instead of running any tests") + proxyURL string +) + +var proxyOnce sync.Once + +// StartProxy starts the Go module proxy running on *proxyAddr (like "localhost:1234") +// and sets proxyURL to the GOPROXY setting to use to access the proxy. +// Subsequent calls are no-ops. +// +// The proxy serves from testdata/mod. See testdata/mod/README. +func StartProxy() { + proxyOnce.Do(func() { + fmt.Fprintf(os.Stderr, "go test proxy starting\n") + readModList() + addr := *proxyAddr + if addr == "" { + addr = "localhost:0" + } + l, err := net.Listen("tcp", addr) + if err != nil { + log.Fatal(err) + } + *proxyAddr = l.Addr().String() + proxyURL = "http://" + *proxyAddr + "/mod" + fmt.Fprintf(os.Stderr, "go test proxy running at GOPROXY=%s\n", proxyURL) + go func() { + log.Fatalf("go proxy: http.Serve: %v", http.Serve(l, http.HandlerFunc(proxyHandler))) + }() + }) +} + +var modList []module.Version + +func readModList() { + infos, err := ioutil.ReadDir("testdata/mod") + if err != nil { + log.Fatal(err) + } + for _, info := range infos { + name := info.Name() + if !strings.HasSuffix(name, ".txt") { + continue + } + name = strings.TrimSuffix(name, ".txt") + i := strings.LastIndex(name, "_v") + if i < 0 { + continue + } + encPath := strings.Replace(name[:i], "_", "/", -1) + path, err := module.DecodePath(encPath) + if err != nil { + fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err) + continue + } + encVers := name[i+1:] + vers, err := module.DecodeVersion(encVers) + if err != nil { + fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err) + continue + } + modList = append(modList, module.Version{Path: path, Version: vers}) + } +} + +var zipCache par.Cache + +// proxyHandler serves the Go module proxy protocol. +// See the proxy section of https://research.swtch.com/vgo-module. +func proxyHandler(w http.ResponseWriter, r *http.Request) { + if !strings.HasPrefix(r.URL.Path, "/mod/") { + http.NotFound(w, r) + return + } + path := strings.TrimPrefix(r.URL.Path, "/mod/") + i := strings.Index(path, "/@v/") + if i < 0 { + http.NotFound(w, r) + return + } + enc, file := path[:i], path[i+len("/@v/"):] + path, err := module.DecodePath(enc) + if err != nil { + fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err) + http.NotFound(w, r) + return + } + if file == "list" { + n := 0 + for _, m := range modList { + if m.Path == path && !modfetch.IsPseudoVersion(m.Version) { + if err := module.Check(m.Path, m.Version); err == nil { + fmt.Fprintf(w, "%s\n", m.Version) + n++ + } + } + } + if n == 0 { + http.NotFound(w, r) + } + return + } + + i = strings.LastIndex(file, ".") + if i < 0 { + http.NotFound(w, r) + return + } + encVers, ext := file[:i], file[i+1:] + vers, err := module.DecodeVersion(encVers) + if err != nil { + fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err) + http.NotFound(w, r) + return + } + + if codehost.AllHex(vers) { + var best string + // Convert commit hash (only) to known version. + // Use latest version in semver priority, to match similar logic + // in the repo-based module server (see modfetch.(*codeRepo).convert). + for _, m := range modList { + if m.Path == path && semver.Compare(best, m.Version) < 0 { + var hash string + if modfetch.IsPseudoVersion(m.Version) { + hash = m.Version[strings.LastIndex(m.Version, "-")+1:] + } else { + hash = findHash(m) + } + if strings.HasPrefix(hash, vers) || strings.HasPrefix(vers, hash) { + best = m.Version + } + } + } + if best != "" { + vers = best + } + } + + a := readArchive(path, vers) + if a == nil { + fmt.Fprintf(os.Stderr, "go proxy: no archive %s %s\n", path, vers) + http.Error(w, "cannot load archive", 500) + return + } + + switch ext { + case "info", "mod": + want := "." + ext + for _, f := range a.Files { + if f.Name == want { + w.Write(f.Data) + return + } + } + + case "zip": + type cached struct { + zip []byte + err error + } + c := zipCache.Do(a, func() interface{} { + var buf bytes.Buffer + z := zip.NewWriter(&buf) + for _, f := range a.Files { + if strings.HasPrefix(f.Name, ".") { + continue + } + zf, err := z.Create(path + "@" + vers + "/" + f.Name) + if err != nil { + return cached{nil, err} + } + if _, err := zf.Write(f.Data); err != nil { + return cached{nil, err} + } + } + if err := z.Close(); err != nil { + return cached{nil, err} + } + return cached{buf.Bytes(), nil} + }).(cached) + + if c.err != nil { + fmt.Fprintf(os.Stderr, "go proxy: %v\n", c.err) + http.Error(w, c.err.Error(), 500) + return + } + w.Write(c.zip) + return + + } + http.NotFound(w, r) +} + +func findHash(m module.Version) string { + a := readArchive(m.Path, m.Version) + if a == nil { + return "" + } + var data []byte + for _, f := range a.Files { + if f.Name == ".info" { + data = f.Data + break + } + } + var info struct{ Short string } + json.Unmarshal(data, &info) + return info.Short +} + +var archiveCache par.Cache + +var cmdGoDir, _ = os.Getwd() + +func readArchive(path, vers string) *txtar.Archive { + enc, err := module.EncodePath(path) + if err != nil { + fmt.Fprintf(os.Stderr, "go proxy: %v\n", err) + return nil + } + encVers, err := module.EncodeVersion(vers) + if err != nil { + fmt.Fprintf(os.Stderr, "go proxy: %v\n", err) + return nil + } + + prefix := strings.Replace(enc, "/", "_", -1) + name := filepath.Join(cmdGoDir, "testdata/mod", prefix+"_"+encVers+".txt") + a := archiveCache.Do(name, func() interface{} { + a, err := txtar.ParseFile(name) + if err != nil { + if testing.Verbose() || !os.IsNotExist(err) { + fmt.Fprintf(os.Stderr, "go proxy: %v\n", err) + } + a = nil + } + return a + }).(*txtar.Archive) + return a +} diff --git a/libgo/go/cmd/go/script_test.go b/libgo/go/cmd/go/script_test.go new file mode 100644 index 0000000..02cb17b --- /dev/null +++ b/libgo/go/cmd/go/script_test.go @@ -0,0 +1,905 @@ +// Copyright 2018 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. + +// Script-driven tests. +// See testdata/script/README for an overview. + +package main_test + +import ( + "bytes" + "fmt" + "internal/testenv" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "strconv" + "strings" + "testing" + "time" + + "cmd/go/internal/imports" + "cmd/go/internal/par" + "cmd/go/internal/txtar" +) + +// TestScript runs the tests in testdata/script/*.txt. +func TestScript(t *testing.T) { + testenv.MustHaveGoBuild(t) + if skipExternal { + t.Skipf("skipping external tests on %s/%s", runtime.GOOS, runtime.GOARCH) + } + + files, err := filepath.Glob("testdata/script/*.txt") + if err != nil { + t.Fatal(err) + } + for _, file := range files { + file := file + name := strings.TrimSuffix(filepath.Base(file), ".txt") + t.Run(name, func(t *testing.T) { + t.Parallel() + ts := &testScript{t: t, name: name, file: file} + ts.setup() + if !*testWork { + defer removeAll(ts.workdir) + } + ts.run() + }) + } +} + +// A testScript holds execution state for a single test script. +type testScript struct { + t *testing.T + workdir string // temporary work dir ($WORK) + log bytes.Buffer // test execution log (printed at end of test) + mark int // offset of next log truncation + cd string // current directory during test execution; initially $WORK/gopath/src + name string // short name of test ("foo") + file string // full file name ("testdata/script/foo.txt") + lineno int // line number currently executing + line string // line currently executing + env []string // environment list (for os/exec) + envMap map[string]string // environment mapping (matches env) + stdout string // standard output from last 'go' command; for 'stdout' command + stderr string // standard error from last 'go' command; for 'stderr' command + stopped bool // test wants to stop early + start time.Time // time phase started +} + +var extraEnvKeys = []string{ + "SYSTEMROOT", // must be preserved on Windows to find DLLs; golang.org/issue/25210 + "LD_LIBRARY_PATH", // must be preserved on Unix systems to find shared libraries + + // For gccgo testing. + "GO_TESTING_GOTOOLS", + "GCCGOTOOLDIR", +} + +// setup sets up the test execution temporary directory and environment. +func (ts *testScript) setup() { + StartProxy() + ts.workdir = filepath.Join(testTmpDir, "script-"+ts.name) + ts.check(os.MkdirAll(filepath.Join(ts.workdir, "tmp"), 0777)) + ts.check(os.MkdirAll(filepath.Join(ts.workdir, "gopath/src"), 0777)) + ts.cd = filepath.Join(ts.workdir, "gopath/src") + ts.env = []string{ + "WORK=" + ts.workdir, // must be first for ts.abbrev + "PATH=" + testBin + string(filepath.ListSeparator) + os.Getenv("PATH"), + homeEnvName() + "=/no-home", + "CCACHE_DISABLE=1", // ccache breaks with non-existent HOME + "GOARCH=" + runtime.GOARCH, + "GOCACHE=" + testGOCACHE, + "GOOS=" + runtime.GOOS, + "GOPATH=" + filepath.Join(ts.workdir, "gopath"), + "GOPROXY=" + proxyURL, + "GOROOT=" + testGOROOT, + tempEnvName() + "=" + filepath.Join(ts.workdir, "tmp"), + "devnull=" + os.DevNull, + ":=" + string(os.PathListSeparator), + } + + if runtime.GOOS == "plan9" { + ts.env = append(ts.env, "path="+testBin+string(filepath.ListSeparator)+os.Getenv("path")) + } + + if runtime.GOOS == "windows" { + ts.env = append(ts.env, "exe=.exe") + } else { + ts.env = append(ts.env, "exe=") + } + for _, key := range extraEnvKeys { + if val := os.Getenv(key); val != "" { + ts.env = append(ts.env, key+"="+val) + } + } + + ts.envMap = make(map[string]string) + for _, kv := range ts.env { + if i := strings.Index(kv, "="); i >= 0 { + ts.envMap[kv[:i]] = kv[i+1:] + } + } +} + +var execCache par.Cache + +// run runs the test script. +func (ts *testScript) run() { + // Truncate log at end of last phase marker, + // discarding details of successful phase. + rewind := func() { + if !testing.Verbose() { + ts.log.Truncate(ts.mark) + } + } + + // Insert elapsed time for phase at end of phase marker + markTime := func() { + if ts.mark > 0 && !ts.start.IsZero() { + afterMark := append([]byte{}, ts.log.Bytes()[ts.mark:]...) + ts.log.Truncate(ts.mark - 1) // cut \n and afterMark + fmt.Fprintf(&ts.log, " (%.3fs)\n", time.Since(ts.start).Seconds()) + ts.log.Write(afterMark) + } + ts.start = time.Time{} + } + + defer func() { + markTime() + // Flush testScript log to testing.T log. + ts.t.Log("\n" + ts.abbrev(ts.log.String())) + }() + + // Unpack archive. + a, err := txtar.ParseFile(ts.file) + ts.check(err) + for _, f := range a.Files { + name := ts.mkabs(ts.expand(f.Name)) + ts.check(os.MkdirAll(filepath.Dir(name), 0777)) + ts.check(ioutil.WriteFile(name, f.Data, 0666)) + } + + // With -v or -testwork, start log with full environment. + if *testWork || testing.Verbose() { + // Display environment. + ts.cmdEnv(false, nil) + fmt.Fprintf(&ts.log, "\n") + ts.mark = ts.log.Len() + } + + // Run script. + // See testdata/script/README for documentation of script form. + script := string(a.Comment) +Script: + for script != "" { + // Extract next line. + ts.lineno++ + var line string + if i := strings.Index(script, "\n"); i >= 0 { + line, script = script[:i], script[i+1:] + } else { + line, script = script, "" + } + + // # is a comment indicating the start of new phase. + if strings.HasPrefix(line, "#") { + // If there was a previous phase, it succeeded, + // so rewind the log to delete its details (unless -v is in use). + // If nothing has happened at all since the mark, + // rewinding is a no-op and adding elapsed time + // for doing nothing is meaningless, so don't. + if ts.log.Len() > ts.mark { + rewind() + markTime() + } + // Print phase heading and mark start of phase output. + fmt.Fprintf(&ts.log, "%s\n", line) + ts.mark = ts.log.Len() + ts.start = time.Now() + continue + } + + // Parse input line. Ignore blanks entirely. + args := ts.parse(line) + if len(args) == 0 { + continue + } + + // Echo command to log. + fmt.Fprintf(&ts.log, "> %s\n", line) + + // Command prefix [cond] means only run this command if cond is satisfied. + for strings.HasPrefix(args[0], "[") && strings.HasSuffix(args[0], "]") { + cond := args[0] + cond = cond[1 : len(cond)-1] + cond = strings.TrimSpace(cond) + args = args[1:] + if len(args) == 0 { + ts.fatalf("missing command after condition") + } + want := true + if strings.HasPrefix(cond, "!") { + want = false + cond = strings.TrimSpace(cond[1:]) + } + // Known conds are: $GOOS, $GOARCH, runtime.Compiler, and 'short' (for testing.Short). + // + // NOTE: If you make changes here, update testdata/script/README too! + // + ok := false + switch cond { + case runtime.GOOS, runtime.GOARCH, runtime.Compiler: + ok = true + case "short": + ok = testing.Short() + case "cgo": + ok = canCgo + case "msan": + ok = canMSan + case "race": + ok = canRace + case "net": + ok = testenv.HasExternalNetwork() + case "link": + ok = testenv.HasLink() + case "symlink": + ok = testenv.HasSymlink() + default: + if strings.HasPrefix(cond, "exec:") { + prog := cond[len("exec:"):] + ok = execCache.Do(prog, func() interface{} { + _, err := exec.LookPath(prog) + return err == nil + }).(bool) + break + } + if !imports.KnownArch[cond] && !imports.KnownOS[cond] && cond != "gc" && cond != "gccgo" { + ts.fatalf("unknown condition %q", cond) + } + } + if ok != want { + // Don't run rest of line. + continue Script + } + } + + // Command prefix ! means negate the expectations about this command: + // go command should fail, match should not be found, etc. + neg := false + if args[0] == "!" { + neg = true + args = args[1:] + if len(args) == 0 { + ts.fatalf("! on line by itself") + } + } + + // Run command. + cmd := scriptCmds[args[0]] + if cmd == nil { + ts.fatalf("unknown command %q", args[0]) + } + cmd(ts, neg, args[1:]) + + // Command can ask script to stop early. + if ts.stopped { + return + } + } + + // Final phase ended. + rewind() + markTime() + fmt.Fprintf(&ts.log, "PASS\n") +} + +// scriptCmds are the script command implementations. +// Keep list and the implementations below sorted by name. +// +// NOTE: If you make changes here, update testdata/script/README too! +// +var scriptCmds = map[string]func(*testScript, bool, []string){ + "addcrlf": (*testScript).cmdAddcrlf, + "cd": (*testScript).cmdCd, + "cmp": (*testScript).cmdCmp, + "cp": (*testScript).cmdCp, + "env": (*testScript).cmdEnv, + "exec": (*testScript).cmdExec, + "exists": (*testScript).cmdExists, + "go": (*testScript).cmdGo, + "grep": (*testScript).cmdGrep, + "mkdir": (*testScript).cmdMkdir, + "rm": (*testScript).cmdRm, + "skip": (*testScript).cmdSkip, + "stale": (*testScript).cmdStale, + "stderr": (*testScript).cmdStderr, + "stdout": (*testScript).cmdStdout, + "stop": (*testScript).cmdStop, + "symlink": (*testScript).cmdSymlink, +} + +// addcrlf adds CRLF line endings to the named files. +func (ts *testScript) cmdAddcrlf(neg bool, args []string) { + if len(args) == 0 { + ts.fatalf("usage: addcrlf file...") + } + + for _, file := range args { + file = ts.mkabs(file) + data, err := ioutil.ReadFile(file) + ts.check(err) + ts.check(ioutil.WriteFile(file, bytes.Replace(data, []byte("\n"), []byte("\r\n"), -1), 0666)) + } +} + +// cd changes to a different directory. +func (ts *testScript) cmdCd(neg bool, args []string) { + if neg { + ts.fatalf("unsupported: ! cd") + } + if len(args) != 1 { + ts.fatalf("usage: cd dir") + } + + dir := args[0] + if !filepath.IsAbs(dir) { + dir = filepath.Join(ts.cd, dir) + } + info, err := os.Stat(dir) + if os.IsNotExist(err) { + ts.fatalf("directory %s does not exist", dir) + } + ts.check(err) + if !info.IsDir() { + ts.fatalf("%s is not a directory", dir) + } + ts.cd = dir + fmt.Fprintf(&ts.log, "%s\n", ts.cd) +} + +// cmp compares two files. +func (ts *testScript) cmdCmp(neg bool, args []string) { + if neg { + // It would be strange to say "this file can have any content except this precise byte sequence". + ts.fatalf("unsupported: ! cmp") + } + if len(args) != 2 { + ts.fatalf("usage: cmp file1 file2") + } + + name1, name2 := args[0], args[1] + var text1, text2 string + if name1 == "stdout" { + text1 = ts.stdout + } else if name1 == "stderr" { + text1 = ts.stderr + } else { + data, err := ioutil.ReadFile(ts.mkabs(name1)) + ts.check(err) + text1 = string(data) + } + + data, err := ioutil.ReadFile(ts.mkabs(name2)) + ts.check(err) + text2 = string(data) + + if text1 == text2 { + return + } + + fmt.Fprintf(&ts.log, "[diff -%s +%s]\n%s\n", name1, name2, diff(text1, text2)) + ts.fatalf("%s and %s differ", name1, name2) +} + +// cp copies files, maybe eventually directories. +func (ts *testScript) cmdCp(neg bool, args []string) { + if neg { + ts.fatalf("unsupported: ! cp") + } + if len(args) < 2 { + ts.fatalf("usage: cp src... dst") + } + + dst := ts.mkabs(args[len(args)-1]) + info, err := os.Stat(dst) + dstDir := err == nil && info.IsDir() + if len(args) > 2 && !dstDir { + ts.fatalf("cp: destination %s is not a directory", dst) + } + + for _, arg := range args[:len(args)-1] { + src := ts.mkabs(arg) + info, err := os.Stat(src) + ts.check(err) + data, err := ioutil.ReadFile(src) + ts.check(err) + targ := dst + if dstDir { + targ = filepath.Join(dst, filepath.Base(src)) + } + ts.check(ioutil.WriteFile(targ, data, info.Mode()&0777)) + } +} + +// env displays or adds to the environment. +func (ts *testScript) cmdEnv(neg bool, args []string) { + if neg { + ts.fatalf("unsupported: ! env") + } + if len(args) == 0 { + printed := make(map[string]bool) // env list can have duplicates; only print effective value (from envMap) once + for _, kv := range ts.env { + k := kv[:strings.Index(kv, "=")] + if !printed[k] { + fmt.Fprintf(&ts.log, "%s=%s\n", k, ts.envMap[k]) + } + } + return + } + for _, env := range args { + i := strings.Index(env, "=") + if i < 0 { + // Display value instead of setting it. + fmt.Fprintf(&ts.log, "%s=%s\n", env, ts.envMap[env]) + continue + } + ts.env = append(ts.env, env) + ts.envMap[env[:i]] = env[i+1:] + } +} + +// exec runs the given command. +func (ts *testScript) cmdExec(neg bool, args []string) { + if len(args) < 1 { + ts.fatalf("usage: exec program [args...]") + } + var err error + ts.stdout, ts.stderr, err = ts.exec(args[0], args[1:]...) + if ts.stdout != "" { + fmt.Fprintf(&ts.log, "[stdout]\n%s", ts.stdout) + } + if ts.stderr != "" { + fmt.Fprintf(&ts.log, "[stderr]\n%s", ts.stderr) + } + if err != nil { + fmt.Fprintf(&ts.log, "[%v]\n", err) + if !neg { + ts.fatalf("unexpected command failure") + } + } else { + if neg { + ts.fatalf("unexpected command success") + } + } +} + +// exists checks that the list of files exists. +func (ts *testScript) cmdExists(neg bool, args []string) { + var readonly bool + if len(args) > 0 && args[0] == "-readonly" { + readonly = true + args = args[1:] + } + if len(args) == 0 { + ts.fatalf("usage: exists [-readonly] file...") + } + + for _, file := range args { + file = ts.mkabs(file) + info, err := os.Stat(file) + if err == nil && neg { + what := "file" + if info.IsDir() { + what = "directory" + } + ts.fatalf("%s %s unexpectedly exists", what, file) + } + if err != nil && !neg { + ts.fatalf("%s does not exist", file) + } + if err == nil && !neg && readonly && info.Mode()&0222 != 0 { + ts.fatalf("%s exists but is writable", file) + } + } +} + +// go runs the go command. +func (ts *testScript) cmdGo(neg bool, args []string) { + ts.cmdExec(neg, append([]string{testGo}, args...)) +} + +// mkdir creates directories. +func (ts *testScript) cmdMkdir(neg bool, args []string) { + if neg { + ts.fatalf("unsupported: ! mkdir") + } + if len(args) < 1 { + ts.fatalf("usage: mkdir dir...") + } + for _, arg := range args { + ts.check(os.MkdirAll(ts.mkabs(arg), 0777)) + } +} + +// rm removes files or directories. +func (ts *testScript) cmdRm(neg bool, args []string) { + if neg { + ts.fatalf("unsupported: ! rm") + } + if len(args) < 1 { + ts.fatalf("usage: rm file...") + } + for _, arg := range args { + file := ts.mkabs(arg) + removeAll(file) // does chmod and then attempts rm + ts.check(os.RemoveAll(file)) // report error + } +} + +// skip marks the test skipped. +func (ts *testScript) cmdSkip(neg bool, args []string) { + if len(args) > 1 { + ts.fatalf("usage: skip [msg]") + } + if neg { + ts.fatalf("unsupported: ! skip") + } + if len(args) == 1 { + ts.t.Skip(args[0]) + } + ts.t.Skip() +} + +// stale checks that the named build targets are stale. +func (ts *testScript) cmdStale(neg bool, args []string) { + if len(args) == 0 { + ts.fatalf("usage: stale target...") + } + tmpl := "{{if .Error}}{{.ImportPath}}: {{.Error.Err}}{else}}" + if neg { + tmpl += "{{if .Stale}}{{.ImportPath}} is unexpectedly stale{{end}}" + } else { + tmpl += "{{if not .Stale}}{{.ImportPath}} is unexpectedly NOT stale{{end}}" + } + tmpl += "{{end}}" + goArgs := append([]string{"list", "-e", "-f=" + tmpl}, args...) + stdout, stderr, err := ts.exec(testGo, goArgs...) + if err != nil { + ts.fatalf("go list: %v\n%s%s", err, stdout, stderr) + } + if stdout != "" { + ts.fatalf("%s", stdout) + } +} + +// stdout checks that the last go command standard output matches a regexp. +func (ts *testScript) cmdStdout(neg bool, args []string) { + scriptMatch(ts, neg, args, ts.stdout, "stdout") +} + +// stderr checks that the last go command standard output matches a regexp. +func (ts *testScript) cmdStderr(neg bool, args []string) { + scriptMatch(ts, neg, args, ts.stderr, "stderr") +} + +// grep checks that file content matches a regexp. +// Like stdout/stderr and unlike Unix grep, it accepts Go regexp syntax. +func (ts *testScript) cmdGrep(neg bool, args []string) { + scriptMatch(ts, neg, args, "", "grep") +} + +// scriptMatch implements both stdout and stderr. +func scriptMatch(ts *testScript, neg bool, args []string, text, name string) { + n := 0 + if len(args) >= 1 && strings.HasPrefix(args[0], "-count=") { + if neg { + ts.fatalf("cannot use -count= with negated match") + } + var err error + n, err = strconv.Atoi(args[0][len("-count="):]) + if err != nil { + ts.fatalf("bad -count=: %v", err) + } + if n < 1 { + ts.fatalf("bad -count=: must be at least 1") + } + args = args[1:] + } + + extraUsage := "" + want := 1 + if name == "grep" { + extraUsage = " file" + want = 2 + } + if len(args) != want { + ts.fatalf("usage: %s [-count=N] 'pattern' file%s", name, extraUsage) + } + + pattern := args[0] + re, err := regexp.Compile(`(?m)` + pattern) + ts.check(err) + + isGrep := name == "grep" + if isGrep { + name = args[1] // for error messages + data, err := ioutil.ReadFile(ts.mkabs(args[1])) + ts.check(err) + text = string(data) + } + + if neg { + if re.MatchString(text) { + if isGrep { + fmt.Fprintf(&ts.log, "[%s]\n%s\n", name, text) + } + ts.fatalf("unexpected match for %#q found in %s: %s", pattern, name, re.FindString(text)) + } + } else { + if !re.MatchString(text) { + if isGrep { + fmt.Fprintf(&ts.log, "[%s]\n%s\n", name, text) + } + ts.fatalf("no match for %#q found in %s", pattern, name) + } + if n > 0 { + count := len(re.FindAllString(text, -1)) + if count != n { + if isGrep { + fmt.Fprintf(&ts.log, "[%s]\n%s\n", name, text) + } + ts.fatalf("have %d matches for %#q, want %d", count, pattern, n) + } + } + } +} + +// stop stops execution of the test (marking it passed). +func (ts *testScript) cmdStop(neg bool, args []string) { + if neg { + ts.fatalf("unsupported: ! stop") + } + if len(args) > 1 { + ts.fatalf("usage: stop [msg]") + } + if len(args) == 1 { + fmt.Fprintf(&ts.log, "stop: %s\n", args[0]) + } else { + fmt.Fprintf(&ts.log, "stop\n") + } + ts.stopped = true +} + +// symlink creates a symbolic link. +func (ts *testScript) cmdSymlink(neg bool, args []string) { + if neg { + ts.fatalf("unsupported: ! symlink") + } + if len(args) != 3 || args[1] != "->" { + ts.fatalf("usage: symlink file -> target") + } + // Note that the link target args[2] is not interpreted with mkabs: + // it will be interpreted relative to the directory file is in. + ts.check(os.Symlink(args[2], ts.mkabs(args[0]))) +} + +// Helpers for command implementations. + +// abbrev abbreviates the actual work directory in the string s to the literal string "$WORK". +func (ts *testScript) abbrev(s string) string { + s = strings.Replace(s, ts.workdir, "$WORK", -1) + if *testWork { + // Expose actual $WORK value in environment dump on first line of work script, + // so that the user can find out what directory -testwork left behind. + s = "WORK=" + ts.workdir + "\n" + strings.TrimPrefix(s, "WORK=$WORK\n") + } + return s +} + +// check calls ts.fatalf if err != nil. +func (ts *testScript) check(err error) { + if err != nil { + ts.fatalf("%v", err) + } +} + +// exec runs the given command line (an actual subprocess, not simulated) +// in ts.cd with environment ts.env and then returns collected standard output and standard error. +func (ts *testScript) exec(command string, args ...string) (stdout, stderr string, err error) { + cmd := exec.Command(command, args...) + cmd.Dir = ts.cd + cmd.Env = append(ts.env, "PWD="+ts.cd) + var stdoutBuf, stderrBuf strings.Builder + cmd.Stdout = &stdoutBuf + cmd.Stderr = &stderrBuf + err = cmd.Run() + return stdoutBuf.String(), stderrBuf.String(), err +} + +// expand applies environment variable expansion to the string s. +func (ts *testScript) expand(s string) string { + return os.Expand(s, func(key string) string { return ts.envMap[key] }) +} + +// fatalf aborts the test with the given failure message. +func (ts *testScript) fatalf(format string, args ...interface{}) { + fmt.Fprintf(&ts.log, "FAIL: %s:%d: %s\n", ts.file, ts.lineno, fmt.Sprintf(format, args...)) + ts.t.FailNow() +} + +// mkabs interprets file relative to the test script's current directory +// and returns the corresponding absolute path. +func (ts *testScript) mkabs(file string) string { + if filepath.IsAbs(file) { + return file + } + return filepath.Join(ts.cd, file) +} + +// parse parses a single line as a list of space-separated arguments +// subject to environment variable expansion (but not resplitting). +// Single quotes around text disable splitting and expansion. +// To embed a single quote, double it: 'Don''t communicate by sharing memory.' +func (ts *testScript) parse(line string) []string { + ts.line = line + + var ( + args []string + arg string // text of current arg so far (need to add line[start:i]) + start = -1 // if >= 0, position where current arg text chunk starts + quoted = false // currently processing quoted text + ) + for i := 0; ; i++ { + if !quoted && (i >= len(line) || line[i] == ' ' || line[i] == '\t' || line[i] == '\r' || line[i] == '#') { + // Found arg-separating space. + if start >= 0 { + arg += ts.expand(line[start:i]) + args = append(args, arg) + start = -1 + arg = "" + } + if i >= len(line) || line[i] == '#' { + break + } + continue + } + if i >= len(line) { + ts.fatalf("unterminated quoted argument") + } + if line[i] == '\'' { + if !quoted { + // starting a quoted chunk + if start >= 0 { + arg += ts.expand(line[start:i]) + } + start = i + 1 + quoted = true + continue + } + // 'foo''bar' means foo'bar, like in rc shell and Pascal. + if i+1 < len(line) && line[i+1] == '\'' { + arg += line[start:i] + start = i + 1 + i++ // skip over second ' before next iteration + continue + } + // ending a quoted chunk + arg += line[start:i] + start = i + 1 + quoted = false + continue + } + // found character worth saving; make sure we're saving + if start < 0 { + start = i + } + } + return args +} + +// diff returns a formatted diff of the two texts, +// showing the entire text and the minimum line-level +// additions and removals to turn text1 into text2. +// (That is, lines only in text1 appear with a leading -, +// and lines only in text2 appear with a leading +.) +func diff(text1, text2 string) string { + if text1 != "" && !strings.HasSuffix(text1, "\n") { + text1 += "(missing final newline)" + } + lines1 := strings.Split(text1, "\n") + lines1 = lines1[:len(lines1)-1] // remove empty string after final line + if text2 != "" && !strings.HasSuffix(text2, "\n") { + text2 += "(missing final newline)" + } + lines2 := strings.Split(text2, "\n") + lines2 = lines2[:len(lines2)-1] // remove empty string after final line + + // Naive dynamic programming algorithm for edit distance. + // https://en.wikipedia.org/wiki/Wagner–Fischer_algorithm + // dist[i][j] = edit distance between lines1[:len(lines1)-i] and lines2[:len(lines2)-j] + // (The reversed indices make following the minimum cost path + // visit lines in the same order as in the text.) + dist := make([][]int, len(lines1)+1) + for i := range dist { + dist[i] = make([]int, len(lines2)+1) + if i == 0 { + for j := range dist[0] { + dist[0][j] = j + } + continue + } + for j := range dist[i] { + if j == 0 { + dist[i][0] = i + continue + } + cost := dist[i][j-1] + 1 + if cost > dist[i-1][j]+1 { + cost = dist[i-1][j] + 1 + } + if lines1[len(lines1)-i] == lines2[len(lines2)-j] { + if cost > dist[i-1][j-1] { + cost = dist[i-1][j-1] + } + } + dist[i][j] = cost + } + } + + var buf strings.Builder + i, j := len(lines1), len(lines2) + for i > 0 || j > 0 { + cost := dist[i][j] + if i > 0 && j > 0 && cost == dist[i-1][j-1] && lines1[len(lines1)-i] == lines2[len(lines2)-j] { + fmt.Fprintf(&buf, " %s\n", lines1[len(lines1)-i]) + i-- + j-- + } else if i > 0 && cost == dist[i-1][j]+1 { + fmt.Fprintf(&buf, "-%s\n", lines1[len(lines1)-i]) + i-- + } else { + fmt.Fprintf(&buf, "+%s\n", lines2[len(lines2)-j]) + j-- + } + } + return buf.String() +} + +var diffTests = []struct { + text1 string + text2 string + diff string +}{ + {"a b c", "a b d e f", "a b -c +d +e +f"}, + {"", "a b c", "+a +b +c"}, + {"a b c", "", "-a -b -c"}, + {"a b c", "d e f", "-a -b -c +d +e +f"}, + {"a b c d e f", "a b d e f", "a b -c d e f"}, + {"a b c e f", "a b c d e f", "a b c +d e f"}, +} + +func TestDiff(t *testing.T) { + for _, tt := range diffTests { + // Turn spaces into \n. + text1 := strings.Replace(tt.text1, " ", "\n", -1) + if text1 != "" { + text1 += "\n" + } + text2 := strings.Replace(tt.text2, " ", "\n", -1) + if text2 != "" { + text2 += "\n" + } + out := diff(text1, text2) + // Cut final \n, cut spaces, turn remaining \n into spaces. + out = strings.Replace(strings.Replace(strings.TrimSuffix(out, "\n"), " ", "", -1), "\n", " ", -1) + if out != tt.diff { + t.Errorf("diff(%q, %q) = %q, want %q", text1, text2, out, tt.diff) + } + } +} diff --git a/libgo/go/cmd/go/testdata/addmod.go b/libgo/go/cmd/go/testdata/addmod.go new file mode 100644 index 0000000..19850af --- /dev/null +++ b/libgo/go/cmd/go/testdata/addmod.go @@ -0,0 +1,154 @@ +// Copyright 2018 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 ignore + +// Addmod adds a module as a txtar archive to the testdata/mod directory. +// +// Usage: +// +// go run addmod.go path@version... +// +// It should only be used for very small modules - we do not want to check +// very large files into testdata/mod. +// +// It is acceptable to edit the archive afterward to remove or shorten files. +// See mod/README for more information. +// +package main + +import ( + "bytes" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + + "../internal/txtar" +) + +func usage() { + fmt.Fprintf(os.Stderr, "usage: go run addmod.go path@version...\n") + os.Exit(2) +} + +var tmpdir string + +func fatalf(format string, args ...interface{}) { + os.RemoveAll(tmpdir) + log.Fatalf(format, args...) +} + +const goCmd = "vgo" + +func main() { + flag.Usage = usage + flag.Parse() + if flag.NArg() == 0 { + usage() + } + + log.SetPrefix("addmod: ") + log.SetFlags(0) + + var err error + tmpdir, err = ioutil.TempDir("", "addmod-") + if err != nil { + log.Fatal(err) + } + + run := func(command string, args ...string) string { + cmd := exec.Command(command, args...) + cmd.Dir = tmpdir + var stderr bytes.Buffer + cmd.Stderr = &stderr + out, err := cmd.Output() + if err != nil { + fatalf("%s %s: %v\n%s", command, strings.Join(args, " "), err, stderr.Bytes()) + } + return string(out) + } + + gopath := strings.TrimSpace(run("go", "env", "GOPATH")) + if gopath == "" { + fatalf("cannot find GOPATH") + } + + exitCode := 0 + for _, arg := range flag.Args() { + if err := ioutil.WriteFile(filepath.Join(tmpdir, "go.mod"), []byte("module m\n"), 0666); err != nil { + fatalf("%v", err) + } + run(goCmd, "get", "-d", arg) + path := arg + if i := strings.Index(path, "@"); i >= 0 { + path = path[:i] + } + out := run(goCmd, "list", "-m", "-f={{.Path}} {{.Version}} {{.Dir}}", path) + f := strings.Fields(out) + if len(f) != 3 { + log.Printf("go list -m %s: unexpected output %q", arg, out) + exitCode = 1 + continue + } + path, vers, dir := f[0], f[1], f[2] + mod, err := ioutil.ReadFile(filepath.Join(gopath, "pkg/mod/cache/download", path, "@v", vers+".mod")) + if err != nil { + log.Printf("%s: %v", arg, err) + exitCode = 1 + continue + } + info, err := ioutil.ReadFile(filepath.Join(gopath, "pkg/mod/cache/download", path, "@v", vers+".info")) + if err != nil { + log.Printf("%s: %v", arg, err) + exitCode = 1 + continue + } + + a := new(txtar.Archive) + title := arg + if !strings.Contains(arg, "@") { + title += "@" + vers + } + a.Comment = []byte(fmt.Sprintf("module %s\n\n", title)) + a.Files = []txtar.File{ + {Name: ".mod", Data: mod}, + {Name: ".info", Data: info}, + } + dir = filepath.Clean(dir) + err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if !info.Mode().IsRegular() { + return nil + } + name := info.Name() + if name == "go.mod" || strings.HasSuffix(name, ".go") { + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + a.Files = append(a.Files, txtar.File{Name: strings.TrimPrefix(path, dir+string(filepath.Separator)), Data: data}) + } + return nil + }) + if err != nil { + log.Printf("%s: %v", arg, err) + exitCode = 1 + continue + } + + data := txtar.Format(a) + target := filepath.Join("mod", strings.Replace(path, "/", "_", -1)+"_"+vers+".txt") + if err := ioutil.WriteFile(target, data, 0666); err != nil { + log.Printf("%s: %v", arg, err) + exitCode = 1 + continue + } + } + os.RemoveAll(tmpdir) + os.Exit(exitCode) +} diff --git a/libgo/go/cmd/go/testdata/badmod/go.mod b/libgo/go/cmd/go/testdata/badmod/go.mod new file mode 100644 index 0000000..f7f6423 --- /dev/null +++ b/libgo/go/cmd/go/testdata/badmod/go.mod @@ -0,0 +1 @@ +module m diff --git a/libgo/go/cmd/go/testdata/badmod/x.go b/libgo/go/cmd/go/testdata/badmod/x.go new file mode 100644 index 0000000..579fb08 --- /dev/null +++ b/libgo/go/cmd/go/testdata/badmod/x.go @@ -0,0 +1,4 @@ +package x + +import _ "appengine" +import _ "nonexistent.rsc.io" // domain does not exist diff --git a/libgo/go/cmd/go/testdata/importcycle/src/selfimport/selfimport.go b/libgo/go/cmd/go/testdata/importcycle/src/selfimport/selfimport.go new file mode 100644 index 0000000..dc63c4b --- /dev/null +++ b/libgo/go/cmd/go/testdata/importcycle/src/selfimport/selfimport.go @@ -0,0 +1,3 @@ +package selfimport + +import "selfimport" diff --git a/libgo/go/cmd/go/testdata/mod/README b/libgo/go/cmd/go/testdata/mod/README new file mode 100644 index 0000000..43ddf77 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/README @@ -0,0 +1,36 @@ +This directory holds Go modules served by a Go module proxy +that runs on localhost during tests, both to make tests avoid +requiring specific network servers and also to make them +significantly faster. + +A small go get'able test module can be added here by running + + cd cmd/go/testdata + go run addmod.go path@vers + +where path and vers are the module path and version to add here. + +For interactive experimentation using this set of modules, run: + + cd cmd/go + go test -proxy=localhost:1234 & + export GOPROXY=http://localhost:1234/mod + +and then run go commands as usual. + +Modules saved to this directory should be small: a few kilobytes at most. +It is acceptable to edit the archives created by addmod.go to remove +or shorten files. It is also acceptable to write module archives by hand: +they need not be backed by some public git repo. + +Each module archive is named path_vers.txt, where slashes in path +have been replaced with underscores. The archive must contain +two files ".info" and ".mod", to be served as the info and mod files +in the proxy protocol (see https://research.swtch.com/vgo-module). +The remaining files are served as the content of the module zip file. +The path@vers prefix required of files in the zip file is added +automatically by the proxy: the files in the archive have names without +the prefix, like plain "go.mod", "x.go", and so on. + +See ../addmod.go and ../savedir.go for tools to generate txtar files, +although again it is also fine to write them by hand. diff --git a/libgo/go/cmd/go/testdata/mod/example.com_join_subpkg_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/example.com_join_subpkg_v1.0.0.txt new file mode 100644 index 0000000..1ecfa0b --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/example.com_join_subpkg_v1.0.0.txt @@ -0,0 +1,9 @@ +Written by hand. +Test case for package moved into a parent module. + +-- .mod -- +module example.com/join/subpkg +-- .info -- +{"Version": "v1.0.0"} +-- x.go -- +package subpkg diff --git a/libgo/go/cmd/go/testdata/mod/example.com_join_subpkg_v1.1.0.txt b/libgo/go/cmd/go/testdata/mod/example.com_join_subpkg_v1.1.0.txt new file mode 100644 index 0000000..9eb823a --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/example.com_join_subpkg_v1.1.0.txt @@ -0,0 +1,9 @@ +Written by hand. +Test case for package moved into a parent module. + +-- .mod -- +module example.com/join/subpkg + +require example.com/join v1.1.0 +-- .info -- +{"Version": "v1.1.0"} diff --git a/libgo/go/cmd/go/testdata/mod/example.com_join_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/example.com_join_v1.0.0.txt new file mode 100644 index 0000000..84c68b1 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/example.com_join_v1.0.0.txt @@ -0,0 +1,7 @@ +Written by hand. +Test case for package moved into a parent module. + +-- .mod -- +module example.com/join +-- .info -- +{"Version": "v1.0.0"} diff --git a/libgo/go/cmd/go/testdata/mod/example.com_join_v1.1.0.txt b/libgo/go/cmd/go/testdata/mod/example.com_join_v1.1.0.txt new file mode 100644 index 0000000..5f92036 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/example.com_join_v1.1.0.txt @@ -0,0 +1,9 @@ +Written by hand. +Test case for package moved into a parent module. + +-- .mod -- +module example.com/join +-- .info -- +{"Version": "v1.1.0"} +-- subpkg/x.go -- +package subpkg diff --git a/libgo/go/cmd/go/testdata/mod/example.com_split_subpkg_v1.1.0.txt b/libgo/go/cmd/go/testdata/mod/example.com_split_subpkg_v1.1.0.txt new file mode 100644 index 0000000..b197b66 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/example.com_split_subpkg_v1.1.0.txt @@ -0,0 +1,11 @@ +Written by hand. +Test case for getting a package that has been moved to a different module. + +-- .mod -- +module example.com/split/subpkg + +require example.com/split v1.1.0 +-- .info -- +{"Version": "v1.1.0"} +-- x.go -- +package subpkg diff --git a/libgo/go/cmd/go/testdata/mod/example.com_split_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/example.com_split_v1.0.0.txt new file mode 100644 index 0000000..b706e59 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/example.com_split_v1.0.0.txt @@ -0,0 +1,9 @@ +Written by hand. +Test case for getting a package that has been moved to a different module. + +-- .mod -- +module example.com/split +-- .info -- +{"Version": "v1.0.0"} +-- subpkg/x.go -- +package subpkg diff --git a/libgo/go/cmd/go/testdata/mod/example.com_split_v1.1.0.txt b/libgo/go/cmd/go/testdata/mod/example.com_split_v1.1.0.txt new file mode 100644 index 0000000..d38971f --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/example.com_split_v1.1.0.txt @@ -0,0 +1,9 @@ +Written by hand. +Test case for getting a package that has been moved to a different module. + +-- .mod -- +module example.com/split + +require example.com/split/subpkg v1.1.0 +-- .info -- +{"Version": "v1.1.0"} diff --git a/libgo/go/cmd/go/testdata/mod/example.com_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/example.com_v1.0.0.txt new file mode 100644 index 0000000..263287d --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/example.com_v1.0.0.txt @@ -0,0 +1,9 @@ +Written by hand. +Test case for module at root of domain. + +-- .mod -- +module example.com +-- .info -- +{"Version": "v1.0.0"} +-- x.go -- +package x diff --git a/libgo/go/cmd/go/testdata/mod/golang.org_notx_useinternal_v0.1.0.txt b/libgo/go/cmd/go/testdata/mod/golang.org_notx_useinternal_v0.1.0.txt new file mode 100644 index 0000000..0420a1a --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/golang.org_notx_useinternal_v0.1.0.txt @@ -0,0 +1,13 @@ +written by hand — attempts to use a prohibited internal package +(https://golang.org/s/go14internal) + +-- .mod -- +module golang.org/notx/useinternal +-- .info -- +{"Version":"v0.1.0","Name":"","Short":"","Time":"2018-07-25T17:24:00Z"} +-- go.mod -- +module golang.org/notx/useinternal +-- useinternal.go -- +package useinternal + +import _ "golang.org/x/internal/subtle" diff --git a/libgo/go/cmd/go/testdata/mod/golang.org_x_internal_v0.1.0.txt b/libgo/go/cmd/go/testdata/mod/golang.org_x_internal_v0.1.0.txt new file mode 100644 index 0000000..5737e95 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/golang.org_x_internal_v0.1.0.txt @@ -0,0 +1,43 @@ +written by hand — loosely derived from golang.org/x/crypto/internal/subtle, +but splitting the internal package across a module boundary + +-- .mod -- +module golang.org/x/internal +-- .info -- +{"Version":"v0.1.0","Name":"","Short":"","Time":"2018-07-25T17:24:00Z"} +-- go.mod -- +module golang.org/x/internal +-- subtle/aliasing.go -- +// Copyright 2018 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 !appengine + +// This is a tiny version of golang.org/x/crypto/internal/subtle. + +package subtle + +import "unsafe" + +func AnyOverlap(x, y []byte) bool { + return len(x) > 0 && len(y) > 0 && + uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) && + uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1])) +} +-- subtle/aliasing_appengine.go -- +// Copyright 2018 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 appengine + +package subtle + +import "reflect" + +func AnyOverlap(x, y []byte) bool { + return len(x) > 0 && len(y) > 0 && + reflect.ValueOf(&x[0]).Pointer() <= reflect.ValueOf(&y[len(y)-1]).Pointer() && + reflect.ValueOf(&y[0]).Pointer() <= reflect.ValueOf(&x[len(x)-1]).Pointer() +} diff --git a/libgo/go/cmd/go/testdata/mod/golang.org_x_text_v0.0.0-20170915032832-14c0d48ead0c.txt b/libgo/go/cmd/go/testdata/mod/golang.org_x_text_v0.0.0-20170915032832-14c0d48ead0c.txt new file mode 100644 index 0000000..f4f50cd --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/golang.org_x_text_v0.0.0-20170915032832-14c0d48ead0c.txt @@ -0,0 +1,47 @@ +written by hand - just enough to compile rsc.io/sampler, rsc.io/quote + +-- .mod -- +module golang.org/x/text +-- .info -- +{"Version":"v0.0.0-20170915032832-14c0d48ead0c","Name":"v0.0.0-20170915032832-14c0d48ead0c","Short":"14c0d48ead0c","Time":"2017-09-15T03:28:32Z"} +-- go.mod -- +module golang.org/x/text +-- unused/unused.go -- +package unused +-- language/lang.go -- +// Copyright 2018 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 is a tiny version of golang.org/x/text. + +package language + +import "strings" + +type Tag string + +func Make(s string) Tag { return Tag(s) } + +func (t Tag) String() string { return string(t) } + +func NewMatcher(tags []Tag) Matcher { return &matcher{tags} } + +type Matcher interface { + Match(...Tag) (Tag, int, int) +} + +type matcher struct { + tags []Tag +} + +func (m *matcher) Match(prefs ...Tag) (Tag, int, int) { + for _, pref := range prefs { + for _, tag := range m.tags { + if tag == pref || strings.HasPrefix(string(pref), string(tag+"-")) || strings.HasPrefix(string(tag), string(pref+"-")) { + return tag, 0, 0 + } + } + } + return m.tags[0], 0, 0 +} diff --git a/libgo/go/cmd/go/testdata/mod/golang.org_x_text_v0.3.0.txt b/libgo/go/cmd/go/testdata/mod/golang.org_x_text_v0.3.0.txt new file mode 100644 index 0000000..5561afa --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/golang.org_x_text_v0.3.0.txt @@ -0,0 +1,47 @@ +written by hand - just enough to compile rsc.io/sampler, rsc.io/quote + +-- .mod -- +module golang.org/x/text +-- .info -- +{"Version":"v0.3.0","Name":"","Short":"","Time":"2017-09-16T03:28:32Z"} +-- go.mod -- +module golang.org/x/text +-- unused/unused.go -- +package unused +-- language/lang.go -- +// Copyright 2018 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 is a tiny version of golang.org/x/text. + +package language + +import "strings" + +type Tag string + +func Make(s string) Tag { return Tag(s) } + +func (t Tag) String() string { return string(t) } + +func NewMatcher(tags []Tag) Matcher { return &matcher{tags} } + +type Matcher interface { + Match(...Tag) (Tag, int, int) +} + +type matcher struct { + tags []Tag +} + +func (m *matcher) Match(prefs ...Tag) (Tag, int, int) { + for _, pref := range prefs { + for _, tag := range m.tags { + if tag == pref || strings.HasPrefix(string(pref), string(tag+"-")) || strings.HasPrefix(string(tag), string(pref+"-")) { + return tag, 0, 0 + } + } + } + return m.tags[0], 0, 0 +} diff --git a/libgo/go/cmd/go/testdata/mod/golang.org_x_useinternal_v0.1.0.txt b/libgo/go/cmd/go/testdata/mod/golang.org_x_useinternal_v0.1.0.txt new file mode 100644 index 0000000..3fcba44 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/golang.org_x_useinternal_v0.1.0.txt @@ -0,0 +1,13 @@ +written by hand — uses an internal package from another module +(https://golang.org/s/go14internal) + +-- .mod -- +module golang.org/x/useinternal +-- .info -- +{"Version":"v0.1.0","Name":"","Short":"","Time":"2018-07-25T17:24:00Z"} +-- go.mod -- +module golang.org/x/useinternal +-- useinternal.go -- +package useinternal + +import _ "golang.org/x/internal/subtle" diff --git a/libgo/go/cmd/go/testdata/mod/gopkg.in_dummy.v2-unstable_v2.0.0.txt b/libgo/go/cmd/go/testdata/mod/gopkg.in_dummy.v2-unstable_v2.0.0.txt new file mode 100644 index 0000000..f174159 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/gopkg.in_dummy.v2-unstable_v2.0.0.txt @@ -0,0 +1,9 @@ +gopkg.in/dummy.v2-unstable v2.0.0 +written by hand + +-- .mod -- +module gopkg.in/dummy.v2-unstable +-- .info -- +{"Version":"v2.0.0"} +-- dummy.go -- +package dummy diff --git a/libgo/go/cmd/go/testdata/mod/research.swtch.com_vgo-tour_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/research.swtch.com_vgo-tour_v1.0.0.txt new file mode 100644 index 0000000..0f060dc --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/research.swtch.com_vgo-tour_v1.0.0.txt @@ -0,0 +1,23 @@ +research.swtch.com/vgo-tour@v1.0.0 + +-- .mod -- +module "research.swtch.com/vgo-tour" +-- .info -- +{"Version":"v1.0.0","Name":"84de74b35823c1e49634f2262f1a58cfc951ebae","Short":"84de74b35823","Time":"2018-02-20T00:04:00Z"} +-- go.mod -- +module "research.swtch.com/vgo-tour" +-- hello.go -- +// Copyright 2018 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 main + +import ( + "fmt" + "rsc.io/quote" +) + +func main() { + fmt.Println(quote.Hello()) +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_!c!g!o_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_!c!g!o_v1.0.0.txt new file mode 100644 index 0000000..6276147 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_!c!g!o_v1.0.0.txt @@ -0,0 +1,19 @@ +rsc.io/CGO v1.0.0 + +-- .mod -- +module rsc.io/CGO +-- .info -- +{"Version":"v1.0.0","Name":"","Short":"","Time":"2018-08-01T18:23:45Z"} +-- go.mod -- +module rsc.io/CGO +-- cgo.go -- +// Copyright 2018 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 CGO + +// #cgo CFLAGS: -I${SRCDIR} +import "C" + +var V = 0 diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_!q!u!o!t!e_v1.5.2.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_!q!u!o!t!e_v1.5.2.txt new file mode 100644 index 0000000..21185c3 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_!q!u!o!t!e_v1.5.2.txt @@ -0,0 +1,88 @@ +rsc.io/QUOTE v1.5.2 + +-- .mod -- +module rsc.io/QUOTE + +require rsc.io/quote v1.5.2 +-- .info -- +{"Version":"v1.5.2","Name":"","Short":"","Time":"2018-07-15T16:25:34Z"} +-- go.mod -- +module rsc.io/QUOTE + +require rsc.io/quote v1.5.2 +-- QUOTE/quote.go -- +// Copyright 2018 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 QUOTE COLLECTS LOUD SAYINGS. +package QUOTE + +import ( + "strings" + + "rsc.io/quote" +) + +// HELLO RETURNS A GREETING. +func HELLO() string { + return strings.ToUpper(quote.Hello()) +} + +// GLASS RETURNS A USEFUL PHRASE FOR WORLD TRAVELERS. +func GLASS() string { + return strings.ToUpper(quote.GLASS()) +} + +// GO RETURNS A GO PROVERB. +func GO() string { + return strings.ToUpper(quote.GO()) +} + +// OPT RETURNS AN OPTIMIZATION TRUTH. +func OPT() string { + return strings.ToUpper(quote.OPT()) +} +-- QUOTE/quote_test.go -- +// Copyright 2018 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 QUOTE + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHELLO(t *testing.T) { + hello := "HELLO, WORLD" + if out := HELLO(); out != hello { + t.Errorf("HELLO() = %q, want %q", out, hello) + } +} + +func TestGLASS(t *testing.T) { + glass := "I CAN EAT GLASS AND IT DOESN'T HURT ME." + if out := GLASS(); out != glass { + t.Errorf("GLASS() = %q, want %q", out, glass) + } +} + +func TestGO(t *testing.T) { + go1 := "DON'T COMMUNICATE BY SHARING MEMORY, SHARE MEMORY BY COMMUNICATING." + if out := GO(); out != go1 { + t.Errorf("GO() = %q, want %q", out, go1) + } +} + +func TestOPT(t *testing.T) { + opt := "IF A PROGRAM IS TOO SLOW, IT MUST HAVE A LOOP." + if out := OPT(); out != opt { + t.Errorf("OPT() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_!q!u!o!t!e_v1.5.3-!p!r!e.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_!q!u!o!t!e_v1.5.3-!p!r!e.txt new file mode 100644 index 0000000..54bac2d --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_!q!u!o!t!e_v1.5.3-!p!r!e.txt @@ -0,0 +1,88 @@ +rsc.io/QUOTE v1.5.3-PRE (sigh) + +-- .mod -- +module rsc.io/QUOTE + +require rsc.io/quote v1.5.2 +-- .info -- +{"Version":"v1.5.3-PRE","Name":"","Short":"","Time":"2018-07-15T16:25:34Z"} +-- go.mod -- +module rsc.io/QUOTE + +require rsc.io/quote v1.5.2 +-- QUOTE/quote.go -- +// Copyright 2018 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 QUOTE COLLECTS LOUD SAYINGS. +package QUOTE + +import ( + "strings" + + "rsc.io/quote" +) + +// HELLO RETURNS A GREETING. +func HELLO() string { + return strings.ToUpper(quote.Hello()) +} + +// GLASS RETURNS A USEFUL PHRASE FOR WORLD TRAVELERS. +func GLASS() string { + return strings.ToUpper(quote.GLASS()) +} + +// GO RETURNS A GO PROVERB. +func GO() string { + return strings.ToUpper(quote.GO()) +} + +// OPT RETURNS AN OPTIMIZATION TRUTH. +func OPT() string { + return strings.ToUpper(quote.OPT()) +} +-- QUOTE/quote_test.go -- +// Copyright 2018 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 QUOTE + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHELLO(t *testing.T) { + hello := "HELLO, WORLD" + if out := HELLO(); out != hello { + t.Errorf("HELLO() = %q, want %q", out, hello) + } +} + +func TestGLASS(t *testing.T) { + glass := "I CAN EAT GLASS AND IT DOESN'T HURT ME." + if out := GLASS(); out != glass { + t.Errorf("GLASS() = %q, want %q", out, glass) + } +} + +func TestGO(t *testing.T) { + go1 := "DON'T COMMUNICATE BY SHARING MEMORY, SHARE MEMORY BY COMMUNICATING." + if out := GO(); out != go1 { + t.Errorf("GO() = %q, want %q", out, go1) + } +} + +func TestOPT(t *testing.T) { + opt := "IF A PROGRAM IS TOO SLOW, IT MUST HAVE A LOOP." + if out := OPT(); out != opt { + t.Errorf("OPT() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_badfile1_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_badfile1_v1.0.0.txt new file mode 100644 index 0000000..9d23e7d --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_badfile1_v1.0.0.txt @@ -0,0 +1,14 @@ +rsc.io/badfile1 v1.0.0 +written by hand +this is part of the badfile test but is a valid zip file. + +-- .mod -- +module rsc.io/badfile1 +-- .info -- +{"Version":"v1.0.0"} +-- go.mod -- +module rsc.io/badfile1 +-- α.go -- +package α +-- .gitignore -- +-- x/y/z/.gitignore -- diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_badfile2_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_badfile2_v1.0.0.txt new file mode 100644 index 0000000..58e1e1c --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_badfile2_v1.0.0.txt @@ -0,0 +1,12 @@ +rsc.io/badfile1 v1.0.0 +written by hand + +-- .mod -- +module rsc.io/badfile2 +-- .info -- +{"Version":"v1.0.0"} +-- go.mod -- +module rsc.io/badfile2 +-- ☺.go -- +package smiley + diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_badfile3_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_badfile3_v1.0.0.txt new file mode 100644 index 0000000..a008448 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_badfile3_v1.0.0.txt @@ -0,0 +1,12 @@ +rsc.io/badfile3 v1.0.0 +written by hand + +-- .mod -- +module rsc.io/badfile3 +-- .info -- +{"Version":"v1.0.0"} +-- go.mod -- +module rsc.io/badfile3 +-- x?y.go -- +package x + diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_badfile4_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_badfile4_v1.0.0.txt new file mode 100644 index 0000000..e28844d --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_badfile4_v1.0.0.txt @@ -0,0 +1,15 @@ +rsc.io/badfile4 v1.0.0 +written by hand + +-- .mod -- +module rsc.io/badfile4 +-- .info -- +{"Version":"v1.0.0"} +-- go.mod -- +module rsc.io/badfile4 +-- x/Y.go -- +package x +-- x/y.go -- +package x + + diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_badfile5_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_badfile5_v1.0.0.txt new file mode 100644 index 0000000..3c7903a --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_badfile5_v1.0.0.txt @@ -0,0 +1,13 @@ +rsc.io/badfile5 v1.0.0 +written by hand + +-- .mod -- +module rsc.io/badfile5 +-- .info -- +{"Version":"v1.0.0"} +-- go.mod -- +module rsc.io/badfile5 +-- x/y/z/w.go -- +package z +-- x/Y/zz/ww.go -- +package zz diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_badmod_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_badmod_v1.0.0.txt new file mode 100644 index 0000000..993ceb7 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_badmod_v1.0.0.txt @@ -0,0 +1,11 @@ +rsc.io/badmod v1.0.0 +written by hand + +-- .mod -- +module rsc.io/badmod +hello world +-- .info -- +{"Version":"v1.0.0"} +-- x.go -- +package x + diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_breaker_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_breaker_v1.0.0.txt new file mode 100644 index 0000000..a103e3f --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_breaker_v1.0.0.txt @@ -0,0 +1,11 @@ +rsc.io/breaker v1.0.0 +written by hand + +-- .mod -- +module rsc.io/breaker +-- .info -- +{"Version":"v1.0.0"} +-- breaker.go -- +package breaker + +const X = 1 diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_breaker_v2.0.0+incompatible.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_breaker_v2.0.0+incompatible.txt new file mode 100644 index 0000000..59d8bac --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_breaker_v2.0.0+incompatible.txt @@ -0,0 +1,11 @@ +rsc.io/breaker v2.0.0+incompatible +written by hand + +-- .mod -- +module rsc.io/breaker +-- .info -- +{"Version":"v2.0.0+incompatible", "Name": "7307b307f4f0dde421900f8e5126fadac1e13aed", "Short": "7307b307f4f0"} +-- breaker.go -- +package breaker + +const XX = 2 diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_breaker_v2.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_breaker_v2.0.0.txt new file mode 100644 index 0000000..59d8bac --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_breaker_v2.0.0.txt @@ -0,0 +1,11 @@ +rsc.io/breaker v2.0.0+incompatible +written by hand + +-- .mod -- +module rsc.io/breaker +-- .info -- +{"Version":"v2.0.0+incompatible", "Name": "7307b307f4f0dde421900f8e5126fadac1e13aed", "Short": "7307b307f4f0"} +-- breaker.go -- +package breaker + +const XX = 2 diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_fortune_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_fortune_v1.0.0.txt new file mode 100644 index 0000000..d8a71f3 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_fortune_v1.0.0.txt @@ -0,0 +1,15 @@ +rsc.io/fortune v1.0.0 +written by hand + +-- .mod -- +module rsc.io/fortune +-- .info -- +{"Version":"v1.0.0"} +-- fortune.go -- +package main + +import "rsc.io/quote" + +func main() { + println(quote.Hello()) +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_fortune_v2_v2.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_fortune_v2_v2.0.0.txt new file mode 100644 index 0000000..cfa91f0 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_fortune_v2_v2.0.0.txt @@ -0,0 +1,15 @@ +rsc.io/fortune v2.0.0 +written by hand + +-- .mod -- +module rsc.io/fortune/v2 +-- .info -- +{"Version":"v2.0.0"} +-- fortune.go -- +package main + +import "rsc.io/quote" + +func main() { + println(quote.Hello()) +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180214005133-e7a685a342c0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180214005133-e7a685a342c0.txt new file mode 100644 index 0000000..8ae173e --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180214005133-e7a685a342c0.txt @@ -0,0 +1,60 @@ +rsc.io/quote@e7a685a342 + +-- .mod -- +module "rsc.io/quote" +-- .info -- +{"Version":"v0.0.0-20180214005133-e7a685a342c0","Name":"e7a685a342c001acc3eb7f5eafa82980480042c7","Short":"e7a685a342c0","Time":"2018-02-14T00:51:33Z"} +-- go.mod -- +module "rsc.io/quote" +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +// Hello returns a greeting. +func Hello() string { + return "Hello, world." +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func Go() string { + return "Don't communicate by sharing memory, share memory by communicating." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import "testing" + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory. Share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180214005840-23179ee8a569.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180214005840-23179ee8a569.txt new file mode 100644 index 0000000..bc626ba --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180214005840-23179ee8a569.txt @@ -0,0 +1,86 @@ +rsc.io/quote@v0.0.0-20180214005840-23179ee8a569 + +-- .mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- .info -- +{"Version":"v0.0.0-20180214005840-23179ee8a569","Name":"23179ee8a569bb05d896ae05c6503ec69a19f99f","Short":"23179ee8a569","Time":"2018-02-14T00:58:40Z"} +-- go.mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/sampler" + +// Hello returns a greeting. +func Hello() string { + return sampler.Hello() +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func Go() string { + return "Don't communicate by sharing memory, share memory by communicating." +} + +// Opt returns an optimization truth. +func Opt() string { + // Wisdom from ken. + return "If a program is too slow, it must have a loop." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180628003336-dd9747d19b04.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180628003336-dd9747d19b04.txt new file mode 100644 index 0000000..bbc8097 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180628003336-dd9747d19b04.txt @@ -0,0 +1,100 @@ +rsc.io/quote@dd9747d + +-- .mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- .info -- +{"Version":"v0.0.0-20180628003336-dd9747d19b04","Name":"dd9747d19b041365fbddf0399ddba6bff5eb1b3e","Short":"dd9747d19b04","Time":"2018-06-28T00:33:36Z"} +-- buggy/buggy_test.go -- +// Copyright 2018 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 buggy + +import "testing" + +func Test(t *testing.T) { + t.Fatal("buggy!") +} +-- go.mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/sampler" + +AN EVEN WORSE CHANGE! + +// Hello returns a greeting. +func Hello() string { + return sampler.Hello() +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func Go() string { + return "Don't communicate by sharing memory, share memory by communicating." +} + +// Opt returns an optimization truth. +func Opt() string { + // Wisdom from ken. + return "If a program is too slow, it must have a loop." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709153244-fd906ed3b100.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709153244-fd906ed3b100.txt new file mode 100644 index 0000000..e461ed4 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709153244-fd906ed3b100.txt @@ -0,0 +1,86 @@ +rsc.io/quote@v2.0.0 + +-- .mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- .info -- +{"Version":"v0.0.0-20180709153244-fd906ed3b100","Name":"fd906ed3b100e47181ffa9ec36d82294525c9109","Short":"fd906ed3b100","Time":"2018-07-09T15:32:44Z"} +-- go.mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/sampler" + +// Hello returns a greeting. +func HelloV2() string { + return sampler.Hello() +} + +// Glass returns a useful phrase for world travelers. +func GlassV2() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func GoV2() string { + return "Don't communicate by sharing memory, share memory by communicating." +} + +// Opt returns an optimization truth. +func OptV2() string { + // Wisdom from ken. + return "If a program is too slow, it must have a loop." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709160352-0d003b9c4bfa.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709160352-0d003b9c4bfa.txt new file mode 100644 index 0000000..c1d511f --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709160352-0d003b9c4bfa.txt @@ -0,0 +1,98 @@ +rsc.io/quote@v0.0.0-20180709160352-0d003b9c4bfa + +-- .mod -- +module rsc.io/quote + +require rsc.io/sampler v1.3.0 +-- .info -- +{"Version":"v0.0.0-20180709160352-0d003b9c4bfa","Name":"0d003b9c4bfac881641be8eb1598b782a467a97f","Short":"0d003b9c4bfa","Time":"2018-07-09T16:03:52Z"} +-- buggy/buggy_test.go -- +// Copyright 2018 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 buggy + +import "testing" + +func Test(t *testing.T) { + t.Fatal("buggy!") +} +-- go.mod -- +module rsc.io/quote + +require rsc.io/sampler v1.3.0 +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/quote/v2" + +// Hello returns a greeting. +func Hello() string { + return quote.HelloV2() +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return quote.GlassV2() +} + +// Go returns a Go proverb. +func Go() string { + return quote.GoV2() +} + +// Opt returns an optimization truth. +func Opt() string { + // Wisdom from ken. + return quote.OptV2() +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162749-b44a0b17b2d1.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162749-b44a0b17b2d1.txt new file mode 100644 index 0000000..f7f794d --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162749-b44a0b17b2d1.txt @@ -0,0 +1,104 @@ +rsc.io/quote@v0.0.0-20180709162749-b44a0b17b2d1 + +-- .mod -- +module rsc.io/quote + +require ( + rsc.io/quote/v2 v2.0.1 + rsc.io/sampler v1.3.0 +) +-- .info -- +{"Version":"v0.0.0-20180709162749-b44a0b17b2d1","Name":"b44a0b17b2d1fe4c98a8d0e7a68c9bf9e762799a","Short":"b44a0b17b2d1","Time":"2018-07-09T16:27:49Z"} +-- buggy/buggy_test.go -- +// Copyright 2018 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 buggy + +import "testing" + +func Test(t *testing.T) { + t.Fatal("buggy!") +} +-- go.mod -- +module rsc.io/quote + +require ( + rsc.io/quote/v2 v2.0.1 + rsc.io/sampler v1.3.0 +) +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/quote/v2" + +// Hello returns a greeting. +func Hello() string { + return quote.HelloV2() +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return quote.GlassV2() +} + +// Go returns a Go proverb. +func Go() string { + return quote.GoV2() +} + +// Opt returns an optimization truth. +func Opt() string { + // Wisdom from ken. + return quote.OptV2() +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162816-fe488b867524.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162816-fe488b867524.txt new file mode 100644 index 0000000..2d5d8b4e --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162816-fe488b867524.txt @@ -0,0 +1,104 @@ +rsc.io/quote@v0.0.0-20180709162816-fe488b867524 + +-- .mod -- +module rsc.io/quote + +require ( + rsc.io/quote/v2 v2.0.1 + rsc.io/sampler v1.3.0 +) +-- .info -- +{"Version":"v0.0.0-20180709162816-fe488b867524","Name":"fe488b867524806e861c3f4f43ae6946a42ca3f1","Short":"fe488b867524","Time":"2018-07-09T16:28:16Z"} +-- buggy/buggy_test.go -- +// Copyright 2018 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 buggy + +import "testing" + +func Test(t *testing.T) { + t.Fatal("buggy!") +} +-- go.mod -- +module rsc.io/quote + +require ( + rsc.io/quote/v2 v2.0.1 + rsc.io/sampler v1.3.0 +) +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/quote/v2" + +// Hello returns a greeting. +func Hello() string { + return quote.HelloV2() +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return quote.GlassV2() +} + +// Go returns a Go proverb. +func Go() string { + return quote.GoV2() +} + +// Opt returns an optimization truth. +func Opt() string { + // Wisdom from ken. + return quote.OptV2() +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162918-a91498bed0a7.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162918-a91498bed0a7.txt new file mode 100644 index 0000000..853a8c2 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162918-a91498bed0a7.txt @@ -0,0 +1,98 @@ +rsc.io/quote@v0.0.0-20180709162918-a91498bed0a7 + +-- .mod -- +module rsc.io/quote + +require rsc.io/sampler v1.3.0 +-- .info -- +{"Version":"v0.0.0-20180709162918-a91498bed0a7","Name":"a91498bed0a73d4bb9c1fb2597925f7883bc40a7","Short":"a91498bed0a7","Time":"2018-07-09T16:29:18Z"} +-- buggy/buggy_test.go -- +// Copyright 2018 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 buggy + +import "testing" + +func Test(t *testing.T) { + t.Fatal("buggy!") +} +-- go.mod -- +module rsc.io/quote + +require rsc.io/sampler v1.3.0 +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/quote/v3" + +// Hello returns a greeting. +func Hello() string { + return quote.HelloV3() +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return quote.GlassV3() +} + +// Go returns a Go proverb. +func Go() string { + return quote.GoV3() +} + +// Opt returns an optimization truth. +func Opt() string { + // Wisdom from ken. + return quote.OptV3() +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180710144737-5d9f230bcfba.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180710144737-5d9f230bcfba.txt new file mode 100644 index 0000000..2ebeac3 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180710144737-5d9f230bcfba.txt @@ -0,0 +1,104 @@ +rsc.io/quote@v0.0.0-20180710144737-5d9f230bcfba + +-- .mod -- +module rsc.io/quote + +require ( + rsc.io/quote/v3 v3.0.0 + rsc.io/sampler v1.3.0 +) +-- .info -- +{"Version":"v0.0.0-20180710144737-5d9f230bcfba","Name":"5d9f230bcfbae514bb6c2215694c2ce7273fc604","Short":"5d9f230bcfba","Time":"2018-07-10T14:47:37Z"} +-- buggy/buggy_test.go -- +// Copyright 2018 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 buggy + +import "testing" + +func Test(t *testing.T) { + t.Fatal("buggy!") +} +-- go.mod -- +module rsc.io/quote + +require ( + rsc.io/quote/v3 v3.0.0 + rsc.io/sampler v1.3.0 +) +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/quote/v3" + +// Hello returns a greeting. +func Hello() string { + return quote.HelloV3() +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return quote.GlassV3() +} + +// Go returns a Go proverb. +func Go() string { + return quote.GoV3() +} + +// Opt returns an optimization truth. +func Opt() string { + // Wisdom from ken. + return quote.OptV3() +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.0.0.txt new file mode 100644 index 0000000..9a07937 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.0.0.txt @@ -0,0 +1,35 @@ +rsc.io/quote@v1.0.0 + +-- .mod -- +module "rsc.io/quote" +-- .info -- +{"Version":"v1.0.0","Name":"f488df80bcdbd3e5bafdc24ad7d1e79e83edd7e6","Short":"f488df80bcdb","Time":"2018-02-14T00:45:20Z"} +-- go.mod -- +module "rsc.io/quote" +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +// Hello returns a greeting. +func Hello() string { + return "Hello, world." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import "testing" + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.1.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.1.0.txt new file mode 100644 index 0000000..0c41605 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.1.0.txt @@ -0,0 +1,48 @@ +rsc.io/quote@v1.1.0 + +-- .mod -- +module "rsc.io/quote" +-- .info -- +{"Version":"v1.1.0","Name":"cfd7145f43f92a8d56b4a3dd603795a3291381a9","Short":"cfd7145f43f9","Time":"2018-02-14T00:46:44Z"} +-- go.mod -- +module "rsc.io/quote" +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +// Hello returns a greeting. +func Hello() string { + return "Hello, world." +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import "testing" + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.2.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.2.0.txt new file mode 100644 index 0000000..e714f0b --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.2.0.txt @@ -0,0 +1,61 @@ +rsc.io/quote@v1.2.0 + +-- .mod -- +module "rsc.io/quote" +-- .info -- +{"Version":"v1.2.0","Name":"d8a3de91045c932a1c71e545308fe97571d6d65c","Short":"d8a3de91045c","Time":"2018-02-14T00:47:51Z"} +-- go.mod -- +module "rsc.io/quote" +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +// Hello returns a greeting. +func Hello() string { + return "Hello, world." +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func Go() string { + return "Don't communicate by sharing memory, share memory by communicating." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import "testing" + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +// Go returns a Go proverb. +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory. Share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.2.1.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.2.1.txt new file mode 100644 index 0000000..89d5191 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.2.1.txt @@ -0,0 +1,60 @@ +rsc.io/quote@v1.2.1 + +-- .mod -- +module "rsc.io/quote" +-- .info -- +{"Version":"v1.2.1","Name":"5c1f03b64ab7aa958798a569a31924655dc41e76","Short":"5c1f03b64ab7","Time":"2018-02-14T00:54:20Z"} +-- go.mod -- +module "rsc.io/quote" +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +// Hello returns a greeting. +func Hello() string { + return "Hello, world." +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func Go() string { + return "Don't communicate by sharing memory, share memory by communicating." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import "testing" + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.3.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.3.0.txt new file mode 100644 index 0000000..d62766c --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.3.0.txt @@ -0,0 +1,73 @@ +rsc.io/quote@v1.3.0 + +-- .mod -- +module "rsc.io/quote" +-- .info -- +{"Version":"v1.3.0","Name":"84de74b35823c1e49634f2262f1a58cfc951ebae","Short":"84de74b35823","Time":"2018-02-14T00:54:53Z"} +-- go.mod -- +module "rsc.io/quote" +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +// Hello returns a greeting. +func Hello() string { + return "Hello, world." +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func Go() string { + return "Don't communicate by sharing memory, share memory by communicating." +} + +// Opt returns an optimization truth. +func Opt() string { + // Wisdom from ken. + return "If a program is too slow, it must have a loop." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import "testing" + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.4.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.4.0.txt new file mode 100644 index 0000000..698ff8d --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.4.0.txt @@ -0,0 +1,79 @@ +rsc.io/quote@v1.4.0 + +-- .mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.0.0 +-- .info -- +{"Version":"v1.4.0","Name":"19e8b977bd2f437798c2cc2dcfe8a1c0f169481b","Short":"19e8b977bd2f","Time":"2018-02-14T00:56:05Z"} +-- go.mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.0.0 +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/sampler" + +// Hello returns a greeting. +func Hello() string { + return sampler.Hello() +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func Go() string { + return "Don't communicate by sharing memory, share memory by communicating." +} + +// Opt returns an optimization truth. +func Opt() string { + // Wisdom from ken. + return "If a program is too slow, it must have a loop." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import "testing" + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.5.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.5.0.txt new file mode 100644 index 0000000..e7fcdbc --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.5.0.txt @@ -0,0 +1,79 @@ +rsc.io/quote@v1.5.0 + +-- .mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- .info -- +{"Version":"v1.5.0","Name":"3ba1e30dc83bd52c990132b9dfb1688a9d22de13","Short":"3ba1e30dc83b","Time":"2018-02-14T00:58:15Z"} +-- go.mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/sampler" + +// Hello returns a greeting. +func Hello() string { + return sampler.Hello() +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func Go() string { + return "Don't communicate by sharing memory, share memory by communicating." +} + +// Opt returns an optimization truth. +func Opt() string { + // Wisdom from ken. + return "If a program is too slow, it must have a loop." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import "testing" + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.5.1.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.5.1.txt new file mode 100644 index 0000000..eed051b --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.5.1.txt @@ -0,0 +1,86 @@ +rsc.io/quote@23179ee8a569 + +-- .mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- .info -- +{"Version":"v1.5.1","Name":"23179ee8a569bb05d896ae05c6503ec69a19f99f","Short":"23179ee8a569","Time":"2018-02-14T00:58:40Z"} +-- go.mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/sampler" + +// Hello returns a greeting. +func Hello() string { + return sampler.Hello() +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func Go() string { + return "Don't communicate by sharing memory, share memory by communicating." +} + +// Opt returns an optimization truth. +func Opt() string { + // Wisdom from ken. + return "If a program is too slow, it must have a loop." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.5.2.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.5.2.txt new file mode 100644 index 0000000..8671f6f --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.5.2.txt @@ -0,0 +1,98 @@ +rsc.io/quote@v1.5.2 + +-- .mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- .info -- +{"Version":"v1.5.2","Name":"c4d4236f92427c64bfbcf1cc3f8142ab18f30b22","Short":"c4d4236f9242","Time":"2018-02-14T15:44:20Z"} +-- buggy/buggy_test.go -- +// Copyright 2018 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 buggy + +import "testing" + +func Test(t *testing.T) { + t.Fatal("buggy!") +} +-- go.mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/sampler" + +// Hello returns a greeting. +func Hello() string { + return sampler.Hello() +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func Go() string { + return "Don't communicate by sharing memory, share memory by communicating." +} + +// Opt returns an optimization truth. +func Opt() string { + // Wisdom from ken. + return "If a program is too slow, it must have a loop." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.5.3-pre1.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.5.3-pre1.txt new file mode 100644 index 0000000..212ef13 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v1.5.3-pre1.txt @@ -0,0 +1,100 @@ +rsc.io/quote@v1.5.3-pre1 + +-- .mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- .info -- +{"Version":"v1.5.3-pre1","Name":"2473dfd877c95382420e47686aa9076bf58c79e0","Short":"2473dfd877c9","Time":"2018-06-28T00:32:53Z"} +-- buggy/buggy_test.go -- +// Copyright 2018 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 buggy + +import "testing" + +func Test(t *testing.T) { + t.Fatal("buggy!") +} +-- go.mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/sampler" + +// A CHANGE! + +// Hello returns a greeting. +func Hello() string { + return sampler.Hello() +} + +// Glass returns a useful phrase for world travelers. +func Glass() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func Go() string { + return "Don't communicate by sharing memory, share memory by communicating." +} + +// Opt returns an optimization truth. +func Opt() string { + // Wisdom from ken. + return "If a program is too slow, it must have a loop." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v2.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v2.0.0.txt new file mode 100644 index 0000000..e461ed4 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v2.0.0.txt @@ -0,0 +1,86 @@ +rsc.io/quote@v2.0.0 + +-- .mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- .info -- +{"Version":"v0.0.0-20180709153244-fd906ed3b100","Name":"fd906ed3b100e47181ffa9ec36d82294525c9109","Short":"fd906ed3b100","Time":"2018-07-09T15:32:44Z"} +-- go.mod -- +module "rsc.io/quote" + +require "rsc.io/sampler" v1.3.0 +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/sampler" + +// Hello returns a greeting. +func HelloV2() string { + return sampler.Hello() +} + +// Glass returns a useful phrase for world travelers. +func GlassV2() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func GoV2() string { + return "Don't communicate by sharing memory, share memory by communicating." +} + +// Opt returns an optimization truth. +func OptV2() string { + // Wisdom from ken. + return "If a program is too slow, it must have a loop." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v2_v2.0.1.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v2_v2.0.1.txt new file mode 100644 index 0000000..d51128c --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v2_v2.0.1.txt @@ -0,0 +1,86 @@ +rsc.io/quote/v2@v2.0.1 + +-- .mod -- +module rsc.io/quote/v2 + +require rsc.io/sampler v1.3.0 +-- .info -- +{"Version":"v2.0.1","Name":"754f68430672776c84704e2d10209a6ec700cd64","Short":"754f68430672","Time":"2018-07-09T16:25:34Z"} +-- go.mod -- +module rsc.io/quote/v2 + +require rsc.io/sampler v1.3.0 +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/sampler" + +// Hello returns a greeting. +func HelloV2() string { + return sampler.Hello() +} + +// Glass returns a useful phrase for world travelers. +func GlassV2() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func GoV2() string { + return "Don't communicate by sharing memory, share memory by communicating." +} + +// Opt returns an optimization truth. +func OptV2() string { + // Wisdom from ken. + return "If a program is too slow, it must have a loop." +} +-- quote_test.go -- +// Copyright 2018 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 quote + +import ( + "os" + "testing" +) + +func init() { + os.Setenv("LC_ALL", "en") +} + +func TestHello(t *testing.T) { + hello := "Hello, world." + if out := Hello(); out != hello { + t.Errorf("Hello() = %q, want %q", out, hello) + } +} + +func TestGlass(t *testing.T) { + glass := "I can eat glass and it doesn't hurt me." + if out := Glass(); out != glass { + t.Errorf("Glass() = %q, want %q", out, glass) + } +} + +func TestGo(t *testing.T) { + go1 := "Don't communicate by sharing memory, share memory by communicating." + if out := Go(); out != go1 { + t.Errorf("Go() = %q, want %q", out, go1) + } +} + +func TestOpt(t *testing.T) { + opt := "If a program is too slow, it must have a loop." + if out := Opt(); out != opt { + t.Errorf("Opt() = %q, want %q", out, opt) + } +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v3_v3.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v3_v3.0.0.txt new file mode 100644 index 0000000..0afe1f0 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_quote_v3_v3.0.0.txt @@ -0,0 +1,45 @@ +rsc.io/quote/v3@v3.0.0 + +-- .mod -- +module rsc.io/quote/v3 + +require rsc.io/sampler v1.3.0 + +-- .info -- +{"Version":"v3.0.0","Name":"d88915d7e77ed0fd35d0a022a2f244e2202fd8c8","Short":"d88915d7e77e","Time":"2018-07-09T15:34:46Z"} +-- go.mod -- +module rsc.io/quote/v3 + +require rsc.io/sampler v1.3.0 + +-- quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote // import "rsc.io/quote" + +import "rsc.io/sampler" + +// Hello returns a greeting. +func HelloV3() string { + return sampler.Hello() +} + +// Glass returns a useful phrase for world travelers. +func GlassV3() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a Go proverb. +func GoV3() string { + return "Don't communicate by sharing memory, share memory by communicating." +} + +// Opt returns an optimization truth. +func OptV3() string { + // Wisdom from ken. + return "If a program is too slow, it must have a loop." +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.0.0.txt new file mode 100644 index 0000000..c4b6a71 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.0.0.txt @@ -0,0 +1,20 @@ +rsc.io/sampler@v1.0.0 + +-- .mod -- +module "rsc.io/sampler" +-- .info -- +{"Version":"v1.0.0","Name":"60bef405c52117ad21d2adb10872b95cf17f8fca","Short":"60bef405c521","Time":"2018-02-13T18:05:54Z"} +-- go.mod -- +module "rsc.io/sampler" +-- sampler.go -- +// Copyright 2018 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 sampler shows simple texts. +package sampler // import "rsc.io/sampler" + +// Hello returns a greeting. +func Hello() string { + return "Hello, world." +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.2.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.2.0.txt new file mode 100644 index 0000000..98c35fa --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.2.0.txt @@ -0,0 +1,138 @@ +rsc.io/sampler@v1.2.0 + +-- .mod -- +module "rsc.io/sampler" + +require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c +-- .info -- +{"Version":"v1.2.0","Name":"25f24110b153246056eccc14a3a4cd81afaff586","Short":"25f24110b153","Time":"2018-02-13T18:13:45Z"} +-- go.mod -- +module "rsc.io/sampler" + +require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c +-- hello.go -- +// Copyright 2018 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. + +// Translations by Google Translate. + +package sampler + +var hello = newText(` + +English: en: Hello, world. +French: fr: Bonjour le monde. +Spanish: es: Hola Mundo. + +`) +-- hello_test.go -- +// Copyright 2018 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 sampler + +import ( + "testing" + + "golang.org/x/text/language" +) + +var helloTests = []struct { + prefs []language.Tag + text string +}{ + { + []language.Tag{language.Make("en-US"), language.Make("fr")}, + "Hello, world.", + }, + { + []language.Tag{language.Make("fr"), language.Make("en-US")}, + "Bonjour la monde.", + }, +} + +func TestHello(t *testing.T) { + for _, tt := range helloTests { + text := Hello(tt.prefs...) + if text != tt.text { + t.Errorf("Hello(%v) = %q, want %q", tt.prefs, text, tt.text) + } + } +} +-- sampler.go -- +// Copyright 2018 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 sampler shows simple texts. +package sampler // import "rsc.io/sampler" + +import ( + "os" + "strings" + + "golang.org/x/text/language" +) + +// DefaultUserPrefs returns the default user language preferences. +// It consults the $LC_ALL, $LC_MESSAGES, and $LANG environment +// variables, in that order. +func DefaultUserPrefs() []language.Tag { + var prefs []language.Tag + for _, k := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} { + if env := os.Getenv(k); env != "" { + prefs = append(prefs, language.Make(env)) + } + } + return prefs +} + +// Hello returns a localized greeting. +// If no prefs are given, Hello uses DefaultUserPrefs. +func Hello(prefs ...language.Tag) string { + if len(prefs) == 0 { + prefs = DefaultUserPrefs() + } + return hello.find(prefs) +} + +// A text is a localized text. +type text struct { + byTag map[string]string + matcher language.Matcher +} + +// newText creates a new localized text, given a list of translations. +func newText(s string) *text { + t := &text{ + byTag: make(map[string]string), + } + var tags []language.Tag + for _, line := range strings.Split(s, "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + f := strings.Split(line, ": ") + if len(f) != 3 { + continue + } + tag := language.Make(f[1]) + tags = append(tags, tag) + t.byTag[tag.String()] = f[2] + } + t.matcher = language.NewMatcher(tags) + return t +} + +// find finds the text to use for the given language tag preferences. +func (t *text) find(prefs []language.Tag) string { + tag, _, _ := t.matcher.Match(prefs...) + s := t.byTag[tag.String()] + if strings.HasPrefix(s, "RTL ") { + s = "\u200F" + strings.TrimPrefix(s, "RTL ") + "\u200E" + } + return s +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.2.1.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.2.1.txt new file mode 100644 index 0000000..00b71bf --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.2.1.txt @@ -0,0 +1,134 @@ +generated by ./addmod.bash rsc.io/sampler@v1.2.1 + +-- .mod -- +module "rsc.io/sampler" + +require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c +-- .info -- +{"Version":"v1.2.1","Name":"cac3af4f8a0ab40054fa6f8d423108a63a1255bb","Short":"cac3af4f8a0a","Time":"2018-02-13T18:16:22Z"}EOF +-- hello.go -- +// Copyright 2018 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. + +// Translations by Google Translate. + +package sampler + +var hello = newText(` + +English: en: Hello, world. +French: fr: Bonjour le monde. +Spanish: es: Hola Mundo. + +`) +-- hello_test.go -- +// Copyright 2018 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 sampler + +import ( + "testing" + + "golang.org/x/text/language" +) + +var helloTests = []struct { + prefs []language.Tag + text string +}{ + { + []language.Tag{language.Make("en-US"), language.Make("fr")}, + "Hello, world.", + }, + { + []language.Tag{language.Make("fr"), language.Make("en-US")}, + "Bonjour le monde.", + }, +} + +func TestHello(t *testing.T) { + for _, tt := range helloTests { + text := Hello(tt.prefs...) + if text != tt.text { + t.Errorf("Hello(%v) = %q, want %q", tt.prefs, text, tt.text) + } + } +} +-- sampler.go -- +// Copyright 2018 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 sampler shows simple texts. +package sampler // import "rsc.io/sampler" + +import ( + "os" + "strings" + + "golang.org/x/text/language" +) + +// DefaultUserPrefs returns the default user language preferences. +// It consults the $LC_ALL, $LC_MESSAGES, and $LANG environment +// variables, in that order. +func DefaultUserPrefs() []language.Tag { + var prefs []language.Tag + for _, k := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} { + if env := os.Getenv(k); env != "" { + prefs = append(prefs, language.Make(env)) + } + } + return prefs +} + +// Hello returns a localized greeting. +// If no prefs are given, Hello uses DefaultUserPrefs. +func Hello(prefs ...language.Tag) string { + if len(prefs) == 0 { + prefs = DefaultUserPrefs() + } + return hello.find(prefs) +} + +// A text is a localized text. +type text struct { + byTag map[string]string + matcher language.Matcher +} + +// newText creates a new localized text, given a list of translations. +func newText(s string) *text { + t := &text{ + byTag: make(map[string]string), + } + var tags []language.Tag + for _, line := range strings.Split(s, "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + f := strings.Split(line, ": ") + if len(f) != 3 { + continue + } + tag := language.Make(f[1]) + tags = append(tags, tag) + t.byTag[tag.String()] = f[2] + } + t.matcher = language.NewMatcher(tags) + return t +} + +// find finds the text to use for the given language tag preferences. +func (t *text) find(prefs []language.Tag) string { + tag, _, _ := t.matcher.Match(prefs...) + s := t.byTag[tag.String()] + if strings.HasPrefix(s, "RTL ") { + s = "\u200F" + strings.TrimPrefix(s, "RTL ") + "\u200E" + } + return s +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.3.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.3.0.txt new file mode 100644 index 0000000..febe51f --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.3.0.txt @@ -0,0 +1,202 @@ +rsc.io/sampler@v1.3.0 + +-- .mod -- +module "rsc.io/sampler" + +require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c +-- .info -- +{"Version":"v1.3.0","Name":"0cc034b51e57ed7832d4c67d526f75a900996e5c","Short":"0cc034b51e57","Time":"2018-02-13T19:05:03Z"} +-- glass.go -- +// Copyright 2018 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. + +// Translations from Frank da Cruz, Ethan Mollick, and many others. +// See http://kermitproject.org/utf8.html. +// http://www.oocities.org/nodotus/hbglass.html +// https://en.wikipedia.org/wiki/I_Can_Eat_Glass + +package sampler + +var glass = newText(` + +English: en: I can eat glass and it doesn't hurt me. +French: fr: Je peux manger du verre, ça ne me fait pas mal. +Spanish: es: Puedo comer vidrio, no me hace daño. + +`) +-- glass_test.go -- +// Copyright 2018 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 sampler + +import ( + "testing" + + "golang.org/x/text/language" + _ "rsc.io/testonly" +) + +var glassTests = []struct { + prefs []language.Tag + text string +}{ + { + []language.Tag{language.Make("en-US"), language.Make("fr")}, + "I can eat glass and it doesn't hurt me.", + }, + { + []language.Tag{language.Make("fr"), language.Make("en-US")}, + "Je peux manger du verre, ça ne me fait pas mal.", + }, +} + +func TestGlass(t *testing.T) { + for _, tt := range glassTests { + text := Glass(tt.prefs...) + if text != tt.text { + t.Errorf("Glass(%v) = %q, want %q", tt.prefs, text, tt.text) + } + } +} +-- go.mod -- +module "rsc.io/sampler" + +require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c +-- hello.go -- +// Copyright 2018 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. + +// Translations by Google Translate. + +package sampler + +var hello = newText(` + +English: en: Hello, world. +French: fr: Bonjour le monde. +Spanish: es: Hola Mundo. + +`) +-- hello_test.go -- +// Copyright 2018 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 sampler + +import ( + "testing" + + "golang.org/x/text/language" +) + +var helloTests = []struct { + prefs []language.Tag + text string +}{ + { + []language.Tag{language.Make("en-US"), language.Make("fr")}, + "Hello, world.", + }, + { + []language.Tag{language.Make("fr"), language.Make("en-US")}, + "Bonjour le monde.", + }, +} + +func TestHello(t *testing.T) { + for _, tt := range helloTests { + text := Hello(tt.prefs...) + if text != tt.text { + t.Errorf("Hello(%v) = %q, want %q", tt.prefs, text, tt.text) + } + } +} +-- sampler.go -- +// Copyright 2018 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 sampler shows simple texts. +package sampler // import "rsc.io/sampler" + +import ( + "os" + "strings" + + "golang.org/x/text/language" +) + +// DefaultUserPrefs returns the default user language preferences. +// It consults the $LC_ALL, $LC_MESSAGES, and $LANG environment +// variables, in that order. +func DefaultUserPrefs() []language.Tag { + var prefs []language.Tag + for _, k := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} { + if env := os.Getenv(k); env != "" { + prefs = append(prefs, language.Make(env)) + } + } + return prefs +} + +// Hello returns a localized greeting. +// If no prefs are given, Hello uses DefaultUserPrefs. +func Hello(prefs ...language.Tag) string { + if len(prefs) == 0 { + prefs = DefaultUserPrefs() + } + return hello.find(prefs) +} + +// Glass returns a localized silly phrase. +// If no prefs are given, Glass uses DefaultUserPrefs. +func Glass(prefs ...language.Tag) string { + if len(prefs) == 0 { + prefs = DefaultUserPrefs() + } + return glass.find(prefs) +} + +// A text is a localized text. +type text struct { + byTag map[string]string + matcher language.Matcher +} + +// newText creates a new localized text, given a list of translations. +func newText(s string) *text { + t := &text{ + byTag: make(map[string]string), + } + var tags []language.Tag + for _, line := range strings.Split(s, "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + f := strings.Split(line, ": ") + if len(f) != 3 { + continue + } + tag := language.Make(f[1]) + tags = append(tags, tag) + t.byTag[tag.String()] = f[2] + } + t.matcher = language.NewMatcher(tags) + return t +} + +// find finds the text to use for the given language tag preferences. +func (t *text) find(prefs []language.Tag) string { + tag, _, _ := t.matcher.Match(prefs...) + s := t.byTag[tag.String()] + if strings.HasPrefix(s, "RTL ") { + s = "\u200F" + strings.TrimPrefix(s, "RTL ") + "\u200E" + } + return s +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.3.1.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.3.1.txt new file mode 100644 index 0000000..a293f10 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.3.1.txt @@ -0,0 +1,201 @@ +rsc.io/sampler@v1.3.1 + +-- .mod -- +module "rsc.io/sampler" + +require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c +-- .info -- +{"Version":"v1.3.1","Name":"f545d0289d06e2add4556ea6a15fc4938014bf87","Short":"f545d0289d06","Time":"2018-02-14T16:34:12Z"} +-- glass.go -- +// Copyright 2018 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. + +// Translations from Frank da Cruz, Ethan Mollick, and many others. +// See http://kermitproject.org/utf8.html. +// http://www.oocities.org/nodotus/hbglass.html +// https://en.wikipedia.org/wiki/I_Can_Eat_Glass + +package sampler + +var glass = newText(` + +English: en: I can eat glass and it doesn't hurt me. +French: fr: Je peux manger du verre, ça ne me fait pas mal. +Spanish: es: Puedo comer vidrio, no me hace daño. + +`) +-- glass_test.go -- +// Copyright 2018 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 sampler + +import ( + "testing" + + "golang.org/x/text/language" +) + +var glassTests = []struct { + prefs []language.Tag + text string +}{ + { + []language.Tag{language.Make("en-US"), language.Make("fr")}, + "I can eat glass and it doesn't hurt me.", + }, + { + []language.Tag{language.Make("fr"), language.Make("en-US")}, + "Je peux manger du verre, ça ne me fait pas mal.", + }, +} + +func TestGlass(t *testing.T) { + for _, tt := range glassTests { + text := Glass(tt.prefs...) + if text != tt.text { + t.Errorf("Glass(%v) = %q, want %q", tt.prefs, text, tt.text) + } + } +} +-- go.mod -- +module "rsc.io/sampler" + +require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c +-- hello.go -- +// Copyright 2018 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. + +// Translations by Google Translate. + +package sampler + +var hello = newText(` + +English: en: Hello, world. +French: fr: Bonjour le monde. +Spanish: es: Hola Mundo. + +`) +-- hello_test.go -- +// Copyright 2018 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 sampler + +import ( + "testing" + + "golang.org/x/text/language" +) + +var helloTests = []struct { + prefs []language.Tag + text string +}{ + { + []language.Tag{language.Make("en-US"), language.Make("fr")}, + "Hello, world.", + }, + { + []language.Tag{language.Make("fr"), language.Make("en-US")}, + "Bonjour le monde.", + }, +} + +func TestHello(t *testing.T) { + for _, tt := range helloTests { + text := Hello(tt.prefs...) + if text != tt.text { + t.Errorf("Hello(%v) = %q, want %q", tt.prefs, text, tt.text) + } + } +} +-- sampler.go -- +// Copyright 2018 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 sampler shows simple texts in a variety of languages. +package sampler // import "rsc.io/sampler" + +import ( + "os" + "strings" + + "golang.org/x/text/language" +) + +// DefaultUserPrefs returns the default user language preferences. +// It consults the $LC_ALL, $LC_MESSAGES, and $LANG environment +// variables, in that order. +func DefaultUserPrefs() []language.Tag { + var prefs []language.Tag + for _, k := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} { + if env := os.Getenv(k); env != "" { + prefs = append(prefs, language.Make(env)) + } + } + return prefs +} + +// Hello returns a localized greeting. +// If no prefs are given, Hello uses DefaultUserPrefs. +func Hello(prefs ...language.Tag) string { + if len(prefs) == 0 { + prefs = DefaultUserPrefs() + } + return hello.find(prefs) +} + +// Glass returns a localized silly phrase. +// If no prefs are given, Glass uses DefaultUserPrefs. +func Glass(prefs ...language.Tag) string { + if len(prefs) == 0 { + prefs = DefaultUserPrefs() + } + return glass.find(prefs) +} + +// A text is a localized text. +type text struct { + byTag map[string]string + matcher language.Matcher +} + +// newText creates a new localized text, given a list of translations. +func newText(s string) *text { + t := &text{ + byTag: make(map[string]string), + } + var tags []language.Tag + for _, line := range strings.Split(s, "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + f := strings.Split(line, ": ") + if len(f) != 3 { + continue + } + tag := language.Make(f[1]) + tags = append(tags, tag) + t.byTag[tag.String()] = f[2] + } + t.matcher = language.NewMatcher(tags) + return t +} + +// find finds the text to use for the given language tag preferences. +func (t *text) find(prefs []language.Tag) string { + tag, _, _ := t.matcher.Match(prefs...) + s := t.byTag[tag.String()] + if strings.HasPrefix(s, "RTL ") { + s = "\u200F" + strings.TrimPrefix(s, "RTL ") + "\u200E" + } + return s +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.99.99.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.99.99.txt new file mode 100644 index 0000000..5036d20 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_sampler_v1.99.99.txt @@ -0,0 +1,140 @@ +rsc.io/sampler@v1.99.99 + +-- .mod -- +module "rsc.io/sampler" + +require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c +-- .info -- +{"Version":"v1.99.99","Name":"732a3c400797d8835f2af34a9561f155bef85435","Short":"732a3c400797","Time":"2018-02-13T22:20:19Z"} +-- go.mod -- +module "rsc.io/sampler" + +require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c +-- hello.go -- +// Copyright 2018 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. + +// Translations by Google Translate. + +package sampler + +var hello = newText(` + +English: en: 99 bottles of beer on the wall, 99 bottles of beer, ... + +`) +-- hello_test.go -- +// Copyright 2018 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 sampler + +import ( + "testing" + + "golang.org/x/text/language" +) + +var helloTests = []struct { + prefs []language.Tag + text string +}{ + { + []language.Tag{language.Make("en-US"), language.Make("fr")}, + "Hello, world.", + }, + { + []language.Tag{language.Make("fr"), language.Make("en-US")}, + "Bonjour le monde.", + }, +} + +func TestHello(t *testing.T) { + for _, tt := range helloTests { + text := Hello(tt.prefs...) + if text != tt.text { + t.Errorf("Hello(%v) = %q, want %q", tt.prefs, text, tt.text) + } + } +} +-- sampler.go -- +// Copyright 2018 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 sampler shows simple texts. +package sampler // import "rsc.io/sampler" + +import ( + "os" + "strings" + + "golang.org/x/text/language" +) + +// DefaultUserPrefs returns the default user language preferences. +// It consults the $LC_ALL, $LC_MESSAGES, and $LANG environment +// variables, in that order. +func DefaultUserPrefs() []language.Tag { + var prefs []language.Tag + for _, k := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} { + if env := os.Getenv(k); env != "" { + prefs = append(prefs, language.Make(env)) + } + } + return prefs +} + +// Hello returns a localized greeting. +// If no prefs are given, Hello uses DefaultUserPrefs. +func Hello(prefs ...language.Tag) string { + if len(prefs) == 0 { + prefs = DefaultUserPrefs() + } + return hello.find(prefs) +} + +func Glass() string { + return "I can eat glass and it doesn't hurt me." +} + +// A text is a localized text. +type text struct { + byTag map[string]string + matcher language.Matcher +} + +// newText creates a new localized text, given a list of translations. +func newText(s string) *text { + t := &text{ + byTag: make(map[string]string), + } + var tags []language.Tag + for _, line := range strings.Split(s, "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + f := strings.Split(line, ": ") + if len(f) != 3 { + continue + } + tag := language.Make(f[1]) + tags = append(tags, tag) + t.byTag[tag.String()] = f[2] + } + t.matcher = language.NewMatcher(tags) + return t +} + +// find finds the text to use for the given language tag preferences. +func (t *text) find(prefs []language.Tag) string { + tag, _, _ := t.matcher.Match(prefs...) + s := t.byTag[tag.String()] + if strings.HasPrefix(s, "RTL ") { + s = "\u200F" + strings.TrimPrefix(s, "RTL ") + "\u200E" + } + return s +} diff --git a/libgo/go/cmd/go/testdata/mod/rsc.io_testonly_v1.0.0.txt b/libgo/go/cmd/go/testdata/mod/rsc.io_testonly_v1.0.0.txt new file mode 100644 index 0000000..dfb8ca2 --- /dev/null +++ b/libgo/go/cmd/go/testdata/mod/rsc.io_testonly_v1.0.0.txt @@ -0,0 +1,9 @@ +rsc.io/testonly v1.0.0 +written by hand + +-- .mod -- +module rsc.io/testonly +-- .info -- +{"Version":"v1.0.0"} +-- testonly.go -- +package testonly diff --git a/libgo/go/cmd/go/testdata/savedir.go b/libgo/go/cmd/go/testdata/savedir.go new file mode 100644 index 0000000..48a6318 --- /dev/null +++ b/libgo/go/cmd/go/testdata/savedir.go @@ -0,0 +1,79 @@ +// Copyright 2018 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 ignore + +// Savedir archives a directory tree as a txtar archive printed to standard output. +// +// Usage: +// +// go run savedir.go /path/to/dir >saved.txt +// +// Typically the tree is later extracted during a test with tg.extract("testdata/saved.txt"). +// +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + "unicode/utf8" + + "../internal/txtar" +) + +func usage() { + fmt.Fprintf(os.Stderr, "usage: go run savedir.go dir >saved.txt\n") + os.Exit(2) +} + +const goCmd = "vgo" + +func main() { + flag.Usage = usage + flag.Parse() + if flag.NArg() != 1 { + usage() + } + + log.SetPrefix("savedir: ") + log.SetFlags(0) + + dir := flag.Arg(0) + + a := new(txtar.Archive) + dir = filepath.Clean(dir) + filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if path == dir { + return nil + } + name := info.Name() + if strings.HasPrefix(name, ".") { + if info.IsDir() { + return filepath.SkipDir + } + return nil + } + if !info.Mode().IsRegular() { + return nil + } + data, err := ioutil.ReadFile(path) + if err != nil { + log.Fatal(err) + } + if !utf8.Valid(data) { + log.Printf("%s: ignoring invalid UTF-8 data", path) + return nil + } + a.Files = append(a.Files, txtar.File{Name: strings.TrimPrefix(path, dir+string(filepath.Separator)), Data: data}) + return nil + }) + + data := txtar.Format(a) + os.Stdout.Write(data) +} diff --git a/libgo/go/cmd/go/testdata/script/README b/libgo/go/cmd/go/testdata/script/README new file mode 100644 index 0000000..a80233b --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/README @@ -0,0 +1,261 @@ +This directory holds test scripts *.txt run during 'go test cmd/go'. +To run a specific script foo.txt + + go test cmd/go -run=Script/^foo$ + +In general script files should have short names: a few words, not whole sentences. +The first word should be the general category of behavior being tested, +often the name of a go subcommand (list, build, test, ...) or concept (vendor, pattern). + +Each script is a text archive (go doc cmd/go/internal/txtar). +The script begins with an actual command script to run +followed by the content of zero or more supporting files to +create in the script's temporary file system before it starts executing. + +As an example, run_hello.txt says: + + # hello world + go run hello.go + stderr 'hello world' + ! stdout . + + -- hello.go -- + package main + func main() { println("hello world") } + +Each script runs in a fresh temporary work directory tree, available to scripts as $WORK. +Scripts also have access to these other environment variables: + + GOARCH=<target GOARCH> + GOCACHE=<actual GOCACHE being used outside the test> + GOOS=<target GOOS> + GOPATH=$WORK/gopath + GOPROXY=<local module proxy serving from cmd/go/testdata/mod> + GOROOT=<actual GOROOT> + HOME=/no-home + PATH=<actual PATH> + TMPDIR=$WORK/tmp + devnull=<value of os.DevNull> + +The environment variable $exe (lowercase) is an empty string on most systems, ".exe" on Windows. + +The scripts supporting files are unpacked relative to $GOPATH/src (aka $WORK/gopath/src) +and then the script begins execution in that directory as well. Thus the example above runs +in $WORK/gopath/src with GOPATH=$WORK/gopath and $WORK/gopath/src/hello.go +containing the listed contents. + +The lines at the top of the script are a sequence of commands to be executed +by a tiny script engine in ../../script_test.go (not the system shell). +The script stops and the overall test fails if any particular command fails. + +Each line is parsed into a sequence of space-separated command words, +with environment variable expansion and # marking an end-of-line comment. +Adding single quotes around text keeps spaces in that text from being treated +as word separators and also disables environment variable expansion. +Inside a single-quoted block of text, a repeated single quote indicates +a literal single quote, as in: + + 'Don''t communicate by sharing memory.' + +A line beginning with # is a comment and conventionally explains what is +being done or tested at the start of a new phase in the script. + +The command prefix ! indicates that the command on the rest of the line +(typically go or a matching predicate) must fail, not succeed. Only certain +commands support this prefix. They are indicated below by [!] in the synopsis. + +The command prefix [cond] indicates that the command on the rest of the line +should only run when the condition is satisfied. The available conditions are: + + - GOOS and GOARCH values, like [386], [windows], and so on. + - Compiler names, like [gccgo], [gc]. + - Test environment details: + - [short] for testing.Short() + - [cgo], [msan], [race] for whether cgo, msan, and the race detector can be used + - [net] for whether the external network can be used + - [link] for testenv.HasLink() + - [symlink] for testenv.HasSymlink() + - [exec:prog] for whether prog is available for execution (found by exec.LookPath) + +A condition can be negated: [!short] means to run the rest of the line +when testing.Short() is false. + +The commands are: + +- cd dir + Change to the given directory for future commands. + +- cmp file1 file2 + Check that the named files have the same content. + By convention, file1 is the actual data and file2 the expected data. + File1 can be "stdout" or "stderr" to use the standard output or standard error + from the most recent exec or go command. + (If the files have differing content, the failure prints a diff.) + +- cp src... dst + Copy the listed files to the target file or existing directory. + +- env [key=value...] + With no arguments, print the environment (useful for debugging). + Otherwise add the listed key=value pairs to the environment. + +- [!] exec program [args...] + Run the given executable program with the arguments. + It must (or must not) succeed. + Note that 'exec' does not terminate the script (unlike in Unix shells). + +- [!] exists [-readonly] file... + Each of the listed files or directories must (or must not) exist. + If -readonly is given, the files or directories must be unwritable. + +- [!] go args... + Run the (test copy of the) go command with the given arguments. + It must (or must not) succeed. + +- [!] grep [-count=N] pattern file + The file's content must (or must not) match the regular expression pattern. + For positive matches, -count=N specifies an exact number of matches to require. + +- mkdir path... + Create the listed directories, if they do not already exists. + +- rm file... + Remove the listed files or directories. + +- skip [message] + Mark the test skipped, including the message if given. + +- [!] stale path... + The packages named by the path arguments must (or must not) + be reported as "stale" by the go command. + +- [!] stderr [-count=N] pattern + Apply the grep command (see above) to the standard error + from the most recent exec or go command. + +- [!] stdout [-count=N] pattern + Apply the grep command (see above) to the standard output + from the most recent exec or go command. + +- stop [message] + Stop the test early (marking it as passing), including the message if given. + +- symlink file -> target + Create file as a symlink to target. The -> (like in ls -l output) is required. + +When TestScript runs a script and the script fails, by default TestScript shows +the execution of the most recent phase of the script (since the last # comment) +and only shows the # comments for earlier phases. For example, here is a +multi-phase script with a bug in it: + + # GOPATH with p1 in d2, p2 in d2 + env GOPATH=$WORK/d1${:}$WORK/d2 + + # build & install p1 + env + go install -i p1 + ! stale p1 + ! stale p2 + + # modify p2 - p1 should appear stale + cp $WORK/p2x.go $WORK/d2/src/p2/p2.go + stale p1 p2 + + # build & install p1 again + go install -i p11 + ! stale p1 + ! stale p2 + + -- $WORK/d1/src/p1/p1.go -- + package p1 + import "p2" + func F() { p2.F() } + -- $WORK/d2/src/p2/p2.go -- + package p2 + func F() {} + -- $WORK/p2x.go -- + package p2 + func F() {} + func G() {} + +The bug is that the final phase installs p11 instead of p1. The test failure looks like: + + $ go test -run=Script + --- FAIL: TestScript (3.75s) + --- FAIL: TestScript/install_rebuild_gopath (0.16s) + script_test.go:223: + # GOPATH with p1 in d2, p2 in d2 (0.000s) + # build & install p1 (0.087s) + # modify p2 - p1 should appear stale (0.029s) + # build & install p1 again (0.022s) + > go install -i p11 + [stderr] + can't load package: package p11: cannot find package "p11" in any of: + /Users/rsc/go/src/p11 (from $GOROOT) + $WORK/d1/src/p11 (from $GOPATH) + $WORK/d2/src/p11 + [exit status 1] + FAIL: unexpected go command failure + + script_test.go:73: failed at testdata/script/install_rebuild_gopath.txt:15 in $WORK/gopath/src + + FAIL + exit status 1 + FAIL cmd/go 4.875s + $ + +Note that the commands in earlier phases have been hidden, so that the relevant +commands are more easily found, and the elapsed time for a completed phase +is shown next to the phase heading. To see the entire execution, use "go test -v", +which also adds an initial environment dump to the beginning of the log. + +Note also that in reported output, the actual name of the per-script temporary directory +has been consistently replaced with the literal string $WORK. + +The cmd/go test flag -testwork (which must appear on the "go test" command line after +standard test flags) causes each test to log the name of its $WORK directory and other +environment variable settings and also to leave that directory behind when it exits, +for manual debugging of failing tests: + + $ go test -run=Script -work + --- FAIL: TestScript (3.75s) + --- FAIL: TestScript/install_rebuild_gopath (0.16s) + script_test.go:223: + WORK=/tmp/cmd-go-test-745953508/script-install_rebuild_gopath + GOARCH= + GOCACHE=/Users/rsc/Library/Caches/go-build + GOOS= + GOPATH=$WORK/gopath + GOROOT=/Users/rsc/go + HOME=/no-home + TMPDIR=$WORK/tmp + exe= + + # GOPATH with p1 in d2, p2 in d2 (0.000s) + # build & install p1 (0.085s) + # modify p2 - p1 should appear stale (0.030s) + # build & install p1 again (0.019s) + > go install -i p11 + [stderr] + can't load package: package p11: cannot find package "p11" in any of: + /Users/rsc/go/src/p11 (from $GOROOT) + $WORK/d1/src/p11 (from $GOPATH) + $WORK/d2/src/p11 + [exit status 1] + FAIL: unexpected go command failure + + script_test.go:73: failed at testdata/script/install_rebuild_gopath.txt:15 in $WORK/gopath/src + + FAIL + exit status 1 + FAIL cmd/go 4.875s + $ + + $ WORK=/tmp/cmd-go-test-745953508/script-install_rebuild_gopath + $ cd $WORK/d1/src/p1 + $ cat p1.go + package p1 + import "p2" + func F() { p2.F() } + $ + diff --git a/libgo/go/cmd/go/testdata/script/binary_only.txt b/libgo/go/cmd/go/testdata/script/binary_only.txt new file mode 100644 index 0000000..397904e --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/binary_only.txt @@ -0,0 +1,10 @@ +# check that error for missing binary-only says where it should be +! go build b +stderr pkg[\\/].*a\.a + +-- a/a.go -- +//go:binary-only-package + +package a +-- b/b.go -- +package b; import "a" diff --git a/libgo/go/cmd/go/testdata/script/build_GOTMPDIR.txt b/libgo/go/cmd/go/testdata/script/build_GOTMPDIR.txt new file mode 100644 index 0000000..4c387af --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/build_GOTMPDIR.txt @@ -0,0 +1,11 @@ +# Build should use GOTMPDIR if set. +env GOTMPDIR=$WORK/my-favorite-tmpdir +env GOCACHE=off +mkdir $GOTMPDIR +go build -work hello.go +stderr ^WORK=.*my-favorite-tmpdir + +-- hello.go -- +package main +func main() { println("hello") } + diff --git a/libgo/go/cmd/go/testdata/script/build_cache_compile.txt b/libgo/go/cmd/go/testdata/script/build_cache_compile.txt new file mode 100644 index 0000000..7db881a --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/build_cache_compile.txt @@ -0,0 +1,18 @@ +# Set up fresh GOCACHE. +env GOCACHE=$WORK/gocache +mkdir $GOCACHE + +# Building trivial non-main package should run compiler the first time. +go build -x lib.go +stderr '(compile|gccgo)( |\.exe).*lib\.go' + +# ... but not again ... +go build -x lib.go +! stderr '(compile|gccgo)( |\.exe).*lib\.go' + +# ... unless we use -a. +go build -a -x lib.go +stderr '(compile|gccgo)( |\.exe)' + +-- lib.go -- +package lib diff --git a/libgo/go/cmd/go/testdata/script/build_cache_link.txt b/libgo/go/cmd/go/testdata/script/build_cache_link.txt new file mode 100644 index 0000000..61e7ee4 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/build_cache_link.txt @@ -0,0 +1,23 @@ +# Set up fresh GOCACHE. +env GOCACHE=$WORK/gocache +mkdir $GOCACHE + +# Building a main package should run the compiler and linker ... +go build -o $devnull -x main.go +stderr '(compile|gccgo)( |\.exe).*main\.go' +stderr '(link|gccgo)( |\.exe)' + +# ... and then the linker again ... +go build -o $devnull -x main.go +! stderr '(compile|gccgo)( |\.exe).*main\.go' +stderr '(link|gccgo)( |\.exe)' + +# ... but the output binary can serve as a cache. +go build -o main$exe -x main.go +stderr '(link|gccgo)( |\.exe)' +go build -o main$exe -x main.go +! stderr '(link|gccgo)( |\.exe)' + +-- main.go -- +package main +func main() {} diff --git a/libgo/go/cmd/go/testdata/script/build_cache_output.txt b/libgo/go/cmd/go/testdata/script/build_cache_output.txt new file mode 100644 index 0000000..ee4099e --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/build_cache_output.txt @@ -0,0 +1,63 @@ +[!gc] skip + +# Set up fresh GOCACHE. +env GOCACHE=$WORK/gocache +mkdir $GOCACHE + +# Building a trivial non-main package should run compiler the first time. +go build -x -gcflags=-m lib.go +stderr 'compile( |\.exe"?)' +stderr 'lib.go:2.* can inline f' + +# ... but not the second, even though it still prints the compiler output. +go build -x -gcflags=-m lib.go +! stderr 'compile( |\.exe"?)' +stderr 'lib.go:2.* can inline f' + +# Building a trivial main package should run the compiler and linker the first time. +go build -x -gcflags=-m -ldflags='-v -w' main.go +stderr 'compile( |\.exe"?)' +stderr 'main.go:2.* can inline main' # from compiler +stderr 'link(\.exe"?)? -' +stderr '\d+ symbols' # from linker + +# ... but not the second, even though it still prints the compiler and linker output. +go build -x -gcflags=-m -ldflags='-v -w' main.go +! stderr 'compile( |\.exe"?)' +stderr 'main.go:2.* can inline main' # from compiler +! stderr 'link(\.exe"?)? -' +stderr '\d+ symbols' # from linker + +# Running a test should run the compiler, linker, and the test the first time. +go test -v -x -gcflags=-m -ldflags=-v p_test.go +stderr 'compile( |\.exe"?)' +stderr 'p_test.go:.*can inline Test' # from compile of p_test +stderr 'testmain\.go:.*inlin' # from compile of testmain +stderr 'link(\.exe"?)? -' +stderr '\d+ symbols' # from linker +stderr 'p\.test( |\.exe"?)' +stdout 'TEST' # from test + +# ... but not the second, even though it still prints the compiler, linker, and test output. +go test -v -x -gcflags=-m -ldflags=-v p_test.go +! stderr 'compile( |\.exe"?)' +stderr 'p_test.go:.*can inline Test' # from compile of p_test +stderr 'testmain\.go:.*inlin' # from compile of testmain +! stderr 'link(\.exe"?)? -' +stderr '\d+ symbols' # from linker +! stderr 'p\.test( |\.exe"?)' +stdout 'TEST' # from test + + +-- lib.go -- +package p +func f(x *int) *int { return x } + +-- main.go -- +package main +func main() {} + +-- p_test.go -- +package p +import "testing" +func Test(t *testing.T) {println("TEST")} diff --git a/libgo/go/cmd/go/testdata/script/cover_atomic_pkgall.txt b/libgo/go/cmd/go/testdata/script/cover_atomic_pkgall.txt new file mode 100644 index 0000000..c122c05 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/cover_atomic_pkgall.txt @@ -0,0 +1,23 @@ +[short] skip + +go test -coverpkg=all -covermode=atomic x +stdout ok[\s\S]+?coverage + +[!race] stop + +go test -coverpkg=all -race x +stdout ok[\s\S]+?coverage + +-- x/x.go -- +package x + +import _ "sync/atomic" + +func F() {} + +-- x/x_test.go -- +package x + +import "testing" + +func TestF(t *testing.T) { F() } diff --git a/libgo/go/cmd/go/testdata/script/cover_pkgall_runtime.txt b/libgo/go/cmd/go/testdata/script/cover_pkgall_runtime.txt new file mode 100644 index 0000000..5d169d6 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/cover_pkgall_runtime.txt @@ -0,0 +1,21 @@ +# Issue 23882 + +[short] skip + +go test -coverpkg=all x +stdout ok[\s\S]+?coverage + +[!race] stop + +go test -coverpkg=all -race x +stdout ok[\s\S]+?coverage + +-- x/x.go -- +package x +import _ "runtime" +func F() {} + +-- x/x_test.go -- +package x +import "testing" +func TestF(t *testing.T) { F() } diff --git a/libgo/go/cmd/go/testdata/script/cpu_profile_twice.txt b/libgo/go/cmd/go/testdata/script/cpu_profile_twice.txt new file mode 100644 index 0000000..142d5ee --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/cpu_profile_twice.txt @@ -0,0 +1,20 @@ +# Issue 23150 + +[short] skip + +go test -o=$WORK/x.test -cpuprofile=$WORK/cpu_profile_twice.out x +rm $WORK/cpu_profile_twice.out + +go test -o=$WORK/x.test -cpuprofile=$WORK/cpu_profile_twice.out x +exists $WORK/cpu_profile_twice.out + + +-- x/x_test.go -- +package x_test +import ( + "testing" + "time" +) +func TestSleep(t *testing.T) { + time.Sleep(10 * time.Millisecond) +} diff --git a/libgo/go/cmd/go/testdata/script/fileline.txt b/libgo/go/cmd/go/testdata/script/fileline.txt new file mode 100644 index 0000000..cdc3be2 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/fileline.txt @@ -0,0 +1,6 @@ +# look for short, relative file:line in error message +! go run ../../gopath/x/y/z/err.go +stderr ^..[\\/]x[\\/]y[\\/]z[\\/]err.go: + +-- ../x/y/z/err.go -- +package main; import "bar" diff --git a/libgo/go/cmd/go/testdata/script/get_with_git_trace.txt b/libgo/go/cmd/go/testdata/script/get_with_git_trace.txt new file mode 100644 index 0000000..93341a3 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/get_with_git_trace.txt @@ -0,0 +1,7 @@ +env GIT_TRACE=1 + +[!net] skip +[!exec:git] skip + +# go get should be success when GIT_TRACE set +go get golang.org/x/text diff --git a/libgo/go/cmd/go/testdata/script/goflags.txt b/libgo/go/cmd/go/testdata/script/goflags.txt new file mode 100644 index 0000000..20de325 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/goflags.txt @@ -0,0 +1,49 @@ +# GOFLAGS sets flags for commands + +env GOFLAGS='-e -f={{.Dir}} --test.benchtime=1s -count=10' +go list asdfasdfasdf # succeeds because of -e +go list runtime +stdout '[\\/]runtime$' + +env GOFLAGS=-race OLDGOARCH=$GOARCH OLDGOOS=$GOOS GOARCH=386 GOOS=linux +! go list runtime +stderr 'race is only supported on' + +env GOARCH=$OLDGOARCH GOOS=$OLDGOOS + +# go env succeeds even though -f={{.Dir}} is inappropriate +go env + +# bad flags are diagnosed +env GOFLAGS=-typoflag +! go list runtime +stderr 'unknown flag -typoflag' + +env GOFLAGS=- +! go list runtime +stderr '^go: parsing \$GOFLAGS: non-flag "-"' + +env GOFLAGS=-- +! go list runtime +stderr '^go: parsing \$GOFLAGS: non-flag "--"' + +env GOFLAGS=---oops +! go list runtime +stderr '^go: parsing \$GOFLAGS: non-flag "---oops"' + +env GOFLAGS=-=noname +! go list runtime +stderr '^go: parsing \$GOFLAGS: non-flag "-=noname"' + +env GOFLAGS=-f +! go list runtime +stderr '^go: flag needs an argument: -f \(from (\$GOFLAGS|%GOFLAGS%)\)$' + +env GOFLAGS=-e=asdf +! go list runtime +stderr '^go: invalid boolean value \"asdf\" for flag -e \(from (\$GOFLAGS|%GOFLAGS%)\)' + +# except in go bug (untested) and go env +go env +stdout GOFLAGS + diff --git a/libgo/go/cmd/go/testdata/script/help.txt b/libgo/go/cmd/go/testdata/script/help.txt new file mode 100644 index 0000000..cbbd154 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/help.txt @@ -0,0 +1,30 @@ +# go help shows overview. +go help +stdout 'Go is a tool' +stdout 'bug.*start a bug report' + +# go help bug shows usage for bug +go help bug +stdout 'usage: go bug' +stdout 'bug report' + +# go bug help is an error (bug takes no arguments) +! go bug help +stderr 'bug takes no arguments' + +# go help mod shows mod subcommands +go help mod +stdout 'go mod <command>' +stdout tidy + +# go help mod tidy explains tidy +go help mod tidy +stdout 'usage: go mod tidy' + +# go mod help tidy does too +go mod help tidy +stdout 'usage: go mod tidy' + +# go mod --help doesn't print help but at least suggests it. +! go mod --help +stderr 'Run ''go help mod'' for usage.' diff --git a/libgo/go/cmd/go/testdata/script/install_cleans_build.txt b/libgo/go/cmd/go/testdata/script/install_cleans_build.txt new file mode 100644 index 0000000..b8d322d --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/install_cleans_build.txt @@ -0,0 +1,22 @@ +# 'go install' with no arguments should clean up after go build +cd mycmd +go build +exists mycmd$exe +go install +! exists mycmd$exe + +# 'go install mycmd' does not clean up, even in the mycmd directory +go build +exists mycmd$exe +go install mycmd +exists mycmd$exe + +# 'go install mycmd' should not clean up in an unrelated current directory either +cd .. +cp mycmd/mycmd$exe mycmd$exe +go install mycmd +exists mycmd$exe + +-- mycmd/main.go -- +package main +func main() {} diff --git a/libgo/go/cmd/go/testdata/script/install_cross_gobin.txt b/libgo/go/cmd/go/testdata/script/install_cross_gobin.txt new file mode 100644 index 0000000..f85e896 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/install_cross_gobin.txt @@ -0,0 +1,25 @@ +cd mycmd +go build mycmd + +[gccgo] stop + +# cross-compile install with implicit GOBIN=$GOPATH/bin can make subdirectory +env GOARCH=386 +[386] env GOARCH=amd64 +env GOOS=linux +go install mycmd +exists $GOPATH/bin/linux_$GOARCH/mycmd + +# cross-compile install with explicit GOBIN cannot make subdirectory +env GOBIN=$WORK/bin +! go install mycmd +! exists $GOBIN/linux_$GOARCH + +# installing standard command should still work +# (should also be mtime update only if cmd/pack is up-to-date). +! stale cmd/pack +[!short] go install cmd/pack + +-- mycmd/x.go -- +package main +func main() {} diff --git a/libgo/go/cmd/go/testdata/script/install_rebuild_gopath.txt b/libgo/go/cmd/go/testdata/script/install_rebuild_gopath.txt new file mode 100644 index 0000000..d42b070 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/install_rebuild_gopath.txt @@ -0,0 +1,28 @@ +# GOPATH with p1 in d1, p2 in d2 +env GOPATH=$WORK/d1${:}$WORK/d2 + +# build & install p1 +go install -i p1 +! stale p1 p2 + +# modify p2 - p1 should appear stale +cp $WORK/p2x.go $WORK/d2/src/p2/p2.go +stale p1 p2 + +# build & install p1 again +go install -i p1 +! stale p1 p2 + +-- $WORK/d1/src/p1/p1.go -- +package p1 +import "p2" +func F() { p2.F() } + +-- $WORK/d2/src/p2/p2.go -- +package p2 +func F() {} + +-- $WORK/p2x.go -- +package p2 +func F() {} +func G() {} diff --git a/libgo/go/cmd/go/testdata/script/install_rebuild_removed.txt b/libgo/go/cmd/go/testdata/script/install_rebuild_removed.txt new file mode 100644 index 0000000..e7620a0 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/install_rebuild_removed.txt @@ -0,0 +1,42 @@ +# go command should detect package staleness as source file set changes +go install mypkg +! stale mypkg + +# z.go was not compiled; removing it should NOT make mypkg stale +rm mypkg/z.go +! stale mypkg + +# y.go was compiled; removing it should make mypkg stale +rm mypkg/y.go +stale mypkg + +# go command should detect executable staleness too +go install mycmd +! stale mycmd +rm mycmd/z.go +! stale mycmd +rm mycmd/y.go +stale mycmd + +-- mypkg/x.go -- +package mypkg + +-- mypkg/y.go -- +package mypkg + +-- mypkg/z.go -- +// +build missingtag + +package mypkg + +-- mycmd/x.go -- +package main +func main() {} + +-- mycmd/y.go -- +package main + +-- mycmd/z.go -- +// +build missingtag + +package main diff --git a/libgo/go/cmd/go/testdata/script/linkname.txt b/libgo/go/cmd/go/testdata/script/linkname.txt new file mode 100644 index 0000000..e2ec00c --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/linkname.txt @@ -0,0 +1,7 @@ +# check for linker name in error message about linker crash +[!gc] skip +! go build -ldflags=-crash_for_testing x.go +stderr [\\/]tool[\\/].*[\\/]link + +-- x.go -- +package main; func main() {} diff --git a/libgo/go/cmd/go/testdata/script/list_bad_import.txt b/libgo/go/cmd/go/testdata/script/list_bad_import.txt new file mode 100644 index 0000000..ba66b09 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/list_bad_import.txt @@ -0,0 +1,67 @@ +# This test matches mod_list_bad_import, but in GOPATH mode. +# Please keep them in sync. + +env GO111MODULE=off +cd example.com + +# Without -e, listing an otherwise-valid package with an unsatisfied direct import should fail. +# BUG: Today it succeeds. +go list -f '{{if .Error}}error{{end}} {{if .Incomplete}}incomplete{{end}} {{range .DepsErrors}}bad dep: {{.Err}}{{end}}' example.com/direct +! stdout ^error +stdout 'incomplete' +stdout 'bad dep: .*example.com[/\\]notfound' + +# Listing with -deps should also fail. +# BUG: Today, it does not. +# ! go list -deps example.com/direct +# stderr example.com[/\\]notfound +go list -deps example.com/direct +stdout example.com/notfound + + +# Listing an otherwise-valid package that imports some *other* package with an +# unsatisfied import should also fail. +# BUG: Today, it succeeds. +go list -f '{{if .Error}}error{{end}} {{if .Incomplete}}incomplete{{end}} {{range .DepsErrors}}bad dep: {{.Err}}{{end}}' example.com/indirect +! stdout ^error +stdout incomplete +stdout 'bad dep: .*example.com[/\\]notfound' + +# Again, -deps should fail. +# BUG: Again, it does not. +# ! go list -deps example.com/indirect +# stderr example.com[/\\]notfound +go list -deps example.com/indirect +stdout example.com/notfound + + +# Listing the missing dependency directly should fail outright... +! go list -f '{{if .Error}}error{{end}} {{if .Incomplete}}incomplete{{end}}' example.com/notfound +stderr 'no Go files in .*example.com[/\\]notfound' +! stdout error +! stdout incomplete + +# ...but listing with -e should succeed. +go list -e -f '{{if .Error}}error{{end}} {{if .Incomplete}}incomplete{{end}}' example.com/notfound +stdout error +stdout incomplete + + +# The pattern "all" should match only packages that acutally exist, +# ignoring those whose existence is merely implied by imports. +go list -e -f '{{.ImportPath}}' all +stdout example.com/direct +stdout example.com/indirect +! stdout example.com/notfound + + +-- example.com/direct/direct.go -- +package direct +import _ "example.com/notfound" + +-- example.com/indirect/indirect.go -- +package indirect +import _ "example.com/direct" + +-- example.com/notfound/README -- +This directory intentionally left blank. diff --git a/libgo/go/cmd/go/testdata/script/list_compiled_imports.txt b/libgo/go/cmd/go/testdata/script/list_compiled_imports.txt new file mode 100644 index 0000000..2c883b6 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/list_compiled_imports.txt @@ -0,0 +1,29 @@ +[!cgo] skip + +# go list should report import "C" +cd x +go list -f '{{.Imports}}' +! stdout runtime/cgo +! stdout unsafe +! stdout syscall +stdout C +stdout unicode +stdout unicode/utf16 + +# go list -compiled should report imports in compiled files as well, +# adding "runtime/cgo", "unsafe", and "syscall" but not dropping "C". +go list -compiled -f '{{.Imports}}' +[!gccgo] stdout runtime/cgo +stdout unsafe +stdout syscall +stdout C +stdout unicode +stdout unicode/utf16 + +-- x/x.go -- +package x +import "C" +import "unicode" // does not use unsafe, syscall, runtime/cgo, unicode/utf16 +-- x/x1.go -- +package x +import "unicode/utf16" // does not use unsafe, syscall, runtime/cgo, unicode diff --git a/libgo/go/cmd/go/testdata/script/list_find.txt b/libgo/go/cmd/go/testdata/script/list_find.txt new file mode 100644 index 0000000..dbe8fb0 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/list_find.txt @@ -0,0 +1,10 @@ +# go list -find should not report imports + +go list -f {{.Incomplete}} x/y/z... # should probably exit non-zero but never has +stdout true +go list -find -f '{{.Incomplete}} {{.Imports}}' x/y/z... +stdout '^false \[\]' + +-- x/y/z/z.go -- +package z +import "does/not/exist" diff --git a/libgo/go/cmd/go/testdata/script/list_std.txt b/libgo/go/cmd/go/testdata/script/list_std.txt new file mode 100644 index 0000000..a63d74d --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/list_std.txt @@ -0,0 +1,12 @@ +[!gc] skip + +# listing GOROOT should only find standard packages +cd $GOROOT/src +go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' ./... +! stdout . +# TODO: ignore _/blah/go/src in output + +# our vendored packages should be reported as standard +go list std cmd +stdout golang_org/x/net/http2/hpack +stdout cmd/vendor/golang\.org/x/arch/x86/x86asm diff --git a/libgo/go/cmd/go/testdata/script/list_tags.txt b/libgo/go/cmd/go/testdata/script/list_tags.txt new file mode 100644 index 0000000..c5dc99e --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/list_tags.txt @@ -0,0 +1,8 @@ +# go list supports -tags +go list -tags=thetag ./my... +stdout mypkg + +-- mypkg/x.go -- +// +build thetag + +package mypkg diff --git a/libgo/go/cmd/go/testdata/script/list_test_e.txt b/libgo/go/cmd/go/testdata/script/list_test_e.txt new file mode 100644 index 0000000..f147332 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/list_test_e.txt @@ -0,0 +1,9 @@ +# issue 25980: crash in go list -e -test +go list -e -test -f '{{.Error}}' p +stdout '^p[/\\]d_test.go:2:8: cannot find package "d" in any of:' + +-- p/d.go -- +package d +-- p/d_test.go -- +package d_test +import _ "d" diff --git a/libgo/go/cmd/go/testdata/script/list_test_imports.txt b/libgo/go/cmd/go/testdata/script/list_test_imports.txt new file mode 100644 index 0000000..51d1ce9 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/list_test_imports.txt @@ -0,0 +1,19 @@ +# issue 26880: list with tests has wrong variant in imports +go list -test -f '{{.ImportPath}}:{{with .Imports}} {{join . ", "}}{{end}}' a b +cmp stdout imports.txt + +-- a/a.go -- +package a; import _ "b" +-- b/b.go -- +package b +-- b/b_test.go -- +package b +-- b/b_x_test.go -- +package b_test; import _ "a" + +-- imports.txt -- +a: b +b: +b.test: b [b.test], b_test [b.test], os, testing, testing/internal/testdeps +b [b.test]: +b_test [b.test]: a [b.test] diff --git a/libgo/go/cmd/go/testdata/script/mod_bad_domain.txt b/libgo/go/cmd/go/testdata/script/mod_bad_domain.txt new file mode 100644 index 0000000..c9fd044 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_bad_domain.txt @@ -0,0 +1,29 @@ +env GO111MODULE=on + +# explicit get should report errors about bad names +! go get appengine +stderr 'malformed module path "appengine": missing dot in first path element' +! go get x/y.z +stderr 'malformed module path "x/y.z": missing dot in first path element' + +# build should report all unsatisfied imports, +# but should be more definitive about non-module import paths +! go build ./useappengine +stderr 'cannot find package' +! go build ./usenonexistent +stderr 'cannot find module providing package nonexistent.rsc.io' + +# go mod vendor and go mod tidy should ignore appengine imports. +rm usenonexistent/x.go +go mod tidy +go mod vendor + +-- go.mod -- +module x + +-- useappengine/x.go -- +package useappengine +import _ "appengine" // package does not exist +-- usenonexistent/x.go -- +package usenonexistent +import _ "nonexistent.rsc.io" // domain does not exist diff --git a/libgo/go/cmd/go/testdata/script/mod_bad_filenames.txt b/libgo/go/cmd/go/testdata/script/mod_bad_filenames.txt new file mode 100644 index 0000000..6e0c8bd --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_bad_filenames.txt @@ -0,0 +1,11 @@ +env GO111MODULE=on + +! go get rsc.io/badfile1 rsc.io/badfile2 rsc.io/badfile3 rsc.io/badfile4 rsc.io/badfile5 +! stderr 'unzip.*badfile1' +stderr 'unzip.*badfile2[\\/]@v[\\/]v1.0.0.zip:.*malformed file path "☺.go": invalid char ''☺''' +stderr 'unzip.*badfile3[\\/]@v[\\/]v1.0.0.zip: malformed file path "x\?y.go": invalid char ''\?''' +stderr 'unzip.*badfile4[\\/]@v[\\/]v1.0.0.zip: case-insensitive file name collision: "x/Y.go" and "x/y.go"' +stderr 'unzip.*badfile5[\\/]@v[\\/]v1.0.0.zip: case-insensitive file name collision: "x/y" and "x/Y"' + +-- go.mod -- +module x diff --git a/libgo/go/cmd/go/testdata/script/mod_build_tags.txt b/libgo/go/cmd/go/testdata/script/mod_build_tags.txt new file mode 100644 index 0000000..1347eaa --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_build_tags.txt @@ -0,0 +1,30 @@ +# Test that build tags are used. +# golang.org/issue/24053. + +env GO111MODULE=on + +cd x +! go list -f {{.GoFiles}} +stderr 'build constraints exclude all Go files' + +go list -f {{.GoFiles}} -tags tag1 +stdout '\[x.go\]' + +go list -f {{.GoFiles}} -tags tag2 +stdout '\[y\.go\]' + +go list -f {{.GoFiles}} -tags 'tag1 tag2' +stdout '\[x\.go y\.go\]' + +-- x/go.mod -- +module x + +-- x/x.go -- +// +build tag1 + +package y + +-- x/y.go -- +// +build tag2 + +package y diff --git a/libgo/go/cmd/go/testdata/script/mod_case.txt b/libgo/go/cmd/go/testdata/script/mod_case.txt new file mode 100644 index 0000000..ee818c2 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_case.txt @@ -0,0 +1,20 @@ +env GO111MODULE=on + +go get rsc.io/QUOTE +go list -m all +stdout '^rsc.io/quote v1.5.2' +stdout '^rsc.io/QUOTE v1.5.2' + +go list -f 'DIR {{.Dir}} DEPS {{.Deps}}' rsc.io/QUOTE/QUOTE +stdout 'DEPS.*rsc.io/quote' +stdout 'DIR.*!q!u!o!t!e' + +go get rsc.io/QUOTE@v1.5.3-PRE +go list -m all +stdout '^rsc.io/QUOTE v1.5.3-PRE' + +go list -f '{{.Dir}}' rsc.io/QUOTE/QUOTE +stdout '!q!u!o!t!e@v1.5.3-!p!r!e' + +-- go.mod -- +module x diff --git a/libgo/go/cmd/go/testdata/script/mod_case_cgo.txt b/libgo/go/cmd/go/testdata/script/mod_case_cgo.txt new file mode 100644 index 0000000..917bce9 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_case_cgo.txt @@ -0,0 +1,9 @@ +[!cgo] skip + +env GO111MODULE=on + +go get rsc.io/CGO +go build rsc.io/CGO + +-- go.mod -- +module x diff --git a/libgo/go/cmd/go/testdata/script/mod_convert_dep.txt b/libgo/go/cmd/go/testdata/script/mod_convert_dep.txt new file mode 100644 index 0000000..cc1083b --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_convert_dep.txt @@ -0,0 +1,9 @@ +env GO111MODULE=on + +cd $WORK/test/x +go list -m all +stdout '^m$' + +-- $WORK/test/Gopkg.lock -- +-- $WORK/test/x/x.go -- +package x // import "m/x" diff --git a/libgo/go/cmd/go/testdata/script/mod_convert_git.txt b/libgo/go/cmd/go/testdata/script/mod_convert_git.txt new file mode 100644 index 0000000..5ef534a --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_convert_git.txt @@ -0,0 +1,10 @@ +env GO111MODULE=on + +# detect root of module tree as root of enclosing git repo +cd $WORK/test/x +go list -m all +stdout '^m$' + +-- $WORK/test/.git/config -- +-- $WORK/test/x/x.go -- +package x // import "m/x" diff --git a/libgo/go/cmd/go/testdata/script/mod_convert_glide.txt b/libgo/go/cmd/go/testdata/script/mod_convert_glide.txt new file mode 100644 index 0000000..50460bb --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_convert_glide.txt @@ -0,0 +1,9 @@ +env GO111MODULE=on + +cd $WORK/test/x +go list -m all +stdout '^m$' + +-- $WORK/test/glide.lock -- +-- $WORK/test/x/x.go -- +package x // import "m/x" diff --git a/libgo/go/cmd/go/testdata/script/mod_convert_glockfile.txt b/libgo/go/cmd/go/testdata/script/mod_convert_glockfile.txt new file mode 100644 index 0000000..4d9aaff --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_convert_glockfile.txt @@ -0,0 +1,9 @@ +env GO111MODULE=on + +cd $WORK/test/x +go list -m all +stdout '^m$' + +-- $WORK/test/GLOCKFILE -- +-- $WORK/test/x/x.go -- +package x // import "m/x" diff --git a/libgo/go/cmd/go/testdata/script/mod_convert_godeps.txt b/libgo/go/cmd/go/testdata/script/mod_convert_godeps.txt new file mode 100644 index 0000000..61fbab1 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_convert_godeps.txt @@ -0,0 +1,10 @@ +env GO111MODULE=on + +cd $WORK/test/x +go list -m all +stdout '^m$' + +-- $WORK/test/Godeps/Godeps.json -- +{} +-- $WORK/test/x/x.go -- +package x // import "m/x" diff --git a/libgo/go/cmd/go/testdata/script/mod_convert_tsv.txt b/libgo/go/cmd/go/testdata/script/mod_convert_tsv.txt new file mode 100644 index 0000000..5b82d85 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_convert_tsv.txt @@ -0,0 +1,9 @@ +env GO111MODULE=on + +cd $WORK/test/x +go list -m all +stdout '^m$' + +-- $WORK/test/dependencies.tsv -- +-- $WORK/test/x/x.go -- +package x // import "m/x" diff --git a/libgo/go/cmd/go/testdata/script/mod_convert_vendor_conf.txt b/libgo/go/cmd/go/testdata/script/mod_convert_vendor_conf.txt new file mode 100644 index 0000000..b45d3b6 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_convert_vendor_conf.txt @@ -0,0 +1,9 @@ +env GO111MODULE=on + +cd $WORK/test/x +go list -m all +stdout '^m$' + +-- $WORK/test/vendor.conf -- +-- $WORK/test/x/x.go -- +package x // import "m/x" diff --git a/libgo/go/cmd/go/testdata/script/mod_convert_vendor_json.txt b/libgo/go/cmd/go/testdata/script/mod_convert_vendor_json.txt new file mode 100644 index 0000000..cb6e5fe --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_convert_vendor_json.txt @@ -0,0 +1,10 @@ +env GO111MODULE=on + +cd $WORK/test/x +go list -m all +stdout '^m$' + +-- $WORK/test/vendor/vendor.json -- +{} +-- $WORK/test/x/x.go -- +package x // import "m/x" diff --git a/libgo/go/cmd/go/testdata/script/mod_convert_vendor_manifest.txt b/libgo/go/cmd/go/testdata/script/mod_convert_vendor_manifest.txt new file mode 100644 index 0000000..bcf1851 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_convert_vendor_manifest.txt @@ -0,0 +1,10 @@ +env GO111MODULE=on + +cd $WORK/test/x +go list -m all +stdout '^m$' + +-- $WORK/test/vendor/manifest -- +{} +-- $WORK/test/x/x.go -- +package x // import "m/x" diff --git a/libgo/go/cmd/go/testdata/script/mod_convert_vendor_yml.txt b/libgo/go/cmd/go/testdata/script/mod_convert_vendor_yml.txt new file mode 100644 index 0000000..0cd245b --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_convert_vendor_yml.txt @@ -0,0 +1,9 @@ +env GO111MODULE=on + +cd $WORK/test/x +go list -m all +stdout '^m$' + +-- $WORK/test/vendor.yml -- +-- $WORK/test/x/x.go -- +package x // import "m/x" diff --git a/libgo/go/cmd/go/testdata/script/mod_doc.txt b/libgo/go/cmd/go/testdata/script/mod_doc.txt new file mode 100644 index 0000000..450d857 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_doc.txt @@ -0,0 +1,36 @@ +# go doc should find module documentation + +[gccgo] stop 'no go doc' + +env GO111MODULE=on + +go doc y +stdout 'Package y is.*alphabet' +stdout 'import "x/y"' +go doc x/y +stdout 'Package y is.*alphabet' +! go doc quote.Hello +stderr 'doc: symbol quote is not a type' # because quote is not in local cache +go list rsc.io/quote # now it is +go doc quote.Hello +stdout 'Hello returns a greeting' +go doc quote +stdout 'Package quote collects pithy sayings.' + +# Double-check go doc y when y is not in GOPATH/src. +env GOPATH=$WORK/altgopath +go doc x/y +stdout 'Package y is.*alphabet' +go doc y +stdout 'Package y is.*alphabet' + +-- go.mod -- +module x +require rsc.io/quote v1.5.2 + +-- y/y.go -- +// Package y is the next to last package of the alphabet. +package y + +-- x.go -- +package x diff --git a/libgo/go/cmd/go/testdata/script/mod_domain_root.txt b/libgo/go/cmd/go/testdata/script/mod_domain_root.txt new file mode 100644 index 0000000..e34cc29 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_domain_root.txt @@ -0,0 +1,12 @@ +# Module paths that are domain roots should resolve. +# (example.com not example.com/something) + +env GO111MODULE=on +go build + +-- go.mod -- +module x + +-- x.go -- +package x +import _ "example.com" diff --git a/libgo/go/cmd/go/testdata/script/mod_download.txt b/libgo/go/cmd/go/testdata/script/mod_download.txt new file mode 100644 index 0000000..6be6acb --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_download.txt @@ -0,0 +1,64 @@ +env GO111MODULE=on + +# download with version should print nothing +go mod download rsc.io/quote@v1.5.0 +! stdout . + +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.0.info +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.0.mod +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.0.zip + +# download -json with version should print JSON +go mod download -json 'rsc.io/quote@<=v1.5.0' +stdout '^\t"Path": "rsc.io/quote"' +stdout '^\t"Version": "v1.5.0"' +stdout '^\t"Info": ".*(\\\\|/)pkg(\\\\|/)mod(\\\\|/)cache(\\\\|/)download(\\\\|/)rsc.io(\\\\|/)quote(\\\\|/)@v(\\\\|/)v1.5.0.info"' +stdout '^\t"GoMod": ".*(\\\\|/)pkg(\\\\|/)mod(\\\\|/)cache(\\\\|/)download(\\\\|/)rsc.io(\\\\|/)quote(\\\\|/)@v(\\\\|/)v1.5.0.mod"' +stdout '^\t"Zip": ".*(\\\\|/)pkg(\\\\|/)mod(\\\\|/)cache(\\\\|/)download(\\\\|/)rsc.io(\\\\|/)quote(\\\\|/)@v(\\\\|/)v1.5.0.zip"' +stdout '^\t"Sum": "h1:6fJa6E\+wGadANKkUMlZ0DhXFpoKlslOQDCo259XtdIE="' # hash of testdata/mod version, not real version! +stdout '^\t"GoModSum": "h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe\+TKr0="' +! stdout '"Error"' + +# download queries above should not have added to go.mod. +go list -m all +! stdout rsc.io + +# add to go.mod so we can test non-query downloads +go mod edit -require rsc.io/quote@v1.5.2 +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.info +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip + +# module loading will page in the info and mod files +go list -m all +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.info +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip + +# download will fetch and unpack the zip file +go mod download +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.info +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip +exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2 + +go mod download -json +stdout '^\t"Path": "rsc.io/quote"' +stdout '^\t"Version": "v1.5.2"' +stdout '^\t"Info": ".*(\\\\|/)pkg(\\\\|/)mod(\\\\|/)cache(\\\\|/)download(\\\\|/)rsc.io(\\\\|/)quote(\\\\|/)@v(\\\\|/)v1.5.2.info"' +stdout '^\t"GoMod": ".*(\\\\|/)pkg(\\\\|/)mod(\\\\|/)cache(\\\\|/)download(\\\\|/)rsc.io(\\\\|/)quote(\\\\|/)@v(\\\\|/)v1.5.2.mod"' +stdout '^\t"Zip": ".*(\\\\|/)pkg(\\\\|/)mod(\\\\|/)cache(\\\\|/)download(\\\\|/)rsc.io(\\\\|/)quote(\\\\|/)@v(\\\\|/)v1.5.2.zip"' +stdout '^\t"Dir": ".*(\\\\|/)pkg(\\\\|/)mod(\\\\|/)rsc.io(\\\\|/)quote@v1.5.2"' + +# download will follow replacements +go mod edit -require rsc.io/quote@v1.5.1 -replace rsc.io/quote@v1.5.1=rsc.io/quote@v1.5.3-pre1 +go mod download +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.1.zip +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.3-pre1.zip + +# download will not follow replacements for explicit module queries +go mod download -json rsc.io/quote@v1.5.1 +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.1.zip + +-- go.mod -- +module m diff --git a/libgo/go/cmd/go/testdata/script/mod_edit.txt b/libgo/go/cmd/go/testdata/script/mod_edit.txt new file mode 100644 index 0000000..60a6f74 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_edit.txt @@ -0,0 +1,136 @@ +env GO111MODULE=on + +# Test that go mod edits and related mod flags work. +# Also test that they can use a dummy name that isn't resolvable. golang.org/issue/24100 + +# go mod init +! go mod init +stderr 'cannot determine module path' +! exists go.mod + +go mod init x.x/y/z +stderr 'creating new go.mod: module x.x/y/z' +cmp go.mod $WORK/go.mod.init + +! go mod init +cmp go.mod $WORK/go.mod.init + +# go mod edits +go mod edit -droprequire=x.1 -require=x.1@v1.0.0 -require=x.2@v1.1.0 -droprequire=x.2 -exclude='x.1 @ v1.2.0' -exclude=x.1@v1.2.1 -replace=x.1@v1.3.0=y.1@v1.4.0 -replace='x.1@v1.4.0 = ../z' +cmp go.mod $WORK/go.mod.edit1 +go mod edit -droprequire=x.1 -dropexclude=x.1@v1.2.1 -dropreplace=x.1@v1.3.0 -require=x.3@v1.99.0 +cmp go.mod $WORK/go.mod.edit2 + +# go mod edit -json +go mod edit -json +cmp stdout $WORK/go.mod.json + +# go mod edit -replace +go mod edit -replace=x.1@v1.3.0=y.1/v2@v2.3.5 -replace=x.1@v1.4.0=y.1/v2@v2.3.5 +cmp go.mod $WORK/go.mod.edit3 +go mod edit -replace=x.1=y.1/v2@v2.3.6 +cmp go.mod $WORK/go.mod.edit4 +go mod edit -dropreplace=x.1 +cmp go.mod $WORK/go.mod.edit5 + +# go mod edit -fmt +cp $WORK/go.mod.badfmt go.mod +go mod edit -fmt -print # -print should avoid writing file +cmp stdout $WORK/go.mod.edit4 +cmp go.mod $WORK/go.mod.badfmt +go mod edit -fmt # without -print, should write file (and nothing to stdout) +! stdout . +cmp go.mod $WORK/go.mod.edit4 + +-- x.go -- +package x + +-- w/w.go -- +package w + +-- $WORK/go.mod.init -- +module x.x/y/z +-- $WORK/go.mod.edit1 -- +module x.x/y/z + +require x.1 v1.0.0 + +exclude ( + x.1 v1.2.0 + x.1 v1.2.1 +) + +replace ( + x.1 v1.3.0 => y.1 v1.4.0 + x.1 v1.4.0 => ../z +) +-- $WORK/go.mod.edit2 -- +module x.x/y/z + +exclude x.1 v1.2.0 + +replace x.1 v1.4.0 => ../z + +require x.3 v1.99.0 +-- $WORK/go.mod.json -- +{ + "Module": { + "Path": "x.x/y/z" + }, + "Require": [ + { + "Path": "x.3", + "Version": "v1.99.0" + } + ], + "Exclude": [ + { + "Path": "x.1", + "Version": "v1.2.0" + } + ], + "Replace": [ + { + "Old": { + "Path": "x.1", + "Version": "v1.4.0" + }, + "New": { + "Path": "../z" + } + } + ] +} +-- $WORK/go.mod.edit3 -- +module x.x/y/z + +exclude x.1 v1.2.0 + +replace ( + x.1 v1.3.0 => y.1/v2 v2.3.5 + x.1 v1.4.0 => y.1/v2 v2.3.5 +) + +require x.3 v1.99.0 +-- $WORK/go.mod.edit4 -- +module x.x/y/z + +exclude x.1 v1.2.0 + +replace x.1 => y.1/v2 v2.3.6 + +require x.3 v1.99.0 +-- $WORK/go.mod.edit5 -- +module x.x/y/z + +exclude x.1 v1.2.0 + +require x.3 v1.99.0 +-- $WORK/go.mod.badfmt -- +module x.x/y/z + +exclude x.1 v1.2.0 + +replace x.1 => y.1/v2 v2.3.6 + +require x.3 v1.99.0 diff --git a/libgo/go/cmd/go/testdata/script/mod_enabled.txt b/libgo/go/cmd/go/testdata/script/mod_enabled.txt new file mode 100644 index 0000000..8eef870 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_enabled.txt @@ -0,0 +1,82 @@ +# GO111MODULE=auto should only trigger outside GOPATH/src +env GO111MODULE=auto + +cd $GOPATH/src/x/y/z +go env GOMOD +! stdout . # no non-empty lines +! go list -m -f {{.GoMod}} +stderr 'not using modules' + +cd $GOPATH/src/x/y/z/w +go env GOMOD +! stdout . + +cd $GOPATH/src/x/y +go env GOMOD +! stdout . + +cd $GOPATH/foo +go env GOMOD +stdout foo[/\\]go.mod +go list -m -f {{.GoMod}} +stdout foo[/\\]go.mod + +cd $GOPATH/foo/bar/baz +go env GOMOD +stdout foo[/\\]go.mod + +# GO111MODULE=on should trigger everywhere +env GO111MODULE=on + +cd $GOPATH/src/x/y/z +go env GOMOD +stdout z[/\\]go.mod + +cd $GOPATH/src/x/y/z/w +go env GOMOD +stdout z[/\\]go.mod + +cd $GOPATH/src/x/y +go env GOMOD +! stdout . +! go list -m +stderr 'cannot find main module' + +cd $GOPATH/foo +go env GOMOD +stdout foo[/\\]go.mod + +cd $GOPATH/foo/bar/baz +go env GOMOD +stdout foo[/\\]go.mod + +# GO111MODULE=off should trigger nowhere +env GO111MODULE=off + +cd $GOPATH/src/x/y/z +go env GOMOD +! stdout .+ + +cd $GOPATH/foo +go env GOMOD +! stdout .+ + +cd $GOPATH/foo/bar/baz +go env GOMOD +! stdout .+ + +# GO111MODULE=auto should ignore and warn about /tmp/go.mod +env GO111MODULE=auto +cp $GOPATH/src/x/y/z/go.mod $WORK/tmp/go.mod +mkdir $WORK/tmp/mydir +cd $WORK/tmp/mydir +go env GOMOD +! stdout .+ +stderr '^go: warning: ignoring go.mod in system temp root ' + +-- $GOPATH/src/x/y/z/go.mod -- +module x/y/z +-- $GOPATH/src/x/y/z/w/w.txt -- +-- $GOPATH/foo/go.mod -- +module example.com/mod +-- $GOPATH/foo/bar/baz/quux.txt -- diff --git a/libgo/go/cmd/go/testdata/script/mod_file_proxy.txt b/libgo/go/cmd/go/testdata/script/mod_file_proxy.txt new file mode 100644 index 0000000..8de6d7d --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_file_proxy.txt @@ -0,0 +1,25 @@ +# Allow (cached) downloads for -mod=readonly. +env GO111MODULE=on +env GOPATH=$WORK/gopath1 +cd $WORK/x +go mod edit -fmt +go list -mod=readonly +env GOPROXY=file:///nonexist +go list +grep v1.5.1 $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/list + +# Use download cache as file:/// proxy. +[windows] stop # TODO: file://$WORK puts backslashes in the URL +env GOPATH=$WORK/gopath2 +env GOPROXY=file:///nonexist +! go list +env GOPROXY=file://$WORK/gopath1/pkg/mod/cache/download +go list +grep v1.5.1 $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/list + +-- $WORK/x/go.mod -- +module x +require rsc.io/quote v1.5.1 +-- $WORK/x/x.go -- +package x +import _ "rsc.io/quote" diff --git a/libgo/go/cmd/go/testdata/script/mod_find.txt b/libgo/go/cmd/go/testdata/script/mod_find.txt new file mode 100644 index 0000000..f4ac8d0 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_find.txt @@ -0,0 +1,91 @@ +# Derive module path from import comment. +cd $WORK/x +exists x.go +go mod init +stderr 'module x' + +# Import comment works even with CRLF line endings. +rm go.mod +addcrlf x.go +go mod init +stderr 'module x' + +# go mod should die in GOPATH if modules are not enabled for GOPATH +cd $GOPATH/src/example.com/x/y +! go mod init +stderr 'go: modules disabled inside GOPATH/src by GO111MODULE=auto; see ''go help modules''' + +env GO111MODULE=on + +# Derive module path from location inside GOPATH. +cd $GOPATH/src/example.com/x/y +go mod init +stderr 'module example.com/x/y$' +rm go.mod + +# Module path from Godeps/Godeps.json overrides GOPATH. +cd $GOPATH/src/example.com/x/y/z +go mod init +stderr 'unexpected.com/z' +rm go.mod + +# Empty directory outside GOPATH fails. +mkdir $WORK/empty +cd $WORK/empty +! go mod init +stderr 'cannot determine module path for source directory' +rm go.mod + +# Empty directory inside GOPATH/src uses location inside GOPATH. +mkdir $GOPATH/src/empty +cd $GOPATH/src/empty +go mod init +stderr 'empty' +rm go.mod + +[!symlink] stop + +# gplink1/src/empty where gopathlink -> GOPATH +symlink $WORK/gopathlink -> gopath +cd $WORK/gopathlink/src/empty +go mod init +rm go.mod + +# GOPATH/src/link where link -> out of GOPATH +symlink $GOPATH/src/link -> $WORK/empty +cd $WORK/empty +! go mod init +cd $GOPATH/src/link +go mod init +stderr link +rm go.mod + +# GOPATH/src/empty where GOPATH itself is a symlink +env GOPATH=$WORK/gopathlink +cd $GOPATH/src/empty +go mod init +rm go.mod +cd $WORK/gopath/src/empty +go mod init +rm go.mod + +# GOPATH/src/link where GOPATH and link are both symlinks +cd $GOPATH/src/link +go mod init +stderr link +rm go.mod + +# Too hard: doesn't match unevaluated nor completely evaluated. (Only partially evaluated.) +# Whether this works depends on which OS we are running on. +# cd $WORK/gopath/src/link +# ! go mod init + +-- $WORK/x/x.go -- +package x // import "x" + +-- $GOPATH/src/example.com/x/y/y.go -- +package y +-- $GOPATH/src/example.com/x/y/z/z.go -- +package z +-- $GOPATH/src/example.com/x/y/z/Godeps/Godeps.json -- +{"ImportPath": "unexpected.com/z"} diff --git a/libgo/go/cmd/go/testdata/script/mod_fs_patterns.txt b/libgo/go/cmd/go/testdata/script/mod_fs_patterns.txt new file mode 100644 index 0000000..d7d3e03 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_fs_patterns.txt @@ -0,0 +1,66 @@ +# File system pattern searches should skip sub-modules and vendor directories. + +env GO111MODULE=on + +cd x + +# all packages +go list all +stdout ^m$ +stdout ^m/vendor$ +! stdout vendor/ +stdout ^m/y$ +! stdout ^m/y/z + +# path pattern +go list m/... +stdout ^m$ +stdout ^m/vendor$ +! stdout vendor/ +stdout ^m/y$ +! stdout ^m/y/z + +# directory pattern +go list ./... +stdout ^m$ +stdout ^m/vendor$ +! stdout vendor/ +stdout ^m/y$ +! stdout ^m/y/z + +# non-existent directory should not prompt lookups +! go build -mod=readonly example.com/nonexist +stderr 'import lookup disabled' + +! go build -mod=readonly ./nonexist +! stderr 'import lookup disabled' +stderr '^go: no such directory ./nonexist' + +! go build -mod=readonly ./go.mod +! stderr 'import lookup disabled' +stderr '^go: ./go.mod is not a directory' + +-- x/go.mod -- +module m + +-- x/x.go -- +package x + +-- x/vendor/v/v.go -- +package v +import _ "golang.org/x/crypto" + +-- x/vendor/v.go -- +package main + +-- x/y/y.go -- +package y + +-- x/y/z/go.mod -- +syntax error! + +-- x/y/z/z.go -- +package z + +-- x/y/z/w/w.go -- +package w diff --git a/libgo/go/cmd/go/testdata/script/mod_get_commit.txt b/libgo/go/cmd/go/testdata/script/mod_get_commit.txt new file mode 100644 index 0000000..589a791 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_get_commit.txt @@ -0,0 +1,53 @@ +env GO111MODULE=on + +# @commit should resolve + +# golang.org/x/text/language@commit should not resolve with -m, +# because that's not a module path. +! go get -m golang.org/x/text/language@14c0d48 + +# ... but it should work without -m. +# because of -d, the compiler should not run +go get -d -x golang.org/x/text/language@14c0d48 +! stderr 'compile|cp|gccgo .*language\.a$' + +# go get should skip build with no Go files in root +go get golang.org/x/text@14c0d48 + +# ... and go get should skip build with -m +go get -m golang.org/x/text@14c0d48 + +# dropping -d, we should see a build. +go get -x golang.org/x/text/language@14c0d48 +stderr 'compile|cp|gccgo .*language\.a$' + +# BUG: after the build, the package should not be stale, as 'go install' would +# not do anything further. +go list -f '{{.Stale}}' golang.org/x/text/language +stdout ^true + +# install after get should not run the compiler again. +go install -x golang.org/x/text/language +! stderr 'compile|cp|gccgo .*language\.a$' + +# even with -d, we should see an error for unknown packages. +! go get -d -x golang.org/x/text/foo@14c0d48 + +# get pseudo-version should record that version +go get rsc.io/quote@v0.0.0-20180214005840-23179ee8a569 +grep 'rsc.io/quote v0.0.0-20180214005840-23179ee8a569' go.mod + +# but as commit should record as v1.5.1 +go get rsc.io/quote@23179ee8 +grep 'rsc.io/quote v1.5.1' go.mod + +# go mod edit -require does not interpret commits +go mod edit -require rsc.io/quote@23179ee +grep 'rsc.io/quote 23179ee' go.mod + +# but other commands fix them +go mod graph +grep 'rsc.io/quote v1.5.1' go.mod + +-- go.mod -- +module x diff --git a/libgo/go/cmd/go/testdata/script/mod_get_downgrade.txt b/libgo/go/cmd/go/testdata/script/mod_get_downgrade.txt new file mode 100644 index 0000000..ac814da --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_get_downgrade.txt @@ -0,0 +1,39 @@ +env GO111MODULE=on + +# downgrade sampler should downgrade quote +go get rsc.io/sampler@v1.0.0 +go list -m all +stdout 'rsc.io/quote v1.4.0' +stdout 'rsc.io/sampler v1.0.0' + +# downgrade sampler away should downgrade quote further +go get rsc.io/sampler@none +go list -m all +stdout 'rsc.io/quote v1.3.0' + +# downgrade should report inconsistencies and not change go.mod +go get rsc.io/quote@v1.5.1 +go list -m all +stdout 'rsc.io/quote v1.5.1' +stdout 'rsc.io/sampler v1.3.0' +! go get rsc.io/sampler@v1.0.0 rsc.io/quote@v1.5.2 golang.org/x/text@none +stderr 'go get: inconsistent versions:\n\trsc.io/quote@v1.5.2 requires golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c \(not golang.org/x/text@none\), rsc.io/sampler@v1.3.0 \(not rsc.io/sampler@v1.0.0\)' +go list -m all +stdout 'rsc.io/quote v1.5.1' +stdout 'rsc.io/sampler v1.3.0' + +# go get -u args should limit upgrades +cp go.mod.empty go.mod +go get -u rsc.io/quote@v1.4.0 rsc.io/sampler@v1.0.0 +go list -m all +stdout 'rsc.io/quote v1.4.0' +stdout 'rsc.io/sampler v1.0.0' +! stdout golang.org/x/text + +-- go.mod -- +module x +require rsc.io/quote v1.5.1 +-- go.mod.empty -- +module x +-- x.go -- +package x diff --git a/libgo/go/cmd/go/testdata/script/mod_get_incompatible.txt b/libgo/go/cmd/go/testdata/script/mod_get_incompatible.txt new file mode 100644 index 0000000..b210715 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_get_incompatible.txt @@ -0,0 +1,26 @@ +env GO111MODULE=on + +go list x +go list -m all +stdout 'rsc.io/breaker v2.0.0\+incompatible' + +cp go.mod2 go.mod +go get rsc.io/breaker@7307b30 +go list -m all +stdout 'rsc.io/breaker v2.0.0\+incompatible' + +go get rsc.io/breaker@v2.0.0 +go list -m all +stdout 'rsc.io/breaker v2.0.0\+incompatible' + +-- go.mod -- +module x + +-- go.mod2 -- +module x +require rsc.io/breaker v1.0.0 + +-- x.go -- +package x +import "rsc.io/breaker" +var _ = breaker.XX diff --git a/libgo/go/cmd/go/testdata/script/mod_get_indirect.txt b/libgo/go/cmd/go/testdata/script/mod_get_indirect.txt new file mode 100644 index 0000000..3ae5833 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_get_indirect.txt @@ -0,0 +1,51 @@ +env GO111MODULE=on + +# get -u should find quote v1.5.2 +go get -u +go list -m all +stdout 'quote v1.5.2$' +grep 'rsc.io/quote v1.5.2$' go.mod + +# it should also update x/text later than requested by v1.5.2 +go list -m -f '{{.Path}} {{.Version}}{{if .Indirect}} // indirect{{end}}' all +stdout '^golang.org/x/text [v0-9a-f\.-]+ // indirect' +grep 'golang.org/x/text [v0-9a-f\.-]+ // indirect' go.mod + +# importing an empty module root as a package makes it direct. +# TODO(bcmills): This doesn't seem correct. Fix is in the next change. +cp $WORK/tmp/usetext.go x.go +go list -e +grep 'golang.org/x/text [v0-9a-f\.-]+ // indirect' go.mod + +# indirect tag should be removed upon seeing direct import. +cp $WORK/tmp/uselang.go x.go +go list +grep 'rsc.io/quote v1.5.2$' go.mod +grep 'golang.org/x/text [v0-9a-f\.-]+$' go.mod + +# indirect tag should be added by go mod tidy +cp $WORK/tmp/usequote.go x.go +go mod tidy +grep 'rsc.io/quote v1.5.2$' go.mod +grep 'golang.org/x/text [v0-9a-f\.-]+ // indirect' go.mod + +# requirement should be dropped entirely if not needed +cp $WORK/tmp/uselang.go x.go +go mod tidy +! grep rsc.io/quote go.mod +grep 'golang.org/x/text [v0-9a-f\.-]+$' go.mod + +-- go.mod -- +module x +require rsc.io/quote v1.5.1 +-- x.go -- +package x +-- $WORK/tmp/usetext.go -- +package x +import _ "golang.org/x/text" +-- $WORK/tmp/uselang.go -- +package x +import _ "golang.org/x/text/language" +-- $WORK/tmp/usequote.go -- +package x +import _ "rsc.io/quote" diff --git a/libgo/go/cmd/go/testdata/script/mod_get_local.txt b/libgo/go/cmd/go/testdata/script/mod_get_local.txt new file mode 100644 index 0000000..4edda99 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_get_local.txt @@ -0,0 +1,61 @@ +# Test 'go get' with a local module with a name that is not valid for network lookup. + +env GO111MODULE=on +go mod edit -fmt +cp go.mod go.mod.orig + +# 'go get -u -m' within the main module should work, even if it has a local-only name. +cp go.mod.orig go.mod +go get -u -m +grep 'rsc.io/quote.*v1.5.2' go.mod +grep 'golang.org/x/text.*v0.3.0' go.mod +cp go.mod go.mod.implicitmod + +# 'go get -u -m' with the name of the main module should be equivalent to +# 'go get -u -m' without any further arguments. +cp go.mod.orig go.mod +go get -u -m local +cmp go.mod go.mod.implicitmod + +# 'go get -u -d' in the empty root of the main module should update the +# dependencies of all packages in the module. +cp go.mod.orig go.mod +go get -u -d +cmp go.mod go.mod.implicitmod + +# 'go get -u -d .' within a package in the main module updates all dependencies +# of the main module. +# TODO: Determine whether that behavior is a bug. +# (https://golang.org/issue/26902) +cp go.mod.orig go.mod +cd uselang +go get -u -d . +cd .. +grep 'rsc.io/quote.*v1.5.2' go.mod +grep 'golang.org/x/text.*v0.3.0' go.mod +cp go.mod go.mod.dotpkg + +# 'go get -u -d' with an explicit package in the main module updates +# all dependencies of the main module. +# TODO: Determine whether that behavior is a bug. +# (https://golang.org/issue/26902) +cp go.mod.orig go.mod +go get -u -d local/uselang +cmp go.mod go.mod.dotpkg + + +-- go.mod -- +module local + +require ( + golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c + rsc.io/quote v1.3.0 +) + +-- uselang/uselang.go -- +package uselang +import _ "golang.org/x/text/language" + +-- usequote/usequote.go -- +package usequote +import _ "rsc.io/quote" diff --git a/libgo/go/cmd/go/testdata/script/mod_get_moved.txt b/libgo/go/cmd/go/testdata/script/mod_get_moved.txt new file mode 100644 index 0000000..be91449 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_get_moved.txt @@ -0,0 +1,37 @@ +env GO111MODULE=on + +# A 'go get' that worked at a previous version should continue to work at that version, +# even if the package was subsequently moved into a submodule. +go mod init example.com/foo +go get -d example.com/split/subpkg@v1.0.0 +go list -m all +stdout 'example.com/split v1.0.0' + +# A 'go get' that simultaneously upgrades away conflicting package defitions is not ambiguous. +go get example.com/split/subpkg@v1.1.0 + +# A 'go get' without an upgrade should find the package. +rm go.mod +go mod init example.com/foo +go get -d example.com/split/subpkg +go list -m all +stdout 'example.com/split/subpkg v1.1.0' + + +# A 'go get' that worked at a previous version should continue to work at that version, +# even if the package was subsequently moved into a parent module. +rm go.mod +go mod init example.com/foo +go get -d example.com/join/subpkg@v1.0.0 +go list -m all +stdout 'example.com/join/subpkg v1.0.0' + +# A 'go get' that simultaneously upgrades away conflicting package definitions is not ambiguous. +go get example.com/join/subpkg@v1.1.0 + +# A 'go get' without an upgrade should find the package. +rm go.mod +go mod init example.com/foo +go get -d example.com/join/subpkg@v1.1.0 +go list -m all +stdout 'example.com/join v1.1.0' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_none.txt b/libgo/go/cmd/go/testdata/script/mod_get_none.txt new file mode 100644 index 0000000..5aec209 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_get_none.txt @@ -0,0 +1,12 @@ +env GO111MODULE=on + +go mod init example.com/foo + +# 'go get bar@none' should be a no-op if module bar is not active. +go get example.com/bar@none +go list -m all +! stdout example.com/bar + +go get example.com/bar@none +go list -m all +! stdout example.com/bar diff --git a/libgo/go/cmd/go/testdata/script/mod_get_pseudo.txt b/libgo/go/cmd/go/testdata/script/mod_get_pseudo.txt new file mode 100644 index 0000000..3945fdf --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_get_pseudo.txt @@ -0,0 +1,80 @@ +env GO111MODULE=on + +# Testing git->module converter's generation of +incompatible tags; turn off proxy. +[!net] skip +[!exec:git] skip +env GOPROXY= + +# We can resolve the @master branch without unshallowing the local repository +# (even with older gits), so try that before we do anything else. +# (This replicates https://golang.org/issue/26713 with git 2.7.4.) +go get -m github.com/rsc/legacytest@master +go list -m all +stdout '^github.com/rsc/legacytest v2\.0\.1-0\.\d{14}-7303f7796364\+incompatible$' + +# get should include incompatible tags in "latest" calculation. +go get -m github.com/rsc/legacytest@latest +go list +go list -m all +stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$' + +# v2.0.1-0.pseudo+incompatible +go get -m ...test@7303f77 +go list -m all +stdout '^github.com/rsc/legacytest v2\.0\.1-0\.\d{14}-7303f7796364\+incompatible$' + +# v2.0.0+incompatible by tag+incompatible +go get -m ...test@v2.0.0+incompatible +go list -m all +stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$' + +# v2.0.0+incompatible by tag +go get -m ...test@v2.0.0 +go list -m all +stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$' + +# v2.0.0+incompatible by hash (back on master) +go get -m ...test@d7ae1e4 +go list -m all +stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$' + +# v1.2.1-0.pseudo +go get -m ...test@d2d4c3e +go list -m all +stdout '^github.com/rsc/legacytest v1\.2\.1-0\.\d{14}-d2d4c3ea6623$' + +# v1.2.0 +go get -m ...test@9f6f860 +go list -m all +stdout '^github.com/rsc/legacytest v1\.2\.0$' + +# v1.1.0-pre.0.pseudo +go get -m ...test@fb3c628 +go list -m all +stdout '^github.com/rsc/legacytest v1\.1\.0-pre\.0\.\d{14}-fb3c628075e3$' + +# v1.1.0-pre (no longer on master) +go get -m ...test@731e3b1 +go list -m all +stdout '^github.com/rsc/legacytest v1\.1\.0-pre$' + +# v1.0.1-0.pseudo +go get -m ...test@fa4f5d6 +go list -m all +stdout '^github.com/rsc/legacytest v1\.0\.1-0\.\d{14}-fa4f5d6a71c6$' + +# v1.0.0 +go get -m ...test@7fff7f3 +go list -m all +stdout '^github.com/rsc/legacytest v1\.0\.0$' + +# v0.0.0-pseudo +go get -m ...test@52853eb +go list -m all +stdout '^github.com/rsc/legacytest v0\.0\.0-\d{14}-52853eb7b552$' + +-- go.mod -- +module x +-- x.go -- +package x +import "github.com/rsc/legacytest" diff --git a/libgo/go/cmd/go/testdata/script/mod_get_upgrade.txt b/libgo/go/cmd/go/testdata/script/mod_get_upgrade.txt new file mode 100644 index 0000000..5eb5ff9 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_get_upgrade.txt @@ -0,0 +1,41 @@ +env GO111MODULE=on + +go get rsc.io/quote@v1.5.1 +go list -m all +stdout 'rsc.io/quote v1.5.1' +grep 'rsc.io/quote v1.5.1$' go.mod + +# get -u should update all dependencies +go get -u +grep 'rsc.io/quote v1.5.2$' go.mod +grep 'golang.org/x/text [v0-9a-f\.-]+ // indirect' go.mod + +# get -u rsc.io/sampler should update only sampler's dependencies +cp go.mod-v1.5.1 go.mod +go get -u rsc.io/sampler +grep 'rsc.io/quote v1.5.1$' go.mod +grep 'golang.org/x/text [v0-9a-f\.-]+ // indirect' go.mod + +# move to a pseudo-version after any tags +go get -m rsc.io/quote@dd9747d +grep 'rsc.io/quote v0.0.0-20180628003336-dd9747d19b04' go.mod + +# get -u should not jump off newer pseudo-version to earlier tag +go get -m -u +grep 'rsc.io/quote v0.0.0-20180628003336-dd9747d19b04' go.mod + +# move to earlier pseudo-version +go get -m rsc.io/quote@e7a685a342 +grep 'rsc.io/quote v0.0.0-20180214005133-e7a685a342c0' go.mod + +# get -u should jump off earlier pseudo-version to newer tag +go get -m -u +grep 'rsc.io/quote v1.5.2' go.mod + +-- go.mod -- +module x +require rsc.io/quote v1.1.0 + +-- go.mod-v1.5.1 -- +module x +require rsc.io/quote v1.5.1 diff --git a/libgo/go/cmd/go/testdata/script/mod_get_warning.txt b/libgo/go/cmd/go/testdata/script/mod_get_warning.txt new file mode 100644 index 0000000..36b5434 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_get_warning.txt @@ -0,0 +1,10 @@ +# go get in GO111MODULE=auto should warn when not using modules and go.mod exists + +env GO111MODULE=auto +mkdir z +cd z +! go get # fails because no code in directory, not the warning +stderr 'go get: warning: modules disabled by GO111MODULE=auto in GOPATH/src;\n\tignoring ..[/\\]go.mod;\n\tsee ''go help modules''' + +-- go.mod -- +module x diff --git a/libgo/go/cmd/go/testdata/script/mod_getmode_vendor.txt b/libgo/go/cmd/go/testdata/script/mod_getmode_vendor.txt new file mode 100644 index 0000000..3dd8d1b --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_getmode_vendor.txt @@ -0,0 +1,23 @@ +env GO111MODULE=on + +go get -m rsc.io/quote@v1.5.1 +go mod vendor +env GOPATH=$WORK/empty +env GOPROXY=file:///nonexist + +go list -mod=vendor +go list -mod=vendor -m -f '{{.Path}} {{.Version}} {{.Dir}}' all +stdout '^rsc.io/quote v1.5.1 .*vendor[\\/]rsc.io[\\/]quote$' +stdout '^golang.org/x/text v0.0.0.* .*vendor[\\/]golang.org[\\/]x[\\/]text$' + +! go list -mod=vendor -m rsc.io/quote@latest +stderr 'module lookup disabled by -mod=vendor' +! go get -mod=vendor -u +stderr 'go get: disabled by -mod=vendor' + +-- go.mod -- +module x + +-- x.go -- +package x +import _ "rsc.io/quote" diff --git a/libgo/go/cmd/go/testdata/script/mod_go_version.txt b/libgo/go/cmd/go/testdata/script/mod_go_version.txt new file mode 100644 index 0000000..f2de74c --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_go_version.txt @@ -0,0 +1,61 @@ +# Test support for declaring needed Go version in module. + +env GO111MODULE=on + +go list +! go build +stderr 'module requires Go 1.999' +go build sub.1 +! go build badsub.1 +stderr 'module requires Go 1.11111' + +go build versioned.1 +go mod edit -require versioned.1@v1.1.0 +! go build versioned.1 +stderr 'module requires Go 1.99999' + +-- go.mod -- +module m +go 1.999 +require ( + sub.1 v1.0.0 + badsub.1 v1.0.0 + versioned.1 v1.0.0 +) +replace ( + sub.1 => ./sub + badsub.1 => ./badsub + versioned.1 v1.0.0 => ./versioned1 + versioned.1 v1.1.0 => ./versioned2 +) + +-- x.go -- +package x + +-- sub/go.mod -- +module m +go 1.11 + +-- sub/x.go -- +package x + +-- badsub/go.mod -- +module m +go 1.11111 + +-- badsub/x.go -- +package x + +-- versioned1/go.mod -- +module versioned +go 1.0 + +-- versioned1/x.go -- +package x + +-- versioned2/go.mod -- +module versioned +go 1.99999 + +-- versioned2/x.go -- +package x diff --git a/libgo/go/cmd/go/testdata/script/mod_gobuild_import.txt b/libgo/go/cmd/go/testdata/script/mod_gobuild_import.txt new file mode 100644 index 0000000..932b8b6 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_gobuild_import.txt @@ -0,0 +1,74 @@ +# go/build's Import should find modules by invoking the go command + +go build -o $WORK/testimport.exe ./testimport + +# GO111MODULE=off +env GO111MODULE=off +! exec $WORK/testimport.exe x/y/z/w . + +# GO111MODULE=auto in GOPATH/src +env GO111MODULE= +! exec $WORK/testimport.exe x/y/z/w . +env GO111MODULE=auto +! exec $WORK/testimport.exe x/y/z/w . + +# GO111MODULE=auto outside GOPATH/src +cd $GOPATH/other +env GO111MODULE= +exec $WORK/testimport.exe other/x/y/z/w . +stdout w2.go + +! exec $WORK/testimport.exe x/y/z/w . +stderr 'cannot find module providing package x/y/z/w' + +cd z +env GO111MODULE=auto +exec $WORK/testimport.exe other/x/y/z/w . +stdout w2.go + +# GO111MODULE=on outside GOPATH/src +env GO111MODULE=on +exec $WORK/testimport.exe other/x/y/z/w . +stdout w2.go + +# GO111MODULE=on in GOPATH/src +cd $GOPATH/src +exec $WORK/testimport.exe x/y/z/w . +stdout w1.go +cd w +exec $WORK/testimport.exe x/y/z/w .. +stdout w1.go + +-- go.mod -- +module x/y/z + +-- z.go -- +package z + +-- w/w1.go -- +package w + +-- testimport/x.go -- +package main + +import ( + "fmt" + "go/build" + "log" + "os" + "strings" +) + +func main() { + p, err := build.Import(os.Args[1], os.Args[2], 0) + if err != nil { + log.Fatal(err) + } + fmt.Printf("%s\n%s\n", p.Dir, strings.Join(p.GoFiles, " ")) +} + +-- $GOPATH/other/go.mod -- +module other/x/y + +-- $GOPATH/other/z/w/w2.go -- +package w diff --git a/libgo/go/cmd/go/testdata/script/mod_gofmt_invalid.txt b/libgo/go/cmd/go/testdata/script/mod_gofmt_invalid.txt new file mode 100644 index 0000000..21edc7d --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_gofmt_invalid.txt @@ -0,0 +1,13 @@ +# Test for a crash in go fmt on invalid input when using modules. +# Issue 26792. + +env GO111MODULE=on +! go fmt x.go +! stderr panic + +-- go.mod -- +module x + +-- x.go -- +// Missing package declaration. +var V int diff --git a/libgo/go/cmd/go/testdata/script/mod_gopkg_unstable.txt b/libgo/go/cmd/go/testdata/script/mod_gopkg_unstable.txt new file mode 100644 index 0000000..d945cf3 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_gopkg_unstable.txt @@ -0,0 +1,22 @@ +env GO111MODULE=on + +cp go.mod.empty go.mod +go get -d gopkg.in/dummy.v2-unstable + +cp x.go.txt x.go +cp go.mod.empty go.mod +go list + +[!net] skip + +env GOPROXY= +go get gopkg.in/macaroon-bakery.v2-unstable/bakery +go list -m all +stdout 'gopkg.in/macaroon-bakery.v2-unstable v2.0.0-[0-9]+-[0-9a-f]+$' + +-- go.mod.empty -- +module m + +-- x.go.txt -- +package x +import _ "gopkg.in/dummy.v2-unstable" diff --git a/libgo/go/cmd/go/testdata/script/mod_graph.txt b/libgo/go/cmd/go/testdata/script/mod_graph.txt new file mode 100644 index 0000000..07968f5 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_graph.txt @@ -0,0 +1,10 @@ +env GO111MODULE=on + +go mod graph +stdout '^m rsc.io/quote@v1.5.2$' +stdout '^rsc.io/quote@v1.5.2 rsc.io/sampler@v1.3.0$' +! stdout '^m rsc.io/sampler@v1.3.0$' + +-- go.mod -- +module m +require rsc.io/quote v1.5.2 diff --git a/libgo/go/cmd/go/testdata/script/mod_import.txt b/libgo/go/cmd/go/testdata/script/mod_import.txt new file mode 100644 index 0000000..3985b43 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_import.txt @@ -0,0 +1,18 @@ +env GO111MODULE=on + +# latest rsc.io/quote should be v1.5.2 not v1.5.3-pre1 +go list +go list -m all +stdout 'rsc.io/quote v1.5.2' + +# but v1.5.3-pre1 should be a known version +go list -m -versions rsc.io/quote +stdout '^rsc.io/quote v1.0.0 v1.1.0 v1.2.0 v1.2.1 v1.3.0 v1.4.0 v1.5.0 v1.5.1 v1.5.2 v1.5.3-pre1$' + +-- go.mod -- +module x + +-- x.go -- +package x +import _ "rsc.io/quote" + diff --git a/libgo/go/cmd/go/testdata/script/mod_import_mod.txt b/libgo/go/cmd/go/testdata/script/mod_import_mod.txt new file mode 100644 index 0000000..b035e3d --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_import_mod.txt @@ -0,0 +1,7 @@ +# Test that GOPATH/pkg/mod is excluded +env GO111MODULE=off +! go list mod/foo +stderr 'disallowed import path' + +-- mod/foo/foo.go -- +package foo diff --git a/libgo/go/cmd/go/testdata/script/mod_init_dep.txt b/libgo/go/cmd/go/testdata/script/mod_init_dep.txt new file mode 100644 index 0000000..29c840b --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_init_dep.txt @@ -0,0 +1,34 @@ +env GO111MODULE=on + +# modconv uses git directly to examine what old 'go get' would +[!net] skip +[!exec:git] skip + +# go build should populate go.mod from Gopkg.lock +cp go.mod1 go.mod +go build +stderr 'copying requirements from Gopkg.lock' +go list -m all +! stderr 'copying requirements from Gopkg.lock' +stdout 'rsc.io/sampler v1.0.0' + +# go list should populate go.mod from Gopkg.lock +cp go.mod1 go.mod +go list +stderr 'copying requirements from Gopkg.lock' +go list +! stderr 'copying requirements from Gopkg.lock' +go list -m all +stdout 'rsc.io/sampler v1.0.0' + +-- go.mod1 -- +module x + +-- x.go -- +package x + +-- Gopkg.lock -- +[[projects]] + name = "rsc.io/sampler" + version = "v1.0.0" + diff --git a/libgo/go/cmd/go/testdata/script/mod_install_versioned.txt b/libgo/go/cmd/go/testdata/script/mod_install_versioned.txt new file mode 100644 index 0000000..03986d0 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_install_versioned.txt @@ -0,0 +1,12 @@ +env GO111MODULE=on + +go list -f '{{.Target}}' rsc.io/fortune +! stdout fortune@v1 +stdout 'fortune(\.exe)?$' + +go list -f '{{.Target}}' rsc.io/fortune/v2 +! stdout v2 +stdout 'fortune(\.exe)?$' + +-- go.mod -- +module m diff --git a/libgo/go/cmd/go/testdata/script/mod_internal.txt b/libgo/go/cmd/go/testdata/script/mod_internal.txt new file mode 100644 index 0000000..84e77c6 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_internal.txt @@ -0,0 +1,102 @@ +env GO111MODULE=on + +# golang.org/x/internal should be importable from other golang.org/x modules. +rm go.mod +go mod init golang.org/x/anything +go build . + +# ...and their tests... +go test +stdout PASS + +# ...but that should not leak into other modules. +! go build ./baddep +stderr golang.org[/\\]notx[/\\]useinternal +stderr 'use of internal package golang.org/x/.* not allowed' + +# Internal packages in the standard library should not leak into modules. +! go build ./fromstd +[!gccgo] stderr 'use of internal package internal/testenv not allowed' + +# Packages found via standard-library vendoring should not leak. +! go build ./fromstdvendor +[!gccgo] stderr 'use of vendored package golang_org/x/net/http/httpguts not allowed' + +env GO111MODULE=off +! go build ./fromstdvendor +[!gccgo] stderr 'cannot find package "golang_org/x/net/http/httpguts" in any of:' +env GO111MODULE=on + +# Dependencies should be able to use their own internal modules... +rm go.mod +go mod init golang.org/notx +go build ./throughdep + +# ... but other modules should not, even if they have transitive dependencies. +! go build . +stderr 'use of internal package golang.org/x/.* not allowed' + +# And transitive dependencies still should not leak. +! go build ./baddep +stderr golang.org[/\\]notx[/\\]useinternal +stderr 'use of internal package golang.org/x/.* not allowed' + +# Replacing an internal module should keep it internal to the same paths. +rm go.mod +go mod init golang.org/notx +go mod edit -replace golang.org/x/internal=./replace/golang.org/notx/internal +go build ./throughdep + +! go build ./baddep +stderr golang.org[/\\]notx[/\\]useinternal +stderr 'use of internal package golang.org/x/.* not allowed' + +go mod edit -replace golang.org/x/internal=./vendor/golang.org/x/internal +go build ./throughdep + +! go build ./baddep +stderr golang.org[/\\]notx[/\\]useinternal +stderr 'use of internal package golang.org/x/.* not allowed' + +-- useinternal.go -- +package useinternal +import _ "golang.org/x/internal/subtle" + +-- useinternal_test.go -- +package useinternal_test +import ( + "testing" + _ "golang.org/x/internal/subtle" +) + +func Test(*testing.T) {} + +-- throughdep/useinternal.go -- +package throughdep +import _ "golang.org/x/useinternal" + +-- baddep/useinternal.go -- +package baddep +import _ "golang.org/notx/useinternal" + +-- fromstd/useinternal.go -- +package fromstd +import _ "internal/testenv" + +-- fromstdvendor/useinternal.go -- +package fromstdvendor +import _ "golang_org/x/net/http/httpguts" + +-- replace/golang.org/notx/internal/go.mod -- +module golang.org/x/internal + +-- replace/golang.org/notx/internal/subtle/subtle.go -- +package subtle +// Ha ha! Nothing here! + +-- vendor/golang.org/x/internal/go.mod -- +module golang.org/x/internal + +-- vendor/golang.org/x/internal/subtle/subtle.go -- +package subtle +// Ha ha! Nothing here! diff --git a/libgo/go/cmd/go/testdata/script/mod_list.txt b/libgo/go/cmd/go/testdata/script/mod_list.txt new file mode 100644 index 0000000..2fa079f --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_list.txt @@ -0,0 +1,60 @@ +env GO111MODULE=on + +# list {{.Dir}} shows main module and go.mod but not not-yet-downloaded dependency dir. +go list -m -f '{{.Path}} {{.Main}} {{.GoMod}} {{.Dir}}' all +stdout '^x true .*[\\/]src[\\/]go.mod .*[\\/]src$' +stdout '^rsc.io/quote false .*[\\/]v1.5.2.mod $' + +# list {{.Dir}} shows dependency after download (and go list without -m downloads it) +go list -f '{{.Dir}}' rsc.io/quote +stdout '.*mod[\\/]rsc.io[\\/]quote@v1.5.2$' + +# downloaded dependencies are read-only +exists -readonly $GOPATH/pkg/mod/rsc.io/quote@v1.5.2 +exists -readonly $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/buggy + +# go clean -modcache can delete read-only dependencies +go clean -modcache +! exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2 + +# list {{.Dir}} shows replaced directories +cp go.mod2 go.mod +go list -f {{.Dir}} rsc.io/quote +go list -m -f '{{.Path}} {{.Version}} {{.Dir}}{{with .Replace}} {{.GoMod}} => {{.Version}} {{.Dir}} {{.GoMod}}{{end}}' all +stdout 'mod[\\/]rsc.io[\\/]quote@v1.5.1' +stdout 'v1.3.0.*mod[\\/]rsc.io[\\/]sampler@v1.3.1 .*[\\/]v1.3.1.mod => v1.3.1.*sampler@v1.3.1 .*[\\/]v1.3.1.mod' + +# list std should work +go list std +[!gccgo] stdout ^math/big + +# rsc.io/quote/buggy should be listable as a package +go list rsc.io/quote/buggy + +# rsc.io/quote/buggy should not be listable as a module +go list -m -e -f '{{.Error.Err}}' nonexist rsc.io/quote/buggy +stdout '^module "nonexist" is not a known dependency' +stdout '^module "rsc.io/quote/buggy" is not a known dependency' + +! go list -m nonexist rsc.io/quote/buggy +stderr '^go list -m nonexist: module "nonexist" is not a known dependency' +stderr '^go list -m rsc.io/quote/buggy: module "rsc.io/quote/buggy" is not a known dependency' + +# Module loader does not interfere with list -e (golang.org/issue/24149). +[!gccgo] go list -e -f '{{.Error.Err}}' database +[!gccgo] stdout 'no Go files in ' +[!gccgo] ! go list database +[!gccgo] stderr 'no Go files in ' + +-- go.mod -- +module x +require rsc.io/quote v1.5.2 + +-- go.mod2 -- +module x +require rsc.io/quote v1.5.1 +replace rsc.io/sampler v1.3.0 => rsc.io/sampler v1.3.1 + +-- x.go -- +package x +import _ "rsc.io/quote" diff --git a/libgo/go/cmd/go/testdata/script/mod_list_bad_import.txt b/libgo/go/cmd/go/testdata/script/mod_list_bad_import.txt new file mode 100644 index 0000000..258eb6a --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_list_bad_import.txt @@ -0,0 +1,73 @@ +# This test matches list_bad_import, but in module mode. +# Please keep them in sync. + +env GO111MODULE=on +cd example.com + +# Without -e, listing an otherwise-valid package with an unsatisfied direct import should fail. +# BUG: Today it succeeds. +go list -f '{{if .Error}}error{{end}} {{if .Incomplete}}incomplete{{end}} {{range .DepsErrors}}bad dep: {{.Err}}{{end}}' example.com/direct +! stdout ^error +stdout 'incomplete' +stdout 'bad dep: .*example.com/notfound' + +# Listing with -deps should also fail. +# BUG: Today, it does not. +# ! go list -deps example.com/direct +# stderr example.com/notfound +go list -deps example.com/direct +stdout example.com/notfound + + +# Listing an otherwise-valid package that imports some *other* package with an +# unsatisfied import should also fail. +# BUG: Today, it succeeds. +go list -f '{{if .Error}}error{{end}} {{if .Incomplete}}incomplete{{end}} {{range .DepsErrors}}bad dep: {{.Err}}{{end}}' example.com/indirect +! stdout ^error +stdout incomplete +stdout 'bad dep: .*example.com/notfound' + +# Again, -deps should fail. +# BUG: Again, it does not. +# ! go list -deps example.com/indirect +# stderr example.com/notfound +go list -deps example.com/indirect +stdout example.com/notfound + + +# Listing the missing dependency directly should fail outright... +! go list -f '{{if .Error}}error{{end}} {{if .Incomplete}}incomplete{{end}}' example.com/notfound +stderr 'cannot find module providing package example.com/notfound' +! stdout error +! stdout incomplete + +# ...but listing with -e should succeed. +go list -e -f '{{if .Error}}error{{end}} {{if .Incomplete}}incomplete{{end}}' example.com/notfound +stdout error +stdout incomplete + + +# The pattern "all" should match only packages that acutally exist, +# ignoring those whose existence is merely implied by imports. +go list -e -f '{{.ImportPath}} {{.Error}}' all +stdout example.com/direct +stdout example.com/indirect +# TODO: go list creates a dummy package with the import-not-found +# but really the Error belongs on example.com/direct, and this package +# should not be printed. +# ! stdout example.com/notfound + + +-- example.com/go.mod -- +module example.com + +-- example.com/direct/direct.go -- +package direct +import _ "example.com/notfound" + +-- example.com/indirect/indirect.go -- +package indirect +import _ "example.com/direct" + +-- example.com/notfound/README -- +This directory intentionally left blank. diff --git a/libgo/go/cmd/go/testdata/script/mod_list_dir.txt b/libgo/go/cmd/go/testdata/script/mod_list_dir.txt new file mode 100644 index 0000000..6d94147 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_list_dir.txt @@ -0,0 +1,32 @@ +# go list with path to directory should work + +[gccgo] stop + +env GO111MODULE=off +go list -f '{{.ImportPath}}' $GOROOT/src/math +stdout ^math$ + +env GO111MODULE=on +go list -f '{{.ImportPath}}' $GOROOT/src/math +stdout ^math$ +go list -f '{{.ImportPath}}' . +stdout ^x$ +! go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/quote@v1.5.2 +stderr '^go: no such directory.*quote@v1.5.2' +go mod download rsc.io/quote@v1.5.2 +go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/quote@v1.5.2 +stdout '^rsc.io/quote$' +go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/sampler@v1.3.0 +stdout '^rsc.io/sampler$' +go get -d rsc.io/sampler@v1.3.1 +go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/sampler@v1.3.1 +stdout '^rsc.io/sampler$' +! go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/sampler@v1.3.0 +stderr 'outside available modules' + +-- go.mod -- +module x +require rsc.io/quote v1.5.2 + +-- x.go -- +package x diff --git a/libgo/go/cmd/go/testdata/script/mod_list_upgrade.txt b/libgo/go/cmd/go/testdata/script/mod_list_upgrade.txt new file mode 100644 index 0000000..474df0d --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_list_upgrade.txt @@ -0,0 +1,8 @@ +env GO111MODULE=on + +go list -m -u all +stdout 'rsc.io/quote v1.2.0 \[v1\.5\.2\]' + +-- go.mod -- +module x +require rsc.io/quote v1.2.0 diff --git a/libgo/go/cmd/go/testdata/script/mod_load_badmod.txt b/libgo/go/cmd/go/testdata/script/mod_load_badmod.txt new file mode 100644 index 0000000..68c8b37 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_load_badmod.txt @@ -0,0 +1,26 @@ +# Unknown lines should be ignored in dependency go.mod files. +env GO111MODULE=on +go list -m all + +# ... and in replaced dependency go.mod files. +cp go.mod go.mod.usesub +go list -m all + +# ... but not in the main module. +cp go.mod.bad go.mod +! go list -m all +stderr 'unknown directive: hello' + +-- go.mod -- +module m +require rsc.io/badmod v1.0.0 +-- go.mod.bad -- +module m +hello world +-- go.mod.usesub -- +module m +require rsc.io/badmod v1.0.0 +replace rsc.io/badmod v1.0.0 => ./sub +-- sub/go.mod -- +module sub +hello world diff --git a/libgo/go/cmd/go/testdata/script/mod_local_replace.txt b/libgo/go/cmd/go/testdata/script/mod_local_replace.txt new file mode 100644 index 0000000..19bc8f3 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_local_replace.txt @@ -0,0 +1,23 @@ +# Test that local replacements work even with dummy module names. +# golang.org/issue/24100. + +env GO111MODULE=on + +cd x/y +go list -f '{{.Dir}}' zz +stdout x[/\\]z$ + +-- x/y/go.mod -- +module x/y +require zz v1.0.0 +replace zz v1.0.0 => ../z + +-- x/y/y.go -- +package y +import _ "zz" + +-- x/z/go.mod -- +module x/z + +-- x/z/z.go -- +package z diff --git a/libgo/go/cmd/go/testdata/script/mod_multirepo.txt b/libgo/go/cmd/go/testdata/script/mod_multirepo.txt new file mode 100644 index 0000000..7f977e8 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_multirepo.txt @@ -0,0 +1,40 @@ +env GO111MODULE=on + +# initial standalone module should use no downloaded modules +go list -deps -f {{.Dir}} +! stdout 'pkg[\\/]mod' + +# v2 import should use a downloaded module +# both without an explicit go.mod entry ... +cp tmp/use_v2.go x.go +go list -deps -f {{.Dir}} +stdout 'pkg[\\/]mod[\\/]rsc.io[\\/]quote[\\/]v2@v2.0.1$' + +# ... and with one ... +cp tmp/use_v2.mod go.mod +go list -deps -f {{.Dir}} +stdout 'pkg[\\/]mod[\\/]rsc.io[\\/]quote[\\/]v2@v2.0.1$' + +# ... and even if there is a v2 module in a subdirectory. +mkdir v2 +cp x.go v2/x.go +cp tmp/v2.mod v2/go.mod +go list -deps -f {{.Dir}} +stdout 'pkg[\\/]mod[\\/]rsc.io[\\/]quote[\\/]v2@v2.0.1$' + +-- go.mod -- +module rsc.io/quote + +-- x.go -- +package quote + +-- tmp/use_v2.go -- +package quote +import _ "rsc.io/quote/v2" + +-- tmp/use_v2.mod -- +module rsc.io/quote +require rsc.io/quote/v2 v2.0.1 + +-- tmp/v2.mod -- +package rsc.io/quote/v2 diff --git a/libgo/go/cmd/go/testdata/script/mod_nomod.txt b/libgo/go/cmd/go/testdata/script/mod_nomod.txt new file mode 100644 index 0000000..c6fb791c --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_nomod.txt @@ -0,0 +1,43 @@ +# Test go commands with no module. +env GO111MODULE=on + +# go mod edit fails unless given explicit mod file argument +! go mod edit -json +go mod edit -json x.mod + +# bug succeeds +[exec:echo] env BROWSER=echo +[exec:echo] go bug + +# commands that load the package in the current directory fail +! go build +! go fmt +! go generate +! go get +! go install +! go list +! go run x.go +! go test +! go vet + +# clean succeeds, even with -modcache +go clean -modcache + +# doc succeeds for standard library +[!gccgo] go doc unsafe + +# env succeeds +go env + +# tool succeeds +go tool -n test2json + +# version succeeds +go version + +-- x.mod -- +module m + +-- x.go -- +package main +func main() {} diff --git a/libgo/go/cmd/go/testdata/script/mod_patterns.txt b/libgo/go/cmd/go/testdata/script/mod_patterns.txt new file mode 100644 index 0000000..9b2ebdf --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_patterns.txt @@ -0,0 +1,66 @@ +env GO111MODULE=on + +cd m + +# 'go list all' should list all of the packages used (directly or indirectly) by +# the packages in the main module, but no other packages from the standard +# library or active modules. +# +# 'go list ...' should list packages in all active modules and the standard library. +# But not cmd/* - see golang.org/issue/26924. +# +# 'go list example.com/m/...' should list packages in all modules that begin with 'example.com/m/'. +# +# 'go list ./...' should list only packages in the current module, not other active modules. +# +# Warnings about unmatched patterns should only be printed once. +# +# And the go command should be able to keep track of all this! +go list -f '{{.ImportPath}}: {{.Match}}' all ... example.com/m/... ./... ./xyz... +stdout 'example.com/m/useunicode: \[all \.\.\. example.com/m/... ./...\]' +stdout 'example.com/m/useunsafe: \[all \.\.\. example.com/m/... ./...\]' +[cgo] stdout 'example.com/m/useC: \[all \.\.\. example.com/m/... ./...\]' +[!cgo] ! stdout example.com/m/useC +stdout 'example.com/unused/useerrors: \[\.\.\.\]' # but not "all" +stdout 'example.com/m/nested/useencoding: \[\.\.\. example.com/m/...\]' # but NOT "all" or "./..." +[!gccgo] stdout '^unicode: \[all \.\.\.\]' +[!gccgo] stdout '^unsafe: \[all \.\.\.\]' +[!gccgo] stdout 'index/suffixarray: \[\.\.\.\]' +! stdout cmd/pprof # golang.org/issue/26924 + +stderr -count=1 '^go: warning: "./xyz..." matched no packages$' + +env CGO_ENABLED=0 +go list -f '{{.ImportPath}}: {{.Match}}' all ... example.com/m/... ./... ./xyz... +! stdout example.com/m/useC + +-- m/go.mod -- +module example.com/m + +require example.com/unused v0.0.0 // indirect +replace example.com/unused => ../unused + +require example.com/m/nested v0.0.0 // indirect +replace example.com/m/nested => ../nested + +-- m/useC/useC.go -- +package useC +import _ "C" // "C" is a pseudo-package, not an actual one +-- m/useunicode/useunicode.go -- +package useunicode +import _ "unicode" +-- m/useunsafe/useunsafe.go -- +package useunsafe +import _ "unsafe" + +-- unused/go.mod -- +module example.com/unused +-- unused/useerrors/useerrors.go -- +package useerrors +import _ "errors" + +-- nested/go.mod -- +module example.com/m/nested +-- nested/useencoding/useencoding.go -- +package useencoding +import _ "encoding" diff --git a/libgo/go/cmd/go/testdata/script/mod_query.txt b/libgo/go/cmd/go/testdata/script/mod_query.txt new file mode 100644 index 0000000..4baaaa8 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_query.txt @@ -0,0 +1,24 @@ +env GO111MODULE=on + +go list -m -versions rsc.io/quote +stdout '^rsc.io/quote v1.0.0 v1.1.0 v1.2.0 v1.2.1 v1.3.0 v1.4.0 v1.5.0 v1.5.1 v1.5.2 v1.5.3-pre1$' + +# latest rsc.io/quote should be v1.5.2 not v1.5.3-pre1 +go list -m rsc.io/quote@latest +stdout 'rsc.io/quote v1.5.2$' + +go list -m rsc.io/quote@>v1.5.2 +stdout 'rsc.io/quote v1.5.3-pre1$' + +go list -m rsc.io/quote@<v1.5.4 +stdout 'rsc.io/quote v1.5.2$' + +! go list -m rsc.io/quote@>v1.5.3 +stderr 'go list -m rsc.io/quote: no matching versions for query ">v1.5.3"' + +go list -m -e -f '{{.Error.Err}}' rsc.io/quote@>v1.5.3 +stdout 'no matching versions for query ">v1.5.3"' + +-- go.mod -- +module x +require rsc.io/quote v1.0.0 diff --git a/libgo/go/cmd/go/testdata/script/mod_query_exclude.txt b/libgo/go/cmd/go/testdata/script/mod_query_exclude.txt new file mode 100644 index 0000000..a64a8e1 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_query_exclude.txt @@ -0,0 +1,26 @@ +env GO111MODULE=on + +# get excluded version +cp go.mod1 go.mod +! go get rsc.io/quote@v1.5.0 +stderr 'rsc.io/quote@v1.5.0 excluded' + +# get non-excluded version +cp go.mod1 go.mod +go get rsc.io/quote@v1.5.1 +stderr 'rsc.io/quote v1.5.1' + +# get range with excluded version +cp go.mod1 go.mod +go get rsc.io/quote@>=v1.5 +go list -m ...quote +stdout 'rsc.io/quote v1.5.[1-9]' + +-- go.mod1 -- +module x +exclude rsc.io/quote v1.5.0 + +-- x.go -- +package x +import _ "rsc.io/quote" + diff --git a/libgo/go/cmd/go/testdata/script/mod_readonly.txt b/libgo/go/cmd/go/testdata/script/mod_readonly.txt new file mode 100644 index 0000000..1b5932e --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_readonly.txt @@ -0,0 +1,42 @@ +env GO111MODULE=on + +# -mod=readonly must not resolve missing modules nor update go.mod +# +# TODO(bcmills): 'go list' should suffice, but today it does not fail due to +# unresolved imports. When that is fixed, use 'go list' instead of 'go list all'. +env GOFLAGS=-mod=readonly +go mod edit -fmt +cp go.mod go.mod.empty +! go list all +stderr 'import lookup disabled by -mod=readonly' +cmp go.mod go.mod.empty + +# update go.mod - go get allowed +go get rsc.io/quote +grep rsc.io/quote go.mod + +# update go.mod - go mod tidy allowed +cp go.mod.empty go.mod +go mod tidy + +# -mod=readonly must succeed once go.mod is up-to-date... +go list + +# ... even if it needs downloads +go clean -modcache +go list + +# -mod=readonly should reject inconsistent go.mod files +# (ones that would be rewritten). +go mod edit -require rsc.io/sampler@v1.2.0 +cp go.mod go.mod.inconsistent +! go list +stderr 'go: updates to go.mod needed, disabled by -mod=readonly' +cmp go.mod go.mod.inconsistent + +-- go.mod -- +module m + +-- x.go -- +package x +import _ "rsc.io/quote" diff --git a/libgo/go/cmd/go/testdata/script/mod_replace.txt b/libgo/go/cmd/go/testdata/script/mod_replace.txt new file mode 100644 index 0000000..5894ed6 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_replace.txt @@ -0,0 +1,87 @@ +env GO111MODULE=on + +go build -o a1.exe . +exec ./a1.exe +stdout 'Don''t communicate by sharing memory' + +# Modules can be replaced by local packages. +go mod edit -replace=rsc.io/quote/v3=./local/rsc.io/quote/v3 +go build -o a2.exe . +exec ./a2.exe +stdout 'Concurrency is not parallelism.' + +# The module path of the replacement doesn't need to match. +# (For example, it could be a long-running fork with its own import path.) +go mod edit -replace=rsc.io/quote/v3=./local/not-rsc.io/quote/v3 +go build -o a3.exe . +exec ./a3.exe +stdout 'Clear is better than clever.' + +# However, the same module can't be used as two different paths. +go mod edit -dropreplace=rsc.io/quote/v3 -replace=not-rsc.io/quote/v3@v3.0.0=rsc.io/quote/v3@v3.0.0 -require=not-rsc.io/quote/v3@v3.0.0 +! go build -o a4.exe . +stderr 'rsc.io/quote/v3@v3.0.0 used for two different module paths \(not-rsc.io/quote/v3 and rsc.io/quote/v3\)' + +-- go.mod -- +module quoter + +require rsc.io/quote/v3 v3.0.0 + +-- main.go -- +package main + +import ( + "fmt" + "rsc.io/quote/v3" +) + +func main() { + fmt.Println(quote.GoV3()) +} + +-- local/rsc.io/quote/v3/go.mod -- +module rsc.io/quote/v3 + +require rsc.io/sampler v1.3.0 + +-- local/rsc.io/quote/v3/quote.go -- +// Copyright 2018 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 quote collects pithy sayings. +package quote + +import "rsc.io/sampler" + +// Hello returns a greeting. +func HelloV3() string { + return sampler.Hello() +} + +// Glass returns a useful phrase for world travelers. +func GlassV3() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a REPLACED Go proverb. +func GoV3() string { + return "Concurrency is not parallelism." +} + +// Opt returns a optimization truth. +func OptV3() string { + // Wisdom from ken. + return "If a program is too slow, it must have a loop." +} + +-- local/not-rsc.io/quote/v3/go.mod -- +module not-rsc.io/quote/v3 + +-- local/not-rsc.io/quote/v3/quote.go -- +package quote + +func GoV3() string { + return "Clear is better than clever." +} diff --git a/libgo/go/cmd/go/testdata/script/mod_require_exclude.txt b/libgo/go/cmd/go/testdata/script/mod_require_exclude.txt new file mode 100644 index 0000000..60f7e3f --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_require_exclude.txt @@ -0,0 +1,33 @@ +# build with no newer version to satisfy exclude +env GO111MODULE=on +! go list -m all +stderr 'no newer version available' + +# build with newer version available +cp go.mod2 go.mod +go list -m all +stdout 'rsc.io/quote v1.5.2' + +# build with excluded newer version +cp go.mod3 go.mod +go list -m all +stdout 'rsc.io/quote v1.5.1' + +-- x.go -- +package x +import _ "rsc.io/quote" + +-- go.mod -- +module x +exclude rsc.io/sampler latest +require rsc.io/sampler latest + +-- go.mod2 -- +module x +exclude rsc.io/quote v1.5.1 +require rsc.io/quote v1.5.1 + +-- go.mod3 -- +module x +exclude rsc.io/quote v1.5.2 +require rsc.io/quote v1.5.1 diff --git a/libgo/go/cmd/go/testdata/script/mod_test.txt b/libgo/go/cmd/go/testdata/script/mod_test.txt new file mode 100644 index 0000000..caeb25a --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_test.txt @@ -0,0 +1,119 @@ +env GO111MODULE=on + +# A test in the module's root package should work. +cd a/ +cp go.mod.empty go.mod +go test +stdout PASS + +cp go.mod.empty go.mod +go list -deps +! stdout ^testing$ + +# list all should include test dependencies, like testing +cp go.mod.empty go.mod +go list all +stdout ^testing$ +stdout ^rsc.io/quote$ +stdout ^rsc.io/testonly$ + +# list -deps -tests should also include testing +# but not deps of tests of deps (rsc.io/testonly). +go list -deps -test +stdout ^testing$ +stdout ^rsc.io/quote$ +! stdout ^rsc.io/testonly$ + +# list -test all should succeed +cp go.mod.empty go.mod +go list -test all +stdout '^testing' + +cp go.mod.empty go.mod +go test +stdout PASS + +# A test with the "_test" suffix in the module root should also work. +cd ../b/ +go test +stdout PASS + +# A test with the "_test" suffix of a *package* with a "_test" suffix should +# even work (not that you should ever do that). +cd ../c_test +go test +stdout PASS + +cd ../d_test +go test +stdout PASS + +-- a/go.mod.empty -- +module example.com/user/a + +-- a/a.go -- +package a + +-- a/a_test.go -- +package a + +import "testing" +import _ "rsc.io/quote" + +func Test(t *testing.T) {} + +-- b/go.mod -- +module example.com/user/b + +-- b/b.go -- +package b + +-- b/b_test.go -- +package b_test + +import "testing" + +func Test(t *testing.T) {} + +-- c_test/go.mod -- +module example.com/c_test + +-- c_test/umm.go -- +// Package c_test is the non-test package for its import path! +package c_test + +-- c_test/c_test_test.go -- +package c_test_test + +import "testing" + +func Test(t *testing.T) {} + +-- d_test/go.mod -- +// Package d is an ordinary package in a deceptively-named directory. +module example.com/d + +-- d_test/d.go -- +package d + +-- d_test/d_test.go -- +package d_test + +import "testing" + +func Test(t *testing.T) {} + +-- e/go.mod -- +module example.com/e_test + +-- e/wat.go -- +// Package e_test is the non-test package for its import path, +// in a deceptively-named directory! +package e_test + +-- e/e_test.go -- +package e_test_test + +import "testing" + +func Test(t *testing.T) {} diff --git a/libgo/go/cmd/go/testdata/script/mod_tidy.txt b/libgo/go/cmd/go/testdata/script/mod_tidy.txt new file mode 100644 index 0000000..449aa07 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_tidy.txt @@ -0,0 +1,64 @@ +env GO111MODULE=on + +# tidy removes unused y, but everything else is used +go mod tidy -v +stderr '^unused y.1' +! stderr '^unused [^y]' + +go list -m all +! stdout '^y' +stdout '^w.1 v1.2.0' +stdout '^z.1 v1.2.0' + +# empty tidy should not crash +cd triv +go mod tidy + +-- go.mod -- +module m + +require ( + x.1 v1.0.0 + y.1 v1.0.0 + w.1 v1.2.0 +) + +replace x.1 v1.0.0 => ./x +replace y.1 v1.0.0 => ./y +replace z.1 v1.1.0 => ./z +replace z.1 v1.2.0 => ./z +replace w.1 => ./w + +-- m.go -- +package m + +import _ "x.1" +import _ "z.1/sub" + +-- w/go.mod -- +module w + +-- w/w.go -- +package w + +-- x/go.mod -- +module x +require w.1 v1.1.0 +require z.1 v1.1.0 + +-- x/x.go -- +package x +import _ "w.1" + +-- y/go.mod -- +module y +require z.1 v1.2.0 + +-- z/go.mod -- +module z + +-- z/sub/sub.go -- +package sub + +-- triv/go.mod -- +module triv diff --git a/libgo/go/cmd/go/testdata/script/mod_tidy_quote.txt b/libgo/go/cmd/go/testdata/script/mod_tidy_quote.txt new file mode 100644 index 0000000..423c7c2 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_tidy_quote.txt @@ -0,0 +1,26 @@ +# Check that mod tidy does not introduce repeated +# require statements when input go.mod has quoted requirements. +env GO111MODULE=on + +go mod tidy +grep -count=1 rsc.io/quote go.mod + +cp go.mod2 go.mod +go mod tidy +grep -count=1 rsc.io/quote go.mod + + +-- go.mod -- +module x + +-- x.go -- +package x +import "rsc.io/quote" +func main() { _ = quote.Hello } + +-- go.mod2 -- +module x +require ( + "rsc.io/sampler" v1.3.0 + "rsc.io/quote" v1.5.2 +) diff --git a/libgo/go/cmd/go/testdata/script/mod_tidy_sum.txt b/libgo/go/cmd/go/testdata/script/mod_tidy_sum.txt new file mode 100644 index 0000000..5a15818 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_tidy_sum.txt @@ -0,0 +1,33 @@ +env GO111MODULE=on + +# go.sum should list directly used modules and dependencies +go get rsc.io/quote@v1.5.2 +go mod tidy +grep rsc.io/sampler go.sum + +# go.sum should not normally lose old entries +go get rsc.io/quote@v1.0.0 +grep 'rsc.io/quote v1.0.0' go.sum +grep 'rsc.io/quote v1.5.2' go.sum +grep rsc.io/sampler go.sum + +# go mod tidy should clear dead entries from go.sum +go mod tidy +grep 'rsc.io/quote v1.0.0' go.sum +! grep 'rsc.io/quote v1.5.2' go.sum +! grep rsc.io/sampler go.sum + +# go.sum with no entries is OK to keep +# (better for version control not to delete and recreate.) +cp x.go.noimports x.go +go mod tidy +exists go.sum +! grep . go.sum + +-- go.mod -- +module x +-- x.go -- +package x +import _ "rsc.io/quote" +-- x.go.noimports -- +package x diff --git a/libgo/go/cmd/go/testdata/script/mod_upgrade_patch.txt b/libgo/go/cmd/go/testdata/script/mod_upgrade_patch.txt new file mode 100644 index 0000000..3c27cdb --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_upgrade_patch.txt @@ -0,0 +1,29 @@ +env GO111MODULE=on + +go list -m all +stdout '^rsc.io/quote v1.4.0' +stdout '^rsc.io/sampler v1.0.0' + +# get -u=patch rsc.io/quote should take latest quote & patch update its deps +go get -m -u=patch rsc.io/quote +go list -m all +stdout '^rsc.io/quote v1.5.2' +stdout '^rsc.io/sampler v1.3.1' +stdout '^golang.org/x/text v0.0.0-' + +# get -u=patch quote@v1.2.0 should take that version of quote & patch update its deps +go get -m -u=patch rsc.io/quote@v1.2.0 +go list -m all +stdout '^rsc.io/quote v1.2.0' +stdout '^rsc.io/sampler v1.3.1' +stdout '^golang.org/x/text v0.0.0-' + +# get -u=patch with no args applies to all deps +go get -m -u=patch +go list -m all +stdout '^rsc.io/quote v1.2.1' + +-- go.mod -- +module x +require rsc.io/quote v1.4.0 + diff --git a/libgo/go/cmd/go/testdata/script/mod_vendor.txt b/libgo/go/cmd/go/testdata/script/mod_vendor.txt new file mode 100644 index 0000000..b3769a8 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_vendor.txt @@ -0,0 +1,230 @@ +env GO111MODULE=on + +go list -m all +stdout '^x v1.0.0 => ./x' +stdout '^w' + +[!short] go build +[!short] ! go build -mod=vendor + +go list -f {{.Dir}} x +stdout 'src[\\/]x' + +go mod vendor -v +stderr '^# x v1.0.0 => ./x' +stderr '^x' +stderr '^# y v1.0.0 => ./y' +stderr '^y' +stderr '^# z v1.0.0 => ./z' +stderr '^z' +! stderr '^w' + +go list -f {{.Dir}} x +stdout 'src[\\/]x' + +go list -f {{.Dir}} -m x +stdout 'src[\\/]x' + +go list -mod=vendor -f {{.Dir}} x +stdout 'src[\\/]vendor[\\/]x' + +go list -mod=vendor -f {{.Dir}} -m x +stdout 'src[\\/]vendor[\\/]x' + +go list -f {{.Dir}} -m w +stdout 'src[\\/]w' + +! go list -mod=vendor -f {{.Dir}} w +stderr 'src[\\/]vendor[\\/]w' + +! exists vendor/x/testdata +! exists vendor/a/foo/bar/b/main_test.go + +exists vendor/a/foo/AUTHORS.txt +exists vendor/a/foo/CONTRIBUTORS +exists vendor/a/foo/LICENSE +exists vendor/a/foo/PATENTS +exists vendor/a/foo/COPYING +exists vendor/a/foo/COPYLEFT +exists vendor/x/NOTICE! +exists vendor/mysite/myname/mypkg/LICENSE.txt + +! exists vendor/a/foo/licensed-to-kill +! exists vendor/w +! exists vendor/w/LICENSE +! exists vendor/x/x2 +! exists vendor/x/x2/LICENSE + +[short] stop + +go build +go build -mod=vendor +go test -mod=vendor . ./subdir +go test -mod=vendor ./... + +-- go.mod -- +module m + +require ( + a v1.0.0 + mysite/myname/mypkg v1.0.0 + w v1.0.0 // indirect + x v1.0.0 + y v1.0.0 + z v1.0.0 +) + +replace ( + a v1.0.0 => ./a + mysite/myname/mypkg v1.0.0 => ./mypkg + w v1.0.0 => ./w + x v1.0.0 => ./x + y v1.0.0 => ./y + z v1.0.0 => ./z +) + +-- a/foo/AUTHORS.txt -- +-- a/foo/CONTRIBUTORS -- +-- a/foo/LICENSE -- +-- a/foo/PATENTS -- +-- a/foo/COPYING -- +-- a/foo/COPYLEFT -- +-- a/foo/licensed-to-kill -- +-- w/LICENSE -- +-- x/NOTICE! -- +-- x/x2/LICENSE -- +-- mypkg/LICENSE.txt -- + +-- a/foo/bar/b/main.go -- +package b +-- a/foo/bar/b/main_test.go -- +package b + +import ( + "os" + "testing" +) + +func TestDir(t *testing.T) { + if _, err := os.Stat("../testdata/1"); err != nil { + t.Fatalf("testdata: %v", err) + } +} +-- a/foo/bar/c/main.go -- +package c +-- a/foo/bar/c/main_test.go -- +package c + +import ( + "os" + "testing" +) + +func TestDir(t *testing.T) { + if _, err := os.Stat("../../../testdata/1"); err != nil { + t.Fatalf("testdata: %v", err) + } + if _, err := os.Stat("./testdata/1"); err != nil { + t.Fatalf("testdata: %v", err) + } +} +-- a/foo/bar/c/testdata/1 -- +-- a/foo/bar/testdata/1 -- +-- a/go.mod -- +module a +-- a/main.go -- +package a +-- a/main_test.go -- +package a + +import ( + "os" + "testing" +) + +func TestDir(t *testing.T) { + if _, err := os.Stat("./testdata/1"); err != nil { + t.Fatalf("testdata: %v", err) + } +} +-- a/testdata/1 -- +-- appengine.go -- +// +build appengine + +package m + +import _ "appengine" +import _ "appengine/datastore" +-- nonexistent.go -- +// +build alternatereality + +package m + +import _ "nonexistent.rsc.io" +-- mypkg/go.mod -- +module me +-- mypkg/mydir/d.go -- +package mydir +-- subdir/v1_test.go -- +package m + +import _ "mysite/myname/mypkg/mydir" +-- testdata1.go -- +package m + +import _ "a" +-- testdata2.go -- +package m + +import _ "a/foo/bar/b" +import _ "a/foo/bar/c" +-- v1.go -- +package m + +import _ "x" +-- v2.go -- +// +build abc + +package mMmMmMm + +import _ "y" +-- v3.go -- +// +build !abc + +package m + +import _ "z" +-- v4.go -- +// +build notmytag + +package m + +import _ "x/x1" +-- w/go.mod -- +module w +-- w/w.go -- +package w +-- x/go.mod -- +module x +-- x/testdata/x.txt -- +placeholder - want directory with no go files +-- x/x.go -- +package x +-- x/x1/x1.go -- +// +build notmytag + +package x1 +-- x/x2/dummy.txt -- +dummy +-- x/x_test.go -- +package x + +import _ "w" +-- y/go.mod -- +module y +-- y/y.go -- +package y +-- z/go.mod -- +module z +-- z/z.go -- +package z diff --git a/libgo/go/cmd/go/testdata/script/mod_vendor_build.txt b/libgo/go/cmd/go/testdata/script/mod_vendor_build.txt new file mode 100644 index 0000000..7b304db --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_vendor_build.txt @@ -0,0 +1,27 @@ +env GO111MODULE=on + +# initial conditions: using sampler v1.3.0, not listed in go.mod. +go list -deps +stdout rsc.io/sampler +! grep 'rsc.io/sampler v1.3.0' go.mod + +# update to v1.3.1, now indirect in go.mod. +go get rsc.io/sampler@v1.3.1 +grep 'rsc.io/sampler v1.3.1 // indirect' go.mod +cp go.mod go.mod.good + +# vendoring can but should not need to make changes. +go mod vendor +cmp go.mod go.mod.good + +# go list -mod=vendor (or go build -mod=vendor) must not modify go.mod. +# golang.org/issue/26704 +go list -mod=vendor +cmp go.mod go.mod.good + +-- go.mod -- +module m + +-- x.go -- +package x +import _ "rsc.io/quote" diff --git a/libgo/go/cmd/go/testdata/script/mod_vendor_nodeps.txt b/libgo/go/cmd/go/testdata/script/mod_vendor_nodeps.txt new file mode 100644 index 0000000..e9a84ca --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_vendor_nodeps.txt @@ -0,0 +1,9 @@ +env GO111MODULE=on + +go mod vendor +stderr '^go: no dependencies to vendor' + +-- go.mod -- +module x +-- x.go -- +package x diff --git a/libgo/go/cmd/go/testdata/script/mod_verify.txt b/libgo/go/cmd/go/testdata/script/mod_verify.txt new file mode 100644 index 0000000..50c9b4a --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_verify.txt @@ -0,0 +1,85 @@ +env GO111MODULE=on + +# With good go.sum, verify succeeds by avoiding download. +cp go.sum.good go.sum +go mod verify +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.1.0.zip + +# With bad go.sum, verify succeeds by avoiding download. +cp go.sum.bad go.sum +go mod verify +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.1.0.zip + +# With bad go.sum, sync (which must download) fails. +# Even if the bad sum is in the old legacy go.modverify file. +rm go.sum +cp go.sum.bad go.modverify +! go mod tidy +stderr 'checksum mismatch' +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.1.0.zip + +# With good go.sum, sync works (and moves go.modverify to go.sum). +rm go.sum +cp go.sum.good go.modverify +go mod tidy +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.1.0.zip +exists $GOPATH/pkg/mod/rsc.io/quote@v1.1.0/quote.go +! exists go.modverify + +# go.sum should have the new checksum for go.mod +grep '^rsc.io/quote v1.1.0/go.mod ' go.sum + +# verify should work +go mod verify + +# basic loading of module graph should detect incorrect go.mod files. +go mod graph +cp go.sum.bad2 go.sum +! go mod graph +stderr 'go.mod: checksum mismatch' + +# go.sum should be created and updated automatically. +rm go.sum +go mod graph +exists go.sum +grep '^rsc.io/quote v1.1.0/go.mod ' go.sum +! grep '^rsc.io/quote v1.1.0 ' go.sum + +go mod tidy +grep '^rsc.io/quote v1.1.0/go.mod ' go.sum +grep '^rsc.io/quote v1.1.0 ' go.sum + +# sync should ignore missing ziphash; verify should not +rm $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.1.0.ziphash +go mod tidy +! go mod verify + +# Packages below module root should not be mentioned in go.sum. +rm go.sum +go mod edit -droprequire rsc.io/quote +go list rsc.io/quote/buggy # re-resolves import path and updates go.mod +grep '^rsc.io/quote v1.5.2/go.mod ' go.sum +! grep buggy go.sum + +# non-existent packages below module root should not be mentioned in go.sum +go mod edit -droprequire rsc.io/quote +! go list rsc.io/quote/morebuggy +grep '^rsc.io/quote v1.5.2/go.mod ' go.sum +! grep buggy go.sum + +-- go.mod -- +module x +require rsc.io/quote v1.1.0 + +-- x.go -- +package x +import _ "rsc.io/quote" + +-- go.sum.good -- +rsc.io/quote v1.1.0 h1:a3YaZoizPtXyv6ZsJ74oo2L4/bwOSTKMY7MAyo4O/0c= + +-- go.sum.bad -- +rsc.io/quote v1.1.0 h1:a3YaZoizPtXyv6ZsJ74oo2L4/bwOSTKMY7MAyo4O/1c= + +-- go.sum.bad2 -- +rsc.io/quote v1.1.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl1= diff --git a/libgo/go/cmd/go/testdata/script/mod_versions.txt b/libgo/go/cmd/go/testdata/script/mod_versions.txt new file mode 100644 index 0000000..fd5e5c5 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_versions.txt @@ -0,0 +1,14 @@ +# Test rejection of pkg@version in GOPATH mode. +env GO111MODULE=off +! go get rsc.io/quote@v1.5.1 +stderr 'cannot use path@version syntax in GOPATH mode' +! go build rsc.io/quote@v1.5.1 +stderr 'cannot use path@version syntax in GOPATH mode' + +env GO111MODULE=on +cd x +! go build rsc.io/quote@v1.5.1 +stderr 'can only use path@version syntax with ''go get''' + +-- x/go.mod -- +module x diff --git a/libgo/go/cmd/go/testdata/script/mod_why.txt b/libgo/go/cmd/go/testdata/script/mod_why.txt new file mode 100644 index 0000000..4d556fc --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_why.txt @@ -0,0 +1,114 @@ +env GO111MODULE=on + +go list -test all +stdout rsc.io/quote +stdout golang.org/x/text/language + +# why a package? +go mod why golang.org/x/text/language +cmp stdout why-language.txt + +# why a module? +go mod why -m golang.org... +cmp stdout why-text-module.txt + +# why a package used only in tests? +go mod why rsc.io/testonly +cmp stdout why-testonly.txt + +# why a module used only in tests? +go mod why -m rsc.io/testonly +cmp stdout why-testonly.txt + +# test package not needed +go mod why golang.org/x/text/unused +cmp stdout why-unused.txt + +# vendor doesn't use packages used only in tests. +go mod why -vendor rsc.io/testonly +cmp stdout why-vendor.txt + +# vendor doesn't use modules used only in tests. +go mod why -vendor -m rsc.io/testonly +cmp stdout why-vendor-module.txt + +# test multiple packages +go mod why golang.org/x/text/language golang.org/x/text/unused +cmp stdout why-both.txt + +# test multiple modules +go mod why -m rsc.io/quote rsc.io/sampler +cmp stdout why-both-module.txt + +-- go.mod -- +module mymodule +require rsc.io/quote v1.5.2 + +-- x/x.go -- +package x +import _ "mymodule/z" + +-- y/y.go -- +package y + +-- y/y_test.go -- +package y +import _ "rsc.io/quote" + +-- z/z.go -- +package z +import _ "mymodule/y" + + +-- why-language.txt -- +# golang.org/x/text/language +mymodule/y +mymodule/y.test +rsc.io/quote +rsc.io/sampler +golang.org/x/text/language +-- why-unused.txt -- +# golang.org/x/text/unused +(main module does not need package golang.org/x/text/unused) +-- why-text-module.txt -- +# golang.org/x/text +mymodule/y +mymodule/y.test +rsc.io/quote +rsc.io/sampler +golang.org/x/text/language +-- why-testonly.txt -- +# rsc.io/testonly +mymodule/y +mymodule/y.test +rsc.io/quote +rsc.io/sampler +rsc.io/sampler.test +rsc.io/testonly +-- why-vendor.txt -- +# rsc.io/testonly +(main module does not need to vendor package rsc.io/testonly) +-- why-vendor-module.txt -- +# rsc.io/testonly +(main module does not need to vendor module rsc.io/testonly) +-- why-both.txt -- +# golang.org/x/text/language +mymodule/y +mymodule/y.test +rsc.io/quote +rsc.io/sampler +golang.org/x/text/language + +# golang.org/x/text/unused +(main module does not need package golang.org/x/text/unused) +-- why-both-module.txt -- +# rsc.io/quote +mymodule/y +mymodule/y.test +rsc.io/quote + +# rsc.io/sampler +mymodule/y +mymodule/y.test +rsc.io/quote +rsc.io/sampler diff --git a/libgo/go/cmd/go/testdata/script/pattern_syntax_error.txt b/libgo/go/cmd/go/testdata/script/pattern_syntax_error.txt new file mode 100644 index 0000000..8e6549b --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/pattern_syntax_error.txt @@ -0,0 +1,10 @@ +# patterns match directories with syntax errors +! go list ./... +! go build ./... +! go install ./... + +-- mypkg/x.go -- +package mypkg + +-- mypkg/y.go -- +pkg mypackage diff --git a/libgo/go/cmd/go/testdata/script/run_hello.txt b/libgo/go/cmd/go/testdata/script/run_hello.txt new file mode 100644 index 0000000..8c4c1c1 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/run_hello.txt @@ -0,0 +1,7 @@ +# hello world +go run hello.go +stderr 'hello world' + +-- hello.go -- +package main +func main() { println("hello world") } diff --git a/libgo/go/cmd/go/testdata/script/test_badtest.txt b/libgo/go/cmd/go/testdata/script/test_badtest.txt new file mode 100644 index 0000000..42fcfed --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_badtest.txt @@ -0,0 +1,30 @@ +! go test badtest/... +! stdout ^ok +stdout ^FAIL\tbadtest/badexec +stdout ^FAIL\tbadtest/badsyntax +stdout ^FAIL\tbadtest/badvar + +-- badtest/badexec/x_test.go -- +package badexec + +func init() { + panic("badexec") +} + +-- badtest/badsyntax/x.go -- +package badsyntax + +-- badtest/badsyntax/x_test.go -- +package badsyntax + +func func func func func! + +-- badtest/badvar/x.go -- +package badvar + +-- badtest/badvar/x_test.go -- +package badvar_test + +func f() { + _ = notdefined +} diff --git a/libgo/go/cmd/go/testdata/script/test_compile_binary.txt b/libgo/go/cmd/go/testdata/script/test_compile_binary.txt new file mode 100644 index 0000000..6c01bc5 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_compile_binary.txt @@ -0,0 +1,6 @@ +! go test -c compile_binary/... +stderr 'build comment' + +-- compile_binary/foo_test.go -- +//+build foo +package foo diff --git a/libgo/go/cmd/go/testdata/script/vendor_complex.txt b/libgo/go/cmd/go/testdata/script/vendor_complex.txt new file mode 100644 index 0000000..6513451 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/vendor_complex.txt @@ -0,0 +1,73 @@ +# smoke test for complex build configuration +go build -o complex.exe complex +[exec:gccgo] go build -compiler=gccgo -o complex.exe complex + +-- complex/main.go -- +package main + +import ( + _ "complex/nest/sub/test12" + _ "complex/nest/sub/test23" + "complex/w" + "v" +) + +func main() { + println(v.Hello + " " + w.World) +} + +-- complex/nest/sub/test12/p.go -- +package test12 + +// Check that vendor/v1 is used but vendor/v2 is NOT used (sub/vendor/v2 wins). + +import ( + "v1" + "v2" +) + +const x = v1.ComplexNestVendorV1 +const y = v2.ComplexNestSubVendorV2 + +-- complex/nest/sub/test23/p.go -- +package test23 + +// Check that vendor/v3 is used but vendor/v2 is NOT used (sub/vendor/v2 wins). + +import ( + "v2" + "v3" +) + +const x = v3.ComplexNestVendorV3 +const y = v2.ComplexNestSubVendorV2 + +-- complex/nest/sub/vendor/v2/v2.go -- +package v2 + +const ComplexNestSubVendorV2 = true + +-- complex/nest/vendor/v1/v1.go -- +package v1 + +const ComplexNestVendorV1 = true + +-- complex/nest/vendor/v2/v2.go -- +package v2 + +const ComplexNestVendorV2 = true + +-- complex/nest/vendor/v3/v3.go -- +package v3 + +const ComplexNestVendorV3 = true + +-- complex/vendor/v/v.go -- +package v + +const Hello = "hello" + +-- complex/w/w.go -- +package w + +const World = "world" diff --git a/libgo/go/cmd/go/testdata/src/badtest/badexec/x_test.go b/libgo/go/cmd/go/testdata/src/badtest/badexec/x_test.go deleted file mode 100644 index 12f5051..0000000 --- a/libgo/go/cmd/go/testdata/src/badtest/badexec/x_test.go +++ /dev/null @@ -1,5 +0,0 @@ -package badexec - -func init() { - panic("badexec") -} diff --git a/libgo/go/cmd/go/testdata/src/badtest/badsyntax/x.go b/libgo/go/cmd/go/testdata/src/badtest/badsyntax/x.go deleted file mode 100644 index c8a5407..0000000 --- a/libgo/go/cmd/go/testdata/src/badtest/badsyntax/x.go +++ /dev/null @@ -1 +0,0 @@ -package badsyntax diff --git a/libgo/go/cmd/go/testdata/src/badtest/badsyntax/x_test.go b/libgo/go/cmd/go/testdata/src/badtest/badsyntax/x_test.go deleted file mode 100644 index 5be1074..0000000 --- a/libgo/go/cmd/go/testdata/src/badtest/badsyntax/x_test.go +++ /dev/null @@ -1,3 +0,0 @@ -package badsyntax - -func func func func func! diff --git a/libgo/go/cmd/go/testdata/src/badtest/badvar/x.go b/libgo/go/cmd/go/testdata/src/badtest/badvar/x.go deleted file mode 100644 index fdd46c4..0000000 --- a/libgo/go/cmd/go/testdata/src/badtest/badvar/x.go +++ /dev/null @@ -1 +0,0 @@ -package badvar diff --git a/libgo/go/cmd/go/testdata/src/badtest/badvar/x_test.go b/libgo/go/cmd/go/testdata/src/badtest/badvar/x_test.go deleted file mode 100644 index c67df01..0000000 --- a/libgo/go/cmd/go/testdata/src/badtest/badvar/x_test.go +++ /dev/null @@ -1,5 +0,0 @@ -package badvar_test - -func f() { - _ = notdefined -} diff --git a/libgo/go/cmd/go/testdata/src/complex/main.go b/libgo/go/cmd/go/testdata/src/complex/main.go deleted file mode 100644 index c38df01..0000000 --- a/libgo/go/cmd/go/testdata/src/complex/main.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -import ( - _ "complex/nest/sub/test12" - _ "complex/nest/sub/test23" - "complex/w" - "v" -) - -func main() { - println(v.Hello + " " + w.World) -} diff --git a/libgo/go/cmd/go/testdata/src/complex/nest/sub/test12/p.go b/libgo/go/cmd/go/testdata/src/complex/nest/sub/test12/p.go deleted file mode 100644 index 94943ec..0000000 --- a/libgo/go/cmd/go/testdata/src/complex/nest/sub/test12/p.go +++ /dev/null @@ -1,11 +0,0 @@ -package test12 - -// Check that vendor/v1 is used but vendor/v2 is NOT used (sub/vendor/v2 wins). - -import ( - "v1" - "v2" -) - -const x = v1.ComplexNestVendorV1 -const y = v2.ComplexNestSubVendorV2 diff --git a/libgo/go/cmd/go/testdata/src/complex/nest/sub/test23/p.go b/libgo/go/cmd/go/testdata/src/complex/nest/sub/test23/p.go deleted file mode 100644 index 8801a48..0000000 --- a/libgo/go/cmd/go/testdata/src/complex/nest/sub/test23/p.go +++ /dev/null @@ -1,11 +0,0 @@ -package test23 - -// Check that vendor/v3 is used but vendor/v2 is NOT used (sub/vendor/v2 wins). - -import ( - "v2" - "v3" -) - -const x = v3.ComplexNestVendorV3 -const y = v2.ComplexNestSubVendorV2 diff --git a/libgo/go/cmd/go/testdata/src/complex/nest/sub/vendor/v2/v2.go b/libgo/go/cmd/go/testdata/src/complex/nest/sub/vendor/v2/v2.go deleted file mode 100644 index 2991871..0000000 --- a/libgo/go/cmd/go/testdata/src/complex/nest/sub/vendor/v2/v2.go +++ /dev/null @@ -1,3 +0,0 @@ -package v2 - -const ComplexNestSubVendorV2 = true diff --git a/libgo/go/cmd/go/testdata/src/complex/nest/vendor/v1/v1.go b/libgo/go/cmd/go/testdata/src/complex/nest/vendor/v1/v1.go deleted file mode 100644 index a55f529..0000000 --- a/libgo/go/cmd/go/testdata/src/complex/nest/vendor/v1/v1.go +++ /dev/null @@ -1,3 +0,0 @@ -package v1 - -const ComplexNestVendorV1 = true diff --git a/libgo/go/cmd/go/testdata/src/complex/nest/vendor/v2/v2.go b/libgo/go/cmd/go/testdata/src/complex/nest/vendor/v2/v2.go deleted file mode 100644 index ac94def..0000000 --- a/libgo/go/cmd/go/testdata/src/complex/nest/vendor/v2/v2.go +++ /dev/null @@ -1,3 +0,0 @@ -package v2 - -const ComplexNestVendorV2 = true diff --git a/libgo/go/cmd/go/testdata/src/complex/nest/vendor/v3/v3.go b/libgo/go/cmd/go/testdata/src/complex/nest/vendor/v3/v3.go deleted file mode 100644 index abf99b9..0000000 --- a/libgo/go/cmd/go/testdata/src/complex/nest/vendor/v3/v3.go +++ /dev/null @@ -1,3 +0,0 @@ -package v3 - -const ComplexNestVendorV3 = true diff --git a/libgo/go/cmd/go/testdata/src/complex/vendor/v/v.go b/libgo/go/cmd/go/testdata/src/complex/vendor/v/v.go deleted file mode 100644 index bb20d86..0000000 --- a/libgo/go/cmd/go/testdata/src/complex/vendor/v/v.go +++ /dev/null @@ -1,3 +0,0 @@ -package v - -const Hello = "hello" diff --git a/libgo/go/cmd/go/testdata/src/complex/w/w.go b/libgo/go/cmd/go/testdata/src/complex/w/w.go deleted file mode 100644 index a9c7fbb..0000000 --- a/libgo/go/cmd/go/testdata/src/complex/w/w.go +++ /dev/null @@ -1,3 +0,0 @@ -package w - -const World = "world" diff --git a/libgo/go/cmd/go/testdata/src/failfast_test.go b/libgo/go/cmd/go/testdata/src/failfast_test.go index fef4d2a..6e64d73 100644 --- a/libgo/go/cmd/go/testdata/src/failfast_test.go +++ b/libgo/go/cmd/go/testdata/src/failfast_test.go @@ -52,3 +52,11 @@ func TestFailingSubtestsA(t *testing.T) { func TestFailingB(t *testing.T) { t.Errorf("FAIL - %s", t.Name()) } + +func TestFatalC(t *testing.T) { + t.Fatalf("FAIL - %s", t.Name()) +} + +func TestFatalD(t *testing.T) { + t.Fatalf("FAIL - %s", t.Name()) +} diff --git a/libgo/go/cmd/go/testdata/src/hello/hello.go b/libgo/go/cmd/go/testdata/src/hello/hello.go new file mode 100644 index 0000000..73d83e64 --- /dev/null +++ b/libgo/go/cmd/go/testdata/src/hello/hello.go @@ -0,0 +1,5 @@ +package main + +func main() { + println("hello, world") +} diff --git a/libgo/go/cmd/go/testdata/src/testnorun/p.go b/libgo/go/cmd/go/testdata/src/testnorun/p.go new file mode 100644 index 0000000..71a9a56 --- /dev/null +++ b/libgo/go/cmd/go/testdata/src/testnorun/p.go @@ -0,0 +1,5 @@ +package p + +func init() { + panic("go test must not link and run test binaries without tests") +} diff --git a/libgo/go/cmd/go/testdata/src/vetfail/p1/p1.go b/libgo/go/cmd/go/testdata/src/vetfail/p1/p1.go index 248317b..eaa9b18 100644 --- a/libgo/go/cmd/go/testdata/src/vetfail/p1/p1.go +++ b/libgo/go/cmd/go/testdata/src/vetfail/p1/p1.go @@ -1,3 +1,5 @@ +// +build !foo-bar + package p1 import "fmt" diff --git a/libgo/go/cmd/go/testdata/testcover/pkg1/a.go b/libgo/go/cmd/go/testdata/testcover/pkg1/a.go new file mode 100644 index 0000000..e291611 --- /dev/null +++ b/libgo/go/cmd/go/testdata/testcover/pkg1/a.go @@ -0,0 +1,7 @@ +package pkg1 + +import "fmt" + +func F() { + fmt.Println("pkg1") +} diff --git a/libgo/go/cmd/go/testdata/testcover/pkg2/a.go b/libgo/go/cmd/go/testdata/testcover/pkg2/a.go new file mode 100644 index 0000000..7bd9bd4 --- /dev/null +++ b/libgo/go/cmd/go/testdata/testcover/pkg2/a.go @@ -0,0 +1,7 @@ +package pkg2 + +import "fmt" + +func F() { + fmt.Println("pkg2") +} diff --git a/libgo/go/cmd/go/testdata/testcover/pkg2/a_test.go b/libgo/go/cmd/go/testdata/testcover/pkg2/a_test.go new file mode 100644 index 0000000..4f791ad --- /dev/null +++ b/libgo/go/cmd/go/testdata/testcover/pkg2/a_test.go @@ -0,0 +1 @@ +package pkg2 diff --git a/libgo/go/cmd/go/testdata/testcover/pkg3/a.go b/libgo/go/cmd/go/testdata/testcover/pkg3/a.go new file mode 100644 index 0000000..bf86ed8 --- /dev/null +++ b/libgo/go/cmd/go/testdata/testcover/pkg3/a.go @@ -0,0 +1,7 @@ +package pkg3 + +import "fmt" + +func F() { + fmt.Println("pkg3") +} diff --git a/libgo/go/cmd/go/testdata/testcover/pkg3/a_test.go b/libgo/go/cmd/go/testdata/testcover/pkg3/a_test.go new file mode 100644 index 0000000..39c2c5a --- /dev/null +++ b/libgo/go/cmd/go/testdata/testcover/pkg3/a_test.go @@ -0,0 +1,7 @@ +package pkg3 + +import "testing" + +func TestF(t *testing.T) { + F() +} diff --git a/libgo/go/cmd/go/testdata/testonly2/t.go b/libgo/go/cmd/go/testdata/testonly2/t.go new file mode 100644 index 0000000..82267d3 --- /dev/null +++ b/libgo/go/cmd/go/testdata/testonly2/t.go @@ -0,0 +1,6 @@ +// This package is not a test-only package, +// but it still matches the pattern ./testdata/testonly... when in cmd/go. + +package main + +func main() {} diff --git a/libgo/go/cmd/go/testdata/vendormod.txt b/libgo/go/cmd/go/testdata/vendormod.txt new file mode 100644 index 0000000..cd009db --- /dev/null +++ b/libgo/go/cmd/go/testdata/vendormod.txt @@ -0,0 +1,160 @@ +generated by: go run savedir.go vendormod + +-- a/foo/AUTHORS.txt -- +-- a/foo/CONTRIBUTORS -- +-- a/foo/LICENSE -- +-- a/foo/PATENTS -- +-- a/foo/COPYING -- +-- a/foo/COPYLEFT -- +-- a/foo/licensed-to-kill -- +-- w/LICENSE -- +-- x/NOTICE! -- +-- x/x2/LICENSE -- +-- mypkg/LICENSE.txt -- +-- a/foo/bar/b/main.go -- +package b +-- a/foo/bar/b/main_test.go -- +package b + +import ( + "os" + "testing" +) + +func TestDir(t *testing.T) { + if _, err := os.Stat("../testdata/1"); err != nil { + t.Fatalf("testdata: %v", err) + } +} +-- a/foo/bar/c/main.go -- +package c +-- a/foo/bar/c/main_test.go -- +package c + +import ( + "os" + "testing" +) + +func TestDir(t *testing.T) { + if _, err := os.Stat("../../../testdata/1"); err != nil { + t.Fatalf("testdata: %v", err) + } + if _, err := os.Stat("./testdata/1"); err != nil { + t.Fatalf("testdata: %v", err) + } +} +-- a/foo/bar/c/testdata/1 -- +-- a/foo/bar/testdata/1 -- +-- a/gcc/go/gofrontend.mod -- +module a +-- a/main.go -- +package a +-- a/main_test.go -- +package a + +import ( + "os" + "testing" +) + +func TestDir(t *testing.T) { + if _, err := os.Stat("./testdata/1"); err != nil { + t.Fatalf("testdata: %v", err) + } +} +-- a/testdata/1 -- +-- appengine.go -- +// +build appengine + +package m + +import _ "appengine" +import _ "appengine/datastore" +-- go.mod -- +module m + +require ( + a v1.0.0 + mysite/myname/mypkg v1.0.0 + w v1.0.0 // indirect + x v1.0.0 + y v1.0.0 + z v1.0.0 +) + +replace ( + a v1.0.0 => ./a + mysite/myname/mypkg v1.0.0 => ./mypkg + w v1.0.0 => ./w + x v1.0.0 => ./x + y v1.0.0 => ./y + z v1.0.0 => ./z +) +-- mypkg/go.mod -- +module me +-- mypkg/mydir/d.go -- +package mydir +-- subdir/v1_test.go -- +package m + +import _ "mysite/myname/mypkg/mydir" +-- testdata1.go -- +package m + +import _ "a" +-- testdata2.go -- +package m + +import _ "a/foo/bar/b" +import _ "a/foo/bar/c" +-- v1.go -- +package m + +import _ "x" +-- v2.go -- +// +build abc + +package mMmMmMm + +import _ "y" +-- v3.go -- +// +build !abc + +package m + +import _ "z" +-- v4.go -- +// +build notmytag + +package m + +import _ "x/x1" +-- w/go.mod -- +module w +-- w/w.go -- +package w +-- x/go.mod -- +module x +-- x/testdata/x.txt -- +placeholder - want directory with no go files +-- x/x.go -- +package x +-- x/x1/x1.go -- +// +build notmytag + +package x1 +-- x/x2/dummy.txt -- +dummy +-- x/x_test.go -- +package x + +import _ "w" +-- y/go.mod -- +module y +-- y/y.go -- +package y +-- z/go.mod -- +module z +-- z/z.go -- +package z diff --git a/libgo/go/cmd/go/vendor_test.go b/libgo/go/cmd/go/vendor_test.go index 0e7a633..22aa643 100644 --- a/libgo/go/cmd/go/vendor_test.go +++ b/libgo/go/cmd/go/vendor_test.go @@ -332,7 +332,7 @@ func TestVendor12156(t *testing.T) { // Module legacy support does path rewriting very similar to vendoring. -func TestModLegacy(t *testing.T) { +func TestLegacyMod(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/modlegacy")) @@ -347,7 +347,7 @@ func TestModLegacy(t *testing.T) { tg.run("build", "old/p1", "new/p1") } -func TestModLegacyGet(t *testing.T) { +func TestLegacyModGet(t *testing.T) { testenv.MustHaveExternalNetwork(t) tg := testgo(t) diff --git a/libgo/go/cmd/internal/buildid/note.go b/libgo/go/cmd/internal/buildid/note.go index f0439fb..2d26ea9 100644 --- a/libgo/go/cmd/internal/buildid/note.go +++ b/libgo/go/cmd/internal/buildid/note.go @@ -30,6 +30,7 @@ func ReadELFNote(filename, name string, typ int32) ([]byte, error) { if err != nil { return nil, err } + defer f.Close() for _, sect := range f.Sections { if sect.Type != elf.SHT_NOTE { continue @@ -147,7 +148,7 @@ func readELF(name string, f *os.File, data []byte) (buildid string, err error) { break } off += notesz - align := uint64(p.Align) + align := p.Align alignedOff := (off + align - 1) &^ (align - 1) notesz += alignedOff - off off = alignedOff diff --git a/libgo/go/cmd/internal/objabi/flag.go b/libgo/go/cmd/internal/objabi/flag.go index 1bd4bc9..30cd7dc 100644 --- a/libgo/go/cmd/internal/objabi/flag.go +++ b/libgo/go/cmd/internal/objabi/flag.go @@ -7,6 +7,9 @@ package objabi import ( "flag" "fmt" + "io" + "io/ioutil" + "log" "os" "strconv" "strings" @@ -20,18 +23,53 @@ func Flagfn1(name, usage string, f func(string)) { flag.Var(fn1(f), name, usage) } -func Flagprint(fd int) { - if fd == 1 { - flag.CommandLine.SetOutput(os.Stdout) - } +func Flagprint(w io.Writer) { + flag.CommandLine.SetOutput(w) flag.PrintDefaults() } func Flagparse(usage func()) { flag.Usage = usage + os.Args = expandArgs(os.Args) flag.Parse() } +// expandArgs expands "response files" arguments in the provided slice. +// +// A "response file" argument starts with '@' and the rest of that +// argument is a filename with CR-or-CRLF-separated arguments. Each +// argument in the named files can also contain response file +// arguments. See Issue 18468. +// +// The returned slice 'out' aliases 'in' iff the input did not contain +// any response file arguments. +// +// TODO: handle relative paths of recursive expansions in different directories? +// Is there a spec for this? Are relative paths allowed? +func expandArgs(in []string) (out []string) { + // out is nil until we see a "@" argument. + for i, s := range in { + if strings.HasPrefix(s, "@") { + if out == nil { + out = make([]string, 0, len(in)*2) + out = append(out, in[:i]...) + } + slurp, err := ioutil.ReadFile(s[1:]) + if err != nil { + log.Fatal(err) + } + args := strings.Split(strings.TrimSpace(strings.Replace(string(slurp), "\r", "", -1)), "\n") + out = append(out, expandArgs(args)...) + } else if out != nil { + out = append(out, s) + } + } + if out == nil { + return in + } + return +} + func AddVersionFlag() { flag.Var(versionFlag{}, "V", "print version and exit") } @@ -107,21 +145,6 @@ func (c *count) IsCountFlag() bool { return true } -type fn0 func() - -func (f fn0) Set(s string) error { - f() - return nil -} - -func (f fn0) Get() interface{} { return nil } - -func (f fn0) String() string { return "" } - -func (f fn0) IsBoolFlag() bool { - return true -} - type fn1 func(string) func (f fn1) Set(s string) error { diff --git a/libgo/go/cmd/internal/objabi/funcdata.go b/libgo/go/cmd/internal/objabi/funcdata.go index 80874ed..a782712 100644 --- a/libgo/go/cmd/internal/objabi/funcdata.go +++ b/libgo/go/cmd/internal/objabi/funcdata.go @@ -13,9 +13,11 @@ package objabi const ( PCDATA_StackMapIndex = 0 PCDATA_InlTreeIndex = 1 + PCDATA_RegMapIndex = 2 FUNCDATA_ArgsPointerMaps = 0 FUNCDATA_LocalsPointerMaps = 1 FUNCDATA_InlTree = 2 + FUNCDATA_RegPointerMaps = 3 // ArgsSizeUnknown is set in Func.argsize to mark all functions // whose argument size is unknown (C vararg functions, and diff --git a/libgo/go/cmd/internal/objabi/funcid.go b/libgo/go/cmd/internal/objabi/funcid.go index 55f1328..15a63ab 100644 --- a/libgo/go/cmd/internal/objabi/funcid.go +++ b/libgo/go/cmd/internal/objabi/funcid.go @@ -13,6 +13,7 @@ type FuncID uint32 const ( FuncID_normal FuncID = iota // not a special function + FuncID_runtime_main FuncID_goexit FuncID_jmpdefer FuncID_mcall @@ -22,13 +23,11 @@ const ( FuncID_asmcgocall FuncID_sigpanic FuncID_runfinq - FuncID_bgsweep - FuncID_forcegchelper - FuncID_timerproc FuncID_gcBgMarkWorker FuncID_systemstack_switch FuncID_systemstack FuncID_cgocallback_gofunc FuncID_gogo FuncID_externalthreadhandler + FuncID_debugCallV1 ) diff --git a/libgo/go/cmd/internal/objabi/head.go b/libgo/go/cmd/internal/objabi/head.go index ff19606..23c7b62 100644 --- a/libgo/go/cmd/internal/objabi/head.go +++ b/libgo/go/cmd/internal/objabi/head.go @@ -40,6 +40,7 @@ const ( Hdarwin Hdragonfly Hfreebsd + Hjs Hlinux Hnacl Hnetbsd @@ -57,6 +58,8 @@ func (h *HeadType) Set(s string) error { *h = Hdragonfly case "freebsd": *h = Hfreebsd + case "js": + *h = Hjs case "linux", "android": *h = Hlinux case "nacl": @@ -85,6 +88,8 @@ func (h *HeadType) String() string { return "dragonfly" case Hfreebsd: return "freebsd" + case Hjs: + return "js" case Hlinux: return "linux" case Hnacl: diff --git a/libgo/go/cmd/internal/objabi/reloctype.go b/libgo/go/cmd/internal/objabi/reloctype.go index 2e0b916..a3e2868 100644 --- a/libgo/go/cmd/internal/objabi/reloctype.go +++ b/libgo/go/cmd/internal/objabi/reloctype.go @@ -167,7 +167,7 @@ const ( // R_ADDRPOWER_PCREL relocates two D-form instructions like R_ADDRPOWER, but // inserts the displacement from the place being relocated to the address of the - // the relocated symbol instead of just its address. + // relocated symbol instead of just its address. R_ADDRPOWER_PCREL // R_ADDRPOWER_TOCREL relocates two D-form instructions like R_ADDRPOWER, but @@ -176,7 +176,7 @@ const ( R_ADDRPOWER_TOCREL // R_ADDRPOWER_TOCREL relocates a D-form, DS-form instruction sequence like - // R_ADDRPOWER_DS but inserts the offset from the TOC to the address of the the + // R_ADDRPOWER_DS but inserts the offset from the TOC to the address of the // relocated symbol rather than the symbol's address. R_ADDRPOWER_TOCREL_DS @@ -193,6 +193,9 @@ const ( // R_ADDRCUOFF resolves to a pointer-sized offset from the start of the // symbol's DWARF compile unit. R_ADDRCUOFF + + // R_WASMIMPORT resolves to the index of the WebAssembly function import. + R_WASMIMPORT ) // IsDirectJump returns whether r is a relocation for a direct jump. diff --git a/libgo/go/cmd/internal/objabi/reloctype_string.go b/libgo/go/cmd/internal/objabi/reloctype_string.go index a6efe9c..2cd3a94 100644 --- a/libgo/go/cmd/internal/objabi/reloctype_string.go +++ b/libgo/go/cmd/internal/objabi/reloctype_string.go @@ -2,16 +2,16 @@ package objabi -import "fmt" +import "strconv" -const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_WEAKADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFF" +const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_WEAKADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORT" -var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 60, 66, 72, 81, 92, 101, 112, 122, 129, 136, 144, 152, 160, 166, 172, 178, 188, 197, 208, 219, 229, 238, 251, 265, 279, 293, 309, 323, 337, 348, 362, 377, 394, 412, 433, 443, 454, 467, 478} +var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 60, 66, 72, 81, 92, 101, 112, 122, 129, 136, 144, 152, 160, 166, 172, 178, 188, 197, 208, 219, 229, 238, 251, 265, 279, 293, 309, 323, 337, 348, 362, 377, 394, 412, 433, 443, 454, 467, 478, 490} func (i RelocType) String() string { i -= 1 if i < 0 || i >= RelocType(len(_RelocType_index)-1) { - return fmt.Sprintf("RelocType(%d)", i+1) + return "RelocType(" + strconv.FormatInt(int64(i+1), 10) + ")" } return _RelocType_name[_RelocType_index[i]:_RelocType_index[i+1]] } diff --git a/libgo/go/cmd/internal/objabi/symkind.go b/libgo/go/cmd/internal/objabi/symkind.go index ea180d0..b95a0d3 100644 --- a/libgo/go/cmd/internal/objabi/symkind.go +++ b/libgo/go/cmd/internal/objabi/symkind.go @@ -34,6 +34,7 @@ package objabi type SymKind uint8 // Defined SymKind values. +// These are used to index into cmd/link/internal/sym/AbiSymKindToSymKind // // TODO(rsc): Give idiomatic Go names. //go:generate stringer -type=SymKind @@ -58,4 +59,7 @@ const ( SDWARFINFO SDWARFRANGE SDWARFLOC + SDWARFMISC + // Update cmd/link/internal/sym/AbiSymKindToSymKind for new SymKind values. + ) diff --git a/libgo/go/cmd/internal/objabi/symkind_string.go b/libgo/go/cmd/internal/objabi/symkind_string.go index 3064c8e..7152d6c 100644 --- a/libgo/go/cmd/internal/objabi/symkind_string.go +++ b/libgo/go/cmd/internal/objabi/symkind_string.go @@ -2,15 +2,15 @@ package objabi -import "fmt" +import "strconv" -const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFINFOSDWARFRANGESDWARFLOC" +const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFINFOSDWARFRANGESDWARFLOCSDWARFMISC" -var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 61, 72, 81} +var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 61, 72, 81, 91} func (i SymKind) String() string { if i >= SymKind(len(_SymKind_index)-1) { - return fmt.Sprintf("SymKind(%d)", i) + return "SymKind(" + strconv.FormatInt(int64(i), 10) + ")" } return _SymKind_name[_SymKind_index[i]:_SymKind_index[i+1]] } diff --git a/libgo/go/cmd/internal/objabi/util.go b/libgo/go/cmd/internal/objabi/util.go index eafef6b..a47e2f9 100644 --- a/libgo/go/cmd/internal/objabi/util.go +++ b/libgo/go/cmd/internal/objabi/util.go @@ -21,13 +21,14 @@ func envOr(key, value string) string { var ( defaultGOROOT string // set by linker - GOROOT = envOr("GOROOT", defaultGOROOT) - GOARCH = envOr("GOARCH", defaultGOARCH) - GOOS = envOr("GOOS", defaultGOOS) - GO386 = envOr("GO386", defaultGO386) - GOARM = goarm() - GOMIPS = gomips() - Version = version + GOROOT = envOr("GOROOT", defaultGOROOT) + GOARCH = envOr("GOARCH", defaultGOARCH) + GOOS = envOr("GOOS", defaultGOOS) + GO386 = envOr("GO386", defaultGO386) + GOARM = goarm() + GOMIPS = gomips() + GOMIPS64 = gomips64() + Version = version ) func goarm() int { @@ -53,6 +54,15 @@ func gomips() string { panic("unreachable") } +func gomips64() string { + switch v := envOr("GOMIPS64", defaultGOMIPS64); v { + case "hardfloat", "softfloat": + return v + } + log.Fatalf("Invalid GOMIPS64 value. Must be hardfloat or softfloat.") + panic("unreachable") +} + func Getgoextlinkenabled() string { return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED) } @@ -95,6 +105,7 @@ var ( Fieldtrack_enabled int Preemptibleloops_enabled int Clobberdead_enabled int + DebugCPU_enabled int ) // Toolchain experiments. @@ -109,6 +120,7 @@ var exper = []struct { {"framepointer", &framepointer_enabled}, {"preemptibleloops", &Preemptibleloops_enabled}, {"clobberdead", &Clobberdead_enabled}, + {"debugcpu", &DebugCPU_enabled}, } var defaultExpstring = Expstring() diff --git a/libgo/go/cmd/internal/test2json/test2json.go b/libgo/go/cmd/internal/test2json/test2json.go index 3e09c8d..f805213 100644 --- a/libgo/go/cmd/internal/test2json/test2json.go +++ b/libgo/go/cmd/internal/test2json/test2json.go @@ -16,6 +16,7 @@ import ( "strconv" "strings" "time" + "unicode" "unicode/utf8" ) @@ -140,6 +141,7 @@ var ( []byte("--- PASS: "), []byte("--- FAIL: "), []byte("--- SKIP: "), + []byte("--- BENCH: "), } fourSpace = []byte(" ") @@ -173,6 +175,7 @@ func (c *converter) handleInputLine(line []byte) { // "=== RUN " // "=== PAUSE " // "=== CONT " + actionColon := false origLine := line ok := false indent := 0 @@ -186,6 +189,7 @@ func (c *converter) handleInputLine(line []byte) { // "--- PASS: " // "--- FAIL: " // "--- SKIP: " + // "--- BENCH: " // but possibly indented. for bytes.HasPrefix(line, fourSpace) { line = line[4:] @@ -193,6 +197,7 @@ func (c *converter) handleInputLine(line []byte) { } for _, magic := range reports { if bytes.HasPrefix(line, magic) { + actionColon = true ok = true break } @@ -206,8 +211,15 @@ func (c *converter) handleInputLine(line []byte) { } // Parse out action and test name. - action := strings.ToLower(strings.TrimSuffix(strings.TrimSpace(string(line[4:4+6])), ":")) - name := strings.TrimSpace(string(line[4+6:])) + i := 0 + if actionColon { + i = bytes.IndexByte(line, ':') + 1 + } + if i == 0 { + i = len(updates[0]) + } + action := strings.ToLower(strings.TrimSuffix(strings.TrimSpace(string(line[4:i])), ":")) + name := strings.TrimSpace(string(line[i:])) e := &event{Action: action} if line[0] == '-' { // PASS or FAIL report @@ -226,6 +238,7 @@ func (c *converter) handleInputLine(line []byte) { if len(c.report) < indent { // Nested deeper than expected. // Treat this line as plain output. + c.output.write(origLine) return } // Flush reports at this indentation level or deeper. @@ -342,6 +355,15 @@ func (l *lineBuffer) write(b []byte) { for i < len(l.b) { j := bytes.IndexByte(l.b[i:], '\n') if j < 0 { + if !l.mid { + if j := bytes.IndexByte(l.b[i:], '\t'); j >= 0 { + if isBenchmarkName(bytes.TrimRight(l.b[i:i+j], " ")) { + l.part(l.b[i : i+j+1]) + l.mid = true + i += j + 1 + } + } + } break } e := i + j + 1 @@ -383,6 +405,21 @@ func (l *lineBuffer) flush() { } } +var benchmark = []byte("Benchmark") + +// isBenchmarkName reports whether b is a valid benchmark name +// that might appear as the first field in a benchmark result line. +func isBenchmarkName(b []byte) bool { + if !bytes.HasPrefix(b, benchmark) { + return false + } + if len(b) == len(benchmark) { // just "Benchmark" + return true + } + r, _ := utf8.DecodeRune(b[len(benchmark):]) + return !unicode.IsLower(r) +} + // trimUTF8 returns a length t as close to len(b) as possible such that b[:t] // does not end in the middle of a possibly-valid UTF-8 sequence. // diff --git a/libgo/go/cmd/internal/test2json/testdata/bench.json b/libgo/go/cmd/internal/test2json/testdata/bench.json new file mode 100644 index 0000000..69e417e --- /dev/null +++ b/libgo/go/cmd/internal/test2json/testdata/bench.json @@ -0,0 +1,14 @@ +{"Action":"output","Output":"goos: darwin\n"} +{"Action":"output","Output":"goarch: 386\n"} +{"Action":"output","Output":"BenchmarkFoo-8 \t2000000000\t 0.00 ns/op\n"} +{"Action":"output","Test":"BenchmarkFoo-8","Output":"--- BENCH: BenchmarkFoo-8\n"} +{"Action":"output","Test":"BenchmarkFoo-8","Output":"\tx_test.go:8: My benchmark\n"} +{"Action":"output","Test":"BenchmarkFoo-8","Output":"\tx_test.go:8: My benchmark\n"} +{"Action":"output","Test":"BenchmarkFoo-8","Output":"\tx_test.go:8: My benchmark\n"} +{"Action":"output","Test":"BenchmarkFoo-8","Output":"\tx_test.go:8: My benchmark\n"} +{"Action":"output","Test":"BenchmarkFoo-8","Output":"\tx_test.go:8: My benchmark\n"} +{"Action":"output","Test":"BenchmarkFoo-8","Output":"\tx_test.go:8: My benchmark\n"} +{"Action":"bench","Test":"BenchmarkFoo-8"} +{"Action":"output","Output":"PASS\n"} +{"Action":"output","Output":"ok \tcommand-line-arguments\t0.009s\n"} +{"Action":"pass"} diff --git a/libgo/go/cmd/internal/test2json/testdata/bench.test b/libgo/go/cmd/internal/test2json/testdata/bench.test new file mode 100644 index 0000000..453bd59 --- /dev/null +++ b/libgo/go/cmd/internal/test2json/testdata/bench.test @@ -0,0 +1,12 @@ +goos: darwin +goarch: 386 +BenchmarkFoo-8 2000000000 0.00 ns/op +--- BENCH: BenchmarkFoo-8 + x_test.go:8: My benchmark + x_test.go:8: My benchmark + x_test.go:8: My benchmark + x_test.go:8: My benchmark + x_test.go:8: My benchmark + x_test.go:8: My benchmark +PASS +ok command-line-arguments 0.009s diff --git a/libgo/go/cmd/internal/test2json/testdata/benchfail.json b/libgo/go/cmd/internal/test2json/testdata/benchfail.json new file mode 100644 index 0000000..ad3ac9e --- /dev/null +++ b/libgo/go/cmd/internal/test2json/testdata/benchfail.json @@ -0,0 +1,6 @@ +{"Action":"output","Test":"BenchmarkFoo","Output":"--- FAIL: BenchmarkFoo\n"} +{"Action":"output","Test":"BenchmarkFoo","Output":"\tx_test.go:8: My benchmark\n"} +{"Action":"fail","Test":"BenchmarkFoo"} +{"Action":"output","Output":"FAIL\n"} +{"Action":"output","Output":"FAIL\tcommand-line-arguments\t0.008s\n"} +{"Action":"fail"} diff --git a/libgo/go/cmd/internal/test2json/testdata/benchfail.test b/libgo/go/cmd/internal/test2json/testdata/benchfail.test new file mode 100644 index 0000000..538d957 --- /dev/null +++ b/libgo/go/cmd/internal/test2json/testdata/benchfail.test @@ -0,0 +1,4 @@ +--- FAIL: BenchmarkFoo + x_test.go:8: My benchmark +FAIL +FAIL command-line-arguments 0.008s diff --git a/libgo/go/cmd/internal/test2json/testdata/benchshort.json b/libgo/go/cmd/internal/test2json/testdata/benchshort.json new file mode 100644 index 0000000..8c61d95 --- /dev/null +++ b/libgo/go/cmd/internal/test2json/testdata/benchshort.json @@ -0,0 +1,7 @@ +{"Action":"output","Output":"# This file ends in an early EOF to trigger the Benchmark prefix test,\n"} +{"Action":"output","Output":"# which only happens when a benchmark prefix is seen ahead of the \\n.\n"} +{"Action":"output","Output":"# Normally that's due to the benchmark running and the \\n coming later,\n"} +{"Action":"output","Output":"# but to avoid questions of timing, we just use a file with no \\n at all.\n"} +{"Action":"output","Output":"BenchmarkFoo \t"} +{"Action":"output","Output":"10000 early EOF"} +{"Action":"fail"} diff --git a/libgo/go/cmd/internal/test2json/testdata/benchshort.test b/libgo/go/cmd/internal/test2json/testdata/benchshort.test new file mode 100644 index 0000000..0b173ab --- /dev/null +++ b/libgo/go/cmd/internal/test2json/testdata/benchshort.test @@ -0,0 +1,5 @@ +# This file ends in an early EOF to trigger the Benchmark prefix test, +# which only happens when a benchmark prefix is seen ahead of the \n. +# Normally that's due to the benchmark running and the \n coming later, +# but to avoid questions of timing, we just use a file with no \n at all. +BenchmarkFoo 10000 early EOF
\ No newline at end of file diff --git a/libgo/go/cmd/internal/test2json/testdata/issue23036.json b/libgo/go/cmd/internal/test2json/testdata/issue23036.json new file mode 100644 index 0000000..935c0c5 --- /dev/null +++ b/libgo/go/cmd/internal/test2json/testdata/issue23036.json @@ -0,0 +1,12 @@ +{"Action":"run","Test":"TestActualCase"} +{"Action":"output","Test":"TestActualCase","Output":"=== RUN TestActualCase\n"} +{"Action":"output","Test":"TestActualCase","Output":"--- FAIL: TestActualCase (0.00s)\n"} +{"Action":"output","Test":"TestActualCase","Output":" foo_test.go:14: Differed.\n"} +{"Action":"output","Test":"TestActualCase","Output":" Expected: MyTest:\n"} +{"Action":"output","Test":"TestActualCase","Output":" --- FAIL: Test output from other tool\n"} +{"Action":"output","Test":"TestActualCase","Output":" Actual: not expected\n"} +{"Action":"fail","Test":"TestActualCase"} +{"Action":"output","Output":"FAIL\n"} +{"Action":"output","Output":"exit status 1\n"} +{"Action":"output","Output":"FAIL github.com/org/project/badtest 0.049s\n"} +{"Action":"fail"} diff --git a/libgo/go/cmd/internal/test2json/testdata/issue23036.test b/libgo/go/cmd/internal/test2json/testdata/issue23036.test new file mode 100644 index 0000000..fd4774f --- /dev/null +++ b/libgo/go/cmd/internal/test2json/testdata/issue23036.test @@ -0,0 +1,9 @@ +=== RUN TestActualCase +--- FAIL: TestActualCase (0.00s) + foo_test.go:14: Differed. + Expected: MyTest: + --- FAIL: Test output from other tool + Actual: not expected +FAIL +exit status 1 +FAIL github.com/org/project/badtest 0.049s diff --git a/libgo/go/cmd/internal/test2json/testdata/issue23920.json b/libgo/go/cmd/internal/test2json/testdata/issue23920.json new file mode 100644 index 0000000..28f7bd5 --- /dev/null +++ b/libgo/go/cmd/internal/test2json/testdata/issue23920.json @@ -0,0 +1,14 @@ +{"Action":"run","Test":"TestWithColons"} +{"Action":"output","Test":"TestWithColons","Output":"=== RUN TestWithColons\n"} +{"Action":"run","Test":"TestWithColons/[::1]"} +{"Action":"output","Test":"TestWithColons/[::1]","Output":"=== RUN TestWithColons/[::1]\n"} +{"Action":"run","Test":"TestWithColons/127.0.0.1:0"} +{"Action":"output","Test":"TestWithColons/127.0.0.1:0","Output":"=== RUN TestWithColons/127.0.0.1:0\n"} +{"Action":"output","Test":"TestWithColons","Output":"--- PASS: TestWithColons (0.00s)\n"} +{"Action":"output","Test":"TestWithColons/[::1]","Output":" --- PASS: TestWithColons/[::1] (0.00s)\n"} +{"Action":"pass","Test":"TestWithColons/[::1]"} +{"Action":"output","Test":"TestWithColons/127.0.0.1:0","Output":" --- PASS: TestWithColons/127.0.0.1:0 (0.00s)\n"} +{"Action":"pass","Test":"TestWithColons/127.0.0.1:0"} +{"Action":"pass","Test":"TestWithColons"} +{"Action":"output","Output":"PASS\n"} +{"Action":"pass"} diff --git a/libgo/go/cmd/internal/test2json/testdata/issue23920.test b/libgo/go/cmd/internal/test2json/testdata/issue23920.test new file mode 100644 index 0000000..43bf058 --- /dev/null +++ b/libgo/go/cmd/internal/test2json/testdata/issue23920.test @@ -0,0 +1,7 @@ +=== RUN TestWithColons +=== RUN TestWithColons/[::1] +=== RUN TestWithColons/127.0.0.1:0 +--- PASS: TestWithColons (0.00s) + --- PASS: TestWithColons/[::1] (0.00s) + --- PASS: TestWithColons/127.0.0.1:0 (0.00s) +PASS diff --git a/libgo/go/cmd/test2json/main.go b/libgo/go/cmd/test2json/main.go index 7bdc867..0385d8f2 100644 --- a/libgo/go/cmd/test2json/main.go +++ b/libgo/go/cmd/test2json/main.go @@ -45,15 +45,17 @@ // pause - the test has been paused // cont - the test has continued running // pass - the test passed -// fail - the test failed +// bench - the benchmark printed log output but did not fail +// fail - the test or benchmark failed // output - the test printed output +// skip - the test was skipped or the package contained no tests // // The Package field, if present, specifies the package being tested. // When the go command runs parallel tests in -json mode, events from // different tests are interlaced; the Package field allows readers to // separate them. // -// The Test field, if present, specifies the test or example, or benchmark +// The Test field, if present, specifies the test, example, or benchmark // function that caused the event. Events for the overall package test // do not set Test. // @@ -67,6 +69,14 @@ // the concatenation of the Output fields of all output events is the exact // output of the test execution. // +// When a benchmark runs, it typically produces a single line of output +// giving timing results. That line is reported in an event with Action == "output" +// and no Test field. If a benchmark logs output or reports a failure +// (for example, by using b.Log or b.Error), that extra output is reported +// as a sequence of events with Test set to the benchmark name, terminated +// by a final event with Action == "bench" or "fail". +// Benchmarks have no events with Action == "run", "pause", or "cont". +// package main import ( diff --git a/libgo/go/cmd/vet/all/main.go b/libgo/go/cmd/vet/all/main.go index 09167af..09181f9 100644 --- a/libgo/go/cmd/vet/all/main.go +++ b/libgo/go/cmd/vet/all/main.go @@ -192,6 +192,12 @@ func vetPlatforms(pp []platform) { } func (p platform) vet() { + if p.os == "linux" && p.arch == "riscv64" { + // TODO(tklauser): enable as soon as the riscv64 port has fully landed + fmt.Println("skipping linux/riscv64") + return + } + var buf bytes.Buffer fmt.Fprintf(&buf, "go run main.go -p %s\n", p) diff --git a/libgo/go/cmd/vet/all/whitelist/386.txt b/libgo/go/cmd/vet/all/whitelist/386.txt index 505856f..f59094e 100644 --- a/libgo/go/cmd/vet/all/whitelist/386.txt +++ b/libgo/go/cmd/vet/all/whitelist/386.txt @@ -1,6 +1,7 @@ // 386-specific vet whitelist. See readme.txt for details. -runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: Compare is in package bytes +internal/bytealg/compare_386.s: [386] cannot check cross-package assembly function: Compare is in package bytes +internal/bytealg/compare_386.s: [386] cannot check cross-package assembly function: cmpstring is in package runtime // startup code uses non-standard calling convention and intentionally // omits args. @@ -15,13 +16,9 @@ runtime/asm_386.s: [386] morestack: use of 4(SP) points beyond argument frame runtime/asm_386.s: [386] ldt0setup: function ldt0setup missing Go declaration runtime/asm_386.s: [386] emptyfunc: function emptyfunc missing Go declaration runtime/asm_386.s: [386] aeshashbody: function aeshashbody missing Go declaration -runtime/asm_386.s: [386] memeqbody: function memeqbody missing Go declaration -runtime/asm_386.s: [386] cmpbody: function cmpbody missing Go declaration runtime/asm_386.s: [386] addmoduledata: function addmoduledata missing Go declaration runtime/duff_386.s: [386] duffzero: function duffzero missing Go declaration runtime/duff_386.s: [386] duffcopy: function duffcopy missing Go declaration runtime/asm_386.s: [386] uint32tofloat64: function uint32tofloat64 missing Go declaration runtime/asm_386.s: [386] float64touint32: function float64touint32 missing Go declaration - -runtime/asm_386.s: [386] stackcheck: function stackcheck missing Go declaration diff --git a/libgo/go/cmd/vet/all/whitelist/all.txt b/libgo/go/cmd/vet/all/whitelist/all.txt index 6792d26..397ee4e 100644 --- a/libgo/go/cmd/vet/all/whitelist/all.txt +++ b/libgo/go/cmd/vet/all/whitelist/all.txt @@ -11,9 +11,14 @@ go/types/scope.go: method WriteTo(w io.Writer, n int, recurse bool) should have // Nothing much to do about cross-package assembly. Unfortunate. runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: call is in package reflect -runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: Equal is in package bytes -runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: IndexByte is in package bytes -runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: IndexByte is in package strings +internal/bytealg/equal_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: Equal is in package bytes +internal/bytealg/equal_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: memequal is in package runtime +internal/bytealg/equal_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: memequal_varlen is in package runtime +internal/bytealg/indexbyte_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: IndexByte is in package bytes +internal/bytealg/indexbyte_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: IndexByte is in package strings + +// The write barrier is called directly by the compiler, so no Go def +runtime/asm_ARCHSUFF.s: [GOARCH] gcWriteBarrier: function gcWriteBarrier missing Go declaration // Legitimate vet complaints in which we are testing for correct runtime behavior // in bad situations that vet can also detect statically. @@ -22,7 +27,6 @@ encoding/json/decode_test.go: struct field m2 has json tag but is not exported encoding/json/tagkey_test.go: struct field tag `:"BadFormat"` not compatible with reflect.StructTag.Get: bad syntax for struct tag key runtime/testdata/testprog/deadlock.go: unreachable code runtime/testdata/testprog/deadlock.go: unreachable code -sync/cond_test.go: assignment copies lock value to c2: sync.Cond contains sync.noCopy // Non-standard method signatures. // These cases are basically ok. diff --git a/libgo/go/cmd/vet/all/whitelist/amd64.txt b/libgo/go/cmd/vet/all/whitelist/amd64.txt index ebde7be5..20e0d48 100644 --- a/libgo/go/cmd/vet/all/whitelist/amd64.txt +++ b/libgo/go/cmd/vet/all/whitelist/amd64.txt @@ -1,34 +1,22 @@ // amd64-specific vet whitelist. See readme.txt for details. - // False positives. +// Nothing much to do about cross-package assembly. Unfortunate. +internal/bytealg/compare_amd64.s: [amd64] cannot check cross-package assembly function: Compare is in package bytes +internal/bytealg/compare_amd64.s: [amd64] cannot check cross-package assembly function: cmpstring is in package runtime // reflect trampolines intentionally omit arg size. Same for morestack. runtime/asm_amd64.s: [amd64] morestack: use of 8(SP) points beyond argument frame runtime/asm_amd64.s: [amd64] morestack: use of 16(SP) points beyond argument frame runtime/asm_amd64.s: [amd64] morestack: use of 8(SP) points beyond argument frame -// Nothing much to do about cross-package assembly. Unfortunate. -runtime/asm_amd64.s: [amd64] cannot check cross-package assembly function: indexShortStr is in package strings -runtime/asm_amd64.s: [GOARCH] cannot check cross-package assembly function: Compare is in package bytes -runtime/asm_amd64.s: [amd64] cannot check cross-package assembly function: indexShortStr is in package bytes -runtime/asm_amd64.s: [amd64] cannot check cross-package assembly function: countByte is in package strings -runtime/asm_amd64.s: [amd64] cannot check cross-package assembly function: countByte is in package bytes - // Intentionally missing declarations. These are special assembly routines. // Some are jumped into from other routines, with values in specific registers. // duff* have direct calls from the compiler. // Others use the platform ABI. // There is no sensible corresponding Go prototype. runtime/asm_amd64.s: [amd64] aeshashbody: function aeshashbody missing Go declaration -runtime/asm_amd64.s: [amd64] memeqbody: function memeqbody missing Go declaration -runtime/asm_amd64.s: [amd64] cmpbody: function cmpbody missing Go declaration -runtime/asm_amd64.s: [amd64] indexbytebody: function indexbytebody missing Go declaration runtime/asm_amd64.s: [amd64] addmoduledata: function addmoduledata missing Go declaration runtime/duff_amd64.s: [amd64] duffzero: function duffzero missing Go declaration runtime/duff_amd64.s: [amd64] duffcopy: function duffcopy missing Go declaration -runtime/asm_amd64.s: [amd64] stackcheck: function stackcheck missing Go declaration -runtime/asm_amd64.s: [amd64] indexShortStr: function indexShortStr missing Go declaration -runtime/asm_amd64.s: [amd64] countByte: function countByte missing Go declaration -runtime/asm_amd64.s: [amd64] gcWriteBarrier: function gcWriteBarrier missing Go declaration diff --git a/libgo/go/cmd/vet/all/whitelist/arm.txt b/libgo/go/cmd/vet/all/whitelist/arm.txt index 839346c..8f98782 100644 --- a/libgo/go/cmd/vet/all/whitelist/arm.txt +++ b/libgo/go/cmd/vet/all/whitelist/arm.txt @@ -1,15 +1,11 @@ // arm-specific vet whitelist. See readme.txt for details. -runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: Compare is in package bytes - -// reflect trampolines intentionally omit arg size. Same for morestack. -runtime/asm_arm.s: [arm] morestack: use of 4(R13) points beyond argument frame +internal/bytealg/compare_arm.s: [arm] cannot check cross-package assembly function: Compare is in package bytes +internal/bytealg/compare_arm.s: [arm] cannot check cross-package assembly function: cmpstring is in package runtime // Intentionally missing declarations. runtime/asm_arm.s: [arm] emptyfunc: function emptyfunc missing Go declaration -runtime/asm_arm.s: [arm] abort: function abort missing Go declaration runtime/asm_arm.s: [arm] armPublicationBarrier: function armPublicationBarrier missing Go declaration -runtime/asm_arm.s: [arm] cmpbody: function cmpbody missing Go declaration runtime/asm_arm.s: [arm] usplitR0: function usplitR0 missing Go declaration runtime/asm_arm.s: [arm] addmoduledata: function addmoduledata missing Go declaration runtime/duff_arm.s: [arm] duffzero: function duffzero missing Go declaration diff --git a/libgo/go/cmd/vet/all/whitelist/arm64.txt b/libgo/go/cmd/vet/all/whitelist/arm64.txt index 24fc6f4..ee0292b 100644 --- a/libgo/go/cmd/vet/all/whitelist/arm64.txt +++ b/libgo/go/cmd/vet/all/whitelist/arm64.txt @@ -1,9 +1,9 @@ // arm64-specific vet whitelist. See readme.txt for details. -runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: Compare is in package bytes +internal/bytealg/compare_arm64.s: [arm64] cannot check cross-package assembly function: Compare is in package bytes +internal/bytealg/compare_arm64.s: [arm64] cannot check cross-package assembly function: cmpstring is in package runtime // Intentionally missing declarations. -runtime/asm_arm64.s: [arm64] abort: function abort missing Go declaration runtime/asm_arm64.s: [arm64] addmoduledata: function addmoduledata missing Go declaration runtime/duff_arm64.s: [arm64] duffzero: function duffzero missing Go declaration runtime/duff_arm64.s: [arm64] duffcopy: function duffcopy missing Go declaration diff --git a/libgo/go/cmd/vet/all/whitelist/darwin_386.txt b/libgo/go/cmd/vet/all/whitelist/darwin_386.txt index d19d7d7..5c25e09 100644 --- a/libgo/go/cmd/vet/all/whitelist/darwin_386.txt +++ b/libgo/go/cmd/vet/all/whitelist/darwin_386.txt @@ -2,8 +2,4 @@ // Ok -runtime/sys_darwin_386.s: [386] now: function now missing Go declaration -runtime/sys_darwin_386.s: [386] bsdthread_start: function bsdthread_start missing Go declaration -runtime/sys_darwin_386.s: [386] sysenter: function sysenter missing Go declaration runtime/sys_darwin_386.s: [386] setldt: function setldt missing Go declaration -runtime/sys_darwin_386.s: [386] cannot check cross-package assembly function: now is in package time diff --git a/libgo/go/cmd/vet/all/whitelist/darwin_amd64.txt b/libgo/go/cmd/vet/all/whitelist/darwin_amd64.txt index 94a4e8f..fcdacb2 100644 --- a/libgo/go/cmd/vet/all/whitelist/darwin_amd64.txt +++ b/libgo/go/cmd/vet/all/whitelist/darwin_amd64.txt @@ -1,5 +1,3 @@ // darwin/amd64-specific vet whitelist. See readme.txt for details. -runtime/sys_darwin_amd64.s: [amd64] bsdthread_start: function bsdthread_start missing Go declaration runtime/sys_darwin_amd64.s: [amd64] settls: function settls missing Go declaration -runtime/sys_darwin_amd64.s: [amd64] cannot check cross-package assembly function: now is in package time diff --git a/libgo/go/cmd/vet/all/whitelist/darwin_arm.txt b/libgo/go/cmd/vet/all/whitelist/darwin_arm.txt index 0e619be..1c25c6a 100644 --- a/libgo/go/cmd/vet/all/whitelist/darwin_arm.txt +++ b/libgo/go/cmd/vet/all/whitelist/darwin_arm.txt @@ -1,12 +1,5 @@ // darwin/arm-specific vet whitelist. See readme.txt for details. -// False positives due to comments in assembly. -// To be removed. See CL 27154. - -runtime/sys_darwin_arm.s: [arm] sigfwd: use of unnamed argument 0(FP); offset 0 is fn+0(FP) - - // Ok. -runtime/sys_darwin_arm.s: [arm] bsdthread_start: function bsdthread_start missing Go declaration runtime/asm_arm.s: [arm] sigreturn: function sigreturn missing Go declaration diff --git a/libgo/go/cmd/vet/all/whitelist/darwin_arm64.txt b/libgo/go/cmd/vet/all/whitelist/darwin_arm64.txt index 793cccf..a1edb71 100644 --- a/libgo/go/cmd/vet/all/whitelist/darwin_arm64.txt +++ b/libgo/go/cmd/vet/all/whitelist/darwin_arm64.txt @@ -1,8 +1,3 @@ // darwin/arm64-specific vet whitelist. See readme.txt for details. -runtime/sys_darwin_arm64.s: [arm64] sigtramp: 24(RSP) should be infostyle+8(FP) -runtime/sys_darwin_arm64.s: [arm64] sigtramp: 24(RSP) should be infostyle+8(FP) -runtime/sys_darwin_arm64.s: [arm64] bsdthread_create: RET without writing to 4-byte ret+24(FP) -runtime/sys_darwin_arm64.s: [arm64] bsdthread_start: function bsdthread_start missing Go declaration -runtime/sys_darwin_arm64.s: [arm64] bsdthread_register: RET without writing to 4-byte ret+0(FP) runtime/asm_arm64.s: [arm64] sigreturn: function sigreturn missing Go declaration diff --git a/libgo/go/cmd/vet/all/whitelist/linux_ppc64x.txt b/libgo/go/cmd/vet/all/whitelist/linux_ppc64x.txt index 21e87e3..0091d97 100644 --- a/libgo/go/cmd/vet/all/whitelist/linux_ppc64x.txt +++ b/libgo/go/cmd/vet/all/whitelist/linux_ppc64x.txt @@ -2,4 +2,3 @@ runtime/sys_linux_ppc64x.s: [GOARCH] _sigtramp: function _sigtramp missing Go declaration runtime/sys_linux_ppc64x.s: [GOARCH] _cgoSigtramp: function _cgoSigtramp missing Go declaration -runtime/asm_ppc64x.s: [GOARCH] procyield: use of 24(R1) points beyond argument frame diff --git a/libgo/go/cmd/vet/all/whitelist/mips.txt b/libgo/go/cmd/vet/all/whitelist/mips.txt index ad29336..fa17c62 100644 --- a/libgo/go/cmd/vet/all/whitelist/mips.txt +++ b/libgo/go/cmd/vet/all/whitelist/mips.txt @@ -1,4 +1,4 @@ -// mips64-specific vet whitelist. See readme.txt for details. +// mips-specific (big endian) vet whitelist. See readme.txt for details. // Work around if-def'd code. Will be fixed by golang.org/issue/17544. runtime/sys_linux_mipsx.s: [mips] walltime: invalid offset sec_lo+0(FP); expected sec_lo+4(FP) diff --git a/libgo/go/cmd/vet/all/whitelist/mips64x.txt b/libgo/go/cmd/vet/all/whitelist/mips64x.txt index 5354d21..1687765 100644 --- a/libgo/go/cmd/vet/all/whitelist/mips64x.txt +++ b/libgo/go/cmd/vet/all/whitelist/mips64x.txt @@ -1,6 +1,5 @@ // mips64-specific vet whitelist. See readme.txt for details. -runtime/asm_mips64x.s: [GOARCH] abort: function abort missing Go declaration runtime/duff_mips64x.s: [GOARCH] duffzero: function duffzero missing Go declaration runtime/tls_mips64x.s: [GOARCH] save_g: function save_g missing Go declaration runtime/tls_mips64x.s: [GOARCH] load_g: function load_g missing Go declaration diff --git a/libgo/go/cmd/vet/all/whitelist/mipsle.txt b/libgo/go/cmd/vet/all/whitelist/mipsle.txt index 9292169..9361dc4 100644 --- a/libgo/go/cmd/vet/all/whitelist/mipsle.txt +++ b/libgo/go/cmd/vet/all/whitelist/mipsle.txt @@ -1,4 +1,4 @@ -// mips64-specific vet whitelist. See readme.txt for details. +// mipsle-specific vet whitelist. See readme.txt for details. // Work around if-def'd code. Will be fixed by golang.org/issue/17544. runtime/sys_linux_mipsx.s: [mipsle] walltime: invalid offset sec_lo+4(FP); expected sec_lo+0(FP) diff --git a/libgo/go/cmd/vet/all/whitelist/mipsx.txt b/libgo/go/cmd/vet/all/whitelist/mipsx.txt index 860f839..1a2cd3f 100644 --- a/libgo/go/cmd/vet/all/whitelist/mipsx.txt +++ b/libgo/go/cmd/vet/all/whitelist/mipsx.txt @@ -1,9 +1,10 @@ -// mips64-specific vet whitelist. See readme.txt for details. +// mips/mipsle-specific vet whitelist. See readme.txt for details. + +internal/bytealg/compare_mipsx.s: [GOARCH] cannot check cross-package assembly function: Compare is in package bytes +internal/bytealg/compare_mipsx.s: [GOARCH] cannot check cross-package assembly function: cmpstring is in package runtime -runtime/asm_mipsx.s: [GOARCH] abort: function abort missing Go declaration runtime/tls_mipsx.s: [GOARCH] save_g: function save_g missing Go declaration runtime/tls_mipsx.s: [GOARCH] load_g: function load_g missing Go declaration -runtime/asm_mipsx.s: [GOARCH] cannot check cross-package assembly function: Compare is in package bytes runtime/sys_linux_mipsx.s: [GOARCH] clone: 12(R29) should be mp+8(FP) runtime/sys_linux_mipsx.s: [GOARCH] clone: 4(R29) should be flags+0(FP) runtime/sys_linux_mipsx.s: [GOARCH] clone: 8(R29) should be stk+4(FP) diff --git a/libgo/go/cmd/vet/all/whitelist/nacl_amd64p32.txt b/libgo/go/cmd/vet/all/whitelist/nacl_amd64p32.txt index 4b2aad2..1ec11f7 100644 --- a/libgo/go/cmd/vet/all/whitelist/nacl_amd64p32.txt +++ b/libgo/go/cmd/vet/all/whitelist/nacl_amd64p32.txt @@ -1,5 +1,8 @@ // nacl/amd64p32-specific vet whitelist. See readme.txt for details. +internal/bytealg/compare_amd64p32.s: [amd64p32] cannot check cross-package assembly function: Compare is in package bytes +internal/bytealg/compare_amd64p32.s: [amd64p32] cannot check cross-package assembly function: cmpstring is in package runtime + // reflect trampolines intentionally omit arg size. Same for morestack. runtime/asm_amd64p32.s: [amd64p32] morestack: use of 8(SP) points beyond argument frame runtime/asm_amd64p32.s: [amd64p32] morestack: use of 16(SP) points beyond argument frame @@ -20,10 +23,4 @@ runtime/sys_nacl_amd64p32.s: [amd64p32] settls: function settls missing Go decla runtime/asm_amd64p32.s: [amd64p32] rt0_go: unknown variable argc runtime/asm_amd64p32.s: [amd64p32] rt0_go: unknown variable argv -runtime/asm_amd64p32.s: [amd64p32] memeqbody: function memeqbody missing Go declaration -runtime/asm_amd64p32.s: [amd64p32] cannot check cross-package assembly function: Compare is in package bytes -runtime/asm_amd64p32.s: [amd64p32] cmpbody: function cmpbody missing Go declaration -runtime/asm_amd64p32.s: [amd64p32] indexbytebody: function indexbytebody missing Go declaration runtime/asm_amd64p32.s: [amd64p32] asmcgocall: RET without writing to 4-byte ret+8(FP) - -runtime/asm_amd64p32.s: [amd64p32] stackcheck: function stackcheck missing Go declaration diff --git a/libgo/go/cmd/vet/all/whitelist/ppc64x.txt b/libgo/go/cmd/vet/all/whitelist/ppc64x.txt index 4f6444e..65a904e 100644 --- a/libgo/go/cmd/vet/all/whitelist/ppc64x.txt +++ b/libgo/go/cmd/vet/all/whitelist/ppc64x.txt @@ -1,10 +1,9 @@ // ppc64-specific vet whitelist. See readme.txt for details. -runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: Compare is in package bytes +internal/bytealg/compare_ppc64x.s: [GOARCH] cannot check cross-package assembly function: Compare is in package bytes +internal/bytealg/compare_ppc64x.s: [GOARCH] cannot check cross-package assembly function: cmpstring is in package runtime runtime/asm_ppc64x.s: [GOARCH] reginit: function reginit missing Go declaration -runtime/asm_ppc64x.s: [GOARCH] abort: function abort missing Go declaration -runtime/asm_ppc64x.s: [GOARCH] memeqbody: function memeqbody missing Go declaration runtime/asm_ppc64x.s: [GOARCH] goexit: use of 24(R1) points beyond argument frame runtime/asm_ppc64x.s: [GOARCH] addmoduledata: function addmoduledata missing Go declaration runtime/duff_ppc64x.s: [GOARCH] duffzero: function duffzero missing Go declaration diff --git a/libgo/go/cmd/vet/all/whitelist/s390x.txt b/libgo/go/cmd/vet/all/whitelist/s390x.txt index f18236c..5bc48e5 100644 --- a/libgo/go/cmd/vet/all/whitelist/s390x.txt +++ b/libgo/go/cmd/vet/all/whitelist/s390x.txt @@ -1,17 +1,14 @@ -runtime/asm_s390x.s: [s390x] abort: function abort missing Go declaration -runtime/asm_s390x.s: [s390x] memeqbody: function memeqbody missing Go declaration -runtime/asm_s390x.s: [s390x] memeqbodyclc: function memeqbodyclc missing Go declaration -runtime/asm_s390x.s: [s390x] indexbytebody: function indexbytebody missing Go declaration -runtime/asm_s390x.s: [s390x] cannot check cross-package assembly function: Compare is in package bytes -runtime/asm_s390x.s: [s390x] cmpbody: function cmpbody missing Go declaration -runtime/asm_s390x.s: [s390x] cmpbodyclc: function cmpbodyclc missing Go declaration -runtime/asm_s390x.s: [s390x] cannot check cross-package assembly function: supportsVX is in package strings -runtime/asm_s390x.s: [s390x] cannot check cross-package assembly function: supportsVX is in package bytes -runtime/asm_s390x.s: [s390x] cannot check cross-package assembly function: indexShortStr is in package strings -runtime/asm_s390x.s: [s390x] cannot check cross-package assembly function: indexShortStr is in package bytes -runtime/asm_s390x.s: [s390x] indexShortStr: function indexShortStr missing Go declaration +internal/bytealg/compare_s390x.s: [s390x] cannot check cross-package assembly function: Compare is in package bytes +internal/bytealg/compare_s390x.s: [s390x] cannot check cross-package assembly function: cmpstring is in package runtime runtime/asm_s390x.s: [s390x] addmoduledata: function addmoduledata missing Go declaration runtime/memclr_s390x.s: [s390x] memclr_s390x_exrl_xc: function memclr_s390x_exrl_xc missing Go declaration runtime/memmove_s390x.s: [s390x] memmove_s390x_exrl_mvc: function memmove_s390x_exrl_mvc missing Go declaration runtime/tls_s390x.s: [s390x] save_g: function save_g missing Go declaration runtime/tls_s390x.s: [s390x] load_g: function load_g missing Go declaration +internal/cpu/cpu_s390x.s: [s390x] stfle: invalid MOVD of ret+0(FP); cpu.facilityList is 32-byte value +internal/cpu/cpu_s390x.s: [s390x] kmQuery: invalid MOVD of ret+0(FP); cpu.queryResult is 16-byte value +internal/cpu/cpu_s390x.s: [s390x] kmcQuery: invalid MOVD of ret+0(FP); cpu.queryResult is 16-byte value +internal/cpu/cpu_s390x.s: [s390x] kmctrQuery: invalid MOVD of ret+0(FP); cpu.queryResult is 16-byte value +internal/cpu/cpu_s390x.s: [s390x] kmaQuery: invalid MOVD of ret+0(FP); cpu.queryResult is 16-byte value +internal/cpu/cpu_s390x.s: [s390x] kimdQuery: invalid MOVD of ret+0(FP); cpu.queryResult is 16-byte value +internal/cpu/cpu_s390x.s: [s390x] klmdQuery: invalid MOVD of ret+0(FP); cpu.queryResult is 16-byte value diff --git a/libgo/go/cmd/vet/all/whitelist/wasm.txt b/libgo/go/cmd/vet/all/whitelist/wasm.txt new file mode 100644 index 0000000..7a8037f --- /dev/null +++ b/libgo/go/cmd/vet/all/whitelist/wasm.txt @@ -0,0 +1,28 @@ +// wasm-specific vet whitelist. See readme.txt for details. + +// False positives. + +// Nothing much to do about cross-package assembly. Unfortunate. +internal/bytealg/compare_wasm.s: [wasm] cannot check cross-package assembly function: Compare is in package bytes +internal/bytealg/compare_wasm.s: [wasm] cannot check cross-package assembly function: cmpstring is in package runtime + +// morestack intentionally omits arg size. +runtime/asm_wasm.s: [wasm] morestack: use of 8(SP) points beyond argument frame +runtime/asm_wasm.s: [wasm] morestack: use of 16(SP) points beyond argument frame +runtime/asm_wasm.s: [wasm] morestack: use of 8(SP) points beyond argument frame + +// rt0_go does not allocate a stack frame. +runtime/asm_wasm.s: [wasm] rt0_go: use of 8(SP) points beyond argument frame + +// Calling WebAssembly import. No write from Go assembly. +runtime/sys_wasm.s: [wasm] nanotime: RET without writing to 8-byte ret+0(FP) +runtime/sys_wasm.s: [wasm] scheduleCallback: RET without writing to 4-byte ret+8(FP) +syscall/js/js_js.s: [wasm] stringVal: RET without writing to 8-byte ret+16(FP) +syscall/js/js_js.s: [wasm] valueGet: RET without writing to 8-byte ret+24(FP) +syscall/js/js_js.s: [wasm] valueIndex: RET without writing to 8-byte ret+16(FP) +syscall/js/js_js.s: [wasm] valueCall: RET without writing to 8-byte ret+48(FP) +syscall/js/js_js.s: [wasm] valueInvoke: RET without writing to 8-byte ret+32(FP) +syscall/js/js_js.s: [wasm] valueNew: RET without writing to 8-byte ret+32(FP) +syscall/js/js_js.s: [wasm] valueLength: RET without writing to 8-byte ret+8(FP) +syscall/js/js_js.s: [wasm] valuePrepareString: RET without writing to 8-byte ret+8(FP) +syscall/js/js_js.s: [wasm] valueInstanceOf: RET without writing to 1-byte ret+16(FP) diff --git a/libgo/go/cmd/vet/asmdecl.go b/libgo/go/cmd/vet/asmdecl.go index b01d23d..ccf6269 100644 --- a/libgo/go/cmd/vet/asmdecl.go +++ b/libgo/go/cmd/vet/asmdecl.go @@ -77,6 +77,7 @@ var ( asmArchPpc64 = asmArch{name: "ppc64", bigEndian: true, stack: "R1", lr: true} asmArchPpc64LE = asmArch{name: "ppc64le", bigEndian: false, stack: "R1", lr: true} asmArchS390X = asmArch{name: "s390x", bigEndian: true, stack: "R15", lr: true} + asmArchWasm = asmArch{name: "wasm", bigEndian: false, stack: "SP", lr: false} arches = []*asmArch{ &asmArch386, @@ -91,6 +92,7 @@ var ( &asmArchPpc64, &asmArchPpc64LE, &asmArchS390X, + &asmArchWasm, } ) @@ -104,6 +106,8 @@ func init() { arch.ptrSize = int(arch.sizes.Sizeof(types.Typ[types.UnsafePointer])) arch.maxAlign = int(arch.sizes.Alignof(types.Typ[types.Int64])) } + + registerPkgCheck("asmdecl", asmCheck) } var ( @@ -119,7 +123,7 @@ var ( ) func asmCheck(pkg *Package) { - if !vet("asmdecl") { + if vcfg.VetxOnly { return } @@ -240,17 +244,17 @@ Files: continue } } + flag := m[3] fn = knownFunc[fnName][arch] if fn != nil { size, _ := strconv.Atoi(m[5]) - flag := m[3] if size != fn.size && (flag != "7" && !strings.Contains(flag, "NOSPLIT") || size != 0) { badf("wrong argument size %d; expected $...-%d", size, fn.size) } } localSize, _ = strconv.Atoi(m[4]) localSize += archDef.intSize - if archDef.lr { + if archDef.lr && !strings.Contains(flag, "NOFRAME") { // Account for caller's saved LR localSize += archDef.intSize } diff --git a/libgo/go/cmd/vet/assign.go b/libgo/go/cmd/vet/assign.go index bfa5b30..223e80d 100644 --- a/libgo/go/cmd/vet/assign.go +++ b/libgo/go/cmd/vet/assign.go @@ -37,7 +37,7 @@ func checkAssignStmt(f *File, node ast.Node) { } for i, lhs := range stmt.Lhs { rhs := stmt.Rhs[i] - if hasSideEffects(lhs) || hasSideEffects(rhs) { + if hasSideEffects(f, lhs) || hasSideEffects(f, rhs) { continue // expressions may not be equal } if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) { diff --git a/libgo/go/cmd/vet/atomic.go b/libgo/go/cmd/vet/atomic.go index b2ca2d8..b425669 100644 --- a/libgo/go/cmd/vet/atomic.go +++ b/libgo/go/cmd/vet/atomic.go @@ -7,6 +7,7 @@ package main import ( "go/ast" "go/token" + "go/types" ) func init() { @@ -36,8 +37,9 @@ func checkAtomicAssignment(f *File, node ast.Node) { if !ok { continue } - pkg, ok := sel.X.(*ast.Ident) - if !ok || pkg.Name != "atomic" { + pkgIdent, _ := sel.X.(*ast.Ident) + pkgName, ok := f.pkg.uses[pkgIdent].(*types.PkgName) + if !ok || pkgName.Imported().Path() != "sync/atomic" { continue } diff --git a/libgo/go/cmd/vet/bool.go b/libgo/go/cmd/vet/bool.go index 07c2a93..1cd477f 100644 --- a/libgo/go/cmd/vet/bool.go +++ b/libgo/go/cmd/vet/bool.go @@ -31,7 +31,7 @@ func checkBool(f *File, n ast.Node) { return } - comm := op.commutativeSets(e) + comm := op.commutativeSets(f, e) for _, exprs := range comm { op.checkRedundant(f, exprs) op.checkSuspect(f, exprs) @@ -53,14 +53,14 @@ var ( // expressions in e that are connected by op. // For example, given 'a || b || f() || c || d' with the or op, // commutativeSets returns {{b, a}, {d, c}}. -func (op boolOp) commutativeSets(e *ast.BinaryExpr) [][]ast.Expr { +func (op boolOp) commutativeSets(f *File, e *ast.BinaryExpr) [][]ast.Expr { exprs := op.split(e) // Partition the slice of expressions into commutative sets. i := 0 var sets [][]ast.Expr for j := 0; j <= len(exprs); j++ { - if j == len(exprs) || hasSideEffects(exprs[j]) { + if j == len(exprs) || hasSideEffects(f, exprs[j]) { if i < j { sets = append(sets, exprs[i:j]) } @@ -136,16 +136,27 @@ func (op boolOp) checkSuspect(f *File, exprs []ast.Expr) { } // hasSideEffects reports whether evaluation of e has side effects. -func hasSideEffects(e ast.Expr) bool { +func hasSideEffects(f *File, e ast.Expr) bool { safe := true ast.Inspect(e, func(node ast.Node) bool { switch n := node.(type) { - // Using CallExpr here will catch conversions - // as well as function and method invocations. - // We'll live with the false negatives for now. case *ast.CallExpr: - safe = false - return false + typVal := f.pkg.types[n.Fun] + switch { + case typVal.IsType(): + // Type conversion, which is safe. + case typVal.IsBuiltin(): + // Builtin func, conservatively assumed to not + // be safe for now. + safe = false + return false + default: + // A non-builtin func or method call. + // Conservatively assume that all of them have + // side effects for now. + safe = false + return false + } case *ast.UnaryExpr: if n.Op == token.ARROW { safe = false diff --git a/libgo/go/cmd/vet/buildtag.go b/libgo/go/cmd/vet/buildtag.go index 80d8f81..ba3a361 100644 --- a/libgo/go/cmd/vet/buildtag.go +++ b/libgo/go/cmd/vet/buildtag.go @@ -18,12 +18,39 @@ var ( plusBuild = []byte("+build") ) +func badfLine(f *File, line int, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + fmt.Fprintf(os.Stderr, "%s:%d: %s\n", f.name, line, msg) + setExit(1) +} + // checkBuildTag checks that build tags are in the correct location and well-formed. -func checkBuildTag(name string, data []byte) { +func checkBuildTag(f *File) { if !vet("buildtags") { return } - lines := bytes.SplitAfter(data, nl) + + // we must look at the raw lines, as build tags may appear in non-Go + // files such as assembly files. + lines := bytes.SplitAfter(f.content, nl) + + // lineWithComment reports whether a line corresponds to a comment in + // the source file. If the source file wasn't Go, the function always + // returns true. + lineWithComment := func(line int) bool { + if f.file == nil { + // Current source file is not Go, so be conservative. + return true + } + for _, group := range f.file.Comments { + startLine := f.fset.Position(group.Pos()).Line + endLine := f.fset.Position(group.End()).Line + if startLine <= line && line <= endLine { + return true + } + } + return false + } // Determine cutpoint where +build comments are no longer valid. // They are valid in leading // comments in the file followed by @@ -46,18 +73,29 @@ func checkBuildTag(name string, data []byte) { if !bytes.HasPrefix(line, slashSlash) { continue } + if !bytes.Contains(line, plusBuild) { + // Check that the comment contains "+build" early, to + // avoid unnecessary lineWithComment calls that may + // incur linear searches. + continue + } + if !lineWithComment(i + 1) { + // This is a line in a Go source file that looks like a + // comment, but actually isn't - such as part of a raw + // string. + continue + } + text := bytes.TrimSpace(line[2:]) if bytes.HasPrefix(text, plusBuild) { fields := bytes.Fields(text) if !bytes.Equal(fields[0], plusBuild) { // Comment is something like +buildasdf not +build. - fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1) - setExit(1) + badfLine(f, i+1, "possible malformed +build comment") continue } if i >= cutoff { - fmt.Fprintf(os.Stderr, "%s:%d: +build comment must appear before package clause and be followed by a blank line\n", name, i+1) - setExit(1) + badfLine(f, i+1, "+build comment must appear before package clause and be followed by a blank line") continue } // Check arguments. @@ -65,15 +103,13 @@ func checkBuildTag(name string, data []byte) { for _, arg := range fields[1:] { for _, elem := range strings.Split(string(arg), ",") { if strings.HasPrefix(elem, "!!") { - fmt.Fprintf(os.Stderr, "%s:%d: invalid double negative in build constraint: %s\n", name, i+1, arg) - setExit(1) + badfLine(f, i+1, "invalid double negative in build constraint: %s", arg) break Args } elem = strings.TrimPrefix(elem, "!") for _, c := range elem { if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { - fmt.Fprintf(os.Stderr, "%s:%d: invalid non-alphanumeric build constraint: %s\n", name, i+1, arg) - setExit(1) + badfLine(f, i+1, "invalid non-alphanumeric build constraint: %s", arg) break Args } } @@ -82,9 +118,8 @@ func checkBuildTag(name string, data []byte) { continue } // Comment with +build but not at beginning. - if bytes.Contains(line, plusBuild) && i < cutoff { - fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1) - setExit(1) + if i < cutoff { + badfLine(f, i+1, "possible malformed +build comment") continue } } diff --git a/libgo/go/cmd/vet/composite.go b/libgo/go/cmd/vet/composite.go index f704f18..861e040 100644 --- a/libgo/go/cmd/vet/composite.go +++ b/libgo/go/cmd/vet/composite.go @@ -18,7 +18,7 @@ var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite wh func init() { register("composites", - "check that composite literals used field-keyed elements", + "check that composite literals of types from imported packages use field-keyed elements", checkUnkeyedLiteral, compositeLit) } @@ -38,11 +38,19 @@ func checkUnkeyedLiteral(f *File, node ast.Node) { // skip whitelisted types return } - if _, ok := typ.Underlying().(*types.Struct); !ok { + under := typ.Underlying() + for { + ptr, ok := under.(*types.Pointer) + if !ok { + break + } + under = ptr.Elem().Underlying() + } + if _, ok := under.(*types.Struct); !ok { // skip non-struct composite literals return } - if isLocalType(f, typeName) { + if isLocalType(f, typ) { // allow unkeyed locally defined composite literal return } @@ -63,20 +71,16 @@ func checkUnkeyedLiteral(f *File, node ast.Node) { f.Badf(cl.Pos(), "%s composite literal uses unkeyed fields", typeName) } -func isLocalType(f *File, typeName string) bool { - if strings.HasPrefix(typeName, "struct{") { +func isLocalType(f *File, typ types.Type) bool { + switch x := typ.(type) { + case *types.Struct: // struct literals are local types return true + case *types.Pointer: + return isLocalType(f, x.Elem()) + case *types.Named: + // names in package foo are local to foo_test too + return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(f.pkg.path, "_test") } - - pkgname := f.pkg.path - if strings.HasPrefix(typeName, pkgname+".") { - return true - } - - // treat types as local inside test packages with _test name suffix - if strings.HasSuffix(pkgname, "_test") { - pkgname = pkgname[:len(pkgname)-len("_test")] - } - return strings.HasPrefix(typeName, pkgname+".") + return false } diff --git a/libgo/go/cmd/vet/copylock.go b/libgo/go/cmd/vet/copylock.go index ce14e1a..ed88ca8 100644 --- a/libgo/go/cmd/vet/copylock.go +++ b/libgo/go/cmd/vet/copylock.go @@ -234,13 +234,11 @@ func lockPath(tpkg *types.Package, typ types.Type) typePath { return nil } - // We're looking for cases in which a reference to this type - // can be locked, but a value cannot. This differentiates + // We're looking for cases in which a pointer to this type + // is a sync.Locker, but a value is not. This differentiates // embedded interfaces from embedded values. - if plock := types.NewMethodSet(types.NewPointer(typ)).Lookup(tpkg, "Lock"); plock != nil { - if lock := types.NewMethodSet(typ).Lookup(tpkg, "Lock"); lock == nil { - return []types.Type{typ} - } + if types.Implements(types.NewPointer(typ), lockerType) && !types.Implements(typ, lockerType) { + return []types.Type{typ} } nfields := styp.NumFields() @@ -254,3 +252,15 @@ func lockPath(tpkg *types.Package, typ types.Type) typePath { return nil } + +var lockerType *types.Interface + +// Construct a sync.Locker interface type. +func init() { + nullary := types.NewSignature(nil, nil, nil, false) // func() + methods := []*types.Func{ + types.NewFunc(token.NoPos, nil, "Lock", nullary), + types.NewFunc(token.NoPos, nil, "Unlock", nullary), + } + lockerType = types.NewInterface(methods, nil).Complete() +} diff --git a/libgo/go/cmd/vet/dead.go b/libgo/go/cmd/vet/dead.go index 130f619..0facec5 100644 --- a/libgo/go/cmd/vet/dead.go +++ b/libgo/go/cmd/vet/dead.go @@ -45,7 +45,7 @@ func (f *File) updateDead(node ast.Node) { } for _, expr := range cc.List { v := f.pkg.types[expr].Value - if v == nil || constant.BoolVal(v) { + if v == nil || v.Kind() != constant.Bool || constant.BoolVal(v) { continue BodyLoopBool } } diff --git a/libgo/go/cmd/vet/doc.go b/libgo/go/cmd/vet/doc.go index 3df975c..d9af0a8 100644 --- a/libgo/go/cmd/vet/doc.go +++ b/libgo/go/cmd/vet/doc.go @@ -119,22 +119,17 @@ Printf family Flag: -printf -Suspicious calls to functions in the Printf family, including any functions -with these names, disregarding case: - Print Printf Println - Fprint Fprintf Fprintln - Sprint Sprintf Sprintln - Error Errorf - Fatal Fatalf - Log Logf - Panic Panicf Panicln -The -printfuncs flag can be used to redefine this list. -If the function name ends with an 'f', the function is assumed to take -a format descriptor string in the manner of fmt.Printf. If not, vet -complains about arguments that look like format descriptor strings. - -It also checks for errors such as using a Writer as the first argument of -Printf. +Suspicious calls to fmt.Print, fmt.Printf, and related functions. +The check applies to known functions (for example, those in package fmt) +as well as any detected wrappers of known functions. + +The -printfuncs flag specifies a comma-separated list of names of +additional known formatting functions. Each name can be of the form +pkg.Name or pkg.Type.Name, where pkg is a complete import path, +or else can be a case-insensitive unqualified identifier like "errorf". +If a listed name ends in f, the function is assumed to be Printf-like, +taking a format string before the argument list. Otherwise it is +assumed to be Print-like, taking a list of arguments with no format string. Range loop variables diff --git a/libgo/go/cmd/vet/main.go b/libgo/go/cmd/vet/main.go index 49c1d32..c50d488 100644 --- a/libgo/go/cmd/vet/main.go +++ b/libgo/go/cmd/vet/main.go @@ -4,10 +4,12 @@ // Vet is a simple checker for static errors in Go source code. // See doc.go for more information. + package main import ( "bytes" + "encoding/gob" "encoding/json" "flag" "fmt" @@ -22,8 +24,11 @@ import ( "io/ioutil" "os" "path/filepath" + "sort" "strconv" "strings" + + "cmd/internal/objabi" ) // Important! If you add flags here, make sure to update cmd/go/internal/vet/vetflag.go. @@ -154,9 +159,31 @@ var ( // checkers is a two-level map. // The outer level is keyed by a nil pointer, one of the AST vars above. // The inner level is keyed by checker name. - checkers = make(map[ast.Node]map[string]func(*File, ast.Node)) + checkers = make(map[ast.Node]map[string]func(*File, ast.Node)) + pkgCheckers = make(map[string]func(*Package)) + exporters = make(map[string]func() interface{}) ) +// The exporters data as written to the vetx output file. +type vetxExport struct { + Name string + Data interface{} +} + +// Vet can provide its own "export information" +// about package A to future invocations of vet +// on packages importing A. If B imports A, +// then running "go vet B" actually invokes vet twice: +// first, it runs vet on A, in "vetx-only" mode, which +// skips most checks and only computes export data +// describing A. Then it runs vet on B, making A's vetx +// data available for consultation. The vet of B +// computes vetx data for B in addition to its +// usual vet checks. + +// register registers the named check function, +// to be called with AST nodes of the given types. +// The registered functions are not called in vetx-only mode. func register(name, usage string, fn func(*File, ast.Node), types ...ast.Node) { report[name] = triStateFlag(name, unset, usage) for _, typ := range types { @@ -169,6 +196,25 @@ func register(name, usage string, fn func(*File, ast.Node), types ...ast.Node) { } } +// registerPkgCheck registers a package-level checking function, +// to be invoked with the whole package being vetted +// before any of the per-node handlers. +// The registered function fn is called even in vetx-only mode +// (see comment above), so fn must take care not to report +// errors when vcfg.VetxOnly is true. +func registerPkgCheck(name string, fn func(*Package)) { + pkgCheckers[name] = fn +} + +// registerExport registers a function to return vetx export data +// that should be saved and provided to future invocations of vet +// when checking packages importing this one. +// The value returned by fn should be nil or else valid to encode using gob. +// Typically a registerExport call is paired with a call to gob.Register. +func registerExport(name string, fn func() interface{}) { + exporters[name] = fn +} + // Usage is a replacement usage function for the flags package. func Usage() { fmt.Fprintf(os.Stderr, "Usage of vet:\n") @@ -195,9 +241,11 @@ type File struct { // Parsed package "foo" when checking package "foo_test" basePkg *Package - // The objects that are receivers of a "String() string" method. + // The keys are the objects that are receivers of a "String() + // string" method. The value reports whether the method has a + // pointer receiver. // This is used by the recursiveStringer method in print.go. - stringers map[*ast.Object]bool + stringerPtrs map[*ast.Object]bool // Registered checkers to run. checkers map[ast.Node][]func(*File, ast.Node) @@ -207,6 +255,7 @@ type File struct { } func main() { + objabi.AddVersionFlag() flag.Usage = Usage flag.Parse() @@ -293,6 +342,9 @@ type vetConfig struct { ImportMap map[string]string PackageFile map[string]string Standard map[string]bool + PackageVetx map[string]string // map from import path to vetx data file + VetxOnly bool // only compute vetx output; don't run ordinary checks + VetxOutput string // file where vetx output should be written SucceedOnTypecheckFailure bool @@ -353,6 +405,28 @@ func doPackageCfg(cfgFile string) { inittypes() mustTypecheck = true doPackage(vcfg.GoFiles, nil) + if vcfg.VetxOutput != "" { + out := make([]vetxExport, 0, len(exporters)) + for name, fn := range exporters { + out = append(out, vetxExport{ + Name: name, + Data: fn(), + }) + } + // Sort the data so that it is consistent across builds. + sort.Slice(out, func(i, j int) bool { + return out[i].Name < out[j].Name + }) + var buf bytes.Buffer + if err := gob.NewEncoder(&buf).Encode(out); err != nil { + errorf("encoding vet output: %v", err) + return + } + if err := ioutil.WriteFile(vcfg.VetxOutput, buf.Bytes(), 0666); err != nil { + errorf("saving vet output: %v", err) + return + } + } } // doPackageDir analyzes the single package found in the directory, if there is one, @@ -413,23 +487,23 @@ func doPackage(names []string, basePkg *Package) *Package { warnf("%s: %s", name, err) return nil } - checkBuildTag(name, data) var parsedFile *ast.File if strings.HasSuffix(name, ".go") { - parsedFile, err = parser.ParseFile(fs, name, data, 0) + parsedFile, err = parser.ParseFile(fs, name, data, parser.ParseComments) if err != nil { warnf("%s: %s", name, err) return nil } astFiles = append(astFiles, parsedFile) } - files = append(files, &File{ + file := &File{ fset: fs, content: data, name: name, file: parsedFile, dead: make(map[ast.Node]bool), - }) + } + files = append(files, file) } if len(astFiles) == 0 { return nil @@ -458,6 +532,19 @@ func doPackage(names []string, basePkg *Package) *Package { } // Check. + for _, file := range files { + file.pkg = pkg + file.basePkg = basePkg + } + for name, fn := range pkgCheckers { + if vet(name) { + fn(pkg) + } + } + if vcfg.VetxOnly { + return pkg + } + chk := make(map[ast.Node][]func(*File, ast.Node)) for typ, set := range checkers { for name, fn := range set { @@ -467,14 +554,12 @@ func doPackage(names []string, basePkg *Package) *Package { } } for _, file := range files { - file.pkg = pkg - file.basePkg = basePkg + checkBuildTag(file) file.checkers = chk if file.file != nil { file.walkFile(file.name, file.file) } } - asmCheck(pkg) return pkg } @@ -627,3 +712,39 @@ func (f *File) gofmt(x ast.Expr) string { printer.Fprint(&f.b, f.fset, x) return f.b.String() } + +// imported[path][key] is previously written export data. +var imported = make(map[string]map[string]interface{}) + +// readVetx reads export data written by a previous +// invocation of vet on an imported package (path). +// The key is the name passed to registerExport +// when the data was originally generated. +// readVetx returns nil if the data is unavailable. +func readVetx(path, key string) interface{} { + if path == "unsafe" || vcfg.ImportPath == "" { + return nil + } + m := imported[path] + if m == nil { + file := vcfg.PackageVetx[path] + if file == "" { + return nil + } + data, err := ioutil.ReadFile(file) + if err != nil { + return nil + } + var out []vetxExport + err = gob.NewDecoder(bytes.NewReader(data)).Decode(&out) + if err != nil { + return nil + } + m = make(map[string]interface{}) + for _, x := range out { + m[x.Name] = x.Data + } + imported[path] = m + } + return m[key] +} diff --git a/libgo/go/cmd/vet/method.go b/libgo/go/cmd/vet/method.go index b13ce2f..5783278 100644 --- a/libgo/go/cmd/vet/method.go +++ b/libgo/go/cmd/vet/method.go @@ -166,9 +166,7 @@ func (f *File) matchParams(expect []string, actual []ast.Expr, prefix string) bo // Does this one type match? func (f *File) matchParamType(expect string, actual ast.Expr) bool { - if strings.HasPrefix(expect, "=") { - expect = expect[1:] - } + expect = strings.TrimPrefix(expect, "=") // Strip package name if we're in that package. if n := len(f.file.Name.Name); len(expect) > n && expect[:n] == f.file.Name.Name && expect[n] == '.' { expect = expect[n+1:] diff --git a/libgo/go/cmd/vet/print.go b/libgo/go/cmd/vet/print.go index 456fbcc..a55da1d 100644 --- a/libgo/go/cmd/vet/print.go +++ b/libgo/go/cmd/vet/print.go @@ -8,6 +8,7 @@ package main import ( "bytes" + "encoding/gob" "flag" "fmt" "go/ast" @@ -15,6 +16,7 @@ import ( "go/token" "go/types" "regexp" + "sort" "strconv" "strings" "unicode/utf8" @@ -27,6 +29,9 @@ func init() { "check printf-like invocations", checkFmtPrintfCall, funcDecl, callExpr) + registerPkgCheck("printf", findPrintfLike) + registerExport("printf", exportPrintfLike) + gob.Register([]printfExport(nil)) } func initPrintFlags() { @@ -44,73 +49,304 @@ func initPrintFlags() { name = name[:colon] } - isPrint[strings.ToLower(name)] = true + if !strings.Contains(name, ".") { + name = strings.ToLower(name) + } + isPrint[name] = true } } -// TODO(rsc): Incorporate user-defined printf wrappers again. -// The general plan is to allow vet of one package P to output -// additional information to supply to later vets of packages -// importing P. Then vet of P can record a list of printf wrappers -// and the later vet using P.Printf will find it in the list and check it. -// That's not ready for Go 1.10. -// When that does happen, uncomment the user-defined printf -// wrapper tests in testdata/print.go. +var localPrintfLike = make(map[string]int) + +type printfExport struct { + Name string + Kind int +} + +// printfImported maps from package name to the printf vet data +// exported by that package. +var printfImported = make(map[string]map[string]int) + +type printfWrapper struct { + name string + fn *ast.FuncDecl + format *ast.Field + args *ast.Field + callers []printfCaller + failed bool // if true, not a printf wrapper +} + +type printfCaller struct { + w *printfWrapper + call *ast.CallExpr +} + +// maybePrintfWrapper decides whether decl (a declared function) may be a wrapper +// around a fmt.Printf or fmt.Print function. If so it returns a printfWrapper +// function describing the declaration. Later processing will analyze the +// graph of potential printf wrappers to pick out the ones that are true wrappers. +// A function may be a Printf or Print wrapper if its last argument is ...interface{}. +// If the next-to-last argument is a string, then this may be a Printf wrapper. +// Otherwise it may be a Print wrapper. +func maybePrintfWrapper(decl ast.Decl) *printfWrapper { + // Look for functions with final argument type ...interface{}. + fn, ok := decl.(*ast.FuncDecl) + if !ok || fn.Body == nil { + return nil + } + name := fn.Name.Name + if fn.Recv != nil { + // For (*T).Name or T.name, use "T.name". + rcvr := fn.Recv.List[0].Type + if ptr, ok := rcvr.(*ast.StarExpr); ok { + rcvr = ptr.X + } + id, ok := rcvr.(*ast.Ident) + if !ok { + return nil + } + name = id.Name + "." + name + } + params := fn.Type.Params.List + if len(params) == 0 { + return nil + } + args := params[len(params)-1] + if len(args.Names) != 1 { + return nil + } + ddd, ok := args.Type.(*ast.Ellipsis) + if !ok { + return nil + } + iface, ok := ddd.Elt.(*ast.InterfaceType) + if !ok || len(iface.Methods.List) > 0 { + return nil + } + var format *ast.Field + if len(params) >= 2 { + p := params[len(params)-2] + if len(p.Names) == 1 { + if id, ok := p.Type.(*ast.Ident); ok && id.Name == "string" { + format = p + } + } + } + + return &printfWrapper{ + name: name, + fn: fn, + format: format, + args: args, + } +} + +// findPrintfLike scans the entire package to find printf-like functions. +func findPrintfLike(pkg *Package) { + if vcfg.ImportPath == "" { // no type or vetx information; don't bother + return + } + + // Gather potential wrappesr and call graph between them. + byName := make(map[string]*printfWrapper) + var wrappers []*printfWrapper + for _, file := range pkg.files { + if file.file == nil { + continue + } + for _, decl := range file.file.Decls { + w := maybePrintfWrapper(decl) + if w == nil { + continue + } + byName[w.name] = w + wrappers = append(wrappers, w) + } + } + + // Walk the graph to figure out which are really printf wrappers. + for _, w := range wrappers { + // Scan function for calls that could be to other printf-like functions. + ast.Inspect(w.fn.Body, func(n ast.Node) bool { + if w.failed { + return false + } + + // TODO: Relax these checks; issue 26555. + if assign, ok := n.(*ast.AssignStmt); ok { + for _, lhs := range assign.Lhs { + if match(lhs, w.format) || match(lhs, w.args) { + // Modifies the format + // string or args in + // some way, so not a + // simple wrapper. + w.failed = true + return false + } + } + } + if un, ok := n.(*ast.UnaryExpr); ok && un.Op == token.AND { + if match(un.X, w.format) || match(un.X, w.args) { + // Taking the address of the + // format string or args, + // so not a simple wrapper. + w.failed = true + return false + } + } + + call, ok := n.(*ast.CallExpr) + if !ok || len(call.Args) == 0 || !match(call.Args[len(call.Args)-1], w.args) { + return true + } + + pkgpath, name, kind := printfNameAndKind(pkg, call.Fun) + if kind != 0 { + checkPrintfFwd(pkg, w, call, kind) + return true + } + + // If the call is to another function in this package, + // maybe we will find out it is printf-like later. + // Remember this call for later checking. + if pkgpath == "" && byName[name] != nil { + callee := byName[name] + callee.callers = append(callee.callers, printfCaller{w, call}) + } + + return true + }) + } +} + +func match(arg ast.Expr, param *ast.Field) bool { + id, ok := arg.(*ast.Ident) + return ok && id.Obj != nil && id.Obj.Decl == param +} + +const ( + kindPrintf = 1 + kindPrint = 2 +) + +// printfLike reports whether a call to fn should be considered a call to a printf-like function. +// It returns 0 (indicating not a printf-like function), kindPrintf, or kindPrint. +func printfLike(pkg *Package, fn ast.Expr, byName map[string]*printfWrapper) int { + if id, ok := fn.(*ast.Ident); ok && id.Obj != nil { + if w := byName[id.Name]; w != nil && id.Obj.Decl == w.fn { + // Found call to function in same package. + return localPrintfLike[id.Name] + } + } + if sel, ok := fn.(*ast.SelectorExpr); ok { + if id, ok := sel.X.(*ast.Ident); ok && id.Name == "fmt" && strings.Contains(sel.Sel.Name, "rint") { + if strings.HasSuffix(sel.Sel.Name, "f") { + return kindPrintf + } + return kindPrint + } + } + return 0 +} + +// checkPrintfFwd checks that a printf-forwarding wrapper is forwarding correctly. +// It diagnoses writing fmt.Printf(format, args) instead of fmt.Printf(format, args...). +func checkPrintfFwd(pkg *Package, w *printfWrapper, call *ast.CallExpr, kind int) { + matched := kind == kindPrint || + kind == kindPrintf && len(call.Args) >= 2 && match(call.Args[len(call.Args)-2], w.format) + if !matched { + return + } + + if !call.Ellipsis.IsValid() { + typ, ok := pkg.types[call.Fun].Type.(*types.Signature) + if !ok { + return + } + if len(call.Args) > typ.Params().Len() { + // If we're passing more arguments than what the + // print/printf function can take, adding an ellipsis + // would break the program. For example: + // + // func foo(arg1 string, arg2 ...interface{} { + // fmt.Printf("%s %v", arg1, arg2) + // } + return + } + if !vcfg.VetxOnly { + desc := "printf" + if kind == kindPrint { + desc = "print" + } + pkg.files[0].Badf(call.Pos(), "missing ... in args forwarded to %s-like function", desc) + } + return + } + name := w.name + if localPrintfLike[name] == 0 { + localPrintfLike[name] = kind + for _, caller := range w.callers { + checkPrintfFwd(pkg, caller.w, caller.call, kind) + } + } +} + +func exportPrintfLike() interface{} { + out := make([]printfExport, 0, len(localPrintfLike)) + for name, kind := range localPrintfLike { + out = append(out, printfExport{ + Name: name, + Kind: kind, + }) + } + sort.Slice(out, func(i, j int) bool { + return out[i].Name < out[j].Name + }) + return out +} // isPrint records the print functions. // If a key ends in 'f' then it is assumed to be a formatted print. var isPrint = map[string]bool{ - "fmt.Errorf": true, - "fmt.Fprint": true, - "fmt.Fprintf": true, - "fmt.Fprintln": true, - "fmt.Print": true, - "fmt.Printf": true, - "fmt.Println": true, - "fmt.Sprint": true, - "fmt.Sprintf": true, - "fmt.Sprintln": true, - "log.Fatal": true, - "log.Fatalf": true, - "log.Fatalln": true, - "log.Logger.Fatal": true, - "log.Logger.Fatalf": true, - "log.Logger.Fatalln": true, - "log.Logger.Panic": true, - "log.Logger.Panicf": true, - "log.Logger.Panicln": true, - "log.Logger.Printf": true, - "log.Logger.Println": true, - "log.Panic": true, - "log.Panicf": true, - "log.Panicln": true, - "log.Print": true, - "log.Printf": true, - "log.Println": true, - "testing.B.Error": true, - "testing.B.Errorf": true, - "testing.B.Fatal": true, - "testing.B.Fatalf": true, - "testing.B.Log": true, - "testing.B.Logf": true, - "testing.B.Skip": true, - "testing.B.Skipf": true, - "testing.T.Error": true, - "testing.T.Errorf": true, - "testing.T.Fatal": true, - "testing.T.Fatalf": true, - "testing.T.Log": true, - "testing.T.Logf": true, - "testing.T.Skip": true, - "testing.T.Skipf": true, - "testing.TB.Error": true, - "testing.TB.Errorf": true, - "testing.TB.Fatal": true, - "testing.TB.Fatalf": true, - "testing.TB.Log": true, - "testing.TB.Logf": true, - "testing.TB.Skip": true, - "testing.TB.Skipf": true, + "fmt.Errorf": true, + "fmt.Fprint": true, + "fmt.Fprintf": true, + "fmt.Fprintln": true, + "fmt.Print": true, + "fmt.Printf": true, + "fmt.Println": true, + "fmt.Sprint": true, + "fmt.Sprintf": true, + "fmt.Sprintln": true, + + // testing.B, testing.T not auto-detected + // because the methods are picked up by embedding. + "testing.B.Error": true, + "testing.B.Errorf": true, + "testing.B.Fatal": true, + "testing.B.Fatalf": true, + "testing.B.Log": true, + "testing.B.Logf": true, + "testing.B.Skip": true, + "testing.B.Skipf": true, + "testing.T.Error": true, + "testing.T.Errorf": true, + "testing.T.Fatal": true, + "testing.T.Fatalf": true, + "testing.T.Log": true, + "testing.T.Logf": true, + "testing.T.Skip": true, + "testing.T.Skipf": true, + + // testing.TB is an interface, so can't detect wrapping. + "testing.TB.Error": true, + "testing.TB.Errorf": true, + "testing.TB.Fatal": true, + "testing.TB.Fatalf": true, + "testing.TB.Log": true, + "testing.TB.Logf": true, + "testing.TB.Skip": true, + "testing.TB.Skipf": true, } // formatString returns the format string argument and its index within @@ -187,12 +423,14 @@ func checkFmtPrintfCall(f *File, node ast.Node) { if d, ok := node.(*ast.FuncDecl); ok && isStringer(f, d) { // Remember we saw this. - if f.stringers == nil { - f.stringers = make(map[*ast.Object]bool) + if f.stringerPtrs == nil { + f.stringerPtrs = make(map[*ast.Object]bool) } if l := d.Recv.List; len(l) == 1 { if n := l[0].Names; len(n) == 1 { - f.stringers[n[0].Obj] = true + typ := f.pkg.types[l[0].Type] + _, ptrRecv := typ.Type.(*types.Pointer) + f.stringerPtrs[n[0].Obj] = ptrRecv } } return @@ -204,66 +442,93 @@ func checkFmtPrintfCall(f *File, node ast.Node) { } // Construct name like pkg.Printf or pkg.Type.Printf for lookup. - var name string - switch x := call.Fun.(type) { + _, name, kind := printfNameAndKind(f.pkg, call.Fun) + if kind == kindPrintf { + f.checkPrintf(call, name) + } + if kind == kindPrint { + f.checkPrint(call, name) + } +} + +func printfName(pkg *Package, called ast.Expr) (pkgpath, name string) { + switch x := called.(type) { case *ast.Ident: - if fn, ok := f.pkg.uses[x].(*types.Func); ok { - var pkg string - if fn.Pkg() == nil || fn.Pkg() == f.pkg.typesPkg { - pkg = vcfg.ImportPath + if fn, ok := pkg.uses[x].(*types.Func); ok { + if fn.Pkg() == nil || fn.Pkg() == pkg.typesPkg { + pkgpath = "" } else { - pkg = fn.Pkg().Path() + pkgpath = fn.Pkg().Path() } - name = pkg + "." + x.Name - break + return pkgpath, x.Name } case *ast.SelectorExpr: // Check for "fmt.Printf". if id, ok := x.X.(*ast.Ident); ok { - if pkgName, ok := f.pkg.uses[id].(*types.PkgName); ok { - name = pkgName.Imported().Path() + "." + x.Sel.Name - break + if pkgName, ok := pkg.uses[id].(*types.PkgName); ok { + return pkgName.Imported().Path(), x.Sel.Name } } // Check for t.Logf where t is a *testing.T. - if sel := f.pkg.selectors[x]; sel != nil { + if sel := pkg.selectors[x]; sel != nil { recv := sel.Recv() if p, ok := recv.(*types.Pointer); ok { recv = p.Elem() } if named, ok := recv.(*types.Named); ok { obj := named.Obj() - var pkg string - if obj.Pkg() == nil || obj.Pkg() == f.pkg.typesPkg { - pkg = vcfg.ImportPath + if obj.Pkg() == nil || obj.Pkg() == pkg.typesPkg { + pkgpath = "" } else { - pkg = obj.Pkg().Path() + pkgpath = obj.Pkg().Path() } - name = pkg + "." + obj.Name() + "." + x.Sel.Name - break + return pkgpath, obj.Name() + "." + x.Sel.Name } } } + return "", "" +} + +func printfNameAndKind(pkg *Package, called ast.Expr) (pkgpath, name string, kind int) { + pkgpath, name = printfName(pkg, called) if name == "" { - return + return pkgpath, name, 0 } - shortName := name[strings.LastIndex(name, ".")+1:] - - _, ok = isPrint[name] - if !ok { - // Next look up just "printf", for use with -printfuncs. - _, ok = isPrint[strings.ToLower(shortName)] + if pkgpath == "" { + kind = localPrintfLike[name] + } else if m, ok := printfImported[pkgpath]; ok { + kind = m[name] + } else { + var m map[string]int + if out, ok := readVetx(pkgpath, "printf").([]printfExport); ok { + m = make(map[string]int) + for _, x := range out { + m[x.Name] = x.Kind + } + } + printfImported[pkgpath] = m + kind = m[name] } - if ok { - if strings.HasSuffix(name, "f") { - f.checkPrintf(call, shortName) - } else { - f.checkPrint(call, shortName) + + if kind == 0 { + _, ok := isPrint[pkgpath+"."+name] + if !ok { + // Next look up just "printf", for use with -printfuncs. + short := name[strings.LastIndex(name, ".")+1:] + _, ok = isPrint[strings.ToLower(short)] + } + if ok { + if strings.HasSuffix(name, "f") { + kind = kindPrintf + } else { + kind = kindPrint + } } } + return pkgpath, name, kind } // isStringer returns true if the provided declaration is a "String() string" @@ -293,6 +558,7 @@ type formatState struct { file *File call *ast.CallExpr argNum int // Which argument we're expecting to format now. + hasIndex bool // Whether the argument is indexed. indexPending bool // Whether we have an indexed argument that has not resolved. nbytes int // number of bytes of the format string consumed. } @@ -317,6 +583,7 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string) { // Hard part: check formats against args. argNum := firstArg maxArgNum := firstArg + anyIndex := false for i, w := 0, 0; i < len(format); i += w { w = 1 if format[i] != '%' { @@ -330,6 +597,9 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string) { if !f.okPrintfArg(call, state) { // One error per format is enough. return } + if state.hasIndex { + anyIndex = true + } if len(state.argNums) > 0 { // Continue with the next sequential argument. argNum = state.argNums[len(state.argNums)-1] + 1 @@ -344,6 +614,10 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string) { if call.Ellipsis.IsValid() && maxArgNum >= len(call.Args)-1 { return } + // If any formats are indexed, extra arguments are ignored. + if anyIndex { + return + } // There should be no leftover arguments. if maxArgNum != len(call.Args) { expect := maxArgNum - firstArg @@ -402,6 +676,7 @@ func (s *formatState) parseIndex() bool { arg := int(arg32) arg += s.firstArg - 1 // We want to zero-index the actual arguments. s.argNum = arg + s.hasIndex = true s.indexPending = true return true } @@ -522,7 +797,7 @@ var printVerbs = []printVerb{ {'%', noFlag, 0}, {'b', numFlag, argInt | argFloat | argComplex}, {'c', "-", argRune | argInt}, - {'d', numFlag, argInt}, + {'d', numFlag, argInt | argPointer}, {'e', sharpNumFlag, argFloat | argComplex}, {'E', sharpNumFlag, argFloat | argComplex}, {'f', sharpNumFlag, argFloat | argComplex}, @@ -537,8 +812,8 @@ var printVerbs = []printVerb{ {'T', "-", anyType}, {'U', "-#", argRune | argInt}, {'v', allFlags, anyType}, - {'x', sharpNumFlag, argRune | argInt | argString}, - {'X', sharpNumFlag, argRune | argInt | argString}, + {'x', sharpNumFlag, argRune | argInt | argString | argPointer}, + {'X', sharpNumFlag, argRune | argInt | argString | argPointer}, } // okPrintfArg compares the formatState to the arguments actually present, @@ -569,6 +844,11 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) { return false } for _, flag := range state.flags { + // TODO: Disable complaint about '0' for Go 1.10. To be fixed properly in 1.11. + // See issues 23598 and 23605. + if flag == '0' { + continue + } if !strings.ContainsRune(v.flags, rune(flag)) { f.Badf(call.Pos(), "%s format %s has unrecognized flag %c", state.name, state.format, flag) return false @@ -623,9 +903,10 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) { // recursiveStringer reports whether the provided argument is r or &r for the // fmt.Stringer receiver identifier r. func (f *File) recursiveStringer(e ast.Expr) bool { - if len(f.stringers) == 0 { + if len(f.stringerPtrs) == 0 { return false } + ptr := false var obj *ast.Object switch e := e.(type) { case *ast.Ident: @@ -633,13 +914,13 @@ func (f *File) recursiveStringer(e ast.Expr) bool { case *ast.UnaryExpr: if id, ok := e.X.(*ast.Ident); ok && e.Op == token.AND { obj = id.Obj + ptr = true } } // It's unlikely to be a recursive stringer if it has a Format method. if typ := f.pkg.types[e].Type; typ != nil { - // Not a perfect match; see issue 6259. - if f.hasMethod(typ, "Format") { + if f.isFormatter(typ) { return false } } @@ -647,7 +928,16 @@ func (f *File) recursiveStringer(e ast.Expr) bool { // We compare the underlying Object, which checks that the identifier // is the one we declared as the receiver for the String method in // which this printf appears. - return f.stringers[obj] + ptrRecv, exist := f.stringerPtrs[obj] + if !exist { + return false + } + // We also need to check that using &t when we declared String + // on (t *T) is ok; in such a case, the address is printed. + if ptr && ptrRecv { + return false + } + return true } // isFunctionValue reports whether the expression is a function as opposed to a function call. @@ -681,7 +971,7 @@ func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, state *formatS // There are bad indexes in the format or there are fewer arguments than the format needs. // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi". arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed. - f.Badf(call.Pos(), "%s format %s reads arg #%d, but call has only %v", state.name, state.format, arg, count(len(call.Args)-state.firstArg, "arg")) + f.Badf(call.Pos(), "%s format %s reads arg #%d, but call has %v", state.name, state.format, arg, count(len(call.Args)-state.firstArg, "arg")) return false } @@ -694,7 +984,7 @@ const ( flagsRE = `[+\-#]*` indexOptRE = `(\[[0-9]+\])?` numOptRE = `([0-9]+|` + indexOptRE + `\*)?` - verbRE = `[bcdefgopqstvxEFGUX]` + verbRE = `[bcdefgopqstvxEFGTUX]` ) // checkPrint checks a call to an unformatted print routine such as Println. diff --git a/libgo/go/cmd/vet/testdata/asm/asm.go b/libgo/go/cmd/vet/testdata/asm/asm.go index e6d6d03..2237ddc 100644 --- a/libgo/go/cmd/vet/testdata/asm/asm.go +++ b/libgo/go/cmd/vet/testdata/asm/asm.go @@ -43,3 +43,6 @@ func wrapper(x int) func f15271() (x uint32) func f17584(x float32, y complex64) + +func noframe1(x int32) +func noframe2(x int32) diff --git a/libgo/go/cmd/vet/testdata/asm/asm3.s b/libgo/go/cmd/vet/testdata/asm/asm3.s index 3d69356..83e5386 100644 --- a/libgo/go/cmd/vet/testdata/asm/asm3.s +++ b/libgo/go/cmd/vet/testdata/asm/asm3.s @@ -176,3 +176,17 @@ TEXT ·leaf(SB),0,$-4-12 MOVW y+4(FP), AX MOVW AX, ret+8(FP) RET + +TEXT ·noframe1(SB),0,$0-4 + MOVW 0(R13), AX // Okay; our saved LR + MOVW 4(R13), AX // Okay; caller's saved LR + MOVW x+8(R13), AX // Okay; x argument + MOVW 12(R13), AX // ERROR "use of 12\(R13\) points beyond argument frame" + RET + +TEXT ·noframe2(SB),NOFRAME,$0-4 + MOVW 0(R13), AX // Okay; caller's saved LR + MOVW x+4(R13), AX // Okay; x argument + MOVW 8(R13), AX // ERROR "use of 8\(R13\) points beyond argument frame" + MOVW 12(R13), AX // ERROR "use of 12\(R13\) points beyond argument frame" + RET diff --git a/libgo/go/cmd/vet/testdata/atomic.go b/libgo/go/cmd/vet/testdata/atomic.go index d5a8e61..69730b4 100644 --- a/libgo/go/cmd/vet/testdata/atomic.go +++ b/libgo/go/cmd/vet/testdata/atomic.go @@ -43,10 +43,20 @@ func AtomicTests() { { // A variable declaration creates a new variable in the current scope. - x := atomic.AddUint64(&x, 1) // ERROR "declaration of .x. shadows declaration at testdata/atomic.go:16" + x := atomic.AddUint64(&x, 1) // ERROR "declaration of .x. shadows declaration at atomic.go:16" // Re-declaration assigns a new value. x, w := atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value" _ = w } } + +type T struct{} + +func (T) AddUint64(addr *uint64, delta uint64) uint64 { return 0 } + +func NonAtomic() { + x := uint64(1) + var atomic T + x = atomic.AddUint64(&x, 1) // ok; not the imported pkg +} diff --git a/libgo/go/cmd/vet/testdata/bool.go b/libgo/go/cmd/vet/testdata/bool.go index af6cc01..80c44d2 100644 --- a/libgo/go/cmd/vet/testdata/bool.go +++ b/libgo/go/cmd/vet/testdata/bool.go @@ -8,15 +8,33 @@ package testdata import "io" +type T int + +func (t T) Foo() int { return int(t) } + +type FT func() int + +var S []int + func RatherStupidConditions() { var f, g func() int if f() == 0 || f() == 0 { // OK f might have side effects } + var t T + _ = t.Foo() == 2 || t.Foo() == 2 // OK Foo might have side effects if v, w := f(), g(); v == w || v == w { // ERROR "redundant or: v == w || v == w" } _ = f == nil || f == nil // ERROR "redundant or: f == nil || f == nil" - _ = i == byte(1) || i == byte(1) // TODO conversions are treated as if they may have side effects + _ = i == byte(1) || i == byte(1) // ERROR "redundant or: i == byte(1) || i == byte(1)" + _ = i == T(2) || i == T(2) // ERROR "redundant or: i == T(2) || i == T(2)" + _ = FT(f) == nil || FT(f) == nil // ERROR "redundant or: FT(f) == nil || FT(f) == nil" + + _ = (func() int)(f) == nil || (func() int)(f) == nil // ERROR "redundant or: (func() int)(f) == nil || (func() int)(f) == nil" + _ = append(S, 3) == nil || append(S, 3) == nil // OK append has side effects + + var namedFuncVar FT + _ = namedFuncVar() == namedFuncVar() // OK still func calls var c chan int _ = 0 == <-c || 0 == <-c // OK subsequent receives may yield different values diff --git a/libgo/go/cmd/vet/testdata/buildtag/buildtag.go b/libgo/go/cmd/vet/testdata/buildtag/buildtag.go index f12f895..c2fd6aa 100644 --- a/libgo/go/cmd/vet/testdata/buildtag/buildtag.go +++ b/libgo/go/cmd/vet/testdata/buildtag/buildtag.go @@ -9,6 +9,10 @@ package testdata -// +build toolate // ERROR "build comment must appear before package clause and be followed by a blank line" +// +build toolate // ERROR "build comment must appear before package clause and be followed by a blank line$" var _ = 3 + +var _ = ` +// +build notacomment +` diff --git a/libgo/go/cmd/vet/testdata/composite.go b/libgo/go/cmd/vet/testdata/composite.go index 2e6ce26..3fe3eac 100644 --- a/libgo/go/cmd/vet/testdata/composite.go +++ b/libgo/go/cmd/vet/testdata/composite.go @@ -62,6 +62,11 @@ var Okay6 = []MyStruct{ {"aa", "bb", "cc"}, } +var Okay7 = []*MyStruct{ + {"foo", "bar", "baz"}, + {"aa", "bb", "cc"}, +} + // Testing is awkward because we need to reference things from a separate package // to trigger the warnings. @@ -101,3 +106,15 @@ var whitelistedPoint = image.Point{1, 2} // Do not check type from unknown package. // See issue 15408. var unknownPkgVar = unknownpkg.Foobar{"foo", "bar"} + +// A named pointer slice of CaseRange to test issue 23539. In +// particular, we're interested in how some slice elements omit their +// type. +var goodNamedPointerSliceLiteral = []*unicode.CaseRange{ + {Lo: 1, Hi: 2}, + &unicode.CaseRange{Lo: 1, Hi: 2}, +} +var badNamedPointerSliceLiteral = []*unicode.CaseRange{ + {1, 2}, // ERROR "unkeyed fields" + &unicode.CaseRange{1, 2}, // ERROR "unkeyed fields" +} diff --git a/libgo/go/cmd/vet/testdata/deadcode.go b/libgo/go/cmd/vet/testdata/deadcode.go index 5370bc3..d1a7ade 100644 --- a/libgo/go/cmd/vet/testdata/deadcode.go +++ b/libgo/go/cmd/vet/testdata/deadcode.go @@ -2123,3 +2123,12 @@ var _ = func() { // goto without label used to panic goto } + +func _() int { + // Empty switch tag with non-bool case value used to panic. + switch { + case 1: + println() + } + println() +} diff --git a/libgo/go/cmd/vet/testdata/print.go b/libgo/go/cmd/vet/testdata/print.go index 55ab84f..ecafed5 100644 --- a/libgo/go/cmd/vet/testdata/print.go +++ b/libgo/go/cmd/vet/testdata/print.go @@ -14,6 +14,7 @@ package testdata import ( "fmt" . "fmt" + logpkg "log" // renamed to make it harder to see "math" "os" "testing" @@ -79,7 +80,7 @@ func PrintfTests() { fmt.Printf("%G %G %G %G", 3e9, x, fslice, c) fmt.Printf("%b %b %b %b", 3e9, x, fslice, c) fmt.Printf("%o %o", 3, i) - fmt.Printf("%p %p", p, nil) + fmt.Printf("%p", p) fmt.Printf("%q %q %q %q", 3, i, 'x', r) fmt.Printf("%s %s %s", "hi", s, []byte{65}) fmt.Printf("%t %t", true, b) @@ -122,6 +123,7 @@ func PrintfTests() { fmt.Printf("%g", imap) // ERROR "Printf format %g has arg imap of wrong type map\[int\]int" fmt.Printf("%G", i) // ERROR "Printf format %G has arg i of wrong type int" fmt.Printf("%o", x) // ERROR "Printf format %o has arg x of wrong type float64" + fmt.Printf("%p", nil) // ERROR "Printf format %p has arg nil of wrong type untyped nil" fmt.Printf("%p", 23) // ERROR "Printf format %p has arg 23 of wrong type int" fmt.Printf("%q", x) // ERROR "Printf format %q has arg x of wrong type float64" fmt.Printf("%s", b) // ERROR "Printf format %s has arg b of wrong type bool" @@ -130,8 +132,8 @@ func PrintfTests() { fmt.Printf("%U", x) // ERROR "Printf format %U has arg x of wrong type float64" fmt.Printf("%x", nil) // ERROR "Printf format %x has arg nil of wrong type untyped nil" fmt.Printf("%X", 2.3) // ERROR "Printf format %X has arg 2.3 of wrong type float64" - fmt.Printf("%s", stringerv) // ERROR "Printf format %s has arg stringerv of wrong type testdata.stringer" - fmt.Printf("%t", stringerv) // ERROR "Printf format %t has arg stringerv of wrong type testdata.stringer" + fmt.Printf("%s", stringerv) // ERROR "Printf format %s has arg stringerv of wrong type testdata.ptrStringer" + fmt.Printf("%t", stringerv) // ERROR "Printf format %t has arg stringerv of wrong type testdata.ptrStringer" fmt.Printf("%s", embeddedStringerv) // ERROR "Printf format %s has arg embeddedStringerv of wrong type testdata.embeddedStringer" fmt.Printf("%t", embeddedStringerv) // ERROR "Printf format %t has arg embeddedStringerv of wrong type testdata.embeddedStringer" fmt.Printf("%q", notstringerv) // ERROR "Printf format %q has arg notstringerv of wrong type testdata.notstringer" @@ -147,6 +149,7 @@ func PrintfTests() { fmt.Println() // not an error fmt.Println("%s", "hi") // ERROR "Println call has possible formatting directive %s" fmt.Println("%v", "hi") // ERROR "Println call has possible formatting directive %v" + fmt.Println("%T", "hi") // ERROR "Println call has possible formatting directive %T" fmt.Println("0.0%") // correct (trailing % couldn't be a formatting directive) fmt.Printf("%s", "hi", 3) // ERROR "Printf call needs 1 arg but has 2 args" _ = fmt.Sprintf("%"+("s"), "hi", 3) // ERROR "Sprintf call needs 1 arg but has 2 args" @@ -166,13 +169,25 @@ func PrintfTests() { Printf("hi") // ok const format = "%s %s\n" Printf(format, "hi", "there") - Printf(format, "hi") // ERROR "Printf format %s reads arg #2, but call has only 1 arg$" - Printf("%s %d %.3v %q", "str", 4) // ERROR "Printf format %.3v reads arg #3, but call has only 2 args" - f := new(stringer) + Printf(format, "hi") // ERROR "Printf format %s reads arg #2, but call has 1 arg$" + Printf("%s %d %.3v %q", "str", 4) // ERROR "Printf format %.3v reads arg #3, but call has 2 args" + f := new(ptrStringer) f.Warn(0, "%s", "hello", 3) // ERROR "Warn call has possible formatting directive %s" f.Warnf(0, "%s", "hello", 3) // ERROR "Warnf call needs 1 arg but has 2 args" f.Warnf(0, "%r", "hello") // ERROR "Warnf format %r has unknown verb r" f.Warnf(0, "%#s", "hello") // ERROR "Warnf format %#s has unrecognized flag #" + f.Warn2(0, "%s", "hello", 3) // ERROR "Warn2 call has possible formatting directive %s" + f.Warnf2(0, "%s", "hello", 3) // ERROR "Warnf2 call needs 1 arg but has 2 args" + f.Warnf2(0, "%r", "hello") // ERROR "Warnf2 format %r has unknown verb r" + f.Warnf2(0, "%#s", "hello") // ERROR "Warnf2 format %#s has unrecognized flag #" + f.Wrap(0, "%s", "hello", 3) // ERROR "Wrap call has possible formatting directive %s" + f.Wrapf(0, "%s", "hello", 3) // ERROR "Wrapf call needs 1 arg but has 2 args" + f.Wrapf(0, "%r", "hello") // ERROR "Wrapf format %r has unknown verb r" + f.Wrapf(0, "%#s", "hello") // ERROR "Wrapf format %#s has unrecognized flag #" + f.Wrap2(0, "%s", "hello", 3) // ERROR "Wrap2 call has possible formatting directive %s" + f.Wrapf2(0, "%s", "hello", 3) // ERROR "Wrapf2 call needs 1 arg but has 2 args" + f.Wrapf2(0, "%r", "hello") // ERROR "Wrapf2 format %r has unknown verb r" + f.Wrapf2(0, "%#s", "hello") // ERROR "Wrapf2 format %#s has unrecognized flag #" fmt.Printf("%#s", FormatterVal(true)) // correct (the type is responsible for formatting) Printf("d%", 2) // ERROR "Printf format % is missing verb at end of string" Printf("%d", percentDV) @@ -180,6 +195,7 @@ func PrintfTests() { Printf("%d", notPercentDV) // ERROR "Printf format %d has arg notPercentDV of wrong type testdata.notPercentDStruct" Printf("%d", ¬PercentDV) // ERROR "Printf format %d has arg ¬PercentDV of wrong type \*testdata.notPercentDStruct" Printf("%p", ¬PercentDV) // Works regardless: we print it as a pointer. + Printf("%q", &percentDV) // ERROR "Printf format %q has arg &percentDV of wrong type \*testdata.percentDStruct" Printf("%s", percentSV) Printf("%s", &percentSV) // Good argument reorderings. @@ -240,7 +256,7 @@ func PrintfTests() { // Multiple string arguments before variadic args // errorf("WARNING", "foobar") // OK // errorf("INFO", "s=%s, n=%d", "foo", 1) // OK - // errorf("ERROR", "%d") // no error "errorf format %d reads arg #1, but call has only 0 args" + // errorf("ERROR", "%d") // no error "errorf format %d reads arg #1, but call has 0 args" // Printf from external package // externalprintf.Printf("%d", 42) // OK @@ -248,7 +264,7 @@ func PrintfTests() { // level := 123 // externalprintf.Logf(level, "%d", 42) // OK // externalprintf.Errorf(level, level, "foo %q bar", "foobar") // OK - // externalprintf.Logf(level, "%d") // no error "Logf format %d reads arg #1, but call has only 0 args" + // externalprintf.Logf(level, "%d") // no error "Logf format %d reads arg #1, but call has 0 args" // var formatStr = "%s %s" // externalprintf.Sprintf(formatStr, "a", "b") // OK // externalprintf.Logf(level, formatStr, "a", "b") // OK @@ -269,9 +285,10 @@ func PrintfTests() { Printf("%d %[0]d %d %[2]d x", 1, 2, 3, 4) // ERROR "Printf format has invalid argument index \[0\]" Printf("%d %[3]d %d %[-2]d x", 1, 2, 3, 4) // ERROR "Printf format has invalid argument index \[-2\]" Printf("%d %[3]d %d %[2234234234234]d x", 1, 2, 3, 4) // ERROR "Printf format has invalid argument index \[2234234234234\]" - Printf("%d %[3]d %-10d %[2]d x", 1, 2, 3) // ERROR "Printf format %-10d reads arg #4, but call has only 3 args" - Printf("%d %[3]d %d %[2]d x", 1, 2, 3, 4, 5) // ERROR "Printf call needs 4 args but has 5 args" + Printf("%d %[3]d %-10d %[2]d x", 1, 2, 3) // ERROR "Printf format %-10d reads arg #4, but call has 3 args" Printf("%[1][3]d x", 1, 2) // ERROR "Printf format %\[1\]\[ has unknown verb \[" + Printf("%[1]d x", 1, 2) // OK + Printf("%d %[3]d %d %[2]d x", 1, 2, 3, 4, 5) // OK // wrote Println but meant Fprintln Printf("%p\n", os.Stdout) // OK @@ -279,6 +296,31 @@ func PrintfTests() { Printf(someString(), "hello") // OK + // Printf wrappers in package log should be detected automatically + logpkg.Fatal("%d", 1) // ERROR "Fatal call has possible formatting directive %d" + logpkg.Fatalf("%d", "x") // ERROR "Fatalf format %d has arg \x22x\x22 of wrong type string" + logpkg.Fatalln("%d", 1) // ERROR "Fatalln call has possible formatting directive %d" + logpkg.Panic("%d", 1) // ERROR "Panic call has possible formatting directive %d" + logpkg.Panicf("%d", "x") // ERROR "Panicf format %d has arg \x22x\x22 of wrong type string" + logpkg.Panicln("%d", 1) // ERROR "Panicln call has possible formatting directive %d" + logpkg.Print("%d", 1) // ERROR "Print call has possible formatting directive %d" + logpkg.Printf("%d", "x") // ERROR "Printf format %d has arg \x22x\x22 of wrong type string" + logpkg.Println("%d", 1) // ERROR "Println call has possible formatting directive %d" + + // Methods too. + var l *logpkg.Logger + l.Fatal("%d", 1) // ERROR "Fatal call has possible formatting directive %d" + l.Fatalf("%d", "x") // ERROR "Fatalf format %d has arg \x22x\x22 of wrong type string" + l.Fatalln("%d", 1) // ERROR "Fatalln call has possible formatting directive %d" + l.Panic("%d", 1) // ERROR "Panic call has possible formatting directive %d" + l.Panicf("%d", "x") // ERROR "Panicf format %d has arg \x22x\x22 of wrong type string" + l.Panicln("%d", 1) // ERROR "Panicln call has possible formatting directive %d" + l.Print("%d", 1) // ERROR "Print call has possible formatting directive %d" + l.Printf("%d", "x") // ERROR "Printf format %d has arg \x22x\x22 of wrong type string" + l.Println("%d", 1) // ERROR "Println call has possible formatting directive %d" + + // Issue 26486 + dbg("", 1) // no error "call has arguments but no formatting directive" } func someString() string { return "X" } @@ -352,25 +394,65 @@ func multi() []interface{} { panic("don't call - testing only") } -type stringer float64 +type stringer int -var stringerv stringer +func (stringer) String() string { return "string" } -func (*stringer) String() string { +type ptrStringer float64 + +var stringerv ptrStringer + +func (*ptrStringer) String() string { return "string" } -func (*stringer) Warn(int, ...interface{}) string { +func (p *ptrStringer) Warn2(x int, args ...interface{}) string { + return p.Warn(x, args...) +} + +func (p *ptrStringer) Warnf2(x int, format string, args ...interface{}) string { + return p.Warnf(x, format, args...) +} + +func (*ptrStringer) Warn(x int, args ...interface{}) string { return "warn" } -func (*stringer) Warnf(int, string, ...interface{}) string { +func (*ptrStringer) Warnf(x int, format string, args ...interface{}) string { return "warnf" } +func (p *ptrStringer) Wrap2(x int, args ...interface{}) string { + return p.Wrap(x, args...) +} + +func (p *ptrStringer) Wrapf2(x int, format string, args ...interface{}) string { + return p.Wrapf(x, format, args...) +} + +func (*ptrStringer) Wrap(x int, args ...interface{}) string { + return fmt.Sprint(args...) +} + +func (*ptrStringer) Wrapf(x int, format string, args ...interface{}) string { + return fmt.Sprintf(format, args...) +} + +func (*ptrStringer) BadWrap(x int, args ...interface{}) string { + return fmt.Sprint(args) // ERROR "missing ... in args forwarded to print-like function" +} + +func (*ptrStringer) BadWrapf(x int, format string, args ...interface{}) string { + return fmt.Sprintf(format, args) // ERROR "missing ... in args forwarded to printf-like function" +} + +func (*ptrStringer) WrapfFalsePositive(x int, arg1 string, arg2 ...interface{}) string { + return fmt.Sprintf("%s %v", arg1, arg2) +} + type embeddedStringer struct { foo string - stringer + ptrStringer bar int } @@ -440,6 +522,7 @@ type recursivePtrStringer int func (p *recursivePtrStringer) String() string { _ = fmt.Sprintf("%v", *p) + _ = fmt.Sprint(&p) // ok; prints address return fmt.Sprintln(p) // ERROR "Sprintln arg p causes recursive call to String method" } @@ -478,13 +561,17 @@ type RecursiveStruct2 struct { var recursiveStruct1V = &RecursiveStruct1{} -// Issue 17798: unexported stringer cannot be formatted. +type unexportedInterface struct { + f interface{} +} + +// Issue 17798: unexported ptrStringer cannot be formatted. type unexportedStringer struct { - t stringer + t ptrStringer } type unexportedStringerOtherFields struct { s string - t stringer + t ptrStringer S string } @@ -502,7 +589,23 @@ type errorer struct{} func (e errorer) Error() string { return "errorer" } +type unexportedCustomError struct { + e errorer +} + +type errorInterface interface { + error + ExtraMethod() +} + +type unexportedErrorInterface struct { + e errorInterface +} + func UnexportedStringerOrError() { + fmt.Printf("%s", unexportedInterface{"foo"}) // ok; prints {foo} + fmt.Printf("%s", unexportedInterface{3}) // ok; we can't see the problem + us := unexportedStringer{} fmt.Printf("%s", us) // ERROR "Printf format %s has arg us of wrong type testdata.unexportedStringer" fmt.Printf("%s", &us) // ERROR "Printf format %s has arg &us of wrong type [*]testdata.unexportedStringer" @@ -528,8 +631,37 @@ func UnexportedStringerOrError() { fmt.Printf("%s", uef) // ERROR "Printf format %s has arg uef of wrong type testdata.unexportedErrorOtherFields" fmt.Printf("%s", &uef) // ERROR "Printf format %s has arg &uef of wrong type [*]testdata.unexportedErrorOtherFields" + uce := unexportedCustomError{ + e: errorer{}, + } + fmt.Printf("%s", uce) // ERROR "Printf format %s has arg uce of wrong type testdata.unexportedCustomError" + + uei := unexportedErrorInterface{} + fmt.Printf("%s", uei) // ERROR "Printf format %s has arg uei of wrong type testdata.unexportedErrorInterface" fmt.Println("foo\n", "bar") // not an error - fmt.Println("foo\n") // ERROR "Println arg list ends with redundant newline" - fmt.Println("foo\\n") // not an error - fmt.Println(`foo\n`) // not an error + + fmt.Println("foo\n") // ERROR "Println arg list ends with redundant newline" + fmt.Println("foo\\n") // not an error + fmt.Println(`foo\n`) // not an error + + intSlice := []int{3, 4} + fmt.Printf("%s", intSlice) // ERROR "Printf format %s has arg intSlice of wrong type \[\]int" + nonStringerArray := [1]unexportedStringer{{}} + fmt.Printf("%s", nonStringerArray) // ERROR "Printf format %s has arg nonStringerArray of wrong type \[1\]testdata.unexportedStringer" + fmt.Printf("%s", []stringer{3, 4}) // not an error + fmt.Printf("%s", [2]stringer{3, 4}) // not an error +} + +// TODO: Disable complaint about '0' for Go 1.10. To be fixed properly in 1.11. +// See issues 23598 and 23605. +func DisableErrorForFlag0() { + fmt.Printf("%0t", true) +} + +// Issue 26486. +func dbg(format string, args ...interface{}) { + if format == "" { + format = "%v" + } + fmt.Printf(format, args...) } diff --git a/libgo/go/cmd/vet/testdata/shadow.go b/libgo/go/cmd/vet/testdata/shadow.go index 3b61137..c55cb27 100644 --- a/libgo/go/cmd/vet/testdata/shadow.go +++ b/libgo/go/cmd/vet/testdata/shadow.go @@ -17,7 +17,7 @@ func ShadowRead(f *os.File, buf []byte) (err error) { _ = err } if f != nil { - _, err := f.Read(buf) // ERROR "declaration of .err. shadows declaration at testdata/shadow.go:13" + _, err := f.Read(buf) // ERROR "declaration of .err. shadows declaration at shadow.go:13" if err != nil { return err } @@ -25,8 +25,8 @@ func ShadowRead(f *os.File, buf []byte) (err error) { _ = i } if f != nil { - x := one() // ERROR "declaration of .x. shadows declaration at testdata/shadow.go:14" - var _, err = f.Read(buf) // ERROR "declaration of .err. shadows declaration at testdata/shadow.go:13" + x := one() // ERROR "declaration of .x. shadows declaration at shadow.go:14" + var _, err = f.Read(buf) // ERROR "declaration of .err. shadows declaration at shadow.go:13" if x == 1 && err != nil { return err } @@ -46,7 +46,7 @@ func ShadowRead(f *os.File, buf []byte) (err error) { if shadowTemp := shadowTemp; true { // OK: obviously intentional idiomatic redeclaration var f *os.File // OK because f is not mentioned later in the function. // The declaration of x is a shadow because x is mentioned below. - var x int // ERROR "declaration of .x. shadows declaration at testdata/shadow.go:14" + var x int // ERROR "declaration of .x. shadows declaration at shadow.go:14" _, _, _ = x, f, shadowTemp } // Use a couple of variables to trigger shadowing errors. diff --git a/libgo/go/cmd/vet/testdata/structtag.go b/libgo/go/cmd/vet/testdata/structtag.go index c87e42f..ce21e80 100644 --- a/libgo/go/cmd/vet/testdata/structtag.go +++ b/libgo/go/cmd/vet/testdata/structtag.go @@ -44,40 +44,40 @@ type AnonymousXML struct{} type DuplicateJSONFields struct { JSON int `json:"a"` - DuplicateJSON int `json:"a"` // ERROR "struct field DuplicateJSON repeats json tag .a. also at testdata/structtag.go:46" + DuplicateJSON int `json:"a"` // ERROR "struct field DuplicateJSON repeats json tag .a. also at structtag.go:46" IgnoredJSON int `json:"-"` OtherIgnoredJSON int `json:"-"` OmitJSON int `json:",omitempty"` OtherOmitJSON int `json:",omitempty"` - DuplicateOmitJSON int `json:"a,omitempty"` // ERROR "struct field DuplicateOmitJSON repeats json tag .a. also at testdata/structtag.go:46" + DuplicateOmitJSON int `json:"a,omitempty"` // ERROR "struct field DuplicateOmitJSON repeats json tag .a. also at structtag.go:46" NonJSON int `foo:"a"` DuplicateNonJSON int `foo:"a"` Embedded struct { DuplicateJSON int `json:"a"` // OK because its not in the same struct type } - AnonymousJSON `json:"a"` // ERROR "struct field AnonymousJSON repeats json tag .a. also at testdata/structtag.go:46" + AnonymousJSON `json:"a"` // ERROR "struct field AnonymousJSON repeats json tag .a. also at structtag.go:46" XML int `xml:"a"` - DuplicateXML int `xml:"a"` // ERROR "struct field DuplicateXML repeats xml tag .a. also at testdata/structtag.go:60" + DuplicateXML int `xml:"a"` // ERROR "struct field DuplicateXML repeats xml tag .a. also at structtag.go:60" IgnoredXML int `xml:"-"` OtherIgnoredXML int `xml:"-"` OmitXML int `xml:",omitempty"` OtherOmitXML int `xml:",omitempty"` - DuplicateOmitXML int `xml:"a,omitempty"` // ERROR "struct field DuplicateOmitXML repeats xml tag .a. also at testdata/structtag.go:60" + DuplicateOmitXML int `xml:"a,omitempty"` // ERROR "struct field DuplicateOmitXML repeats xml tag .a. also at structtag.go:60" NonXML int `foo:"a"` DuplicateNonXML int `foo:"a"` Embedded struct { DuplicateXML int `xml:"a"` // OK because its not in the same struct type } - AnonymousXML `xml:"a"` // ERROR "struct field AnonymousXML repeats xml tag .a. also at testdata/structtag.go:60" + AnonymousXML `xml:"a"` // ERROR "struct field AnonymousXML repeats xml tag .a. also at structtag.go:60" Attribute struct { XMLName xml.Name `xml:"b"` NoDup int `xml:"b"` // OK because XMLName above affects enclosing struct. Attr int `xml:"b,attr"` // OK because <b b="0"><b>0</b></b> is valid. - DupAttr int `xml:"b,attr"` // ERROR "struct field DupAttr repeats xml attribute tag .b. also at testdata/structtag.go:76" - DupOmitAttr int `xml:"b,omitempty,attr"` // ERROR "struct field DupOmitAttr repeats xml attribute tag .b. also at testdata/structtag.go:76" + DupAttr int `xml:"b,attr"` // ERROR "struct field DupAttr repeats xml attribute tag .b. also at structtag.go:76" + DupOmitAttr int `xml:"b,omitempty,attr"` // ERROR "struct field DupOmitAttr repeats xml attribute tag .b. also at structtag.go:76" - AnonymousXML `xml:"b,attr"` // ERROR "struct field AnonymousXML repeats xml attribute tag .b. also at testdata/structtag.go:76" + AnonymousXML `xml:"b,attr"` // ERROR "struct field AnonymousXML repeats xml attribute tag .b. also at structtag.go:76" } } diff --git a/libgo/go/cmd/vet/types.go b/libgo/go/cmd/vet/types.go index d83611b..5f8e481 100644 --- a/libgo/go/cmd/vet/types.go +++ b/libgo/go/cmd/vet/types.go @@ -12,7 +12,6 @@ import ( "go/importer" "go/token" "go/types" - "runtime" ) // stdImporter is the importer we use to import packages. @@ -173,7 +172,7 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp return true // %s matches []byte } // Recur: []int matches %d. - return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem().Underlying(), arg, inProgress) + return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress) case *types.Slice: // Same as array. @@ -202,8 +201,8 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp if str, ok := typ.Elem().Underlying().(*types.Struct); ok { return f.matchStructArgType(t, str, arg, inProgress) } - // The rest can print with %p as pointers, or as integers with %x etc. - return t&(argInt|argPointer) != 0 + // Check whether the rest can print pointers. + return t&argPointer != 0 case *types.Struct: return f.matchStructArgType(t, typ, arg, inProgress) @@ -255,7 +254,7 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp return t&(argInt|argRune) != 0 case types.UntypedNil: - return t&argPointer != 0 // TODO? + return false case types.Invalid: if *verbose { @@ -270,7 +269,19 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp } func isConvertibleToString(typ types.Type) bool { - return types.AssertableTo(errorType, typ) || stringerType != nil && types.AssertableTo(stringerType, typ) + if bt, ok := typ.(*types.Basic); ok && bt.Kind() == types.UntypedNil { + // We explicitly don't want untyped nil, which is + // convertible to both of the interfaces below, as it + // would just panic anyway. + return false + } + if types.ConvertibleTo(typ, errorType) { + return true // via .Error() + } + if stringerType != nil && types.ConvertibleTo(typ, stringerType) { + return true // via .String() + } + return false } // hasBasicType reports whether x's type is a types.Basic with the given kind. @@ -299,15 +310,4 @@ func (f *File) matchStructArgType(t printfArgType, typ *types.Struct, arg ast.Ex return true } -// hasMethod reports whether the type contains a method with the given name. -// It is part of the workaround for Formatters and should be deleted when -// that workaround is no longer necessary. -// TODO: This could be better once issue 6259 is fixed. -func (f *File) hasMethod(typ types.Type, name string) bool { - // assume we have an addressable variable of type typ - obj, _, _ := types.LookupFieldOrMethod(typ, true, f.pkg.typesPkg, name) - _, ok := obj.(*types.Func) - return ok -} - -var archSizes = types.SizesFor(runtime.Compiler, build.Default.GOARCH) +var archSizes = types.SizesFor("gc", build.Default.GOARCH) diff --git a/libgo/go/cmd/vet/vet_test.go b/libgo/go/cmd/vet/vet_test.go index 116b77e..3e42525 100644 --- a/libgo/go/cmd/vet/vet_test.go +++ b/libgo/go/cmd/vet/vet_test.go @@ -6,12 +6,17 @@ package main_test import ( "bytes" + "errors" "fmt" "internal/testenv" + "io/ioutil" + "log" "os" "os/exec" "path/filepath" + "regexp" "runtime" + "strconv" "strings" "sync" "testing" @@ -19,7 +24,7 @@ import ( const ( dataDir = "testdata" - binary = "testvet.exe" + binary = "./testvet.exe" ) // We implement TestMain so remove the test binary when all is done. @@ -29,16 +34,6 @@ func TestMain(m *testing.M) { os.Exit(result) } -func MustHavePerl(t *testing.T) { - switch runtime.GOOS { - case "plan9", "windows": - t.Skipf("skipping test: perl not available on %s", runtime.GOOS) - } - if _, err := exec.LookPath("perl"); err != nil { - t.Skipf("skipping test: perl not found in path") - } -} - var ( buildMu sync.Mutex // guards following built = false // We have built the binary. @@ -55,7 +50,6 @@ func Build(t *testing.T) { t.Skip("cannot run on this environment") } testenv.MustHaveGoBuild(t) - MustHavePerl(t) cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", binary) output, err := cmd.CombinedOutput() if err != nil { @@ -67,26 +61,19 @@ func Build(t *testing.T) { } func Vet(t *testing.T, files []string) { - errchk := filepath.Join(runtime.GOROOT(), "test", "errchk") - if _, err := os.Stat(errchk); err != nil { - t.Skipf("skipping because no errchk: %v", err) - } flags := []string{ - "./" + binary, "-printfuncs=Warn:1,Warnf:1", "-all", "-shadow", } - cmd := exec.Command(errchk, append(flags, files...)...) - if !run(cmd, t) { - t.Fatal("vet command failed") - } + cmd := exec.Command(binary, append(flags, files...)...) + errchk(cmd, files, t) } -// Run this shell script, but do it in Go so it can be run by "go test". -// go build -o testvet -// $(GOROOT)/test/errchk ./testvet -shadow -printfuncs='Warn:1,Warnf:1' testdata/*.go testdata/*.s -// rm testvet +// TestVet is equivalent to running this: +// go build -o ./testvet +// errorCheck the output of ./testvet -shadow -printfuncs='Warn:1,Warnf:1' testdata/*.go testdata/*.s +// rm ./testvet // // TestVet tests self-contained files in testdata/*.go. @@ -98,7 +85,6 @@ func TestVet(t *testing.T) { Build(t) t.Parallel() - // errchk ./testvet gos, err := filepath.Glob(filepath.Join(dataDir, "*.go")) if err != nil { t.Fatal(err) @@ -130,21 +116,23 @@ func TestVet(t *testing.T) { } func TestVetPrint(t *testing.T) { - Build(t) - errchk := filepath.Join(runtime.GOROOT(), "test", "errchk") - if _, err := os.Stat(errchk); err != nil { - t.Skipf("skipping because no errchk: %v", err) + if runtime.Compiler == "gccgo" { + // This test currently fails with gccgo because, in + // the absence of standard library sources, gccgo can + // not deduce that the standard log package formatting + // functions are just printf wrappers. + t.Skip("skipping for gccgo because there are no standard library sources") } + + Build(t) + file := filepath.Join("testdata", "print.go") cmd := exec.Command( - errchk, - "go", "vet", "-vettool=./"+binary, + "go", "vet", "-vettool="+binary, "-printf", "-printfuncs=Warn:1,Warnf:1", - "testdata/print.go", + file, ) - if !run(cmd, t) { - t.Fatal("vet command failed") - } + errchk(cmd, []string{file}, t) } func TestVetAsm(t *testing.T) { @@ -161,7 +149,6 @@ func TestVetAsm(t *testing.T) { } t.Parallel() - // errchk ./testvet Vet(t, append(gos, asms...)) } @@ -187,23 +174,20 @@ func TestVetDirs(t *testing.T) { } } -func run(c *exec.Cmd, t *testing.T) bool { +func errchk(c *exec.Cmd, files []string, t *testing.T) { output, err := c.CombinedOutput() - if err != nil { + if _, ok := err.(*exec.ExitError); !ok { t.Logf("vet output:\n%s", output) t.Fatal(err) } - // Errchk delights by not returning non-zero status if it finds errors, so we look at the output. - // It prints "BUG" if there is a failure. - if !c.ProcessState.Success() { - t.Logf("vet output:\n%s", output) - return false + fullshort := make([]string, 0, len(files)*2) + for _, f := range files { + fullshort = append(fullshort, f, filepath.Base(f)) } - ok := !bytes.Contains(output, []byte("BUG")) - if !ok { - t.Logf("vet output:\n%s", output) + err = errorCheck(string(output), false, fullshort...) + if err != nil { + t.Errorf("error check failed: %s", err) } - return ok } // TestTags verifies that the -tags argument controls which files to check. @@ -220,7 +204,7 @@ func TestTags(t *testing.T) { "-v", // We're going to look at the files it examines. "testdata/tagtest", } - cmd := exec.Command("./"+binary, args...) + cmd := exec.Command(binary, args...) output, err := cmd.CombinedOutput() if err != nil { t.Fatal(err) @@ -240,10 +224,225 @@ func TestTags(t *testing.T) { func TestVetVerbose(t *testing.T) { t.Parallel() Build(t) - cmd := exec.Command("./"+binary, "-v", "-all", "testdata/cgo/cgo3.go") + cmd := exec.Command(binary, "-v", "-all", "testdata/cgo/cgo3.go") out, err := cmd.CombinedOutput() if err != nil { t.Logf("%s", out) t.Error(err) } } + +// All declarations below were adapted from test/run.go. + +// errorCheck matches errors in outStr against comments in source files. +// For each line of the source files which should generate an error, +// there should be a comment of the form // ERROR "regexp". +// If outStr has an error for a line which has no such comment, +// this function will report an error. +// Likewise if outStr does not have an error for a line which has a comment, +// or if the error message does not match the <regexp>. +// The <regexp> syntax is Perl but its best to stick to egrep. +// +// Sources files are supplied as fullshort slice. +// It consists of pairs: full path to source file and it's base name. +func errorCheck(outStr string, wantAuto bool, fullshort ...string) (err error) { + var errs []error + out := splitOutput(outStr, wantAuto) + // Cut directory name. + for i := range out { + for j := 0; j < len(fullshort); j += 2 { + full, short := fullshort[j], fullshort[j+1] + out[i] = strings.Replace(out[i], full, short, -1) + } + } + + var want []wantedError + for j := 0; j < len(fullshort); j += 2 { + full, short := fullshort[j], fullshort[j+1] + want = append(want, wantedErrors(full, short)...) + } + for _, we := range want { + var errmsgs []string + if we.auto { + errmsgs, out = partitionStrings("<autogenerated>", out) + } else { + errmsgs, out = partitionStrings(we.prefix, out) + } + if len(errmsgs) == 0 { + errs = append(errs, fmt.Errorf("%s:%d: missing error %q", we.file, we.lineNum, we.reStr)) + continue + } + matched := false + n := len(out) + for _, errmsg := range errmsgs { + // Assume errmsg says "file:line: foo". + // Cut leading "file:line: " to avoid accidental matching of file name instead of message. + text := errmsg + if i := strings.Index(text, " "); i >= 0 { + text = text[i+1:] + } + if we.re.MatchString(text) { + matched = true + } else { + out = append(out, errmsg) + } + } + if !matched { + errs = append(errs, fmt.Errorf("%s:%d: no match for %#q in:\n\t%s", we.file, we.lineNum, we.reStr, strings.Join(out[n:], "\n\t"))) + continue + } + } + + if len(out) > 0 { + errs = append(errs, fmt.Errorf("Unmatched Errors:")) + for _, errLine := range out { + errs = append(errs, fmt.Errorf("%s", errLine)) + } + } + + if len(errs) == 0 { + return nil + } + if len(errs) == 1 { + return errs[0] + } + var buf bytes.Buffer + fmt.Fprintf(&buf, "\n") + for _, err := range errs { + fmt.Fprintf(&buf, "%s\n", err.Error()) + } + return errors.New(buf.String()) +} + +func splitOutput(out string, wantAuto bool) []string { + // gc error messages continue onto additional lines with leading tabs. + // Split the output at the beginning of each line that doesn't begin with a tab. + // <autogenerated> lines are impossible to match so those are filtered out. + var res []string + for _, line := range strings.Split(out, "\n") { + line = strings.TrimSuffix(line, "\r") // normalize Windows output + if strings.HasPrefix(line, "\t") { + res[len(res)-1] += "\n" + line + } else if strings.HasPrefix(line, "go tool") || strings.HasPrefix(line, "#") || !wantAuto && strings.HasPrefix(line, "<autogenerated>") { + continue + } else if strings.TrimSpace(line) != "" { + res = append(res, line) + } + } + return res +} + +// matchPrefix reports whether s starts with file name prefix followed by a :, +// and possibly preceded by a directory name. +func matchPrefix(s, prefix string) bool { + i := strings.Index(s, ":") + if i < 0 { + return false + } + j := strings.LastIndex(s[:i], "/") + s = s[j+1:] + if len(s) <= len(prefix) || s[:len(prefix)] != prefix { + return false + } + if s[len(prefix)] == ':' { + return true + } + return false +} + +func partitionStrings(prefix string, strs []string) (matched, unmatched []string) { + for _, s := range strs { + if matchPrefix(s, prefix) { + matched = append(matched, s) + } else { + unmatched = append(unmatched, s) + } + } + return +} + +type wantedError struct { + reStr string + re *regexp.Regexp + lineNum int + auto bool // match <autogenerated> line + file string + prefix string +} + +var ( + errRx = regexp.MustCompile(`// (?:GC_)?ERROR (.*)`) + errAutoRx = regexp.MustCompile(`// (?:GC_)?ERRORAUTO (.*)`) + errQuotesRx = regexp.MustCompile(`"([^"]*)"`) + lineRx = regexp.MustCompile(`LINE(([+-])([0-9]+))?`) +) + +// wantedErrors parses expected errors from comments in a file. +func wantedErrors(file, short string) (errs []wantedError) { + cache := make(map[string]*regexp.Regexp) + + src, err := ioutil.ReadFile(file) + if err != nil { + log.Fatal(err) + } + for i, line := range strings.Split(string(src), "\n") { + lineNum := i + 1 + if strings.Contains(line, "////") { + // double comment disables ERROR + continue + } + var auto bool + m := errAutoRx.FindStringSubmatch(line) + if m != nil { + auto = true + } else { + m = errRx.FindStringSubmatch(line) + } + if m == nil { + continue + } + all := m[1] + mm := errQuotesRx.FindAllStringSubmatch(all, -1) + if mm == nil { + log.Fatalf("%s:%d: invalid errchk line: %s", file, lineNum, line) + } + for _, m := range mm { + replacedOnce := false + rx := lineRx.ReplaceAllStringFunc(m[1], func(m string) string { + if replacedOnce { + return m + } + replacedOnce = true + n := lineNum + if strings.HasPrefix(m, "LINE+") { + delta, _ := strconv.Atoi(m[5:]) + n += delta + } else if strings.HasPrefix(m, "LINE-") { + delta, _ := strconv.Atoi(m[5:]) + n -= delta + } + return fmt.Sprintf("%s:%d", short, n) + }) + re := cache[rx] + if re == nil { + var err error + re, err = regexp.Compile(rx) + if err != nil { + log.Fatalf("%s:%d: invalid regexp \"%#q\" in ERROR line: %v", file, lineNum, rx, err) + } + cache[rx] = re + } + prefix := fmt.Sprintf("%s:%d", short, lineNum) + errs = append(errs, wantedError{ + reStr: rx, + re: re, + prefix: prefix, + auto: auto, + lineNum: lineNum, + file: short, + }) + } + } + + return +} diff --git a/libgo/go/compress/bzip2/bzip2.go b/libgo/go/compress/bzip2/bzip2.go index f07c7e8..c40129b 100644 --- a/libgo/go/compress/bzip2/bzip2.go +++ b/libgo/go/compress/bzip2/bzip2.go @@ -8,7 +8,7 @@ package bzip2 import "io" // There's no RFC for bzip2. I used the Wikipedia page for reference and a lot -// of guessing: http://en.wikipedia.org/wiki/Bzip2 +// of guessing: https://en.wikipedia.org/wiki/Bzip2 // The source code to pyflate was useful for debugging: // http://www.paul.sladen.org/projects/pyflate diff --git a/libgo/go/compress/bzip2/huffman.go b/libgo/go/compress/bzip2/huffman.go index dbba9a5..36ae954 100644 --- a/libgo/go/compress/bzip2/huffman.go +++ b/libgo/go/compress/bzip2/huffman.go @@ -43,30 +43,34 @@ func (t *huffmanTree) Decode(br *bitReader) (v uint16) { if br.bits > 0 { // Get next bit - fast path. br.bits-- - bit = 0 - (uint16(br.n>>br.bits) & 1) + bit = uint16(br.n>>(br.bits&63)) & 1 } else { // Get next bit - slow path. // Use ReadBits to retrieve a single bit // from the underling io.ByteReader. - bit = 0 - uint16(br.ReadBits(1)) + bit = uint16(br.ReadBits(1)) } - // now - // bit = 0xffff if the next bit was 1 - // bit = 0x0000 if the next bit was 0 - // 1 means left, 0 means right. - // - // if bit == 0xffff { - // nodeIndex = node.left - // } else { - // nodeIndex = node.right - // } - nodeIndex = (bit & node.left) | (^bit & node.right) + // Trick a compiler into generating conditional move instead of branch, + // by making both loads unconditional. + l, r := node.left, node.right + + if bit == 1 { + nodeIndex = l + } else { + nodeIndex = r + } if nodeIndex == invalidNodeValue { // We found a leaf. Use the value of bit to decide // whether is a left or a right value. - return (bit & node.leftValue) | (^bit & node.rightValue) + l, r := node.leftValue, node.rightValue + if bit == 1 { + v = l + } else { + v = r + } + return } } } @@ -90,13 +94,24 @@ func newHuffmanTree(lengths []uint8) (huffmanTree, error) { // First we sort the code length assignments by ascending code length, // using the symbol value to break ties. - pairs := huffmanSymbolLengthPairs(make([]huffmanSymbolLengthPair, len(lengths))) + pairs := make([]huffmanSymbolLengthPair, len(lengths)) for i, length := range lengths { pairs[i].value = uint16(i) pairs[i].length = length } - sort.Sort(pairs) + sort.Slice(pairs, func(i, j int) bool { + if pairs[i].length < pairs[j].length { + return true + } + if pairs[i].length > pairs[j].length { + return false + } + if pairs[i].value < pairs[j].value { + return true + } + return false + }) // Now we assign codes to the symbols, starting with the longest code. // We keep the codes packed into a uint32, at the most-significant end. @@ -105,7 +120,7 @@ func newHuffmanTree(lengths []uint8) (huffmanTree, error) { code := uint32(0) length := uint8(32) - codes := huffmanCodes(make([]huffmanCode, len(lengths))) + codes := make([]huffmanCode, len(lengths)) for i := len(pairs) - 1; i >= 0; i-- { if length > pairs[i].length { length = pairs[i].length @@ -120,7 +135,9 @@ func newHuffmanTree(lengths []uint8) (huffmanTree, error) { // Now we can sort by the code so that the left half of each branch are // grouped together, recursively. - sort.Sort(codes) + sort.Slice(codes, func(i, j int) bool { + return codes[i].code < codes[j].code + }) t.nodes = make([]huffmanNode, len(codes)) _, err := buildHuffmanNode(&t, codes, 0) @@ -133,30 +150,6 @@ type huffmanSymbolLengthPair struct { length uint8 } -// huffmanSymbolLengthPair is used to provide an interface for sorting. -type huffmanSymbolLengthPairs []huffmanSymbolLengthPair - -func (h huffmanSymbolLengthPairs) Len() int { - return len(h) -} - -func (h huffmanSymbolLengthPairs) Less(i, j int) bool { - if h[i].length < h[j].length { - return true - } - if h[i].length > h[j].length { - return false - } - if h[i].value < h[j].value { - return true - } - return false -} - -func (h huffmanSymbolLengthPairs) Swap(i, j int) { - h[i], h[j] = h[j], h[i] -} - // huffmanCode contains a symbol, its code and code length. type huffmanCode struct { code uint32 @@ -164,21 +157,6 @@ type huffmanCode struct { value uint16 } -// huffmanCodes is used to provide an interface for sorting. -type huffmanCodes []huffmanCode - -func (n huffmanCodes) Len() int { - return len(n) -} - -func (n huffmanCodes) Less(i, j int) bool { - return n[i].code < n[j].code -} - -func (n huffmanCodes) Swap(i, j int) { - n[i], n[j] = n[j], n[i] -} - // buildHuffmanNode takes a slice of sorted huffmanCodes and builds a node in // the Huffman tree at the given level. It returns the index of the newly // constructed node. diff --git a/libgo/go/compress/flate/deflate.go b/libgo/go/compress/flate/deflate.go index 4d6a535..8b92f15 100644 --- a/libgo/go/compress/flate/deflate.go +++ b/libgo/go/compress/flate/deflate.go @@ -720,7 +720,7 @@ func (w *Writer) Write(data []byte) (n int, err error) { // In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH. func (w *Writer) Flush() error { // For more about flushing: - // http://www.bolet.org/~pornin/deflate-flush.html + // https://www.bolet.org/~pornin/deflate-flush.html return w.d.syncFlush() } diff --git a/libgo/go/compress/flate/inflate.go b/libgo/go/compress/flate/inflate.go index faa33cc..25e81f3 100644 --- a/libgo/go/compress/flate/inflate.go +++ b/libgo/go/compress/flate/inflate.go @@ -10,7 +10,7 @@ package flate import ( "bufio" "io" - mathbits "math/bits" + "math/bits" "strconv" "sync" ) @@ -113,7 +113,7 @@ type huffmanDecoder struct { // tree (i.e., neither over-subscribed nor under-subscribed). The exception is a // degenerate case where the tree has only a single symbol with length 1. Empty // trees are permitted. -func (h *huffmanDecoder) init(bits []int) bool { +func (h *huffmanDecoder) init(lengths []int) bool { // Sanity enables additional runtime tests during Huffman // table construction. It's intended to be used during // development to supplement the currently ad-hoc unit tests. @@ -127,7 +127,7 @@ func (h *huffmanDecoder) init(bits []int) bool { // compute min and max length. var count [maxCodeLen]int var min, max int - for _, n := range bits { + for _, n := range lengths { if n == 0 { continue } @@ -177,7 +177,7 @@ func (h *huffmanDecoder) init(bits []int) bool { link := nextcode[huffmanChunkBits+1] >> 1 h.links = make([][]uint32, huffmanNumChunks-link) for j := uint(link); j < huffmanNumChunks; j++ { - reverse := int(mathbits.Reverse16(uint16(j))) + reverse := int(bits.Reverse16(uint16(j))) reverse >>= uint(16 - huffmanChunkBits) off := j - uint(link) if sanity && h.chunks[reverse] != 0 { @@ -188,14 +188,14 @@ func (h *huffmanDecoder) init(bits []int) bool { } } - for i, n := range bits { + for i, n := range lengths { if n == 0 { continue } code := nextcode[n] nextcode[n]++ chunk := uint32(i<<huffmanValueShift | n) - reverse := int(mathbits.Reverse16(uint16(code))) + reverse := int(bits.Reverse16(uint16(code))) reverse >>= uint(16 - n) if n <= huffmanChunkBits { for off := reverse; off < len(h.chunks); off += 1 << uint(n) { @@ -557,7 +557,7 @@ readLiteral: return } } - dist = int(mathbits.Reverse8(uint8(f.b & 0x1F << 3))) + dist = int(bits.Reverse8(uint8(f.b & 0x1F << 3))) f.b >>= 5 f.nb -= 5 } else { @@ -629,10 +629,7 @@ func (f *decompressor) dataBlock() { nr, err := io.ReadFull(f.r, f.buf[0:4]) f.roffset += int64(nr) if err != nil { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - f.err = err + f.err = noEOF(err) return } n := int(f.buf[0]) | int(f.buf[1])<<8 @@ -665,10 +662,7 @@ func (f *decompressor) copyData() { f.copyLen -= cnt f.dict.writeMark(cnt) if err != nil { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - f.err = err + f.err = noEOF(err) return } @@ -690,13 +684,18 @@ func (f *decompressor) finishBlock() { f.step = (*decompressor).nextBlock } +// noEOF returns err, unless err == io.EOF, in which case it returns io.ErrUnexpectedEOF. +func noEOF(e error) error { + if e == io.EOF { + return io.ErrUnexpectedEOF + } + return e +} + func (f *decompressor) moreBits() error { c, err := f.r.ReadByte() if err != nil { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - return err + return noEOF(err) } f.roffset++ f.b |= uint32(c) << f.nb @@ -711,25 +710,37 @@ func (f *decompressor) huffSym(h *huffmanDecoder) (int, error) { // cases, the chunks slice will be 0 for the invalid sequence, leading it // satisfy the n == 0 check below. n := uint(h.min) + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + nb, b := f.nb, f.b for { - for f.nb < n { - if err := f.moreBits(); err != nil { - return 0, err + for nb < n { + c, err := f.r.ReadByte() + if err != nil { + f.b = b + f.nb = nb + return 0, noEOF(err) } + f.roffset++ + b |= uint32(c) << (nb & 31) + nb += 8 } - chunk := h.chunks[f.b&(huffmanNumChunks-1)] + chunk := h.chunks[b&(huffmanNumChunks-1)] n = uint(chunk & huffmanCountMask) if n > huffmanChunkBits { - chunk = h.links[chunk>>huffmanValueShift][(f.b>>huffmanChunkBits)&h.linkMask] + chunk = h.links[chunk>>huffmanValueShift][(b>>huffmanChunkBits)&h.linkMask] n = uint(chunk & huffmanCountMask) } - if n <= f.nb { + if n <= nb { if n == 0 { + f.b = b + f.nb = nb f.err = CorruptInputError(f.roffset) return 0, f.err } - f.b >>= n - f.nb -= n + f.b = b >> (n & 31) + f.nb = nb - n return int(chunk >> huffmanValueShift), nil } } diff --git a/libgo/go/compress/gzip/gzip.go b/libgo/go/compress/gzip/gzip.go index 0cc44c5..eaeb185 100644 --- a/libgo/go/compress/gzip/gzip.go +++ b/libgo/go/compress/gzip/gzip.go @@ -41,7 +41,7 @@ type Writer struct { // NewWriter returns a new Writer. // Writes to the returned writer are compressed and written to w. // -// It is the caller's responsibility to call Close on the WriteCloser when done. +// It is the caller's responsibility to call Close on the Writer when done. // Writes may be buffered and not flushed until Close. // // Callers that wish to set the fields in Writer.Header must do so before @@ -165,26 +165,26 @@ func (z *Writer) Write(p []byte) (int, error) { z.buf[8] = 4 } z.buf[9] = z.OS - n, z.err = z.w.Write(z.buf[:10]) + _, z.err = z.w.Write(z.buf[:10]) if z.err != nil { - return n, z.err + return 0, z.err } if z.Extra != nil { z.err = z.writeBytes(z.Extra) if z.err != nil { - return n, z.err + return 0, z.err } } if z.Name != "" { z.err = z.writeString(z.Name) if z.err != nil { - return n, z.err + return 0, z.err } } if z.Comment != "" { z.err = z.writeString(z.Comment) if z.err != nil { - return n, z.err + return 0, z.err } } if z.compressor == nil { diff --git a/libgo/go/compress/gzip/gzip_test.go b/libgo/go/compress/gzip/gzip_test.go index 865c529..e16aba1 100644 --- a/libgo/go/compress/gzip/gzip_test.go +++ b/libgo/go/compress/gzip/gzip_test.go @@ -7,6 +7,7 @@ package gzip import ( "bufio" "bytes" + "io" "io/ioutil" "reflect" "testing" @@ -233,3 +234,40 @@ func TestWriterReset(t *testing.T) { t.Errorf("buf2 %q != original buf of %q", buf2.String(), buf.String()) } } + +type limitedWriter struct { + N int +} + +func (l *limitedWriter) Write(p []byte) (n int, err error) { + if n := l.N; n < len(p) { + l.N = 0 + return n, io.ErrShortWrite + } + l.N -= len(p) + return len(p), nil +} + +// Write should never return more bytes than the input slice. +func TestLimitedWrite(t *testing.T) { + msg := []byte("a") + + for lim := 2; lim < 20; lim++ { + z := NewWriter(&limitedWriter{lim}) + if n, _ := z.Write(msg); n > len(msg) { + t.Errorf("Write() = %d, want %d or less", n, len(msg)) + } + + z.Reset(&limitedWriter{lim}) + z.Header = Header{ + Comment: "comment", + Extra: []byte("extra"), + ModTime: time.Now(), + Name: "name", + OS: 1, + } + if n, _ := z.Write(msg); n > len(msg) { + t.Errorf("Write() = %d, want %d or less", n, len(msg)) + } + } +} diff --git a/libgo/go/compress/gzip/issue14937_test.go b/libgo/go/compress/gzip/issue14937_test.go index 30c1390..7a19672 100644 --- a/libgo/go/compress/gzip/issue14937_test.go +++ b/libgo/go/compress/gzip/issue14937_test.go @@ -21,6 +21,10 @@ func TestGZIPFilesHaveZeroMTimes(t *testing.T) { if testenv.Builder() == "" { t.Skip("skipping test on non-builder") } + if !testenv.HasSrc() { + t.Skip("skipping; no GOROOT available") + } + goroot, err := filepath.EvalSymlinks(runtime.GOROOT()) if err != nil { t.Fatal("error evaluating GOROOT: ", err) diff --git a/libgo/go/compress/lzw/reader_test.go b/libgo/go/compress/lzw/reader_test.go index f8974de..98bbfbb 100644 --- a/libgo/go/compress/lzw/reader_test.go +++ b/libgo/go/compress/lzw/reader_test.go @@ -66,7 +66,7 @@ var lzwTests = []lzwTest{ "\x54\x9e\x08\x29\xf2\x44\x8a\x93\x27\x54\x04", io.ErrUnexpectedEOF, }, - // This example comes from http://en.wikipedia.org/wiki/Graphics_Interchange_Format. + // This example comes from https://en.wikipedia.org/wiki/Graphics_Interchange_Format. { "gif;LSB;8", "\x28\xff\xff\xff\x28\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", diff --git a/libgo/go/compress/zlib/reader_test.go b/libgo/go/compress/zlib/reader_test.go index 7e27aec..70e33ba 100644 --- a/libgo/go/compress/zlib/reader_test.go +++ b/libgo/go/compress/zlib/reader_test.go @@ -19,7 +19,7 @@ type zlibTest struct { } // Compare-to-golden test data was generated by the ZLIB example program at -// http://www.zlib.net/zpipe.c +// https://www.zlib.net/zpipe.c var zlibTests = []zlibTest{ { diff --git a/libgo/go/compress/zlib/writer.go b/libgo/go/compress/zlib/writer.go index 1620c00..a7b2194 100644 --- a/libgo/go/compress/zlib/writer.go +++ b/libgo/go/compress/zlib/writer.go @@ -38,7 +38,7 @@ type Writer struct { // NewWriter creates a new Writer. // Writes to the returned Writer are compressed and written to w. // -// It is the caller's responsibility to call Close on the WriteCloser when done. +// It is the caller's responsibility to call Close on the Writer when done. // Writes may be buffered and not flushed until Close. func NewWriter(w io.Writer) *Writer { z, _ := NewWriterLevelDict(w, DefaultCompression, nil) diff --git a/libgo/go/container/heap/heap.go b/libgo/go/container/heap/heap.go index b2c6427..67b5efc 100644 --- a/libgo/go/container/heap/heap.go +++ b/libgo/go/container/heap/heap.go @@ -18,7 +18,9 @@ package heap import "sort" -// Any type that implements heap.Interface may be used as a +// The Interface type describes the requirements +// for a type using the routines in this package. +// Any type that implements it may be used as a // min-heap with the following invariants (established after // Init has been called or if the data is empty or sorted): // @@ -33,11 +35,10 @@ type Interface interface { Pop() interface{} // remove and return element Len() - 1. } -// A heap must be initialized before any of the heap operations -// can be used. Init is idempotent with respect to the heap invariants +// Init establishes the heap invariants required by the other routines in this package. +// Init is idempotent with respect to the heap invariants // and may be called whenever the heap invariants may have been invalidated. // Its complexity is O(n) where n = h.Len(). -// func Init(h Interface) { // heapify n := h.Len() diff --git a/libgo/go/context/benchmark_test.go b/libgo/go/context/benchmark_test.go index 3c526dd..5d56863 100644 --- a/libgo/go/context/benchmark_test.go +++ b/libgo/go/context/benchmark_test.go @@ -13,6 +13,30 @@ import ( "time" ) +func BenchmarkCommonParentCancel(b *testing.B) { + root := WithValue(Background(), "key", "value") + shared, sharedcancel := WithCancel(root) + defer sharedcancel() + + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + x := 0 + for pb.Next() { + ctx, cancel := WithCancel(shared) + if ctx.Value("key").(string) != "value" { + b.Fatal("should not be reached") + } + for i := 0; i < 100; i++ { + x /= x + 1 + } + cancel() + for i := 0; i < 100; i++ { + x /= x + 1 + } + } + }) +} + func BenchmarkWithTimeout(b *testing.B) { for concurrency := 40; concurrency <= 4e5; concurrency *= 100 { name := fmt.Sprintf("concurrency=%d", concurrency) @@ -96,3 +120,21 @@ func buildContextTree(root Context, depth int) { root, _ = WithCancel(root) } } + +func BenchmarkCheckCanceled(b *testing.B) { + ctx, cancel := WithCancel(Background()) + cancel() + b.Run("Err", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ctx.Err() + } + }) + b.Run("Done", func(b *testing.B) { + for i := 0; i < b.N; i++ { + select { + case <-ctx.Done(): + default: + } + } + }) +} diff --git a/libgo/go/context/context.go b/libgo/go/context/context.go index 06580e0..1b4fa41 100644 --- a/libgo/go/context/context.go +++ b/libgo/go/context/context.go @@ -334,8 +334,9 @@ func (c *cancelCtx) Done() <-chan struct{} { func (c *cancelCtx) Err() error { c.mu.Lock() - defer c.mu.Unlock() - return c.err + err := c.err + c.mu.Unlock() + return err } func (c *cancelCtx) String() string { diff --git a/libgo/go/context/example_test.go b/libgo/go/context/example_test.go index b2c2aa9..2b28b57 100644 --- a/libgo/go/context/example_test.go +++ b/libgo/go/context/example_test.go @@ -93,6 +93,8 @@ func ExampleWithTimeout() { // context deadline exceeded } +// This example demonstrates how a value can be passed to the context +// and also how to retrieve it if it exists. func ExampleWithValue() { type favContextKey string diff --git a/libgo/go/crypto/aes/aes_gcm.go b/libgo/go/crypto/aes/aes_gcm.go index 3e5e235..3888010 100644 --- a/libgo/go/crypto/aes/aes_gcm.go +++ b/libgo/go/crypto/aes/aes_gcm.go @@ -3,21 +3,18 @@ // license that can be found in the LICENSE file. // +build ignore -// -build amd64 +// -build amd64 arm64 package aes import ( "crypto/cipher" + subtleoverlap "crypto/internal/subtle" "crypto/subtle" "errors" ) -// The following functions are defined in gcm_amd64.s. -func hasGCMAsm() bool - -//go:noescape -func aesEncBlock(dst, src *[16]byte, ks []uint32) +// The following functions are defined in gcm_*.s. //go:noescape func gcmAesInit(productTable *[256]byte, ks []uint32) @@ -37,6 +34,7 @@ func gcmAesFinish(productTable *[256]byte, tagMask, T *[16]byte, pLen, dLen uint const ( gcmBlockSize = 16 gcmTagSize = 16 + gcmMinimumTagSize = 12 // NIST SP 800-38D recommends tags with 12 or more bytes. gcmStandardNonceSize = 12 ) @@ -54,8 +52,8 @@ var _ gcmAble = (*aesCipherGCM)(nil) // NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only // called by crypto/cipher.NewGCM via the gcmAble interface. -func (c *aesCipherGCM) NewGCM(nonceSize int) (cipher.AEAD, error) { - g := &gcmAsm{ks: c.enc, nonceSize: nonceSize} +func (c *aesCipherGCM) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) { + g := &gcmAsm{ks: c.enc, nonceSize: nonceSize, tagSize: tagSize} gcmAesInit(&g.productTable, g.ks) return g, nil } @@ -69,14 +67,16 @@ type gcmAsm struct { productTable [256]byte // nonceSize contains the expected size of the nonce, in bytes. nonceSize int + // tagSize contains the size of the tag, in bytes. + tagSize int } func (g *gcmAsm) NonceSize() int { return g.nonceSize } -func (*gcmAsm) Overhead() int { - return gcmTagSize +func (g *gcmAsm) Overhead() int { + return g.tagSize } // sliceForAppend takes a slice and a requested number of bytes. It returns a @@ -98,10 +98,10 @@ func sliceForAppend(in []byte, n int) (head, tail []byte) { // details. func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte { if len(nonce) != g.nonceSize { - panic("cipher: incorrect nonce length given to GCM") + panic("crypto/cipher: incorrect nonce length given to GCM") } if uint64(len(plaintext)) > ((1<<32)-2)*BlockSize { - panic("cipher: message too large for GCM") + panic("crypto/cipher: message too large for GCM") } var counter, tagMask [gcmBlockSize]byte @@ -116,12 +116,15 @@ func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte { gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0)) } - aesEncBlock(&tagMask, &counter, g.ks) + encryptBlockAsm(len(g.ks)/4-1, &g.ks[0], &tagMask[0], &counter[0]) var tagOut [gcmTagSize]byte gcmAesData(&g.productTable, data, &tagOut) - ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize) + ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize) + if subtleoverlap.InexactOverlap(out[:len(plaintext)], plaintext) { + panic("crypto/cipher: invalid buffer overlap") + } if len(plaintext) > 0 { gcmAesEnc(&g.productTable, out, plaintext, &counter, &tagOut, g.ks) } @@ -135,18 +138,23 @@ func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte { // for details. func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { if len(nonce) != g.nonceSize { - panic("cipher: incorrect nonce length given to GCM") + panic("crypto/cipher: incorrect nonce length given to GCM") + } + // Sanity check to prevent the authentication from always succeeding if an implementation + // leaves tagSize uninitialized, for example. + if g.tagSize < gcmMinimumTagSize { + panic("crypto/cipher: incorrect GCM tag size") } - if len(ciphertext) < gcmTagSize { + if len(ciphertext) < g.tagSize { return nil, errOpen } - if uint64(len(ciphertext)) > ((1<<32)-2)*BlockSize+gcmTagSize { + if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(BlockSize)+uint64(g.tagSize) { return nil, errOpen } - tag := ciphertext[len(ciphertext)-gcmTagSize:] - ciphertext = ciphertext[:len(ciphertext)-gcmTagSize] + tag := ciphertext[len(ciphertext)-g.tagSize:] + ciphertext = ciphertext[:len(ciphertext)-g.tagSize] // See GCM spec, section 7.1. var counter, tagMask [gcmBlockSize]byte @@ -161,18 +169,21 @@ func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0)) } - aesEncBlock(&tagMask, &counter, g.ks) + encryptBlockAsm(len(g.ks)/4-1, &g.ks[0], &tagMask[0], &counter[0]) var expectedTag [gcmTagSize]byte gcmAesData(&g.productTable, data, &expectedTag) ret, out := sliceForAppend(dst, len(ciphertext)) + if subtleoverlap.InexactOverlap(out, ciphertext) { + panic("crypto/cipher: invalid buffer overlap") + } if len(ciphertext) > 0 { gcmAesDec(&g.productTable, out, ciphertext, &counter, &expectedTag, g.ks) } gcmAesFinish(&g.productTable, &tagMask, &expectedTag, uint64(len(ciphertext)), uint64(len(data))) - if subtle.ConstantTimeCompare(expectedTag[:], tag) != 1 { + if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 { for i := range out { out[i] = 0 } diff --git a/libgo/go/crypto/aes/aes_test.go b/libgo/go/crypto/aes/aes_test.go index 2814496..bedc2da 100644 --- a/libgo/go/crypto/aes/aes_test.go +++ b/libgo/go/crypto/aes/aes_test.go @@ -122,7 +122,7 @@ func TestTd(t *testing.T) { } // Test vectors are from FIPS 197: -// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf +// https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf // Appendix A of FIPS 197: Key expansion examples type KeyTest struct { diff --git a/libgo/go/crypto/aes/block.go b/libgo/go/crypto/aes/block.go index 41ea9cf..8647019 100644 --- a/libgo/go/crypto/aes/block.go +++ b/libgo/go/crypto/aes/block.go @@ -31,8 +31,8 @@ // // See FIPS 197 for specification, and see Daemen and Rijmen's Rijndael submission // for implementation details. -// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf -// http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf +// https://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf +// https://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf package aes diff --git a/libgo/go/crypto/aes/cbc_s390x.go b/libgo/go/crypto/aes/cbc_s390x.go index 6b011b2..67e928e 100644 --- a/libgo/go/crypto/aes/cbc_s390x.go +++ b/libgo/go/crypto/aes/cbc_s390x.go @@ -8,6 +8,7 @@ package aes import ( "crypto/cipher" + "crypto/internal/subtle" ) // Assert that aesCipherAsm implements the cbcEncAble and cbcDecAble interfaces. @@ -50,6 +51,9 @@ func (x *cbc) CryptBlocks(dst, src []byte) { if len(dst) < len(src) { panic("crypto/cipher: output smaller than input") } + if subtle.InexactOverlap(dst[:len(src)], src) { + panic("crypto/cipher: invalid buffer overlap") + } if len(src) > 0 { cryptBlocksChain(x.c, &x.iv[0], &x.b.key[0], &dst[0], &src[0], len(src)) } diff --git a/libgo/go/crypto/aes/cipher.go b/libgo/go/crypto/aes/cipher.go index c5a8e91..bb93fbb 100644 --- a/libgo/go/crypto/aes/cipher.go +++ b/libgo/go/crypto/aes/cipher.go @@ -6,6 +6,7 @@ package aes import ( "crypto/cipher" + "crypto/internal/subtle" "strconv" ) @@ -57,6 +58,9 @@ func (c *aesCipher) Encrypt(dst, src []byte) { if len(dst) < BlockSize { panic("crypto/aes: output not full block") } + if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) { + panic("crypto/aes: invalid buffer overlap") + } encryptBlockGo(c.enc, dst, src) } @@ -67,5 +71,8 @@ func (c *aesCipher) Decrypt(dst, src []byte) { if len(dst) < BlockSize { panic("crypto/aes: output not full block") } + if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) { + panic("crypto/aes: invalid buffer overlap") + } decryptBlockGo(c.dec, dst, src) } diff --git a/libgo/go/crypto/aes/cipher_amd64.go b/libgo/go/crypto/aes/cipher_asm.go index fbd157e..dc4251a 100644 --- a/libgo/go/crypto/aes/cipher_amd64.go +++ b/libgo/go/crypto/aes/cipher_asm.go @@ -2,33 +2,42 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build ignore +// +build ignore_for_gccgo +// +build amd64 arm64 package aes import ( "crypto/cipher" - "crypto/internal/cipherhw" + "crypto/internal/subtle" + "internal/cpu" ) -// defined in asm_amd64.s +// defined in asm_*.s + +//go:noescape func encryptBlockAsm(nr int, xk *uint32, dst, src *byte) + +//go:noescape func decryptBlockAsm(nr int, xk *uint32, dst, src *byte) + +//go:noescape func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32) type aesCipherAsm struct { aesCipher } -var useAsm = cipherhw.AESGCMSupport() +var supportsAES = cpu.X86.HasAES || cpu.ARM64.HasAES +var supportsGFMUL = cpu.X86.HasPCLMULQDQ || cpu.ARM64.HasPMULL func newCipher(key []byte) (cipher.Block, error) { - if !useAsm { + if !supportsAES { return newCipherGeneric(key) } n := len(key) + 28 c := aesCipherAsm{aesCipher{make([]uint32, n), make([]uint32, n)}} - rounds := 10 + var rounds int switch len(key) { case 128 / 8: rounds = 10 @@ -37,11 +46,11 @@ func newCipher(key []byte) (cipher.Block, error) { case 256 / 8: rounds = 14 } + expandKeyAsm(rounds, &key[0], &c.enc[0], &c.dec[0]) - if hasGCMAsm() { + if supportsAES && supportsGFMUL { return &aesCipherGCM{c}, nil } - return &c, nil } @@ -54,6 +63,9 @@ func (c *aesCipherAsm) Encrypt(dst, src []byte) { if len(dst) < BlockSize { panic("crypto/aes: output not full block") } + if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) { + panic("crypto/aes: invalid buffer overlap") + } encryptBlockAsm(len(c.enc)/4-1, &c.enc[0], &dst[0], &src[0]) } @@ -64,13 +76,16 @@ func (c *aesCipherAsm) Decrypt(dst, src []byte) { if len(dst) < BlockSize { panic("crypto/aes: output not full block") } + if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) { + panic("crypto/aes: invalid buffer overlap") + } decryptBlockAsm(len(c.dec)/4-1, &c.dec[0], &dst[0], &src[0]) } // expandKey is used by BenchmarkExpand to ensure that the asm implementation // of key expansion is used for the benchmark when it is available. func expandKey(key []byte, enc, dec []uint32) { - if useAsm { + if supportsAES { rounds := 10 // rounds needed for AES128 switch len(key) { case 192 / 8: diff --git a/libgo/go/crypto/aes/cipher_generic.go b/libgo/go/crypto/aes/cipher_generic.go index 98169bf..19a930f0 100644 --- a/libgo/go/crypto/aes/cipher_generic.go +++ b/libgo/go/crypto/aes/cipher_generic.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// -build !amd64,!s390x,!ppc64le +// -build !amd64,!s390x,!ppc64le,!arm64 package aes diff --git a/libgo/go/crypto/aes/cipher_ppc64le.go b/libgo/go/crypto/aes/cipher_ppc64le.go index 1d16fe0..599ea82 100644 --- a/libgo/go/crypto/aes/cipher_ppc64le.go +++ b/libgo/go/crypto/aes/cipher_ppc64le.go @@ -8,28 +8,24 @@ package aes import ( "crypto/cipher" + "crypto/internal/subtle" ) // defined in asm_ppc64le.s //go:noescape - func setEncryptKeyAsm(key *byte, keylen int, enc *uint32) int //go:noescape - func setDecryptKeyAsm(key *byte, keylen int, dec *uint32) int //go:noescape - func doEncryptKeyAsm(key *byte, keylen int, dec *uint32) int //go:noescape - func encryptBlockAsm(dst, src *byte, enc *uint32) //go:noescape - func decryptBlockAsm(dst, src *byte, dec *uint32) type aesCipherAsm struct { @@ -61,6 +57,9 @@ func (c *aesCipherAsm) Encrypt(dst, src []byte) { if len(dst) < BlockSize { panic("crypto/aes: output not full block") } + if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) { + panic("crypto/aes: invalid buffer overlap") + } encryptBlockAsm(&dst[0], &src[0], &c.enc[0]) } @@ -71,6 +70,9 @@ func (c *aesCipherAsm) Decrypt(dst, src []byte) { if len(dst) < BlockSize { panic("crypto/aes: output not full block") } + if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) { + panic("crypto/aes: invalid buffer overlap") + } decryptBlockAsm(&dst[0], &src[0], &c.dec[0]) } diff --git a/libgo/go/crypto/aes/cipher_s390x.go b/libgo/go/crypto/aes/cipher_s390x.go index 253ce89..b885a45 100644 --- a/libgo/go/crypto/aes/cipher_s390x.go +++ b/libgo/go/crypto/aes/cipher_s390x.go @@ -8,7 +8,8 @@ package aes import ( "crypto/cipher" - "crypto/internal/cipherhw" + "crypto/internal/subtle" + "internal/cpu" ) type code int @@ -21,9 +22,9 @@ const ( ) type aesCipherAsm struct { - function code // code for cipher message instruction - key []byte // key (128, 192 or 256 bytes) - storage [256]byte // array backing key slice + function code // code for cipher message instruction + key []byte // key (128, 192 or 256 bits) + storage [32]byte // array backing key slice } // cryptBlocks invokes the cipher message (KM) instruction with @@ -32,10 +33,12 @@ type aesCipherAsm struct { //go:noescape func cryptBlocks(c code, key, dst, src *byte, length int) -var useAsm = cipherhw.AESGCMSupport() - func newCipher(key []byte) (cipher.Block, error) { - if !useAsm { + // The aesCipherAsm type implements the cbcEncAble, cbcDecAble, + // ctrAble and gcmAble interfaces. We therefore need to check + // for all the features required to implement these modes. + // Keep in sync with crypto/tls/common.go. + if !(cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)) { return newCipherGeneric(key) } @@ -67,6 +70,9 @@ func (c *aesCipherAsm) Encrypt(dst, src []byte) { if len(dst) < BlockSize { panic("crypto/aes: output not full block") } + if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) { + panic("crypto/aes: invalid buffer overlap") + } cryptBlocks(c.function, &c.key[0], &dst[0], &src[0], BlockSize) } @@ -77,6 +83,9 @@ func (c *aesCipherAsm) Decrypt(dst, src []byte) { if len(dst) < BlockSize { panic("crypto/aes: output not full block") } + if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) { + panic("crypto/aes: invalid buffer overlap") + } // The decrypt function code is equal to the function code + 128. cryptBlocks(c.function+128, &c.key[0], &dst[0], &src[0], BlockSize) } diff --git a/libgo/go/crypto/aes/const.go b/libgo/go/crypto/aes/const.go index cbac5ff..4eca4b9 100644 --- a/libgo/go/crypto/aes/const.go +++ b/libgo/go/crypto/aes/const.go @@ -15,7 +15,7 @@ package aes // This file contains AES constants - 8720 bytes of initialized data. -// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf +// https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf // AES is based on the mathematical behavior of binary polynomials // (polynomials over GF(2)) modulo the irreducible polynomial x⁸ + x⁴ + x³ + x + 1. diff --git a/libgo/go/crypto/aes/ctr_s390x.go b/libgo/go/crypto/aes/ctr_s390x.go index 7892f79..a3c43a3 100644 --- a/libgo/go/crypto/aes/ctr_s390x.go +++ b/libgo/go/crypto/aes/ctr_s390x.go @@ -8,6 +8,7 @@ package aes import ( "crypto/cipher" + "crypto/internal/subtle" "unsafe" ) @@ -66,9 +67,11 @@ func (c *aesctr) refill() { } func (c *aesctr) XORKeyStream(dst, src []byte) { - if len(src) > 0 { - // Assert len(dst) >= len(src) - _ = dst[len(src)-1] + if len(dst) < len(src) { + panic("crypto/cipher: output smaller than input") + } + if subtle.InexactOverlap(dst[:len(src)], src) { + panic("crypto/cipher: invalid buffer overlap") } for len(src) > 0 { if len(c.buffer) == 0 { diff --git a/libgo/go/crypto/aes/gcm_s390x.go b/libgo/go/crypto/aes/gcm_s390x.go index ba6d7ce..18b0e82 100644 --- a/libgo/go/crypto/aes/gcm_s390x.go +++ b/libgo/go/crypto/aes/gcm_s390x.go @@ -8,8 +8,10 @@ package aes import ( "crypto/cipher" + subtleoverlap "crypto/internal/subtle" "crypto/subtle" "errors" + "internal/cpu" ) // This file contains two implementations of AES-GCM. The first implementation @@ -60,11 +62,13 @@ type gcmAsm struct { block *aesCipherAsm hashKey gcmHashKey nonceSize int + tagSize int } const ( gcmBlockSize = 16 gcmTagSize = 16 + gcmMinimumTagSize = 12 // NIST SP 800-38D recommends tags with 12 or more bytes. gcmStandardNonceSize = 12 ) @@ -75,15 +79,16 @@ var _ gcmAble = (*aesCipherAsm)(nil) // NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only // called by crypto/cipher.NewGCM via the gcmAble interface. -func (c *aesCipherAsm) NewGCM(nonceSize int) (cipher.AEAD, error) { +func (c *aesCipherAsm) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) { var hk gcmHashKey c.Encrypt(hk[:], hk[:]) g := gcmAsm{ block: c, hashKey: hk, nonceSize: nonceSize, + tagSize: tagSize, } - if hasKMA { + if cpu.S390X.HasAESGCM { g := gcmKMA{g} return &g, nil } @@ -94,8 +99,8 @@ func (g *gcmAsm) NonceSize() int { return g.nonceSize } -func (*gcmAsm) Overhead() int { - return gcmTagSize +func (g *gcmAsm) Overhead() int { + return g.tagSize } // sliceForAppend takes a slice and a requested number of bytes. It returns a @@ -218,13 +223,16 @@ func (g *gcmAsm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSi // details. func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte { if len(nonce) != g.nonceSize { - panic("cipher: incorrect nonce length given to GCM") + panic("crypto/cipher: incorrect nonce length given to GCM") } if uint64(len(plaintext)) > ((1<<32)-2)*BlockSize { - panic("cipher: message too large for GCM") + panic("crypto/cipher: message too large for GCM") } - ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize) + ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize) + if subtleoverlap.InexactOverlap(out[:len(plaintext)], plaintext) { + panic("crypto/cipher: invalid buffer overlap") + } counter := g.deriveCounter(nonce) @@ -232,8 +240,10 @@ func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte { g.block.Encrypt(tagMask[:], counter[:]) counter.inc() + var tagOut [gcmTagSize]byte g.counterCrypt(out, plaintext, &counter) - g.auth(out[len(plaintext):], out[:len(plaintext)], data, &tagMask) + g.auth(tagOut[:], out[:len(plaintext)], data, &tagMask) + copy(out[len(plaintext):], tagOut[:]) return ret } @@ -242,17 +252,22 @@ func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte { // for details. func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { if len(nonce) != g.nonceSize { - panic("cipher: incorrect nonce length given to GCM") + panic("crypto/cipher: incorrect nonce length given to GCM") + } + // Sanity check to prevent the authentication from always succeeding if an implementation + // leaves tagSize uninitialized, for example. + if g.tagSize < gcmMinimumTagSize { + panic("crypto/cipher: incorrect GCM tag size") } - if len(ciphertext) < gcmTagSize { + if len(ciphertext) < g.tagSize { return nil, errOpen } - if uint64(len(ciphertext)) > ((1<<32)-2)*BlockSize+gcmTagSize { + if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(BlockSize)+uint64(g.tagSize) { return nil, errOpen } - tag := ciphertext[len(ciphertext)-gcmTagSize:] - ciphertext = ciphertext[:len(ciphertext)-gcmTagSize] + tag := ciphertext[len(ciphertext)-g.tagSize:] + ciphertext = ciphertext[:len(ciphertext)-g.tagSize] counter := g.deriveCounter(nonce) @@ -264,8 +279,11 @@ func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { g.auth(expectedTag[:], ciphertext, data, &tagMask) ret, out := sliceForAppend(dst, len(ciphertext)) + if subtleoverlap.InexactOverlap(out, ciphertext) { + panic("crypto/cipher: invalid buffer overlap") + } - if subtle.ConstantTimeCompare(expectedTag[:], tag) != 1 { + if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 { // The AESNI code decrypts and authenticates concurrently, and // so overwrites dst in the event of a tag mismatch. That // behavior is mimicked here in order to be consistent across @@ -280,13 +298,6 @@ func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { return ret, nil } -// supportsKMA reports whether the message-security-assist 8 facility is available. -// This function call may be expensive so hasKMA should be queried instead. -func supportsKMA() bool - -// hasKMA contains the result of supportsKMA. -var hasKMA = supportsKMA() - // gcmKMA implements the cipher.AEAD interface using the KMA instruction. It should // only be used if hasKMA is true. type gcmKMA struct { @@ -312,13 +323,16 @@ func kmaGCM(fn code, key, dst, src, aad []byte, tag *[16]byte, cnt *gcmCount) // details. func (g *gcmKMA) Seal(dst, nonce, plaintext, data []byte) []byte { if len(nonce) != g.nonceSize { - panic("cipher: incorrect nonce length given to GCM") + panic("crypto/cipher: incorrect nonce length given to GCM") } if uint64(len(plaintext)) > ((1<<32)-2)*BlockSize { - panic("cipher: message too large for GCM") + panic("crypto/cipher: message too large for GCM") } - ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize) + ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize) + if subtleoverlap.InexactOverlap(out[:len(plaintext)], plaintext) { + panic("crypto/cipher: invalid buffer overlap") + } counter := g.deriveCounter(nonce) fc := g.block.function | kmaLAAD | kmaLPC @@ -334,18 +348,25 @@ func (g *gcmKMA) Seal(dst, nonce, plaintext, data []byte) []byte { // for details. func (g *gcmKMA) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { if len(nonce) != g.nonceSize { - panic("cipher: incorrect nonce length given to GCM") + panic("crypto/cipher: incorrect nonce length given to GCM") } - if len(ciphertext) < gcmTagSize { + if len(ciphertext) < g.tagSize { return nil, errOpen } - if uint64(len(ciphertext)) > ((1<<32)-2)*BlockSize+gcmTagSize { + if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(BlockSize)+uint64(g.tagSize) { return nil, errOpen } - tag := ciphertext[len(ciphertext)-gcmTagSize:] - ciphertext = ciphertext[:len(ciphertext)-gcmTagSize] + tag := ciphertext[len(ciphertext)-g.tagSize:] + ciphertext = ciphertext[:len(ciphertext)-g.tagSize] ret, out := sliceForAppend(dst, len(ciphertext)) + if subtleoverlap.InexactOverlap(out, ciphertext) { + panic("crypto/cipher: invalid buffer overlap") + } + + if g.tagSize < gcmMinimumTagSize { + panic("crypto/cipher: incorrect GCM tag size") + } counter := g.deriveCounter(nonce) fc := g.block.function | kmaLAAD | kmaLPC | kmaDecrypt @@ -353,7 +374,7 @@ func (g *gcmKMA) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { var expectedTag [gcmTagSize]byte kmaGCM(fc, g.block.key, out[:len(ciphertext)], ciphertext, data, &expectedTag, &counter) - if subtle.ConstantTimeCompare(expectedTag[:], tag) != 1 { + if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 { // The AESNI code decrypts and authenticates concurrently, and // so overwrites dst in the event of a tag mismatch. That // behavior is mimicked here in order to be consistent across diff --git a/libgo/go/crypto/aes/modes.go b/libgo/go/crypto/aes/modes.go index 1623fc1..5c0b08e 100644 --- a/libgo/go/crypto/aes/modes.go +++ b/libgo/go/crypto/aes/modes.go @@ -12,7 +12,7 @@ import ( // implementation of GCM through the AEAD interface. // See crypto/cipher/gcm.go. type gcmAble interface { - NewGCM(size int) (cipher.AEAD, error) + NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) } // cbcEncAble is implemented by cipher.Blocks that can provide an optimized diff --git a/libgo/go/crypto/aes/modes_test.go b/libgo/go/crypto/aes/modes_test.go index 8c2e5f0..a3364c9 100644 --- a/libgo/go/crypto/aes/modes_test.go +++ b/libgo/go/crypto/aes/modes_test.go @@ -25,7 +25,7 @@ type testBlock struct{} func (*testBlock) BlockSize() int { return 0 } func (*testBlock) Encrypt(a, b []byte) {} func (*testBlock) Decrypt(a, b []byte) {} -func (*testBlock) NewGCM(int) (cipher.AEAD, error) { +func (*testBlock) NewGCM(int, int) (cipher.AEAD, error) { return &testAEAD{}, nil } func (*testBlock) NewCBCEncrypter([]byte) cipher.BlockMode { diff --git a/libgo/go/crypto/cipher/cbc.go b/libgo/go/crypto/cipher/cbc.go index 0367d59..0d07192 100644 --- a/libgo/go/crypto/cipher/cbc.go +++ b/libgo/go/crypto/cipher/cbc.go @@ -11,6 +11,8 @@ package cipher +import "crypto/internal/subtle" + type cbc struct { b Block blockSize int @@ -59,6 +61,9 @@ func (x *cbcEncrypter) CryptBlocks(dst, src []byte) { if len(dst) < len(src) { panic("crypto/cipher: output smaller than input") } + if subtle.InexactOverlap(dst[:len(src)], src) { + panic("crypto/cipher: invalid buffer overlap") + } iv := x.iv @@ -116,6 +121,9 @@ func (x *cbcDecrypter) CryptBlocks(dst, src []byte) { if len(dst) < len(src) { panic("crypto/cipher: output smaller than input") } + if subtle.InexactOverlap(dst[:len(src)], src) { + panic("crypto/cipher: invalid buffer overlap") + } if len(src) == 0 { return } diff --git a/libgo/go/crypto/cipher/cfb.go b/libgo/go/crypto/cipher/cfb.go index 9b4eebf..80c9bc2 100644 --- a/libgo/go/crypto/cipher/cfb.go +++ b/libgo/go/crypto/cipher/cfb.go @@ -6,6 +6,8 @@ package cipher +import "crypto/internal/subtle" + type cfb struct { b Block next []byte @@ -16,6 +18,12 @@ type cfb struct { } func (x *cfb) XORKeyStream(dst, src []byte) { + if len(dst) < len(src) { + panic("crypto/cipher: output smaller than input") + } + if subtle.InexactOverlap(dst[:len(src)], src) { + panic("crypto/cipher: invalid buffer overlap") + } for len(src) > 0 { if x.outUsed == len(x.out) { x.b.Encrypt(x.out, x.next) diff --git a/libgo/go/crypto/cipher/cfb_test.go b/libgo/go/crypto/cipher/cfb_test.go index 9b544bb..ecb716d 100644 --- a/libgo/go/crypto/cipher/cfb_test.go +++ b/libgo/go/crypto/cipher/cfb_test.go @@ -14,7 +14,7 @@ import ( ) // cfbTests contains the test vectors from -// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf, section +// https://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf, section // F.3.13. var cfbTests = []struct { key, iv, plaintext, ciphertext string diff --git a/libgo/go/crypto/cipher/cipher.go b/libgo/go/crypto/cipher/cipher.go index 31c14d7..7e1a4de 100644 --- a/libgo/go/crypto/cipher/cipher.go +++ b/libgo/go/crypto/cipher/cipher.go @@ -4,7 +4,7 @@ // Package cipher implements standard block cipher modes that can be wrapped // around low-level block cipher implementations. -// See http://csrc.nist.gov/groups/ST/toolkit/BCM/current_modes.html +// See https://csrc.nist.gov/groups/ST/toolkit/BCM/current_modes.html // and NIST Special Publication 800-38A. package cipher diff --git a/libgo/go/crypto/cipher/ctr.go b/libgo/go/crypto/cipher/ctr.go index 75f46cf..cba028d 100644 --- a/libgo/go/crypto/cipher/ctr.go +++ b/libgo/go/crypto/cipher/ctr.go @@ -12,6 +12,8 @@ package cipher +import "crypto/internal/subtle" + type ctr struct { b Block ctr []byte @@ -71,6 +73,12 @@ func (x *ctr) refill() { } func (x *ctr) XORKeyStream(dst, src []byte) { + if len(dst) < len(src) { + panic("crypto/cipher: output smaller than input") + } + if subtle.InexactOverlap(dst[:len(src)], src) { + panic("crypto/cipher: invalid buffer overlap") + } for len(src) > 0 { if x.outUsed >= len(x.out)-x.b.BlockSize() { x.refill() diff --git a/libgo/go/crypto/cipher/gcm.go b/libgo/go/crypto/cipher/gcm.go index 28f3ddd..6321e9e 100644 --- a/libgo/go/crypto/cipher/gcm.go +++ b/libgo/go/crypto/cipher/gcm.go @@ -5,6 +5,7 @@ package cipher import ( + subtleoverlap "crypto/internal/subtle" "crypto/subtle" "errors" ) @@ -26,8 +27,8 @@ type AEAD interface { // slice. The nonce must be NonceSize() bytes long and unique for all // time, for a given key. // - // The plaintext and dst must overlap exactly or not at all. To reuse - // plaintext's storage for the encrypted output, use plaintext[:0] as dst. + // To reuse plaintext's storage for the encrypted output, use plaintext[:0] + // as dst. Otherwise, the remaining capacity of dst must not overlap plaintext. Seal(dst, nonce, plaintext, additionalData []byte) []byte // Open decrypts and authenticates ciphertext, authenticates the @@ -36,8 +37,8 @@ type AEAD interface { // bytes long and both it and the additional data must match the // value passed to Seal. // - // The ciphertext and dst must overlap exactly or not at all. To reuse - // ciphertext's storage for the decrypted output, use ciphertext[:0] as dst. + // To reuse ciphertext's storage for the decrypted output, use ciphertext[:0] + // as dst. Otherwise, the remaining capacity of dst must not overlap plaintext. // // Even if the function fails, the contents of dst, up to its capacity, // may be overwritten. @@ -48,7 +49,7 @@ type AEAD interface { // implementation of GCM, like crypto/aes. NewGCM will check for this interface // and return the specific AEAD if found. type gcmAble interface { - NewGCM(int) (AEAD, error) + NewGCM(nonceSize, tagSize int) (AEAD, error) } // gcmFieldElement represents a value in GF(2¹²⁸). In order to reflect the GCM @@ -63,10 +64,11 @@ type gcmFieldElement struct { } // gcm represents a Galois Counter Mode with a specific key. See -// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf +// https://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf type gcm struct { cipher Block nonceSize int + tagSize int // productTable contains the first sixteen powers of the key, H. // However, they are in bit reversed order. See NewGCMWithNonceSize. productTable [16]gcmFieldElement @@ -79,7 +81,7 @@ type gcm struct { // An exception is when the underlying Block was created by aes.NewCipher // on systems with hardware support for AES. See the crypto/aes package documentation for details. func NewGCM(cipher Block) (AEAD, error) { - return NewGCMWithNonceSize(cipher, gcmStandardNonceSize) + return newGCMWithNonceAndTagSize(cipher, gcmStandardNonceSize, gcmTagSize) } // NewGCMWithNonceSize returns the given 128-bit, block cipher wrapped in Galois @@ -89,8 +91,28 @@ func NewGCM(cipher Block) (AEAD, error) { // cryptosystem that uses non-standard nonce lengths. All other users should use // NewGCM, which is faster and more resistant to misuse. func NewGCMWithNonceSize(cipher Block, size int) (AEAD, error) { + return newGCMWithNonceAndTagSize(cipher, size, gcmTagSize) +} + +// NewGCMWithTagSize returns the given 128-bit, block cipher wrapped in Galois +// Counter Mode, which generates tags with the given length. +// +// Tag sizes between 12 and 16 bytes are allowed. +// +// Only use this function if you require compatibility with an existing +// cryptosystem that uses non-standard tag lengths. All other users should use +// NewGCM, which is more resistant to misuse. +func NewGCMWithTagSize(cipher Block, tagSize int) (AEAD, error) { + return newGCMWithNonceAndTagSize(cipher, gcmStandardNonceSize, tagSize) +} + +func newGCMWithNonceAndTagSize(cipher Block, nonceSize, tagSize int) (AEAD, error) { + if tagSize < gcmMinimumTagSize || tagSize > gcmBlockSize { + return nil, errors.New("cipher: incorrect tag size given to GCM") + } + if cipher, ok := cipher.(gcmAble); ok { - return cipher.NewGCM(size) + return cipher.NewGCM(nonceSize, tagSize) } if cipher.BlockSize() != gcmBlockSize { @@ -100,7 +122,7 @@ func NewGCMWithNonceSize(cipher Block, size int) (AEAD, error) { var key [gcmBlockSize]byte cipher.Encrypt(key[:], key[:]) - g := &gcm{cipher: cipher, nonceSize: size} + g := &gcm{cipher: cipher, nonceSize: nonceSize, tagSize: tagSize} // We precompute 16 multiples of |key|. However, when we do lookups // into this table we'll be using bits from a field element and @@ -124,6 +146,7 @@ func NewGCMWithNonceSize(cipher Block, size int) (AEAD, error) { const ( gcmBlockSize = 16 gcmTagSize = 16 + gcmMinimumTagSize = 12 // NIST SP 800-38D recommends tags with 12 or more bytes. gcmStandardNonceSize = 12 ) @@ -131,19 +154,22 @@ func (g *gcm) NonceSize() int { return g.nonceSize } -func (*gcm) Overhead() int { - return gcmTagSize +func (g *gcm) Overhead() int { + return g.tagSize } func (g *gcm) Seal(dst, nonce, plaintext, data []byte) []byte { if len(nonce) != g.nonceSize { - panic("cipher: incorrect nonce length given to GCM") + panic("crypto/cipher: incorrect nonce length given to GCM") } if uint64(len(plaintext)) > ((1<<32)-2)*uint64(g.cipher.BlockSize()) { - panic("cipher: message too large for GCM") + panic("crypto/cipher: message too large for GCM") } - ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize) + ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize) + if subtleoverlap.InexactOverlap(out, plaintext) { + panic("crypto/cipher: invalid buffer overlap") + } var counter, tagMask [gcmBlockSize]byte g.deriveCounter(&counter, nonce) @@ -152,7 +178,10 @@ func (g *gcm) Seal(dst, nonce, plaintext, data []byte) []byte { gcmInc32(&counter) g.counterCrypt(out, plaintext, &counter) - g.auth(out[len(plaintext):], out[:len(plaintext)], data, &tagMask) + + var tag [gcmTagSize]byte + g.auth(tag[:], out[:len(plaintext)], data, &tagMask) + copy(out[len(plaintext):], tag[:]) return ret } @@ -161,18 +190,23 @@ var errOpen = errors.New("cipher: message authentication failed") func (g *gcm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { if len(nonce) != g.nonceSize { - panic("cipher: incorrect nonce length given to GCM") + panic("crypto/cipher: incorrect nonce length given to GCM") + } + // Sanity check to prevent the authentication from always succeeding if an implementation + // leaves tagSize uninitialized, for example. + if g.tagSize < gcmMinimumTagSize { + panic("crypto/cipher: incorrect GCM tag size") } - if len(ciphertext) < gcmTagSize { + if len(ciphertext) < g.tagSize { return nil, errOpen } - if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(g.cipher.BlockSize())+gcmTagSize { + if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(g.cipher.BlockSize())+uint64(g.tagSize) { return nil, errOpen } - tag := ciphertext[len(ciphertext)-gcmTagSize:] - ciphertext = ciphertext[:len(ciphertext)-gcmTagSize] + tag := ciphertext[len(ciphertext)-g.tagSize:] + ciphertext = ciphertext[:len(ciphertext)-g.tagSize] var counter, tagMask [gcmBlockSize]byte g.deriveCounter(&counter, nonce) @@ -184,8 +218,11 @@ func (g *gcm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { g.auth(expectedTag[:], ciphertext, data, &tagMask) ret, out := sliceForAppend(dst, len(ciphertext)) + if subtleoverlap.InexactOverlap(out, ciphertext) { + panic("crypto/cipher: invalid buffer overlap") + } - if subtle.ConstantTimeCompare(expectedTag[:], tag) != 1 { + if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 { // The AESNI code decrypts and authenticates concurrently, and // so overwrites dst in the event of a tag mismatch. That // behavior is mimicked here in order to be consistent across @@ -387,6 +424,7 @@ func (g *gcm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSize] } func getUint64(data []byte) uint64 { + _ = data[7] // bounds check hint to compiler; see golang.org/issue/14808 r := uint64(data[0])<<56 | uint64(data[1])<<48 | uint64(data[2])<<40 | @@ -399,6 +437,7 @@ func getUint64(data []byte) uint64 { } func putUint64(out []byte, v uint64) { + _ = out[7] // bounds check hint to compiler; see golang.org/issue/14808 out[0] = byte(v >> 56) out[1] = byte(v >> 48) out[2] = byte(v >> 40) diff --git a/libgo/go/crypto/cipher/gcm_test.go b/libgo/go/crypto/cipher/gcm_test.go index 6878b4c..64d5cc0 100644 --- a/libgo/go/crypto/cipher/gcm_test.go +++ b/libgo/go/crypto/cipher/gcm_test.go @@ -188,6 +188,35 @@ var aesGCMTests = []struct { "0feccdfae8ed65fa31a0858a1c466f79e8aa658c2f3ba93c3f92158b4e30955e1c62580450beff", "b69a7e17bb5af688883274550a4ded0d1aff49a0b18343f4b382f745c163f7f714c9206a32a1ff012427e19431951edd0a755e5f491b0eedfd7df68bbc6085dd2888607a2f998c3e881eb1694109250db28291e71f4ad344a125624fb92e16ea9815047cd1111cabfdc9cb8c3b4b0f40aa91d31774009781231400789ed545404af6c3f76d07ddc984a7bd8f52728159782832e298cc4d529be96d17be898efd83e44dc7b0e2efc645849fd2bba61fef0ae7be0dcab233cc4e2b7ba4e887de9c64b97f2a1818aa54371a8d629dae37975f7784e5e3cc77055ed6e975b1e5f55e6bbacdc9f295ce4ada2c16113cd5b323cf78b7dde39f4a87aa8c141a31174e3584ccbd380cf5ec6d1dba539928b084fa9683e9c0953acf47cc3ac384a2c38914f1da01fb2cfd78905c2b58d36b2574b9df15535d82", }, + // These cases test non-standard tag sizes. + { + "89c54b0d3bc3c397d5039058c220685f", + "bc7f45c00868758d62d4bb4d", + "582670b0baf5540a3775b6615605bd05", + "48d16cda0337105a50e2ed76fd18e114", + "fc2d4c4eee2209ddbba6663c02765e6955e783b00156f5da0446e2970b877f", + }, + { + "bad6049678bf75c9087b3e3ae7e72c13", + "a0a017b83a67d8f1b883e561", + "a1be93012f05a1958440f74a5311f4a1", + "f7c27b51d5367161dc2ff1e9e3edc6f2", + "36f032f7e3dc3275ca22aedcdc68436b99a2227f8bb69d45ea5d8842cd08", + }, + { + "66a3c722ccf9709525650973ecc100a9", + "1621d42d3a6d42a2d2bf9494", + "61fa9dbbed2190fbc2ffabf5d2ea4ff8", + "d7a9b6523b8827068a6354a6d166c6b9", + "fef3b20f40e08a49637cc82f4c89b8603fd5c0132acfab97b5fff651c4", + }, + { + "562ae8aadb8d23e0f271a99a7d1bd4d1", + "f7a5e2399413b89b6ad31aff", + "bbdc3504d803682aa08a773cde5f231a", + "2b9680b886b3efb7c6354b38c63b5373", + "e2b7e5ed5ff27fc8664148f5a628a46dcbf2015184fffb82f2651c36", + }, } func TestAESGCM(t *testing.T) { @@ -201,9 +230,29 @@ func TestAESGCM(t *testing.T) { nonce, _ := hex.DecodeString(test.nonce) plaintext, _ := hex.DecodeString(test.plaintext) ad, _ := hex.DecodeString(test.ad) - aesgcm, err := cipher.NewGCMWithNonceSize(aes, len(nonce)) - if err != nil { - t.Fatal(err) + tagSize := (len(test.result) - len(test.plaintext)) / 2 + + var aesgcm cipher.AEAD + switch { + // Handle non-standard nonce sizes + case tagSize != 16: + aesgcm, err = cipher.NewGCMWithTagSize(aes, tagSize) + if err != nil { + t.Fatal(err) + } + + // Handle non-standard tag sizes + case len(nonce) != 12: + aesgcm, err = cipher.NewGCMWithNonceSize(aes, len(nonce)) + if err != nil { + t.Fatal(err) + } + + default: + aesgcm, err = cipher.NewGCM(aes) + if err != nil { + t.Fatal(err) + } } ct := aesgcm.Seal(nil, nonce, plaintext, ad) @@ -245,6 +294,19 @@ func TestAESGCM(t *testing.T) { } } +func TestGCMInvalidTagSize(t *testing.T) { + key, _ := hex.DecodeString("ab72c77b97cb5fe9a382d9fe81ffdbed") + + aes, _ := aes.NewCipher(key) + + for _, tagSize := range []int{0, 1, aes.BlockSize() + 1} { + aesgcm, err := cipher.NewGCMWithTagSize(aes, tagSize) + if aesgcm != nil || err == nil { + t.Fatalf("NewGCMWithNonceAndTagSize was successful with an invalid %d-byte tag size", tagSize) + } + } +} + func TestTagFailureOverwrite(t *testing.T) { // The AESNI GCM code decrypts and authenticates concurrently and so // overwrites the output buffer before checking the authentication tag. @@ -362,7 +424,7 @@ func TestGCMAsm(t *testing.T) { // generate permutations type pair struct{ align, length int } - lengths := []int{0, 8192, 8193, 8208} + lengths := []int{0, 156, 8192, 8193, 8208} keySizes := []int{16, 24, 32} alignments := []int{0, 1, 2, 3} if testing.Short() { diff --git a/libgo/go/crypto/cipher/ofb.go b/libgo/go/crypto/cipher/ofb.go index 7b35f89..fc47724 100644 --- a/libgo/go/crypto/cipher/ofb.go +++ b/libgo/go/crypto/cipher/ofb.go @@ -6,6 +6,8 @@ package cipher +import "crypto/internal/subtle" + type ofb struct { b Block cipher []byte @@ -54,6 +56,12 @@ func (x *ofb) refill() { } func (x *ofb) XORKeyStream(dst, src []byte) { + if len(dst) < len(src) { + panic("crypto/cipher: output smaller than input") + } + if subtle.InexactOverlap(dst[:len(src)], src) { + panic("crypto/cipher: invalid buffer overlap") + } for len(src) > 0 { if x.outUsed >= len(x.out)-x.b.BlockSize() { x.refill() diff --git a/libgo/go/crypto/des/cipher.go b/libgo/go/crypto/des/cipher.go index 46af5b0..9e6779c 100644 --- a/libgo/go/crypto/des/cipher.go +++ b/libgo/go/crypto/des/cipher.go @@ -6,6 +6,7 @@ package des import ( "crypto/cipher" + "crypto/internal/subtle" "encoding/binary" "strconv" ) @@ -37,9 +38,31 @@ func NewCipher(key []byte) (cipher.Block, error) { func (c *desCipher) BlockSize() int { return BlockSize } -func (c *desCipher) Encrypt(dst, src []byte) { encryptBlock(c.subkeys[:], dst, src) } +func (c *desCipher) Encrypt(dst, src []byte) { + if len(src) < BlockSize { + panic("crypto/des: input not full block") + } + if len(dst) < BlockSize { + panic("crypto/des: output not full block") + } + if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) { + panic("crypto/des: invalid buffer overlap") + } + encryptBlock(c.subkeys[:], dst, src) +} -func (c *desCipher) Decrypt(dst, src []byte) { decryptBlock(c.subkeys[:], dst, src) } +func (c *desCipher) Decrypt(dst, src []byte) { + if len(src) < BlockSize { + panic("crypto/des: input not full block") + } + if len(dst) < BlockSize { + panic("crypto/des: output not full block") + } + if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) { + panic("crypto/des: invalid buffer overlap") + } + decryptBlock(c.subkeys[:], dst, src) +} // A tripleDESCipher is an instance of TripleDES encryption. type tripleDESCipher struct { @@ -62,6 +85,16 @@ func NewTripleDESCipher(key []byte) (cipher.Block, error) { func (c *tripleDESCipher) BlockSize() int { return BlockSize } func (c *tripleDESCipher) Encrypt(dst, src []byte) { + if len(src) < BlockSize { + panic("crypto/des: input not full block") + } + if len(dst) < BlockSize { + panic("crypto/des: output not full block") + } + if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) { + panic("crypto/des: invalid buffer overlap") + } + b := binary.BigEndian.Uint64(src) b = permuteInitialBlock(b) left, right := uint32(b>>32), uint32(b) @@ -87,6 +120,16 @@ func (c *tripleDESCipher) Encrypt(dst, src []byte) { } func (c *tripleDESCipher) Decrypt(dst, src []byte) { + if len(src) < BlockSize { + panic("crypto/des: input not full block") + } + if len(dst) < BlockSize { + panic("crypto/des: output not full block") + } + if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) { + panic("crypto/des: invalid buffer overlap") + } + b := binary.BigEndian.Uint64(src) b = permuteInitialBlock(b) left, right := uint32(b>>32), uint32(b) diff --git a/libgo/go/crypto/dsa/dsa.go b/libgo/go/crypto/dsa/dsa.go index e945855..575314b 100644 --- a/libgo/go/crypto/dsa/dsa.go +++ b/libgo/go/crypto/dsa/dsa.go @@ -11,6 +11,8 @@ import ( "errors" "io" "math/big" + + "crypto/internal/randutil" ) // Parameters represents the domain parameters for a key. These parameters can @@ -195,6 +197,8 @@ func fermatInverse(k, P *big.Int) *big.Int { // Be aware that calling Sign with an attacker-controlled PrivateKey may // require an arbitrary amount of CPU. func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) { + randutil.MaybeReadByte(rand) + // FIPS 186-3, section 4.6 n := priv.Q.BitLen() diff --git a/libgo/go/crypto/ecdsa/ecdsa.go b/libgo/go/crypto/ecdsa/ecdsa.go index 755ed28..2bab14c 100644 --- a/libgo/go/crypto/ecdsa/ecdsa.go +++ b/libgo/go/crypto/ecdsa/ecdsa.go @@ -26,6 +26,8 @@ import ( "errors" "io" "math/big" + + "crypto/internal/randutil" ) // A invertible implements fast inverse mod Curve.Params().N @@ -152,6 +154,8 @@ var errZeroParam = errors.New("zero parameter") // returns the signature as a pair of integers. The security of the private key // depends on the entropy of rand. func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) { + randutil.MaybeReadByte(rand) + // Get min(log2(q) / 2, 256) bits of entropy from rand. entropylen := (priv.Curve.Params().BitSize + 7) / 16 if entropylen > 32 { diff --git a/libgo/go/crypto/ecdsa/ecdsa_test.go b/libgo/go/crypto/ecdsa/ecdsa_test.go index 9224a03..6284e06 100644 --- a/libgo/go/crypto/ecdsa/ecdsa_test.go +++ b/libgo/go/crypto/ecdsa/ecdsa_test.go @@ -213,7 +213,7 @@ func fromHex(s string) *big.Int { func TestVectors(t *testing.T) { // This test runs the full set of NIST test vectors from - // http://csrc.nist.gov/groups/STM/cavp/documents/dss/186-3ecdsatestvectors.zip + // https://csrc.nist.gov/groups/STM/cavp/documents/dss/186-3ecdsatestvectors.zip // // The SigVer.rsp file has been edited to remove test vectors for // unsupported algorithms and has been compressed. diff --git a/libgo/go/crypto/ecdsa/example_test.go b/libgo/go/crypto/ecdsa/example_test.go new file mode 100644 index 0000000..7c7fb1b --- /dev/null +++ b/libgo/go/crypto/ecdsa/example_test.go @@ -0,0 +1,34 @@ +// Copyright 2018 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 gccgo_examples + +package ecdsa_test + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "fmt" +) + +func Example() { + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + panic(err) + } + + msg := "hello, world" + hash := sha256.Sum256([]byte(msg)) + + r, s, err := ecdsa.Sign(rand.Reader, privateKey, hash[:]) + if err != nil { + panic(err) + } + fmt.Printf("signature: (0x%x, 0x%x)\n", r, s) + + valid := ecdsa.Verify(&privateKey.PublicKey, hash[:], r, s) + fmt.Println("signature verified:", valid) +} diff --git a/libgo/go/crypto/elliptic/elliptic.go b/libgo/go/crypto/elliptic/elliptic.go index 35aacf2..4fc2b5e 100644 --- a/libgo/go/crypto/elliptic/elliptic.go +++ b/libgo/go/crypto/elliptic/elliptic.go @@ -20,7 +20,7 @@ import ( ) // A Curve represents a short-form Weierstrass curve with a=-3. -// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html +// See https://www.hyperelliptic.org/EFD/g1p/auto-shortw.html type Curve interface { // Params returns the parameters for the curve. Params() *CurveParams @@ -108,7 +108,7 @@ func (curve *CurveParams) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { // addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and // (x2, y2, z2) and returns their sum, also in Jacobian form. func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { - // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl + // See https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl x3, y3, z3 := new(big.Int), new(big.Int), new(big.Int) if z1.Sign() == 0 { x3.Set(x2) @@ -191,7 +191,7 @@ func (curve *CurveParams) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { // doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and // returns its double, also in Jacobian form. func (curve *CurveParams) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { - // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b + // See https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b delta := new(big.Int).Mul(z, z) delta.Mod(delta, curve.P) gamma := new(big.Int).Mul(y, y) diff --git a/libgo/go/crypto/elliptic/elliptic_test.go b/libgo/go/crypto/elliptic/elliptic_test.go index f661359..09c5483 100644 --- a/libgo/go/crypto/elliptic/elliptic_test.go +++ b/libgo/go/crypto/elliptic/elliptic_test.go @@ -608,7 +608,7 @@ func TestUnmarshalToLargeCoordinates(t *testing.T) { copy(invalidX[33:], y.Bytes()) if X, Y := Unmarshal(curve, invalidX); X != nil || Y != nil { - t.Errorf("Unmarshal accpets invalid X coordinate") + t.Errorf("Unmarshal accepts invalid X coordinate") } // This is a point on the curve with a small y value, small enough that we can add p and still be within 32 bytes. @@ -625,6 +625,6 @@ func TestUnmarshalToLargeCoordinates(t *testing.T) { copy(invalidY[33:], y.Bytes()) if X, Y := Unmarshal(curve, invalidY); X != nil || Y != nil { - t.Errorf("Unmarshal accpets invalid Y coordinate") + t.Errorf("Unmarshal accepts invalid Y coordinate") } } diff --git a/libgo/go/crypto/elliptic/fuzz_test.go b/libgo/go/crypto/elliptic/fuzz_test.go new file mode 100644 index 0000000..10196cf --- /dev/null +++ b/libgo/go/crypto/elliptic/fuzz_test.go @@ -0,0 +1,54 @@ +// Copyright 2018 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 amd64 arm64 + +package elliptic + +import ( + "crypto/rand" + "testing" + "time" +) + +func TestFuzz(t *testing.T) { + + p256 := P256() + p256Generic := p256.Params() + + var scalar1 [32]byte + var scalar2 [32]byte + var timeout *time.Timer + + if testing.Short() { + timeout = time.NewTimer(500 * time.Millisecond) + } else { + timeout = time.NewTimer(2 * time.Second) + } + + for { + select { + case <-timeout.C: + return + default: + } + + rand.Read(scalar1[:]) + rand.Read(scalar2[:]) + + x, y := p256.ScalarBaseMult(scalar1[:]) + x2, y2 := p256Generic.ScalarBaseMult(scalar1[:]) + + xx, yy := p256.ScalarMult(x, y, scalar2[:]) + xx2, yy2 := p256Generic.ScalarMult(x2, y2, scalar2[:]) + + if x.Cmp(x2) != 0 || y.Cmp(y2) != 0 { + t.Fatalf("ScalarBaseMult does not match reference result with scalar: %x, please report this error to security@golang.org", scalar1) + } + + if xx.Cmp(xx2) != 0 || yy.Cmp(yy2) != 0 { + t.Fatalf("ScalarMult does not match reference result with scalars: %x and %x, please report this error to security@golang.org", scalar1, scalar2) + } + } +} diff --git a/libgo/go/crypto/elliptic/p224.go b/libgo/go/crypto/elliptic/p224.go index 22d0e24..2ea63f3 100644 --- a/libgo/go/crypto/elliptic/p224.go +++ b/libgo/go/crypto/elliptic/p224.go @@ -7,7 +7,7 @@ package elliptic // This is a constant-time, 32-bit implementation of P224. See FIPS 186-3, // section D.2.2. // -// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background. +// See https://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background. import ( "math/big" @@ -503,7 +503,7 @@ func p224Contract(out, in *p224FieldElement) { // p224AddJacobian computes *out = a+b where a != b. func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) { - // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-p224Add-2007-bl + // See https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-p224Add-2007-bl var z1z1, z2z2, u1, u2, s1, s2, h, i, j, r, v p224FieldElement var c p224LargeFieldElement diff --git a/libgo/go/crypto/elliptic/p256.go b/libgo/go/crypto/elliptic/p256.go index a0933dc..937d2a4 100644 --- a/libgo/go/crypto/elliptic/p256.go +++ b/libgo/go/crypto/elliptic/p256.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// -build !amd64 +// -build !amd64,!arm64 package elliptic @@ -817,7 +817,7 @@ func p256Scalar8(out *[p256Limbs]uint32) { // p256PointDouble sets {xOut,yOut,zOut} = 2*{x,y,z}. // -// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l +// See https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l func p256PointDouble(xOut, yOut, zOut, x, y, z *[p256Limbs]uint32) { var delta, gamma, alpha, beta, tmp, tmp2 [p256Limbs]uint32 @@ -850,7 +850,7 @@ func p256PointDouble(xOut, yOut, zOut, x, y, z *[p256Limbs]uint32) { // p256PointAddMixed sets {xOut,yOut,zOut} = {x1,y1,z1} + {x2,y2,1}. // (i.e. the second point is affine.) // -// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl +// See https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl // // Note that this function does not handle P+P, infinity+P nor P+infinity // correctly. @@ -886,7 +886,7 @@ func p256PointAddMixed(xOut, yOut, zOut, x1, y1, z1, x2, y2 *[p256Limbs]uint32) // p256PointAdd sets {xOut,yOut,zOut} = {x1,y1,z1} + {x2,y2,z2}. // -// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl +// See https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl // // Note that this function does not handle P+P, infinity+P nor P+infinity // correctly. diff --git a/libgo/go/crypto/elliptic/p256_amd64.go b/libgo/go/crypto/elliptic/p256_asm.go index f108e73..aee065b 100644 --- a/libgo/go/crypto/elliptic/p256_amd64.go +++ b/libgo/go/crypto/elliptic/p256_asm.go @@ -7,11 +7,11 @@ // detail in: // S.Gueron and V.Krasnov, "Fast prime field elliptic-curve cryptography with // 256-bit primes" -// http://link.springer.com/article/10.1007%2Fs13389-014-0090-x +// https://link.springer.com/article/10.1007%2Fs13389-014-0090-x // https://eprint.iacr.org/2013/816.pdf -// +build ignore -// +build amd64 +// +build ignore_for_gccgo +// +build amd64 arm64 package elliptic @@ -32,7 +32,7 @@ type ( var ( p256 p256Curve - p256Precomputed *[37][64 * 8]uint64 + p256Precomputed *[43][32 * 8]uint64 precomputeOnce sync.Once ) @@ -51,14 +51,14 @@ func (curve p256Curve) Params() *CurveParams { return curve.CurveParams } -// Functions implemented in p256_asm_amd64.s +// Functions implemented in p256_asm_*64.s // Montgomery multiplication modulo P256 //go:noescape func p256Mul(res, in1, in2 []uint64) -// Montgomery square modulo P256 +// Montgomery square modulo P256, repeated n times (n >= 1) //go:noescape -func p256Sqr(res, in []uint64) +func p256Sqr(res, in []uint64, n int) // Montgomery multiplication by 1 //go:noescape @@ -122,11 +122,20 @@ func (curve p256Curve) Inverse(k *big.Int) *big.Int { k = new(big.Int).Mod(k, p256.N) } - // table will store precomputed powers of x. The four words at index - // 4×i store x^(i+1). - var table [4 * 15]uint64 + // table will store precomputed powers of x. + var table [4 * 9]uint64 + var ( + _1 = table[4*0 : 4*1] + _11 = table[4*1 : 4*2] + _101 = table[4*2 : 4*3] + _111 = table[4*3 : 4*4] + _1111 = table[4*4 : 4*5] + _10101 = table[4*5 : 4*6] + _101111 = table[4*6 : 4*7] + x = table[4*7 : 4*8] + t = table[4*8 : 4*9] + ) - x := make([]uint64, 4) fromBig(x[:], k) // This code operates in the Montgomery domain where R = 2^256 mod n // and n is the order of the scalar field. (See initP256 for the @@ -134,53 +143,49 @@ func (curve p256Curve) Inverse(k *big.Int) *big.Int { // multiplication of x and y in the calculates (x × y × R^-1) mod n. RR // is R×R mod n thus the Montgomery multiplication x and RR gives x×R, // i.e. converts x into the Montgomery domain. + // Window values borrowed from https://briansmith.org/ecc-inversion-addition-chains-01#p256_scalar_inversion RR := []uint64{0x83244c95be79eea2, 0x4699799c49bd6fa6, 0x2845b2392b6bec59, 0x66e12d94f3d95620} - p256OrdMul(table[:4], x, RR) - - // Prepare the table, no need in constant time access, because the - // power is not a secret. (Entry 0 is never used.) - for i := 2; i < 16; i += 2 { - p256OrdSqr(table[4*(i-1):], table[4*((i/2)-1):], 1) - p256OrdMul(table[4*i:], table[4*(i-1):], table[:4]) - } - - x[0] = table[4*14+0] // f - x[1] = table[4*14+1] - x[2] = table[4*14+2] - x[3] = table[4*14+3] - - p256OrdSqr(x, x, 4) - p256OrdMul(x, x, table[4*14:4*14+4]) // ff - t := make([]uint64, 4, 4) - t[0] = x[0] - t[1] = x[1] - t[2] = x[2] - t[3] = x[3] - - p256OrdSqr(x, x, 8) - p256OrdMul(x, x, t) // ffff - t[0] = x[0] - t[1] = x[1] - t[2] = x[2] - t[3] = x[3] - - p256OrdSqr(x, x, 16) - p256OrdMul(x, x, t) // ffffffff - t[0] = x[0] - t[1] = x[1] - t[2] = x[2] - t[3] = x[3] - - p256OrdSqr(x, x, 64) // ffffffff0000000000000000 - p256OrdMul(x, x, t) // ffffffff00000000ffffffff - p256OrdSqr(x, x, 32) // ffffffff00000000ffffffff00000000 - p256OrdMul(x, x, t) // ffffffff00000000ffffffffffffffff - - // Remaining 32 windows - expLo := [32]byte{0xb, 0xc, 0xe, 0x6, 0xf, 0xa, 0xa, 0xd, 0xa, 0x7, 0x1, 0x7, 0x9, 0xe, 0x8, 0x4, 0xf, 0x3, 0xb, 0x9, 0xc, 0xa, 0xc, 0x2, 0xf, 0xc, 0x6, 0x3, 0x2, 0x5, 0x4, 0xf} - for i := 0; i < 32; i++ { - p256OrdSqr(x, x, 4) - p256OrdMul(x, x, table[4*(expLo[i]-1):]) + p256OrdMul(_1, x, RR) // _1 + p256OrdSqr(x, _1, 1) // _10 + p256OrdMul(_11, x, _1) // _11 + p256OrdMul(_101, x, _11) // _101 + p256OrdMul(_111, x, _101) // _111 + p256OrdSqr(x, _101, 1) // _1010 + p256OrdMul(_1111, _101, x) // _1111 + + p256OrdSqr(t, x, 1) // _10100 + p256OrdMul(_10101, t, _1) // _10101 + p256OrdSqr(x, _10101, 1) // _101010 + p256OrdMul(_101111, _101, x) // _101111 + p256OrdMul(x, _10101, x) // _111111 = x6 + p256OrdSqr(t, x, 2) // _11111100 + p256OrdMul(t, t, _11) // _11111111 = x8 + p256OrdSqr(x, t, 8) // _ff00 + p256OrdMul(x, x, t) // _ffff = x16 + p256OrdSqr(t, x, 16) // _ffff0000 + p256OrdMul(t, t, x) // _ffffffff = x32 + + p256OrdSqr(x, t, 64) + p256OrdMul(x, x, t) + p256OrdSqr(x, x, 32) + p256OrdMul(x, x, t) + + sqrs := []uint8{ + 6, 5, 4, 5, 5, + 4, 3, 3, 5, 9, + 6, 2, 5, 6, 5, + 4, 5, 5, 3, 10, + 2, 5, 5, 3, 7, 6} + muls := [][]uint64{ + _101111, _111, _11, _1111, _10101, + _101, _101, _101, _111, _101111, + _1111, _1, _1, _1111, _111, + _111, _111, _101, _11, _101111, + _11, _11, _11, _1, _10101, _1111} + + for i, s := range sqrs { + p256OrdSqr(x, x, int(s)) + p256OrdMul(x, x, muls[i]) } // Multiplying by one in the Montgomery domain converts a Montgomery @@ -310,7 +315,7 @@ func (p *p256Point) p256PointToAffine() (x, y *big.Int) { zInv := make([]uint64, 4) zInvSq := make([]uint64, 4) p256Inverse(zInv, p.xyz[8:12]) - p256Sqr(zInvSq, zInv) + p256Sqr(zInvSq, zInv, 1) p256Mul(zInv, zInv, zInvSq) p256Mul(zInvSq, p.xyz[0:4], zInvSq) @@ -347,71 +352,43 @@ func p256Inverse(out, in []uint64) { p16 := stack[4*3 : 4*3+4] p32 := stack[4*4 : 4*4+4] - p256Sqr(out, in) + p256Sqr(out, in, 1) p256Mul(p2, out, in) // 3*p - p256Sqr(out, p2) - p256Sqr(out, out) + p256Sqr(out, p2, 2) p256Mul(p4, out, p2) // f*p - p256Sqr(out, p4) - p256Sqr(out, out) - p256Sqr(out, out) - p256Sqr(out, out) + p256Sqr(out, p4, 4) p256Mul(p8, out, p4) // ff*p - p256Sqr(out, p8) - - for i := 0; i < 7; i++ { - p256Sqr(out, out) - } + p256Sqr(out, p8, 8) p256Mul(p16, out, p8) // ffff*p - p256Sqr(out, p16) - for i := 0; i < 15; i++ { - p256Sqr(out, out) - } + p256Sqr(out, p16, 16) p256Mul(p32, out, p16) // ffffffff*p - p256Sqr(out, p32) - - for i := 0; i < 31; i++ { - p256Sqr(out, out) - } + p256Sqr(out, p32, 32) p256Mul(out, out, in) - for i := 0; i < 32*4; i++ { - p256Sqr(out, out) - } + p256Sqr(out, out, 128) p256Mul(out, out, p32) - for i := 0; i < 32; i++ { - p256Sqr(out, out) - } + p256Sqr(out, out, 32) p256Mul(out, out, p32) - for i := 0; i < 16; i++ { - p256Sqr(out, out) - } + p256Sqr(out, out, 16) p256Mul(out, out, p16) - for i := 0; i < 8; i++ { - p256Sqr(out, out) - } + p256Sqr(out, out, 8) p256Mul(out, out, p8) - p256Sqr(out, out) - p256Sqr(out, out) - p256Sqr(out, out) - p256Sqr(out, out) + p256Sqr(out, out, 4) p256Mul(out, out, p4) - p256Sqr(out, out) - p256Sqr(out, out) + p256Sqr(out, out, 2) p256Mul(out, out, p2) - p256Sqr(out, out) - p256Sqr(out, out) + p256Sqr(out, out, 2) p256Mul(out, out, in) } @@ -427,16 +404,16 @@ func boothW5(in uint) (int, int) { return int(d), int(s & 1) } -func boothW7(in uint) (int, int) { - var s uint = ^((in >> 7) - 1) - var d uint = (1 << 8) - in - 1 +func boothW6(in uint) (int, int) { + var s uint = ^((in >> 6) - 1) + var d uint = (1 << 7) - in - 1 d = (d & s) | (in & (^s)) d = (d >> 1) + (d & 1) return int(d), int(s & 1) } func initTable() { - p256Precomputed = new([37][64 * 8]uint64) + p256Precomputed = new([43][32 * 8]uint64) basePoint := []uint64{ 0x79e730d418a9143c, 0x75ba95fc5fedb601, 0x79fb732b77622510, 0x18905f76a53755c6, @@ -449,19 +426,19 @@ func initTable() { zInv := make([]uint64, 4) zInvSq := make([]uint64, 4) - for j := 0; j < 64; j++ { + for j := 0; j < 32; j++ { copy(t1, t2) - for i := 0; i < 37; i++ { - // The window size is 7 so we need to double 7 times. + for i := 0; i < 43; i++ { + // The window size is 6 so we need to double 6 times. if i != 0 { - for k := 0; k < 7; k++ { + for k := 0; k < 6; k++ { p256PointDoubleAsm(t1, t1) } } // Convert the point to affine form. (Its values are // still in Montgomery form however.) p256Inverse(zInv, t1[8:12]) - p256Sqr(zInvSq, zInv) + p256Sqr(zInvSq, zInv, 1) p256Mul(zInv, zInv, zInvSq) p256Mul(t1[:4], t1[:4], zInvSq) @@ -482,8 +459,8 @@ func initTable() { func (p *p256Point) p256BaseMult(scalar []uint64) { precomputeOnce.Do(initTable) - wvalue := (scalar[0] << 1) & 0xff - sel, sign := boothW7(uint(wvalue)) + wvalue := (scalar[0] << 1) & 0x7f + sel, sign := boothW6(uint(wvalue)) p256SelectBase(p.xyz[0:8], p256Precomputed[0][0:], sel) p256NegCond(p.xyz[4:8], sign) @@ -500,17 +477,17 @@ func (p *p256Point) p256BaseMult(scalar []uint64) { t0.xyz[10] = 0xffffffffffffffff t0.xyz[11] = 0x00000000fffffffe - index := uint(6) + index := uint(5) zero := sel - for i := 1; i < 37; i++ { + for i := 1; i < 43; i++ { if index < 192 { - wvalue = ((scalar[index/64] >> (index % 64)) + (scalar[index/64+1] << (64 - (index % 64)))) & 0xff + wvalue = ((scalar[index/64] >> (index % 64)) + (scalar[index/64+1] << (64 - (index % 64)))) & 0x7f } else { - wvalue = (scalar[index/64] >> (index % 64)) & 0xff + wvalue = (scalar[index/64] >> (index % 64)) & 0x7f } - index += 7 - sel, sign = boothW7(uint(wvalue)) + index += 6 + sel, sign = boothW6(uint(wvalue)) p256SelectBase(t0.xyz[0:8], p256Precomputed[i][0:], sel) p256PointAddAffineAsm(p.xyz[0:12], p.xyz[0:12], t0.xyz[0:8], sign, sel, zero) zero |= sel diff --git a/libgo/go/crypto/elliptic/p256_generic.go b/libgo/go/crypto/elliptic/p256_generic.go index 49445c3..48954a2 100644 --- a/libgo/go/crypto/elliptic/p256_generic.go +++ b/libgo/go/crypto/elliptic/p256_generic.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// -build !amd64,!s390x +// -build !amd64,!s390x,!arm64 package elliptic diff --git a/libgo/go/crypto/hmac/hmac.go b/libgo/go/crypto/hmac/hmac.go index 3c8e727..c8c0617 100644 --- a/libgo/go/crypto/hmac/hmac.go +++ b/libgo/go/crypto/hmac/hmac.go @@ -27,7 +27,7 @@ import ( ) // FIPS 198-1: -// http://csrc.nist.gov/publications/fips/fips198-1/FIPS-198-1_final.pdf +// https://csrc.nist.gov/publications/fips/fips198-1/FIPS-198-1_final.pdf // key is zero padded to the block size of the hash function // ipad = 0x36 byte repeated for key length diff --git a/libgo/go/crypto/hmac/hmac_test.go b/libgo/go/crypto/hmac/hmac_test.go index aac9aa9..eea345e 100644 --- a/libgo/go/crypto/hmac/hmac_test.go +++ b/libgo/go/crypto/hmac/hmac_test.go @@ -25,7 +25,7 @@ type hmacTest struct { var hmacTests = []hmacTest{ // Tests from US FIPS 198 - // http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf + // https://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf { sha1.New, []byte{ @@ -205,7 +205,7 @@ var hmacTests = []hmacTest{ sha256.BlockSize, }, - // Tests from http://csrc.nist.gov/groups/ST/toolkit/examples.html + // Tests from https://csrc.nist.gov/groups/ST/toolkit/examples.html // (truncated tag tests are left out) { sha1.New, diff --git a/libgo/go/crypto/internal/cipherhw/cipherhw_amd64.go b/libgo/go/crypto/internal/cipherhw/cipherhw_amd64.go deleted file mode 100644 index be0d490..0000000 --- a/libgo/go/crypto/internal/cipherhw/cipherhw_amd64.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2016 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 amd64,!gccgo,!appengine - -package cipherhw - -// defined in asm_amd64.s -func hasAESNI() bool - -// AESGCMSupport returns true if the Go standard library supports AES-GCM in -// hardware. -func AESGCMSupport() bool { - return hasAESNI() -} diff --git a/libgo/go/crypto/internal/cipherhw/cipherhw_s390x.go b/libgo/go/crypto/internal/cipherhw/cipherhw_s390x.go deleted file mode 100644 index 9cd7679..0000000 --- a/libgo/go/crypto/internal/cipherhw/cipherhw_s390x.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2016 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 s390x,!gccgo,!appengine - -package cipherhw - -// hasHWSupport reports whether the AES-128, AES-192 and AES-256 cipher message -// (KM) function codes are supported. Note that this function is expensive. -// defined in asm_s390x.s -func hasHWSupport() bool - -var hwSupport = hasHWSupport() - -func AESGCMSupport() bool { - return hwSupport -} diff --git a/libgo/go/crypto/internal/cipherhw/doc.go b/libgo/go/crypto/internal/cipherhw/doc.go deleted file mode 100644 index a75fcf6..0000000 --- a/libgo/go/crypto/internal/cipherhw/doc.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2016 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 cipherhw exposes common functions for detecting whether hardware -// support for certain ciphers and authenticators is present. -package cipherhw diff --git a/libgo/go/crypto/internal/cipherhw/generic.go b/libgo/go/crypto/internal/cipherhw/generic.go deleted file mode 100644 index 64d90d3..0000000 --- a/libgo/go/crypto/internal/cipherhw/generic.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2016 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 !amd64,!s390x gccgo appengine - -package cipherhw - -func AESGCMSupport() bool { - return false -} diff --git a/libgo/go/crypto/internal/randutil/randutil.go b/libgo/go/crypto/internal/randutil/randutil.go new file mode 100644 index 0000000..84b1295 --- /dev/null +++ b/libgo/go/crypto/internal/randutil/randutil.go @@ -0,0 +1,38 @@ +// Copyright 2018 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 randutil contains internal randomness utilities for various +// crypto packages. +package randutil + +import ( + "io" + "sync" +) + +var ( + closedChanOnce sync.Once + closedChan chan struct{} +) + +// MaybeReadByte reads a single byte from r with ~50% probability. This is used +// to ensure that callers do not depend on non-guaranteed behaviour, e.g. +// assuming that rsa.GenerateKey is deterministic w.r.t. a given random stream. +// +// This does not affect tests that pass a stream of fixed bytes as the random +// source (e.g. a zeroReader). +func MaybeReadByte(r io.Reader) { + closedChanOnce.Do(func() { + closedChan = make(chan struct{}) + close(closedChan) + }) + + select { + case <-closedChan: + return + case <-closedChan: + var buf [1]byte + r.Read(buf[:]) + } +} diff --git a/libgo/go/crypto/internal/subtle/aliasing.go b/libgo/go/crypto/internal/subtle/aliasing.go new file mode 100644 index 0000000..812ce3c --- /dev/null +++ b/libgo/go/crypto/internal/subtle/aliasing.go @@ -0,0 +1,34 @@ +// Copyright 2018 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 !appengine + +// Package subtle implements functions that are often useful in cryptographic +// code but require careful thought to use correctly. +// +// This is a mirror of golang.org/x/crypto/internal/subtle. +package subtle // import "crypto/internal/subtle" + +import "unsafe" + +// AnyOverlap reports whether x and y share memory at any (not necessarily +// corresponding) index. The memory beyond the slice length is ignored. +func AnyOverlap(x, y []byte) bool { + return len(x) > 0 && len(y) > 0 && + uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) && + uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1])) +} + +// InexactOverlap reports whether x and y share memory at any non-corresponding +// index. The memory beyond the slice length is ignored. Note that x and y can +// have different lengths and still not have any inexact overlap. +// +// InexactOverlap can be used to implement the requirements of the crypto/cipher +// AEAD, Block, BlockMode and Stream interfaces. +func InexactOverlap(x, y []byte) bool { + if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] { + return false + } + return AnyOverlap(x, y) +} diff --git a/libgo/go/crypto/internal/subtle/aliasing_appengine.go b/libgo/go/crypto/internal/subtle/aliasing_appengine.go new file mode 100644 index 0000000..844f901 --- /dev/null +++ b/libgo/go/crypto/internal/subtle/aliasing_appengine.go @@ -0,0 +1,37 @@ +// Copyright 2018 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 appengine + +// Package subtle implements functions that are often useful in cryptographic +// code but require careful thought to use correctly. +// +// This is a mirror of golang.org/x/crypto/internal/subtle. +package subtle // import "crypto/internal/subtle" + +// This is the Google App Engine standard variant based on reflect +// because the unsafe package and cgo are disallowed. + +import "reflect" + +// AnyOverlap reports whether x and y share memory at any (not necessarily +// corresponding) index. The memory beyond the slice length is ignored. +func AnyOverlap(x, y []byte) bool { + return len(x) > 0 && len(y) > 0 && + reflect.ValueOf(&x[0]).Pointer() <= reflect.ValueOf(&y[len(y)-1]).Pointer() && + reflect.ValueOf(&y[0]).Pointer() <= reflect.ValueOf(&x[len(x)-1]).Pointer() +} + +// InexactOverlap reports whether x and y share memory at any non-corresponding +// index. The memory beyond the slice length is ignored. Note that x and y can +// have different lengths and still not have any inexact overlap. +// +// InexactOverlap can be used to implement the requirements of the crypto/cipher +// AEAD, Block, BlockMode and Stream interfaces. +func InexactOverlap(x, y []byte) bool { + if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] { + return false + } + return AnyOverlap(x, y) +} diff --git a/libgo/go/crypto/internal/subtle/aliasing_test.go b/libgo/go/crypto/internal/subtle/aliasing_test.go new file mode 100644 index 0000000..f1e7238 --- /dev/null +++ b/libgo/go/crypto/internal/subtle/aliasing_test.go @@ -0,0 +1,50 @@ +// Copyright 2018 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 subtle_test + +import ( + "testing" + + "crypto/internal/subtle" +) + +var a, b [100]byte + +var aliasingTests = []struct { + x, y []byte + anyOverlap, inexactOverlap bool +}{ + {a[:], b[:], false, false}, + {a[:], b[:0], false, false}, + {a[:], b[:50], false, false}, + {a[40:50], a[50:60], false, false}, + {a[40:50], a[60:70], false, false}, + {a[:51], a[50:], true, true}, + {a[:], a[:], true, false}, + {a[:50], a[:60], true, false}, + {a[:], nil, false, false}, + {nil, nil, false, false}, + {a[:], a[:0], false, false}, + {a[:10], a[:10:20], true, false}, + {a[:10], a[5:10:20], true, true}, +} + +func testAliasing(t *testing.T, i int, x, y []byte, anyOverlap, inexactOverlap bool) { + any := subtle.AnyOverlap(x, y) + if any != anyOverlap { + t.Errorf("%d: wrong AnyOverlap result, expected %v, got %v", i, anyOverlap, any) + } + inexact := subtle.InexactOverlap(x, y) + if inexact != inexactOverlap { + t.Errorf("%d: wrong InexactOverlap result, expected %v, got %v", i, inexactOverlap, any) + } +} + +func TestAliasing(t *testing.T) { + for i, tt := range aliasingTests { + testAliasing(t, i, tt.x, tt.y, tt.anyOverlap, tt.inexactOverlap) + testAliasing(t, i, tt.y, tt.x, tt.anyOverlap, tt.inexactOverlap) + } +} diff --git a/libgo/go/crypto/md5/gen.go b/libgo/go/crypto/md5/gen.go index 178fad1..a815dc2 100644 --- a/libgo/go/crypto/md5/gen.go +++ b/libgo/go/crypto/md5/gen.go @@ -179,8 +179,7 @@ var program = `// Copyright 2013 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. -// DO NOT EDIT. -// Generate with: go run gen.go{{if .Full}} -full{{end}} -output md5block.go +// Code generated by go run gen.go{{if .Full}} -full{{end}} -output md5block.go; DO NOT EDIT. package md5 diff --git a/libgo/go/crypto/md5/md5.go b/libgo/go/crypto/md5/md5.go index 7aeee60..88d914d 100644 --- a/libgo/go/crypto/md5/md5.go +++ b/libgo/go/crypto/md5/md5.go @@ -64,7 +64,7 @@ func (d *digest) MarshalBinary() ([]byte, error) { b = appendUint32(b, d.s[2]) b = appendUint32(b, d.s[3]) b = append(b, d.x[:d.nx]...) - b = b[:len(b)+len(d.x)-int(d.nx)] // already zero + b = b[:len(b)+len(d.x)-d.nx] // already zero b = appendUint64(b, d.len) return b, nil } @@ -160,10 +160,10 @@ func (d *digest) Write(p []byte) (nn int, err error) { return } -func (d0 *digest) Sum(in []byte) []byte { - // Make a copy of d0 so that caller can keep writing and summing. - d := *d0 - hash := d.checkSum() +func (d *digest) Sum(in []byte) []byte { + // Make a copy of d so that caller can keep writing and summing. + d0 := *d + hash := d0.checkSum() return append(in, hash[:]...) } diff --git a/libgo/go/crypto/md5/md5block.go b/libgo/go/crypto/md5/md5block.go index 64e1e7c..8ac32ff 100644 --- a/libgo/go/crypto/md5/md5block.go +++ b/libgo/go/crypto/md5/md5block.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// DO NOT EDIT. -// Generate with: go run gen.go -full -output md5block.go +// Code generated by go run gen.go -full -output md5block.go; DO NOT EDIT. package md5 diff --git a/libgo/go/crypto/md5/md5block_decl.go b/libgo/go/crypto/md5/md5block_decl.go index 4de38cf..5f9d7ab 100644 --- a/libgo/go/crypto/md5/md5block_decl.go +++ b/libgo/go/crypto/md5/md5block_decl.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // +build ignore -// -build amd64 amd64p32 386 arm ppc64le s390x +// -build amd64 amd64p32 386 arm ppc64le s390x arm64 package md5 diff --git a/libgo/go/crypto/md5/md5block_generic.go b/libgo/go/crypto/md5/md5block_generic.go index c23d02b..c580bc6 100644 --- a/libgo/go/crypto/md5/md5block_generic.go +++ b/libgo/go/crypto/md5/md5block_generic.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// -build !amd64,!amd64p32,!386,!arm,!ppc64le,!s390x +// -build !amd64,!amd64p32,!386,!arm,!ppc64le,!s390x,!arm64 package md5 diff --git a/libgo/go/crypto/rand/rand.go b/libgo/go/crypto/rand/rand.go index 6f7523d..b8df8a3 100644 --- a/libgo/go/crypto/rand/rand.go +++ b/libgo/go/crypto/rand/rand.go @@ -3,18 +3,19 @@ // license that can be found in the LICENSE file. // Package rand implements a cryptographically secure -// pseudorandom number generator. +// random number generator. package rand import "io" // Reader is a global, shared instance of a cryptographically -// strong pseudo-random generator. +// secure random number generator. // // On Linux, Reader uses getrandom(2) if available, /dev/urandom otherwise. // On OpenBSD, Reader uses getentropy(2). // On other Unix-like systems, Reader reads from /dev/urandom. // On Windows systems, Reader uses the CryptGenRandom API. +// On Wasm, Reader uses the Web Crypto API. var Reader io.Reader // Read is a helper function that calls Reader.Read using io.ReadFull. diff --git a/libgo/go/crypto/rand/rand_js.go b/libgo/go/crypto/rand/rand_js.go new file mode 100644 index 0000000..bb21396 --- /dev/null +++ b/libgo/go/crypto/rand/rand_js.go @@ -0,0 +1,27 @@ +// Copyright 2018 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 js,wasm + +package rand + +import "syscall/js" + +func init() { + Reader = &reader{} +} + +var jsCrypto = js.Global().Get("crypto") + +// reader implements a pseudorandom generator +// using JavaScript crypto.getRandomValues method. +// See https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues. +type reader struct{} + +func (r *reader) Read(b []byte) (int, error) { + a := js.TypedArrayOf(b) + jsCrypto.Call("getRandomValues", a) + a.Release() + return len(b), nil +} diff --git a/libgo/go/crypto/rc4/rc4.go b/libgo/go/crypto/rc4/rc4.go index 8274325..c445bb0 100644 --- a/libgo/go/crypto/rc4/rc4.go +++ b/libgo/go/crypto/rc4/rc4.go @@ -9,7 +9,10 @@ // applications. package rc4 -import "strconv" +import ( + "crypto/internal/subtle" + "strconv" +) // A Cipher is an instance of RC4 using a particular key. type Cipher struct { @@ -57,12 +60,22 @@ func (c *Cipher) Reset() { // This is the pure Go version. rc4_{amd64,386,arm}* contain assembly // implementations. This is here for tests and to prevent bitrot. func (c *Cipher) xorKeyStreamGeneric(dst, src []byte) { + if len(src) == 0 { + return + } + if subtle.InexactOverlap(dst[:len(src)], src) { + panic("crypto/rc4: invalid buffer overlap") + } i, j := c.i, c.j + _ = dst[len(src)-1] + dst = dst[:len(src)] // eliminate bounds check from loop for k, v := range src { i += 1 - j += uint8(c.s[i]) - c.s[i], c.s[j] = c.s[j], c.s[i] - dst[k] = v ^ uint8(c.s[uint8(c.s[i]+c.s[j])]) + x := c.s[i] + j += uint8(x) + y := c.s[j] + c.s[i], c.s[j] = y, x + dst[k] = v ^ uint8(c.s[uint8(x+y)]) } c.i, c.j = i, j } diff --git a/libgo/go/crypto/rc4/rc4_asm.go b/libgo/go/crypto/rc4/rc4_asm.go index aea1939..ce2162d 100644 --- a/libgo/go/crypto/rc4/rc4_asm.go +++ b/libgo/go/crypto/rc4/rc4_asm.go @@ -8,6 +8,8 @@ package rc4 +import "crypto/internal/subtle" + func xorKeyStream(dst, src *byte, n int, state *[256]uint32, i, j *uint8) // XORKeyStream sets dst to the result of XORing src with the key stream. @@ -16,7 +18,11 @@ func (c *Cipher) XORKeyStream(dst, src []byte) { if len(src) == 0 { return } - // Assert len(dst) >= len(src) - _ = dst[len(src)-1] + if len(dst) < len(src) { + panic("crypto/cipher: output smaller than input") + } + if subtle.InexactOverlap(dst[:len(src)], src) { + panic("crypto/cipher: invalid buffer overlap") + } xorKeyStream(&dst[0], &src[0], len(src), &c.s, &c.i, &c.j) } diff --git a/libgo/go/crypto/rc4/rc4_test.go b/libgo/go/crypto/rc4/rc4_test.go index af79882..1fc08b8 100644 --- a/libgo/go/crypto/rc4/rc4_test.go +++ b/libgo/go/crypto/rc4/rc4_test.go @@ -16,7 +16,7 @@ type rc4Test struct { var golden = []rc4Test{ // Test vectors from the original cypherpunk posting of ARC4: - // http://groups.google.com/group/sci.crypt/msg/10a300c9d21afca0?pli=1 + // https://groups.google.com/group/sci.crypt/msg/10a300c9d21afca0?pli=1 { []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, []byte{0x74, 0x94, 0xc2, 0xe7, 0x10, 0x4b, 0x08, 0x79}, @@ -30,7 +30,7 @@ var golden = []rc4Test{ []byte{0xd6, 0xa1, 0x41, 0xa7, 0xec, 0x3c, 0x38, 0xdf, 0xbd, 0x61}, }, - // Test vectors from the Wikipedia page: http://en.wikipedia.org/wiki/RC4 + // Test vectors from the Wikipedia page: https://en.wikipedia.org/wiki/RC4 { []byte{0x4b, 0x65, 0x79}, []byte{0xeb, 0x9f, 0x77, 0x81, 0xb7, 0x34, 0xca, 0x72, 0xa7, 0x19}, diff --git a/libgo/go/crypto/rsa/pkcs1v15.go b/libgo/go/crypto/rsa/pkcs1v15.go index 3517a8c..37790ac 100644 --- a/libgo/go/crypto/rsa/pkcs1v15.go +++ b/libgo/go/crypto/rsa/pkcs1v15.go @@ -10,6 +10,8 @@ import ( "errors" "io" "math/big" + + "crypto/internal/randutil" ) // This file implements encryption and decryption using PKCS#1 v1.5 padding. @@ -35,10 +37,12 @@ type PKCS1v15DecryptOptions struct { // WARNING: use of this function to encrypt plaintexts other than // session keys is dangerous. Use RSA OAEP in new protocols. func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) ([]byte, error) { + randutil.MaybeReadByte(rand) + if err := checkPub(pub); err != nil { return nil, err } - k := (pub.N.BitLen() + 7) / 8 + k := pub.Size() if len(msg) > k-11 { return nil, ErrMessageTooLong } @@ -106,7 +110,7 @@ func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []by if err := checkPub(&priv.PublicKey); err != nil { return err } - k := (priv.N.BitLen() + 7) / 8 + k := priv.Size() if k-(len(key)+3+8) < 0 { return ErrDecryption } @@ -134,7 +138,7 @@ func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []by // in order to maintain constant memory access patterns. If the plaintext was // valid then index contains the index of the original message in em. func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid int, em []byte, index int, err error) { - k := (priv.N.BitLen() + 7) / 8 + k := priv.Size() if k < 11 { err = ErrDecryption return @@ -232,7 +236,7 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b } tLen := len(prefix) + hashLen - k := (priv.N.BitLen() + 7) / 8 + k := priv.Size() if k < tLen+11 { return nil, ErrMessageTooLong } @@ -268,7 +272,7 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) } tLen := len(prefix) + hashLen - k := (pub.N.BitLen() + 7) / 8 + k := pub.Size() if k < tLen+11 { return ErrVerification } diff --git a/libgo/go/crypto/rsa/pss.go b/libgo/go/crypto/rsa/pss.go index 75558a9..3ff0c2f 100644 --- a/libgo/go/crypto/rsa/pss.go +++ b/libgo/go/crypto/rsa/pss.go @@ -36,7 +36,7 @@ func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byt // 3. If emLen < hLen + sLen + 2, output "encoding error" and stop. if emLen < hLen+sLen+2 { - return nil, errors.New("crypto/rsa: encoding error") + return nil, errors.New("crypto/rsa: key size too small for PSS signature") } em := make([]byte, emLen) diff --git a/libgo/go/crypto/rsa/rsa.go b/libgo/go/crypto/rsa/rsa.go index 0faca43..ad32d3e 100644 --- a/libgo/go/crypto/rsa/rsa.go +++ b/libgo/go/crypto/rsa/rsa.go @@ -31,6 +31,8 @@ import ( "io" "math" "math/big" + + "crypto/internal/randutil" ) var bigZero = big.NewInt(0) @@ -42,6 +44,12 @@ type PublicKey struct { E int // public exponent } +// Size returns the modulus size in bytes. Raw signatures and ciphertexts +// for or by this public key will have the same size. +func (pub *PublicKey) Size() int { + return (pub.N.BitLen() + 7) / 8 +} + // OAEPOptions is an interface for passing options to OAEP decryption using the // crypto.Decrypter interface. type OAEPOptions struct { @@ -62,7 +70,7 @@ var ( // We require pub.E to fit into a 32-bit integer so that we // do not have different behavior depending on whether // int is 32 or 64 bits. See also -// http://www.imperialviolet.org/2012/03/16/rsae.html. +// https://www.imperialviolet.org/2012/03/16/rsae.html. func checkPub(pub *PublicKey) error { if pub.N == nil { return errPublicModulus @@ -212,6 +220,8 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) { // [1] US patent 4405829 (1972, expired) // [2] http://www.cacr.math.uwaterloo.ca/techreports/2006/cacr2006-16.pdf func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (*PrivateKey, error) { + randutil.MaybeReadByte(random) + priv := new(PrivateKey) priv.E = 65537 @@ -286,18 +296,13 @@ NextSetOfPrimes: continue NextSetOfPrimes } - g := new(big.Int) priv.D = new(big.Int) e := big.NewInt(int64(priv.E)) - g.GCD(priv.D, nil, e, totient) + ok := priv.D.ModInverse(e, totient) - if g.Cmp(bigOne) == 0 { - if priv.D.Sign() < 0 { - priv.D.Add(priv.D, totient) - } + if ok != nil { priv.Primes = primes priv.N = n - break } } @@ -373,7 +378,7 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l return nil, err } hash.Reset() - k := (pub.N.BitLen() + 7) / 8 + k := pub.Size() if len(msg) > k-2*hash.Size()-2 { return nil, ErrMessageTooLong } @@ -421,29 +426,6 @@ var ErrDecryption = errors.New("crypto/rsa: decryption error") // It is deliberately vague to avoid adaptive attacks. var ErrVerification = errors.New("crypto/rsa: verification error") -// modInverse returns ia, the inverse of a in the multiplicative group of prime -// order n. It requires that a be a member of the group (i.e. less than n). -func modInverse(a, n *big.Int) (ia *big.Int, ok bool) { - g := new(big.Int) - x := new(big.Int) - g.GCD(x, nil, a, n) - if g.Cmp(bigOne) != 0 { - // In this case, a and n aren't coprime and we cannot calculate - // the inverse. This happens because the values of n are nearly - // prime (being the product of two primes) rather than truly - // prime. - return - } - - if x.Cmp(bigOne) < 0 { - // 0 is not the multiplicative inverse of any element so, if x - // < 1, then x is negative. - x.Add(x, n) - } - - return x, true -} - // Precompute performs some calculations that speed up private key operations // in the future. func (priv *PrivateKey) Precompute() { @@ -489,13 +471,15 @@ func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err er var ir *big.Int if random != nil { + randutil.MaybeReadByte(random) + // Blinding enabled. Blinding involves multiplying c by r^e. // Then the decryption operation performs (m^e * r^e)^d mod n // which equals mr mod n. The factor of r can then be removed // by multiplying by the multiplicative inverse of r. var r *big.Int - + ir = new(big.Int) for { r, err = rand.Int(random, priv.N) if err != nil { @@ -504,9 +488,8 @@ func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err er if r.Cmp(bigZero) == 0 { r = bigOne } - var ok bool - ir, ok = modInverse(r, priv.N) - if ok { + ok := ir.ModInverse(r, priv.N) + if ok != nil { break } } @@ -587,7 +570,7 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext if err := checkPub(&priv.PublicKey); err != nil { return nil, err } - k := (priv.N.BitLen() + 7) / 8 + k := priv.Size() if len(ciphertext) > k || k < hash.Size()*2+2 { return nil, ErrDecryption diff --git a/libgo/go/crypto/sha1/sha1.go b/libgo/go/crypto/sha1/sha1.go index ae4896f..db70b7d 100644 --- a/libgo/go/crypto/sha1/sha1.go +++ b/libgo/go/crypto/sha1/sha1.go @@ -150,10 +150,10 @@ func (d *digest) Write(p []byte) (nn int, err error) { return } -func (d0 *digest) Sum(in []byte) []byte { - // Make a copy of d0 so that caller can keep writing and summing. - d := *d0 - hash := d.checkSum() +func (d *digest) Sum(in []byte) []byte { + // Make a copy of d so that caller can keep writing and summing. + d0 := *d + hash := d0.checkSum() return append(in, hash[:]...) } @@ -189,9 +189,9 @@ func (d *digest) checkSum() [Size]byte { } // ConstantTimeSum computes the same result of Sum() but in constant time -func (d0 *digest) ConstantTimeSum(in []byte) []byte { - d := *d0 - hash := d.constSum() +func (d *digest) ConstantTimeSum(in []byte) []byte { + d0 := *d + hash := d0.constSum() return append(in, hash[:]...) } diff --git a/libgo/go/crypto/sha1/sha1block_s390x.go b/libgo/go/crypto/sha1/sha1block_s390x.go index 55a2359..64c4018 100644 --- a/libgo/go/crypto/sha1/sha1block_s390x.go +++ b/libgo/go/crypto/sha1/sha1block_s390x.go @@ -6,9 +6,6 @@ package sha1 -// featureCheck reports whether the CPU supports the -// SHA-1 compute intermediate message digest (KIMD) -// function code. -func featureCheck() bool +import "internal/cpu" -var useAsm = featureCheck() +var useAsm = cpu.S390X.HasSHA1 diff --git a/libgo/go/crypto/sha256/sha256.go b/libgo/go/crypto/sha256/sha256.go index b8ddaf4..1389de2 100644 --- a/libgo/go/crypto/sha256/sha256.go +++ b/libgo/go/crypto/sha256/sha256.go @@ -104,27 +104,35 @@ func (d *digest) UnmarshalBinary(b []byte) error { return nil } +func putUint32(x []byte, s uint32) { + _ = x[3] + x[0] = byte(s >> 24) + x[1] = byte(s >> 16) + x[2] = byte(s >> 8) + x[3] = byte(s) +} + +func putUint64(x []byte, s uint64) { + _ = x[7] + x[0] = byte(s >> 56) + x[1] = byte(s >> 48) + x[2] = byte(s >> 40) + x[3] = byte(s >> 32) + x[4] = byte(s >> 24) + x[5] = byte(s >> 16) + x[6] = byte(s >> 8) + x[7] = byte(s) +} + func appendUint64(b []byte, x uint64) []byte { - a := [8]byte{ - byte(x >> 56), - byte(x >> 48), - byte(x >> 40), - byte(x >> 32), - byte(x >> 24), - byte(x >> 16), - byte(x >> 8), - byte(x), - } + var a [8]byte + putUint64(a[:], x) return append(b, a[:]...) } func appendUint32(b []byte, x uint32) []byte { - a := [4]byte{ - byte(x >> 24), - byte(x >> 16), - byte(x >> 8), - byte(x), - } + var a [4]byte + putUint32(a[:], x) return append(b, a[:]...) } @@ -215,11 +223,11 @@ func (d *digest) Write(p []byte) (nn int, err error) { return } -func (d0 *digest) Sum(in []byte) []byte { - // Make a copy of d0 so that caller can keep writing and summing. - d := *d0 - hash := d.checkSum() - if d.is224 { +func (d *digest) Sum(in []byte) []byte { + // Make a copy of d so that caller can keep writing and summing. + d0 := *d + hash := d0.checkSum() + if d0.is224 { return append(in, hash[:Size224]...) } return append(in, hash[:]...) @@ -238,26 +246,24 @@ func (d *digest) checkSum() [Size]byte { // Length in bits. len <<= 3 - for i := uint(0); i < 8; i++ { - tmp[i] = byte(len >> (56 - 8*i)) - } + putUint64(tmp[:], len) d.Write(tmp[0:8]) if d.nx != 0 { panic("d.nx != 0") } - h := d.h[:] - if d.is224 { - h = d.h[:7] - } - var digest [Size]byte - for i, s := range h { - digest[i*4] = byte(s >> 24) - digest[i*4+1] = byte(s >> 16) - digest[i*4+2] = byte(s >> 8) - digest[i*4+3] = byte(s) + + putUint32(digest[0:], d.h[0]) + putUint32(digest[4:], d.h[1]) + putUint32(digest[8:], d.h[2]) + putUint32(digest[12:], d.h[3]) + putUint32(digest[16:], d.h[4]) + putUint32(digest[20:], d.h[5]) + putUint32(digest[24:], d.h[6]) + if !d.is224 { + putUint32(digest[28:], d.h[7]) } return digest diff --git a/libgo/go/crypto/sha256/sha256block_s390x.go b/libgo/go/crypto/sha256/sha256block_s390x.go index 8e5f69d..4de1c83 100644 --- a/libgo/go/crypto/sha256/sha256block_s390x.go +++ b/libgo/go/crypto/sha256/sha256block_s390x.go @@ -6,9 +6,6 @@ package sha256 -// featureCheck reports whether the CPU supports the -// SHA256 compute intermediate message digest (KIMD) -// function code. -func featureCheck() bool +import "internal/cpu" -var useAsm = featureCheck() +var useAsm = cpu.S390X.HasSHA256 diff --git a/libgo/go/crypto/sha512/sha512.go b/libgo/go/crypto/sha512/sha512.go index b199951..24fde7dc 100644 --- a/libgo/go/crypto/sha512/sha512.go +++ b/libgo/go/crypto/sha512/sha512.go @@ -195,17 +195,21 @@ func (d *digest) UnmarshalBinary(b []byte) error { return nil } +func putUint64(x []byte, s uint64) { + _ = x[7] + x[0] = byte(s >> 56) + x[1] = byte(s >> 48) + x[2] = byte(s >> 40) + x[3] = byte(s >> 32) + x[4] = byte(s >> 24) + x[5] = byte(s >> 16) + x[6] = byte(s >> 8) + x[7] = byte(s) +} + func appendUint64(b []byte, x uint64) []byte { - a := [8]byte{ - byte(x >> 56), - byte(x >> 48), - byte(x >> 40), - byte(x >> 32), - byte(x >> 24), - byte(x >> 16), - byte(x >> 8), - byte(x), - } + var a [8]byte + putUint64(a[:], x) return append(b, a[:]...) } @@ -282,12 +286,12 @@ func (d *digest) Write(p []byte) (nn int, err error) { return } -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 - hash := d.checkSum() - switch d.function { +func (d *digest) Sum(in []byte) []byte { + // Make a copy of d so that caller can keep writing and summing. + d0 := new(digest) + *d0 = *d + hash := d0.checkSum() + switch d0.function { case crypto.SHA384: return append(in, hash[:Size384]...) case crypto.SHA512_224: @@ -312,30 +316,24 @@ func (d *digest) checkSum() [Size]byte { // Length in bits. len <<= 3 - for i := uint(0); i < 16; i++ { - tmp[i] = byte(len >> (120 - 8*i)) - } + putUint64(tmp[0:], 0) // upper 64 bits are always zero, because len variable has type uint64 + putUint64(tmp[8:], len) d.Write(tmp[0:16]) if d.nx != 0 { panic("d.nx != 0") } - h := d.h[:] - if d.function == crypto.SHA384 { - h = d.h[:6] - } - var digest [Size]byte - for i, s := range h { - digest[i*8] = byte(s >> 56) - digest[i*8+1] = byte(s >> 48) - digest[i*8+2] = byte(s >> 40) - digest[i*8+3] = byte(s >> 32) - digest[i*8+4] = byte(s >> 24) - digest[i*8+5] = byte(s >> 16) - digest[i*8+6] = byte(s >> 8) - digest[i*8+7] = byte(s) + putUint64(digest[0:], d.h[0]) + putUint64(digest[8:], d.h[1]) + putUint64(digest[16:], d.h[2]) + putUint64(digest[24:], d.h[3]) + putUint64(digest[32:], d.h[4]) + putUint64(digest[40:], d.h[5]) + if d.function != crypto.SHA384 { + putUint64(digest[48:], d.h[6]) + putUint64(digest[56:], d.h[7]) } return digest diff --git a/libgo/go/crypto/sha512/sha512block_s390x.go b/libgo/go/crypto/sha512/sha512block_s390x.go index 26896fc..f8ab231 100644 --- a/libgo/go/crypto/sha512/sha512block_s390x.go +++ b/libgo/go/crypto/sha512/sha512block_s390x.go @@ -6,9 +6,6 @@ package sha512 -// featureCheck reports whether the CPU supports the -// SHA512 compute intermediate message digest (KIMD) -// function code. -func featureCheck() bool +import "internal/cpu" -var useAsm = featureCheck() +var useAsm = cpu.S390X.HasSHA512 diff --git a/libgo/go/crypto/tls/auth.go b/libgo/go/crypto/tls/auth.go new file mode 100644 index 0000000..88face4c --- /dev/null +++ b/libgo/go/crypto/tls/auth.go @@ -0,0 +1,108 @@ +// Copyright 2017 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 tls + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "encoding/asn1" + "errors" + "fmt" +) + +// pickSignatureAlgorithm selects a signature algorithm that is compatible with +// the given public key and the list of algorithms from the peer and this side. +// The lists of signature algorithms (peerSigAlgs and ourSigAlgs) are ignored +// for tlsVersion < VersionTLS12. +// +// The returned SignatureScheme codepoint is only meaningful for TLS 1.2, +// previous TLS versions have a fixed hash function. +func pickSignatureAlgorithm(pubkey crypto.PublicKey, peerSigAlgs, ourSigAlgs []SignatureScheme, tlsVersion uint16) (sigAlg SignatureScheme, sigType uint8, hashFunc crypto.Hash, err error) { + if tlsVersion < VersionTLS12 || len(peerSigAlgs) == 0 { + // For TLS 1.1 and before, the signature algorithm could not be + // negotiated and the hash is fixed based on the signature type. + // For TLS 1.2, if the client didn't send signature_algorithms + // extension then we can assume that it supports SHA1. See + // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 + switch pubkey.(type) { + case *rsa.PublicKey: + if tlsVersion < VersionTLS12 { + return 0, signaturePKCS1v15, crypto.MD5SHA1, nil + } else { + return PKCS1WithSHA1, signaturePKCS1v15, crypto.SHA1, nil + } + case *ecdsa.PublicKey: + return ECDSAWithSHA1, signatureECDSA, crypto.SHA1, nil + default: + return 0, 0, 0, fmt.Errorf("tls: unsupported public key: %T", pubkey) + } + } + for _, sigAlg := range peerSigAlgs { + if !isSupportedSignatureAlgorithm(sigAlg, ourSigAlgs) { + continue + } + hashAlg, err := lookupTLSHash(sigAlg) + if err != nil { + panic("tls: supported signature algorithm has an unknown hash function") + } + sigType := signatureFromSignatureScheme(sigAlg) + switch pubkey.(type) { + case *rsa.PublicKey: + if sigType == signaturePKCS1v15 || sigType == signatureRSAPSS { + return sigAlg, sigType, hashAlg, nil + } + case *ecdsa.PublicKey: + if sigType == signatureECDSA { + return sigAlg, sigType, hashAlg, nil + } + default: + return 0, 0, 0, fmt.Errorf("tls: unsupported public key: %T", pubkey) + } + } + return 0, 0, 0, errors.New("tls: peer doesn't support any common signature algorithms") +} + +// verifyHandshakeSignature verifies a signature against pre-hashed handshake +// contents. +func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, digest, sig []byte) error { + switch sigType { + case signatureECDSA: + pubKey, ok := pubkey.(*ecdsa.PublicKey) + if !ok { + return errors.New("tls: ECDSA signing requires a ECDSA public key") + } + ecdsaSig := new(ecdsaSignature) + if _, err := asn1.Unmarshal(sig, ecdsaSig); err != nil { + return err + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errors.New("tls: ECDSA signature contained zero or negative values") + } + if !ecdsa.Verify(pubKey, digest, ecdsaSig.R, ecdsaSig.S) { + return errors.New("tls: ECDSA verification failure") + } + case signaturePKCS1v15: + pubKey, ok := pubkey.(*rsa.PublicKey) + if !ok { + return errors.New("tls: RSA signing requires a RSA public key") + } + if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil { + return err + } + case signatureRSAPSS: + pubKey, ok := pubkey.(*rsa.PublicKey) + if !ok { + return errors.New("tls: RSA signing requires a RSA public key") + } + signOpts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash} + if err := rsa.VerifyPSS(pubKey, hashFunc, digest, sig, signOpts); err != nil { + return err + } + default: + return errors.New("tls: unknown signature algorithm") + } + return nil +} diff --git a/libgo/go/crypto/tls/auth_test.go b/libgo/go/crypto/tls/auth_test.go new file mode 100644 index 0000000..3f876b9 --- /dev/null +++ b/libgo/go/crypto/tls/auth_test.go @@ -0,0 +1,101 @@ +// Copyright 2017 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 tls + +import ( + "crypto" + "testing" +) + +func TestSignatureSelection(t *testing.T) { + rsaCert := &testRSAPrivateKey.PublicKey + ecdsaCert := &testECDSAPrivateKey.PublicKey + sigsPKCS1WithSHA := []SignatureScheme{PKCS1WithSHA256, PKCS1WithSHA1} + sigsPSSWithSHA := []SignatureScheme{PSSWithSHA256, PSSWithSHA384} + sigsECDSAWithSHA := []SignatureScheme{ECDSAWithP256AndSHA256, ECDSAWithSHA1} + + tests := []struct { + pubkey crypto.PublicKey + peerSigAlgs []SignatureScheme + ourSigAlgs []SignatureScheme + tlsVersion uint16 + + expectedSigAlg SignatureScheme // or 0 if ignored + expectedSigType uint8 + expectedHash crypto.Hash + }{ + // Hash is fixed for RSA in TLS 1.1 and before. + // https://tools.ietf.org/html/rfc4346#page-44 + {rsaCert, nil, nil, VersionTLS11, 0, signaturePKCS1v15, crypto.MD5SHA1}, + {rsaCert, nil, nil, VersionTLS10, 0, signaturePKCS1v15, crypto.MD5SHA1}, + {rsaCert, nil, nil, VersionSSL30, 0, signaturePKCS1v15, crypto.MD5SHA1}, + + // Before TLS 1.2, there is no signature_algorithms extension + // nor field in CertificateRequest and digitally-signed and thus + // it should be ignored. + {rsaCert, sigsPKCS1WithSHA, nil, VersionTLS11, 0, signaturePKCS1v15, crypto.MD5SHA1}, + {rsaCert, sigsPKCS1WithSHA, sigsPKCS1WithSHA, VersionTLS11, 0, signaturePKCS1v15, crypto.MD5SHA1}, + // Use SHA-1 for TLS 1.0 and 1.1 with ECDSA, see https://tools.ietf.org/html/rfc4492#page-20 + {ecdsaCert, sigsPKCS1WithSHA, sigsPKCS1WithSHA, VersionTLS11, 0, signatureECDSA, crypto.SHA1}, + {ecdsaCert, sigsPKCS1WithSHA, sigsPKCS1WithSHA, VersionTLS10, 0, signatureECDSA, crypto.SHA1}, + + // TLS 1.2 without signature_algorithms extension + // https://tools.ietf.org/html/rfc5246#page-47 + {rsaCert, nil, sigsPKCS1WithSHA, VersionTLS12, PKCS1WithSHA1, signaturePKCS1v15, crypto.SHA1}, + {ecdsaCert, nil, sigsPKCS1WithSHA, VersionTLS12, ECDSAWithSHA1, signatureECDSA, crypto.SHA1}, + + {rsaCert, []SignatureScheme{PKCS1WithSHA1}, sigsPKCS1WithSHA, VersionTLS12, PKCS1WithSHA1, signaturePKCS1v15, crypto.SHA1}, + {rsaCert, []SignatureScheme{PKCS1WithSHA256}, sigsPKCS1WithSHA, VersionTLS12, PKCS1WithSHA256, signaturePKCS1v15, crypto.SHA256}, + // "sha_hash" may denote hashes other than SHA-1 + // https://tools.ietf.org/html/draft-ietf-tls-rfc4492bis-17#page-17 + {ecdsaCert, []SignatureScheme{ECDSAWithSHA1}, sigsECDSAWithSHA, VersionTLS12, ECDSAWithSHA1, signatureECDSA, crypto.SHA1}, + {ecdsaCert, []SignatureScheme{ECDSAWithP256AndSHA256}, sigsECDSAWithSHA, VersionTLS12, ECDSAWithP256AndSHA256, signatureECDSA, crypto.SHA256}, + + // RSASSA-PSS is defined in TLS 1.3 for TLS 1.2 + // https://tools.ietf.org/html/draft-ietf-tls-tls13-21#page-45 + {rsaCert, []SignatureScheme{PSSWithSHA256}, sigsPSSWithSHA, VersionTLS12, PSSWithSHA256, signatureRSAPSS, crypto.SHA256}, + } + + for testNo, test := range tests { + sigAlg, sigType, hashFunc, err := pickSignatureAlgorithm(test.pubkey, test.peerSigAlgs, test.ourSigAlgs, test.tlsVersion) + if err != nil { + t.Errorf("test[%d]: unexpected error: %v", testNo, err) + } + if test.expectedSigAlg != 0 && test.expectedSigAlg != sigAlg { + t.Errorf("test[%d]: expected signature scheme %#x, got %#x", testNo, test.expectedSigAlg, sigAlg) + } + if test.expectedSigType != sigType { + t.Errorf("test[%d]: expected signature algorithm %#x, got %#x", testNo, test.expectedSigType, sigType) + } + if test.expectedHash != hashFunc { + t.Errorf("test[%d]: expected hash function %#x, got %#x", testNo, test.expectedHash, hashFunc) + } + } + + badTests := []struct { + pubkey crypto.PublicKey + peerSigAlgs []SignatureScheme + ourSigAlgs []SignatureScheme + tlsVersion uint16 + }{ + {rsaCert, sigsECDSAWithSHA, sigsPKCS1WithSHA, VersionTLS12}, + {ecdsaCert, sigsPKCS1WithSHA, sigsPKCS1WithSHA, VersionTLS12}, + {ecdsaCert, sigsECDSAWithSHA, sigsPKCS1WithSHA, VersionTLS12}, + {rsaCert, []SignatureScheme{0}, sigsPKCS1WithSHA, VersionTLS12}, + + // ECDSA is unspecified for SSL 3.0 in RFC 4492. + // TODO a SSL 3.0 client cannot advertise signature_algorithms, + // but if an application feeds an ECDSA certificate anyway, it + // will be accepted rather than trigger a handshake failure. Ok? + //{ecdsaCert, nil, nil, VersionSSL30}, + } + + for testNo, test := range badTests { + sigAlg, sigType, hashFunc, err := pickSignatureAlgorithm(test.pubkey, test.peerSigAlgs, test.ourSigAlgs, test.tlsVersion) + if err == nil { + t.Errorf("test[%d]: unexpected success, got %#x %#x %#x", testNo, sigAlg, sigType, hashFunc) + } + } +} diff --git a/libgo/go/crypto/tls/cipher_suites.go b/libgo/go/crypto/tls/cipher_suites.go index beb0f19..3c8dc4b 100644 --- a/libgo/go/crypto/tls/cipher_suites.go +++ b/libgo/go/crypto/tls/cipher_suites.go @@ -333,14 +333,14 @@ func rsaKA(version uint16) keyAgreement { func ecdheECDSAKA(version uint16) keyAgreement { return &ecdheKeyAgreement{ - sigType: signatureECDSA, + isRSA: false, version: version, } } func ecdheRSAKA(version uint16) keyAgreement { return &ecdheKeyAgreement{ - sigType: signatureRSA, + isRSA: true, version: version, } } @@ -364,7 +364,7 @@ func mutualCipherSuite(have []uint16, want uint16) *cipherSuite { // A list of cipher suite IDs that are, or have been, implemented by this // package. // -// Taken from http://www.iana.org/assignments/tls-parameters/tls-parameters.xml +// Taken from https://www.iana.org/assignments/tls-parameters/tls-parameters.xml const ( TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a diff --git a/libgo/go/crypto/tls/common.go b/libgo/go/crypto/tls/common.go index 646b107..7b627fc 100644 --- a/libgo/go/crypto/tls/common.go +++ b/libgo/go/crypto/tls/common.go @@ -7,12 +7,12 @@ package tls import ( "container/list" "crypto" - "crypto/internal/cipherhw" "crypto/rand" "crypto/sha512" "crypto/x509" "errors" "fmt" + "internal/cpu" "io" "math/big" "net" @@ -91,7 +91,7 @@ const ( ) // CurveID is the type of a TLS identifier for an elliptic curve. See -// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8 +// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8 type CurveID uint16 const ( @@ -102,7 +102,7 @@ const ( ) // TLS Elliptic Curve Point Formats -// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9 +// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9 const ( pointFormatUncompressed uint8 = 0 ) @@ -127,10 +127,12 @@ const ( // Rest of these are reserved by the TLS spec ) -// Signature algorithms for TLS 1.2 (See RFC 5246, section A.4.1) +// Signature algorithms (for internal signaling use). Starting at 16 to avoid overlap with +// TLS 1.2 codepoints (RFC 5246, section A.4.1), with which these have nothing to do. const ( - signatureRSA uint8 = 1 - signatureECDSA uint8 = 3 + signaturePKCS1v15 uint8 = iota + 16 + signatureECDSA + signatureRSAPSS ) // supportedSignatureAlgorithms contains the signature and hash algorithms that @@ -162,6 +164,9 @@ type ConnectionState struct { SignedCertificateTimestamps [][]byte // SCTs from the server, if any OCSPResponse []byte // stapled OCSP response from server, if any + // ekm is a closure exposed via ExportKeyingMaterial. + ekm func(label string, context []byte, length int) ([]byte, error) + // TLSUnique contains the "tls-unique" channel binding value (see RFC // 5929, section 3). For resumed sessions this value will be nil // because resumption does not include enough context (see @@ -171,6 +176,14 @@ type ConnectionState struct { TLSUnique []byte } +// ExportKeyingMaterial returns length bytes of exported key material in a new +// slice as defined in https://tools.ietf.org/html/rfc5705. If context is nil, +// it is not used as part of the seed. If the connection was set to allow +// renegotiation via Config.Renegotiation, this function will return an error. +func (cs *ConnectionState) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) { + return cs.ekm(label, context, length) +} + // ClientAuthType declares the policy the server will follow for // TLS Client Authentication. type ClientAuthType int @@ -240,19 +253,19 @@ type ClientHelloInfo struct { // ServerName indicates the name of the server requested by the client // in order to support virtual hosting. ServerName is only set if the // client is using SNI (see - // http://tools.ietf.org/html/rfc4366#section-3.1). + // https://tools.ietf.org/html/rfc4366#section-3.1). ServerName string // SupportedCurves lists the elliptic curves supported by the client. // SupportedCurves is set only if the Supported Elliptic Curves // Extension is being used (see - // http://tools.ietf.org/html/rfc4492#section-5.1.1). + // https://tools.ietf.org/html/rfc4492#section-5.1.1). SupportedCurves []CurveID // SupportedPoints lists the point formats supported by the client. // SupportedPoints is set only if the Supported Point Formats Extension // is being used (see - // http://tools.ietf.org/html/rfc4492#section-5.1.2). + // https://tools.ietf.org/html/rfc4492#section-5.1.2). SupportedPoints []uint8 // SignatureSchemes lists the signature and hash schemes that the client @@ -453,7 +466,8 @@ type Config struct { PreferServerCipherSuites bool // SessionTicketsDisabled may be set to true to disable session ticket - // (resumption) support. + // (resumption) support. Note that on clients, session ticket support is + // also disabled if ClientSessionCache is nil. SessionTicketsDisabled bool // SessionTicketKey is used by TLS servers to provide session @@ -467,7 +481,7 @@ type Config struct { SessionTicketKey [32]byte // ClientSessionCache is a cache of ClientSessionState entries for TLS - // session resumption. + // session resumption. It is only used by clients. ClientSessionCache ClientSessionCache // MinVersion contains the minimum SSL/TLS version that is acceptable. @@ -911,7 +925,19 @@ func defaultCipherSuites() []uint16 { func initDefaultCipherSuites() { var topCipherSuites []uint16 - if cipherhw.AESGCMSupport() { + + // Check the cpu flags for each platform that has optimized GCM implementations. + // Worst case, these variables will just all be false + hasGCMAsmAMD64 := cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ + + hasGCMAsmARM64 := cpu.ARM64.HasAES && cpu.ARM64.HasPMULL + + // Keep in sync with crypto/aes/cipher_s390x.go. + hasGCMAsmS390X := cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM) + + hasGCMAsm := hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X + + if hasGCMAsm { // If AES-GCM hardware is provided then prioritise AES-GCM // cipher suites. topCipherSuites = []uint16{ @@ -970,7 +996,9 @@ func isSupportedSignatureAlgorithm(sigAlg SignatureScheme, supportedSignatureAlg func signatureFromSignatureScheme(signatureAlgorithm SignatureScheme) uint8 { switch signatureAlgorithm { case PKCS1WithSHA1, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512: - return signatureRSA + return signaturePKCS1v15 + case PSSWithSHA256, PSSWithSHA384, PSSWithSHA512: + return signatureRSAPSS case ECDSAWithSHA1, ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512: return signatureECDSA default: diff --git a/libgo/go/crypto/tls/conn.go b/libgo/go/crypto/tls/conn.go index 31c5053..6e27e69 100644 --- a/libgo/go/crypto/tls/conn.go +++ b/libgo/go/crypto/tls/conn.go @@ -27,19 +27,16 @@ type Conn struct { conn net.Conn isClient bool - // constant after handshake; protected by handshakeMutex - handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex - // handshakeCond, if not nil, indicates that a goroutine is committed - // to running the handshake for this Conn. Other goroutines that need - // to wait for the handshake can wait on this, under handshakeMutex. - handshakeCond *sync.Cond - handshakeErr error // error resulting from handshake - vers uint16 // TLS version - haveVers bool // version has been negotiated - config *Config // configuration passed to constructor - // handshakeComplete is true if the connection is currently transferring + // handshakeStatus is 1 if the connection is currently transferring // application data (i.e. is not currently processing a handshake). - handshakeComplete bool + // This field is only to be accessed with sync/atomic. + handshakeStatus uint32 + // constant after handshake; protected by handshakeMutex + handshakeMutex sync.Mutex + handshakeErr error // error resulting from handshake + vers uint16 // TLS version + haveVers bool // version has been negotiated + config *Config // configuration passed to constructor // handshakes counts the number of handshakes performed on the // connection so far. If renegotiation is disabled then this is either // zero or one. @@ -58,6 +55,8 @@ type Conn struct { // renegotiation extension. (This is meaningless as a server because // renegotiation is not supported in that case.) secureRenegotiation bool + // ekm is a closure for exporting keying material. + ekm func(label string, context []byte, length int) ([]byte, error) // clientFinishedIsFirst is true if the client sent the first Finished // message during the most recent handshake. This is recorded because @@ -82,7 +81,7 @@ type Conn struct { clientProtocolFallback bool // input/output - in, out halfConn // in.Mutex < out.Mutex + in, out halfConn rawInput *block // raw input, right off the wire input *block // application data waiting to be read hand bytes.Buffer // handshake data waiting to be read @@ -564,7 +563,6 @@ func (c *Conn) newRecordHeaderError(msg string) (err RecordHeaderError) { // readRecord reads the next TLS record from the connection // and updates the record layer state. -// c.in.Mutex <= L; c.input == nil. func (c *Conn) readRecord(want recordType) error { // Caller must be in sync with connection: // handshake data if handshake not yet completed, @@ -574,12 +572,12 @@ func (c *Conn) readRecord(want recordType) error { c.sendAlert(alertInternalError) return c.in.setErrorLocked(errors.New("tls: unknown record type requested")) case recordTypeHandshake, recordTypeChangeCipherSpec: - if c.handshakeComplete { + if c.handshakeComplete() { c.sendAlert(alertInternalError) return c.in.setErrorLocked(errors.New("tls: handshake or ChangeCipherSpec requested while not in handshake")) } case recordTypeApplicationData: - if !c.handshakeComplete { + if !c.handshakeComplete() { c.sendAlert(alertInternalError) return c.in.setErrorLocked(errors.New("tls: application data record requested while in handshake")) } @@ -736,7 +734,6 @@ Again: } // sendAlert sends a TLS alert message. -// c.out.Mutex <= L. func (c *Conn) sendAlertLocked(err alert) error { switch err { case alertNoRenegotiation, alertCloseNotify: @@ -756,7 +753,6 @@ func (c *Conn) sendAlertLocked(err alert) error { } // sendAlert sends a TLS alert message. -// L < c.out.Mutex. func (c *Conn) sendAlert(err alert) error { c.out.Lock() defer c.out.Unlock() @@ -793,8 +789,6 @@ const ( // // In the interests of simplicity and determinism, this code does not attempt // to reset the record size once the connection is idle, however. -// -// c.out.Mutex <= L. func (c *Conn) maxPayloadSizeForWrite(typ recordType, explicitIVLen int) int { if c.config.DynamicRecordSizingDisabled || typ != recordTypeApplicationData { return maxPlaintext @@ -844,7 +838,6 @@ func (c *Conn) maxPayloadSizeForWrite(typ recordType, explicitIVLen int) int { return n } -// c.out.Mutex <= L. func (c *Conn) write(data []byte) (int, error) { if c.buffering { c.sendBuf = append(c.sendBuf, data...) @@ -870,7 +863,6 @@ func (c *Conn) flush() (int, error) { // writeRecordLocked writes a TLS record with the given type and payload to the // connection and updates the record layer state. -// c.out.Mutex <= L. func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) { b := c.out.newBlock() defer c.out.freeBlock(b) @@ -946,7 +938,6 @@ func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) { // writeRecord writes a TLS record with the given type and payload to the // connection and updates the record layer state. -// L < c.out.Mutex. func (c *Conn) writeRecord(typ recordType, data []byte) (int, error) { c.out.Lock() defer c.out.Unlock() @@ -956,7 +947,6 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (int, error) { // readHandshake reads the next handshake message from // the record layer. -// c.in.Mutex < L; c.out.Mutex < L. func (c *Conn) readHandshake() (interface{}, error) { for c.hand.Len() < 4 { if err := c.in.err; err != nil { @@ -1059,7 +1049,7 @@ func (c *Conn) Write(b []byte) (int, error) { return 0, err } - if !c.handshakeComplete { + if !c.handshakeComplete() { return 0, alertInternalError } @@ -1072,9 +1062,9 @@ func (c *Conn) Write(b []byte) (int, error) { // This can be prevented by splitting each Application Data // record into two records, effectively randomizing the IV. // - // http://www.openssl.org/~bodo/tls-cbc.txt + // https://www.openssl.org/~bodo/tls-cbc.txt // https://bugzilla.mozilla.org/show_bug.cgi?id=665814 - // http://www.imperialviolet.org/2012/01/15/beastfollowup.html + // https://www.imperialviolet.org/2012/01/15/beastfollowup.html var m int if len(b) > 1 && c.vers <= VersionTLS10 { @@ -1092,7 +1082,6 @@ func (c *Conn) Write(b []byte) (int, error) { } // handleRenegotiation processes a HelloRequest handshake message. -// c.in.Mutex <= L func (c *Conn) handleRenegotiation() error { msg, err := c.readHandshake() if err != nil { @@ -1126,7 +1115,7 @@ func (c *Conn) handleRenegotiation() error { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() - c.handshakeComplete = false + atomic.StoreUint32(&c.handshakeStatus, 0) if c.handshakeErr = c.clientHandshake(); c.handshakeErr == nil { c.handshakes++ } @@ -1227,11 +1216,9 @@ func (c *Conn) Close() error { var alertErr error - c.handshakeMutex.Lock() - if c.handshakeComplete { + if c.handshakeComplete() { alertErr = c.closeNotify() } - c.handshakeMutex.Unlock() if err := c.conn.Close(); err != nil { return err @@ -1245,9 +1232,7 @@ var errEarlyCloseWrite = errors.New("tls: CloseWrite called before handshake com // called once the handshake has completed and does not call CloseWrite on the // underlying connection. Most callers should just use Close. func (c *Conn) CloseWrite() error { - c.handshakeMutex.Lock() - defer c.handshakeMutex.Unlock() - if !c.handshakeComplete { + if !c.handshakeComplete() { return errEarlyCloseWrite } @@ -1270,61 +1255,19 @@ func (c *Conn) closeNotify() error { // Most uses of this package need not call Handshake // explicitly: the first Read or Write will call it automatically. func (c *Conn) Handshake() error { - // c.handshakeErr and c.handshakeComplete are protected by - // c.handshakeMutex. In order to perform a handshake, we need to lock - // c.in also and c.handshakeMutex must be locked after c.in. - // - // However, if a Read() operation is hanging then it'll be holding the - // lock on c.in and so taking it here would cause all operations that - // need to check whether a handshake is pending (such as Write) to - // block. - // - // Thus we first take c.handshakeMutex to check whether a handshake is - // needed. - // - // If so then, previously, this code would unlock handshakeMutex and - // then lock c.in and handshakeMutex in the correct order to run the - // handshake. The problem was that it was possible for a Read to - // complete the handshake once handshakeMutex was unlocked and then - // keep c.in while waiting for network data. Thus a concurrent - // operation could be blocked on c.in. - // - // Thus handshakeCond is used to signal that a goroutine is committed - // to running the handshake and other goroutines can wait on it if they - // need. handshakeCond is protected by handshakeMutex. c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() - for { - if err := c.handshakeErr; err != nil { - return err - } - if c.handshakeComplete { - return nil - } - if c.handshakeCond == nil { - break - } - - c.handshakeCond.Wait() + if err := c.handshakeErr; err != nil { + return err + } + if c.handshakeComplete() { + return nil } - - // Set handshakeCond to indicate that this goroutine is committing to - // running the handshake. - c.handshakeCond = sync.NewCond(&c.handshakeMutex) - c.handshakeMutex.Unlock() c.in.Lock() defer c.in.Unlock() - c.handshakeMutex.Lock() - - // The handshake cannot have completed when handshakeMutex was unlocked - // because this goroutine set handshakeCond. - if c.handshakeErr != nil || c.handshakeComplete { - panic("handshake should not have been able to complete after handshakeCond was set") - } - if c.isClient { c.handshakeErr = c.clientHandshake() } else { @@ -1338,15 +1281,10 @@ func (c *Conn) Handshake() error { c.flush() } - if c.handshakeErr == nil && !c.handshakeComplete { + if c.handshakeErr == nil && !c.handshakeComplete() { panic("handshake should have had a result.") } - // Wake any other goroutines that are waiting for this handshake to - // complete. - c.handshakeCond.Broadcast() - c.handshakeCond = nil - return c.handshakeErr } @@ -1356,10 +1294,10 @@ func (c *Conn) ConnectionState() ConnectionState { defer c.handshakeMutex.Unlock() var state ConnectionState - state.HandshakeComplete = c.handshakeComplete + state.HandshakeComplete = c.handshakeComplete() state.ServerName = c.serverName - if c.handshakeComplete { + if state.HandshakeComplete { state.Version = c.vers state.NegotiatedProtocol = c.clientProtocol state.DidResume = c.didResume @@ -1376,6 +1314,11 @@ func (c *Conn) ConnectionState() ConnectionState { state.TLSUnique = c.serverFinished[:] } } + if c.config.Renegotiation != RenegotiateNever { + state.ekm = noExportedKeyingMaterial + } else { + state.ekm = c.ekm + } } return state @@ -1399,7 +1342,7 @@ func (c *Conn) VerifyHostname(host string) error { if !c.isClient { return errors.New("tls: VerifyHostname called on TLS server connection") } - if !c.handshakeComplete { + if !c.handshakeComplete() { return errors.New("tls: handshake has not yet been performed") } if len(c.verifiedChains) == 0 { @@ -1407,3 +1350,7 @@ func (c *Conn) VerifyHostname(host string) error { } return c.peerCertificates[0].VerifyHostname(host) } + +func (c *Conn) handshakeComplete() bool { + return atomic.LoadUint32(&c.handshakeStatus) == 1 +} diff --git a/libgo/go/crypto/tls/generate_cert.go b/libgo/go/crypto/tls/generate_cert.go index 8ee2b59..8d012be 100644 --- a/libgo/go/crypto/tls/generate_cert.go +++ b/libgo/go/crypto/tls/generate_cert.go @@ -146,16 +146,24 @@ func main() { if err != nil { log.Fatalf("failed to open cert.pem for writing: %s", err) } - pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - certOut.Close() - log.Print("written cert.pem\n") + if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { + log.Fatalf("failed to write data to cert.pem: %s", err) + } + if err := certOut.Close(); err != nil { + log.Fatalf("error closing cert.pem: %s", err) + } + log.Print("wrote cert.pem\n") keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { log.Print("failed to open key.pem for writing:", err) return } - pem.Encode(keyOut, pemBlockForKey(priv)) - keyOut.Close() - log.Print("written key.pem\n") + if err := pem.Encode(keyOut, pemBlockForKey(priv)); err != nil { + log.Fatalf("failed to write data to key.pem: %s", err) + } + if err := keyOut.Close(); err != nil { + log.Fatalf("error closing key.pem: %s", err) + } + log.Print("wrote key.pem\n") } diff --git a/libgo/go/crypto/tls/handshake_client.go b/libgo/go/crypto/tls/handshake_client.go index e5e0df2..32fdc6d 100644 --- a/libgo/go/crypto/tls/handshake_client.go +++ b/libgo/go/crypto/tls/handshake_client.go @@ -17,6 +17,7 @@ import ( "net" "strconv" "strings" + "sync/atomic" ) type clientHandshakeState struct { @@ -91,7 +92,6 @@ NextCipherSuite: return hello, nil } -// c.out.Mutex <= L; c.handshakeMutex <= L. func (c *Conn) clientHandshake() error { if c.config == nil { c.config = defaultConfig() @@ -265,8 +265,9 @@ func (hs *clientHandshakeState) handshake() error { } } + c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random) c.didResume = isResume - c.handshakeComplete = true + atomic.StoreUint32(&c.handshakeStatus, 1) return nil } @@ -479,31 +480,25 @@ func (hs *clientHandshakeState) doFullHandshake() error { return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey) } - var signatureType uint8 - switch key.Public().(type) { - case *ecdsa.PublicKey: - signatureType = signatureECDSA - case *rsa.PublicKey: - signatureType = signatureRSA - default: + signatureAlgorithm, sigType, hashFunc, err := pickSignatureAlgorithm(key.Public(), certReq.supportedSignatureAlgorithms, hs.hello.supportedSignatureAlgorithms, c.vers) + if err != nil { c.sendAlert(alertInternalError) - return fmt.Errorf("tls: failed to sign handshake with client certificate: unknown client certificate key type: %T", key) + return err } - // SignatureAndHashAlgorithm was introduced in TLS 1.2. if certVerify.hasSignatureAndHash { - certVerify.signatureAlgorithm, err = hs.finishedHash.selectClientCertSignatureAlgorithm(certReq.supportedSignatureAlgorithms, signatureType) - if err != nil { - c.sendAlert(alertInternalError) - return err - } + certVerify.signatureAlgorithm = signatureAlgorithm } - digest, hashFunc, err := hs.finishedHash.hashForClientCertificate(signatureType, certVerify.signatureAlgorithm, hs.masterSecret) + digest, err := hs.finishedHash.hashForClientCertificate(sigType, hashFunc, hs.masterSecret) if err != nil { c.sendAlert(alertInternalError) return err } - certVerify.signature, err = key.Sign(c.config.rand(), digest, hashFunc) + signOpts := crypto.SignerOpts(hashFunc) + if sigType == signatureRSAPSS { + signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: hashFunc} + } + certVerify.signature, err = key.Sign(c.config.rand(), digest, signOpts) if err != nil { c.sendAlert(alertInternalError) return err diff --git a/libgo/go/crypto/tls/handshake_client_test.go b/libgo/go/crypto/tls/handshake_client_test.go index cc3ab71..1f1c93d 100644 --- a/libgo/go/crypto/tls/handshake_client_test.go +++ b/libgo/go/crypto/tls/handshake_client_test.go @@ -979,6 +979,24 @@ func TestRenegotiateTwiceRejected(t *testing.T) { runClientTestTLS12(t, test) } +func TestHandshakeClientExportKeyingMaterial(t *testing.T) { + test := &clientTest{ + name: "ExportKeyingMaterial", + command: []string{"openssl", "s_server"}, + config: testConfig.Clone(), + validate: func(state ConnectionState) error { + if km, err := state.ExportKeyingMaterial("test", nil, 42); err != nil { + return fmt.Errorf("ExportKeyingMaterial failed: %v", err) + } else if len(km) != 42 { + return fmt.Errorf("Got %d bytes from ExportKeyingMaterial, wanted %d", len(km), 42) + } + return nil + }, + } + runClientTestTLS10(t, test) + runClientTestTLS12(t, test) +} + var hostnameInSNITests = []struct { in, out string }{ @@ -1578,3 +1596,61 @@ func TestGetClientCertificate(t *testing.T) { } } } + +func TestRSAPSSKeyError(t *testing.T) { + // crypto/tls does not support the rsa_pss_pss_xxx SignatureSchemes. If support for + // public keys with OID RSASSA-PSS is added to crypto/x509, they will be misused with + // the rsa_pss_rsae_xxx SignatureSchemes. Assert that RSASSA-PSS certificates don't + // parse, or that they don't carry *rsa.PublicKey keys. + b, _ := pem.Decode([]byte(` +-----BEGIN CERTIFICATE----- +MIIDZTCCAhygAwIBAgIUCF2x0FyTgZG0CC9QTDjGWkB5vgEwPgYJKoZIhvcNAQEK +MDGgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogQC +AgDeMBIxEDAOBgNVBAMMB1JTQS1QU1MwHhcNMTgwNjI3MjI0NDM2WhcNMTgwNzI3 +MjI0NDM2WjASMRAwDgYDVQQDDAdSU0EtUFNTMIIBIDALBgkqhkiG9w0BAQoDggEP +ADCCAQoCggEBANxDm0f76JdI06YzsjB3AmmjIYkwUEGxePlafmIASFjDZl/elD0Z +/a7xLX468b0qGxLS5al7XCcEprSdsDR6DF5L520+pCbpfLyPOjuOvGmk9KzVX4x5 +b05YXYuXdsQ0Kjxcx2i3jjCday6scIhMJVgBZxTEyMj1thPQM14SHzKCd/m6HmCL +QmswpH2yMAAcBRWzRpp/vdH5DeOJEB3aelq7094no731mrLUCHRiZ1htq8BDB3ou +czwqgwspbqZ4dnMXl2MvfySQ5wJUxQwILbiuAKO2lVVPUbFXHE9pgtznNoPvKwQT +JNcX8ee8WIZc2SEGzofjk3NpjR+2ADB2u3sCAwEAAaNTMFEwHQYDVR0OBBYEFNEz +AdyJ2f+fU+vSCS6QzohnOnprMB8GA1UdIwQYMBaAFNEzAdyJ2f+fU+vSCS6Qzohn +OnprMA8GA1UdEwEB/wQFMAMBAf8wPgYJKoZIhvcNAQEKMDGgDTALBglghkgBZQME +AgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogQCAgDeA4IBAQCjEdrR5aab +sZmCwrMeKidXgfkmWvfuLDE+TCbaqDZp7BMWcMQXT9O0UoUT5kqgKj2ARm2pEW0Z +H3Z1vj3bbds72qcDIJXp+l0fekyLGeCrX/CbgnMZXEP7+/+P416p34ChR1Wz4dU1 +KD3gdsUuTKKeMUog3plxlxQDhRQmiL25ygH1LmjLd6dtIt0GVRGr8lj3euVeprqZ +bZ3Uq5eLfsn8oPgfC57gpO6yiN+UURRTlK3bgYvLh4VWB3XXk9UaQZ7Mq1tpXjoD +HYFybkWzibkZp4WRo+Fa28rirH+/wHt0vfeN7UCceURZEx4JaxIIfe4ku7uDRhJi +RwBA9Xk1KBNF +-----END CERTIFICATE-----`)) + if b == nil { + t.Fatal("Failed to decode certificate") + } + cert, err := x509.ParseCertificate(b.Bytes) + if err != nil { + return + } + if _, ok := cert.PublicKey.(*rsa.PublicKey); ok { + t.Error("A RSA-PSS certificate was parsed like a PKCS1 one, and it will be mistakenly used with rsa_pss_rsae_xxx signature algorithms") + } +} + +func TestCloseClientConnectionOnIdleServer(t *testing.T) { + clientConn, serverConn := net.Pipe() + client := Client(clientConn, testConfig.Clone()) + go func() { + var b [1]byte + serverConn.Read(b[:]) + client.Close() + }() + client.SetWriteDeadline(time.Now().Add(time.Second)) + err := client.Handshake() + if err != nil { + if !strings.Contains(err.Error(), "read/write on closed pipe") { + t.Errorf("Error expected containing 'read/write on closed pipe' but got '%s'", err.Error()) + } + } else { + t.Errorf("Error expected, but no error returned") + } +} diff --git a/libgo/go/crypto/tls/handshake_messages.go b/libgo/go/crypto/tls/handshake_messages.go index f8c8d57..a5bf10e 100644 --- a/libgo/go/crypto/tls/handshake_messages.go +++ b/libgo/go/crypto/tls/handshake_messages.go @@ -192,7 +192,7 @@ func (m *clientHelloMsg) marshal() []byte { z = z[9:] } if len(m.supportedCurves) > 0 { - // http://tools.ietf.org/html/rfc4492#section-5.5.1 + // https://tools.ietf.org/html/rfc4492#section-5.5.1 z[0] = byte(extensionSupportedCurves >> 8) z[1] = byte(extensionSupportedCurves) l := 2 + 2*len(m.supportedCurves) @@ -209,7 +209,7 @@ func (m *clientHelloMsg) marshal() []byte { } } if len(m.supportedPoints) > 0 { - // http://tools.ietf.org/html/rfc4492#section-5.5.2 + // https://tools.ietf.org/html/rfc4492#section-5.5.2 z[0] = byte(extensionSupportedPoints >> 8) z[1] = byte(extensionSupportedPoints) l := 1 + len(m.supportedPoints) @@ -224,7 +224,7 @@ func (m *clientHelloMsg) marshal() []byte { } } if m.ticketSupported { - // http://tools.ietf.org/html/rfc5077#section-3.2 + // https://tools.ietf.org/html/rfc5077#section-3.2 z[0] = byte(extensionSessionTicket >> 8) z[1] = byte(extensionSessionTicket) l := len(m.sessionTicket) @@ -414,7 +414,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { case extensionStatusRequest: m.ocspStapling = length > 0 && data[0] == statusTypeOCSP case extensionSupportedCurves: - // http://tools.ietf.org/html/rfc4492#section-5.5.1 + // https://tools.ietf.org/html/rfc4492#section-5.5.1 if length < 2 { return false } @@ -430,7 +430,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { d = d[2:] } case extensionSupportedPoints: - // http://tools.ietf.org/html/rfc4492#section-5.5.2 + // https://tools.ietf.org/html/rfc4492#section-5.5.2 if length < 1 { return false } @@ -441,7 +441,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { m.supportedPoints = make([]uint8, l) copy(m.supportedPoints, data[1:]) case extensionSessionTicket: - // http://tools.ietf.org/html/rfc5077#section-3.2 + // https://tools.ietf.org/html/rfc5077#section-3.2 m.ticketSupported = true m.sessionTicket = data[:length] case extensionSignatureAlgorithms: @@ -1224,7 +1224,7 @@ func (m *certificateRequestMsg) marshal() (x []byte) { return m.raw } - // See http://tools.ietf.org/html/rfc4346#section-7.4.4 + // See https://tools.ietf.org/html/rfc4346#section-7.4.4 length := 1 + len(m.certificateTypes) + 2 casLength := 0 for _, ca := range m.certificateAuthorities { @@ -1374,7 +1374,7 @@ func (m *certificateVerifyMsg) marshal() (x []byte) { return m.raw } - // See http://tools.ietf.org/html/rfc4346#section-7.4.8 + // See https://tools.ietf.org/html/rfc4346#section-7.4.8 siglength := len(m.signature) length := 2 + siglength if m.hasSignatureAndHash { @@ -1452,7 +1452,7 @@ func (m *newSessionTicketMsg) marshal() (x []byte) { return m.raw } - // See http://tools.ietf.org/html/rfc5077#section-3.3 + // See https://tools.ietf.org/html/rfc5077#section-3.3 ticketLen := len(m.ticket) length := 2 + 4 + ticketLen x = make([]byte, 4+length) diff --git a/libgo/go/crypto/tls/handshake_server.go b/libgo/go/crypto/tls/handshake_server.go index 991b4e9..ac491ba 100644 --- a/libgo/go/crypto/tls/handshake_server.go +++ b/libgo/go/crypto/tls/handshake_server.go @@ -10,10 +10,10 @@ import ( "crypto/rsa" "crypto/subtle" "crypto/x509" - "encoding/asn1" "errors" "fmt" "io" + "sync/atomic" ) // serverHandshakeState contains details of a server handshake in progress. @@ -36,7 +36,6 @@ type serverHandshakeState struct { } // serverHandshake performs a TLS handshake as a server. -// c.out.Mutex <= L; c.handshakeMutex <= L. func (c *Conn) serverHandshake() error { // If this is the first server handshake, we generate a random key to // encrypt the tickets with. @@ -103,7 +102,9 @@ func (c *Conn) serverHandshake() error { return err } } - c.handshakeComplete = true + + c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random) + atomic.StoreUint32(&c.handshakeStatus, 1) return nil } @@ -519,59 +520,15 @@ func (hs *serverHandshakeState) doFullHandshake() error { } // Determine the signature type. - var signatureAlgorithm SignatureScheme - var sigType uint8 - if certVerify.hasSignatureAndHash { - signatureAlgorithm = certVerify.signatureAlgorithm - if !isSupportedSignatureAlgorithm(signatureAlgorithm, supportedSignatureAlgorithms) { - return errors.New("tls: unsupported hash function for client certificate") - } - sigType = signatureFromSignatureScheme(signatureAlgorithm) - } else { - // Before TLS 1.2 the signature algorithm was implicit - // from the key type, and only one hash per signature - // algorithm was possible. Leave signatureAlgorithm - // unset. - switch pub.(type) { - case *ecdsa.PublicKey: - sigType = signatureECDSA - case *rsa.PublicKey: - sigType = signatureRSA - } + _, sigType, hashFunc, err := pickSignatureAlgorithm(pub, []SignatureScheme{certVerify.signatureAlgorithm}, supportedSignatureAlgorithms, c.vers) + if err != nil { + c.sendAlert(alertIllegalParameter) + return err } - switch key := pub.(type) { - case *ecdsa.PublicKey: - if sigType != signatureECDSA { - err = errors.New("tls: bad signature type for client's ECDSA certificate") - break - } - ecdsaSig := new(ecdsaSignature) - if _, err = asn1.Unmarshal(certVerify.signature, ecdsaSig); err != nil { - break - } - if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { - err = errors.New("tls: ECDSA signature contained zero or negative values") - break - } - var digest []byte - if digest, _, err = hs.finishedHash.hashForClientCertificate(sigType, signatureAlgorithm, hs.masterSecret); err != nil { - break - } - if !ecdsa.Verify(key, digest, ecdsaSig.R, ecdsaSig.S) { - err = errors.New("tls: ECDSA verification failure") - } - case *rsa.PublicKey: - if sigType != signatureRSA { - err = errors.New("tls: bad signature type for client's RSA certificate") - break - } - var digest []byte - var hashFunc crypto.Hash - if digest, hashFunc, err = hs.finishedHash.hashForClientCertificate(sigType, signatureAlgorithm, hs.masterSecret); err != nil { - break - } - err = rsa.VerifyPKCS1v15(key, hashFunc, digest, certVerify.signature) + var digest []byte + if digest, err = hs.finishedHash.hashForClientCertificate(sigType, hashFunc, hs.masterSecret); err == nil { + err = verifyHandshakeSignature(sigType, pub, hashFunc, digest, certVerify.signature) } if err != nil { c.sendAlert(alertBadCertificate) diff --git a/libgo/go/crypto/tls/handshake_server_test.go b/libgo/go/crypto/tls/handshake_server_test.go index 6716021..c366f47 100644 --- a/libgo/go/crypto/tls/handshake_server_test.go +++ b/libgo/go/crypto/tls/handshake_server_test.go @@ -194,9 +194,9 @@ func TestDontSelectRSAWithECDSAKey(t *testing.T) { func TestRenegotiationExtension(t *testing.T) { clientHello := &clientHelloMsg{ - vers: VersionTLS12, - compressionMethods: []uint8{compressionNone}, - random: make([]byte, 32), + vers: VersionTLS12, + compressionMethods: []uint8{compressionNone}, + random: make([]byte, 32), secureRenegotiationSupported: true, cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, } @@ -992,12 +992,30 @@ func TestFallbackSCSV(t *testing.T) { name: "FallbackSCSV", config: &serverConfig, // OpenSSL 1.0.1j is needed for the -fallback_scsv option. - command: []string{"openssl", "s_client", "-fallback_scsv"}, + command: []string{"openssl", "s_client", "-fallback_scsv"}, expectHandshakeErrorIncluding: "inappropriate protocol fallback", } runServerTestTLS11(t, test) } +func TestHandshakeServerExportKeyingMaterial(t *testing.T) { + test := &serverTest{ + name: "ExportKeyingMaterial", + command: []string{"openssl", "s_client"}, + config: testConfig.Clone(), + validate: func(state ConnectionState) error { + if km, err := state.ExportKeyingMaterial("test", nil, 42); err != nil { + return fmt.Errorf("ExportKeyingMaterial failed: %v", err) + } else if len(km) != 42 { + return fmt.Errorf("Got %d bytes from ExportKeyingMaterial, wanted %d", len(km), 42) + } + return nil + }, + } + runServerTestTLS10(t, test) + runServerTestTLS12(t, test) +} + func benchmarkHandshakeServer(b *testing.B, cipherSuite uint16, curve CurveID, cert []byte, key crypto.PrivateKey) { config := testConfig.Clone() config.CipherSuites = []uint16{cipherSuite} @@ -1403,3 +1421,21 @@ var testECDSAPrivateKey = &ecdsa.PrivateKey{ } var testP256PrivateKey, _ = x509.ParseECPrivateKey(fromHex("30770201010420012f3b52bc54c36ba3577ad45034e2e8efe1e6999851284cb848725cfe029991a00a06082a8648ce3d030107a14403420004c02c61c9b16283bbcc14956d886d79b358aa614596975f78cece787146abf74c2d5dc578c0992b4f3c631373479ebf3892efe53d21c4f4f1cc9a11c3536b7f75")) + +func TestCloseServerConnectionOnIdleClient(t *testing.T) { + clientConn, serverConn := net.Pipe() + server := Server(serverConn, testConfig.Clone()) + go func() { + clientConn.Write([]byte{'0'}) + server.Close() + }() + server.SetReadDeadline(time.Now().Add(time.Second)) + err := server.Handshake() + if err != nil { + if !strings.Contains(err.Error(), "read/write on closed pipe") { + t.Errorf("Error expected containing 'read/write on closed pipe' but got '%s'", err.Error()) + } + } else { + t.Errorf("Error expected, but no error returned") + } +} diff --git a/libgo/go/crypto/tls/key_agreement.go b/libgo/go/crypto/tls/key_agreement.go index 3f570b6..1e77fac 100644 --- a/libgo/go/crypto/tls/key_agreement.go +++ b/libgo/go/crypto/tls/key_agreement.go @@ -6,13 +6,11 @@ package tls import ( "crypto" - "crypto/ecdsa" "crypto/elliptic" "crypto/md5" "crypto/rsa" "crypto/sha1" "crypto/x509" - "encoding/asn1" "errors" "io" "math/big" @@ -110,58 +108,21 @@ func md5SHA1Hash(slices [][]byte) []byte { } // hashForServerKeyExchange hashes the given slices and returns their digest -// and the identifier of the hash function used. The signatureAlgorithm argument -// is only used for >= TLS 1.2 and identifies the hash function to use. -func hashForServerKeyExchange(sigType uint8, signatureAlgorithm SignatureScheme, version uint16, slices ...[]byte) ([]byte, crypto.Hash, error) { +// using the given hash function (for >= TLS 1.2) or using a default based on +// the sigType (for earlier TLS versions). +func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint16, slices ...[]byte) ([]byte, error) { if version >= VersionTLS12 { - if !isSupportedSignatureAlgorithm(signatureAlgorithm, supportedSignatureAlgorithms) { - return nil, crypto.Hash(0), errors.New("tls: unsupported hash function used by peer") - } - hashFunc, err := lookupTLSHash(signatureAlgorithm) - if err != nil { - return nil, crypto.Hash(0), err - } h := hashFunc.New() for _, slice := range slices { h.Write(slice) } digest := h.Sum(nil) - return digest, hashFunc, nil + return digest, nil } if sigType == signatureECDSA { - return sha1Hash(slices), crypto.SHA1, nil - } - return md5SHA1Hash(slices), crypto.MD5SHA1, nil -} - -// pickTLS12HashForSignature returns a TLS 1.2 hash identifier for signing a -// ServerKeyExchange given the signature type being used and the client's -// advertised list of supported signature and hash combinations. -func pickTLS12HashForSignature(sigType uint8, clientList []SignatureScheme) (SignatureScheme, error) { - if len(clientList) == 0 { - // If the client didn't specify any signature_algorithms - // extension then we can assume that it supports SHA1. See - // http://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 - switch sigType { - case signatureRSA: - return PKCS1WithSHA1, nil - case signatureECDSA: - return ECDSAWithSHA1, nil - default: - return 0, errors.New("tls: unknown signature algorithm") - } + return sha1Hash(slices), nil } - - for _, sigAlg := range clientList { - if signatureFromSignatureScheme(sigAlg) != sigType { - continue - } - if isSupportedSignatureAlgorithm(sigAlg, supportedSignatureAlgorithms) { - return sigAlg, nil - } - } - - return 0, errors.New("tls: client doesn't support any common hash functions") + return md5SHA1Hash(slices), nil } func curveForCurveID(id CurveID) (elliptic.Curve, bool) { @@ -178,13 +139,13 @@ func curveForCurveID(id CurveID) (elliptic.Curve, bool) { } -// ecdheRSAKeyAgreement implements a TLS key agreement where the server +// ecdheKeyAgreement implements a TLS key agreement where the server // generates an ephemeral EC public/private key pair and signs it. The // pre-master secret is then calculated using ECDH. The signature may // either be ECDSA or RSA. type ecdheKeyAgreement struct { version uint16 - sigType uint8 + isRSA bool privateKey []byte curveid CurveID @@ -239,7 +200,7 @@ NextCandidate: ecdhePublic = elliptic.Marshal(curve, x, y) } - // http://tools.ietf.org/html/rfc4492#section-5.4 + // https://tools.ietf.org/html/rfc4492#section-5.4 serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic)) serverECDHParams[0] = 3 // named curve serverECDHParams[1] = byte(ka.curveid >> 8) @@ -247,41 +208,29 @@ NextCandidate: serverECDHParams[3] = byte(len(ecdhePublic)) copy(serverECDHParams[4:], ecdhePublic) - var signatureAlgorithm SignatureScheme - - if ka.version >= VersionTLS12 { - var err error - signatureAlgorithm, err = pickTLS12HashForSignature(ka.sigType, clientHello.supportedSignatureAlgorithms) - if err != nil { - return nil, err - } + priv, ok := cert.PrivateKey.(crypto.Signer) + if !ok { + return nil, errors.New("tls: certificate private key does not implement crypto.Signer") } - digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, signatureAlgorithm, ka.version, clientHello.random, hello.random, serverECDHParams) + signatureAlgorithm, sigType, hashFunc, err := pickSignatureAlgorithm(priv.Public(), clientHello.supportedSignatureAlgorithms, supportedSignatureAlgorithms, ka.version) if err != nil { return nil, err } + if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA { + return nil, errors.New("tls: certificate cannot be used with the selected cipher suite") + } - priv, ok := cert.PrivateKey.(crypto.Signer) - if !ok { - return nil, errors.New("tls: certificate private key does not implement crypto.Signer") + digest, err := hashForServerKeyExchange(sigType, hashFunc, ka.version, clientHello.random, hello.random, serverECDHParams) + if err != nil { + return nil, err } - var sig []byte - switch ka.sigType { - case signatureECDSA: - _, ok := priv.Public().(*ecdsa.PublicKey) - if !ok { - return nil, errors.New("tls: ECDHE ECDSA requires an ECDSA server key") - } - case signatureRSA: - _, ok := priv.Public().(*rsa.PublicKey) - if !ok { - return nil, errors.New("tls: ECDHE RSA requires a RSA server key") - } - default: - return nil, errors.New("tls: unknown ECDHE signature algorithm") + + signOpts := crypto.SignerOpts(hashFunc) + if sigType == signatureRSAPSS { + signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: hashFunc} } - sig, err = priv.Sign(config.rand(), digest, hashFunc) + sig, err := priv.Sign(config.rand(), digest, signOpts) if err != nil { return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error()) } @@ -380,53 +329,30 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell if ka.version >= VersionTLS12 { // handle SignatureAndHashAlgorithm signatureAlgorithm = SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1]) - if signatureFromSignatureScheme(signatureAlgorithm) != ka.sigType { - return errServerKeyExchange - } sig = sig[2:] if len(sig) < 2 { return errServerKeyExchange } } + _, sigType, hashFunc, err := pickSignatureAlgorithm(cert.PublicKey, []SignatureScheme{signatureAlgorithm}, clientHello.supportedSignatureAlgorithms, ka.version) + if err != nil { + return err + } + if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA { + return errServerKeyExchange + } + sigLen := int(sig[0])<<8 | int(sig[1]) if sigLen+2 != len(sig) { return errServerKeyExchange } sig = sig[2:] - digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, signatureAlgorithm, ka.version, clientHello.random, serverHello.random, serverECDHParams) + digest, err := hashForServerKeyExchange(sigType, hashFunc, ka.version, clientHello.random, serverHello.random, serverECDHParams) if err != nil { return err } - switch ka.sigType { - case signatureECDSA: - pubKey, ok := cert.PublicKey.(*ecdsa.PublicKey) - if !ok { - return errors.New("tls: ECDHE ECDSA requires a ECDSA server public key") - } - ecdsaSig := new(ecdsaSignature) - if _, err := asn1.Unmarshal(sig, ecdsaSig); err != nil { - return err - } - if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { - return errors.New("tls: ECDSA signature contained zero or negative values") - } - if !ecdsa.Verify(pubKey, digest, ecdsaSig.R, ecdsaSig.S) { - return errors.New("tls: ECDSA verification failure") - } - case signatureRSA: - pubKey, ok := cert.PublicKey.(*rsa.PublicKey) - if !ok { - return errors.New("tls: ECDHE RSA requires a RSA server public key") - } - if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil { - return err - } - default: - return errors.New("tls: unknown ECDHE signature algorithm") - } - - return nil + return verifyHandshakeSignature(sigType, cert.PublicKey, hashFunc, digest, sig) } func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { diff --git a/libgo/go/crypto/tls/prf.go b/libgo/go/crypto/tls/prf.go index 74438f8..a8cf21d 100644 --- a/libgo/go/crypto/tls/prf.go +++ b/libgo/go/crypto/tls/prf.go @@ -140,7 +140,7 @@ func prfForVersion(version uint16, suite *cipherSuite) func(result, secret, labe } // masterFromPreMasterSecret generates the master secret from the pre-master -// secret. See http://tools.ietf.org/html/rfc5246#section-8.1 +// secret. See https://tools.ietf.org/html/rfc5246#section-8.1 func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, clientRandom, serverRandom []byte) []byte { seed := make([]byte, 0, len(clientRandom)+len(serverRandom)) seed = append(seed, clientRandom...) @@ -309,50 +309,35 @@ func (h finishedHash) serverSum(masterSecret []byte) []byte { return out } -// selectClientCertSignatureAlgorithm returns a SignatureScheme to sign a -// client's CertificateVerify with, or an error if none can be found. -func (h finishedHash) selectClientCertSignatureAlgorithm(serverList []SignatureScheme, sigType uint8) (SignatureScheme, error) { - for _, v := range serverList { - if signatureFromSignatureScheme(v) == sigType && isSupportedSignatureAlgorithm(v, supportedSignatureAlgorithms) { - return v, nil - } - } - return 0, errors.New("tls: no supported signature algorithm found for signing client certificate") -} - -// hashForClientCertificate returns a digest, hash function, and TLS 1.2 hash -// id suitable for signing by a TLS client certificate. -func (h finishedHash) hashForClientCertificate(sigType uint8, signatureAlgorithm SignatureScheme, masterSecret []byte) ([]byte, crypto.Hash, error) { +// hashForClientCertificate returns a digest over the handshake messages so far, +// suitable for signing by a TLS client certificate. +func (h finishedHash) hashForClientCertificate(sigType uint8, hashAlg crypto.Hash, masterSecret []byte) ([]byte, error) { if (h.version == VersionSSL30 || h.version >= VersionTLS12) && h.buffer == nil { panic("a handshake hash for a client-certificate was requested after discarding the handshake buffer") } if h.version == VersionSSL30 { - if sigType != signatureRSA { - return nil, 0, errors.New("tls: unsupported signature type for client certificate") + if sigType != signaturePKCS1v15 { + return nil, errors.New("tls: unsupported signature type for client certificate") } md5Hash := md5.New() md5Hash.Write(h.buffer) sha1Hash := sha1.New() sha1Hash.Write(h.buffer) - return finishedSum30(md5Hash, sha1Hash, masterSecret, nil), crypto.MD5SHA1, nil + return finishedSum30(md5Hash, sha1Hash, masterSecret, nil), nil } if h.version >= VersionTLS12 { - hashAlg, err := lookupTLSHash(signatureAlgorithm) - if err != nil { - return nil, 0, err - } hash := hashAlg.New() hash.Write(h.buffer) - return hash.Sum(nil), hashAlg, nil + return hash.Sum(nil), nil } if sigType == signatureECDSA { - return h.server.Sum(nil), crypto.SHA1, nil + return h.server.Sum(nil), nil } - return h.Sum(), crypto.MD5SHA1, nil + return h.Sum(), nil } // discardHandshakeBuffer is called when there is no more need to @@ -360,3 +345,43 @@ func (h finishedHash) hashForClientCertificate(sigType uint8, signatureAlgorithm func (h *finishedHash) discardHandshakeBuffer() { h.buffer = nil } + +// noExportedKeyingMaterial is used as a value of +// ConnectionState.ekm when renegotation is enabled and thus +// we wish to fail all key-material export requests. +func noExportedKeyingMaterial(label string, context []byte, length int) ([]byte, error) { + return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled") +} + +// ekmFromMasterSecret generates exported keying material as defined in +// https://tools.ietf.org/html/rfc5705. +func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte) func(string, []byte, int) ([]byte, error) { + return func(label string, context []byte, length int) ([]byte, error) { + switch label { + case "client finished", "server finished", "master secret", "key expansion": + // These values are reserved and may not be used. + return nil, fmt.Errorf("crypto/tls: reserved ExportKeyingMaterial label: %s", label) + } + + seedLen := len(serverRandom) + len(clientRandom) + if context != nil { + seedLen += 2 + len(context) + } + seed := make([]byte, 0, seedLen) + + seed = append(seed, clientRandom...) + seed = append(seed, serverRandom...) + + if context != nil { + if len(context) >= 1<<16 { + return nil, fmt.Errorf("crypto/tls: ExportKeyingMaterial context too long") + } + seed = append(seed, byte(len(context)>>8), byte(len(context))) + seed = append(seed, context...) + } + + keyMaterial := make([]byte, length) + prfForVersion(version, suite)(keyMaterial, masterSecret, []byte(label), seed) + return keyMaterial, nil + } +} diff --git a/libgo/go/crypto/tls/prf_test.go b/libgo/go/crypto/tls/prf_test.go index 0a1b1bc..f201253 100644 --- a/libgo/go/crypto/tls/prf_test.go +++ b/libgo/go/crypto/tls/prf_test.go @@ -34,14 +34,15 @@ func TestSplitPreMasterSecret(t *testing.T) { } type testKeysFromTest struct { - version uint16 - suite *cipherSuite - preMasterSecret string - clientRandom, serverRandom string - masterSecret string - clientMAC, serverMAC string - clientKey, serverKey string - macLen, keyLen int + version uint16 + suite *cipherSuite + preMasterSecret string + clientRandom, serverRandom string + masterSecret string + clientMAC, serverMAC string + clientKey, serverKey string + macLen, keyLen int + contextKeyingMaterial, noContextKeyingMaterial string } func TestKeysFromPreMasterSecret(t *testing.T) { @@ -67,6 +68,22 @@ func TestKeysFromPreMasterSecret(t *testing.T) { serverKeyString != test.serverKey { t.Errorf("#%d: got: (%s, %s, %s, %s) want: (%s, %s, %s, %s)", i, clientMACString, serverMACString, clientKeyString, serverKeyString, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey) } + + ekm := ekmFromMasterSecret(test.version, test.suite, masterSecret, clientRandom, serverRandom) + contextKeyingMaterial, err := ekm("label", []byte("context"), 32) + if err != nil { + t.Fatalf("ekmFromMasterSecret failed: %v", err) + } + + noContextKeyingMaterial, err := ekm("label", nil, 32) + if err != nil { + t.Fatalf("ekmFromMasterSecret failed: %v", err) + } + + if hex.EncodeToString(contextKeyingMaterial) != test.contextKeyingMaterial || + hex.EncodeToString(noContextKeyingMaterial) != test.noContextKeyingMaterial { + t.Errorf("#%d: got keying material: (%s, %s) want: (%s, %s)", i, contextKeyingMaterial, noContextKeyingMaterial, test.contextKeyingMaterial, test.noContextKeyingMaterial) + } } } @@ -94,6 +111,8 @@ var testKeysFromTests = []testKeysFromTest{ "e076e33206b30507a85c32855acd0919", 20, 16, + "4d1bb6fc278c37d27aa6e2a13c2e079095d143272c2aa939da33d88c1c0cec22", + "93fba89599b6321ae538e27c6548ceb8b46821864318f5190d64a375e5d69d41", }, { VersionTLS10, @@ -108,6 +127,8 @@ var testKeysFromTests = []testKeysFromTest{ "df3f94f6e1eacc753b815fe16055cd43", 20, 16, + "2c9f8961a72b97cbe76553b5f954caf8294fc6360ef995ac1256fe9516d0ce7f", + "274f19c10291d188857ad8878e2119f5aa437d4da556601cf1337aff23154016", }, { VersionTLS10, @@ -122,6 +143,8 @@ var testKeysFromTests = []testKeysFromTest{ "ff07edde49682b45466bd2e39464b306", 20, 16, + "678b0d43f607de35241dc7e9d1a7388a52c35033a1a0336d4d740060a6638fe2", + "f3b4ac743f015ef21d79978297a53da3e579ee047133f38c234d829c0f907dab", }, { VersionSSL30, @@ -136,5 +159,7 @@ var testKeysFromTests = []testKeysFromTest{ "2b9d4b4a60cb7f396780ebff50650419", 20, 16, + "d230d8fc4f695be60368635e5268c414ca3ae0995dd93aba9f877272049f35bf", + "6b5e9646e04df8e99482a9b22dbfbe42ddd4725e4b041d02d11e4ef44ad13120", }, } diff --git a/libgo/go/crypto/tls/testdata/Client-TLSv10-ExportKeyingMaterial b/libgo/go/crypto/tls/testdata/Client-TLSv10-ExportKeyingMaterial new file mode 100644 index 0000000..571769e --- /dev/null +++ b/libgo/go/crypto/tls/testdata/Client-TLSv10-ExportKeyingMaterial @@ -0,0 +1,89 @@ +>>> Flow 1 (client to server) +00000000 16 03 01 00 95 01 00 00 91 03 03 00 00 00 00 00 |................| +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 2c cc a8 |.............,..| +00000030 cc a9 c0 2f c0 2b c0 30 c0 2c c0 27 c0 13 c0 23 |.../.+.0.,.'...#| +00000040 c0 09 c0 14 c0 0a 00 9c 00 9d 00 3c 00 2f 00 35 |...........<./.5| +00000050 c0 12 00 0a 00 05 c0 11 c0 07 01 00 00 3c 00 05 |.............<..| +00000060 00 05 01 00 00 00 00 00 0a 00 0a 00 08 00 1d 00 |................| +00000070 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 12 00 |................| +00000080 10 04 01 04 03 05 01 05 03 06 01 06 03 02 01 02 |................| +00000090 03 ff 01 00 01 00 00 12 00 00 |..........| +>>> Flow 2 (server to client) +00000000 16 03 01 00 59 02 00 00 55 03 01 67 4f 02 da 87 |....Y...U..gO...| +00000010 52 30 9a f0 3b e0 63 42 bf 6c 18 58 00 06 70 cf |R0..;.cB.l.X..p.| +00000020 2a 27 5a 00 a7 57 49 fe 03 dd 3b 20 7c 2c 74 00 |*'Z..WI...; |,t.| +00000030 6e b2 35 ca 1b b5 8c 46 f7 78 ab 11 92 43 8c f6 |n.5....F.x...C..| +00000040 97 d3 b8 07 4c 9c 95 2b 08 fe e8 82 c0 13 00 00 |....L..+........| +00000050 0d ff 01 00 01 00 00 0b 00 04 03 00 01 02 16 03 |................| +00000060 01 02 59 0b 00 02 55 00 02 52 00 02 4f 30 82 02 |..Y...U..R..O0..| +00000070 4b 30 82 01 b4 a0 03 02 01 02 02 09 00 e8 f0 9d |K0..............| +00000080 3f e2 5b ea a6 30 0d 06 09 2a 86 48 86 f7 0d 01 |?.[..0...*.H....| +00000090 01 0b 05 00 30 1f 31 0b 30 09 06 03 55 04 0a 13 |....0.1.0...U...| +000000a0 02 47 6f 31 10 30 0e 06 03 55 04 03 13 07 47 6f |.Go1.0...U....Go| +000000b0 20 52 6f 6f 74 30 1e 17 0d 31 36 30 31 30 31 30 | Root0...1601010| +000000c0 30 30 30 30 30 5a 17 0d 32 35 30 31 30 31 30 30 |00000Z..25010100| +000000d0 30 30 30 30 5a 30 1a 31 0b 30 09 06 03 55 04 0a |0000Z0.1.0...U..| +000000e0 13 02 47 6f 31 0b 30 09 06 03 55 04 03 13 02 47 |..Go1.0...U....G| +000000f0 6f 30 81 9f 30 0d 06 09 2a 86 48 86 f7 0d 01 01 |o0..0...*.H.....| +00000100 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 db 46 |.......0.......F| +00000110 7d 93 2e 12 27 06 48 bc 06 28 21 ab 7e c4 b6 a2 |}...'.H..(!.~...| +00000120 5d fe 1e 52 45 88 7a 36 47 a5 08 0d 92 42 5b c2 |]..RE.z6G....B[.| +00000130 81 c0 be 97 79 98 40 fb 4f 6d 14 fd 2b 13 8b c2 |....y.@.Om..+...| +00000140 a5 2e 67 d8 d4 09 9e d6 22 38 b7 4a 0b 74 73 2b |..g....."8.J.ts+| +00000150 c2 34 f1 d1 93 e5 96 d9 74 7b f3 58 9f 6c 61 3c |.4......t{.X.la<| +00000160 c0 b0 41 d4 d9 2b 2b 24 23 77 5b 1c 3b bd 75 5d |..A..++$#w[.;.u]| +00000170 ce 20 54 cf a1 63 87 1d 1e 24 c4 f3 1d 1a 50 8b |. T..c...$....P.| +00000180 aa b6 14 43 ed 97 a7 75 62 f4 14 c8 52 d7 02 03 |...C...ub...R...| +00000190 01 00 01 a3 81 93 30 81 90 30 0e 06 03 55 1d 0f |......0..0...U..| +000001a0 01 01 ff 04 04 03 02 05 a0 30 1d 06 03 55 1d 25 |.........0...U.%| +000001b0 04 16 30 14 06 08 2b 06 01 05 05 07 03 01 06 08 |..0...+.........| +000001c0 2b 06 01 05 05 07 03 02 30 0c 06 03 55 1d 13 01 |+.......0...U...| +000001d0 01 ff 04 02 30 00 30 19 06 03 55 1d 0e 04 12 04 |....0.0...U.....| +000001e0 10 9f 91 16 1f 43 43 3e 49 a6 de 6d b6 80 d7 9f |.....CC>I..m....| +000001f0 60 30 1b 06 03 55 1d 23 04 14 30 12 80 10 48 13 |`0...U.#..0...H.| +00000200 49 4d 13 7e 16 31 bb a3 01 d5 ac ab 6e 7b 30 19 |IM.~.1......n{0.| +00000210 06 03 55 1d 11 04 12 30 10 82 0e 65 78 61 6d 70 |..U....0...examp| +00000220 6c 65 2e 67 6f 6c 61 6e 67 30 0d 06 09 2a 86 48 |le.golang0...*.H| +00000230 86 f7 0d 01 01 0b 05 00 03 81 81 00 9d 30 cc 40 |.............0.@| +00000240 2b 5b 50 a0 61 cb ba e5 53 58 e1 ed 83 28 a9 58 |+[P.a...SX...(.X| +00000250 1a a9 38 a4 95 a1 ac 31 5a 1a 84 66 3d 43 d3 2d |..8....1Z..f=C.-| +00000260 d9 0b f2 97 df d3 20 64 38 92 24 3a 00 bc cf 9c |...... d8.$:....| +00000270 7d b7 40 20 01 5f aa d3 16 61 09 a2 76 fd 13 c3 |}.@ ._...a..v...| +00000280 cc e1 0c 5c ee b1 87 82 f1 6c 04 ed 73 bb b3 43 |...\.....l..s..C| +00000290 77 8d 0c 1c f1 0f a1 d8 40 83 61 c9 4c 72 2b 9d |w.......@.a.Lr+.| +000002a0 ae db 46 06 06 4d f4 c1 b3 3e c0 d1 bd 42 d4 db |..F..M...>...B..| +000002b0 fe 3d 13 60 84 5c 21 d3 3b e9 fa e7 16 03 01 00 |.=.`.\!.;.......| +000002c0 aa 0c 00 00 a6 03 00 1d 20 a0 0e 1d 92 2d b0 a5 |........ ....-..| +000002d0 f0 ab d5 79 a0 bb 12 ff 23 46 bc 27 0d 73 ff 3e |...y....#F.'.s.>| +000002e0 ad 06 d6 57 6b c2 11 76 2d 00 80 77 bf cd 2b cb |...Wk..v-..w..+.| +000002f0 66 c2 fa 30 ed b1 e7 44 79 1b 28 e6 89 62 17 07 |f..0...Dy.(..b..| +00000300 82 c1 5f dc b2 20 4e 42 ed 54 d6 28 3a 2a e3 a3 |.._.. NB.T.(:*..| +00000310 79 06 e3 08 3c c1 3e b9 c6 41 71 2f d0 29 82 36 |y...<.>..Aq/.).6| +00000320 ef 8d 67 c8 77 d0 32 d3 33 5f 77 92 dd 98 bb 03 |..g.w.2.3_w.....| +00000330 cc 0b a6 75 8f 4a 1d f5 6e 1b 06 5b 4a 8b 16 a4 |...u.J..n..[J...| +00000340 c1 ce 11 9d 70 bc 62 7f 58 a5 86 76 91 3d 3a 04 |....p.b.X..v.=:.| +00000350 93 92 89 42 9b a7 7d 9d 75 25 6d 98 f3 e6 68 7e |...B..}.u%m...h~| +00000360 a8 c6 b1 db a7 95 63 39 94 5a 05 16 03 01 00 04 |......c9.Z......| +00000370 0e 00 00 00 |....| +>>> Flow 3 (client to server) +00000000 16 03 01 00 25 10 00 00 21 20 2f e5 7d a3 47 cd |....%...! /.}.G.| +00000010 62 43 15 28 da ac 5f bb 29 07 30 ff f6 84 af c4 |bC.(.._.).0.....| +00000020 cf c2 ed 90 99 5f 58 cb 3b 74 14 03 01 00 01 01 |....._X.;t......| +00000030 16 03 01 00 30 73 ad 46 66 66 e8 bd 44 e4 bf 71 |....0s.Fff..D..q| +00000040 a2 d4 87 e2 4b a3 4a b2 a0 ca ed ac 61 8c 1e 7f |....K.J.....a...| +00000050 68 bf 6f 98 b1 fb 10 1a 5a e6 36 61 91 ac c4 55 |h.o.....Z.6a...U| +00000060 a3 4d 69 66 6e |.Mifn| +>>> Flow 4 (server to client) +00000000 14 03 01 00 01 01 16 03 01 00 30 57 aa 5c d5 dc |..........0W.\..| +00000010 83 4b 23 80 34 4e 36 e8 d6 f3 40 7e ae 12 44 a6 |.K#.4N6...@~..D.| +00000020 c7 48 99 99 0a 85 3c 59 75 32 4e 88 3c 98 a0 23 |.H....<Yu2N.<..#| +00000030 78 c8 a7 2b 43 25 6a ad d1 78 54 |x..+C%j..xT| +>>> Flow 5 (client to server) +00000000 17 03 01 00 20 e4 9c f4 fa 6b e8 85 87 6f 20 45 |.... ....k...o E| +00000010 71 d3 e2 9e e3 14 2a 7c 64 e8 11 53 fd 93 c1 4a |q.....*|d..S...J| +00000020 1b 94 f8 48 78 17 03 01 00 20 b9 41 32 1d e8 70 |...Hx.... .A2..p| +00000030 87 5f 2c c6 67 d1 77 3c 30 83 0c 66 35 eb 1d da |._,.g.w<0..f5...| +00000040 6e dd 30 ff 82 05 5f f1 cd e7 15 03 01 00 20 6c |n.0..._....... l| +00000050 47 82 5e 90 5b 84 15 78 05 bd 48 63 d5 46 2f 7e |G.^.[..x..Hc.F/~| +00000060 83 49 ce 3c 0f 04 92 52 5b e7 d5 cf 2c bf 65 |.I.<...R[...,.e| diff --git a/libgo/go/crypto/tls/testdata/Client-TLSv12-ExportKeyingMaterial b/libgo/go/crypto/tls/testdata/Client-TLSv12-ExportKeyingMaterial new file mode 100644 index 0000000..29964f0 --- /dev/null +++ b/libgo/go/crypto/tls/testdata/Client-TLSv12-ExportKeyingMaterial @@ -0,0 +1,84 @@ +>>> Flow 1 (client to server) +00000000 16 03 01 00 95 01 00 00 91 03 03 00 00 00 00 00 |................| +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 2c cc a8 |.............,..| +00000030 cc a9 c0 2f c0 2b c0 30 c0 2c c0 27 c0 13 c0 23 |.../.+.0.,.'...#| +00000040 c0 09 c0 14 c0 0a 00 9c 00 9d 00 3c 00 2f 00 35 |...........<./.5| +00000050 c0 12 00 0a 00 05 c0 11 c0 07 01 00 00 3c 00 05 |.............<..| +00000060 00 05 01 00 00 00 00 00 0a 00 0a 00 08 00 1d 00 |................| +00000070 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 12 00 |................| +00000080 10 04 01 04 03 05 01 05 03 06 01 06 03 02 01 02 |................| +00000090 03 ff 01 00 01 00 00 12 00 00 |..........| +>>> Flow 2 (server to client) +00000000 16 03 03 00 59 02 00 00 55 03 03 fc 37 e8 a4 e3 |....Y...U...7...| +00000010 5d da a5 95 0b fb e0 c3 d9 78 8b 91 bd 5c 1c b1 |]........x...\..| +00000020 c6 8d 69 62 f9 c6 0f 12 da 46 ba 20 34 a3 22 f2 |..ib.....F. 4.".| +00000030 a9 f7 da 3a c4 5f 6f f7 4b be df 03 e5 b6 d0 ff |...:._o.K.......| +00000040 ca 54 68 59 57 53 63 a5 2f 91 1d 1e cc a8 00 00 |.ThYWSc./.......| +00000050 0d ff 01 00 01 00 00 0b 00 04 03 00 01 02 16 03 |................| +00000060 03 02 59 0b 00 02 55 00 02 52 00 02 4f 30 82 02 |..Y...U..R..O0..| +00000070 4b 30 82 01 b4 a0 03 02 01 02 02 09 00 e8 f0 9d |K0..............| +00000080 3f e2 5b ea a6 30 0d 06 09 2a 86 48 86 f7 0d 01 |?.[..0...*.H....| +00000090 01 0b 05 00 30 1f 31 0b 30 09 06 03 55 04 0a 13 |....0.1.0...U...| +000000a0 02 47 6f 31 10 30 0e 06 03 55 04 03 13 07 47 6f |.Go1.0...U....Go| +000000b0 20 52 6f 6f 74 30 1e 17 0d 31 36 30 31 30 31 30 | Root0...1601010| +000000c0 30 30 30 30 30 5a 17 0d 32 35 30 31 30 31 30 30 |00000Z..25010100| +000000d0 30 30 30 30 5a 30 1a 31 0b 30 09 06 03 55 04 0a |0000Z0.1.0...U..| +000000e0 13 02 47 6f 31 0b 30 09 06 03 55 04 03 13 02 47 |..Go1.0...U....G| +000000f0 6f 30 81 9f 30 0d 06 09 2a 86 48 86 f7 0d 01 01 |o0..0...*.H.....| +00000100 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 db 46 |.......0.......F| +00000110 7d 93 2e 12 27 06 48 bc 06 28 21 ab 7e c4 b6 a2 |}...'.H..(!.~...| +00000120 5d fe 1e 52 45 88 7a 36 47 a5 08 0d 92 42 5b c2 |]..RE.z6G....B[.| +00000130 81 c0 be 97 79 98 40 fb 4f 6d 14 fd 2b 13 8b c2 |....y.@.Om..+...| +00000140 a5 2e 67 d8 d4 09 9e d6 22 38 b7 4a 0b 74 73 2b |..g....."8.J.ts+| +00000150 c2 34 f1 d1 93 e5 96 d9 74 7b f3 58 9f 6c 61 3c |.4......t{.X.la<| +00000160 c0 b0 41 d4 d9 2b 2b 24 23 77 5b 1c 3b bd 75 5d |..A..++$#w[.;.u]| +00000170 ce 20 54 cf a1 63 87 1d 1e 24 c4 f3 1d 1a 50 8b |. T..c...$....P.| +00000180 aa b6 14 43 ed 97 a7 75 62 f4 14 c8 52 d7 02 03 |...C...ub...R...| +00000190 01 00 01 a3 81 93 30 81 90 30 0e 06 03 55 1d 0f |......0..0...U..| +000001a0 01 01 ff 04 04 03 02 05 a0 30 1d 06 03 55 1d 25 |.........0...U.%| +000001b0 04 16 30 14 06 08 2b 06 01 05 05 07 03 01 06 08 |..0...+.........| +000001c0 2b 06 01 05 05 07 03 02 30 0c 06 03 55 1d 13 01 |+.......0...U...| +000001d0 01 ff 04 02 30 00 30 19 06 03 55 1d 0e 04 12 04 |....0.0...U.....| +000001e0 10 9f 91 16 1f 43 43 3e 49 a6 de 6d b6 80 d7 9f |.....CC>I..m....| +000001f0 60 30 1b 06 03 55 1d 23 04 14 30 12 80 10 48 13 |`0...U.#..0...H.| +00000200 49 4d 13 7e 16 31 bb a3 01 d5 ac ab 6e 7b 30 19 |IM.~.1......n{0.| +00000210 06 03 55 1d 11 04 12 30 10 82 0e 65 78 61 6d 70 |..U....0...examp| +00000220 6c 65 2e 67 6f 6c 61 6e 67 30 0d 06 09 2a 86 48 |le.golang0...*.H| +00000230 86 f7 0d 01 01 0b 05 00 03 81 81 00 9d 30 cc 40 |.............0.@| +00000240 2b 5b 50 a0 61 cb ba e5 53 58 e1 ed 83 28 a9 58 |+[P.a...SX...(.X| +00000250 1a a9 38 a4 95 a1 ac 31 5a 1a 84 66 3d 43 d3 2d |..8....1Z..f=C.-| +00000260 d9 0b f2 97 df d3 20 64 38 92 24 3a 00 bc cf 9c |...... d8.$:....| +00000270 7d b7 40 20 01 5f aa d3 16 61 09 a2 76 fd 13 c3 |}.@ ._...a..v...| +00000280 cc e1 0c 5c ee b1 87 82 f1 6c 04 ed 73 bb b3 43 |...\.....l..s..C| +00000290 77 8d 0c 1c f1 0f a1 d8 40 83 61 c9 4c 72 2b 9d |w.......@.a.Lr+.| +000002a0 ae db 46 06 06 4d f4 c1 b3 3e c0 d1 bd 42 d4 db |..F..M...>...B..| +000002b0 fe 3d 13 60 84 5c 21 d3 3b e9 fa e7 16 03 03 00 |.=.`.\!.;.......| +000002c0 ac 0c 00 00 a8 03 00 1d 20 cc e9 71 f5 36 52 5a |........ ..q.6RZ| +000002d0 d8 19 ce e4 0d 41 8d a6 9b f3 19 56 8d 81 fe 84 |.....A.....V....| +000002e0 71 2f d7 fb e7 86 23 4c 04 04 01 00 80 90 da 29 |q/....#L.......)| +000002f0 79 18 70 e8 81 66 83 70 97 f1 d1 5f dc 1d a2 0a |y.p..f.p..._....| +00000300 94 d8 e8 b8 32 4f 03 34 0b af e8 2d 94 b2 eb 30 |....2O.4...-...0| +00000310 57 b5 a5 92 9e 9a df a6 bc 3e 25 0e 18 cb ea 84 |W........>%.....| +00000320 34 89 08 8a d4 be 16 a3 5d 3a 7d 32 10 9b 41 1c |4.......]:}2..A.| +00000330 2a 1e 05 68 5f fa d9 56 30 b6 44 08 b0 a5 25 5a |*..h_..V0.D...%Z| +00000340 c3 60 c0 9a 98 fd 48 5f a4 18 d0 15 0f fb b3 ea |.`....H_........| +00000350 b9 c4 e3 c6 0c 27 51 64 01 de 65 78 c7 a0 57 df |.....'Qd..ex..W.| +00000360 9b de 2f 74 bc 72 e5 e0 57 7c 59 e6 ae 16 03 03 |../t.r..W|Y.....| +00000370 00 04 0e 00 00 00 |......| +>>> Flow 3 (client to server) +00000000 16 03 03 00 25 10 00 00 21 20 2f e5 7d a3 47 cd |....%...! /.}.G.| +00000010 62 43 15 28 da ac 5f bb 29 07 30 ff f6 84 af c4 |bC.(.._.).0.....| +00000020 cf c2 ed 90 99 5f 58 cb 3b 74 14 03 03 00 01 01 |....._X.;t......| +00000030 16 03 03 00 20 92 0a 4e aa 2d b3 9b c8 b9 80 28 |.... ..N.-.....(| +00000040 f3 22 e2 57 15 ff a1 9a 33 9b e8 4c 5c dc f4 29 |.".W....3..L\..)| +00000050 7d 25 d7 df bc |}%...| +>>> Flow 4 (server to client) +00000000 14 03 03 00 01 01 16 03 03 00 20 91 85 06 0e 00 |.......... .....| +00000010 ad 96 2e 1c a5 4d f7 63 f9 84 1c 6e da 54 0b e0 |.....M.c...n.T..| +00000020 44 37 6a 90 4c fd f5 e8 45 1d ce |D7j.L...E..| +>>> Flow 5 (client to server) +00000000 17 03 03 00 16 4c e8 8a e0 a6 95 f3 df 37 8a 2d |.....L.......7.-| +00000010 4f 11 ce a6 53 16 2c b0 bb c5 7f 15 03 03 00 12 |O...S.,.........| +00000020 4e 91 d8 67 c5 16 d2 4e cc b8 0a 00 76 91 68 7a |N..g...N....v.hz| +00000030 85 2e |..| diff --git a/libgo/go/crypto/tls/testdata/Server-TLSv10-ExportKeyingMaterial b/libgo/go/crypto/tls/testdata/Server-TLSv10-ExportKeyingMaterial new file mode 100644 index 0000000..84e0e37 --- /dev/null +++ b/libgo/go/crypto/tls/testdata/Server-TLSv10-ExportKeyingMaterial @@ -0,0 +1,92 @@ +>>> Flow 1 (client to server) +00000000 16 03 01 00 61 01 00 00 5d 03 01 f4 ec 99 73 ec |....a...].....s.| +00000010 36 30 c7 0b 26 33 a2 c4 26 8e 9f 04 f7 5b e7 4f |60..&3..&....[.O| +00000020 86 85 14 bf f7 49 96 a4 ae c9 1d 00 00 12 c0 0a |.....I..........| +00000030 c0 14 00 39 c0 09 c0 13 00 33 00 35 00 2f 00 ff |...9.....3.5./..| +00000040 01 00 00 22 00 0b 00 04 03 00 01 02 00 0a 00 0a |..."............| +00000050 00 08 00 1d 00 17 00 19 00 18 00 23 00 00 00 16 |...........#....| +00000060 00 00 00 17 00 00 |......| +>>> Flow 2 (server to client) +00000000 16 03 01 00 35 02 00 00 31 03 01 00 00 00 00 00 |....5...1.......| +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 c0 14 00 00 |................| +00000030 09 00 23 00 00 ff 01 00 01 00 16 03 01 02 59 0b |..#...........Y.| +00000040 00 02 55 00 02 52 00 02 4f 30 82 02 4b 30 82 01 |..U..R..O0..K0..| +00000050 b4 a0 03 02 01 02 02 09 00 e8 f0 9d 3f e2 5b ea |............?.[.| +00000060 a6 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 |.0...*.H........| +00000070 30 1f 31 0b 30 09 06 03 55 04 0a 13 02 47 6f 31 |0.1.0...U....Go1| +00000080 10 30 0e 06 03 55 04 03 13 07 47 6f 20 52 6f 6f |.0...U....Go Roo| +00000090 74 30 1e 17 0d 31 36 30 31 30 31 30 30 30 30 30 |t0...16010100000| +000000a0 30 5a 17 0d 32 35 30 31 30 31 30 30 30 30 30 30 |0Z..250101000000| +000000b0 5a 30 1a 31 0b 30 09 06 03 55 04 0a 13 02 47 6f |Z0.1.0...U....Go| +000000c0 31 0b 30 09 06 03 55 04 03 13 02 47 6f 30 81 9f |1.0...U....Go0..| +000000d0 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 |0...*.H.........| +000000e0 81 8d 00 30 81 89 02 81 81 00 db 46 7d 93 2e 12 |...0.......F}...| +000000f0 27 06 48 bc 06 28 21 ab 7e c4 b6 a2 5d fe 1e 52 |'.H..(!.~...]..R| +00000100 45 88 7a 36 47 a5 08 0d 92 42 5b c2 81 c0 be 97 |E.z6G....B[.....| +00000110 79 98 40 fb 4f 6d 14 fd 2b 13 8b c2 a5 2e 67 d8 |y.@.Om..+.....g.| +00000120 d4 09 9e d6 22 38 b7 4a 0b 74 73 2b c2 34 f1 d1 |...."8.J.ts+.4..| +00000130 93 e5 96 d9 74 7b f3 58 9f 6c 61 3c c0 b0 41 d4 |....t{.X.la<..A.| +00000140 d9 2b 2b 24 23 77 5b 1c 3b bd 75 5d ce 20 54 cf |.++$#w[.;.u]. T.| +00000150 a1 63 87 1d 1e 24 c4 f3 1d 1a 50 8b aa b6 14 43 |.c...$....P....C| +00000160 ed 97 a7 75 62 f4 14 c8 52 d7 02 03 01 00 01 a3 |...ub...R.......| +00000170 81 93 30 81 90 30 0e 06 03 55 1d 0f 01 01 ff 04 |..0..0...U......| +00000180 04 03 02 05 a0 30 1d 06 03 55 1d 25 04 16 30 14 |.....0...U.%..0.| +00000190 06 08 2b 06 01 05 05 07 03 01 06 08 2b 06 01 05 |..+.........+...| +000001a0 05 07 03 02 30 0c 06 03 55 1d 13 01 01 ff 04 02 |....0...U.......| +000001b0 30 00 30 19 06 03 55 1d 0e 04 12 04 10 9f 91 16 |0.0...U.........| +000001c0 1f 43 43 3e 49 a6 de 6d b6 80 d7 9f 60 30 1b 06 |.CC>I..m....`0..| +000001d0 03 55 1d 23 04 14 30 12 80 10 48 13 49 4d 13 7e |.U.#..0...H.IM.~| +000001e0 16 31 bb a3 01 d5 ac ab 6e 7b 30 19 06 03 55 1d |.1......n{0...U.| +000001f0 11 04 12 30 10 82 0e 65 78 61 6d 70 6c 65 2e 67 |...0...example.g| +00000200 6f 6c 61 6e 67 30 0d 06 09 2a 86 48 86 f7 0d 01 |olang0...*.H....| +00000210 01 0b 05 00 03 81 81 00 9d 30 cc 40 2b 5b 50 a0 |.........0.@+[P.| +00000220 61 cb ba e5 53 58 e1 ed 83 28 a9 58 1a a9 38 a4 |a...SX...(.X..8.| +00000230 95 a1 ac 31 5a 1a 84 66 3d 43 d3 2d d9 0b f2 97 |...1Z..f=C.-....| +00000240 df d3 20 64 38 92 24 3a 00 bc cf 9c 7d b7 40 20 |.. d8.$:....}.@ | +00000250 01 5f aa d3 16 61 09 a2 76 fd 13 c3 cc e1 0c 5c |._...a..v......\| +00000260 ee b1 87 82 f1 6c 04 ed 73 bb b3 43 77 8d 0c 1c |.....l..s..Cw...| +00000270 f1 0f a1 d8 40 83 61 c9 4c 72 2b 9d ae db 46 06 |....@.a.Lr+...F.| +00000280 06 4d f4 c1 b3 3e c0 d1 bd 42 d4 db fe 3d 13 60 |.M...>...B...=.`| +00000290 84 5c 21 d3 3b e9 fa e7 16 03 01 00 aa 0c 00 00 |.\!.;...........| +000002a0 a6 03 00 1d 20 2f e5 7d a3 47 cd 62 43 15 28 da |.... /.}.G.bC.(.| +000002b0 ac 5f bb 29 07 30 ff f6 84 af c4 cf c2 ed 90 99 |._.).0..........| +000002c0 5f 58 cb 3b 74 00 80 8e fe 28 f2 06 d8 b9 d6 74 |_X.;t....(.....t| +000002d0 72 34 dc fa 00 38 56 1a fc a1 68 e8 ca 8f 7a 61 |r4...8V...h...za| +000002e0 92 e2 2a 63 ce 4d 96 c6 bb 84 82 41 2d 97 35 13 |..*c.M.....A-.5.| +000002f0 e1 ff 4c ec f2 e6 62 16 15 35 da 8a 57 55 cb 28 |..L...b..5..WU.(| +00000300 26 35 e6 86 00 b0 92 44 b7 40 7b 6a c4 b0 b8 10 |&5.....D.@{j....| +00000310 b7 16 97 a7 26 eb 1e 0b 99 b3 22 4a 6b 7f 0b 69 |....&....."Jk..i| +00000320 0d 21 1e 33 6d fd 78 b5 62 68 53 db 62 69 ba b4 |.!.3m.x.bhS.bi..| +00000330 bc 74 b3 d4 ce a2 41 d7 ba 62 aa cc b2 39 65 86 |.t....A..b...9e.| +00000340 5f 00 68 e2 16 a5 13 16 03 01 00 04 0e 00 00 00 |_.h.............| +>>> Flow 3 (client to server) +00000000 16 03 01 00 25 10 00 00 21 20 81 08 e4 37 1d 03 |....%...! ...7..| +00000010 87 5a 00 68 ae 49 76 08 4a e2 20 82 0b e5 7c 3e |.Z.h.Iv.J. ...|>| +00000020 90 49 9b c3 b9 c7 c9 3c 29 24 14 03 01 00 01 01 |.I.....<)$......| +00000030 16 03 01 00 30 33 07 d5 08 ca ae f9 70 50 93 0a |....03......pP..| +00000040 55 2e e0 df 1d 88 ae 1e 06 17 47 64 a3 52 36 37 |U.........Gd.R67| +00000050 d5 ca f1 b1 d2 76 7b f8 89 59 13 e9 ab b1 cb dc |.....v{..Y......| +00000060 1f a8 89 f4 2f |..../| +>>> Flow 4 (server to client) +00000000 16 03 01 00 82 04 00 00 7e 00 00 00 00 00 78 50 |........~.....xP| +00000010 46 ad c1 db a8 38 86 7b 2b bb fd d0 c3 42 3e 00 |F....8.{+....B>.| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 |................| +00000030 6d ec a4 83 61 a4 a1 9c 14 de f8 59 c8 c7 f0 10 |m...a......Y....| +00000040 08 fe c9 37 29 ed 47 05 d2 bd a8 4c 05 b9 8c f8 |...7).G....L....| +00000050 b5 4d e4 a6 30 0f 49 4a b1 73 1f 89 73 c8 bb 36 |.M..0.IJ.s..s..6| +00000060 14 9d d2 95 70 33 94 fb 82 e6 fe 3e 64 8c 9d e8 |....p3.....>d...| +00000070 e3 e5 93 3d fe 4e 23 a3 97 8a a3 91 80 c9 00 01 |...=.N#.........| +00000080 a6 f0 47 cf 11 a6 90 14 03 01 00 01 01 16 03 01 |..G.............| +00000090 00 30 1f 70 17 a1 30 82 5a 32 e7 aa a1 7f 1b f6 |.0.p..0.Z2......| +000000a0 d8 aa 6a 51 64 1b 4a f1 94 12 08 2f 5d 95 fe 83 |..jQd.J..../]...| +000000b0 52 c8 3b d4 58 73 50 19 b8 08 61 b3 3a 5d f6 d3 |R.;.XsP...a.:]..| +000000c0 67 e6 17 03 01 00 20 bd 79 44 08 9d 86 cf 5e e9 |g..... .yD....^.| +000000d0 e4 3c 80 ed b7 18 10 07 0f 42 85 ca a4 51 fd 9b |.<.......B...Q..| +000000e0 38 3e 04 7e 72 6e 80 17 03 01 00 30 2c 46 c2 71 |8>.~rn.....0,F.q| +000000f0 4a 83 46 eb 63 87 f5 83 b4 72 70 4f a3 59 b3 ff |J.F.c....rpO.Y..| +00000100 3c 00 74 12 db 33 51 4c 7c e0 c1 27 44 20 68 25 |<.t..3QL|..'D h%| +00000110 95 f1 37 2a 24 f1 85 a3 5a e4 50 fe 15 03 01 00 |..7*$...Z.P.....| +00000120 20 72 01 cc 74 d5 b4 6b 05 ce de f0 b4 fe 4f 6b | r..t..k......Ok| +00000130 a8 8f ad 5a c2 7d 40 65 d6 a2 57 52 b8 8a c5 4f |...Z.}@e..WR...O| +00000140 d9 |.| diff --git a/libgo/go/crypto/tls/testdata/Server-TLSv12-ExportKeyingMaterial b/libgo/go/crypto/tls/testdata/Server-TLSv12-ExportKeyingMaterial new file mode 100644 index 0000000..6415c42 --- /dev/null +++ b/libgo/go/crypto/tls/testdata/Server-TLSv12-ExportKeyingMaterial @@ -0,0 +1,92 @@ +>>> Flow 1 (client to server) +00000000 16 03 01 00 ab 01 00 00 a7 03 03 7a 49 9d 20 62 |...........zI. b| +00000010 45 8d 0c 1e 8e eb b1 5e 73 62 6d 48 61 31 cb 1a |E......^sbmHa1..| +00000020 89 b2 68 1b 2c cb 35 87 2a 17 fb 00 00 38 c0 2c |..h.,.5.*....8.,| +00000030 c0 30 00 9f cc a9 cc a8 cc aa c0 2b c0 2f 00 9e |.0.........+./..| +00000040 c0 24 c0 28 00 6b c0 23 c0 27 00 67 c0 0a c0 14 |.$.(.k.#.'.g....| +00000050 00 39 c0 09 c0 13 00 33 00 9d 00 9c 00 3d 00 3c |.9.....3.....=.<| +00000060 00 35 00 2f 00 ff 01 00 00 46 00 0b 00 04 03 00 |.5./.....F......| +00000070 01 02 00 0a 00 0a 00 08 00 1d 00 17 00 19 00 18 |................| +00000080 00 23 00 00 00 16 00 00 00 17 00 00 00 0d 00 20 |.#............. | +00000090 00 1e 06 01 06 02 06 03 05 01 05 02 05 03 04 01 |................| +000000a0 04 02 04 03 03 01 03 02 03 03 02 01 02 02 02 03 |................| +>>> Flow 2 (server to client) +00000000 16 03 03 00 35 02 00 00 31 03 03 00 00 00 00 00 |....5...1.......| +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 c0 30 00 00 |.............0..| +00000030 09 00 23 00 00 ff 01 00 01 00 16 03 03 02 59 0b |..#...........Y.| +00000040 00 02 55 00 02 52 00 02 4f 30 82 02 4b 30 82 01 |..U..R..O0..K0..| +00000050 b4 a0 03 02 01 02 02 09 00 e8 f0 9d 3f e2 5b ea |............?.[.| +00000060 a6 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 |.0...*.H........| +00000070 30 1f 31 0b 30 09 06 03 55 04 0a 13 02 47 6f 31 |0.1.0...U....Go1| +00000080 10 30 0e 06 03 55 04 03 13 07 47 6f 20 52 6f 6f |.0...U....Go Roo| +00000090 74 30 1e 17 0d 31 36 30 31 30 31 30 30 30 30 30 |t0...16010100000| +000000a0 30 5a 17 0d 32 35 30 31 30 31 30 30 30 30 30 30 |0Z..250101000000| +000000b0 5a 30 1a 31 0b 30 09 06 03 55 04 0a 13 02 47 6f |Z0.1.0...U....Go| +000000c0 31 0b 30 09 06 03 55 04 03 13 02 47 6f 30 81 9f |1.0...U....Go0..| +000000d0 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 |0...*.H.........| +000000e0 81 8d 00 30 81 89 02 81 81 00 db 46 7d 93 2e 12 |...0.......F}...| +000000f0 27 06 48 bc 06 28 21 ab 7e c4 b6 a2 5d fe 1e 52 |'.H..(!.~...]..R| +00000100 45 88 7a 36 47 a5 08 0d 92 42 5b c2 81 c0 be 97 |E.z6G....B[.....| +00000110 79 98 40 fb 4f 6d 14 fd 2b 13 8b c2 a5 2e 67 d8 |y.@.Om..+.....g.| +00000120 d4 09 9e d6 22 38 b7 4a 0b 74 73 2b c2 34 f1 d1 |...."8.J.ts+.4..| +00000130 93 e5 96 d9 74 7b f3 58 9f 6c 61 3c c0 b0 41 d4 |....t{.X.la<..A.| +00000140 d9 2b 2b 24 23 77 5b 1c 3b bd 75 5d ce 20 54 cf |.++$#w[.;.u]. T.| +00000150 a1 63 87 1d 1e 24 c4 f3 1d 1a 50 8b aa b6 14 43 |.c...$....P....C| +00000160 ed 97 a7 75 62 f4 14 c8 52 d7 02 03 01 00 01 a3 |...ub...R.......| +00000170 81 93 30 81 90 30 0e 06 03 55 1d 0f 01 01 ff 04 |..0..0...U......| +00000180 04 03 02 05 a0 30 1d 06 03 55 1d 25 04 16 30 14 |.....0...U.%..0.| +00000190 06 08 2b 06 01 05 05 07 03 01 06 08 2b 06 01 05 |..+.........+...| +000001a0 05 07 03 02 30 0c 06 03 55 1d 13 01 01 ff 04 02 |....0...U.......| +000001b0 30 00 30 19 06 03 55 1d 0e 04 12 04 10 9f 91 16 |0.0...U.........| +000001c0 1f 43 43 3e 49 a6 de 6d b6 80 d7 9f 60 30 1b 06 |.CC>I..m....`0..| +000001d0 03 55 1d 23 04 14 30 12 80 10 48 13 49 4d 13 7e |.U.#..0...H.IM.~| +000001e0 16 31 bb a3 01 d5 ac ab 6e 7b 30 19 06 03 55 1d |.1......n{0...U.| +000001f0 11 04 12 30 10 82 0e 65 78 61 6d 70 6c 65 2e 67 |...0...example.g| +00000200 6f 6c 61 6e 67 30 0d 06 09 2a 86 48 86 f7 0d 01 |olang0...*.H....| +00000210 01 0b 05 00 03 81 81 00 9d 30 cc 40 2b 5b 50 a0 |.........0.@+[P.| +00000220 61 cb ba e5 53 58 e1 ed 83 28 a9 58 1a a9 38 a4 |a...SX...(.X..8.| +00000230 95 a1 ac 31 5a 1a 84 66 3d 43 d3 2d d9 0b f2 97 |...1Z..f=C.-....| +00000240 df d3 20 64 38 92 24 3a 00 bc cf 9c 7d b7 40 20 |.. d8.$:....}.@ | +00000250 01 5f aa d3 16 61 09 a2 76 fd 13 c3 cc e1 0c 5c |._...a..v......\| +00000260 ee b1 87 82 f1 6c 04 ed 73 bb b3 43 77 8d 0c 1c |.....l..s..Cw...| +00000270 f1 0f a1 d8 40 83 61 c9 4c 72 2b 9d ae db 46 06 |....@.a.Lr+...F.| +00000280 06 4d f4 c1 b3 3e c0 d1 bd 42 d4 db fe 3d 13 60 |.M...>...B...=.`| +00000290 84 5c 21 d3 3b e9 fa e7 16 03 03 00 ac 0c 00 00 |.\!.;...........| +000002a0 a8 03 00 1d 20 2f e5 7d a3 47 cd 62 43 15 28 da |.... /.}.G.bC.(.| +000002b0 ac 5f bb 29 07 30 ff f6 84 af c4 cf c2 ed 90 99 |._.).0..........| +000002c0 5f 58 cb 3b 74 06 01 00 80 7f ee dd 6b 38 23 29 |_X.;t.......k8#)| +000002d0 56 ff d2 c2 08 86 52 b6 e3 8a d5 fe 47 79 5e ef |V.....R.....Gy^.| +000002e0 99 7a 0b d7 44 84 b9 2f 7a 2c 64 4f b3 7c aa 44 |.z..D../z,dO.|.D| +000002f0 aa 38 5d 1b 69 16 9f f2 7d f8 24 43 47 ad 31 bc |.8].i...}.$CG.1.| +00000300 f5 3d b8 c8 33 6e 3f 6f 2b ea 19 a2 30 32 2b 2a |.=..3n?o+...02+*| +00000310 81 64 3c ee ed 78 4c fa 80 fd e7 5f ef 85 98 d4 |.d<..xL...._....| +00000320 48 06 b8 f5 5e 1e e6 f3 42 a8 2f 99 5f ea b3 ba |H...^...B./._...| +00000330 8e a8 31 99 85 f2 46 11 a3 d2 c6 81 4b f1 22 7d |..1...F.....K."}| +00000340 d7 45 04 f1 a6 d6 7e 8f 9d 16 03 03 00 04 0e 00 |.E....~.........| +00000350 00 00 |..| +>>> Flow 3 (client to server) +00000000 16 03 03 00 25 10 00 00 21 20 22 e7 e7 61 a9 27 |....%...! "..a.'| +00000010 7b 93 d1 42 76 dd 16 32 e8 92 37 37 2f fd 0d 92 |{..Bv..2..77/...| +00000020 1f 8e b7 c5 69 40 d3 1a 7d 06 14 03 03 00 01 01 |....i@..}.......| +00000030 16 03 03 00 28 4e 7f b2 a2 20 5d cf a1 5a de 42 |....(N... ]..Z.B| +00000040 c5 72 c3 ef c3 23 a7 2c f3 5b 3d a4 81 21 ac db |.r...#.,.[=..!..| +00000050 44 1c f3 a1 83 aa a1 b7 85 9a c7 23 03 |D..........#.| +>>> Flow 4 (server to client) +00000000 16 03 03 00 82 04 00 00 7e 00 00 00 00 00 78 50 |........~.....xP| +00000010 46 ad c1 db a8 38 86 7b 2b bb fd d0 c3 42 3e 00 |F....8.{+....B>.| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 |................| +00000030 6f ec 80 83 61 3f 55 e3 9d ab 39 87 5b d0 ba 44 |o...a?U...9.[..D| +00000040 07 91 a8 d0 37 8a 7e 51 0d 00 97 ec 1b 61 f3 3b |....7.~Q.....a.;| +00000050 9f 29 24 d5 98 f7 4d 3b 80 ef 2f 4d aa 02 98 93 |.)$...M;../M....| +00000060 81 03 87 d8 06 33 94 f5 ed 5d cc 8f 57 97 70 26 |.....3...]..W.p&| +00000070 00 dc 0d d2 96 16 a2 6d fc be 8d 4b fa 5f b3 04 |.......m...K._..| +00000080 ce bb 48 ee c0 75 23 14 03 03 00 01 01 16 03 03 |..H..u#.........| +00000090 00 28 00 00 00 00 00 00 00 00 3a 69 e0 40 e2 d1 |.(........:i.@..| +000000a0 a6 96 33 0f b3 58 5a dc 41 ea d1 80 44 66 9f 2e |..3..XZ.A...Df..| +000000b0 00 e4 9e 10 13 56 b4 1b c9 42 17 03 03 00 25 00 |.....V...B....%.| +000000c0 00 00 00 00 00 00 01 88 f3 d9 5b ed 6b 3c 70 0c |..........[.k<p.| +000000d0 df 36 9d 1c f6 f6 83 38 53 ad e2 06 47 3c e2 9f |.6.....8S...G<..| +000000e0 42 87 d7 8a 15 03 03 00 1a 00 00 00 00 00 00 00 |B...............| +000000f0 02 df 4a 92 13 c4 e6 ac 76 25 c6 72 27 be d6 09 |..J.....v%.r'...| +00000100 eb 90 ed |...| diff --git a/libgo/go/crypto/tls/testdata/example-cert.pem b/libgo/go/crypto/tls/testdata/example-cert.pem new file mode 100644 index 0000000..e0bf7db --- /dev/null +++ b/libgo/go/crypto/tls/testdata/example-cert.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw +DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow +EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d +7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B +5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr +BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1 +NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l +Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc +6MF9+Yw1Yy0t +-----END CERTIFICATE----- diff --git a/libgo/go/crypto/tls/testdata/example-key.pem b/libgo/go/crypto/tls/testdata/example-key.pem new file mode 100644 index 0000000..104fb09 --- /dev/null +++ b/libgo/go/crypto/tls/testdata/example-key.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49 +AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q +EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA== +-----END EC PRIVATE KEY----- diff --git a/libgo/go/crypto/tls/tls.go b/libgo/go/crypto/tls/tls.go index 615d1e5..8fd4294 100644 --- a/libgo/go/crypto/tls/tls.go +++ b/libgo/go/crypto/tls/tls.go @@ -237,15 +237,14 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) { skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type) } - var err error - cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes) + // We don't need to parse the public key for TLS, but we so do anyway + // to check that it looks sane and matches the private key. + x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) if err != nil { return fail(err) } - // We don't need to parse the public key for TLS, but we so do anyway - // to check that it looks sane and matches the private key. - x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) + cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes) if err != nil { return fail(err) } diff --git a/libgo/go/crypto/tls/tls_test.go b/libgo/go/crypto/tls/tls_test.go index 97934cc..7542699 100644 --- a/libgo/go/crypto/tls/tls_test.go +++ b/libgo/go/crypto/tls/tls_test.go @@ -7,6 +7,7 @@ package tls import ( "bytes" "crypto/x509" + "encoding/json" "errors" "fmt" "internal/testenv" @@ -907,3 +908,11 @@ func BenchmarkLatency(b *testing.B) { } } } + +func TestConnectionStateMarshal(t *testing.T) { + cs := &ConnectionState{} + _, err := json.Marshal(cs) + if err != nil { + t.Errorf("json.Marshal failed on ConnectionState: %v", err) + } +} diff --git a/libgo/go/crypto/x509/cert_pool.go b/libgo/go/crypto/x509/cert_pool.go index 71ffbdf..a1646b9 100644 --- a/libgo/go/crypto/x509/cert_pool.go +++ b/libgo/go/crypto/x509/cert_pool.go @@ -25,16 +25,43 @@ func NewCertPool() *CertPool { } } +func (s *CertPool) copy() *CertPool { + p := &CertPool{ + bySubjectKeyId: make(map[string][]int, len(s.bySubjectKeyId)), + byName: make(map[string][]int, len(s.byName)), + certs: make([]*Certificate, len(s.certs)), + } + for k, v := range s.bySubjectKeyId { + indexes := make([]int, len(v)) + copy(indexes, v) + p.bySubjectKeyId[k] = indexes + } + for k, v := range s.byName { + indexes := make([]int, len(v)) + copy(indexes, v) + p.byName[k] = indexes + } + copy(p.certs, s.certs) + return p +} + // SystemCertPool returns a copy of the system cert pool. // // Any mutations to the returned pool are not written to disk and do // not affect any other pool. +// +// New changes in the the system cert pool might not be reflected +// in subsequent calls. func SystemCertPool() (*CertPool, error) { if runtime.GOOS == "windows" { // Issue 16736, 18609: return nil, errors.New("crypto/x509: system root pool is not available on Windows") } + if sysRoots := systemRootsPool(); sysRoots != nil { + return sysRoots.copy(), nil + } + return loadSystemRoots() } diff --git a/libgo/go/crypto/x509/name_constraints_test.go b/libgo/go/crypto/x509/name_constraints_test.go index 95d55fd..4c9bc1b 100644 --- a/libgo/go/crypto/x509/name_constraints_test.go +++ b/libgo/go/crypto/x509/name_constraints_test.go @@ -46,6 +46,7 @@ type nameConstraintsTest struct { requestedEKUs []ExtKeyUsage expectedError string noOpenSSL bool + ignoreCN bool } type constraintsSpec struct { @@ -57,6 +58,7 @@ type constraintsSpec struct { type leafSpec struct { sans []string ekus []string + cn string } var nameConstraintsTests = []nameConstraintsTest{ @@ -633,7 +635,7 @@ var nameConstraintsTests = []nameConstraintsTest{ }, }, - // #30: without SANs, a certificate is rejected in a constrained chain. + // #30: without SANs, a certificate with a CN is rejected in a constrained chain. nameConstraintsTest{ roots: []constraintsSpec{ constraintsSpec{ @@ -647,9 +649,9 @@ var nameConstraintsTests = []nameConstraintsTest{ }, leaf: leafSpec{ sans: []string{}, + cn: "foo.com", }, expectedError: "leaf doesn't have a SAN extension", - noOpenSSL: true, // OpenSSL doesn't require SANs in this case. }, // #31: IPv6 addresses work in constraints: roots can permit them as @@ -1580,6 +1582,80 @@ var nameConstraintsTests = []nameConstraintsTest{ ekus: []string{"email", "serverAuth"}, }, }, + + // #82: a certificate without SANs and CN is accepted in a constrained chain. + nameConstraintsTest{ + roots: []constraintsSpec{ + constraintsSpec{ + ok: []string{"dns:foo.com", "dns:.foo.com"}, + }, + }, + intermediates: [][]constraintsSpec{ + []constraintsSpec{ + constraintsSpec{}, + }, + }, + leaf: leafSpec{ + sans: []string{}, + }, + }, + + // #83: a certificate without SANs and with a CN that does not parse as a + // hostname is accepted in a constrained chain. + nameConstraintsTest{ + roots: []constraintsSpec{ + constraintsSpec{ + ok: []string{"dns:foo.com", "dns:.foo.com"}, + }, + }, + intermediates: [][]constraintsSpec{ + []constraintsSpec{ + constraintsSpec{}, + }, + }, + leaf: leafSpec{ + sans: []string{}, + cn: "foo,bar", + }, + }, + + // #84: a certificate with SANs and CN is accepted in a constrained chain. + nameConstraintsTest{ + roots: []constraintsSpec{ + constraintsSpec{ + ok: []string{"dns:foo.com", "dns:.foo.com"}, + }, + }, + intermediates: [][]constraintsSpec{ + []constraintsSpec{ + constraintsSpec{}, + }, + }, + leaf: leafSpec{ + sans: []string{"dns:foo.com"}, + cn: "foo.bar", + }, + }, + + // #85: without SANs, a certificate with a valid CN is accepted in a + // constrained chain if x509ignoreCN is set. + nameConstraintsTest{ + roots: []constraintsSpec{ + constraintsSpec{ + ok: []string{"dns:foo.com", "dns:.foo.com"}, + }, + }, + intermediates: [][]constraintsSpec{ + []constraintsSpec{ + constraintsSpec{}, + }, + }, + leaf: leafSpec{ + sans: []string{}, + cn: "foo.com", + }, + ignoreCN: true, + }, } func makeConstraintsCACert(constraints constraintsSpec, name string, key *ecdsa.PrivateKey, parent *Certificate, parentKey *ecdsa.PrivateKey) (*Certificate, error) { @@ -1625,9 +1701,8 @@ func makeConstraintsLeafCert(leaf leafSpec, key *ecdsa.PrivateKey, parent *Certi template := &Certificate{ SerialNumber: new(big.Int).SetBytes(serialBytes[:]), Subject: pkix.Name{ - // Don't set a CommonName because OpenSSL (at least) will try to - // match it against name constraints. OrganizationalUnit: []string{"Leaf"}, + CommonName: leaf.cn, }, NotBefore: time.Unix(1000, 0), NotAfter: time.Unix(2000, 0), @@ -1831,6 +1906,10 @@ func parseEKUs(ekuStrs []string) (ekus []ExtKeyUsage, unknowns []asn1.ObjectIden } func TestConstraintCases(t *testing.T) { + defer func(savedIgnoreCN bool) { + ignoreCN = savedIgnoreCN + }(ignoreCN) + privateKeys := sync.Pool{ New: func() interface{} { priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) @@ -1899,7 +1978,9 @@ func TestConstraintCases(t *testing.T) { t.Fatalf("#%d: cannot create leaf: %s", i, err) } - if !test.noOpenSSL && testNameConstraintsAgainstOpenSSL { + // Skip tests with CommonName set because OpenSSL will try to match it + // against name constraints, while we ignore it when it's not hostname-looking. + if !test.noOpenSSL && testNameConstraintsAgainstOpenSSL && test.leaf.cn == "" { output, err := testChainAgainstOpenSSL(leafCert, intermediatePool, rootPool) if err == nil && len(test.expectedError) > 0 { t.Errorf("#%d: unexpectedly succeeded against OpenSSL", i) @@ -1912,7 +1993,7 @@ func TestConstraintCases(t *testing.T) { if _, ok := err.(*exec.ExitError); !ok { t.Errorf("#%d: OpenSSL failed to run: %s", i, err) } else if len(test.expectedError) == 0 { - t.Errorf("#%d: OpenSSL unexpectedly failed: %q", i, output) + t.Errorf("#%d: OpenSSL unexpectedly failed: %v", i, output) if debugOpenSSLFailure { return } @@ -1920,6 +2001,7 @@ func TestConstraintCases(t *testing.T) { } } + ignoreCN = test.ignoreCN verifyOpts := VerifyOptions{ Roots: rootPool, Intermediates: intermediatePool, @@ -1949,7 +2031,7 @@ func TestConstraintCases(t *testing.T) { certAsPEM := func(cert *Certificate) string { var buf bytes.Buffer pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) - return string(buf.Bytes()) + return buf.String() } t.Errorf("#%d: root:\n%s", i, certAsPEM(rootPool.certs[0])) t.Errorf("#%d: leaf:\n%s", i, certAsPEM(leafCert)) @@ -2012,7 +2094,7 @@ func testChainAgainstOpenSSL(leaf *Certificate, intermediates, roots *CertPool) cmd.Stderr = &output err := cmd.Run() - return string(output.Bytes()), err + return output.String(), err } var rfc2821Tests = []struct { diff --git a/libgo/go/crypto/x509/pkcs8.go b/libgo/go/crypto/x509/pkcs8.go index eb051b6..fb1340c 100644 --- a/libgo/go/crypto/x509/pkcs8.go +++ b/libgo/go/crypto/x509/pkcs8.go @@ -56,7 +56,7 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) { } // MarshalPKCS8PrivateKey converts a private key to PKCS#8 encoded form. -// The following key types are supported: *rsa.PrivateKey, *ecdsa.PublicKey. +// The following key types are supported: *rsa.PrivateKey, *ecdsa.PrivateKey. // Unsupported key types result in an error. // // See RFC 5208. diff --git a/libgo/go/crypto/x509/pkix/pkix.go b/libgo/go/crypto/x509/pkix/pkix.go index 7b32220..3cc4d58 100644 --- a/libgo/go/crypto/x509/pkix/pkix.go +++ b/libgo/go/crypto/x509/pkix/pkix.go @@ -95,7 +95,7 @@ func (r RDNSequence) String() string { type RelativeDistinguishedNameSET []AttributeTypeAndValue // AttributeTypeAndValue mirrors the ASN.1 structure of the same name in -// http://tools.ietf.org/html/rfc5280#section-4.1.2.4 +// https://tools.ietf.org/html/rfc5280#section-4.1.2.4 type AttributeTypeAndValue struct { Type asn1.ObjectIdentifier Value interface{} diff --git a/libgo/go/crypto/x509/root.go b/libgo/go/crypto/x509/root.go index 787d955..24029624 100644 --- a/libgo/go/crypto/x509/root.go +++ b/libgo/go/crypto/x509/root.go @@ -19,4 +19,7 @@ func systemRootsPool() *CertPool { func initSystemRoots() { systemRoots, systemRootsErr = loadSystemRoots() + if systemRootsErr != nil { + systemRoots = nil + } } diff --git a/libgo/go/crypto/x509/root_cgo_darwin.go b/libgo/go/crypto/x509/root_cgo_darwin.go index 80cd250..a02ac3c 100644 --- a/libgo/go/crypto/x509/root_cgo_darwin.go +++ b/libgo/go/crypto/x509/root_cgo_darwin.go @@ -7,7 +7,7 @@ package x509 /* -#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1080 +#cgo CFLAGS: -mmacosx-version-min=10.10 -D__MAC_OS_X_VERSION_MAX_ALLOWED=101300 #cgo LDFLAGS: -framework CoreFoundation -framework Security #include <errno.h> @@ -16,59 +16,6 @@ package x509 #include <CoreFoundation/CoreFoundation.h> #include <Security/Security.h> -// FetchPEMRoots_MountainLion is the version of FetchPEMRoots from Go 1.6 -// which still works on OS X 10.8 (Mountain Lion). -// It lacks support for admin & user cert domains. -// See golang.org/issue/16473 -int FetchPEMRoots_MountainLion(CFDataRef *pemRoots) { - if (pemRoots == NULL) { - return -1; - } - CFArrayRef certs = NULL; - OSStatus err = SecTrustCopyAnchorCertificates(&certs); - if (err != noErr) { - return -1; - } - CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0); - int i, ncerts = CFArrayGetCount(certs); - for (i = 0; i < ncerts; i++) { - CFDataRef data = NULL; - SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i); - if (cert == NULL) { - continue; - } - // Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport. - // Once we support weak imports via cgo we should prefer that, and fall back to this - // for older systems. - err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); - if (err != noErr) { - continue; - } - if (data != NULL) { - CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data)); - CFRelease(data); - } - } - CFRelease(certs); - *pemRoots = combinedData; - return 0; -} - -// useOldCode reports whether the running machine is OS X 10.8 Mountain Lion -// or older. We only support Mountain Lion and higher, but we'll at least try our -// best on older machines and continue to use the old code path. -// -// See golang.org/issue/16473 -int useOldCode() { - char str[256]; - size_t size = sizeof(str); - memset(str, 0, size); - sysctlbyname("kern.osrelease", str, &size, NULL, 0); - // OS X 10.8 is osrelease "12.*", 10.7 is 11.*, 10.6 is 10.*. - // We never supported things before that. - return memcmp(str, "12.", 3) == 0 || memcmp(str, "11.", 3) == 0 || memcmp(str, "10.", 3) == 0; -} - // FetchPEMRoots fetches the system's list of trusted X.509 root certificates. // // On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root @@ -78,9 +25,7 @@ int useOldCode() { // Note: The CFDataRef returned in pemRoots and untrustedPemRoots must // be released (using CFRelease) after we've consumed its content. int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) { - if (useOldCode()) { - return FetchPEMRoots_MountainLion(pemRoots); - } + int i; // Get certificates from all domains, not just System, this lets // the user add CAs to their "login" keychain, and Admins to add @@ -101,7 +46,8 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) { CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0); CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0); - for (int i = 0; i < numDomains; i++) { + for (i = 0; i < numDomains; i++) { + int j; CFArrayRef certs = NULL; OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs); if (err != noErr) { @@ -109,7 +55,7 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) { } CFIndex numCerts = CFArrayGetCount(certs); - for (int j = 0; j < numCerts; j++) { + for (j = 0; j < numCerts; j++) { CFDataRef data = NULL; CFErrorRef errRef = NULL; CFArrayRef trustSettings = NULL; @@ -124,6 +70,9 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) { if (i == 0) { trustAsRoot = 1; } else { + int k; + CFIndex m; + // Certs found in the system domain are always trusted. If the user // configures "Never Trust" on such a cert, it will also be found in the // admin or user domain, causing it to be added to untrustedPemRoots. The @@ -133,7 +82,7 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) { // SecTrustServer.c, "user trust settings overrule admin trust settings", // so take the last trust settings array we find. // Skip the system domain since it is always trusted. - for (int k = i; k < numDomains; k++) { + for (k = i; k < numDomains; k++) { CFArrayRef domainTrustSettings = NULL; err = SecTrustSettingsCopyTrustSettings(cert, domains[k], &domainTrustSettings); if (err == errSecSuccess && domainTrustSettings != NULL) { @@ -147,9 +96,9 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) { // "this certificate must be verified to a known trusted certificate"; aka not a root. continue; } - for (CFIndex k = 0; k < CFArrayGetCount(trustSettings); k++) { + for (m = 0; m < CFArrayGetCount(trustSettings); m++) { CFNumberRef cfNum; - CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, k); + CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, m); if (CFDictionaryGetValueIfPresent(tSetting, policy, (const void**)&cfNum)){ SInt32 result = 0; CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result); @@ -187,10 +136,7 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) { } } - // Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport. - // Once we support weak imports via cgo we should prefer that, and fall back to this - // for older systems. - err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); + err = SecItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); if (err != noErr) { continue; } diff --git a/libgo/go/crypto/x509/root_darwin.go b/libgo/go/crypto/x509/root_darwin.go index bc35a1c..9d7b3a6 100644 --- a/libgo/go/crypto/x509/root_darwin.go +++ b/libgo/go/crypto/x509/root_darwin.go @@ -181,12 +181,12 @@ func verifyCertWithSystem(block *pem.Block, cert *Certificate) bool { } if err := cmd.Run(); err != nil { if debugExecDarwinRoots { - println(fmt.Sprintf("crypto/x509: verify-cert rejected %s: %q", cert.Subject.CommonName, bytes.TrimSpace(stderr.Bytes()))) + println(fmt.Sprintf("crypto/x509: verify-cert rejected %s: %q", cert.Subject, bytes.TrimSpace(stderr.Bytes()))) } return false } if debugExecDarwinRoots { - println(fmt.Sprintf("crypto/x509: verify-cert approved %s", cert.Subject.CommonName)) + println(fmt.Sprintf("crypto/x509: verify-cert approved %s", cert.Subject)) } return true } diff --git a/libgo/go/crypto/x509/root_js.go b/libgo/go/crypto/x509/root_js.go new file mode 100644 index 0000000..70abb73 --- /dev/null +++ b/libgo/go/crypto/x509/root_js.go @@ -0,0 +1,10 @@ +// Copyright 2018 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 js,wasm + +package x509 + +// Possible certificate files; stop after finding one. +var certFiles = []string{} diff --git a/libgo/go/crypto/x509/root_plan9.go b/libgo/go/crypto/x509/root_plan9.go index ebeb7df..09f0e23 100644 --- a/libgo/go/crypto/x509/root_plan9.go +++ b/libgo/go/crypto/x509/root_plan9.go @@ -33,5 +33,8 @@ func loadSystemRoots() (*CertPool, error) { bestErr = err } } + if bestErr == nil { + return roots, nil + } return nil, bestErr } diff --git a/libgo/go/crypto/x509/root_unix.go b/libgo/go/crypto/x509/root_unix.go index 0547460..48de50b 100644 --- a/libgo/go/crypto/x509/root_unix.go +++ b/libgo/go/crypto/x509/root_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package x509 @@ -81,7 +81,7 @@ func loadSystemRoots() (*CertPool, error) { } } - if len(roots.certs) > 0 { + if len(roots.certs) > 0 || firstErr == nil { return roots, nil } diff --git a/libgo/go/crypto/x509/root_unix_test.go b/libgo/go/crypto/x509/root_unix_test.go index 03f935d..9e22019 100644 --- a/libgo/go/crypto/x509/root_unix_test.go +++ b/libgo/go/crypto/x509/root_unix_test.go @@ -103,10 +103,6 @@ func TestEnvVars(t *testing.T) { } if r == nil { - if tc.cns == nil { - // Expected nil - return - } t.Fatal("nil roots") } diff --git a/libgo/go/crypto/x509/root_windows.go b/libgo/go/crypto/x509/root_windows.go index 3621a93..74d395d 100644 --- a/libgo/go/crypto/x509/root_windows.go +++ b/libgo/go/crypto/x509/root_windows.go @@ -95,12 +95,6 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e return nil } -type _CertChainPolicyPara struct { - Size uint32 - Flags uint32 - ExtraPolicyPara unsafe.Pointer -} - // checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for // use as a certificate chain for a SSL/TLS server. func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error { @@ -114,13 +108,13 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex } sslPara.Size = uint32(unsafe.Sizeof(*sslPara)) - para := &_CertChainPolicyPara{ - ExtraPolicyPara: unsafe.Pointer(sslPara), + para := &syscall.CertChainPolicyPara{ + ExtraPolicyPara: (syscall.Pointer)(unsafe.Pointer(sslPara)), } para.Size = uint32(unsafe.Sizeof(*para)) status := syscall.CertChainPolicyStatus{} - err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, (*syscall.CertChainPolicyPara)(unsafe.Pointer(para)), &status) + err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status) if err != nil { return err } diff --git a/libgo/go/crypto/x509/sha2_windows_test.go b/libgo/go/crypto/x509/sha2_windows_test.go deleted file mode 100644 index 79dc685..0000000 --- a/libgo/go/crypto/x509/sha2_windows_test.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2015 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 "syscall" - -func init() { - v, err := syscall.GetVersion() - if err != nil { - return - } - if major := byte(v); major < 6 { - // Windows XP SP2 and Windows 2003 do not support SHA2. - // http://blogs.technet.com/b/pki/archive/2010/09/30/sha2-and-windows.aspx - supportSHA2 = false - } -} diff --git a/libgo/go/crypto/x509/verify.go b/libgo/go/crypto/x509/verify.go index 60e415b..210db4c 100644 --- a/libgo/go/crypto/x509/verify.go +++ b/libgo/go/crypto/x509/verify.go @@ -6,19 +6,21 @@ package x509 import ( "bytes" - "encoding/asn1" "errors" "fmt" "net" "net/url" + "os" "reflect" "runtime" - "strconv" "strings" "time" "unicode/utf8" ) +// ignoreCN disables interpreting Common Name as a hostname. See issue 24151. +var ignoreCN = strings.Contains(os.Getenv("GODEBUG"), "x509ignoreCN=1") + type InvalidReason int const ( @@ -43,13 +45,18 @@ const ( NameMismatch // NameConstraintsWithoutSANs results when a leaf certificate doesn't // contain a Subject Alternative Name extension, but a CA certificate - // contains name constraints. + // contains name constraints, and the Common Name can be interpreted as + // a hostname. + // + // You can avoid this error by setting the experimental GODEBUG environment + // variable to "x509ignoreCN=1", disabling Common Name matching entirely. + // This behavior might become the default in the future. NameConstraintsWithoutSANs // UnconstrainedName results when a CA certificate contains permitted // name constraints, but leaf certificate contains a name of an // unsupported or unconstrained type. UnconstrainedName - // TooManyConstraints results when the number of comparision operations + // TooManyConstraints results when the number of comparison operations // needed to check a certificate exceeds the limit set by // VerifyOptions.MaxConstraintComparisions. This limit exists to // prevent pathological certificates can consuming excessive amounts of @@ -102,6 +109,12 @@ type HostnameError struct { func (h HostnameError) Error() string { c := h.Certificate + if !c.hasSANExtension() && !validHostname(c.Subject.CommonName) && + matchHostnames(toLowerCaseASCII(c.Subject.CommonName), toLowerCaseASCII(h.Host)) { + // This would have validated, if it weren't for the validHostname check on Common Name. + return "x509: Common Name is not a valid hostname: " + c.Subject.CommonName + } + var valid string if ip := net.ParseIP(h.Host); ip != nil { // Trying to validate an IP @@ -115,10 +128,10 @@ func (h HostnameError) Error() string { valid += san.String() } } else { - if c.hasSANExtension() { - valid = strings.Join(c.DNSNames, ", ") - } else { + if c.commonNameAsHostname() { valid = c.Subject.CommonName + } else { + valid = strings.Join(c.DNSNames, ", ") } } @@ -189,7 +202,7 @@ type VerifyOptions struct { KeyUsages []ExtKeyUsage // MaxConstraintComparisions is the maximum number of comparisons to // perform when checking a given certificate's name constraints. If - // zero, a sensible default is used. This limit prevents pathalogical + // zero, a sensible default is used. This limit prevents pathological // certificates from consuming excessive amounts of CPU time when // validating. MaxConstraintComparisions int @@ -583,17 +596,16 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V leaf = currentChain[0] } - if (certType == intermediateCertificate || certType == rootCertificate) && c.hasNameConstraints() { - sanExtension, ok := leaf.getSANExtension() - if !ok { - // This is the deprecated, legacy case of depending on - // the CN as a hostname. Chains modern enough to be - // using name constraints should not be depending on - // CNs. - return CertificateInvalidError{c, NameConstraintsWithoutSANs, ""} - } - - err := forEachSAN(sanExtension, func(tag int, data []byte) error { + checkNameConstraints := (certType == intermediateCertificate || certType == rootCertificate) && c.hasNameConstraints() + if checkNameConstraints && leaf.commonNameAsHostname() { + // This is the deprecated, legacy case of depending on the commonName as + // a hostname. We don't enforce name constraints against the CN, but + // VerifyHostname will look for hostnames in there if there are no SANs. + // In order to ensure VerifyHostname will not accept an unchecked name, + // return an error here. + return CertificateInvalidError{c, NameConstraintsWithoutSANs, ""} + } else if checkNameConstraints && leaf.hasSANExtension() { + err := forEachSAN(leaf.getSANExtension(), func(tag int, data []byte) error { switch tag { case nameTypeEmail: name := string(data) @@ -692,18 +704,6 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V return nil } -// formatOID formats an ASN.1 OBJECT IDENTIFER in the common, dotted style. -func formatOID(oid asn1.ObjectIdentifier) string { - ret := "" - for i, v := range oid { - if i > 0 { - ret += "." - } - ret += strconv.Itoa(v) - } - return ret -} - // Verify attempts to verify c by building one or more chains from c to a // certificate in opts.Roots, using certificates in opts.Intermediates if // needed. If successful, it returns one or more chains where the first @@ -860,6 +860,64 @@ nextIntermediate: return } +// validHostname returns whether host is a valid hostname that can be matched or +// matched against according to RFC 6125 2.2, with some leniency to accomodate +// legacy values. +func validHostname(host string) bool { + host = strings.TrimSuffix(host, ".") + + if len(host) == 0 { + return false + } + + for i, part := range strings.Split(host, ".") { + if part == "" { + // Empty label. + return false + } + if i == 0 && part == "*" { + // Only allow full left-most wildcards, as those are the only ones + // we match, and matching literal '*' characters is probably never + // the expected behavior. + continue + } + for j, c := range part { + if 'a' <= c && c <= 'z' { + continue + } + if '0' <= c && c <= '9' { + continue + } + if 'A' <= c && c <= 'Z' { + continue + } + if c == '-' && j != 0 { + continue + } + if c == '_' { + // _ is not a valid character in hostnames, but it's commonly + // found in deployments outside the WebPKI. + continue + } + return false + } + } + + return true +} + +// commonNameAsHostname reports whether the Common Name field should be +// considered the hostname that the certificate is valid for. This is a legacy +// behavior, disabled if the Subject Alt Name extension is present. +// +// It applies the strict validHostname check to the Common Name field, so that +// certificates without SANs can still be validated against CAs with name +// constraints if there is no risk the CN would be matched as a hostname. +// See NameConstraintsWithoutSANs and issue 24151. +func (c *Certificate) commonNameAsHostname() bool { + return !ignoreCN && !c.hasSANExtension() && validHostname(c.Subject.CommonName) +} + func matchHostnames(pattern, host string) bool { host = strings.TrimSuffix(host, ".") pattern = strings.TrimSuffix(pattern, ".") @@ -940,15 +998,16 @@ func (c *Certificate) VerifyHostname(h string) error { lowered := toLowerCaseASCII(h) - if c.hasSANExtension() { + if c.commonNameAsHostname() { + if matchHostnames(toLowerCaseASCII(c.Subject.CommonName), lowered) { + return nil + } + } else { for _, match := range c.DNSNames { if matchHostnames(toLowerCaseASCII(match), lowered) { return nil } } - // If Subject Alt Name is given, we ignore the common name. - } else if matchHostnames(toLowerCaseASCII(c.Subject.CommonName), lowered) { - return nil } return HostnameError{c, h} diff --git a/libgo/go/crypto/x509/verify_test.go b/libgo/go/crypto/x509/verify_test.go index bd3df47..7684145 100644 --- a/libgo/go/crypto/x509/verify_test.go +++ b/libgo/go/crypto/x509/verify_test.go @@ -15,8 +15,6 @@ import ( "time" ) -var supportSHA2 = true - type verifyTest struct { leaf string intermediates []string @@ -27,6 +25,7 @@ type verifyTest struct { keyUsages []ExtKeyUsage testSystemRootsError bool sha2 bool + ignoreCN bool errorCallback func(*testing.T, int, error) bool expectedChains [][]string @@ -73,7 +72,16 @@ var verifyTests = []verifyTest{ currentTime: 1395785200, dnsName: "www.example.com", - errorCallback: expectHostnameError, + errorCallback: expectHostnameError("certificate is valid for"), + }, + { + leaf: googleLeaf, + intermediates: []string{giag2Intermediate}, + roots: []string{geoTrustRoot}, + currentTime: 1395785200, + dnsName: "1.2.3.4", + + errorCallback: expectHostnameError("doesn't contain any IP SANs"), }, { leaf: googleLeaf, @@ -250,7 +258,7 @@ var verifyTests = []verifyTest{ dnsName: "notfoo.example", systemSkip: true, - errorCallback: expectHostnameError, + errorCallback: expectHostnameError("certificate is valid for"), }, { // The issuer name in the leaf doesn't exactly match the @@ -283,7 +291,7 @@ var verifyTests = []verifyTest{ currentTime: 1486684488, systemSkip: true, - errorCallback: expectHostnameError, + errorCallback: expectHostnameError("certificate is not valid for any names"), }, { // Test that excluded names are respected. @@ -320,19 +328,77 @@ var verifyTests = []verifyTest{ errorCallback: expectUnhandledCriticalExtension, }, + { + // Test that invalid CN are ignored. + leaf: invalidCNWithoutSAN, + dnsName: "foo,invalid", + roots: []string{invalidCNRoot}, + currentTime: 1540000000, + systemSkip: true, + + errorCallback: expectHostnameError("Common Name is not a valid hostname"), + }, + { + // Test that valid CN are respected. + leaf: validCNWithoutSAN, + dnsName: "foo.example.com", + roots: []string{invalidCNRoot}, + currentTime: 1540000000, + systemSkip: true, + + expectedChains: [][]string{ + {"foo.example.com", "Test root"}, + }, + }, + // Replicate CN tests with ignoreCN = true + { + leaf: ignoreCNWithSANLeaf, + dnsName: "foo.example.com", + roots: []string{ignoreCNWithSANRoot}, + currentTime: 1486684488, + systemSkip: true, + ignoreCN: true, + + errorCallback: expectHostnameError("certificate is not valid for any names"), + }, + { + leaf: invalidCNWithoutSAN, + dnsName: "foo,invalid", + roots: []string{invalidCNRoot}, + currentTime: 1540000000, + systemSkip: true, + ignoreCN: true, + + errorCallback: expectHostnameError("Common Name is not a valid hostname"), + }, + { + leaf: validCNWithoutSAN, + dnsName: "foo.example.com", + roots: []string{invalidCNRoot}, + currentTime: 1540000000, + systemSkip: true, + ignoreCN: true, + + errorCallback: expectHostnameError("not valid for any names"), + }, } -func expectHostnameError(t *testing.T, i int, err error) (ok bool) { - if _, ok := err.(HostnameError); !ok { - t.Errorf("#%d: error was not a HostnameError: %s", i, err) - return false +func expectHostnameError(msg string) func(*testing.T, int, error) bool { + return func(t *testing.T, i int, err error) (ok bool) { + if _, ok := err.(HostnameError); !ok { + t.Errorf("#%d: error was not a HostnameError: %v", i, err) + return false + } + if !strings.Contains(err.Error(), msg) { + t.Errorf("#%d: HostnameError did not contain %q: %v", i, msg, err) + } + return true } - return true } func expectExpired(t *testing.T, i int, err error) (ok bool) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != Expired { - t.Errorf("#%d: error was not Expired: %s", i, err) + t.Errorf("#%d: error was not Expired: %v", i, err) return false } return true @@ -340,7 +406,7 @@ func expectExpired(t *testing.T, i int, err error) (ok bool) { func expectUsageError(t *testing.T, i int, err error) (ok bool) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != IncompatibleUsage { - t.Errorf("#%d: error was not IncompatibleUsage: %s", i, err) + t.Errorf("#%d: error was not IncompatibleUsage: %v", i, err) return false } return true @@ -349,11 +415,11 @@ func expectUsageError(t *testing.T, i int, err error) (ok bool) { func expectAuthorityUnknown(t *testing.T, i int, err error) (ok bool) { e, ok := err.(UnknownAuthorityError) if !ok { - t.Errorf("#%d: error was not UnknownAuthorityError: %s", i, err) + t.Errorf("#%d: error was not UnknownAuthorityError: %v", i, err) return false } if e.Cert == nil { - t.Errorf("#%d: error was UnknownAuthorityError, but missing Cert: %s", i, err) + t.Errorf("#%d: error was UnknownAuthorityError, but missing Cert: %v", i, err) return false } return true @@ -361,7 +427,7 @@ func expectAuthorityUnknown(t *testing.T, i int, err error) (ok bool) { func expectSystemRootsError(t *testing.T, i int, err error) bool { if _, ok := err.(SystemRootsError); !ok { - t.Errorf("#%d: error was not SystemRootsError: %s", i, err) + t.Errorf("#%d: error was not SystemRootsError: %v", i, err) return false } return true @@ -373,7 +439,7 @@ func expectHashError(t *testing.T, i int, err error) bool { return false } if expected := "algorithm unimplemented"; !strings.Contains(err.Error(), expected) { - t.Errorf("#%d: error resulting from invalid hash didn't contain '%s', rather it was: %s", i, expected, err) + t.Errorf("#%d: error resulting from invalid hash didn't contain '%s', rather it was: %v", i, expected, err) return false } return true @@ -381,7 +447,7 @@ func expectHashError(t *testing.T, i int, err error) bool { func expectSubjectIssuerMismatcthError(t *testing.T, i int, err error) (ok bool) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != NameMismatch { - t.Errorf("#%d: error was not a NameMismatch: %s", i, err) + t.Errorf("#%d: error was not a NameMismatch: %v", i, err) return false } return true @@ -389,7 +455,7 @@ func expectSubjectIssuerMismatcthError(t *testing.T, i int, err error) (ok bool) func expectNameConstraintsError(t *testing.T, i int, err error) (ok bool) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != CANotAuthorizedForThisName { - t.Errorf("#%d: error was not a CANotAuthorizedForThisName: %s", i, err) + t.Errorf("#%d: error was not a CANotAuthorizedForThisName: %v", i, err) return false } return true @@ -397,7 +463,7 @@ func expectNameConstraintsError(t *testing.T, i int, err error) (ok bool) { func expectNotAuthorizedError(t *testing.T, i int, err error) (ok bool) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != NotAuthorizedToSign { - t.Errorf("#%d: error was not a NotAuthorizedToSign: %s", i, err) + t.Errorf("#%d: error was not a NotAuthorizedToSign: %v", i, err) return false } return true @@ -405,7 +471,7 @@ func expectNotAuthorizedError(t *testing.T, i int, err error) (ok bool) { func expectUnhandledCriticalExtension(t *testing.T, i int, err error) (ok bool) { if _, ok := err.(UnhandledCriticalExtension); !ok { - t.Errorf("#%d: error was not an UnhandledCriticalExtension: %s", i, err) + t.Errorf("#%d: error was not an UnhandledCriticalExtension: %v", i, err) return false } return true @@ -420,6 +486,9 @@ func certificateFromPEM(pemBytes string) (*Certificate, error) { } func testVerify(t *testing.T, useSystemRoots bool) { + defer func(savedIgnoreCN bool) { + ignoreCN = savedIgnoreCN + }(ignoreCN) for i, test := range verifyTests { if useSystemRoots && test.systemSkip { continue @@ -427,10 +496,8 @@ func testVerify(t *testing.T, useSystemRoots bool) { if runtime.GOOS == "windows" && test.testSystemRootsError { continue } - if useSystemRoots && !supportSHA2 && test.sha2 { - continue - } + ignoreCN = test.ignoreCN opts := VerifyOptions{ Intermediates: NewCertPool(), DNSName: test.dnsName, @@ -459,7 +526,7 @@ func testVerify(t *testing.T, useSystemRoots bool) { leaf, err := certificateFromPEM(test.leaf) if err != nil { - t.Errorf("#%d: failed to parse leaf: %s", i, err) + t.Errorf("#%d: failed to parse leaf: %v", i, err) return } @@ -477,7 +544,7 @@ func testVerify(t *testing.T, useSystemRoots bool) { } if test.errorCallback == nil && err != nil { - t.Errorf("#%d: unexpected error: %s", i, err) + t.Errorf("#%d: unexpected error: %v", i, err) } if test.errorCallback != nil { if !test.errorCallback(t, i, err) { @@ -1518,6 +1585,95 @@ yU1yRHUqUYpN0DWFpsPbBqgM6uUAVO2ayBFhPgWUaqkmSbZ/Nq7isGvknaTmcIwT +NQCZDd5eFeU8PpNX7rgaYE4GPq+EEmLVCBYmdctr8QVdqJ//8Xu3+1phjDy -----END CERTIFICATE-----` +const invalidCNRoot = ` +-----BEGIN CERTIFICATE----- +MIIBFjCBvgIJAIsu4r+jb70UMAoGCCqGSM49BAMCMBQxEjAQBgNVBAsMCVRlc3Qg +cm9vdDAeFw0xODA3MTExODMyMzVaFw0yODA3MDgxODMyMzVaMBQxEjAQBgNVBAsM +CVRlc3Qgcm9vdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABF6oDgMg0LV6YhPj +QXaPXYCc2cIyCdqp0ROUksRz0pOLTc5iY2nraUheRUD1vRRneq7GeXOVNn7uXONg +oCGMjNwwCgYIKoZIzj0EAwIDRwAwRAIgDSiwgIn8g1lpruYH0QD1GYeoWVunfmrI +XzZZl0eW/ugCICgOfXeZ2GGy3wIC0352BaC3a8r5AAb2XSGNe+e9wNN6 +-----END CERTIFICATE----- +` + +const invalidCNWithoutSAN = ` +Certificate: + Data: + Version: 1 (0x0) + Serial Number: + 07:ba:bc:b7:d9:ab:0c:02:fe:50:1d:4e:15:a3:0d:e4:11:16:14:a2 + Signature Algorithm: ecdsa-with-SHA256 + Issuer: OU = Test root + Validity + Not Before: Jul 11 18:35:21 2018 GMT + Not After : Jul 8 18:35:21 2028 GMT + Subject: CN = "foo,invalid" + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:a7:a6:7c:22:33:a7:47:7f:08:93:2d:5f:61:35: + 2e:da:45:67:76:f2:97:73:18:b0:01:12:4a:1a:d5: + b7:6f:41:3c:bb:05:69:f4:06:5d:ff:eb:2b:a7:85: + 0b:4c:f7:45:4e:81:40:7a:a9:c6:1d:bb:ba:d9:b9: + 26:b3:ca:50:90 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:21:00:85:96:75:b6:72:3c:67:12:a0:7f:86:04:81: + d2:dd:c8:67:50:d7:5f:85:c0:54:54:fc:e6:6b:45:08:93:d3: + 2a:02:20:60:86:3e:d6:28:a6:4e:da:dd:6e:95:89:cc:00:76: + 78:1c:03:80:85:a6:5a:0b:eb:c5:f3:9c:2e:df:ef:6e:fa +-----BEGIN CERTIFICATE----- +MIIBJDCBywIUB7q8t9mrDAL+UB1OFaMN5BEWFKIwCgYIKoZIzj0EAwIwFDESMBAG +A1UECwwJVGVzdCByb290MB4XDTE4MDcxMTE4MzUyMVoXDTI4MDcwODE4MzUyMVow +FjEUMBIGA1UEAwwLZm9vLGludmFsaWQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AASnpnwiM6dHfwiTLV9hNS7aRWd28pdzGLABEkoa1bdvQTy7BWn0Bl3/6yunhQtM +90VOgUB6qcYdu7rZuSazylCQMAoGCCqGSM49BAMCA0gAMEUCIQCFlnW2cjxnEqB/ +hgSB0t3IZ1DXX4XAVFT85mtFCJPTKgIgYIY+1iimTtrdbpWJzAB2eBwDgIWmWgvr +xfOcLt/vbvo= +-----END CERTIFICATE----- +` + +const validCNWithoutSAN = ` +Certificate: + Data: + Version: 1 (0x0) + Serial Number: + 07:ba:bc:b7:d9:ab:0c:02:fe:50:1d:4e:15:a3:0d:e4:11:16:14:a4 + Signature Algorithm: ecdsa-with-SHA256 + Issuer: OU = Test root + Validity + Not Before: Jul 11 18:47:24 2018 GMT + Not After : Jul 8 18:47:24 2028 GMT + Subject: CN = foo.example.com + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:a7:a6:7c:22:33:a7:47:7f:08:93:2d:5f:61:35: + 2e:da:45:67:76:f2:97:73:18:b0:01:12:4a:1a:d5: + b7:6f:41:3c:bb:05:69:f4:06:5d:ff:eb:2b:a7:85: + 0b:4c:f7:45:4e:81:40:7a:a9:c6:1d:bb:ba:d9:b9: + 26:b3:ca:50:90 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + Signature Algorithm: ecdsa-with-SHA256 + 30:44:02:20:53:6c:d7:b7:59:61:51:72:a5:18:a3:4b:0d:52: + ea:15:fa:d0:93:30:32:54:4b:ed:0f:58:85:b8:a8:1a:82:3b: + 02:20:14:77:4b:0e:7e:4f:0a:4f:64:26:97:dc:d0:ed:aa:67: + 1d:37:85:da:b4:87:ba:25:1c:2a:58:f7:23:11:8b:3d +-----BEGIN CERTIFICATE----- +MIIBJzCBzwIUB7q8t9mrDAL+UB1OFaMN5BEWFKQwCgYIKoZIzj0EAwIwFDESMBAG +A1UECwwJVGVzdCByb290MB4XDTE4MDcxMTE4NDcyNFoXDTI4MDcwODE4NDcyNFow +GjEYMBYGA1UEAwwPZm9vLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D +AQcDQgAEp6Z8IjOnR38Iky1fYTUu2kVndvKXcxiwARJKGtW3b0E8uwVp9AZd/+sr +p4ULTPdFToFAeqnGHbu62bkms8pQkDAKBggqhkjOPQQDAgNHADBEAiBTbNe3WWFR +cqUYo0sNUuoV+tCTMDJUS+0PWIW4qBqCOwIgFHdLDn5PCk9kJpfc0O2qZx03hdq0 +h7olHCpY9yMRiz0= +-----END CERTIFICATE----- +` + var unknownAuthorityErrorTests = []struct { cert string expected string @@ -1535,7 +1691,7 @@ func TestUnknownAuthorityError(t *testing.T) { } c, err := ParseCertificate(der.Bytes) if err != nil { - t.Errorf("#%d: Unable to parse certificate -> %s", i, err) + t.Errorf("#%d: Unable to parse certificate -> %v", i, err) } uae := &UnknownAuthorityError{ Cert: c, @@ -1707,3 +1863,28 @@ UNhY4JhezH9gQYqvDMWrWDAbBgNVHSMEFDASgBArF29S5Bnqw7de8GzGA1nfMAoG CCqGSM49BAMCA0gAMEUCIQClA3d4tdrDu9Eb5ZBpgyC+fU1xTZB0dKQHz6M5fPZA 2AIgN96lM+CPGicwhN24uQI6flOsO3H0TJ5lNzBYLtnQtlc= -----END CERTIFICATE-----` + +func TestValidHostname(t *testing.T) { + tests := []struct { + host string + want bool + }{ + {"example.com", true}, + {"eXample123-.com", true}, + {"-eXample123-.com", false}, + {"", false}, + {".", false}, + {"example..com", false}, + {".example.com", false}, + {"*.example.com", true}, + {"*foo.example.com", false}, + {"foo.*.example.com", false}, + {"exa_mple.com", true}, + {"foo,bar", false}, + } + for _, tt := range tests { + if got := validHostname(tt.host); got != tt.want { + t.Errorf("validHostname(%q) = %v, want %v", tt.host, got, tt.want) + } + } +} diff --git a/libgo/go/crypto/x509/x509.go b/libgo/go/crypto/x509/x509.go index ee08dd9..2e72471 100644 --- a/libgo/go/crypto/x509/x509.go +++ b/libgo/go/crypto/x509/x509.go @@ -420,10 +420,10 @@ func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) SignatureAlgorithm // https://tools.ietf.org/html/rfc3447#section-8.1), that the // salt length matches the hash length, and that the trailer // field has the default value. - if !bytes.Equal(params.Hash.Parameters.FullBytes, asn1.NullBytes) || + if (len(params.Hash.Parameters.FullBytes) != 0 && !bytes.Equal(params.Hash.Parameters.FullBytes, asn1.NullBytes)) || !params.MGF.Algorithm.Equal(oidMGF1) || !mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) || - !bytes.Equal(mgf1HashFunc.Parameters.FullBytes, asn1.NullBytes) || + (len(mgf1HashFunc.Parameters.FullBytes) != 0 && !bytes.Equal(mgf1HashFunc.Parameters.FullBytes, asn1.NullBytes)) || params.TrailerField != 1 { return UnknownSignatureAlgorithm } @@ -843,23 +843,16 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature } func (c *Certificate) hasNameConstraints() bool { - for _, e := range c.Extensions { - if len(e.Id) == 4 && e.Id[0] == 2 && e.Id[1] == 5 && e.Id[2] == 29 && e.Id[3] == 30 { - return true - } - } - - return false + return oidInExtensions(oidExtensionNameConstraints, c.Extensions) } -func (c *Certificate) getSANExtension() ([]byte, bool) { +func (c *Certificate) getSANExtension() []byte { for _, e := range c.Extensions { - if len(e.Id) == 4 && e.Id[0] == 2 && e.Id[1] == 5 && e.Id[2] == 29 && e.Id[3] == 17 { - return e.Value, true + if e.Id.Equal(oidExtensionSubjectAltName) { + return e.Value } } - - return nil, false + return nil } func signaturePublicKeyAlgoMismatchError(expectedPubKeyAlgo PublicKeyAlgorithm, pubKey interface{}) error { @@ -1056,7 +1049,7 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ namedCurveOID := new(asn1.ObjectIdentifier) rest, err := asn1.Unmarshal(paramsData, namedCurveOID) if err != nil { - return nil, err + return nil, errors.New("x509: failed to parse ECDSA parameters as named curve") } if len(rest) != 0 { return nil, errors.New("x509: trailing data after ECDSA parameters") @@ -1952,7 +1945,8 @@ func buildExtensions(template *Certificate, subjectIsEmpty bool, authorityKeyId } // Adding another extension here? Remember to update the maximum number - // of elements in the make() at the top of the function. + // of elements in the make() at the top of the function and the list of + // template fields used in CreateCertificate documentation. return append(ret[:n], template.ExtraExtensions...), nil } @@ -2039,11 +2033,39 @@ func signingParamsForPublicKey(pub interface{}, requestedSigAlgo SignatureAlgori var emptyASN1Subject = []byte{0x30, 0} // CreateCertificate creates a new X.509v3 certificate based on a template. -// The following members of template are used: AuthorityKeyId, -// BasicConstraintsValid, DNSNames, ExcludedDNSDomains, ExtKeyUsage, -// IsCA, KeyUsage, MaxPathLen, MaxPathLenZero, NotAfter, NotBefore, -// PermittedDNSDomains, PermittedDNSDomainsCritical, SerialNumber, -// SignatureAlgorithm, Subject, SubjectKeyId, and UnknownExtKeyUsage. +// The following members of template are used: +// +// - AuthorityKeyId +// - BasicConstraintsValid +// - CRLDistributionPoints +// - DNSNames +// - EmailAddresses +// - ExcludedDNSDomains +// - ExcludedEmailAddresses +// - ExcludedIPRanges +// - ExcludedURIDomains +// - ExtKeyUsage +// - ExtraExtensions +// - IsCA +// - IssuingCertificateURL +// - KeyUsage +// - MaxPathLen +// - MaxPathLenZero +// - NotAfter +// - NotBefore +// - OCSPServer +// - PermittedDNSDomains +// - PermittedDNSDomainsCritical +// - PermittedEmailAddresses +// - PermittedIPRanges +// - PermittedURIDomains +// - PolicyIdentifiers +// - SerialNumber +// - SignatureAlgorithm +// - Subject +// - SubjectKeyId +// - URIs +// - UnknownExtKeyUsage // // 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 @@ -2317,7 +2339,7 @@ func newRawAttributes(attributes []pkix.AttributeTypeAndValueSET) ([]asn1.RawVal return rawAttributes, nil } -// parseRawAttributes Unmarshals RawAttributes intos AttributeTypeAndValueSETs. +// parseRawAttributes Unmarshals RawAttributes into AttributeTypeAndValueSETs. func parseRawAttributes(rawAttributes []asn1.RawValue) []pkix.AttributeTypeAndValueSET { var attributes []pkix.AttributeTypeAndValueSET for _, rawAttr := range rawAttributes { @@ -2365,9 +2387,18 @@ func parseCSRExtensions(rawAttributes []asn1.RawValue) ([]pkix.Extension, error) } // CreateCertificateRequest creates a new certificate request based on a -// template. The following members of template are used: Attributes, DNSNames, -// EmailAddresses, ExtraExtensions, IPAddresses, URIs, SignatureAlgorithm, and -// Subject. The private key is the private key of the signer. +// template. The following members of template are used: +// +// - Attributes +// - DNSNames +// - EmailAddresses +// - ExtraExtensions +// - IPAddresses +// - URIs +// - SignatureAlgorithm +// - Subject +// +// The private key is the private key of the signer. // // The returned slice is the certificate request in DER encoding. // @@ -2410,77 +2441,96 @@ func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv extensions = append(extensions, template.ExtraExtensions...) - var attributes []pkix.AttributeTypeAndValueSET - attributes = append(attributes, template.Attributes...) + // Make a copy of template.Attributes because we may alter it below. + attributes := make([]pkix.AttributeTypeAndValueSET, 0, len(template.Attributes)) + for _, attr := range template.Attributes { + values := make([][]pkix.AttributeTypeAndValue, len(attr.Value)) + copy(values, attr.Value) + attributes = append(attributes, pkix.AttributeTypeAndValueSET{ + Type: attr.Type, + Value: values, + }) + } + extensionsAppended := false if len(extensions) > 0 { - // specifiedExtensions contains all the extensions that we - // found specified via template.Attributes. - specifiedExtensions := make(map[string]bool) - - for _, atvSet := range template.Attributes { - if !atvSet.Type.Equal(oidExtensionRequest) { + // Append the extensions to an existing attribute if possible. + for _, atvSet := range attributes { + if !atvSet.Type.Equal(oidExtensionRequest) || len(atvSet.Value) == 0 { continue } + // specifiedExtensions contains all the extensions that we + // found specified via template.Attributes. + specifiedExtensions := make(map[string]bool) + for _, atvs := range atvSet.Value { for _, atv := range atvs { specifiedExtensions[atv.Type.String()] = true } } - } - atvs := make([]pkix.AttributeTypeAndValue, 0, len(extensions)) - for _, e := range extensions { - if specifiedExtensions[e.Id.String()] { - // Attributes already contained a value for - // this extension and it takes priority. - continue - } + newValue := make([]pkix.AttributeTypeAndValue, 0, len(atvSet.Value[0])+len(extensions)) + newValue = append(newValue, atvSet.Value[0]...) - atvs = append(atvs, pkix.AttributeTypeAndValue{ - // There is no place for the critical flag in a CSR. - Type: e.Id, - Value: e.Value, - }) - } + for _, e := range extensions { + if specifiedExtensions[e.Id.String()] { + // Attributes already contained a value for + // this extension and it takes priority. + continue + } - // Append the extensions to an existing attribute if possible. - appended := false - for _, atvSet := range attributes { - if !atvSet.Type.Equal(oidExtensionRequest) || len(atvSet.Value) == 0 { - continue + newValue = append(newValue, pkix.AttributeTypeAndValue{ + // There is no place for the critical + // flag in an AttributeTypeAndValue. + Type: e.Id, + Value: e.Value, + }) } - atvSet.Value[0] = append(atvSet.Value[0], atvs...) - appended = true + atvSet.Value[0] = newValue + extensionsAppended = true break } + } - // Otherwise, add a new attribute for the extensions. - if !appended { - attributes = append(attributes, pkix.AttributeTypeAndValueSET{ - Type: oidExtensionRequest, - Value: [][]pkix.AttributeTypeAndValue{ - atvs, - }, - }) + rawAttributes, err := newRawAttributes(attributes) + if err != nil { + return + } + + // If not included in attributes, add a new attribute for the + // extensions. + if len(extensions) > 0 && !extensionsAppended { + attr := struct { + Type asn1.ObjectIdentifier + Value [][]pkix.Extension `asn1:"set"` + }{ + Type: oidExtensionRequest, + Value: [][]pkix.Extension{extensions}, } + + b, err := asn1.Marshal(attr) + if err != nil { + return nil, errors.New("x509: failed to serialise extensions attribute: " + err.Error()) + } + + var rawValue asn1.RawValue + if _, err := asn1.Unmarshal(b, &rawValue); err != nil { + return nil, err + } + + rawAttributes = append(rawAttributes, rawValue) } asn1Subject := template.RawSubject if len(asn1Subject) == 0 { asn1Subject, err = asn1.Marshal(template.Subject.ToRDNSequence()) if err != nil { - return + return nil, err } } - rawAttributes, err := newRawAttributes(attributes) - if err != nil { - return - } - tbsCSR := tbsCertificateRequest{ Version: 0, // PKCS #10, RFC 2986 Subject: asn1.RawValue{FullBytes: asn1Subject}, diff --git a/libgo/go/crypto/x509/x509_test.go b/libgo/go/crypto/x509/x509_test.go index 7d75727..388156e 100644 --- a/libgo/go/crypto/x509/x509_test.go +++ b/libgo/go/crypto/x509/x509_test.go @@ -551,7 +551,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) { UnknownExtKeyUsage: testUnknownExtKeyUsage, BasicConstraintsValid: true, - IsCA: true, + IsCA: true, OCSPServer: []string{"http://ocsp.example.com"}, IssuingCertificateURL: []string{"http://crt.example.com/ca1.crt"}, @@ -946,19 +946,52 @@ qsGZWxzFvvkXUkQSl0dQQ5jO/FtUJcAVXVVp20LxPemfatAHpW31WdJYeWSQWky2 +f9b5TXKXVyjlUL7uHxowWrT2AtTchDH22wTEtqLEF9Z3Q== -----END CERTIFICATE-----` +// openssl req -newkey rsa:2048 -keyout test.key -sha256 -sigopt \ +// rsa_padding_mode:pss -sigopt rsa_pss_saltlen:32 -sigopt rsa_mgf1_md:sha256 \ +// -x509 -days 3650 -nodes -subj '/C=US/ST=CA/L=SF/O=Test/CN=Test' -out \ +// test.pem +var rsaPSSSelfSignedOpenSSL110PEM = `-----BEGIN CERTIFICATE----- +MIIDwDCCAnigAwIBAgIJAM9LAMHTE5xpMD0GCSqGSIb3DQEBCjAwoA0wCwYJYIZI +AWUDBAIBoRowGAYJKoZIhvcNAQEIMAsGCWCGSAFlAwQCAaIDAgEgMEUxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDTALBgNVBAoMBFRlc3Qx +DTALBgNVBAMMBFRlc3QwHhcNMTgwMjIyMjIxMzE4WhcNMjgwMjIwMjIxMzE4WjBF +MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMQ0wCwYDVQQK +DARUZXN0MQ0wCwYDVQQDDARUZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA4Zrsydod+GoTAJLLutWNF87qhhVPBsK1zB1Gj+NAAe4+VbrZ1E41H1wp +qITx7DA8DRtJEf+NqrTAnAdZWBG/tAOA5LfXVax0ZSQtLnYLSeylLoMtDyY3eFAj +TmuTOoyVy6raktowCnHCh01NsstqqTfrx6SbmzOmDmKTkq/I+7K0MCVsn41xRDVM ++ShD0WGFGioEGoiWnFSWupxJDA3Q6jIDEygVwNKHwnhv/2NgG2kqZzrZSQA67en0 +iKAXtoDNPpmyD5oS9YbEJ+2Nbm7oLeON30i6kZvXKIzJXx+UWViazHZqnsi5rQ8G +RHF+iVFXsqd0MzDKmkKOT5FDhrsbKQIDAQABo1MwUTAdBgNVHQ4EFgQU9uFY/nlg +gLH00NBnr/o7QvpN9ugwHwYDVR0jBBgwFoAU9uFY/nlggLH00NBnr/o7QvpN9ugw +DwYDVR0TAQH/BAUwAwEB/zA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEa +MBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgGiAwIBIAOCAQEAhJzpwxBNGKvzKWDe +WLqv6RMrl/q4GcH3b7M9wjxe0yOm4F+Tb2zJ7re4h+D39YkJf8cX1NV9UQVu6z4s +Fvo2kmlR0qZOXAg5augmCQ1xS0WHFoF6B52anNzHkZQbAIYJ3kGoFsUHzs7Sz7F/ +656FsRpHA9UzJQ3avPPMrA4Y4aoJ7ANJ6XIwTrdWrhULOVuvYRLCl4CdTVztVFX6 +wxX8nS1ISYd8jXPUMgsBKVbWufvLoIymMJW8CZbpprVZel5zFn0bmPrON8IHS30w +Gs+ITJjKEnZgXmAQ25SLKVzkZkBcGANs2GsdHNJ370Puisy0FIPD2NXR5uASAf7J ++w9fjQ== +-----END CERTIFICATE-----` + func TestRSAPSSSelfSigned(t *testing.T) { - der, _ := pem.Decode([]byte(rsaPSSSelfSignedPEM)) - if der == nil { - t.Fatal("Failed to find PEM block") - } + for i, pemBlock := range []string{rsaPSSSelfSignedPEM, rsaPSSSelfSignedOpenSSL110PEM} { + der, _ := pem.Decode([]byte(pemBlock)) + if der == nil { + t.Errorf("#%d: failed to find PEM block", i) + continue + } - cert, err := ParseCertificate(der.Bytes) - if err != nil { - t.Fatal(err) - } + cert, err := ParseCertificate(der.Bytes) + if err != nil { + t.Errorf("#%d: failed to parse: %s", i, err) + continue + } - if err = cert.CheckSignatureFrom(cert); err != nil { - t.Fatal(err) + if err = cert.CheckSignatureFrom(cert); err != nil { + t.Errorf("#%d: signature check failed: %s", i, err) + continue + } } } @@ -1207,8 +1240,9 @@ func TestCertificateRequestOverrides(t *testing.T) { // template. ExtraExtensions: []pkix.Extension{ { - Id: oidExtensionSubjectAltName, - Value: sanContents, + Id: oidExtensionSubjectAltName, + Value: sanContents, + Critical: true, }, }, } @@ -1219,6 +1253,10 @@ func TestCertificateRequestOverrides(t *testing.T) { t.Errorf("Extension did not override template. Got %v\n", csr.DNSNames) } + if len(csr.Extensions) != 1 || !csr.Extensions[0].Id.Equal(oidExtensionSubjectAltName) || !csr.Extensions[0].Critical { + t.Errorf("SAN extension was not faithfully copied, got %#v", csr.Extensions) + } + // If there is already an attribute with X.509 extensions then the // extra extensions should be added to it rather than creating a CSR // with two extension attributes. @@ -1361,7 +1399,7 @@ func TestMaxPathLen(t *testing.T) { NotAfter: time.Unix(100000, 0), BasicConstraintsValid: true, - IsCA: true, + IsCA: true, } cert1 := serialiseAndParse(t, template) @@ -1402,8 +1440,8 @@ func TestNoAuthorityKeyIdInSelfSignedCert(t *testing.T) { NotAfter: time.Unix(100000, 0), BasicConstraintsValid: true, - IsCA: true, - SubjectKeyId: []byte{1, 2, 3, 4}, + IsCA: true, + SubjectKeyId: []byte{1, 2, 3, 4}, } if cert := serialiseAndParse(t, template); len(cert.AuthorityKeyId) != 0 { @@ -1618,10 +1656,43 @@ func TestSystemCertPool(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("not implemented on Windows; Issue 16736, 18609") } - _, err := SystemCertPool() + a, err := SystemCertPool() if err != nil { t.Fatal(err) } + b, err := SystemCertPool() + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(a, b) { + t.Fatal("two calls to SystemCertPool had different results") + } + if ok := b.AppendCertsFromPEM([]byte(` +-----BEGIN CERTIFICATE----- +MIIDBjCCAe6gAwIBAgIRANXM5I3gjuqDfTp/PYrs+u8wDQYJKoZIhvcNAQELBQAw +EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xODAzMjcxOTU2MjFaFw0xOTAzMjcxOTU2 +MjFaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDK+9m3rjsO2Djes6bIYQZ3eV29JF09ZrjOrEHLtaKrD6/acsoSoTsf +cQr+rzzztdB5ijWXCS64zo/0OiqBeZUNZ67jVdToa9qW5UYe2H0Y+ZNdfA5GYMFD +yk/l3/uBu3suTZPfXiW2TjEi27Q8ruNUIZ54DpTcs6y2rBRFzadPWwn/VQMlvRXM +jrzl8Y08dgnYmaAHprxVzwMXcQ/Brol+v9GvjaH1DooHqkn8O178wsPQNhdtvN01 +IXL46cYdcUwWrE/GX5u+9DaSi+0KWxAPQ+NVD5qUI0CKl4714yGGh7feXMjJdHgl +VG4QJZlJvC4FsURgCHJT6uHGIelnSwhbAgMBAAGjVzBVMA4GA1UdDwEB/wQEAwIF +oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMCAGA1UdEQQZMBeC +FVRlc3RTeXN0ZW1DZXJ0UG9vbC5nbzANBgkqhkiG9w0BAQsFAAOCAQEAwuSRx/VR +BKh2ICxZjL6jBwk/7UlU1XKbhQD96RqkidDNGEc6eLZ90Z5XXTurEsXqdm5jQYPs +1cdcSW+fOSMl7MfW9e5tM66FaIPZl9rKZ1r7GkOfgn93xdLAWe8XHd19xRfDreub +YC8DVqgLASOEYFupVSl76ktPfxkU5KCvmUf3P2PrRybk1qLGFytGxfyice2gHSNI +gify3K/+H/7wCkyFW4xYvzl7WW4mXxoqPRPjQt1J423DhnnQ4G1P8V/vhUpXNXOq +N9IEPnWuihC09cyx/WMQIUlWnaQLHdfpPS04Iez3yy2PdfXJzwfPrja7rNE+skK6 +pa/O1nF0AfWOpw== +-----END CERTIFICATE----- + `)); !ok { + t.Fatal("AppendCertsFromPEM failed") + } + if reflect.DeepEqual(a, b) { + t.Fatal("changing one pool modified the other") + } } const emptyNameConstraintsPEM = ` diff --git a/libgo/go/database/sql/convert.go b/libgo/go/database/sql/convert.go index b79ec3f..92a2ebe 100644 --- a/libgo/go/database/sql/convert.go +++ b/libgo/go/database/sql/convert.go @@ -379,10 +379,9 @@ func convertAssign(dest, src interface{}) error { if src == nil { dv.Set(reflect.Zero(dv.Type())) return nil - } else { - dv.Set(reflect.New(dv.Type().Elem())) - return convertAssign(dv.Interface(), src) } + dv.Set(reflect.New(dv.Type().Elem())) + return convertAssign(dv.Interface(), src) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: s := asString(src) i64, err := strconv.ParseInt(s, 10, dv.Type().Bits()) @@ -434,11 +433,10 @@ func strconvErr(err error) error { func cloneBytes(b []byte) []byte { if b == nil { return nil - } else { - c := make([]byte, len(b)) - copy(c, b) - return c } + c := make([]byte, len(b)) + copy(c, b) + return c } func asString(src interface{}) string { diff --git a/libgo/go/database/sql/fakedb_test.go b/libgo/go/database/sql/fakedb_test.go index abb8d40f..a21bae6 100644 --- a/libgo/go/database/sql/fakedb_test.go +++ b/libgo/go/database/sql/fakedb_test.go @@ -296,10 +296,10 @@ func (db *fakeDB) createTable(name string, columnNames, columnTypes []string) er db.tables = make(map[string]*table) } if _, exist := db.tables[name]; exist { - return fmt.Errorf("table %q already exists", name) + return fmt.Errorf("fakedb: table %q already exists", name) } if len(columnNames) != len(columnTypes) { - return fmt.Errorf("create table of %q len(names) != len(types): %d vs %d", + return fmt.Errorf("fakedb: create table of %q len(names) != len(types): %d vs %d", name, len(columnNames), len(columnTypes)) } db.tables[name] = &table{colname: columnNames, coltype: columnTypes} @@ -365,7 +365,7 @@ func (c *fakeConn) Begin() (driver.Tx, error) { return nil, driver.ErrBadConn } if c.currTx != nil { - return nil, errors.New("already in a transaction") + return nil, errors.New("fakedb: already in a transaction") } c.touchMem() c.currTx = &fakeTx{c: c} @@ -419,13 +419,13 @@ func (c *fakeConn) Close() (err error) { }() c.touchMem() if c.currTx != nil { - return errors.New("can't close fakeConn; in a Transaction") + return errors.New("fakedb: can't close fakeConn; in a Transaction") } if c.db == nil { - return errors.New("can't close fakeConn; already closed") + return errors.New("fakedb: can't close fakeConn; already closed") } if c.stmtsMade > c.stmtsClosed { - return errors.New("can't close; dangling statement(s)") + return errors.New("fakedb: can't close; dangling statement(s)") } c.db = nil return nil @@ -437,7 +437,7 @@ func checkSubsetTypes(allowAny bool, args []driver.NamedValue) error { case int64, float64, bool, nil, []byte, string, time.Time: default: if !allowAny { - return fmt.Errorf("fakedb_test: invalid argument ordinal %[1]d: %[2]v, type %[2]T", arg.Ordinal, arg.Value) + return fmt.Errorf("fakedb: invalid argument ordinal %[1]d: %[2]v, type %[2]T", arg.Ordinal, arg.Value) } } } @@ -729,7 +729,7 @@ func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (d return nil, driver.ErrBadConn } if s.c.isDirtyAndMark() { - return nil, errors.New("session is dirty") + return nil, errors.New("fakedb: session is dirty") } err := checkSubsetTypes(s.c.db.allowAny, args) @@ -765,8 +765,7 @@ func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (d // Used for some of the concurrent tests. return s.execInsert(args, false) } - fmt.Printf("EXEC statement, cmd=%q: %#v\n", s.cmd, s) - return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd) + return nil, fmt.Errorf("fakedb: unimplemented statement Exec command type of %q", s.cmd) } // When doInsert is true, add the row to the table. @@ -844,7 +843,7 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) ( return nil, driver.ErrBadConn } if s.c.isDirtyAndMark() { - return nil, errors.New("session is dirty") + return nil, errors.New("fakedb: session is dirty") } err := checkSubsetTypes(s.c.db.allowAny, args) @@ -900,7 +899,7 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) ( idx := t.columnIndex(wcol.Column) if idx == -1 { t.mu.Unlock() - return nil, fmt.Errorf("db: invalid where clause column %q", wcol) + return nil, fmt.Errorf("fakedb: invalid where clause column %q", wcol) } tcol := trow.cols[idx] if bs, ok := tcol.([]byte); ok { @@ -1003,7 +1002,7 @@ type rowsCursor struct { err error // a clone of slices to give out to clients, indexed by the - // the original slice's first byte address. we clone them + // original slice's first byte address. we clone them // just so we're able to corrupt them on close. bytesClone map[*byte][]byte diff --git a/libgo/go/database/sql/sql.go b/libgo/go/database/sql/sql.go index 8f5588e..3617985 100644 --- a/libgo/go/database/sql/sql.go +++ b/libgo/go/database/sql/sql.go @@ -24,6 +24,7 @@ import ( "reflect" "runtime" "sort" + "strconv" "sync" "sync/atomic" "time" @@ -132,6 +133,31 @@ const ( LevelLinearizable ) +func (i IsolationLevel) String() string { + switch i { + case LevelDefault: + return "Default" + case LevelReadUncommitted: + return "Read Uncommitted" + case LevelReadCommitted: + return "Read Committed" + case LevelWriteCommitted: + return "Write Committed" + case LevelRepeatableRead: + return "Repeatable Read" + case LevelSnapshot: + return "Snapshot" + case LevelSerializable: + return "Serializable" + case LevelLinearizable: + return "Linearizable" + default: + return "IsolationLevel(" + strconv.Itoa(int(i)) + ")" + } +} + +var _ fmt.Stringer = LevelDefault + // TxOptions holds the transaction options to be used in DB.BeginTx. type TxOptions struct { // Isolation is the transaction isolation level. @@ -275,6 +301,10 @@ type Scanner interface { // // An error should be returned if the value cannot be stored // without loss of information. + // + // Reference types such as []byte are only valid until the next call to Scan + // and should not be retained. Their underlying memory is owned by the driver. + // If retention is necessary, copy their values before the next call to Scan. Scan(src interface{}) error } @@ -310,13 +340,17 @@ var ErrNoRows = errors.New("sql: no rows in result set") // // The sql package creates and frees connections automatically; it // also maintains a free pool of idle connections. If the database has -// a concept of per-connection state, such state can only be reliably -// observed within a transaction. Once DB.Begin is called, the +// a concept of per-connection state, such state can be reliably observed +// within a transaction (Tx) or connection (Conn). Once DB.Begin is called, the // returned Tx is bound to a single connection. Once Commit or // Rollback is called on the transaction, that transaction's // connection is returned to DB's idle connection pool. The pool size // can be controlled with SetMaxIdleConns. type DB struct { + // Atomic access only. At top of struct to prevent mis-alignment + // on 32-bit platforms. Of type time.Duration. + waitDuration int64 // Total time waited for new connections. + connector driver.Connector // numClosed is an atomic counter which represents a total number of // closed connections. Stmt.openStmt checks it before cleaning closed @@ -333,15 +367,18 @@ type DB struct { // maybeOpenNewConnections sends on the chan (one send per needed connection) // It is closed during db.Close(). The close tells the connectionOpener // goroutine to exit. - openerCh chan struct{} - resetterCh chan *driverConn - closed bool - dep map[finalCloser]depSet - lastPut map[*driverConn]string // stacktrace of last conn's put; debug only - maxIdle int // zero means defaultMaxIdleConns; negative means 0 - maxOpen int // <= 0 means unlimited - maxLifetime time.Duration // maximum amount of time a connection may be reused - cleanerCh chan struct{} + openerCh chan struct{} + resetterCh chan *driverConn + closed bool + dep map[finalCloser]depSet + lastPut map[*driverConn]string // stacktrace of last conn's put; debug only + maxIdle int // zero means defaultMaxIdleConns; negative means 0 + maxOpen int // <= 0 means unlimited + maxLifetime time.Duration // maximum amount of time a connection may be reused + cleanerCh chan struct{} + waitCount int64 // Total number of connections waited for. + maxIdleClosed int64 // Total number of connections closed due to idle. + maxLifetimeClosed int64 // Total number of connections closed due to max free limit. stop func() // stop cancels the connection opener and the session resetter. } @@ -503,7 +540,7 @@ type driverStmt struct { closeErr error // return value of previous Close call } -// Close ensures dirver.Stmt is only closed once any always returns the same +// Close ensures driver.Stmt is only closed once and always returns the same // result. func (ds *driverStmt) Close() error { ds.Lock() @@ -712,7 +749,9 @@ func (db *DB) Ping() error { return db.PingContext(context.Background()) } -// Close closes the database, releasing any open resources. +// Close closes the database and prevents new queries from starting. +// Close then waits for all queries that have started processing on the server +// to finish. // // It is rare to Close a DB, as the DB handle is meant to be // long-lived and shared between many goroutines. @@ -764,10 +803,13 @@ func (db *DB) maxIdleConnsLocked() int { // SetMaxIdleConns sets the maximum number of connections in the idle // connection pool. // -// If MaxOpenConns is greater than 0 but less than the new MaxIdleConns -// then the new MaxIdleConns will be reduced to match the MaxOpenConns limit +// If MaxOpenConns is greater than 0 but less than the new MaxIdleConns, +// then the new MaxIdleConns will be reduced to match the MaxOpenConns limit. // // If n <= 0, no idle connections are retained. +// +// The default max idle connections is currently 2. This may change in +// a future release. func (db *DB) SetMaxIdleConns(n int) { db.mu.Lock() if n > 0 { @@ -787,6 +829,7 @@ func (db *DB) SetMaxIdleConns(n int) { closing = db.freeConn[maxIdle:] db.freeConn = db.freeConn[:maxIdle] } + db.maxIdleClosed += int64(len(closing)) db.mu.Unlock() for _, c := range closing { c.Close() @@ -797,7 +840,7 @@ func (db *DB) SetMaxIdleConns(n int) { // // If MaxIdleConns is greater than 0 and the new MaxOpenConns is less than // MaxIdleConns, then MaxIdleConns will be reduced to match the new -// MaxOpenConns limit +// MaxOpenConns limit. // // If n <= 0, then there is no limit on the number of open connections. // The default is 0 (unlimited). @@ -879,6 +922,7 @@ func (db *DB) connectionCleaner(d time.Duration) { i-- } } + db.maxLifetimeClosed += int64(len(closing)) db.mu.Unlock() for _, c := range closing { @@ -894,17 +938,39 @@ func (db *DB) connectionCleaner(d time.Duration) { // DBStats contains database statistics. type DBStats struct { - // OpenConnections is the number of open connections to the database. - OpenConnections int + MaxOpenConnections int // Maximum number of open connections to the database. + + // Pool Status + OpenConnections int // The number of established connections both in use and idle. + InUse int // The number of connections currently in use. + Idle int // The number of idle connections. + + // Counters + WaitCount int64 // The total number of connections waited for. + WaitDuration time.Duration // The total time blocked waiting for a new connection. + MaxIdleClosed int64 // The total number of connections closed due to SetMaxIdleConns. + MaxLifetimeClosed int64 // The total number of connections closed due to SetConnMaxLifetime. } // Stats returns database statistics. func (db *DB) Stats() DBStats { + wait := atomic.LoadInt64(&db.waitDuration) + db.mu.Lock() + defer db.mu.Unlock() + stats := DBStats{ + MaxOpenConnections: db.maxOpen, + + Idle: len(db.freeConn), OpenConnections: db.numOpen, + InUse: db.numOpen - len(db.freeConn), + + WaitCount: db.waitCount, + WaitDuration: time.Duration(wait), + MaxIdleClosed: db.maxIdleClosed, + MaxLifetimeClosed: db.maxLifetimeClosed, } - db.mu.Unlock() return stats } @@ -1057,8 +1123,11 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn req := make(chan connRequest, 1) reqKey := db.nextRequestKeyLocked() db.connRequests[reqKey] = req + db.waitCount++ db.mu.Unlock() + waitStart := time.Now() + // Timeout the connection request with the context. select { case <-ctx.Done(): @@ -1067,15 +1136,20 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn db.mu.Lock() delete(db.connRequests, reqKey) db.mu.Unlock() + + atomic.AddInt64(&db.waitDuration, int64(time.Since(waitStart))) + select { default: case ret, ok := <-req: - if ok { + if ok && ret.conn != nil { db.putConn(ret.conn, ret.err, false) } } return nil, ctx.Err() case ret, ok := <-req: + atomic.AddInt64(&db.waitDuration, int64(time.Since(waitStart))) + if !ok { return nil, errDBClosed } @@ -1250,6 +1324,7 @@ func (db *DB) putConnDBLocked(dc *driverConn, err error) bool { return true } else if err == nil && !db.closed && db.maxIdleConnsLocked() > len(db.freeConn) { db.freeConn = append(db.freeConn, dc) + db.maxIdleClosed++ db.startCleanerLocked() return true } @@ -1603,7 +1678,7 @@ func (db *DB) Driver() driver.Driver { // ErrConnDone is returned by any operation that is performed on a connection // that has already been returned to the connection pool. -var ErrConnDone = errors.New("database/sql: connection is already closed") +var ErrConnDone = errors.New("sql: connection is already closed") // Conn returns a single connection by either opening a new connection // or returning an existing connection from the connection pool. Conn will @@ -1851,7 +1926,7 @@ func (tx *Tx) isDone() bool { // ErrTxDone is returned by any operation that is performed on a transaction // that has already been committed or rolled back. -var ErrTxDone = errors.New("sql: Transaction has already been committed or rolled back") +var ErrTxDone = errors.New("sql: transaction has already been committed or rolled back") // close returns the connection to the pool and // must only be called by Tx.rollback or Tx.Commit. @@ -1914,7 +1989,7 @@ func (tx *Tx) closePrepared() { // Commit commits the transaction. func (tx *Tx) Commit() error { // Check context first to avoid transaction leak. - // If put it behind tx.done CompareAndSwap statement, we cant't ensure + // If put it behind tx.done CompareAndSwap statement, we can't ensure // the consistency between tx.done and the real COMMIT operation. select { default: @@ -2262,13 +2337,6 @@ func resultFromStatement(ctx context.Context, ci driver.Conn, ds *driverStmt, ar return nil, err } - // -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 := ds.si.NumInput(); want >= 0 && want != len(dargs) { - return nil, fmt.Errorf("sql: statement expects %d inputs; got %d", want, len(dargs)) - } - resi, err := ctxDriverStmtExec(ctx, ds.si, dargs) if err != nil { return nil, err @@ -2317,7 +2385,7 @@ func (s *Stmt) connStmt(ctx context.Context, strategy connReuseStrategy) (dc *dr } // In a transaction or connection, we always use the connection that the - // the stmt was created on. + // stmt was created on. if s.cg != nil { s.mu.Unlock() dc, releaseConn, err = s.cg.grabConn(ctx) // blocks, waiting for the connection. @@ -2434,24 +2502,11 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) { func rowsiFromStatement(ctx context.Context, ci driver.Conn, ds *driverStmt, args ...interface{}) (driver.Rows, error) { ds.Lock() defer ds.Unlock() - dargs, err := driverArgsConnLocked(ci, ds, args) if err != nil { return nil, err } - - // -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 := ds.si.NumInput(); want >= 0 && want != len(dargs) { - return nil, fmt.Errorf("sql: statement expects %d inputs; got %d", want, len(dargs)) - } - - rowsi, err := ctxDriverStmtQuery(ctx, ds.si, dargs) - if err != nil { - return nil, err - } - return rowsi, nil + return ctxDriverStmtQuery(ctx, ds.si, dargs) } // QueryRowContext executes a prepared query statement with the given arguments. @@ -2460,11 +2515,6 @@ func rowsiFromStatement(ctx context.Context, ci driver.Conn, ds *driverStmt, arg // If the query selects no rows, the *Row's Scan will return ErrNoRows. // Otherwise, the *Row's Scan scans the first selected row and discards // the rest. -// -// Example usage: -// -// var name string -// err := nameByUseridStmt.QueryRowContext(ctx, id).Scan(&name) func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *Row { rows, err := s.QueryContext(ctx, args...) if err != nil { @@ -2533,19 +2583,7 @@ func (s *Stmt) finalClose() error { } // Rows is the result of a query. Its cursor starts before the first row -// of the result set. Use Next to advance through the rows: -// -// rows, err := db.Query("SELECT ...") -// ... -// defer rows.Close() -// for rows.Next() { -// var id int -// var name string -// err = rows.Scan(&id, &name) -// ... -// } -// err = rows.Err() // get any error encountered during iteration -// ... +// of the result set. Use Next to advance from row to row. type Rows struct { dc *driverConn // owned; must call releaseConn when closed to release releaseConn func(error) @@ -2568,6 +2606,9 @@ type Rows struct { } func (rs *Rows) initContextClose(ctx, txctx context.Context) { + if ctx.Done() == nil && (txctx == nil || txctx.Done() == nil) { + return + } ctx, rs.cancel = context.WithCancel(ctx) go rs.awaitDone(ctx, txctx) } @@ -2861,7 +2902,7 @@ func rowsColumnInfoSetupConnLocked(rowsi driver.Rows) []*ColumnType { // // Source values of type time.Time may be scanned into values of type // *time.Time, *interface{}, *string, or *[]byte. When converting to -// the latter two, time.Format3339Nano is used. +// the latter two, time.RFC3339Nano is used. // // Source values of type bool may be scanned into types *bool, // *interface{}, *string, *[]byte, or *RawBytes. @@ -2870,6 +2911,11 @@ func rowsColumnInfoSetupConnLocked(rowsi driver.Rows) []*ColumnType { // string inputs parseable by strconv.ParseBool. func (rs *Rows) Scan(dest ...interface{}) error { rs.closemu.RLock() + + if rs.lasterr != nil && rs.lasterr != io.EOF { + rs.closemu.RUnlock() + return rs.lasterr + } if rs.closed { rs.closemu.RUnlock() return errors.New("sql: Rows are closed") @@ -2885,7 +2931,7 @@ func (rs *Rows) Scan(dest ...interface{}) error { for i, sv := range rs.lastcols { err := convertAssign(dest[i], sv) if err != nil { - return fmt.Errorf("sql: Scan error on column index %d: %v", i, err) + return fmt.Errorf(`sql: Scan error on column index %d, name %q: %v`, i, rs.rowsi.Columns()[i], err) } } return nil @@ -2981,11 +3027,7 @@ func (r *Row) Scan(dest ...interface{}) error { return err } // Make sure the query can be processed to completion with no errors. - if err := r.rows.Close(); err != nil { - return err - } - - return nil + return r.rows.Close() } // A Result summarizes an executed SQL command. diff --git a/libgo/go/database/sql/sql_test.go b/libgo/go/database/sql/sql_test.go index ae6bf71..f194744 100644 --- a/libgo/go/database/sql/sql_test.go +++ b/libgo/go/database/sql/sql_test.go @@ -325,8 +325,8 @@ func TestQueryContext(t *testing.T) { } t.Fatalf("Scan: %v", err) } - if index == 2 && err == nil { - t.Fatal("expected an error on last scan") + if index == 2 && err != context.Canceled { + t.Fatalf("Scan: %v; want context.Canceled", err) } got = append(got, r) index++ @@ -1338,6 +1338,57 @@ func TestConnQuery(t *testing.T) { } } +func TestInvalidNilValues(t *testing.T) { + var date1 time.Time + var date2 int + + tests := []struct { + name string + input interface{} + expectedError string + }{ + { + name: "time.Time", + input: &date1, + expectedError: `sql: Scan error on column index 0, name "bdate": unsupported Scan, storing driver.Value type <nil> into type *time.Time`, + }, + { + name: "int", + input: &date2, + expectedError: `sql: Scan error on column index 0, name "bdate": converting driver.Value type <nil> ("<nil>") to a int: invalid syntax`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + conn, err := db.Conn(ctx) + if err != nil { + t.Fatal(err) + } + conn.dc.ci.(*fakeConn).skipDirtySession = true + defer conn.Close() + + err = conn.QueryRowContext(ctx, "SELECT|people|bdate|age=?", 1).Scan(tt.input) + if err == nil { + t.Fatal("expected error when querying nil column, but succeeded") + } + if err.Error() != tt.expectedError { + t.Fatalf("Expected error: %s\nReceived: %s", tt.expectedError, err.Error()) + } + + err = conn.PingContext(ctx) + if err != nil { + t.Fatal(err) + } + }) + } +} + func TestConnTx(t *testing.T) { db := newTestDB(t, "people") defer closeDB(t, db) @@ -1659,7 +1710,7 @@ func TestQueryRowNilScanDest(t *testing.T) { defer closeDB(t, db) var name *string // nil pointer err := db.QueryRow("SELECT|people|name|").Scan(name) - want := "sql: Scan error on column index 0: destination pointer is nil" + want := `sql: Scan error on column index 0, name "name": destination pointer is nil` if err == nil || err.Error() != want { t.Errorf("error = %q; want %q", err.Error(), want) } diff --git a/libgo/go/debug/dwarf/attr_string.go b/libgo/go/debug/dwarf/attr_string.go new file mode 100644 index 0000000..34e3659 --- /dev/null +++ b/libgo/go/debug/dwarf/attr_string.go @@ -0,0 +1,89 @@ +// Code generated by "stringer -type Attr -trimprefix=Attr"; DO NOT EDIT. + +package dwarf + +import "strconv" + +const _Attr_name = "SiblingLocationNameOrderingByteSizeBitOffsetBitSizeStmtListLowpcHighpcLanguageDiscrDiscrValueVisibilityImportStringLengthCommonRefCompDirConstValueContainingTypeDefaultValueInlineIsOptionalLowerBoundProducerPrototypedReturnAddrStartScopeStrideSizeUpperBoundAbstractOriginAccessibilityAddrClassArtificialBaseTypesCallingCountDataMemberLocDeclColumnDeclFileDeclLineDeclarationDiscrListEncodingExternalFrameBaseFriendIdentifierCaseMacroInfoNamelistItemPrioritySegmentSpecificationStaticLinkTypeUseLocationVarParamVirtualityVtableElemLocAllocatedAssociatedDataLocationStrideEntrypcUseUTF8ExtensionRangesTrampolineCallColumnCallFileCallLineDescription" + +var _Attr_map = map[Attr]string{ + 1: _Attr_name[0:7], + 2: _Attr_name[7:15], + 3: _Attr_name[15:19], + 9: _Attr_name[19:27], + 11: _Attr_name[27:35], + 12: _Attr_name[35:44], + 13: _Attr_name[44:51], + 16: _Attr_name[51:59], + 17: _Attr_name[59:64], + 18: _Attr_name[64:70], + 19: _Attr_name[70:78], + 21: _Attr_name[78:83], + 22: _Attr_name[83:93], + 23: _Attr_name[93:103], + 24: _Attr_name[103:109], + 25: _Attr_name[109:121], + 26: _Attr_name[121:130], + 27: _Attr_name[130:137], + 28: _Attr_name[137:147], + 29: _Attr_name[147:161], + 30: _Attr_name[161:173], + 32: _Attr_name[173:179], + 33: _Attr_name[179:189], + 34: _Attr_name[189:199], + 37: _Attr_name[199:207], + 39: _Attr_name[207:217], + 42: _Attr_name[217:227], + 44: _Attr_name[227:237], + 46: _Attr_name[237:247], + 47: _Attr_name[247:257], + 49: _Attr_name[257:271], + 50: _Attr_name[271:284], + 51: _Attr_name[284:293], + 52: _Attr_name[293:303], + 53: _Attr_name[303:312], + 54: _Attr_name[312:319], + 55: _Attr_name[319:324], + 56: _Attr_name[324:337], + 57: _Attr_name[337:347], + 58: _Attr_name[347:355], + 59: _Attr_name[355:363], + 60: _Attr_name[363:374], + 61: _Attr_name[374:383], + 62: _Attr_name[383:391], + 63: _Attr_name[391:399], + 64: _Attr_name[399:408], + 65: _Attr_name[408:414], + 66: _Attr_name[414:428], + 67: _Attr_name[428:437], + 68: _Attr_name[437:449], + 69: _Attr_name[449:457], + 70: _Attr_name[457:464], + 71: _Attr_name[464:477], + 72: _Attr_name[477:487], + 73: _Attr_name[487:491], + 74: _Attr_name[491:502], + 75: _Attr_name[502:510], + 76: _Attr_name[510:520], + 77: _Attr_name[520:533], + 78: _Attr_name[533:542], + 79: _Attr_name[542:552], + 80: _Attr_name[552:564], + 81: _Attr_name[564:570], + 82: _Attr_name[570:577], + 83: _Attr_name[577:584], + 84: _Attr_name[584:593], + 85: _Attr_name[593:599], + 86: _Attr_name[599:609], + 87: _Attr_name[609:619], + 88: _Attr_name[619:627], + 89: _Attr_name[627:635], + 90: _Attr_name[635:646], +} + +func (i Attr) String() string { + if str, ok := _Attr_map[i]; ok { + return str + } + return "Attr(" + strconv.FormatInt(int64(i), 10) + ")" +} diff --git a/libgo/go/debug/dwarf/class_string.go b/libgo/go/debug/dwarf/class_string.go index d57d9f7..a6aabff 100644 --- a/libgo/go/debug/dwarf/class_string.go +++ b/libgo/go/debug/dwarf/class_string.go @@ -1,16 +1,16 @@ -// generated by stringer -type=Class; DO NOT EDIT +// Code generated by "stringer -type=Class"; DO NOT EDIT. package dwarf -import "fmt" +import "strconv" const _Class_name = "ClassUnknownClassAddressClassBlockClassConstantClassExprLocClassFlagClassLinePtrClassLocListPtrClassMacPtrClassRangeListPtrClassReferenceClassReferenceSigClassStringClassReferenceAltClassStringAlt" var _Class_index = [...]uint8{0, 12, 24, 34, 47, 59, 68, 80, 95, 106, 123, 137, 154, 165, 182, 196} func (i Class) String() string { - if i < 0 || i+1 >= Class(len(_Class_index)) { - return fmt.Sprintf("Class(%d)", i) + if i < 0 || i >= Class(len(_Class_index)-1) { + return "Class(" + strconv.FormatInt(int64(i), 10) + ")" } return _Class_name[_Class_index[i]:_Class_index[i+1]] } diff --git a/libgo/go/debug/dwarf/const.go b/libgo/go/debug/dwarf/const.go index 04d8c50..4dda83e 100644 --- a/libgo/go/debug/dwarf/const.go +++ b/libgo/go/debug/dwarf/const.go @@ -6,7 +6,7 @@ package dwarf -import "strconv" +//go:generate stringer -type Attr -trimprefix=Attr // An Attr identifies the attribute type in a DWARF Entry's Field. type Attr uint32 @@ -86,99 +86,11 @@ const ( AttrDescription Attr = 0x5A ) -var attrNames = [...]string{ - AttrSibling: "Sibling", - AttrLocation: "Location", - AttrName: "Name", - AttrOrdering: "Ordering", - AttrByteSize: "ByteSize", - AttrBitOffset: "BitOffset", - AttrBitSize: "BitSize", - AttrStmtList: "StmtList", - AttrLowpc: "Lowpc", - AttrHighpc: "Highpc", - AttrLanguage: "Language", - AttrDiscr: "Discr", - AttrDiscrValue: "DiscrValue", - AttrVisibility: "Visibility", - AttrImport: "Import", - AttrStringLength: "StringLength", - AttrCommonRef: "CommonRef", - AttrCompDir: "CompDir", - AttrConstValue: "ConstValue", - AttrContainingType: "ContainingType", - AttrDefaultValue: "DefaultValue", - AttrInline: "Inline", - AttrIsOptional: "IsOptional", - AttrLowerBound: "LowerBound", - AttrProducer: "Producer", - AttrPrototyped: "Prototyped", - AttrReturnAddr: "ReturnAddr", - AttrStartScope: "StartScope", - AttrStrideSize: "StrideSize", - AttrUpperBound: "UpperBound", - AttrAbstractOrigin: "AbstractOrigin", - AttrAccessibility: "Accessibility", - AttrAddrClass: "AddrClass", - AttrArtificial: "Artificial", - AttrBaseTypes: "BaseTypes", - AttrCalling: "Calling", - AttrCount: "Count", - AttrDataMemberLoc: "DataMemberLoc", - AttrDeclColumn: "DeclColumn", - AttrDeclFile: "DeclFile", - AttrDeclLine: "DeclLine", - AttrDeclaration: "Declaration", - AttrDiscrList: "DiscrList", - AttrEncoding: "Encoding", - AttrExternal: "External", - AttrFrameBase: "FrameBase", - AttrFriend: "Friend", - AttrIdentifierCase: "IdentifierCase", - AttrMacroInfo: "MacroInfo", - AttrNamelistItem: "NamelistItem", - AttrPriority: "Priority", - AttrSegment: "Segment", - AttrSpecification: "Specification", - AttrStaticLink: "StaticLink", - AttrType: "Type", - AttrUseLocation: "UseLocation", - AttrVarParam: "VarParam", - AttrVirtuality: "Virtuality", - AttrVtableElemLoc: "VtableElemLoc", - AttrAllocated: "Allocated", - AttrAssociated: "Associated", - AttrDataLocation: "DataLocation", - AttrStride: "Stride", - AttrEntrypc: "Entrypc", - AttrUseUTF8: "UseUTF8", - AttrExtension: "Extension", - AttrRanges: "Ranges", - AttrTrampoline: "Trampoline", - AttrCallColumn: "CallColumn", - AttrCallFile: "CallFile", - AttrCallLine: "CallLine", - AttrDescription: "Description", -} - -func (a Attr) String() string { - if int(a) < len(attrNames) { - s := attrNames[a] - if s != "" { - return s - } - } - return strconv.Itoa(int(a)) -} - func (a Attr) GoString() string { - if int(a) < len(attrNames) { - s := attrNames[a] - if s != "" { - return "dwarf.Attr" + s - } + if str, ok := _Attr_map[a]; ok { + return "dwarf.Attr" + str } - return "dwarf.Attr(" + strconv.FormatInt(int64(a), 10) + ")" + return "dwarf." + a.String() } // A format is a DWARF data encoding format. @@ -218,6 +130,8 @@ const ( formGnuStrpAlt format = 0x1f21 ) +//go:generate stringer -type Tag -trimprefix=Tag + // A Tag is the classification (the type) of an Entry. type Tag uint32 @@ -287,88 +201,11 @@ const ( TagTemplateAlias Tag = 0x43 ) -var tagNames = [...]string{ - TagArrayType: "ArrayType", - TagClassType: "ClassType", - TagEntryPoint: "EntryPoint", - TagEnumerationType: "EnumerationType", - TagFormalParameter: "FormalParameter", - TagImportedDeclaration: "ImportedDeclaration", - TagLabel: "Label", - TagLexDwarfBlock: "LexDwarfBlock", - TagMember: "Member", - TagPointerType: "PointerType", - TagReferenceType: "ReferenceType", - TagCompileUnit: "CompileUnit", - TagStringType: "StringType", - TagStructType: "StructType", - TagSubroutineType: "SubroutineType", - TagTypedef: "Typedef", - TagUnionType: "UnionType", - TagUnspecifiedParameters: "UnspecifiedParameters", - TagVariant: "Variant", - TagCommonDwarfBlock: "CommonDwarfBlock", - TagCommonInclusion: "CommonInclusion", - TagInheritance: "Inheritance", - TagInlinedSubroutine: "InlinedSubroutine", - TagModule: "Module", - TagPtrToMemberType: "PtrToMemberType", - TagSetType: "SetType", - TagSubrangeType: "SubrangeType", - TagWithStmt: "WithStmt", - TagAccessDeclaration: "AccessDeclaration", - TagBaseType: "BaseType", - TagCatchDwarfBlock: "CatchDwarfBlock", - TagConstType: "ConstType", - TagConstant: "Constant", - TagEnumerator: "Enumerator", - TagFileType: "FileType", - TagFriend: "Friend", - TagNamelist: "Namelist", - TagNamelistItem: "NamelistItem", - TagPackedType: "PackedType", - TagSubprogram: "Subprogram", - TagTemplateTypeParameter: "TemplateTypeParameter", - TagTemplateValueParameter: "TemplateValueParameter", - TagThrownType: "ThrownType", - TagTryDwarfBlock: "TryDwarfBlock", - TagVariantPart: "VariantPart", - TagVariable: "Variable", - TagVolatileType: "VolatileType", - TagDwarfProcedure: "DwarfProcedure", - TagRestrictType: "RestrictType", - TagInterfaceType: "InterfaceType", - TagNamespace: "Namespace", - TagImportedModule: "ImportedModule", - TagUnspecifiedType: "UnspecifiedType", - TagPartialUnit: "PartialUnit", - TagImportedUnit: "ImportedUnit", - TagMutableType: "MutableType", - TagCondition: "Condition", - TagSharedType: "SharedType", - TagTypeUnit: "TypeUnit", - TagRvalueReferenceType: "RvalueReferenceType", - TagTemplateAlias: "TemplateAlias", -} - -func (t Tag) String() string { - if int(t) < len(tagNames) { - s := tagNames[t] - if s != "" { - return s - } - } - return strconv.Itoa(int(t)) -} - func (t Tag) GoString() string { - if int(t) < len(tagNames) { - s := tagNames[t] - if s != "" { - return "dwarf.Tag" + s - } + if t <= TagTemplateAlias { + return "dwarf.Tag" + t.String() } - return "dwarf.Tag(" + strconv.FormatInt(int64(t), 10) + ")" + return "dwarf." + t.String() } // Location expression operators. diff --git a/libgo/go/debug/dwarf/tag_string.go b/libgo/go/debug/dwarf/tag_string.go new file mode 100644 index 0000000..ac396af --- /dev/null +++ b/libgo/go/debug/dwarf/tag_string.go @@ -0,0 +1,44 @@ +// Code generated by "stringer -type Tag -trimprefix=Tag"; DO NOT EDIT. + +package dwarf + +import "strconv" + +const ( + _Tag_name_0 = "ArrayTypeClassTypeEntryPointEnumerationTypeFormalParameter" + _Tag_name_1 = "ImportedDeclaration" + _Tag_name_2 = "LabelLexDwarfBlock" + _Tag_name_3 = "Member" + _Tag_name_4 = "PointerTypeReferenceTypeCompileUnitStringTypeStructType" + _Tag_name_5 = "SubroutineTypeTypedefUnionTypeUnspecifiedParametersVariantCommonDwarfBlockCommonInclusionInheritanceInlinedSubroutineModulePtrToMemberTypeSetTypeSubrangeTypeWithStmtAccessDeclarationBaseTypeCatchDwarfBlockConstTypeConstantEnumeratorFileTypeFriendNamelistNamelistItemPackedTypeSubprogramTemplateTypeParameterTemplateValueParameterThrownTypeTryDwarfBlockVariantPartVariableVolatileTypeDwarfProcedureRestrictTypeInterfaceTypeNamespaceImportedModuleUnspecifiedTypePartialUnitImportedUnitMutableTypeConditionSharedTypeTypeUnitRvalueReferenceTypeTemplateAlias" +) + +var ( + _Tag_index_0 = [...]uint8{0, 9, 18, 28, 43, 58} + _Tag_index_2 = [...]uint8{0, 5, 18} + _Tag_index_4 = [...]uint8{0, 11, 24, 35, 45, 55} + _Tag_index_5 = [...]uint16{0, 14, 21, 30, 51, 58, 74, 89, 100, 117, 123, 138, 145, 157, 165, 182, 190, 205, 214, 222, 232, 240, 246, 254, 266, 276, 286, 307, 329, 339, 352, 363, 371, 383, 397, 409, 422, 431, 445, 460, 471, 483, 494, 503, 513, 521, 540, 553} +) + +func (i Tag) String() string { + switch { + case 1 <= i && i <= 5: + i -= 1 + return _Tag_name_0[_Tag_index_0[i]:_Tag_index_0[i+1]] + case i == 8: + return _Tag_name_1 + case 10 <= i && i <= 11: + i -= 10 + return _Tag_name_2[_Tag_index_2[i]:_Tag_index_2[i+1]] + case i == 13: + return _Tag_name_3 + case 15 <= i && i <= 19: + i -= 15 + return _Tag_name_4[_Tag_index_4[i]:_Tag_index_4[i+1]] + case 21 <= i && i <= 67: + i -= 21 + return _Tag_name_5[_Tag_index_5[i]:_Tag_index_5[i+1]] + default: + return "Tag(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/libgo/go/debug/elf/elf.go b/libgo/go/debug/elf/elf.go index c8a4fe6..07c03e7 100644 --- a/libgo/go/debug/elf/elf.go +++ b/libgo/go/debug/elf/elf.go @@ -11,7 +11,10 @@ * $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $ * $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $ * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $ + * "System V ABI" (http://www.sco.com/developers/gabi/latest/ch4.eheader.html) * "ELF for the ARM® 64-bit Architecture (AArch64)" (ARM IHI 0056B) + * "RISC-V ELF psABI specification" (https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md) + * llvm/BinaryFormat/ELF.h - ELF constants and structures * * Copyright (c) 1996-1998 John D. Polstra. All rights reserved. * Copyright (c) 2001 David E. O'Brien @@ -132,6 +135,9 @@ const ( ELFOSABI_OPENBSD OSABI = 12 /* OpenBSD */ ELFOSABI_OPENVMS OSABI = 13 /* Open VMS */ ELFOSABI_NSK OSABI = 14 /* HP Non-Stop Kernel */ + ELFOSABI_AROS OSABI = 15 /* Amiga Research OS */ + ELFOSABI_FENIXOS OSABI = 16 /* The FenixOS highly scalable multi-core OS */ + ELFOSABI_CLOUDABI OSABI = 17 /* Nuxi CloudABI */ ELFOSABI_ARM OSABI = 97 /* ARM */ ELFOSABI_STANDALONE OSABI = 255 /* Standalone (embedded) application */ ) @@ -152,6 +158,9 @@ var osabiStrings = []intName{ {12, "ELFOSABI_OPENBSD"}, {13, "ELFOSABI_OPENVMS"}, {14, "ELFOSABI_NSK"}, + {15, "ELFOSABI_AROS"}, + {16, "ELFOSABI_FENIXOS"}, + {17, "ELFOSABI_CLOUDABI"}, {97, "ELFOSABI_ARM"}, {255, "ELFOSABI_STANDALONE"}, } @@ -193,50 +202,188 @@ func (i Type) GoString() string { return stringName(uint32(i), typeStrings, true type Machine uint16 const ( - EM_NONE Machine = 0 /* Unknown machine. */ - EM_M32 Machine = 1 /* AT&T WE32100. */ - EM_SPARC Machine = 2 /* Sun SPARC. */ - EM_386 Machine = 3 /* Intel i386. */ - EM_68K Machine = 4 /* Motorola 68000. */ - EM_88K Machine = 5 /* Motorola 88000. */ - EM_860 Machine = 7 /* Intel i860. */ - EM_MIPS Machine = 8 /* MIPS R3000 Big-Endian only. */ - EM_S370 Machine = 9 /* IBM System/370. */ - EM_MIPS_RS3_LE Machine = 10 /* MIPS R3000 Little-Endian. */ - EM_PARISC Machine = 15 /* HP PA-RISC. */ - EM_VPP500 Machine = 17 /* Fujitsu VPP500. */ - EM_SPARC32PLUS Machine = 18 /* SPARC v8plus. */ - EM_960 Machine = 19 /* Intel 80960. */ - EM_PPC Machine = 20 /* PowerPC 32-bit. */ - EM_PPC64 Machine = 21 /* PowerPC 64-bit. */ - EM_S390 Machine = 22 /* IBM System/390. */ - EM_V800 Machine = 36 /* NEC V800. */ - EM_FR20 Machine = 37 /* Fujitsu FR20. */ - EM_RH32 Machine = 38 /* TRW RH-32. */ - EM_RCE Machine = 39 /* Motorola RCE. */ - EM_ARM Machine = 40 /* ARM. */ - EM_SH Machine = 42 /* Hitachi SH. */ - EM_SPARCV9 Machine = 43 /* SPARC v9 64-bit. */ - EM_TRICORE Machine = 44 /* Siemens TriCore embedded processor. */ - EM_ARC Machine = 45 /* Argonaut RISC Core. */ - EM_H8_300 Machine = 46 /* Hitachi H8/300. */ - EM_H8_300H Machine = 47 /* Hitachi H8/300H. */ - EM_H8S Machine = 48 /* Hitachi H8S. */ - EM_H8_500 Machine = 49 /* Hitachi H8/500. */ - EM_IA_64 Machine = 50 /* Intel IA-64 Processor. */ - EM_MIPS_X Machine = 51 /* Stanford MIPS-X. */ - EM_COLDFIRE Machine = 52 /* Motorola ColdFire. */ - EM_68HC12 Machine = 53 /* Motorola M68HC12. */ - EM_MMA Machine = 54 /* Fujitsu MMA. */ - EM_PCP Machine = 55 /* Siemens PCP. */ - EM_NCPU Machine = 56 /* Sony nCPU. */ - EM_NDR1 Machine = 57 /* Denso NDR1 microprocessor. */ - EM_STARCORE Machine = 58 /* Motorola Star*Core processor. */ - EM_ME16 Machine = 59 /* Toyota ME16 processor. */ - EM_ST100 Machine = 60 /* STMicroelectronics ST100 processor. */ - EM_TINYJ Machine = 61 /* Advanced Logic Corp. TinyJ processor. */ - EM_X86_64 Machine = 62 /* Advanced Micro Devices x86-64 */ - EM_AARCH64 Machine = 183 /* ARM 64-bit Architecture (AArch64) */ + EM_NONE Machine = 0 /* Unknown machine. */ + EM_M32 Machine = 1 /* AT&T WE32100. */ + EM_SPARC Machine = 2 /* Sun SPARC. */ + EM_386 Machine = 3 /* Intel i386. */ + EM_68K Machine = 4 /* Motorola 68000. */ + EM_88K Machine = 5 /* Motorola 88000. */ + EM_860 Machine = 7 /* Intel i860. */ + EM_MIPS Machine = 8 /* MIPS R3000 Big-Endian only. */ + EM_S370 Machine = 9 /* IBM System/370. */ + EM_MIPS_RS3_LE Machine = 10 /* MIPS R3000 Little-Endian. */ + EM_PARISC Machine = 15 /* HP PA-RISC. */ + EM_VPP500 Machine = 17 /* Fujitsu VPP500. */ + EM_SPARC32PLUS Machine = 18 /* SPARC v8plus. */ + EM_960 Machine = 19 /* Intel 80960. */ + EM_PPC Machine = 20 /* PowerPC 32-bit. */ + EM_PPC64 Machine = 21 /* PowerPC 64-bit. */ + EM_S390 Machine = 22 /* IBM System/390. */ + EM_V800 Machine = 36 /* NEC V800. */ + EM_FR20 Machine = 37 /* Fujitsu FR20. */ + EM_RH32 Machine = 38 /* TRW RH-32. */ + EM_RCE Machine = 39 /* Motorola RCE. */ + EM_ARM Machine = 40 /* ARM. */ + EM_SH Machine = 42 /* Hitachi SH. */ + EM_SPARCV9 Machine = 43 /* SPARC v9 64-bit. */ + EM_TRICORE Machine = 44 /* Siemens TriCore embedded processor. */ + EM_ARC Machine = 45 /* Argonaut RISC Core. */ + EM_H8_300 Machine = 46 /* Hitachi H8/300. */ + EM_H8_300H Machine = 47 /* Hitachi H8/300H. */ + EM_H8S Machine = 48 /* Hitachi H8S. */ + EM_H8_500 Machine = 49 /* Hitachi H8/500. */ + EM_IA_64 Machine = 50 /* Intel IA-64 Processor. */ + EM_MIPS_X Machine = 51 /* Stanford MIPS-X. */ + EM_COLDFIRE Machine = 52 /* Motorola ColdFire. */ + EM_68HC12 Machine = 53 /* Motorola M68HC12. */ + EM_MMA Machine = 54 /* Fujitsu MMA. */ + EM_PCP Machine = 55 /* Siemens PCP. */ + EM_NCPU Machine = 56 /* Sony nCPU. */ + EM_NDR1 Machine = 57 /* Denso NDR1 microprocessor. */ + EM_STARCORE Machine = 58 /* Motorola Star*Core processor. */ + EM_ME16 Machine = 59 /* Toyota ME16 processor. */ + EM_ST100 Machine = 60 /* STMicroelectronics ST100 processor. */ + EM_TINYJ Machine = 61 /* Advanced Logic Corp. TinyJ processor. */ + EM_X86_64 Machine = 62 /* Advanced Micro Devices x86-64 */ + EM_PDSP Machine = 63 /* Sony DSP Processor */ + EM_PDP10 Machine = 64 /* Digital Equipment Corp. PDP-10 */ + EM_PDP11 Machine = 65 /* Digital Equipment Corp. PDP-11 */ + EM_FX66 Machine = 66 /* Siemens FX66 microcontroller */ + EM_ST9PLUS Machine = 67 /* STMicroelectronics ST9+ 8/16 bit microcontroller */ + EM_ST7 Machine = 68 /* STMicroelectronics ST7 8-bit microcontroller */ + EM_68HC16 Machine = 69 /* Motorola MC68HC16 Microcontroller */ + EM_68HC11 Machine = 70 /* Motorola MC68HC11 Microcontroller */ + EM_68HC08 Machine = 71 /* Motorola MC68HC08 Microcontroller */ + EM_68HC05 Machine = 72 /* Motorola MC68HC05 Microcontroller */ + EM_SVX Machine = 73 /* Silicon Graphics SVx */ + EM_ST19 Machine = 74 /* STMicroelectronics ST19 8-bit microcontroller */ + EM_VAX Machine = 75 /* Digital VAX */ + EM_CRIS Machine = 76 /* Axis Communications 32-bit embedded processor */ + EM_JAVELIN Machine = 77 /* Infineon Technologies 32-bit embedded processor */ + EM_FIREPATH Machine = 78 /* Element 14 64-bit DSP Processor */ + EM_ZSP Machine = 79 /* LSI Logic 16-bit DSP Processor */ + EM_MMIX Machine = 80 /* Donald Knuth's educational 64-bit processor */ + EM_HUANY Machine = 81 /* Harvard University machine-independent object files */ + EM_PRISM Machine = 82 /* SiTera Prism */ + EM_AVR Machine = 83 /* Atmel AVR 8-bit microcontroller */ + EM_FR30 Machine = 84 /* Fujitsu FR30 */ + EM_D10V Machine = 85 /* Mitsubishi D10V */ + EM_D30V Machine = 86 /* Mitsubishi D30V */ + EM_V850 Machine = 87 /* NEC v850 */ + EM_M32R Machine = 88 /* Mitsubishi M32R */ + EM_MN10300 Machine = 89 /* Matsushita MN10300 */ + EM_MN10200 Machine = 90 /* Matsushita MN10200 */ + EM_PJ Machine = 91 /* picoJava */ + EM_OPENRISC Machine = 92 /* OpenRISC 32-bit embedded processor */ + EM_ARC_COMPACT Machine = 93 /* ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5) */ + EM_XTENSA Machine = 94 /* Tensilica Xtensa Architecture */ + EM_VIDEOCORE Machine = 95 /* Alphamosaic VideoCore processor */ + EM_TMM_GPP Machine = 96 /* Thompson Multimedia General Purpose Processor */ + EM_NS32K Machine = 97 /* National Semiconductor 32000 series */ + EM_TPC Machine = 98 /* Tenor Network TPC processor */ + EM_SNP1K Machine = 99 /* Trebia SNP 1000 processor */ + EM_ST200 Machine = 100 /* STMicroelectronics (www.st.com) ST200 microcontroller */ + EM_IP2K Machine = 101 /* Ubicom IP2xxx microcontroller family */ + EM_MAX Machine = 102 /* MAX Processor */ + EM_CR Machine = 103 /* National Semiconductor CompactRISC microprocessor */ + EM_F2MC16 Machine = 104 /* Fujitsu F2MC16 */ + EM_MSP430 Machine = 105 /* Texas Instruments embedded microcontroller msp430 */ + EM_BLACKFIN Machine = 106 /* Analog Devices Blackfin (DSP) processor */ + EM_SE_C33 Machine = 107 /* S1C33 Family of Seiko Epson processors */ + EM_SEP Machine = 108 /* Sharp embedded microprocessor */ + EM_ARCA Machine = 109 /* Arca RISC Microprocessor */ + EM_UNICORE Machine = 110 /* Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University */ + EM_EXCESS Machine = 111 /* eXcess: 16/32/64-bit configurable embedded CPU */ + EM_DXP Machine = 112 /* Icera Semiconductor Inc. Deep Execution Processor */ + EM_ALTERA_NIOS2 Machine = 113 /* Altera Nios II soft-core processor */ + EM_CRX Machine = 114 /* National Semiconductor CompactRISC CRX microprocessor */ + EM_XGATE Machine = 115 /* Motorola XGATE embedded processor */ + EM_C166 Machine = 116 /* Infineon C16x/XC16x processor */ + EM_M16C Machine = 117 /* Renesas M16C series microprocessors */ + EM_DSPIC30F Machine = 118 /* Microchip Technology dsPIC30F Digital Signal Controller */ + EM_CE Machine = 119 /* Freescale Communication Engine RISC core */ + EM_M32C Machine = 120 /* Renesas M32C series microprocessors */ + EM_TSK3000 Machine = 131 /* Altium TSK3000 core */ + EM_RS08 Machine = 132 /* Freescale RS08 embedded processor */ + EM_SHARC Machine = 133 /* Analog Devices SHARC family of 32-bit DSP processors */ + EM_ECOG2 Machine = 134 /* Cyan Technology eCOG2 microprocessor */ + EM_SCORE7 Machine = 135 /* Sunplus S+core7 RISC processor */ + EM_DSP24 Machine = 136 /* New Japan Radio (NJR) 24-bit DSP Processor */ + EM_VIDEOCORE3 Machine = 137 /* Broadcom VideoCore III processor */ + EM_LATTICEMICO32 Machine = 138 /* RISC processor for Lattice FPGA architecture */ + EM_SE_C17 Machine = 139 /* Seiko Epson C17 family */ + EM_TI_C6000 Machine = 140 /* The Texas Instruments TMS320C6000 DSP family */ + EM_TI_C2000 Machine = 141 /* The Texas Instruments TMS320C2000 DSP family */ + EM_TI_C5500 Machine = 142 /* The Texas Instruments TMS320C55x DSP family */ + EM_TI_ARP32 Machine = 143 /* Texas Instruments Application Specific RISC Processor, 32bit fetch */ + EM_TI_PRU Machine = 144 /* Texas Instruments Programmable Realtime Unit */ + EM_MMDSP_PLUS Machine = 160 /* STMicroelectronics 64bit VLIW Data Signal Processor */ + EM_CYPRESS_M8C Machine = 161 /* Cypress M8C microprocessor */ + EM_R32C Machine = 162 /* Renesas R32C series microprocessors */ + EM_TRIMEDIA Machine = 163 /* NXP Semiconductors TriMedia architecture family */ + EM_QDSP6 Machine = 164 /* QUALCOMM DSP6 Processor */ + EM_8051 Machine = 165 /* Intel 8051 and variants */ + EM_STXP7X Machine = 166 /* STMicroelectronics STxP7x family of configurable and extensible RISC processors */ + EM_NDS32 Machine = 167 /* Andes Technology compact code size embedded RISC processor family */ + EM_ECOG1 Machine = 168 /* Cyan Technology eCOG1X family */ + EM_ECOG1X Machine = 168 /* Cyan Technology eCOG1X family */ + EM_MAXQ30 Machine = 169 /* Dallas Semiconductor MAXQ30 Core Micro-controllers */ + EM_XIMO16 Machine = 170 /* New Japan Radio (NJR) 16-bit DSP Processor */ + EM_MANIK Machine = 171 /* M2000 Reconfigurable RISC Microprocessor */ + EM_CRAYNV2 Machine = 172 /* Cray Inc. NV2 vector architecture */ + EM_RX Machine = 173 /* Renesas RX family */ + EM_METAG Machine = 174 /* Imagination Technologies META processor architecture */ + EM_MCST_ELBRUS Machine = 175 /* MCST Elbrus general purpose hardware architecture */ + EM_ECOG16 Machine = 176 /* Cyan Technology eCOG16 family */ + EM_CR16 Machine = 177 /* National Semiconductor CompactRISC CR16 16-bit microprocessor */ + EM_ETPU Machine = 178 /* Freescale Extended Time Processing Unit */ + EM_SLE9X Machine = 179 /* Infineon Technologies SLE9X core */ + EM_L10M Machine = 180 /* Intel L10M */ + EM_K10M Machine = 181 /* Intel K10M */ + EM_AARCH64 Machine = 183 /* ARM 64-bit Architecture (AArch64) */ + EM_AVR32 Machine = 185 /* Atmel Corporation 32-bit microprocessor family */ + EM_STM8 Machine = 186 /* STMicroeletronics STM8 8-bit microcontroller */ + EM_TILE64 Machine = 187 /* Tilera TILE64 multicore architecture family */ + EM_TILEPRO Machine = 188 /* Tilera TILEPro multicore architecture family */ + EM_MICROBLAZE Machine = 189 /* Xilinx MicroBlaze 32-bit RISC soft processor core */ + EM_CUDA Machine = 190 /* NVIDIA CUDA architecture */ + EM_TILEGX Machine = 191 /* Tilera TILE-Gx multicore architecture family */ + EM_CLOUDSHIELD Machine = 192 /* CloudShield architecture family */ + EM_COREA_1ST Machine = 193 /* KIPO-KAIST Core-A 1st generation processor family */ + EM_COREA_2ND Machine = 194 /* KIPO-KAIST Core-A 2nd generation processor family */ + EM_ARC_COMPACT2 Machine = 195 /* Synopsys ARCompact V2 */ + EM_OPEN8 Machine = 196 /* Open8 8-bit RISC soft processor core */ + EM_RL78 Machine = 197 /* Renesas RL78 family */ + EM_VIDEOCORE5 Machine = 198 /* Broadcom VideoCore V processor */ + EM_78KOR Machine = 199 /* Renesas 78KOR family */ + EM_56800EX Machine = 200 /* Freescale 56800EX Digital Signal Controller (DSC) */ + EM_BA1 Machine = 201 /* Beyond BA1 CPU architecture */ + EM_BA2 Machine = 202 /* Beyond BA2 CPU architecture */ + EM_XCORE Machine = 203 /* XMOS xCORE processor family */ + EM_MCHP_PIC Machine = 204 /* Microchip 8-bit PIC(r) family */ + EM_INTEL205 Machine = 205 /* Reserved by Intel */ + EM_INTEL206 Machine = 206 /* Reserved by Intel */ + EM_INTEL207 Machine = 207 /* Reserved by Intel */ + EM_INTEL208 Machine = 208 /* Reserved by Intel */ + EM_INTEL209 Machine = 209 /* Reserved by Intel */ + EM_KM32 Machine = 210 /* KM211 KM32 32-bit processor */ + EM_KMX32 Machine = 211 /* KM211 KMX32 32-bit processor */ + EM_KMX16 Machine = 212 /* KM211 KMX16 16-bit processor */ + EM_KMX8 Machine = 213 /* KM211 KMX8 8-bit processor */ + EM_KVARC Machine = 214 /* KM211 KVARC processor */ + EM_CDP Machine = 215 /* Paneve CDP architecture family */ + EM_COGE Machine = 216 /* Cognitive Smart Memory Processor */ + EM_COOL Machine = 217 /* Bluechip Systems CoolEngine */ + EM_NORC Machine = 218 /* Nanoradio Optimized RISC */ + EM_CSR_KALIMBA Machine = 219 /* CSR Kalimba architecture family */ + EM_Z80 Machine = 220 /* Zilog Z80 */ + EM_VISIUM Machine = 221 /* Controls and Data Services VISIUMcore processor */ + EM_FT32 Machine = 222 /* FTDI Chip FT32 high performance 32-bit RISC architecture */ + EM_MOXIE Machine = 223 /* Moxie processor family */ + EM_AMDGPU Machine = 224 /* AMD GPU architecture */ + EM_RISCV Machine = 243 /* RISC-V */ + EM_LANAI Machine = 244 /* Lanai 32-bit processor */ + EM_BPF Machine = 247 /* Linux BPF – in-kernel virtual machine */ /* Non-standard or deprecated. */ EM_486 Machine = 6 /* Intel i486. */ @@ -289,6 +436,145 @@ var machineStrings = []intName{ {60, "EM_ST100"}, {61, "EM_TINYJ"}, {62, "EM_X86_64"}, + {63, "EM_PDSP"}, + {64, "EM_PDP10"}, + {65, "EM_PDP11"}, + {66, "EM_FX66"}, + {67, "EM_ST9PLUS"}, + {68, "EM_ST7"}, + {69, "EM_68HC16"}, + {70, "EM_68HC11"}, + {71, "EM_68HC08"}, + {72, "EM_68HC05"}, + {73, "EM_SVX"}, + {74, "EM_ST19"}, + {75, "EM_VAX"}, + {76, "EM_CRIS"}, + {77, "EM_JAVELIN"}, + {78, "EM_FIREPATH"}, + {79, "EM_ZSP"}, + {80, "EM_MMIX"}, + {81, "EM_HUANY"}, + {82, "EM_PRISM"}, + {83, "EM_AVR"}, + {84, "EM_FR30"}, + {85, "EM_D10V"}, + {86, "EM_D30V"}, + {87, "EM_V850"}, + {88, "EM_M32R"}, + {89, "EM_MN10300"}, + {90, "EM_MN10200"}, + {91, "EM_PJ"}, + {92, "EM_OPENRISC"}, + {93, "EM_ARC_COMPACT"}, + {94, "EM_XTENSA"}, + {95, "EM_VIDEOCORE"}, + {96, "EM_TMM_GPP"}, + {97, "EM_NS32K"}, + {98, "EM_TPC"}, + {99, "EM_SNP1K"}, + {100, "EM_ST200"}, + {101, "EM_IP2K"}, + {102, "EM_MAX"}, + {103, "EM_CR"}, + {104, "EM_F2MC16"}, + {105, "EM_MSP430"}, + {106, "EM_BLACKFIN"}, + {107, "EM_SE_C33"}, + {108, "EM_SEP"}, + {109, "EM_ARCA"}, + {110, "EM_UNICORE"}, + {111, "EM_EXCESS"}, + {112, "EM_DXP"}, + {113, "EM_ALTERA_NIOS2"}, + {114, "EM_CRX"}, + {115, "EM_XGATE"}, + {116, "EM_C166"}, + {117, "EM_M16C"}, + {118, "EM_DSPIC30F"}, + {119, "EM_CE"}, + {120, "EM_M32C"}, + {131, "EM_TSK3000"}, + {132, "EM_RS08"}, + {133, "EM_SHARC"}, + {134, "EM_ECOG2"}, + {135, "EM_SCORE7"}, + {136, "EM_DSP24"}, + {137, "EM_VIDEOCORE3"}, + {138, "EM_LATTICEMICO32"}, + {139, "EM_SE_C17"}, + {140, "EM_TI_C6000"}, + {141, "EM_TI_C2000"}, + {142, "EM_TI_C5500"}, + {143, "EM_TI_ARP32"}, + {144, "EM_TI_PRU"}, + {160, "EM_MMDSP_PLUS"}, + {161, "EM_CYPRESS_M8C"}, + {162, "EM_R32C"}, + {163, "EM_TRIMEDIA"}, + {164, "EM_QDSP6"}, + {165, "EM_8051"}, + {166, "EM_STXP7X"}, + {167, "EM_NDS32"}, + {168, "EM_ECOG1"}, + {168, "EM_ECOG1X"}, + {169, "EM_MAXQ30"}, + {170, "EM_XIMO16"}, + {171, "EM_MANIK"}, + {172, "EM_CRAYNV2"}, + {173, "EM_RX"}, + {174, "EM_METAG"}, + {175, "EM_MCST_ELBRUS"}, + {176, "EM_ECOG16"}, + {177, "EM_CR16"}, + {178, "EM_ETPU"}, + {179, "EM_SLE9X"}, + {180, "EM_L10M"}, + {181, "EM_K10M"}, + {183, "EM_AARCH64"}, + {185, "EM_AVR32"}, + {186, "EM_STM8"}, + {187, "EM_TILE64"}, + {188, "EM_TILEPRO"}, + {189, "EM_MICROBLAZE"}, + {190, "EM_CUDA"}, + {191, "EM_TILEGX"}, + {192, "EM_CLOUDSHIELD"}, + {193, "EM_COREA_1ST"}, + {194, "EM_COREA_2ND"}, + {195, "EM_ARC_COMPACT2"}, + {196, "EM_OPEN8"}, + {197, "EM_RL78"}, + {198, "EM_VIDEOCORE5"}, + {199, "EM_78KOR"}, + {200, "EM_56800EX"}, + {201, "EM_BA1"}, + {202, "EM_BA2"}, + {203, "EM_XCORE"}, + {204, "EM_MCHP_PIC"}, + {205, "EM_INTEL205"}, + {206, "EM_INTEL206"}, + {207, "EM_INTEL207"}, + {208, "EM_INTEL208"}, + {209, "EM_INTEL209"}, + {210, "EM_KM32"}, + {211, "EM_KMX32"}, + {212, "EM_KMX16"}, + {213, "EM_KMX8"}, + {214, "EM_KVARC"}, + {215, "EM_CDP"}, + {216, "EM_COGE"}, + {217, "EM_COOL"}, + {218, "EM_NORC"}, + {219, "EM_CSR_KALIMBA "}, + {220, "EM_Z80 "}, + {221, "EM_VISIUM "}, + {222, "EM_FT32 "}, + {223, "EM_MOXIE"}, + {224, "EM_AMDGPU"}, + {243, "EM_RISCV"}, + {244, "EM_LANAI"}, + {247, "EM_BPF"}, /* Non-standard or deprecated. */ {6, "EM_486"}, @@ -2081,6 +2367,124 @@ var rppc64Strings = []intName{ func (i R_PPC64) String() string { return stringName(uint32(i), rppc64Strings, false) } func (i R_PPC64) GoString() string { return stringName(uint32(i), rppc64Strings, true) } +// Relocation types for RISC-V processors. +type R_RISCV int + +const ( + R_RISCV_NONE R_RISCV = 0 /* No relocation. */ + R_RISCV_32 R_RISCV = 1 /* Add 32 bit zero extended symbol value */ + R_RISCV_64 R_RISCV = 2 /* Add 64 bit symbol value. */ + R_RISCV_RELATIVE R_RISCV = 3 /* Add load address of shared object. */ + R_RISCV_COPY R_RISCV = 4 /* Copy data from shared object. */ + R_RISCV_JUMP_SLOT R_RISCV = 5 /* Set GOT entry to code address. */ + R_RISCV_TLS_DTPMOD32 R_RISCV = 6 /* 32 bit ID of module containing symbol */ + R_RISCV_TLS_DTPMOD64 R_RISCV = 7 /* ID of module containing symbol */ + R_RISCV_TLS_DTPREL32 R_RISCV = 8 /* 32 bit relative offset in TLS block */ + R_RISCV_TLS_DTPREL64 R_RISCV = 9 /* Relative offset in TLS block */ + R_RISCV_TLS_TPREL32 R_RISCV = 10 /* 32 bit relative offset in static TLS block */ + R_RISCV_TLS_TPREL64 R_RISCV = 11 /* Relative offset in static TLS block */ + R_RISCV_BRANCH R_RISCV = 16 /* PC-relative branch */ + R_RISCV_JAL R_RISCV = 17 /* PC-relative jump */ + R_RISCV_CALL R_RISCV = 18 /* PC-relative call */ + R_RISCV_CALL_PLT R_RISCV = 19 /* PC-relative call (PLT) */ + R_RISCV_GOT_HI20 R_RISCV = 20 /* PC-relative GOT reference */ + R_RISCV_TLS_GOT_HI20 R_RISCV = 21 /* PC-relative TLS IE GOT offset */ + R_RISCV_TLS_GD_HI20 R_RISCV = 22 /* PC-relative TLS GD reference */ + R_RISCV_PCREL_HI20 R_RISCV = 23 /* PC-relative reference */ + R_RISCV_PCREL_LO12_I R_RISCV = 24 /* PC-relative reference */ + R_RISCV_PCREL_LO12_S R_RISCV = 25 /* PC-relative reference */ + R_RISCV_HI20 R_RISCV = 26 /* Absolute address */ + R_RISCV_LO12_I R_RISCV = 27 /* Absolute address */ + R_RISCV_LO12_S R_RISCV = 28 /* Absolute address */ + R_RISCV_TPREL_HI20 R_RISCV = 29 /* TLS LE thread offset */ + R_RISCV_TPREL_LO12_I R_RISCV = 30 /* TLS LE thread offset */ + R_RISCV_TPREL_LO12_S R_RISCV = 31 /* TLS LE thread offset */ + R_RISCV_TPREL_ADD R_RISCV = 32 /* TLS LE thread usage */ + R_RISCV_ADD8 R_RISCV = 33 /* 8-bit label addition */ + R_RISCV_ADD16 R_RISCV = 34 /* 16-bit label addition */ + R_RISCV_ADD32 R_RISCV = 35 /* 32-bit label addition */ + R_RISCV_ADD64 R_RISCV = 36 /* 64-bit label addition */ + R_RISCV_SUB8 R_RISCV = 37 /* 8-bit label subtraction */ + R_RISCV_SUB16 R_RISCV = 38 /* 16-bit label subtraction */ + R_RISCV_SUB32 R_RISCV = 39 /* 32-bit label subtraction */ + R_RISCV_SUB64 R_RISCV = 40 /* 64-bit label subtraction */ + R_RISCV_GNU_VTINHERIT R_RISCV = 41 /* GNU C++ vtable hierarchy */ + R_RISCV_GNU_VTENTRY R_RISCV = 42 /* GNU C++ vtable member usage */ + R_RISCV_ALIGN R_RISCV = 43 /* Alignment statement */ + R_RISCV_RVC_BRANCH R_RISCV = 44 /* PC-relative branch offset */ + R_RISCV_RVC_JUMP R_RISCV = 45 /* PC-relative jump offset */ + R_RISCV_RVC_LUI R_RISCV = 46 /* Absolute address */ + R_RISCV_GPREL_I R_RISCV = 47 /* GP-relative reference */ + R_RISCV_GPREL_S R_RISCV = 48 /* GP-relative reference */ + R_RISCV_TPREL_I R_RISCV = 49 /* TP-relative TLS LE load */ + R_RISCV_TPREL_S R_RISCV = 50 /* TP-relative TLS LE store */ + R_RISCV_RELAX R_RISCV = 51 /* Instruction pair can be relaxed */ + R_RISCV_SUB6 R_RISCV = 52 /* Local label subtraction */ + R_RISCV_SET6 R_RISCV = 53 /* Local label subtraction */ + R_RISCV_SET8 R_RISCV = 54 /* Local label subtraction */ + R_RISCV_SET16 R_RISCV = 55 /* Local label subtraction */ + R_RISCV_SET32 R_RISCV = 56 /* Local label subtraction */ +) + +var rriscvStrings = []intName{ + {0, "R_RISCV_NONE"}, + {1, "R_RISCV_32"}, + {2, "R_RISCV_64"}, + {3, "R_RISCV_RELATIVE"}, + {4, "R_RISCV_COPY"}, + {5, "R_RISCV_JUMP_SLOT"}, + {6, "R_RISCV_TLS_DTPMOD32"}, + {7, "R_RISCV_TLS_DTPMOD64"}, + {8, "R_RISCV_TLS_DTPREL32"}, + {9, "R_RISCV_TLS_DTPREL64"}, + {10, "R_RISCV_TLS_TPREL32"}, + {11, "R_RISCV_TLS_TPREL64"}, + {16, "R_RISCV_BRANCH"}, + {17, "R_RISCV_JAL"}, + {18, "R_RISCV_CALL"}, + {19, "R_RISCV_CALL_PLT"}, + {20, "R_RISCV_GOT_HI20"}, + {21, "R_RISCV_TLS_GOT_HI20"}, + {22, "R_RISCV_TLS_GD_HI20"}, + {23, "R_RISCV_PCREL_HI20"}, + {24, "R_RISCV_PCREL_LO12_I"}, + {25, "R_RISCV_PCREL_LO12_S"}, + {26, "R_RISCV_HI20"}, + {27, "R_RISCV_LO12_I"}, + {28, "R_RISCV_LO12_S"}, + {29, "R_RISCV_TPREL_HI20"}, + {30, "R_RISCV_TPREL_LO12_I"}, + {31, "R_RISCV_TPREL_LO12_S"}, + {32, "R_RISCV_TPREL_ADD"}, + {33, "R_RISCV_ADD8"}, + {34, "R_RISCV_ADD16"}, + {35, "R_RISCV_ADD32"}, + {36, "R_RISCV_ADD64"}, + {37, "R_RISCV_SUB8"}, + {38, "R_RISCV_SUB16"}, + {39, "R_RISCV_SUB32"}, + {40, "R_RISCV_SUB64"}, + {41, "R_RISCV_GNU_VTINHERIT"}, + {42, "R_RISCV_GNU_VTENTRY"}, + {43, "R_RISCV_ALIGN"}, + {44, "R_RISCV_RVC_BRANCH"}, + {45, "R_RISCV_RVC_JUMP"}, + {46, "R_RISCV_RVC_LUI"}, + {47, "R_RISCV_GPREL_I"}, + {48, "R_RISCV_GPREL_S"}, + {49, "R_RISCV_TPREL_I"}, + {50, "R_RISCV_TPREL_S"}, + {51, "R_RISCV_RELAX"}, + {52, "R_RISCV_SUB6"}, + {53, "R_RISCV_SET6"}, + {54, "R_RISCV_SET8"}, + {55, "R_RISCV_SET16"}, + {56, "R_RISCV_SET32"}, +} + +func (i R_RISCV) String() string { return stringName(uint32(i), rriscvStrings, false) } +func (i R_RISCV) GoString() string { return stringName(uint32(i), rriscvStrings, true) } + // Relocation types for s390x processors. type R_390 int diff --git a/libgo/go/debug/elf/file.go b/libgo/go/debug/elf/file.go index f28ae27..bd61464 100644 --- a/libgo/go/debug/elf/file.go +++ b/libgo/go/debug/elf/file.go @@ -609,6 +609,8 @@ func (f *File) applyRelocations(dst []byte, rels []byte) error { return f.applyRelocationsMIPS(dst, rels) case f.Class == ELFCLASS64 && f.Machine == EM_MIPS: return f.applyRelocationsMIPS64(dst, rels) + case f.Class == ELFCLASS64 && f.Machine == EM_RISCV: + return f.applyRelocationsRISCV64(dst, rels) case f.Class == ELFCLASS64 && f.Machine == EM_S390: return f.applyRelocationss390x(dst, rels) case f.Class == ELFCLASS32 && (f.Machine == EM_SPARC || f.Machine == EM_SPARC32PLUS): @@ -970,6 +972,55 @@ func (f *File) applyRelocationsMIPS64(dst []byte, rels []byte) error { return nil } +func (f *File) applyRelocationsRISCV64(dst []byte, rels []byte) error { + // 24 is the size of Rela64. + if len(rels)%24 != 0 { + return errors.New("length of relocation section is not a multiple of 24") + } + + symbols, _, err := f.getSymbols(SHT_SYMTAB) + if err != nil { + return err + } + + b := bytes.NewReader(rels) + var rela Rela64 + + for b.Len() > 0 { + binary.Read(b, f.ByteOrder, &rela) + symNo := rela.Info >> 32 + t := R_RISCV(rela.Info & 0xffff) + + if symNo == 0 || symNo > uint64(len(symbols)) { + continue + } + sym := &symbols[symNo-1] + switch SymType(sym.Info & 0xf) { + case STT_SECTION, STT_NOTYPE: + break + default: + continue + } + + switch t { + case R_RISCV_64: + if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + val := sym.Value + uint64(rela.Addend) + f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val) + case R_RISCV_32: + if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + val := uint32(sym.Value) + uint32(rela.Addend) + f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val) + } + } + + return nil +} + func (f *File) applyRelocationss390x(dst []byte, rels []byte) error { // 24 is the size of Rela64. if len(rels)%24 != 0 { @@ -1154,6 +1205,17 @@ func (f *File) applyRelocationsALPHA(dst []byte, rels []byte) error { } func (f *File) DWARF() (*dwarf.Data, error) { + dwarfSuffix := func(s *Section) string { + switch { + case strings.HasPrefix(s.Name, ".debug_"): + return s.Name[7:] + case strings.HasPrefix(s.Name, ".zdebug_"): + return s.Name[8:] + default: + return "" + } + + } // sectionData gets the data for s, checks its size, and // applies any applicable relations. sectionData := func(i int, s *Section) ([]byte, error) { @@ -1202,13 +1264,8 @@ func (f *File) DWARF() (*dwarf.Data, error) { // Don't bother loading others. var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil} for i, s := range f.Sections { - suffix := "" - switch { - case strings.HasPrefix(s.Name, ".debug_"): - suffix = s.Name[7:] - case strings.HasPrefix(s.Name, ".zdebug_"): - suffix = s.Name[8:] - default: + suffix := dwarfSuffix(s) + if suffix == "" { continue } if _, ok := dat[suffix]; !ok { @@ -1228,16 +1285,19 @@ func (f *File) DWARF() (*dwarf.Data, error) { // Look for DWARF4 .debug_types sections. for i, s := range f.Sections { - if s.Name == ".debug_types" { - b, err := sectionData(i, s) - if err != nil { - return nil, err - } + suffix := dwarfSuffix(s) + if suffix != "types" { + continue + } - err = d.AddTypes(fmt.Sprintf("types-%d", i), b) - if err != nil { - return nil, err - } + b, err := sectionData(i, s) + if err != nil { + return nil, err + } + + err = d.AddTypes(fmt.Sprintf("types-%d", i), b) + if err != nil { + return nil, err } } diff --git a/libgo/go/debug/elf/file_test.go b/libgo/go/debug/elf/file_test.go index 58bdf27..11d8992 100644 --- a/libgo/go/debug/elf/file_test.go +++ b/libgo/go/debug/elf/file_test.go @@ -568,6 +568,25 @@ var relocationTests = []relocationTest{ }, }, { + "testdata/go-relocation-test-gcc720-riscv64.obj", + []relocationTestEntry{ + {0, &dwarf.Entry{ + Offset: 0xb, + Tag: dwarf.TagCompileUnit, + Children: true, + Field: []dwarf.Field{ + {Attr: dwarf.AttrProducer, Val: "GNU C11 7.2.0 -march=rv64imafdc -mabi=lp64d -g -gdwarf-2", Class: dwarf.ClassString}, + {Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant}, + {Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString}, + {Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}, + {Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, + {Attr: dwarf.AttrHighpc, Val: uint64(0x2c), Class: dwarf.ClassAddress}, + {Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}, + }, + }}, + }, + }, + { "testdata/go-relocation-test-clang-x86.obj", []relocationTestEntry{ {0, &dwarf.Entry{ @@ -763,9 +782,10 @@ func TestCompressedSection(t *testing.T) { } func TestNoSectionOverlaps(t *testing.T) { - // Ensure 6l outputs sections without overlaps. - if runtime.GOOS != "linux" && runtime.GOOS != "freebsd" { - return // not ELF + // Ensure cmd/link outputs sections without overlaps. + switch runtime.GOOS { + case "android", "darwin", "js", "nacl", "plan9", "windows": + t.Skipf("cmd/link doesn't produce ELF binaries on %s", runtime.GOOS) } _ = net.ResolveIPAddr // force dynamic linkage f, err := Open(os.Args[0]) diff --git a/libgo/go/debug/elf/testdata/go-relocation-test-gcc720-riscv64.obj b/libgo/go/debug/elf/testdata/go-relocation-test-gcc720-riscv64.obj Binary files differnew file mode 100644 index 0000000..91ae648 --- /dev/null +++ b/libgo/go/debug/elf/testdata/go-relocation-test-gcc720-riscv64.obj diff --git a/libgo/go/debug/gosym/pclntab.go b/libgo/go/debug/gosym/pclntab.go index ba1cf8b..ad99b4d 100644 --- a/libgo/go/debug/gosym/pclntab.go +++ b/libgo/go/debug/gosym/pclntab.go @@ -9,6 +9,7 @@ package gosym import ( + "bytes" "encoding/binary" "sync" ) @@ -42,6 +43,7 @@ type LineTable struct { filetab []byte nfiletab uint32 fileMap map[string]uint32 + strings map[uint32]string // interned substrings of Data, keyed by offset } // NOTE(rsc): This is wrong for GOARCH=arm, which uses a quantum of 4, @@ -120,7 +122,7 @@ func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 { // Text must be the start address of the // corresponding text segment. func NewLineTable(data []byte, text uint64) *LineTable { - return &LineTable{Data: data, PC: text, Line: 0} + return &LineTable{Data: data, PC: text, Line: 0, strings: make(map[uint32]string)} } // Go 1.2 symbol table format. @@ -266,11 +268,13 @@ func (t *LineTable) readvarint(pp *[]byte) uint32 { // string returns a Go string found at off. func (t *LineTable) string(off uint32) string { - for i := off; ; i++ { - if t.Data[i] == 0 { - return string(t.Data[off:i]) - } + if s, ok := t.strings[off]; ok { + return s } + i := bytes.IndexByte(t.Data[off:], 0) + s := string(t.Data[off : off+uint32(i)]) + t.strings[off] = s + return s } // step advances to the next pc, value pair in the encoded table. diff --git a/libgo/go/debug/macho/file.go b/libgo/go/debug/macho/file.go index 7b9e83e..16708e5 100644 --- a/libgo/go/debug/macho/file.go +++ b/libgo/go/debug/macho/file.go @@ -9,11 +9,13 @@ package macho import ( "bytes" + "compress/zlib" "debug/dwarf" "encoding/binary" "fmt" "io" "os" + "strings" ) // A File represents an open Mach-O file. @@ -545,8 +547,9 @@ func (f *File) pushSection(sh *Section, r io.ReaderAt) error { } func cstring(b []byte) string { - var i int - for i = 0; i < len(b) && b[i] != 0; i++ { + i := bytes.IndexByte(b, 0) + if i == -1 { + i = len(b) } return string(b[0:i]) } @@ -574,26 +577,84 @@ func (f *File) Section(name string) *Section { // DWARF returns the DWARF debug information for the Mach-O file. func (f *File) DWARF() (*dwarf.Data, error) { + dwarfSuffix := func(s *Section) string { + switch { + case strings.HasPrefix(s.Name, "__debug_"): + return s.Name[8:] + case strings.HasPrefix(s.Name, "__zdebug_"): + return s.Name[9:] + default: + return "" + } + + } + sectionData := func(s *Section) ([]byte, error) { + b, err := s.Data() + if err != nil && uint64(len(b)) < s.Size { + return nil, err + } + + if len(b) >= 12 && string(b[:4]) == "ZLIB" { + dlen := binary.BigEndian.Uint64(b[4:12]) + dbuf := make([]byte, dlen) + r, err := zlib.NewReader(bytes.NewBuffer(b[12:])) + if err != nil { + return nil, err + } + if _, err := io.ReadFull(r, dbuf); err != nil { + return nil, err + } + if err := r.Close(); err != nil { + return nil, err + } + b = dbuf + } + return b, nil + } + // There are many other DWARF sections, but these // are the ones the debug/dwarf package uses. // Don't bother loading others. - var names = [...]string{"abbrev", "info", "line", "ranges", "str"} - var dat [len(names)][]byte - for i, name := range names { - name = "__debug_" + name - s := f.Section(name) - if s == nil { + var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil} + for _, s := range f.Sections { + suffix := dwarfSuffix(s) + if suffix == "" { continue } - b, err := s.Data() - if err != nil && uint64(len(b)) < s.Size { + if _, ok := dat[suffix]; !ok { + continue + } + b, err := sectionData(s) + if err != nil { + return nil, err + } + dat[suffix] = b + } + + d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"]) + if err != nil { + return nil, err + } + + // Look for DWARF4 .debug_types sections. + for i, s := range f.Sections { + suffix := dwarfSuffix(s) + if suffix != "types" { + continue + } + + b, err := sectionData(s) + if err != nil { + return nil, err + } + + err = d.AddTypes(fmt.Sprintf("types-%d", i), b) + if err != nil { return nil, err } - dat[i] = b } - abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4] - return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str) + return d, nil } // ImportedSymbols returns the names of all symbols diff --git a/libgo/go/debug/macho/macho.go b/libgo/go/debug/macho/macho.go index fed8eb2..7bc1950 100644 --- a/libgo/go/debug/macho/macho.go +++ b/libgo/go/debug/macho/macho.go @@ -60,6 +60,7 @@ const ( Cpu386 Cpu = 7 CpuAmd64 Cpu = Cpu386 | cpuArch64 CpuArm Cpu = 12 + CpuArm64 Cpu = CpuArm | cpuArch64 CpuPpc Cpu = 18 CpuPpc64 Cpu = CpuPpc | cpuArch64 ) @@ -68,6 +69,7 @@ var cpuStrings = []intName{ {uint32(Cpu386), "Cpu386"}, {uint32(CpuAmd64), "CpuAmd64"}, {uint32(CpuArm), "CpuArm"}, + {uint32(CpuArm64), "CpuArm64"}, {uint32(CpuPpc), "CpuPpc"}, {uint32(CpuPpc64), "CpuPpc64"}, } diff --git a/libgo/go/debug/macho/reloctype_string.go b/libgo/go/debug/macho/reloctype_string.go index 6d5c5d8..9c2b131 100644 --- a/libgo/go/debug/macho/reloctype_string.go +++ b/libgo/go/debug/macho/reloctype_string.go @@ -2,7 +2,7 @@ package macho -import "fmt" +import "strconv" const _RelocTypeGeneric_name = "GENERIC_RELOC_VANILLAGENERIC_RELOC_PAIRGENERIC_RELOC_SECTDIFFGENERIC_RELOC_PB_LA_PTRGENERIC_RELOC_LOCAL_SECTDIFFGENERIC_RELOC_TLV" @@ -10,7 +10,7 @@ var _RelocTypeGeneric_index = [...]uint8{0, 21, 39, 61, 84, 112, 129} func (i RelocTypeGeneric) String() string { if i < 0 || i >= RelocTypeGeneric(len(_RelocTypeGeneric_index)-1) { - return fmt.Sprintf("RelocTypeGeneric(%d)", i) + return "RelocTypeGeneric(" + strconv.FormatInt(int64(i), 10) + ")" } return _RelocTypeGeneric_name[_RelocTypeGeneric_index[i]:_RelocTypeGeneric_index[i+1]] } @@ -21,7 +21,7 @@ var _RelocTypeX86_64_index = [...]uint8{0, 21, 40, 59, 80, 96, 119, 140, 161, 18 func (i RelocTypeX86_64) String() string { if i < 0 || i >= RelocTypeX86_64(len(_RelocTypeX86_64_index)-1) { - return fmt.Sprintf("RelocTypeX86_64(%d)", i) + return "RelocTypeX86_64(" + strconv.FormatInt(int64(i), 10) + ")" } return _RelocTypeX86_64_name[_RelocTypeX86_64_index[i]:_RelocTypeX86_64_index[i+1]] } @@ -32,7 +32,7 @@ var _RelocTypeARM_index = [...]uint8{0, 17, 31, 49, 73, 92, 106, 126, 148, 162, func (i RelocTypeARM) String() string { if i < 0 || i >= RelocTypeARM(len(_RelocTypeARM_index)-1) { - return fmt.Sprintf("RelocTypeARM(%d)", i) + return "RelocTypeARM(" + strconv.FormatInt(int64(i), 10) + ")" } return _RelocTypeARM_name[_RelocTypeARM_index[i]:_RelocTypeARM_index[i+1]] } @@ -43,7 +43,7 @@ var _RelocTypeARM64_index = [...]uint16{0, 20, 42, 62, 80, 101, 128, 158, 184, 2 func (i RelocTypeARM64) String() string { if i < 0 || i >= RelocTypeARM64(len(_RelocTypeARM64_index)-1) { - return fmt.Sprintf("RelocTypeARM64(%d)", i) + return "RelocTypeARM64(" + strconv.FormatInt(int64(i), 10) + ")" } return _RelocTypeARM64_name[_RelocTypeARM64_index[i]:_RelocTypeARM64_index[i+1]] } diff --git a/libgo/go/debug/pe/file.go b/libgo/go/debug/pe/file.go index 87f225c..2f5efae 100644 --- a/libgo/go/debug/pe/file.go +++ b/libgo/go/debug/pe/file.go @@ -6,11 +6,14 @@ package pe import ( + "bytes" + "compress/zlib" "debug/dwarf" "encoding/binary" "fmt" "io" "os" + "strings" ) // Avoid use of post-Go 1.4 io features, to make safe for toolchain bootstrap. @@ -217,29 +220,91 @@ func (f *File) Section(name string) *Section { } func (f *File) DWARF() (*dwarf.Data, error) { - // There are many other DWARF sections, but these - // are the ones the debug/dwarf package uses. - // Don't bother loading others. - var names = [...]string{"abbrev", "info", "line", "ranges", "str"} - var dat [len(names)][]byte - for i, name := range names { - name = ".debug_" + name - s := f.Section(name) - if s == nil { - continue + dwarfSuffix := func(s *Section) string { + switch { + case strings.HasPrefix(s.Name, ".debug_"): + return s.Name[7:] + case strings.HasPrefix(s.Name, ".zdebug_"): + return s.Name[8:] + default: + return "" } + + } + + // sectionData gets the data for s and checks its size. + sectionData := func(s *Section) ([]byte, error) { b, err := s.Data() if err != nil && uint32(len(b)) < s.Size { return nil, err } + if 0 < s.VirtualSize && s.VirtualSize < s.Size { b = b[:s.VirtualSize] } - dat[i] = b + + if len(b) >= 12 && string(b[:4]) == "ZLIB" { + dlen := binary.BigEndian.Uint64(b[4:12]) + dbuf := make([]byte, dlen) + r, err := zlib.NewReader(bytes.NewBuffer(b[12:])) + if err != nil { + return nil, err + } + if _, err := io.ReadFull(r, dbuf); err != nil { + return nil, err + } + if err := r.Close(); err != nil { + return nil, err + } + b = dbuf + } + return b, nil + } + + // There are many other DWARF sections, but these + // are the ones the debug/dwarf package uses. + // Don't bother loading others. + var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil} + for _, s := range f.Sections { + suffix := dwarfSuffix(s) + if suffix == "" { + continue + } + if _, ok := dat[suffix]; !ok { + continue + } + + b, err := sectionData(s) + if err != nil { + return nil, err + } + dat[suffix] = b + } + + d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"]) + if err != nil { + return nil, err } - abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4] - return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str) + // Look for DWARF4 .debug_types sections. + for i, s := range f.Sections { + suffix := dwarfSuffix(s) + if suffix != "types" { + continue + } + + b, err := sectionData(s) + if err != nil { + return nil, err + } + + err = d.AddTypes(fmt.Sprintf("types-%d", i), b) + if err != nil { + return nil, err + } + } + + return d, nil } // TODO(brainman): document ImportDirectory once we decide what to do with it. @@ -260,19 +325,59 @@ type ImportDirectory struct { // It does not return weak symbols. func (f *File) ImportedSymbols() ([]string, error) { pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 - ds := f.Section(".idata") + + // grab the number of data directory entries + var dd_length uint32 + if pe64 { + dd_length = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes + } else { + dd_length = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes + } + + // check that the length of data directory entries is large + // enough to include the imports directory. + if dd_length < IMAGE_DIRECTORY_ENTRY_IMPORT+1 { + return nil, nil + } + + // grab the import data directory entry + var idd DataDirectory + if pe64 { + idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] + } else { + idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] + } + + // figure out which section contains the import directory table + var ds *Section + ds = nil + for _, s := range f.Sections { + if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress < s.VirtualAddress+s.VirtualSize { + ds = s + break + } + } + + // didn't find a section, so no import libraries were found if ds == nil { - // not dynamic, so no libraries return nil, nil } + d, err := ds.Data() if err != nil { return nil, err } + + // seek to the virtual address specified in the import data directory + d = d[idd.VirtualAddress-ds.VirtualAddress:] + + // start decoding the import directory var ida []ImportDirectory for len(d) > 0 { var dt ImportDirectory dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4]) + dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8]) + dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12]) dt.Name = binary.LittleEndian.Uint32(d[12:16]) dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20]) d = d[20:] @@ -282,7 +387,7 @@ func (f *File) ImportedSymbols() ([]string, error) { ida = append(ida, dt) } // TODO(brainman): this needs to be rewritten - // ds.Data() return contets of .idata section. Why store in variable called "names"? + // ds.Data() returns contents of section containing import table. Why store in variable called "names"? // Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere. // getString does not extracts a string from symbol string table (as getString doco says). // Why ds.Data() called again and again in the loop? diff --git a/libgo/go/debug/pe/file_test.go b/libgo/go/debug/pe/file_test.go index 8645d67..24cd673 100644 --- a/libgo/go/debug/pe/file_test.go +++ b/libgo/go/debug/pe/file_test.go @@ -532,3 +532,31 @@ func TestBuildingWindowsGUI(t *testing.T) { t.Fatalf("unexpected OptionalHeader type: have %T, but want *pe.OptionalHeader32 or *pe.OptionalHeader64", oh) } } + +func TestImportTableInUnknownSection(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skip("skipping windows only test") + } + + // first we need to find this font driver + path, err := exec.LookPath("atmfd.dll") + if err != nil { + t.Fatalf("unable to locate required file %q in search path: %s", "atmfd.dll", err) + } + + f, err := Open(path) + if err != nil { + t.Error(err) + } + defer f.Close() + + // now we can extract its imports + symbols, err := f.ImportedSymbols() + if err != nil { + t.Error(err) + } + + if len(symbols) == 0 { + t.Fatalf("unable to locate any imported symbols within file %q.", path) + } +} diff --git a/libgo/go/debug/pe/pe.go b/libgo/go/debug/pe/pe.go index 8050d59..e933ae1 100644 --- a/libgo/go/debug/pe/pe.go +++ b/libgo/go/debug/pe/pe.go @@ -91,6 +91,7 @@ const ( IMAGE_FILE_MACHINE_AM33 = 0x1d3 IMAGE_FILE_MACHINE_AMD64 = 0x8664 IMAGE_FILE_MACHINE_ARM = 0x1c0 + IMAGE_FILE_MACHINE_ARM64 = 0xaa64 IMAGE_FILE_MACHINE_EBC = 0xebc IMAGE_FILE_MACHINE_I386 = 0x14c IMAGE_FILE_MACHINE_IA64 = 0x200 @@ -108,3 +109,22 @@ const ( IMAGE_FILE_MACHINE_THUMB = 0x1c2 IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169 ) + +// IMAGE_DIRECTORY_ENTRY constants +const ( + IMAGE_DIRECTORY_ENTRY_EXPORT = 0 + IMAGE_DIRECTORY_ENTRY_IMPORT = 1 + IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 + IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 + IMAGE_DIRECTORY_ENTRY_SECURITY = 4 + IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 + IMAGE_DIRECTORY_ENTRY_DEBUG = 6 + IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 + IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 + IMAGE_DIRECTORY_ENTRY_TLS = 9 + IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 + IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 + IMAGE_DIRECTORY_ENTRY_IAT = 12 + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 + IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 +) diff --git a/libgo/go/debug/pe/string.go b/libgo/go/debug/pe/string.go index c30255f..cab0366 100644 --- a/libgo/go/debug/pe/string.go +++ b/libgo/go/debug/pe/string.go @@ -5,6 +5,7 @@ package pe import ( + "bytes" "encoding/binary" "fmt" "io" @@ -13,8 +14,9 @@ import ( // cstring converts ASCII byte sequence b to string. // It stops once it finds 0 or reaches end of b. func cstring(b []byte) string { - var i int - for i = 0; i < len(b) && b[i] != 0; i++ { + i := bytes.IndexByte(b, 0) + if i == -1 { + i = len(b) } return string(b[:i]) } diff --git a/libgo/go/encoding/asn1/asn1.go b/libgo/go/encoding/asn1/asn1.go index 26868a3..1ed357a 100644 --- a/libgo/go/encoding/asn1/asn1.go +++ b/libgo/go/encoding/asn1/asn1.go @@ -250,7 +250,7 @@ func (oi ObjectIdentifier) String() string { // parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and // returns it. An object identifier is a sequence of variable length integers // that are assigned in a hierarchy. -func parseObjectIdentifier(bytes []byte) (s []int, err error) { +func parseObjectIdentifier(bytes []byte) (s ObjectIdentifier, err error) { if len(bytes) == 0 { err = SyntaxError{"zero length OBJECT IDENTIFIER"} return @@ -793,6 +793,12 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam matchAnyClassAndTag = false } + if !params.explicit && params.private && params.tag != nil { + expectedClass = ClassPrivate + expectedTag = *params.tag + matchAnyClassAndTag = false + } + // We have unwrapped any explicit tagging at this point. if !matchAnyClassAndTag && (t.class != expectedClass || t.tag != expectedTag) || (!matchAny && t.isCompound != compoundType) { @@ -1028,6 +1034,7 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { // The following tags on struct fields have special meaning to Unmarshal: // // application specifies that an APPLICATION tag is used +// private specifies that a PRIVATE tag is used // default:x sets the default value for optional integer fields (only used if optional is also present) // explicit specifies that an additional, explicit tag wraps the implicit one // optional marks the field as ASN.1 OPTIONAL diff --git a/libgo/go/encoding/asn1/asn1_test.go b/libgo/go/encoding/asn1/asn1_test.go index 5e67dc5..f0a54e0 100644 --- a/libgo/go/encoding/asn1/asn1_test.go +++ b/libgo/go/encoding/asn1/asn1_test.go @@ -227,7 +227,7 @@ func TestBitStringRightAlign(t *testing.T) { type objectIdentifierTest struct { in []byte ok bool - out []int + out ObjectIdentifier // has base type[]int } var objectIdentifierTestData = []objectIdentifierTest{ @@ -428,11 +428,12 @@ var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParame {"optional", fieldParameters{optional: true}}, {"explicit", fieldParameters{explicit: true, tag: new(int)}}, {"application", fieldParameters{application: true, tag: new(int)}}, + {"private", fieldParameters{private: true, tag: new(int)}}, {"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}}, {"default:42", fieldParameters{defaultValue: newInt64(42)}}, {"tag:17", fieldParameters{tag: newInt(17)}}, {"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}}, - {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, false, newInt64(42), newInt(17), 0, 0, false, false}}, + {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{optional: true, explicit: true, application: false, defaultValue: newInt64(42), tag: newInt(17), stringType: 0, timeType: 0, set: false, omitEmpty: false}}, {"set", fieldParameters{set: true}}, } @@ -1079,6 +1080,7 @@ func TestTaggedRawValue(t *testing.T) { {true, []byte{0x30, 3, (ClassContextSpecific << 6) | tag, 1, 1}}, {true, []byte{0x30, 3, (ClassContextSpecific << 6) | tag | isCompound, 1, 1}}, {false, []byte{0x30, 3, (ClassApplication << 6) | tag | isCompound, 1, 1}}, + {false, []byte{0x30, 3, (ClassPrivate << 6) | tag | isCompound, 1, 1}}, } for i, test := range tests { diff --git a/libgo/go/encoding/asn1/common.go b/libgo/go/encoding/asn1/common.go index a6589a5..255d1eb 100644 --- a/libgo/go/encoding/asn1/common.go +++ b/libgo/go/encoding/asn1/common.go @@ -75,6 +75,7 @@ type fieldParameters struct { optional bool // true iff the field is OPTIONAL explicit bool // true iff an EXPLICIT tag is in use. application bool // true iff an APPLICATION tag is in use. + private bool // true iff a PRIVATE tag is in use. defaultValue *int64 // a default value for INTEGER typed fields (maybe nil). tag *int // the EXPLICIT or IMPLICIT tag (maybe nil). stringType int // the string tag to use when marshaling. @@ -130,6 +131,11 @@ func parseFieldParameters(str string) (ret fieldParameters) { if ret.tag == nil { ret.tag = new(int) } + case part == "private": + ret.private = true + if ret.tag == nil { + ret.tag = new(int) + } case part == "omitempty": ret.omitEmpty = true } diff --git a/libgo/go/encoding/asn1/marshal.go b/libgo/go/encoding/asn1/marshal.go index 3e85651..c9ae2ca 100644 --- a/libgo/go/encoding/asn1/marshal.go +++ b/libgo/go/encoding/asn1/marshal.go @@ -631,6 +631,8 @@ func makeField(v reflect.Value, params fieldParameters) (e encoder, err error) { if params.tag != nil { if params.application { class = ClassApplication + } else if params.private { + class = ClassPrivate } else { class = ClassContextSpecific } diff --git a/libgo/go/encoding/asn1/marshal_test.go b/libgo/go/encoding/asn1/marshal_test.go index 4f755a1..a77826a 100644 --- a/libgo/go/encoding/asn1/marshal_test.go +++ b/libgo/go/encoding/asn1/marshal_test.go @@ -8,6 +8,7 @@ import ( "bytes" "encoding/hex" "math/big" + "reflect" "strings" "testing" "time" @@ -80,6 +81,13 @@ type applicationTest struct { B int `asn1:"application,tag:1,explicit"` } +type privateTest struct { + A int `asn1:"private,tag:0"` + B int `asn1:"private,tag:1,explicit"` + C int `asn1:"private,tag:31"` // tag size should be 2 octet + D int `asn1:"private,tag:128"` // tag size should be 3 octet +} + type numericStringTest struct { A string `asn1:"numeric"` } @@ -169,6 +177,7 @@ var marshalTests = []marshalTest{ {defaultTest{1}, "3000"}, {defaultTest{2}, "3003020102"}, {applicationTest{1, 2}, "30084001016103020102"}, + {privateTest{1, 2, 3, 4}, "3011c00101e103020102df1f0103df81000104"}, {numericStringTest{"1 9"}, "30051203312039"}, } @@ -195,6 +204,7 @@ type marshalWithParamsTest struct { var marshalWithParamsTests = []marshalWithParamsTest{ {intStruct{10}, "set", "310302010a"}, {intStruct{10}, "application", "600302010a"}, + {intStruct{10}, "private", "e00302010a"}, } func TestMarshalWithParams(t *testing.T) { @@ -244,6 +254,62 @@ func TestInvalidUTF8(t *testing.T) { } } +func TestMarshalOID(t *testing.T) { + var marshalTestsOID = []marshalTest{ + {[]byte("\x06\x01\x30"), "0403060130"}, // bytes format returns a byte sequence \x04 + // {ObjectIdentifier([]int{0}), "060100"}, // returns an error as OID 0.0 has the same encoding + {[]byte("\x06\x010"), "0403060130"}, // same as above "\x06\x010" = "\x06\x01" + "0" + {ObjectIdentifier([]int{2, 999, 3}), "0603883703"}, // Example of ITU-T X.690 + {ObjectIdentifier([]int{0, 0}), "060100"}, // zero OID + } + for i, test := range marshalTestsOID { + data, err := Marshal(test.in) + if err != nil { + t.Errorf("#%d failed: %s", i, err) + } + out, _ := hex.DecodeString(test.out) + if !bytes.Equal(out, data) { + t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out) + } + } +} + +func TestIssue11130(t *testing.T) { + data := []byte("\x06\x010") // == \x06\x01\x30 == OID = 0 (the figure) + var v interface{} + // v has Zero value here and Elem() would panic + _, err := Unmarshal(data, &v) + if err != nil { + t.Errorf("%v", err) + return + } + if reflect.TypeOf(v).String() != reflect.TypeOf(ObjectIdentifier{}).String() { + t.Errorf("marshal OID returned an invalid type") + return + } + + data1, err := Marshal(v) + if err != nil { + t.Errorf("%v", err) + return + } + + if !bytes.Equal(data, data1) { + t.Errorf("got: %q, want: %q \n", data1, data) + return + } + + var v1 interface{} + _, err = Unmarshal(data1, &v1) + if err != nil { + t.Errorf("%v", err) + return + } + if !reflect.DeepEqual(v, v1) { + t.Errorf("got: %#v data=%q , want : %#v data=%q\n ", v1, data1, v, data) + } +} + func BenchmarkMarshal(b *testing.B) { b.ReportAllocs() diff --git a/libgo/go/encoding/base32/base32.go b/libgo/go/encoding/base32/base32.go index e72ba74..3fb6cac 100644 --- a/libgo/go/encoding/base32/base32.go +++ b/libgo/go/encoding/base32/base32.go @@ -21,7 +21,7 @@ import ( // introduced for SASL GSSAPI and standardized in RFC 4648. // The alternate "base32hex" encoding is used in DNSSEC. type Encoding struct { - encode string + encode [32]byte decodeMap [256]byte padChar rune } @@ -37,8 +37,12 @@ const encodeHex = "0123456789ABCDEFGHIJKLMNOPQRSTUV" // NewEncoding returns a new Encoding defined by the given alphabet, // which must be a 32-byte string. func NewEncoding(encoder string) *Encoding { + if len(encoder) != 32 { + panic("encoding alphabet is not 32-bytes long") + } + e := new(Encoding) - e.encode = encoder + copy(e.encode[:], encoder) e.padChar = StdPadding for i := 0; i < len(e.decodeMap); i++ { @@ -96,10 +100,6 @@ func (enc Encoding) WithPadding(padding rune) *Encoding { // so Encode is not appropriate for use on individual blocks // of a large data stream. Use NewEncoder() instead. func (enc *Encoding) Encode(dst, src []byte) { - if len(src) == 0 { - return - } - for len(src) > 0 { var b [8]byte @@ -133,17 +133,17 @@ func (enc *Encoding) Encode(dst, src []byte) { size := len(dst) if size >= 8 { // Common case, unrolled for extra performance - dst[0] = enc.encode[b[0]] - dst[1] = enc.encode[b[1]] - dst[2] = enc.encode[b[2]] - dst[3] = enc.encode[b[3]] - dst[4] = enc.encode[b[4]] - dst[5] = enc.encode[b[5]] - dst[6] = enc.encode[b[6]] - dst[7] = enc.encode[b[7]] + dst[0] = enc.encode[b[0]&31] + dst[1] = enc.encode[b[1]&31] + dst[2] = enc.encode[b[2]&31] + dst[3] = enc.encode[b[3]&31] + dst[4] = enc.encode[b[4]&31] + dst[5] = enc.encode[b[5]&31] + dst[6] = enc.encode[b[6]&31] + dst[7] = enc.encode[b[7]&31] } else { for i := 0; i < size; i++ { - dst[i] = enc.encode[b[i]] + dst[i] = enc.encode[b[i]&31] } } @@ -244,8 +244,9 @@ func (e *encoder) Close() error { // If there's anything left in the buffer, flush it out if e.err == nil && e.nbuf > 0 { e.enc.Encode(e.out[0:], e.buf[0:e.nbuf]) + encodedLen := e.enc.EncodedLen(e.nbuf) e.nbuf = 0 - _, e.err = e.w.Write(e.out[0:8]) + _, e.err = e.w.Write(e.out[0:encodedLen]) } return e.err } @@ -403,15 +404,22 @@ type decoder struct { outbuf [1024 / 8 * 5]byte } -func readEncodedData(r io.Reader, buf []byte, min int) (n int, err error) { +func readEncodedData(r io.Reader, buf []byte, min int, expectsPadding bool) (n int, err error) { for n < min && err == nil { var nn int nn, err = r.Read(buf[n:]) n += nn } + // data was read, less than min bytes could be read if n < min && n > 0 && err == io.EOF { err = io.ErrUnexpectedEOF } + // no data was read, the buffer already contains some data + // when padding is disabled this is not an error, as the message can be of + // any length + if expectsPadding && min < 8 && n == 0 && err == io.EOF { + err = io.ErrUnexpectedEOF + } return } @@ -439,15 +447,32 @@ func (d *decoder) Read(p []byte) (n int, err error) { nn = len(d.buf) } - nn, d.err = readEncodedData(d.r, d.buf[d.nbuf:nn], 8-d.nbuf) + // Minimum amount of bytes that needs to be read each cycle + var min int + var expectsPadding bool + if d.enc.padChar == NoPadding { + min = 1 + expectsPadding = false + } else { + min = 8 - d.nbuf + expectsPadding = true + } + + nn, d.err = readEncodedData(d.r, d.buf[d.nbuf:nn], min, expectsPadding) d.nbuf += nn - if d.nbuf < 8 { + if d.nbuf < min { return 0, d.err } // Decode chunk into p, or d.out and then p if p is too small. - nr := d.nbuf / 8 * 8 - nw := d.nbuf / 8 * 5 + var nr int + if d.enc.padChar == NoPadding { + nr = d.nbuf + } else { + nr = d.nbuf / 8 * 8 + } + nw := d.enc.DecodedLen(d.nbuf) + if nw > len(p) { nw, d.end, err = d.enc.decode(d.outbuf[0:], d.buf[0:nr]) d.out = d.outbuf[0:nw] diff --git a/libgo/go/encoding/base32/base32_test.go b/libgo/go/encoding/base32/base32_test.go index 56b229d..c5506ed 100644 --- a/libgo/go/encoding/base32/base32_test.go +++ b/libgo/go/encoding/base32/base32_test.go @@ -530,6 +530,86 @@ func TestDecodeWithWrongPadding(t *testing.T) { } } +func TestBufferedDecodingSameError(t *testing.T) { + testcases := []struct { + prefix string + chunkCombinations [][]string + expected error + }{ + // NBSWY3DPO5XXE3DE == helloworld + // Test with "ZZ" as extra input + {"helloworld", [][]string{ + []string{"NBSW", "Y3DP", "O5XX", "E3DE", "ZZ"}, + []string{"NBSWY3DPO5XXE3DE", "ZZ"}, + []string{"NBSWY3DPO5XXE3DEZZ"}, + []string{"NBS", "WY3", "DPO", "5XX", "E3D", "EZZ"}, + []string{"NBSWY3DPO5XXE3", "DEZZ"}, + }, io.ErrUnexpectedEOF}, + + // Test with "ZZY" as extra input + {"helloworld", [][]string{ + []string{"NBSW", "Y3DP", "O5XX", "E3DE", "ZZY"}, + []string{"NBSWY3DPO5XXE3DE", "ZZY"}, + []string{"NBSWY3DPO5XXE3DEZZY"}, + []string{"NBS", "WY3", "DPO", "5XX", "E3D", "EZZY"}, + []string{"NBSWY3DPO5XXE3", "DEZZY"}, + }, io.ErrUnexpectedEOF}, + + // Normal case, this is valid input + {"helloworld", [][]string{ + []string{"NBSW", "Y3DP", "O5XX", "E3DE"}, + []string{"NBSWY3DPO5XXE3DE"}, + []string{"NBS", "WY3", "DPO", "5XX", "E3D", "E"}, + []string{"NBSWY3DPO5XXE3", "DE"}, + }, nil}, + + // MZXW6YTB = fooba + {"fooba", [][]string{ + []string{"MZXW6YTBZZ"}, + []string{"MZXW6YTBZ", "Z"}, + []string{"MZXW6YTB", "ZZ"}, + []string{"MZXW6YT", "BZZ"}, + []string{"MZXW6Y", "TBZZ"}, + []string{"MZXW6Y", "TB", "ZZ"}, + []string{"MZXW6", "YTBZZ"}, + []string{"MZXW6", "YTB", "ZZ"}, + []string{"MZXW6", "YT", "BZZ"}, + }, io.ErrUnexpectedEOF}, + + // Normal case, this is valid input + {"fooba", [][]string{ + []string{"MZXW6YTB"}, + []string{"MZXW6YT", "B"}, + []string{"MZXW6Y", "TB"}, + []string{"MZXW6", "YTB"}, + []string{"MZXW6", "YT", "B"}, + []string{"MZXW", "6YTB"}, + []string{"MZXW", "6Y", "TB"}, + }, nil}, + } + + for _, testcase := range testcases { + for _, chunks := range testcase.chunkCombinations { + pr, pw := io.Pipe() + + // Write the encoded chunks into the pipe + go func() { + for _, chunk := range chunks { + pw.Write([]byte(chunk)) + } + pw.Close() + }() + + decoder := NewDecoder(StdEncoding, pr) + _, err := ioutil.ReadAll(decoder) + + if err != testcase.expected { + t.Errorf("Expected %v, got %v; case %s %+v", testcase.expected, err, testcase.prefix, chunks) + } + } + } +} + func TestEncodedDecodedLen(t *testing.T) { type test struct { in int @@ -578,3 +658,94 @@ func TestEncodedDecodedLen(t *testing.T) { }) } } + +func TestWithoutPaddingClose(t *testing.T) { + encodings := []*Encoding{ + StdEncoding, + StdEncoding.WithPadding(NoPadding), + } + + for _, encoding := range encodings { + for _, testpair := range pairs { + + var buf bytes.Buffer + encoder := NewEncoder(encoding, &buf) + encoder.Write([]byte(testpair.decoded)) + encoder.Close() + + expected := testpair.encoded + if encoding.padChar == NoPadding { + expected = strings.Replace(expected, "=", "", -1) + } + + res := buf.String() + + if res != expected { + t.Errorf("Expected %s got %s; padChar=%d", expected, res, encoding.padChar) + } + } + } +} + +func TestDecodeReadAll(t *testing.T) { + encodings := []*Encoding{ + StdEncoding, + StdEncoding.WithPadding(NoPadding), + } + + for _, pair := range pairs { + for encIndex, encoding := range encodings { + encoded := pair.encoded + if encoding.padChar == NoPadding { + encoded = strings.Replace(encoded, "=", "", -1) + } + + decReader, err := ioutil.ReadAll(NewDecoder(encoding, strings.NewReader(encoded))) + if err != nil { + t.Errorf("NewDecoder error: %v", err) + } + + if pair.decoded != string(decReader) { + t.Errorf("Expected %s got %s; Encoding %d", pair.decoded, decReader, encIndex) + } + } + } +} + +func TestDecodeSmallBuffer(t *testing.T) { + encodings := []*Encoding{ + StdEncoding, + StdEncoding.WithPadding(NoPadding), + } + + for bufferSize := 1; bufferSize < 200; bufferSize++ { + for _, pair := range pairs { + for encIndex, encoding := range encodings { + encoded := pair.encoded + if encoding.padChar == NoPadding { + encoded = strings.Replace(encoded, "=", "", -1) + } + + decoder := NewDecoder(encoding, strings.NewReader(encoded)) + + var allRead []byte + + for { + buf := make([]byte, bufferSize) + n, err := decoder.Read(buf) + allRead = append(allRead, buf[0:n]...) + if err == io.EOF { + break + } + if err != nil { + t.Error(err) + } + } + + if pair.decoded != string(allRead) { + t.Errorf("Expected %s got %s; Encoding %d; bufferSize %d", pair.decoded, allRead, encIndex, bufferSize) + } + } + } + } +} diff --git a/libgo/go/encoding/base64/base64_test.go b/libgo/go/encoding/base64/base64_test.go index 9f5c493..f019654 100644 --- a/libgo/go/encoding/base64/base64_test.go +++ b/libgo/go/encoding/base64/base64_test.go @@ -159,7 +159,7 @@ func TestDecode(t *testing.T) { dbuf, err = tt.enc.DecodeString(encoded) testEqual(t, "DecodeString(%q) = error %v, want %v", encoded, err, error(nil)) - testEqual(t, "DecodeString(%q) = %q, want %q", string(dbuf), p.decoded) + testEqual(t, "DecodeString(%q) = %q, want %q", encoded, string(dbuf), p.decoded) } } } diff --git a/libgo/go/encoding/binary/binary.go b/libgo/go/encoding/binary/binary.go index 2d01a3c..85b3bc2 100644 --- a/libgo/go/encoding/binary/binary.go +++ b/libgo/go/encoding/binary/binary.go @@ -419,70 +419,71 @@ func sizeof(t reflect.Type) int { } type coder struct { - order ByteOrder - buf []byte + order ByteOrder + buf []byte + offset int } type decoder coder type encoder coder func (d *decoder) bool() bool { - x := d.buf[0] - d.buf = d.buf[1:] + x := d.buf[d.offset] + d.offset++ return x != 0 } func (e *encoder) bool(x bool) { if x { - e.buf[0] = 1 + e.buf[e.offset] = 1 } else { - e.buf[0] = 0 + e.buf[e.offset] = 0 } - e.buf = e.buf[1:] + e.offset++ } func (d *decoder) uint8() uint8 { - x := d.buf[0] - d.buf = d.buf[1:] + x := d.buf[d.offset] + d.offset++ return x } func (e *encoder) uint8(x uint8) { - e.buf[0] = x - e.buf = e.buf[1:] + e.buf[e.offset] = x + e.offset++ } func (d *decoder) uint16() uint16 { - x := d.order.Uint16(d.buf[0:2]) - d.buf = d.buf[2:] + x := d.order.Uint16(d.buf[d.offset : d.offset+2]) + d.offset += 2 return x } func (e *encoder) uint16(x uint16) { - e.order.PutUint16(e.buf[0:2], x) - e.buf = e.buf[2:] + e.order.PutUint16(e.buf[e.offset:e.offset+2], x) + e.offset += 2 } func (d *decoder) uint32() uint32 { - x := d.order.Uint32(d.buf[0:4]) - d.buf = d.buf[4:] + x := d.order.Uint32(d.buf[d.offset : d.offset+4]) + d.offset += 4 return x } func (e *encoder) uint32(x uint32) { - e.order.PutUint32(e.buf[0:4], x) - e.buf = e.buf[4:] + e.order.PutUint32(e.buf[e.offset:e.offset+4], x) + e.offset += 4 } func (d *decoder) uint64() uint64 { - x := d.order.Uint64(d.buf[0:8]) - d.buf = d.buf[8:] + x := d.order.Uint64(d.buf[d.offset : d.offset+8]) + d.offset += 8 return x } func (e *encoder) uint64(x uint64) { - e.order.PutUint64(e.buf[0:8], x) - e.buf = e.buf[8:] + e.order.PutUint64(e.buf[e.offset:e.offset+8], x) + e.offset += 8 } func (d *decoder) int8() int8 { return int8(d.uint8()) } @@ -646,15 +647,16 @@ func (e *encoder) value(v reflect.Value) { } func (d *decoder) skip(v reflect.Value) { - d.buf = d.buf[dataSize(v):] + d.offset += dataSize(v) } func (e *encoder) skip(v reflect.Value) { n := dataSize(v) - for i := range e.buf[0:n] { - e.buf[i] = 0 + zero := e.buf[e.offset : e.offset+n] + for i := range zero { + zero[i] = 0 } - e.buf = e.buf[n:] + e.offset += n } // intDataSize returns the size of the data required to represent the data when encoded. @@ -663,6 +665,8 @@ func intDataSize(data interface{}) int { switch data := data.(type) { case bool, int8, uint8, *bool, *int8, *uint8: return 1 + case []bool: + return len(data) case []int8: return len(data) case []uint8: diff --git a/libgo/go/encoding/csv/reader.go b/libgo/go/encoding/csv/reader.go index 2efc7ad..a2fd4c0 100644 --- a/libgo/go/encoding/csv/reader.go +++ b/libgo/go/encoding/csv/reader.go @@ -91,7 +91,7 @@ var ( var errInvalidDelim = errors.New("csv: invalid field or comment delimiter") func validDelim(r rune) bool { - return r != 0 && r != '\r' && r != '\n' && utf8.ValidRune(r) && r != utf8.RuneError + return r != 0 && r != '"' && r != '\r' && r != '\n' && utf8.ValidRune(r) && r != utf8.RuneError } // A Reader reads records from a CSV-encoded file. diff --git a/libgo/go/encoding/csv/reader_test.go b/libgo/go/encoding/csv/reader_test.go index 1fc69f9..5121791 100644 --- a/libgo/go/encoding/csv/reader_test.go +++ b/libgo/go/encoding/csv/reader_test.go @@ -359,6 +359,10 @@ x,,, Error: errInvalidDelim, }, { Name: "BadComma3", + Comma: '"', + Error: errInvalidDelim, + }, { + Name: "BadComma4", Comma: utf8.RuneError, Error: errInvalidDelim, }, { diff --git a/libgo/go/encoding/csv/writer.go b/libgo/go/encoding/csv/writer.go index ef3594e..31c4f9c 100644 --- a/libgo/go/encoding/csv/writer.go +++ b/libgo/go/encoding/csv/writer.go @@ -57,33 +57,46 @@ func (w *Writer) Write(record []string) error { } continue } + if err := w.w.WriteByte('"'); err != nil { return err } + for len(field) > 0 { + // Search for special characters. + i := strings.IndexAny(field, "\"\r\n") + if i < 0 { + i = len(field) + } + + // Copy verbatim everything before the special character. + if _, err := w.w.WriteString(field[:i]); err != nil { + return err + } + field = field[i:] - for _, r1 := range field { - var err error - switch r1 { - case '"': - _, err = w.w.WriteString(`""`) - case '\r': - if !w.UseCRLF { - err = w.w.WriteByte('\r') + // Encode the special character. + if len(field) > 0 { + var err error + switch field[0] { + case '"': + _, err = w.w.WriteString(`""`) + case '\r': + if !w.UseCRLF { + err = w.w.WriteByte('\r') + } + case '\n': + if w.UseCRLF { + _, err = w.w.WriteString("\r\n") + } else { + err = w.w.WriteByte('\n') + } } - case '\n': - if w.UseCRLF { - _, err = w.w.WriteString("\r\n") - } else { - err = w.w.WriteByte('\n') + field = field[1:] + if err != nil { + return err } - default: - _, err = w.w.WriteRune(r1) - } - if err != nil { - return err } } - if err := w.w.WriteByte('"'); err != nil { return err } diff --git a/libgo/go/encoding/csv/writer_test.go b/libgo/go/encoding/csv/writer_test.go index 8ddca0a..011f01c 100644 --- a/libgo/go/encoding/csv/writer_test.go +++ b/libgo/go/encoding/csv/writer_test.go @@ -13,7 +13,9 @@ import ( var writeTests = []struct { Input [][]string Output string + Error error UseCRLF bool + Comma rune }{ {Input: [][]string{{"abc"}}, Output: "abc\n"}, {Input: [][]string{{"abc"}}, Output: "abc\r\n", UseCRLF: true}, @@ -39,6 +41,11 @@ var writeTests = []struct { {Input: [][]string{{"a", "a", ""}}, Output: "a,a,\n"}, {Input: [][]string{{"a", "a", "a"}}, Output: "a,a,a\n"}, {Input: [][]string{{`\.`}}, Output: "\"\\.\"\n"}, + {Input: [][]string{{"x09\x41\xb4\x1c", "aktau"}}, Output: "x09\x41\xb4\x1c,aktau\n"}, + {Input: [][]string{{",x09\x41\xb4\x1c", "aktau"}}, Output: "\",x09\x41\xb4\x1c\",aktau\n"}, + {Input: [][]string{{"a", "a", ""}}, Output: "a|a|\n", Comma: '|'}, + {Input: [][]string{{",", ",", ""}}, Output: ",|,|\n", Comma: '|'}, + {Input: [][]string{{"foo"}}, Comma: '"', Error: errInvalidDelim}, } func TestWrite(t *testing.T) { @@ -46,9 +53,12 @@ func TestWrite(t *testing.T) { b := &bytes.Buffer{} f := NewWriter(b) f.UseCRLF = tt.UseCRLF + if tt.Comma != 0 { + f.Comma = tt.Comma + } err := f.WriteAll(tt.Input) - if err != nil { - t.Errorf("Unexpected error: %s\n", err) + if err != tt.Error { + t.Errorf("Unexpected error:\ngot %v\nwant %v", err, tt.Error) } out := b.String() if out != tt.Output { diff --git a/libgo/go/encoding/gob/codec_test.go b/libgo/go/encoding/gob/codec_test.go index 8f7b6f3..520afde 100644 --- a/libgo/go/encoding/gob/codec_test.go +++ b/libgo/go/encoding/gob/codec_test.go @@ -1478,6 +1478,10 @@ func TestFuzzOneByte(t *testing.T) { switch i { case 14, 167, 231, 265: // a slice length, corruptions are not handled yet. continue + case 248: + // Large map size, which currently causes an out of memory panic. + // See golang.org/issue/24308 and golang.org/issue/20221. + continue } indices = append(indices, i) } diff --git a/libgo/go/encoding/gob/dec_helpers.go b/libgo/go/encoding/gob/dec_helpers.go index 3aa038d..26eb9e4 100644 --- a/libgo/go/encoding/gob/dec_helpers.go +++ b/libgo/go/encoding/gob/dec_helpers.go @@ -1,4 +1,4 @@ -// Created by decgen --output dec_helpers.go; DO NOT EDIT +// Code generated by go run decgen.go -output dec_helpers.go; DO NOT EDIT. // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/libgo/go/encoding/gob/decgen.go b/libgo/go/encoding/gob/decgen.go index ef73f2d..bad4fe5 100644 --- a/libgo/go/encoding/gob/decgen.go +++ b/libgo/go/encoding/gob/decgen.go @@ -171,7 +171,7 @@ func main() { log.Fatal("usage: decgen [--output filename]") } var b bytes.Buffer - fmt.Fprintf(&b, "// Created by decgen --output %s; DO NOT EDIT\n", *output) + fmt.Fprintf(&b, "// Code generated by go run decgen.go -output %s; DO NOT EDIT.\n", *output) fmt.Fprint(&b, header) printMaps(&b, "Array") fmt.Fprint(&b, "\n") diff --git a/libgo/go/encoding/gob/decode.go b/libgo/go/encoding/gob/decode.go index 2da913f..d2f6c74 100644 --- a/libgo/go/encoding/gob/decode.go +++ b/libgo/go/encoding/gob/decode.go @@ -1070,14 +1070,14 @@ func (dec *Decoder) compileSingle(remoteId typeId, ut *userTypeInfo) (engine *de } // compileIgnoreSingle compiles the decoder engine for a non-struct top-level value that will be discarded. -func (dec *Decoder) compileIgnoreSingle(remoteId typeId) (engine *decEngine, err error) { - engine = new(decEngine) +func (dec *Decoder) compileIgnoreSingle(remoteId typeId) *decEngine { + engine := new(decEngine) engine.instr = make([]decInstr, 1) // one item op := dec.decIgnoreOpFor(remoteId, make(map[typeId]*decOp)) ovfl := overflow(dec.typeString(remoteId)) engine.instr[0] = decInstr{*op, 0, nil, ovfl} engine.numInstr = 1 - return + return engine } // compileDec compiles the decoder engine for a value. If the value is not a struct, @@ -1168,7 +1168,7 @@ func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, er if wire != nil && wire.StructT != nil { *enginePtr, err = dec.compileDec(wireId, userType(emptyStructType)) } else { - *enginePtr, err = dec.compileIgnoreSingle(wireId) + *enginePtr = dec.compileIgnoreSingle(wireId) } if err != nil { delete(dec.ignorerCache, wireId) diff --git a/libgo/go/encoding/gob/enc_helpers.go b/libgo/go/encoding/gob/enc_helpers.go index 804e539..c3b4ca8 100644 --- a/libgo/go/encoding/gob/enc_helpers.go +++ b/libgo/go/encoding/gob/enc_helpers.go @@ -1,4 +1,4 @@ -// Created by encgen --output enc_helpers.go; DO NOT EDIT +// Code generated by go run encgen.go -output enc_helpers.go; DO NOT EDIT. // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/libgo/go/encoding/gob/encgen.go b/libgo/go/encoding/gob/encgen.go index efdd928..0c051d2 100644 --- a/libgo/go/encoding/gob/encgen.go +++ b/libgo/go/encoding/gob/encgen.go @@ -150,7 +150,7 @@ func main() { log.Fatal("usage: encgen [--output filename]") } var b bytes.Buffer - fmt.Fprintf(&b, "// Created by encgen --output %s; DO NOT EDIT\n", *output) + fmt.Fprintf(&b, "// Code generated by go run encgen.go -output %s; DO NOT EDIT.\n", *output) fmt.Fprint(&b, header) printMaps(&b, "Array") fmt.Fprint(&b, "\n") diff --git a/libgo/go/encoding/gob/encoder_test.go b/libgo/go/encoding/gob/encoder_test.go index a1ca252..dc9bbcf 100644 --- a/libgo/go/encoding/gob/encoder_test.go +++ b/libgo/go/encoding/gob/encoder_test.go @@ -10,6 +10,7 @@ import ( "fmt" "io/ioutil" "reflect" + "runtime" "strings" "testing" ) @@ -1014,7 +1015,7 @@ type Bug4Secret struct { } // Test that a failed compilation doesn't leave around an executable encoder. -// Issue 3273. +// Issue 3723. func TestMutipleEncodingsOfBadType(t *testing.T) { x := Bug4Public{ Name: "name", @@ -1130,6 +1131,9 @@ func TestBadData(t *testing.T) { // TestHugeWriteFails tests that enormous messages trigger an error. func TestHugeWriteFails(t *testing.T) { + if runtime.GOARCH == "wasm" { + t.Skip("out of memory on wasm") + } if testing.Short() { // Requires allocating a monster, so don't do this from all.bash. t.Skip("skipping huge allocation in short mode") diff --git a/libgo/go/encoding/hex/hex.go b/libgo/go/encoding/hex/hex.go index e4df6cb..aee5aec 100644 --- a/libgo/go/encoding/hex/hex.go +++ b/libgo/go/encoding/hex/hex.go @@ -50,8 +50,8 @@ func DecodedLen(x int) int { return x / 2 } // Decode decodes src into DecodedLen(len(src)) bytes, // returning the actual number of bytes written to dst. // -// Decode expects that src contain only hexadecimal -// characters and that src should have an even length. +// Decode expects that src contains only hexadecimal +// characters and that src has even length. // If the input is malformed, Decode returns the number // of bytes decoded before the error. func Decode(dst, src []byte) (int, error) { @@ -101,10 +101,10 @@ func EncodeToString(src []byte) string { // DecodeString returns the bytes represented by the hexadecimal string s. // -// DecodeString expects that src contain only hexadecimal -// characters and that src should have an even length. -// If the input is malformed, DecodeString returns a string -// containing the bytes decoded before the error. +// DecodeString expects that src contains only hexadecimal +// characters and that src has even length. +// If the input is malformed, DecodeString returns +// the bytes decoded before the error. func DecodeString(s string) ([]byte, error) { src := []byte(s) // We can use the source slice itself as the destination @@ -211,6 +211,7 @@ type dumper struct { buf [14]byte used int // number of bytes in the current line n uint // number of bytes, total + closed bool } func toChar(b byte) byte { @@ -221,6 +222,10 @@ func toChar(b byte) byte { } func (h *dumper) Write(data []byte) (n int, err error) { + if h.closed { + return 0, errors.New("encoding/hex: dumper closed") + } + // Output lines look like: // 00000010 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d |./0123456789:;<=| // ^ offset ^ extra space ^ ASCII of line. @@ -277,6 +282,10 @@ func (h *dumper) Write(data []byte) (n int, err error) { func (h *dumper) Close() (err error) { // See the comments in Write() for the details of this format. + if h.closed { + return + } + h.closed = true if h.used == 0 { return } diff --git a/libgo/go/encoding/hex/hex_test.go b/libgo/go/encoding/hex/hex_test.go index b6bab21..6ba054e 100644 --- a/libgo/go/encoding/hex/hex_test.go +++ b/libgo/go/encoding/hex/hex_test.go @@ -188,6 +188,35 @@ func TestDumper(t *testing.T) { } } +func TestDumper_doubleclose(t *testing.T) { + var out bytes.Buffer + dumper := Dumper(&out) + + dumper.Write([]byte(`gopher`)) + dumper.Close() + dumper.Close() + dumper.Write([]byte(`gopher`)) + dumper.Close() + + expected := "00000000 67 6f 70 68 65 72 |gopher|\n" + if out.String() != expected { + t.Fatalf("got:\n%#v\nwant:\n%#v", out.String(), expected) + } +} + +func TestDumper_earlyclose(t *testing.T) { + var out bytes.Buffer + dumper := Dumper(&out) + + dumper.Close() + dumper.Write([]byte(`gopher`)) + + expected := "" + if out.String() != expected { + t.Fatalf("got:\n%#v\nwant:\n%#v", out.String(), expected) + } +} + func TestDump(t *testing.T) { var in [40]byte for i := range in { diff --git a/libgo/go/encoding/json/bench_test.go b/libgo/go/encoding/json/bench_test.go index 42439eb7..bd322db 100644 --- a/libgo/go/encoding/json/bench_test.go +++ b/libgo/go/encoding/json/bench_test.go @@ -13,9 +13,14 @@ package json import ( "bytes" "compress/gzip" + "fmt" + "internal/testenv" "io/ioutil" "os" + "reflect" + "runtime" "strings" + "sync" "testing" ) @@ -265,3 +270,66 @@ func BenchmarkUnmapped(b *testing.B) { } }) } + +func BenchmarkTypeFieldsCache(b *testing.B) { + var maxTypes int = 1e6 + if testenv.Builder() != "" { + maxTypes = 1e3 // restrict cache sizes on builders + } + + // Dynamically generate many new types. + types := make([]reflect.Type, maxTypes) + fs := []reflect.StructField{{ + Type: reflect.TypeOf(""), + Index: []int{0}, + }} + for i := range types { + fs[0].Name = fmt.Sprintf("TypeFieldsCache%d", i) + types[i] = reflect.StructOf(fs) + } + + // clearClear clears the cache. Other JSON operations, must not be running. + clearCache := func() { + fieldCache = sync.Map{} + } + + // MissTypes tests the performance of repeated cache misses. + // This measures the time to rebuild a cache of size nt. + for nt := 1; nt <= maxTypes; nt *= 10 { + ts := types[:nt] + b.Run(fmt.Sprintf("MissTypes%d", nt), func(b *testing.B) { + nc := runtime.GOMAXPROCS(0) + for i := 0; i < b.N; i++ { + clearCache() + var wg sync.WaitGroup + for j := 0; j < nc; j++ { + wg.Add(1) + go func(j int) { + for _, t := range ts[(j*len(ts))/nc : ((j+1)*len(ts))/nc] { + cachedTypeFields(t) + } + wg.Done() + }(j) + } + wg.Wait() + } + }) + } + + // HitTypes tests the performance of repeated cache hits. + // This measures the average time of each cache lookup. + for nt := 1; nt <= maxTypes; nt *= 10 { + // Pre-warm a cache of size nt. + clearCache() + for _, t := range types[:nt] { + cachedTypeFields(t) + } + b.Run(fmt.Sprintf("HitTypes%d", nt), func(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + cachedTypeFields(types[0]) + } + }) + }) + } +} diff --git a/libgo/go/encoding/json/decode.go b/libgo/go/encoding/json/decode.go index 730fb92..0b29249 100644 --- a/libgo/go/encoding/json/decode.go +++ b/libgo/go/encoding/json/decode.go @@ -14,7 +14,6 @@ import ( "errors" "fmt" "reflect" - "runtime" "strconv" "unicode" "unicode/utf16" @@ -168,25 +167,20 @@ func (e *InvalidUnmarshalError) Error() string { return "json: Unmarshal(nil " + e.Type.String() + ")" } -func (d *decodeState) unmarshal(v interface{}) (err error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - err = r.(error) - } - }() - +func (d *decodeState) unmarshal(v interface{}) error { rv := reflect.ValueOf(v) if rv.Kind() != reflect.Ptr || rv.IsNil() { return &InvalidUnmarshalError{reflect.TypeOf(v)} } d.scan.reset() + d.scanWhile(scanSkipSpace) // We decode rv not rv.Elem because the Unmarshaler interface // test must be applied at the top level of the value. - d.value(rv) + err := d.value(rv) + if err != nil { + return err + } return d.savedError } @@ -210,7 +204,7 @@ func (n Number) Int64() (int64, error) { func isValidNumber(s string) bool { // This function implements the JSON numbers grammar. // See https://tools.ietf.org/html/rfc7159#section-6 - // and http://json.org/number.gif + // and https://json.org/number.gif if s == "" { return false @@ -269,9 +263,9 @@ func isValidNumber(s string) bool { // decodeState represents the state while decoding a JSON value. type decodeState struct { data []byte - off int // read offset in data + off int // next read offset in data + opcode int // last read result scan scanner - nextscan scanner // for calls to nextValue errorContext struct { // provides context for type errors Struct string Field string @@ -281,6 +275,11 @@ type decodeState struct { disallowUnknownFields bool } +// readIndex returns the position of the last byte read. +func (d *decodeState) readIndex() int { + return d.off - 1 +} + // errPhase is used for errors that should not happen unless // there is a bug in the JSON decoder or something is editing // the data slice while the decoder executes. @@ -295,11 +294,6 @@ func (d *decodeState) init(data []byte) *decodeState { return d } -// error aborts the decoding by panicking with err. -func (d *decodeState) error(err error) { - panic(d.addErrorContext(err)) -} - // saveError saves the first err it is called with, // for reporting at the end of the unmarshal. func (d *decodeState) saveError(err error) { @@ -321,95 +315,91 @@ func (d *decodeState) addErrorContext(err error) error { return err } -// next cuts off and returns the next full JSON value in d.data[d.off:]. -// The next value is known to be an object or array, not a literal. -func (d *decodeState) next() []byte { - c := d.data[d.off] - item, rest, err := nextValue(d.data[d.off:], &d.nextscan) - if err != nil { - d.error(err) +// skip scans to the end of what was started. +func (d *decodeState) skip() { + s, data, i := &d.scan, d.data, d.off + depth := len(s.parseState) + for { + op := s.step(s, data[i]) + i++ + if len(s.parseState) < depth { + d.off = i + d.opcode = op + return + } } - d.off = len(d.data) - len(rest) +} - // Our scanner has seen the opening brace/bracket - // and thinks we're still in the middle of the object. - // invent a closing brace/bracket to get it out. - if c == '{' { - d.scan.step(&d.scan, '}') +// scanNext processes the byte at d.data[d.off]. +func (d *decodeState) scanNext() { + s, data, i := &d.scan, d.data, d.off + if i < len(data) { + d.opcode = s.step(s, data[i]) + d.off = i + 1 } else { - d.scan.step(&d.scan, ']') + d.opcode = s.eof() + d.off = len(data) + 1 // mark processed EOF with len+1 } - - return item } // scanWhile processes bytes in d.data[d.off:] until it // receives a scan code not equal to op. -// It updates d.off and returns the new scan code. -func (d *decodeState) scanWhile(op int) int { - var newOp int - for { - if d.off >= len(d.data) { - newOp = d.scan.eof() - d.off = len(d.data) + 1 // mark processed EOF with len+1 - } else { - c := d.data[d.off] - d.off++ - newOp = d.scan.step(&d.scan, c) - } +func (d *decodeState) scanWhile(op int) { + s, data, i := &d.scan, d.data, d.off + for i < len(d.data) { + newOp := s.step(s, data[i]) + i++ if newOp != op { - break + d.opcode = newOp + d.off = i + return } } - return newOp -} - -// value decodes a JSON value from d.data[d.off:] into the value. -// it updates d.off to point past the decoded value. -func (d *decodeState) value(v reflect.Value) { - if !v.IsValid() { - _, rest, err := nextValue(d.data[d.off:], &d.nextscan) - if err != nil { - d.error(err) - } - d.off = len(d.data) - len(rest) - // 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.redo { - // rewind. - d.scan.redo = false - d.scan.step = stateBeginValue - } - d.scan.step(&d.scan, '"') - d.scan.step(&d.scan, '"') - - n := len(d.scan.parseState) - if n > 0 && d.scan.parseState[n-1] == parseObjectKey { - // d.scan thinks we just read an object key; finish the object - d.scan.step(&d.scan, ':') - d.scan.step(&d.scan, '"') - d.scan.step(&d.scan, '"') - d.scan.step(&d.scan, '}') - } - - return - } + d.off = len(d.data) + 1 // mark processed EOF with len+1 + d.opcode = d.scan.eof() +} - switch op := d.scanWhile(scanSkipSpace); op { +// value consumes a JSON value from d.data[d.off-1:], decoding into v, and +// reads the following byte ahead. If v is invalid, the value is discarded. +// The first byte of the value has been read already. +func (d *decodeState) value(v reflect.Value) error { + switch d.opcode { default: - d.error(errPhase) + return errPhase case scanBeginArray: - d.array(v) + if v.IsValid() { + if err := d.array(v); err != nil { + return err + } + } else { + d.skip() + } + d.scanNext() case scanBeginObject: - d.object(v) + if v.IsValid() { + if err := d.object(v); err != nil { + return err + } + } else { + d.skip() + } + d.scanNext() case scanBeginLiteral: - d.literal(v) + // All bytes inside literal return scanContinue op code. + start := d.readIndex() + d.scanWhile(scanContinue) + + if v.IsValid() { + if err := d.literalStore(d.data[start:d.readIndex()], v, false); err != nil { + return err + } + } } + return nil } type unquotedValue struct{} @@ -418,31 +408,37 @@ type unquotedValue struct{} // quoted string literal or literal null into an interface value. // If it finds anything other than a quoted string literal or null, // valueQuoted returns unquotedValue{}. -func (d *decodeState) valueQuoted() interface{} { - switch op := d.scanWhile(scanSkipSpace); op { +func (d *decodeState) valueQuoted() (interface{}, error) { + switch d.opcode { default: - d.error(errPhase) + return nil, errPhase case scanBeginArray: - d.array(reflect.Value{}) + d.skip() + d.scanNext() case scanBeginObject: - d.object(reflect.Value{}) + d.skip() + d.scanNext() case scanBeginLiteral: - switch v := d.literalInterface().(type) { + v, err := d.literalInterface() + if err != nil { + return nil, err + } + switch v.(type) { case nil, string: - return v + return v, nil } } - return unquotedValue{} + return unquotedValue{}, nil } // indirect walks down v allocating pointers as needed, // until it gets to a non-pointer. // if it encounters an Unmarshaler, indirect stops and returns that. // if decodingNull is true, indirect stops at the last pointer so it can be set to nil. -func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) { +func indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) { // Issue #24153 indicates that it is generally not a guaranteed property // that you may round-trip a reflect.Value by calling Value.Addr().Elem() // and expect the value to still be settable for values derived from @@ -507,26 +503,21 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, return nil, nil, v } -// array consumes an array from d.data[d.off-1:], decoding into the value v. -// the first byte of the array ('[') has been read already. -func (d *decodeState) array(v reflect.Value) { +// array consumes an array from d.data[d.off-1:], decoding into v. +// The first byte of the array ('[') has been read already. +func (d *decodeState) array(v reflect.Value) error { // Check for unmarshaler. - u, ut, pv := d.indirect(v, false) + u, ut, pv := indirect(v, false) if u != nil { - d.off-- - err := u.UnmarshalJSON(d.next()) - if err != nil { - d.error(err) - } - return + start := d.readIndex() + d.skip() + return u.UnmarshalJSON(d.data[start:d.off]) } if ut != nil { d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)}) - d.off-- - d.next() - return + d.skip() + return nil } - v = pv // Check type of target. @@ -534,16 +525,19 @@ func (d *decodeState) array(v reflect.Value) { case reflect.Interface: if v.NumMethod() == 0 { // Decoding into nil interface? Switch to non-reflect code. - v.Set(reflect.ValueOf(d.arrayInterface())) - return + ai, err := d.arrayInterface() + if err != nil { + return err + } + v.Set(reflect.ValueOf(ai)) + return nil } // Otherwise it's invalid. fallthrough default: d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)}) - d.off-- - d.next() - return + d.skip() + return nil case reflect.Array: case reflect.Slice: break @@ -552,15 +546,11 @@ func (d *decodeState) array(v reflect.Value) { i := 0 for { // Look ahead for ] - can only happen on first iteration. - op := d.scanWhile(scanSkipSpace) - if op == scanEndArray { + d.scanWhile(scanSkipSpace) + if d.opcode == scanEndArray { break } - // Back up so d.value can have the byte we just read. - d.off-- - d.scan.undo(op) - // Get element of array, growing if necessary. if v.Kind() == reflect.Slice { // Grow slice if necessary @@ -580,20 +570,26 @@ func (d *decodeState) array(v reflect.Value) { if i < v.Len() { // Decode into element. - d.value(v.Index(i)) + if err := d.value(v.Index(i)); err != nil { + return err + } } else { // Ran out of fixed array: skip. - d.value(reflect.Value{}) + if err := d.value(reflect.Value{}); err != nil { + return err + } } i++ // Next token must be , or ]. - op = d.scanWhile(scanSkipSpace) - if op == scanEndArray { + if d.opcode == scanSkipSpace { + d.scanWhile(scanSkipSpace) + } + if d.opcode == scanEndArray { break } - if op != scanArrayValue { - d.error(errPhase) + if d.opcode != scanArrayValue { + return errPhase } } @@ -611,36 +607,37 @@ func (d *decodeState) array(v reflect.Value) { if i == 0 && v.Kind() == reflect.Slice { v.Set(reflect.MakeSlice(v.Type(), 0, 0)) } + return nil } var nullLiteral = []byte("null") var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem() -// object consumes an object from d.data[d.off-1:], decoding into the value v. -// the first byte ('{') of the object has been read already. -func (d *decodeState) object(v reflect.Value) { +// object consumes an object from d.data[d.off-1:], decoding into v. +// The first byte ('{') of the object has been read already. +func (d *decodeState) object(v reflect.Value) error { // Check for unmarshaler. - u, ut, pv := d.indirect(v, false) + u, ut, pv := indirect(v, false) if u != nil { - d.off-- - err := u.UnmarshalJSON(d.next()) - if err != nil { - d.error(err) - } - return + start := d.readIndex() + d.skip() + return u.UnmarshalJSON(d.data[start:d.off]) } if ut != nil { d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)}) - d.off-- - d.next() // skip over { } in input - return + d.skip() + return nil } v = pv // Decoding into nil interface? Switch to non-reflect code. if v.Kind() == reflect.Interface && v.NumMethod() == 0 { - v.Set(reflect.ValueOf(d.objectInterface())) - return + oi, err := d.objectInterface() + if err != nil { + return err + } + v.Set(reflect.ValueOf(oi)) + return nil } // Check type of target: @@ -659,9 +656,8 @@ func (d *decodeState) object(v reflect.Value) { default: if !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) { d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)}) - d.off-- - d.next() // skip over { } in input - return + d.skip() + return nil } } if v.IsNil() { @@ -671,31 +667,30 @@ func (d *decodeState) object(v reflect.Value) { // ok default: d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)}) - d.off-- - d.next() // skip over { } in input - return + d.skip() + return nil } var mapElem reflect.Value for { // Read opening " of string key or closing }. - op := d.scanWhile(scanSkipSpace) - if op == scanEndObject { + d.scanWhile(scanSkipSpace) + if d.opcode == scanEndObject { // closing } - can only happen on first iteration. break } - if op != scanBeginLiteral { - d.error(errPhase) + if d.opcode != scanBeginLiteral { + return errPhase } // Read key. - start := d.off - 1 - op = d.scanWhile(scanContinue) - item := d.data[start : d.off-1] + start := d.readIndex() + d.scanWhile(scanContinue) + item := d.data[start:d.readIndex()] key, ok := unquoteBytes(item) if !ok { - d.error(errPhase) + return errPhase } // Figure out field corresponding to key. @@ -756,24 +751,35 @@ func (d *decodeState) object(v reflect.Value) { } // Read : before value. - if op == scanSkipSpace { - op = d.scanWhile(scanSkipSpace) + if d.opcode == scanSkipSpace { + d.scanWhile(scanSkipSpace) } - if op != scanObjectKey { - d.error(errPhase) + if d.opcode != scanObjectKey { + return errPhase } + d.scanWhile(scanSkipSpace) if destring { - switch qv := d.valueQuoted().(type) { + q, err := d.valueQuoted() + if err != nil { + return err + } + switch qv := q.(type) { case nil: - d.literalStore(nullLiteral, subv, false) + if err := d.literalStore(nullLiteral, subv, false); err != nil { + return err + } case string: - d.literalStore([]byte(qv), subv, true) + if err := d.literalStore([]byte(qv), subv, true); err != nil { + return err + } default: d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into %v", subv.Type())) } } else { - d.value(subv) + if err := d.value(subv); err != nil { + return err + } } // Write value back to map; @@ -786,7 +792,9 @@ func (d *decodeState) object(v reflect.Value) { kv = reflect.ValueOf(key).Convert(kt) case reflect.PtrTo(kt).Implements(textUnmarshalerType): kv = reflect.New(v.Type().Key()) - d.literalStore(item, kv, true) + if err := d.literalStore(item, kv, true); err != nil { + return err + } kv = kv.Elem() default: switch kt.Kind() { @@ -795,7 +803,7 @@ func (d *decodeState) object(v reflect.Value) { n, err := strconv.ParseInt(s, 10, 64) if err != nil || reflect.Zero(kt).OverflowInt(n) { d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: kt, Offset: int64(start + 1)}) - return + return nil } kv = reflect.ValueOf(n).Convert(kt) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: @@ -803,7 +811,7 @@ func (d *decodeState) object(v reflect.Value) { n, err := strconv.ParseUint(s, 10, 64) if err != nil || reflect.Zero(kt).OverflowUint(n) { d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: kt, Offset: int64(start + 1)}) - return + return nil } kv = reflect.ValueOf(n).Convert(kt) default: @@ -814,32 +822,20 @@ func (d *decodeState) object(v reflect.Value) { } // Next token must be , or }. - op = d.scanWhile(scanSkipSpace) - if op == scanEndObject { + if d.opcode == scanSkipSpace { + d.scanWhile(scanSkipSpace) + } + if d.opcode == scanEndObject { break } - if op != scanObjectValue { - d.error(errPhase) + if d.opcode != scanObjectValue { + return errPhase } d.errorContext.Struct = "" d.errorContext.Field = "" } -} - -// literal consumes a literal from d.data[d.off-1:], decoding into the value v. -// The first byte of the literal has been read already -// (that's how the caller knows it's a literal). -func (d *decodeState) literal(v reflect.Value) { - // All bytes inside literal return scanContinue op code. - start := d.off - 1 - op := d.scanWhile(scanContinue) - - // Scan read one byte too far; back up. - d.off-- - d.scan.undo(op) - - d.literalStore(d.data[start:d.off], v, false) + return nil } // convertNumber converts the number literal s to a float64 or a Number @@ -862,21 +858,17 @@ var numberType = reflect.TypeOf(Number("")) // fromQuoted indicates whether this literal came from unwrapping a // string from the ",string" struct tag option. this is used only to // produce more helpful error messages. -func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) { +func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) error { // Check for unmarshaler. if len(item) == 0 { //Empty string given d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - return + return nil } isNull := item[0] == 'n' // null - u, ut, pv := d.indirect(v, isNull) + u, ut, pv := indirect(v, isNull) if u != nil { - err := u.UnmarshalJSON(item) - if err != nil { - d.error(err) - } - return + return u.UnmarshalJSON(item) } if ut != nil { if item[0] != '"' { @@ -892,23 +884,18 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool default: val = "number" } - d.saveError(&UnmarshalTypeError{Value: val, Type: v.Type(), Offset: int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: val, Type: v.Type(), Offset: int64(d.readIndex())}) } - return + return nil } s, ok := unquoteBytes(item) if !ok { if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(errPhase) + return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()) } + return errPhase } - err := ut.UnmarshalText(s) - if err != nil { - d.error(err) - } - return + return ut.UnmarshalText(s) } v = pv @@ -939,7 +926,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool if fromQuoted { d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) } else { - d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.readIndex())}) } case reflect.Bool: v.SetBool(value) @@ -947,7 +934,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool if v.NumMethod() == 0 { v.Set(reflect.ValueOf(value)) } else { - d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.readIndex())}) } } @@ -955,17 +942,16 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool s, ok := unquoteBytes(item) if !ok { if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(errPhase) + return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()) } + return errPhase } switch v.Kind() { default: - d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.readIndex())}) case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { - d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.readIndex())}) break } b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) @@ -981,17 +967,16 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool if v.NumMethod() == 0 { v.Set(reflect.ValueOf(string(s))) } else { - d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.readIndex())}) } } default: // number if c != '-' && (c < '0' || c > '9') { if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(errPhase) + return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()) } + return errPhase } s := string(item) switch v.Kind() { @@ -999,15 +984,14 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool if v.Kind() == reflect.String && v.Type() == numberType { v.SetString(s) if !isValidNumber(s) { - d.error(fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", item)) + return fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", item) } break } if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off)}) + return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()) } + return &UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.readIndex())} case reflect.Interface: n, err := d.convertNumber(s) if err != nil { @@ -1015,7 +999,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool break } if v.NumMethod() != 0 { - d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.readIndex())}) break } v.Set(reflect.ValueOf(n)) @@ -1023,7 +1007,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: n, err := strconv.ParseInt(s, 10, 64) if err != nil || v.OverflowInt(n) { - d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.readIndex())}) break } v.SetInt(n) @@ -1031,7 +1015,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: n, err := strconv.ParseUint(s, 10, 64) if err != nil || v.OverflowUint(n) { - d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.readIndex())}) break } v.SetUint(n) @@ -1039,12 +1023,13 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool case reflect.Float32, reflect.Float64: n, err := strconv.ParseFloat(s, v.Type().Bits()) if err != nil || v.OverflowFloat(n) { - d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.readIndex())}) break } v.SetFloat(n) } } + return nil } // The xxxInterface routines build up a value to be stored @@ -1052,128 +1037,138 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool // but they avoid the weight of reflection in this common case. // valueInterface is like value but returns interface{} -func (d *decodeState) valueInterface() interface{} { - switch d.scanWhile(scanSkipSpace) { +func (d *decodeState) valueInterface() (val interface{}, err error) { + switch d.opcode { default: - d.error(errPhase) - panic("unreachable") + err = errPhase case scanBeginArray: - return d.arrayInterface() + val, err = d.arrayInterface() + d.scanNext() case scanBeginObject: - return d.objectInterface() + val, err = d.objectInterface() + d.scanNext() case scanBeginLiteral: - return d.literalInterface() + val, err = d.literalInterface() } + return } // arrayInterface is like array but returns []interface{}. -func (d *decodeState) arrayInterface() []interface{} { +func (d *decodeState) arrayInterface() ([]interface{}, error) { var v = make([]interface{}, 0) for { // Look ahead for ] - can only happen on first iteration. - op := d.scanWhile(scanSkipSpace) - if op == scanEndArray { + d.scanWhile(scanSkipSpace) + if d.opcode == scanEndArray { break } - // Back up so d.value can have the byte we just read. - d.off-- - d.scan.undo(op) - - v = append(v, d.valueInterface()) + vi, err := d.valueInterface() + if err != nil { + return nil, err + } + v = append(v, vi) // Next token must be , or ]. - op = d.scanWhile(scanSkipSpace) - if op == scanEndArray { + if d.opcode == scanSkipSpace { + d.scanWhile(scanSkipSpace) + } + if d.opcode == scanEndArray { break } - if op != scanArrayValue { - d.error(errPhase) + if d.opcode != scanArrayValue { + return nil, errPhase } } - return v + return v, nil } // objectInterface is like object but returns map[string]interface{}. -func (d *decodeState) objectInterface() map[string]interface{} { +func (d *decodeState) objectInterface() (map[string]interface{}, error) { m := make(map[string]interface{}) for { // Read opening " of string key or closing }. - op := d.scanWhile(scanSkipSpace) - if op == scanEndObject { + d.scanWhile(scanSkipSpace) + if d.opcode == scanEndObject { // closing } - can only happen on first iteration. break } - if op != scanBeginLiteral { - d.error(errPhase) + if d.opcode != scanBeginLiteral { + return nil, errPhase } // Read string key. - start := d.off - 1 - op = d.scanWhile(scanContinue) - item := d.data[start : d.off-1] + start := d.readIndex() + d.scanWhile(scanContinue) + item := d.data[start:d.readIndex()] key, ok := unquote(item) if !ok { - d.error(errPhase) + return nil, errPhase } // Read : before value. - if op == scanSkipSpace { - op = d.scanWhile(scanSkipSpace) + if d.opcode == scanSkipSpace { + d.scanWhile(scanSkipSpace) } - if op != scanObjectKey { - d.error(errPhase) + if d.opcode != scanObjectKey { + return nil, errPhase } + d.scanWhile(scanSkipSpace) // Read value. - m[key] = d.valueInterface() + vi, err := d.valueInterface() + if err != nil { + return nil, err + } + m[key] = vi // Next token must be , or }. - op = d.scanWhile(scanSkipSpace) - if op == scanEndObject { + if d.opcode == scanSkipSpace { + d.scanWhile(scanSkipSpace) + } + if d.opcode == scanEndObject { break } - if op != scanObjectValue { - d.error(errPhase) + if d.opcode != scanObjectValue { + return nil, errPhase } } - return m + return m, nil } -// literalInterface is like literal but returns an interface value. -func (d *decodeState) literalInterface() interface{} { +// literalInterface consumes and returns a literal from d.data[d.off-1:] and +// it reads the following byte ahead. The first byte of the literal has been +// read already (that's how the caller knows it's a literal). +func (d *decodeState) literalInterface() (interface{}, error) { // All bytes inside literal return scanContinue op code. - start := d.off - 1 - op := d.scanWhile(scanContinue) + start := d.readIndex() + d.scanWhile(scanContinue) - // Scan read one byte too far; back up. - d.off-- - d.scan.undo(op) - item := d.data[start:d.off] + item := d.data[start:d.readIndex()] switch c := item[0]; c { case 'n': // null - return nil + return nil, nil case 't', 'f': // true, false - return c == 't' + return c == 't', nil case '"': // string s, ok := unquote(item) if !ok { - d.error(errPhase) + return nil, errPhase } - return s + return s, nil default: // number if c != '-' && (c < '0' || c > '9') { - d.error(errPhase) + return nil, errPhase } n, err := d.convertNumber(string(item)) if err != nil { d.saveError(err) } - return n + return n, nil } } diff --git a/libgo/go/encoding/json/decode_test.go b/libgo/go/encoding/json/decode_test.go index fa1531f..ab83b81 100644 --- a/libgo/go/encoding/json/decode_test.go +++ b/libgo/go/encoding/json/decode_test.go @@ -2208,3 +2208,17 @@ func TestUnmarshalEmbeddedUnexported(t *testing.T) { } } } + +type unmarshalPanic struct{} + +func (unmarshalPanic) UnmarshalJSON([]byte) error { panic(0xdead) } + +func TestUnmarshalPanic(t *testing.T) { + defer func() { + if got := recover(); !reflect.DeepEqual(got, 0xdead) { + t.Errorf("panic() = (%T)(%v), want 0xdead", got, got) + } + }() + Unmarshal([]byte("{}"), &unmarshalPanic{}) + t.Fatalf("Unmarshal should have panicked") +} diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go index 1e45e44..28ca5fe 100644 --- a/libgo/go/encoding/json/encode.go +++ b/libgo/go/encoding/json/encode.go @@ -17,12 +17,10 @@ import ( "fmt" "math" "reflect" - "runtime" "sort" "strconv" "strings" "sync" - "sync/atomic" "unicode" "unicode/utf8" ) @@ -157,12 +155,18 @@ import ( // an infinite recursion. // func Marshal(v interface{}) ([]byte, error) { - e := &encodeState{} + e := newEncodeState() + err := e.marshal(v, encOpts{escapeHTML: true}) if err != nil { return nil, err } - return e.Bytes(), nil + buf := append([]byte(nil), e.Bytes()...) + + e.Reset() + encodeStatePool.Put(e) + + return buf, nil } // MarshalIndent is like Marshal but applies Indent to format the output. @@ -283,24 +287,28 @@ func newEncodeState() *encodeState { return new(encodeState) } +// jsonError is an error wrapper type for internal use only. +// Panics with errors are wrapped in jsonError so that the top-level recover +// can distinguish intentional panics from this package. +type jsonError struct{ error } + func (e *encodeState) marshal(v interface{}, opts encOpts) (err error) { defer func() { if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { + if je, ok := r.(jsonError); ok { + err = je.error + } else { panic(r) } - if s, ok := r.(string); ok { - panic(s) - } - err = r.(error) } }() e.reflectValue(reflect.ValueOf(v), opts) return nil } +// error aborts the encoding by panicking with err wrapped in jsonError. func (e *encodeState) error(err error) { - panic(err) + panic(jsonError{err}) } func isEmptyValue(v reflect.Value) bool { @@ -1229,65 +1237,22 @@ func typeFields(t reflect.Type) []field { // will be false: This condition is an error in Go and we skip all // the fields. func dominantField(fields []field) (field, bool) { - // The fields are sorted in increasing index-length order. The winner - // must therefore be one with the shortest index length. Drop all - // longer entries, which is easy: just truncate the slice. - length := len(fields[0].index) - tagged := -1 // Index of first tagged field. - for i, f := range fields { - if len(f.index) > length { - fields = fields[:i] - break - } - if f.tag { - if tagged >= 0 { - // Multiple tagged fields at the same level: conflict. - // Return no field. - return field{}, false - } - tagged = i - } - } - if tagged >= 0 { - return fields[tagged], true - } - // All remaining fields have the same length. If there's more than one, - // we have a conflict (two fields named "X" at the same level) and we - // return no field. - if len(fields) > 1 { + // The fields are sorted in increasing index-length order, then by presence of tag. + // That means that the first field is the dominant one. We need only check + // for error cases: two fields at top level, either both tagged or neither tagged. + if len(fields) > 1 && len(fields[0].index) == len(fields[1].index) && fields[0].tag == fields[1].tag { return field{}, false } return fields[0], true } -var fieldCache struct { - value atomic.Value // map[reflect.Type][]field - mu sync.Mutex // used only by writers -} +var fieldCache sync.Map // map[reflect.Type][]field // cachedTypeFields is like typeFields but uses a cache to avoid repeated work. func cachedTypeFields(t reflect.Type) []field { - m, _ := fieldCache.value.Load().(map[reflect.Type][]field) - f := m[t] - if f != nil { - return f - } - - // Compute fields without lock. - // Might duplicate effort but won't hold other computations back. - f = typeFields(t) - if f == nil { - f = []field{} - } - - fieldCache.mu.Lock() - m, _ = fieldCache.value.Load().(map[reflect.Type][]field) - newM := make(map[reflect.Type][]field, len(m)+1) - for k, v := range m { - newM[k] = v + if f, ok := fieldCache.Load(t); ok { + return f.([]field) } - newM[t] = f - fieldCache.value.Store(newM) - fieldCache.mu.Unlock() - return f + f, _ := fieldCache.LoadOrStore(t, typeFields(t)) + return f.([]field) } diff --git a/libgo/go/encoding/json/encode_test.go b/libgo/go/encoding/json/encode_test.go index 0f194e1..b90483c 100644 --- a/libgo/go/encoding/json/encode_test.go +++ b/libgo/go/encoding/json/encode_test.go @@ -981,3 +981,17 @@ func TestMarshalRawMessageValue(t *testing.T) { } } } + +type marshalPanic struct{} + +func (marshalPanic) MarshalJSON() ([]byte, error) { panic(0xdead) } + +func TestMarshalPanic(t *testing.T) { + defer func() { + if got := recover(); !reflect.DeepEqual(got, 0xdead) { + t.Errorf("panic() = (%T)(%v), want 0xdead", got, got) + } + }() + Marshal(&marshalPanic{}) + t.Error("Marshal should have panicked") +} diff --git a/libgo/go/encoding/json/number_test.go b/libgo/go/encoding/json/number_test.go index 4b86999..cc67018 100644 --- a/libgo/go/encoding/json/number_test.go +++ b/libgo/go/encoding/json/number_test.go @@ -10,7 +10,7 @@ import ( ) func TestNumberIsValid(t *testing.T) { - // From: http://stackoverflow.com/a/13340826 + // From: https://stackoverflow.com/a/13340826 var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) validTests := []string{ diff --git a/libgo/go/encoding/json/scanner.go b/libgo/go/encoding/json/scanner.go index ae34418..9e6d482 100644 --- a/libgo/go/encoding/json/scanner.go +++ b/libgo/go/encoding/json/scanner.go @@ -8,7 +8,7 @@ package json // Just about at the limit of what is reasonable to write by hand. // Some parts are a bit tedious, but overall it nicely factors out the // otherwise common code from the multiple scanning functions -// in this package (Compact, Indent, checkValid, nextValue, etc). +// in this package (Compact, Indent, checkValid, etc). // // This file starts with two simple examples using the scanner // before diving into the scanner itself. @@ -36,35 +36,6 @@ func checkValid(data []byte, scan *scanner) error { return nil } -// nextValue splits data after the next whole JSON value, -// returning that value and the bytes that follow it as separate slices. -// scan is passed in for use by nextValue to avoid an allocation. -func nextValue(data []byte, scan *scanner) (value, rest []byte, err error) { - scan.reset() - for i, c := range data { - v := scan.step(scan, c) - if v >= scanEndObject { - switch v { - // probe the scanner with a space to determine whether we will - // get scanEnd on the next character. Otherwise, if the next character - // is not a space, scanEndTop allocates a needless error. - case scanEndObject, scanEndArray: - if scan.step(scan, ' ') == scanEnd { - return data[:i+1], data[i+1:], nil - } - case scanError: - return nil, nil, scan.err - case scanEnd: - return data[:i], data[i:], nil - } - } - } - if scan.eof() == scanError { - return nil, nil, scan.err - } - return data, nil, nil -} - // A SyntaxError is a description of a JSON syntax error. type SyntaxError struct { msg string // description of error @@ -101,11 +72,6 @@ type scanner struct { // Error that happened, if any. err error - // 1-byte redo (see undo method) - redo bool - redoCode int - redoState func(*scanner, byte) int - // total bytes consumed, updated by decoder.Decode bytes int64 } @@ -151,7 +117,6 @@ func (s *scanner) reset() { s.step = stateBeginValue s.parseState = s.parseState[0:0] s.err = nil - s.redo = false s.endTop = false } @@ -184,7 +149,6 @@ 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 @@ -607,22 +571,3 @@ func quoteChar(c byte) string { s := strconv.Quote(string(c)) return "'" + s[1:len(s)-1] + "'" } - -// 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.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 byte) 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 0d4518a..6cdbe7d 100644 --- a/libgo/go/encoding/json/scanner_test.go +++ b/libgo/go/encoding/json/scanner_test.go @@ -200,43 +200,6 @@ func TestIndentErrors(t *testing.T) { } } -func TestNextValueBig(t *testing.T) { - initBig() - var scan scanner - item, rest, err := nextValue(jsonBig, &scan) - if err != nil { - t.Fatalf("nextValue: %s", err) - } - if len(item) != len(jsonBig) || &item[0] != &jsonBig[0] { - t.Errorf("invalid item: %d %d", len(item), len(jsonBig)) - } - if len(rest) != 0 { - t.Errorf("invalid rest: %d", len(rest)) - } - - item, rest, err = nextValue(append(jsonBig, "HELLO WORLD"...), &scan) - if err != nil { - t.Fatalf("nextValue extra: %s", err) - } - if len(item) != len(jsonBig) { - t.Errorf("invalid item: %d %d", len(item), len(jsonBig)) - } - if string(rest) != "HELLO WORLD" { - t.Errorf("invalid rest: %d", len(rest)) - } -} - -var benchScan scanner - -func BenchmarkSkipValue(b *testing.B) { - initBig() - b.ResetTimer() - for i := 0; i < b.N; i++ { - nextValue(jsonBig, &benchScan) - } - b.SetBytes(int64(len(jsonBig))) -} - func diff(t *testing.T, a, b []byte) { for i := 0; ; i++ { if i >= len(a) || i >= len(b) || a[i] != b[i] { diff --git a/libgo/go/encoding/xml/xml.go b/libgo/go/encoding/xml/xml.go index f408623..ca05944 100644 --- a/libgo/go/encoding/xml/xml.go +++ b/libgo/go/encoding/xml/xml.go @@ -7,8 +7,8 @@ package xml // References: -// Annotated XML spec: http://www.xml.com/axml/testaxml.htm -// XML name spaces: http://www.w3.org/TR/REC-xml-names/ +// Annotated XML spec: https://www.xml.com/axml/testaxml.htm +// XML name spaces: https://www.w3.org/TR/REC-xml-names/ // TODO(rsc): // Test error handling. @@ -167,9 +167,9 @@ type Decoder struct { // // Setting: // - // d.Strict = false; - // d.AutoClose = HTMLAutoClose; - // d.Entity = HTMLEntity + // d.Strict = false + // d.AutoClose = xml.HTMLAutoClose + // d.Entity = xml.HTMLEntity // // creates a parser that can handle typical HTML. // @@ -198,7 +198,7 @@ type Decoder struct { // charset-conversion readers, converting from the provided // non-UTF-8 charset into UTF-8. If CharsetReader is nil or // returns an error, parsing stops with an error. One of the - // the CharsetReader's result values must be non-nil. + // CharsetReader's result values must be non-nil. CharsetReader func(charset string, input io.Reader) (io.Reader, error) // DefaultSpace sets the default name space used for unadorned tags, @@ -271,7 +271,7 @@ func NewTokenDecoder(t TokenReader) *Decoder { // it will return an error. // // Token implements XML name spaces as described by -// http://www.w3.org/TR/REC-xml-names/. Each of the +// https://www.w3.org/TR/REC-xml-names/. Each of the // Name structures contained in the Token has the Space // set to the URL identifying its name space when known. // If Token encounters an unrecognized name space prefix, @@ -806,18 +806,7 @@ func (d *Decoder) rawToken() (Token, error) { } d.ungetc(b) - n := len(attr) - if n >= cap(attr) { - nCap := 2 * cap(attr) - if nCap == 0 { - nCap = 4 - } - nattr := make([]Attr, n, nCap) - copy(nattr, attr) - attr = nattr - } - attr = attr[0 : n+1] - a := &attr[n] + a := Attr{} if a.Name, ok = d.nsname(); !ok { if d.err == nil { d.err = d.syntaxError("expected attribute name in element") @@ -843,6 +832,7 @@ func (d *Decoder) rawToken() (Token, error) { } a.Value = string(data) } + attr = append(attr, a) } if empty { d.needClose = true @@ -873,7 +863,7 @@ func (d *Decoder) attrval() []byte { if !ok { return nil } - // http://www.w3.org/TR/REC-html40/intro/sgmltut.html#h-3.2.2 + // https://www.w3.org/TR/REC-html40/intro/sgmltut.html#h-3.2.2 if 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' || '0' <= b && b <= '9' || b == '_' || b == ':' || b == '-' { d.buf.WriteByte(b) @@ -1144,13 +1134,13 @@ Input: } // Decide whether the given rune is in the XML Character Range, per -// the Char production of http://www.xml.com/axml/testaxml.htm, +// the Char production of https://www.xml.com/axml/testaxml.htm, // Section 2.2 Characters. func isInCharacterRange(r rune) (inrange bool) { return r == 0x09 || r == 0x0A || r == 0x0D || - r >= 0x20 && r <= 0xDF77 || + r >= 0x20 && r <= 0xD7FF || r >= 0xE000 && r <= 0xFFFD || r >= 0x10000 && r <= 0x10FFFF } @@ -1273,7 +1263,7 @@ func isNameString(s string) bool { } // These tables were generated by cut and paste from Appendix B of -// the XML spec at http://www.xml.com/axml/testaxml.htm +// the XML spec at https://www.xml.com/axml/testaxml.htm // and then reformatting. First corresponds to (Letter | '_' | ':') // and second corresponds to NameChar. @@ -1591,7 +1581,9 @@ var second = &unicode.RangeTable{ // HTMLEntity is an entity map containing translations for the // standard HTML entity characters. -var HTMLEntity = htmlEntity +// +// See the Decoder.Strict and Decoder.Entity fields' documentation. +var HTMLEntity map[string]string = htmlEntity var htmlEntity = map[string]string{ /* @@ -1858,7 +1850,9 @@ var htmlEntity = map[string]string{ // HTMLAutoClose is the set of HTML elements that // should be considered to close automatically. -var HTMLAutoClose = htmlAutoClose +// +// See the Decoder.Strict and Decoder.Entity fields' documentation. +var HTMLAutoClose []string = htmlAutoClose var htmlAutoClose = []string{ /* @@ -1942,10 +1936,8 @@ func escapeText(w io.Writer, s []byte, escapeNewline bool) error { } last = i } - if _, err := w.Write(s[last:]); err != nil { - return err - } - return nil + _, err := w.Write(s[last:]) + return err } // EscapeString writes to p the properly escaped XML equivalent @@ -2028,10 +2020,8 @@ func emitCDATA(w io.Writer, s []byte) error { } s = s[i:] } - if _, err := w.Write(cdataEnd); err != nil { - return err - } - return nil + _, err := w.Write(cdataEnd) + return err } // procInst parses the `param="..."` or `param='...'` diff --git a/libgo/go/encoding/xml/xml_test.go b/libgo/go/encoding/xml/xml_test.go index 7a3511d..ee4ffa24 100644 --- a/libgo/go/encoding/xml/xml_test.go +++ b/libgo/go/encoding/xml/xml_test.go @@ -650,6 +650,20 @@ func TestDisallowedCharacters(t *testing.T) { } } +func TestIsInCharacterRange(t *testing.T) { + invalid := []rune{ + utf8.MaxRune + 1, + 0xD800, // surrogate min + 0xDFFF, // surrogate max + -1, + } + for _, r := range invalid { + if isInCharacterRange(r) { + t.Errorf("rune %U considered valid", r) + } + } +} + var procInstTests = []struct { input string expect [2]string diff --git a/libgo/go/expvar/expvar.go b/libgo/go/expvar/expvar.go index 8290e0b..174873a 100644 --- a/libgo/go/expvar/expvar.go +++ b/libgo/go/expvar/expvar.go @@ -22,7 +22,6 @@ package expvar import ( - "bytes" "encoding/json" "fmt" "log" @@ -32,6 +31,7 @@ import ( "runtime" "sort" "strconv" + "strings" "sync" "sync/atomic" ) @@ -111,7 +111,7 @@ type KeyValue struct { } func (v *Map) String() string { - var b bytes.Buffer + var b strings.Builder fmt.Fprintf(&b, "{") first := true v.Do(func(kv KeyValue) { diff --git a/libgo/go/flag/example_value_test.go b/libgo/go/flag/example_value_test.go new file mode 100644 index 0000000..9d464c6 --- /dev/null +++ b/libgo/go/flag/example_value_test.go @@ -0,0 +1,44 @@ +// Copyright 2018 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 flag_test + +import ( + "flag" + "fmt" + "net/url" +) + +type URLValue struct { + URL *url.URL +} + +func (v URLValue) String() string { + if v.URL != nil { + return v.URL.String() + } + return "" +} + +func (v URLValue) Set(s string) error { + if u, err := url.Parse(s); err != nil { + return err + } else { + *v.URL = *u + } + return nil +} + +var u = &url.URL{} + +func ExampleValue() { + fs := flag.NewFlagSet("ExampleValue", flag.ExitOnError) + fs.Var(&URLValue{u}, "url", "URL to parse") + + fs.Parse([]string{"-url", "https://golang.org/pkg/flag/"}) + fmt.Printf(`{scheme: %q, host: %q, path: %q}`, u.Scheme, u.Host, u.Path) + + // Output: + // {scheme: "https", host: "golang.org", path: "/pkg/flag/"} +} diff --git a/libgo/go/flag/flag.go b/libgo/go/flag/flag.go index edde528..2cd7829 100644 --- a/libgo/go/flag/flag.go +++ b/libgo/go/flag/flag.go @@ -5,7 +5,7 @@ /* Package flag implements command-line flag parsing. - Usage: + Usage Define flags using flag.String(), Bool(), Int(), etc. @@ -35,7 +35,10 @@ slice flag.Args() or individually as flag.Arg(i). The arguments are indexed from 0 through flag.NArg()-1. - Command line flag syntax: + Command line flag syntax + + The following forms are permitted: + -flag -flag=x -flag x // non-boolean flags only @@ -395,8 +398,8 @@ func Set(name, value string) error { return CommandLine.Set(name, value) } -// isZeroValue guesses whether the string represents the zero -// value for a flag. It is not accurate but in practice works OK. +// isZeroValue determines whether the string represents the zero +// value for a flag. func isZeroValue(flag *Flag, value string) bool { // Build a zero value of the flag's Value type, and see if the // result of calling its String method equals the value passed in. @@ -408,15 +411,7 @@ func isZeroValue(flag *Flag, value string) bool { } else { z = reflect.Zero(typ) } - if value == z.Interface().(Value).String() { - return true - } - - switch value { - case "false", "", "0": - return true - } - return false + return value == z.Interface().(Value).String() } // UnquoteUsage extracts a back-quoted name from the usage @@ -981,7 +976,8 @@ func commandLineUsage() { } // NewFlagSet returns a new, empty flag set with the specified name and -// error handling property. +// error handling property. If the name is not empty, it will be printed +// in the default usage message and in error messages. func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet { f := &FlagSet{ name: name, diff --git a/libgo/go/flag/flag_test.go b/libgo/go/flag/flag_test.go index 67c409f..c7f0c07 100644 --- a/libgo/go/flag/flag_test.go +++ b/libgo/go/flag/flag_test.go @@ -386,6 +386,8 @@ const defaultOutput = ` -A for bootstrapping, allow 'any' type -C a boolean defaulting to true (default true) -D path set relative path for local imports + -E string + issue 23543 (default "0") -F number a non-zero number (default 2.7) -G float @@ -412,6 +414,7 @@ func TestPrintDefaults(t *testing.T) { fs.Bool("Alongflagname", false, "disable bounds checking") fs.Bool("C", true, "a boolean defaulting to true") fs.String("D", "", "set relative `path` for local imports") + fs.String("E", "0", "issue 23543") fs.Float64("F", 2.7, "a non-zero `number`") fs.Float64("G", 0, "a float that defaults to zero") fs.String("M", "", "a multiline\nhelp\nstring") diff --git a/libgo/go/fmt/doc.go b/libgo/go/fmt/doc.go index 375cdb4..3b657f3 100644 --- a/libgo/go/fmt/doc.go +++ b/libgo/go/fmt/doc.go @@ -45,6 +45,8 @@ %q a double-quoted string safely escaped with Go syntax %x base 16, lower-case, two characters per byte %X base 16, upper-case, two characters per byte + Slice: + %p address of 0th element in base 16 notation, with leading 0x Pointer: %p base 16 notation, with leading 0x The %b, %d, %o, %x and %X verbs also work with pointers, @@ -62,7 +64,7 @@ laid out like this: struct: {field0 field1 ...} array, slice: [elem0 elem1 ...] - maps: map[key1:value1 key2:value2] + maps: map[key1:value1 key2:value2 ...] pointer to above: &{}, &[], &map[] Width is specified by an optional decimal number immediately preceding the verb. @@ -95,10 +97,11 @@ For floating-point values, width sets the minimum width of the field and precision sets the number of places after the decimal, if appropriate, - except that for %g/%G precision sets the total number of significant - digits. For example, given 12.345 the format %6.3f prints 12.345 while - %.3g prints 12.3. The default precision for %e, %f and %#g is 6; for %g it - is the smallest number of digits necessary to identify the value uniquely. + except that for %g/%G precision sets the maximum number of significant + digits (trailing zeros are removed). For example, given 12.345 the format + %6.3f prints 12.345 while %.3g prints 12.3. The default precision for %e, %f + and %#g is 6; for %g it is the smallest number of digits necessary to identify + the value uniquely. For complex numbers, the width and precision apply to the two components independently and the result is parenthesized, so %f applied @@ -279,9 +282,11 @@ The verbs behave analogously to those of Printf. For example, %x will scan an integer as a hexadecimal number, and %v will scan the default representation format for the value. - The Printf verbs %p and %T and the flags # and + are not implemented, - and the verbs %e %E %f %F %g and %G are all equivalent and scan any - floating-point or complex value. + The Printf verbs %p and %T and the flags # and + are not implemented. + The verbs %e %E %f %F %g and %G are all equivalent and scan any + floating-point or complex value. For float and complex literals in + scientific notation, both the decimal (e) and binary (p) exponent + formats are supported (for example: "2.3e+7" and "4.5p-8"). Input processed by verbs is implicitly space-delimited: the implementation of every verb except %c starts by discarding diff --git a/libgo/go/fmt/format.go b/libgo/go/fmt/format.go index d4b92f8..91103f2 100644 --- a/libgo/go/fmt/format.go +++ b/libgo/go/fmt/format.go @@ -122,8 +122,8 @@ func (f *fmt) padString(s string) { } } -// fmt_boolean formats a boolean. -func (f *fmt) fmt_boolean(v bool) { +// fmtBoolean formats a boolean. +func (f *fmt) fmtBoolean(v bool) { if v { f.padString("true") } else { @@ -131,8 +131,8 @@ func (f *fmt) fmt_boolean(v bool) { } } -// fmt_unicode formats a uint64 as "U+0078" or with f.sharp set as "U+0078 'x'". -func (f *fmt) fmt_unicode(u uint64) { +// fmtUnicode formats a uint64 as "U+0078" or with f.sharp set as "U+0078 'x'". +func (f *fmt) fmtUnicode(u uint64) { buf := f.intbuf[0:] // With default precision set the maximum needed buf length is 18 @@ -190,8 +190,8 @@ func (f *fmt) fmt_unicode(u uint64) { f.zero = oldZero } -// fmt_integer formats signed and unsigned integers. -func (f *fmt) fmt_integer(u uint64, base int, isSigned bool, digits string) { +// fmtInteger formats signed and unsigned integers. +func (f *fmt) fmtInteger(u uint64, base int, isSigned bool, digits string) { negative := isSigned && int64(u) < 0 if negative { u = -u @@ -322,14 +322,14 @@ func (f *fmt) truncate(s string) string { return s } -// fmt_s formats a string. -func (f *fmt) fmt_s(s string) { +// fmtS formats a string. +func (f *fmt) fmtS(s string) { s = f.truncate(s) f.padString(s) } -// fmt_sbx formats a string or byte slice as a hexadecimal encoding of its bytes. -func (f *fmt) fmt_sbx(s string, b []byte, digits string) { +// fmtSbx formats a string or byte slice as a hexadecimal encoding of its bytes. +func (f *fmt) fmtSbx(s string, b []byte, digits string) { length := len(b) if b == nil { // No byte slice present. Assume string s should be encoded. @@ -394,20 +394,20 @@ func (f *fmt) fmt_sbx(s string, b []byte, digits string) { } } -// fmt_sx formats a string as a hexadecimal encoding of its bytes. -func (f *fmt) fmt_sx(s, digits string) { - f.fmt_sbx(s, nil, digits) +// fmtSx formats a string as a hexadecimal encoding of its bytes. +func (f *fmt) fmtSx(s, digits string) { + f.fmtSbx(s, nil, digits) } -// fmt_bx formats a byte slice as a hexadecimal encoding of its bytes. -func (f *fmt) fmt_bx(b []byte, digits string) { - f.fmt_sbx("", b, digits) +// fmtBx formats a byte slice as a hexadecimal encoding of its bytes. +func (f *fmt) fmtBx(b []byte, digits string) { + f.fmtSbx("", b, digits) } -// fmt_q formats a string as a double-quoted, escaped Go string constant. +// fmtQ formats a string as a double-quoted, escaped Go string constant. // If f.sharp is set a raw (backquoted) string may be returned instead // if the string does not contain any control characters other than tab. -func (f *fmt) fmt_q(s string) { +func (f *fmt) fmtQ(s string) { s = f.truncate(s) if f.sharp && strconv.CanBackquote(s) { f.padString("`" + s + "`") @@ -421,9 +421,9 @@ func (f *fmt) fmt_q(s string) { } } -// fmt_c formats an integer as a Unicode character. +// fmtC formats an integer as a Unicode character. // If the character is not valid Unicode, it will print '\ufffd'. -func (f *fmt) fmt_c(c uint64) { +func (f *fmt) fmtC(c uint64) { r := rune(c) if c > utf8.MaxRune { r = utf8.RuneError @@ -433,9 +433,9 @@ func (f *fmt) fmt_c(c uint64) { f.pad(buf[:w]) } -// fmt_qc formats an integer as a single-quoted, escaped Go character constant. +// fmtQc formats an integer as a single-quoted, escaped Go character constant. // If the character is not valid Unicode, it will print '\ufffd'. -func (f *fmt) fmt_qc(c uint64) { +func (f *fmt) fmtQc(c uint64) { r := rune(c) if c > utf8.MaxRune { r = utf8.RuneError @@ -448,9 +448,9 @@ func (f *fmt) fmt_qc(c uint64) { } } -// fmt_float formats a float64. It assumes that verb is a valid format specifier +// fmtFloat formats a float64. It assumes that verb is a valid format specifier // for strconv.AppendFloat and therefore fits into a byte. -func (f *fmt) fmt_float(v float64, size int, verb rune, prec int) { +func (f *fmt) fmtFloat(v float64, size int, verb rune, prec int) { // Explicit precision in format specifier overrules default precision. if f.precPresent { prec = f.prec diff --git a/libgo/go/fmt/print.go b/libgo/go/fmt/print.go index 98c156a..f67f805 100644 --- a/libgo/go/fmt/print.go +++ b/libgo/go/fmt/print.go @@ -341,7 +341,7 @@ func (p *pp) badVerb(verb rune) { func (p *pp) fmtBool(v bool, verb rune) { switch verb { case 't', 'v': - p.fmt.fmt_boolean(v) + p.fmt.fmtBoolean(v) default: p.badVerb(verb) } @@ -352,7 +352,7 @@ func (p *pp) fmtBool(v bool, verb rune) { func (p *pp) fmt0x64(v uint64, leading0x bool) { sharp := p.fmt.sharp p.fmt.sharp = leading0x - p.fmt.fmt_integer(v, 16, unsigned, ldigits) + p.fmt.fmtInteger(v, 16, unsigned, ldigits) p.fmt.sharp = sharp } @@ -363,28 +363,28 @@ func (p *pp) fmtInteger(v uint64, isSigned bool, verb rune) { if p.fmt.sharpV && !isSigned { p.fmt0x64(v, true) } else { - p.fmt.fmt_integer(v, 10, isSigned, ldigits) + p.fmt.fmtInteger(v, 10, isSigned, ldigits) } case 'd': - p.fmt.fmt_integer(v, 10, isSigned, ldigits) + p.fmt.fmtInteger(v, 10, isSigned, ldigits) case 'b': - p.fmt.fmt_integer(v, 2, isSigned, ldigits) + p.fmt.fmtInteger(v, 2, isSigned, ldigits) case 'o': - p.fmt.fmt_integer(v, 8, isSigned, ldigits) + p.fmt.fmtInteger(v, 8, isSigned, ldigits) case 'x': - p.fmt.fmt_integer(v, 16, isSigned, ldigits) + p.fmt.fmtInteger(v, 16, isSigned, ldigits) case 'X': - p.fmt.fmt_integer(v, 16, isSigned, udigits) + p.fmt.fmtInteger(v, 16, isSigned, udigits) case 'c': - p.fmt.fmt_c(v) + p.fmt.fmtC(v) case 'q': if v <= utf8.MaxRune { - p.fmt.fmt_qc(v) + p.fmt.fmtQc(v) } else { p.badVerb(verb) } case 'U': - p.fmt.fmt_unicode(v) + p.fmt.fmtUnicode(v) default: p.badVerb(verb) } @@ -395,13 +395,13 @@ func (p *pp) fmtInteger(v uint64, isSigned bool, verb rune) { func (p *pp) fmtFloat(v float64, size int, verb rune) { switch verb { case 'v': - p.fmt.fmt_float(v, size, 'g', -1) + p.fmt.fmtFloat(v, size, 'g', -1) case 'b', 'g', 'G': - p.fmt.fmt_float(v, size, verb, -1) + p.fmt.fmtFloat(v, size, verb, -1) case 'f', 'e', 'E': - p.fmt.fmt_float(v, size, verb, 6) + p.fmt.fmtFloat(v, size, verb, 6) case 'F': - p.fmt.fmt_float(v, size, 'f', 6) + p.fmt.fmtFloat(v, size, 'f', 6) default: p.badVerb(verb) } @@ -432,18 +432,18 @@ func (p *pp) fmtString(v string, verb rune) { switch verb { case 'v': if p.fmt.sharpV { - p.fmt.fmt_q(v) + p.fmt.fmtQ(v) } else { - p.fmt.fmt_s(v) + p.fmt.fmtS(v) } case 's': - p.fmt.fmt_s(v) + p.fmt.fmtS(v) case 'x': - p.fmt.fmt_sx(v, ldigits) + p.fmt.fmtSx(v, ldigits) case 'X': - p.fmt.fmt_sx(v, udigits) + p.fmt.fmtSx(v, udigits) case 'q': - p.fmt.fmt_q(v) + p.fmt.fmtQ(v) default: p.badVerb(verb) } @@ -472,18 +472,18 @@ func (p *pp) fmtBytes(v []byte, verb rune, typeString string) { if i > 0 { p.buf.WriteByte(' ') } - p.fmt.fmt_integer(uint64(c), 10, unsigned, ldigits) + p.fmt.fmtInteger(uint64(c), 10, unsigned, ldigits) } p.buf.WriteByte(']') } case 's': - p.fmt.fmt_s(string(v)) + p.fmt.fmtS(string(v)) case 'x': - p.fmt.fmt_bx(v, ldigits) + p.fmt.fmtBx(v, ldigits) case 'X': - p.fmt.fmt_bx(v, udigits) + p.fmt.fmtBx(v, udigits) case 'q': - p.fmt.fmt_q(string(v)) + p.fmt.fmtQ(string(v)) default: p.printValue(reflect.ValueOf(v), verb, 0) } @@ -577,7 +577,7 @@ func (p *pp) handleMethods(verb rune) (handled bool) { handled = true defer p.catchPanic(p.arg, verb) // Print the result of GoString unadorned. - p.fmt.fmt_s(stringer.GoString()) + p.fmt.fmtS(stringer.GoString()) return } } else { @@ -626,7 +626,7 @@ func (p *pp) printArg(arg interface{}, verb rune) { // %T (the value's type) and %p (its address) are special; we always do them first. switch verb { case 'T': - p.fmt.fmt_s(reflect.TypeOf(arg).String()) + p.fmt.fmtS(reflect.TypeOf(arg).String()) return case 'p': p.fmtPointer(reflect.ValueOf(arg), 'p') diff --git a/libgo/go/go/ast/ast.go b/libgo/go/go/ast/ast.go index c07dd5a..fd10950 100644 --- a/libgo/go/go/ast/ast.go +++ b/libgo/go/go/ast/ast.go @@ -153,10 +153,12 @@ func (g *CommentGroup) Text() string { // A Field represents a Field declaration list in a struct type, // a method list in an interface type, or a parameter/result declaration // in a signature. +// Field.Names is nil for unnamed parameters (parameter lists which only contain types) +// and embedded struct fields. In the latter case, the field name is the type name. // type Field struct { Doc *CommentGroup // associated documentation; or nil - Names []*Ident // field/method/parameter names; or nil if anonymous field + Names []*Ident // field/method/parameter names; or nil Type Expr // field/method/parameter type Tag *BasicLit // field tag; or nil Comment *CommentGroup // line comments; or nil @@ -207,14 +209,14 @@ func (f *FieldList) End() token.Pos { return token.NoPos } -// NumFields returns the number of (named and anonymous fields) in a FieldList. +// NumFields returns the number of parameters or struct fields represented by a FieldList. func (f *FieldList) NumFields() int { n := 0 if f != nil { for _, g := range f.List { m := len(g.Names) if m == 0 { - m = 1 // anonymous field + m = 1 } n += m } @@ -264,10 +266,11 @@ type ( // A CompositeLit node represents a composite literal. CompositeLit struct { - Type Expr // literal type; or nil - Lbrace token.Pos // position of "{" - Elts []Expr // list of composite elements; or nil - Rbrace token.Pos // position of "}" + Type Expr // literal type; or nil + Lbrace token.Pos // position of "{" + Elts []Expr // list of composite elements; or nil + Rbrace token.Pos // position of "}" + Incomplete bool // true if (source) expressions are missing in the Elts list } // A ParenExpr node represents a parenthesized expression. diff --git a/libgo/go/go/ast/filter.go b/libgo/go/go/ast/filter.go index bb57116..32352cb 100644 --- a/libgo/go/go/ast/filter.go +++ b/libgo/go/go/ast/filter.go @@ -109,6 +109,34 @@ func filterFieldList(fields *FieldList, filter Filter, export bool) (removedFiel return } +func filterCompositeLit(lit *CompositeLit, filter Filter, export bool) { + n := len(lit.Elts) + lit.Elts = filterExprList(lit.Elts, filter, export) + if len(lit.Elts) < n { + lit.Incomplete = true + } +} + +func filterExprList(list []Expr, filter Filter, export bool) []Expr { + j := 0 + for _, exp := range list { + switch x := exp.(type) { + case *CompositeLit: + filterCompositeLit(x, filter, export) + case *KeyValueExpr: + if x, ok := x.Key.(*Ident); ok && !filter(x.Name) { + continue + } + if x, ok := x.Value.(*CompositeLit); ok { + filterCompositeLit(x, filter, export) + } + } + list[j] = exp + j++ + } + return list[0:j] +} + func filterParamList(fields *FieldList, filter Filter, export bool) bool { if fields == nil { return false @@ -158,6 +186,7 @@ func filterSpec(spec Spec, f Filter, export bool) bool { switch s := spec.(type) { case *ValueSpec: s.Names = filterIdentList(s.Names, f) + s.Values = filterExprList(s.Values, f, export) if len(s.Names) > 0 { if export { filterType(s.Type, f, export) diff --git a/libgo/go/go/build/build.go b/libgo/go/go/build/build.go index 9df4930..e067fd0 100644 --- a/libgo/go/go/build/build.go +++ b/libgo/go/go/build/build.go @@ -12,10 +12,12 @@ import ( "go/doc" "go/parser" "go/token" + "internal/goroot" "io" "io/ioutil" "log" "os" + "os/exec" pathpkg "path" "path/filepath" "runtime" @@ -277,6 +279,8 @@ func defaultGOPATH() string { return "" } +var defaultReleaseTags []string + func defaultContext() Context { var c Context @@ -292,11 +296,13 @@ func defaultContext() Context { // say "+build go1.x", and code that should only be built before Go 1.x // (perhaps it is the stub to use in that case) should say "+build !go1.x". // NOTE: If you add to this list, also update the doc comment in doc.go. - const version = 10 // go1.10 + const version = 11 // go1.11 for i := 1; i <= version; i++ { c.ReleaseTags = append(c.ReleaseTags, "go1."+strconv.Itoa(i)) } + defaultReleaseTags = append([]string{}, c.ReleaseTags...) // our own private copy + env := os.Getenv("CGO_ENABLED") // No defaultCGO_ENABLED in gccgo. // if env == "" { @@ -540,7 +546,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa inTestdata := func(sub string) bool { return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || strings.HasPrefix(sub, "testdata/") || sub == "testdata" } - if ctxt.GOROOT != "" && ctxt.Compiler != "gccgo" { + if ctxt.GOROOT != "" { root := ctxt.joinPath(ctxt.GOROOT, "src") if sub, ok := ctxt.hasSubdir(root, p.Dir); ok && !inTestdata(sub) { p.Goroot = true @@ -585,13 +591,19 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa return p, fmt.Errorf("import %q: cannot import absolute path", path) } + gopath := ctxt.gopath() // needed by both importGo and below; avoid computing twice + if err := ctxt.importGo(p, path, srcDir, mode, gopath); err == nil { + goto Found + } else if err != errNoModules { + return p, err + } + // tried records the location of unsuccessful package lookups var tried struct { vendor []string goroot string gopath []string } - gopath := ctxt.gopath() // Vendor directories get first chance to satisfy import. if mode&IgnoreVendor == 0 && srcDir != "" { @@ -647,7 +659,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa } tried.goroot = dir } - if ctxt.Compiler == "gccgo" && isStandardPackage(path) { + if ctxt.Compiler == "gccgo" && goroot.IsStandardPackage(ctxt.GOROOT, ctxt.Compiler, path) { p.Dir = ctxt.joinPath(ctxt.GOROOT, "src", path) p.Goroot = true p.Root = ctxt.GOROOT @@ -705,6 +717,11 @@ Found: // non-nil *Package returned when an error occurs. // We need to do this before we return early on FindOnly flag. if IsLocalImport(path) && !ctxt.isDir(p.Dir) { + if ctxt.Compiler == "gccgo" && p.Goroot { + // gccgo has no sources for GOROOT packages. + return p, nil + } + // package was not found return p, fmt.Errorf("cannot find package %q in:\n\t%s", path, p.Dir) } @@ -828,7 +845,8 @@ Found: }) p.InvalidGoFiles = append(p.InvalidGoFiles, name) } - if pf.Doc != nil && p.Doc == "" { + // Grab the first package comment as docs, provided it is not from a test file. + if pf.Doc != nil && p.Doc == "" && !isTest && !isXTest { p.Doc = doc.Synopsis(pf.Doc.Text()) } @@ -931,6 +949,116 @@ Found: return p, pkgerr } +var errNoModules = errors.New("not using modules") + +// importGo checks whether it can use the go command to find the directory for path. +// If using the go command is not appopriate, importGo returns errNoModules. +// Otherwise, importGo tries using the go command and reports whether that succeeded. +// Using the go command lets build.Import and build.Context.Import find code +// in Go modules. In the long term we want tools to use go/packages (currently golang.org/x/tools/go/packages), +// which will also use the go command. +// Invoking the go command here is not very efficient in that it computes information +// about the requested package and all dependencies and then only reports about the requested package. +// Then we reinvoke it for every dependency. But this is still better than not working at all. +// See golang.org/issue/26504. +func (ctxt *Context) importGo(p *Package, path, srcDir string, mode ImportMode, gopath []string) error { + const debugImportGo = false + + // To invoke the go command, we must know the source directory, + // we must not being doing special things like AllowBinary or IgnoreVendor, + // and all the file system callbacks must be nil (we're meant to use the local file system). + if srcDir == "" || mode&AllowBinary != 0 || mode&IgnoreVendor != 0 || + ctxt.JoinPath != nil || ctxt.SplitPathList != nil || ctxt.IsAbsPath != nil || ctxt.IsDir != nil || ctxt.HasSubdir != nil || ctxt.ReadDir != nil || ctxt.OpenFile != nil || !equal(ctxt.ReleaseTags, defaultReleaseTags) { + return errNoModules + } + + // If modules are not enabled, then the in-process code works fine and we should keep using it. + switch os.Getenv("GO111MODULE") { + case "off": + return errNoModules + case "on": + // ok + default: // "", "auto", anything else + // Automatic mode: no module use in $GOPATH/src. + for _, root := range gopath { + sub, ok := ctxt.hasSubdir(root, srcDir) + if ok && strings.HasPrefix(sub, "src/") { + return errNoModules + } + } + } + + // For efficiency, if path is a standard library package, let the usual lookup code handle it. + if ctxt.GOROOT != "" { + dir := ctxt.joinPath(ctxt.GOROOT, "src", path) + if ctxt.isDir(dir) { + return errNoModules + } + } + + // Look to see if there is a go.mod. + abs, err := filepath.Abs(srcDir) + if err != nil { + return errNoModules + } + for { + info, err := os.Stat(filepath.Join(abs, "go.mod")) + if err == nil && !info.IsDir() { + break + } + d := filepath.Dir(abs) + if len(d) >= len(abs) { + return errNoModules // reached top of file system, no go.mod + } + abs = d + } + + cmd := exec.Command("go", "list", "-compiler="+ctxt.Compiler, "-tags="+strings.Join(ctxt.BuildTags, ","), "-installsuffix="+ctxt.InstallSuffix, "-f={{.Dir}}\n{{.ImportPath}}\n{{.Root}}\n{{.Goroot}}\n", path) + cmd.Dir = srcDir + var stdout, stderr strings.Builder + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + cgo := "0" + if ctxt.CgoEnabled { + cgo = "1" + } + cmd.Env = append(os.Environ(), + "GOOS="+ctxt.GOOS, + "GOARCH="+ctxt.GOARCH, + "GOROOT="+ctxt.GOROOT, + "GOPATH="+ctxt.GOPATH, + "CGO_ENABLED="+cgo, + ) + + if err := cmd.Run(); err != nil { + return fmt.Errorf("go/build: importGo %s: %v\n%s\n", path, err, stderr.String()) + } + + f := strings.Split(stdout.String(), "\n") + if len(f) != 5 || f[4] != "" { + return fmt.Errorf("go/build: importGo %s: unexpected output:\n%s\n", path, stdout.String()) + } + + p.Dir = f[0] + p.ImportPath = f[1] + p.Root = f[2] + p.Goroot = f[3] == "true" + return nil +} + +func equal(x, y []string) bool { + if len(x) != len(y) { + return false + } + for i, xi := range x { + if xi != y[i] { + return false + } + } + return true +} + // hasGoFiles reports whether dir contains any files with names ending in .go. // For a vendor check we must exclude directories that contain no .go files. // Otherwise it is not possible to vendor just a/b/c and still import the @@ -1385,7 +1513,8 @@ func (ctxt *Context) makePathsAbsolute(args []string, srcDir string) { // See golang.org/issue/6038. // The @ is for OS X. See golang.org/issue/13720. // The % is for Jenkins. See golang.org/issue/16959. -const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@% " +// The ! is because module paths may use them. See golang.org/issue/26716. +const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@%! " func safeCgoName(s string) bool { if s == "" { @@ -1566,32 +1695,10 @@ func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool { } n := len(l) if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] { - if allTags != nil { - allTags[l[n-2]] = true - allTags[l[n-1]] = true - } - if l[n-1] != ctxt.GOARCH { - return false - } - if ctxt.GOOS == "android" && l[n-2] == "linux" { - return true - } - return l[n-2] == ctxt.GOOS + return ctxt.match(l[n-1], allTags) && ctxt.match(l[n-2], allTags) } - if n >= 1 && knownOS[l[n-1]] { - if allTags != nil { - allTags[l[n-1]] = true - } - if ctxt.GOOS == "android" && l[n-1] == "linux" { - return true - } - return l[n-1] == ctxt.GOOS - } - if n >= 1 && knownArch[l[n-1]] { - if allTags != nil { - allTags[l[n-1]] = true - } - return l[n-1] == ctxt.GOARCH + if n >= 1 && (knownOS[l[n-1]] || knownArch[l[n-1]]) { + return ctxt.match(l[n-1], allTags) } return true } diff --git a/libgo/go/go/build/build_test.go b/libgo/go/go/build/build_test.go index 87d8d04..f21bc32 100644 --- a/libgo/go/go/build/build_test.go +++ b/libgo/go/go/build/build_test.go @@ -177,6 +177,18 @@ func TestShouldBuild(t *testing.T) { } } +func TestGoodOSArchFile(t *testing.T) { + ctx := &Context{BuildTags: []string{"linux"}, GOOS: "darwin"} + m := map[string]bool{} + want := map[string]bool{"linux": true} + if !ctx.goodOSArchFile("hello_linux.go", m) { + t.Errorf("goodOSArchFile(hello_linux.go) = false, want true") + } + if !reflect.DeepEqual(m, want) { + t.Errorf("goodOSArchFile(hello_linux.go) tags = %v, want %v", m, want) + } +} + type readNopCloser struct { io.Reader } @@ -397,3 +409,19 @@ func TestImportDirTarget(t *testing.T) { t.Errorf("p.PkgTargetRoot == %q, p.PkgObj == %q, want non-empty", p.PkgTargetRoot, p.PkgObj) } } + +// TestIssue23594 prevents go/build from regressing and populating Package.Doc +// from comments in test files. +func TestIssue23594(t *testing.T) { + // Package testdata/doc contains regular and external test files + // with comments attached to their package declarations. The names of the files + // ensure that we see the comments from the test files first. + p, err := ImportDir("testdata/doc", 0) + if err != nil { + t.Fatalf("could not import testdata: %v", err) + } + + if p.Doc != "Correct" { + t.Fatalf("incorrectly set .Doc to %q", p.Doc) + } +} diff --git a/libgo/go/go/build/deps_test.go b/libgo/go/go/build/deps_test.go index 6f9838b..b9593787 100644 --- a/libgo/go/go/build/deps_test.go +++ b/libgo/go/go/build/deps_test.go @@ -36,14 +36,15 @@ var pkgDeps = map[string][]string{ // L0 is the lowest level, core, nearly unavoidable packages. "errors": {}, "io": {"errors", "sync", "sync/atomic"}, - "runtime": {"unsafe", "runtime/internal/atomic", "runtime/internal/sys"}, + "runtime": {"unsafe", "runtime/internal/atomic", "runtime/internal/sys", "internal/cpu", "internal/bytealg"}, "runtime/internal/sys": {}, "runtime/internal/atomic": {"unsafe", "runtime/internal/sys"}, "internal/race": {"runtime", "unsafe"}, "sync": {"internal/race", "runtime", "sync/atomic", "unsafe"}, "sync/atomic": {"unsafe"}, "unsafe": {}, - "internal/cpu": {"runtime"}, + "internal/cpu": {}, + "internal/bytealg": {"unsafe", "internal/cpu"}, "L0": { "errors", @@ -54,6 +55,7 @@ var pkgDeps = map[string][]string{ "sync/atomic", "unsafe", "internal/cpu", + "internal/bytealg", }, // L1 adds simple functions and strings processing, @@ -62,7 +64,7 @@ var pkgDeps = map[string][]string{ "math/bits": {}, "math/cmplx": {"math"}, "math/rand": {"L0", "math"}, - "strconv": {"L0", "unicode/utf8", "math"}, + "strconv": {"L0", "unicode/utf8", "math", "math/bits"}, "unicode/utf16": {}, "unicode/utf8": {}, @@ -97,28 +99,29 @@ var pkgDeps = map[string][]string{ // L3 adds reflection and some basic utility packages // and interface definitions, but nothing that makes // system calls. - "crypto": {"L2", "hash"}, // interfaces - "crypto/cipher": {"L2", "crypto/subtle"}, - "crypto/subtle": {}, - "encoding/base32": {"L2"}, - "encoding/base64": {"L2", "encoding/binary"}, - "encoding/binary": {"L2", "reflect"}, - "hash": {"L2"}, // interfaces - "hash/adler32": {"L2", "hash"}, - "hash/crc32": {"L2", "hash"}, - "hash/crc64": {"L2", "hash"}, - "hash/fnv": {"L2", "hash"}, - "image": {"L2", "image/color"}, // interfaces - "image/color": {"L2"}, // interfaces - "image/color/palette": {"L2", "image/color"}, - "reflect": {"L2"}, - "sort": {"reflect"}, + "crypto": {"L2", "hash"}, // interfaces + "crypto/cipher": {"L2", "crypto/subtle", "crypto/internal/subtle"}, + "crypto/internal/subtle": {"unsafe", "reflect"}, // reflect behind a appengine tag + "crypto/subtle": {}, + "encoding/base32": {"L2"}, + "encoding/base64": {"L2", "encoding/binary"}, + "encoding/binary": {"L2", "reflect"}, + "hash": {"L2"}, // interfaces + "hash/adler32": {"L2", "hash"}, + "hash/crc32": {"L2", "hash"}, + "hash/crc64": {"L2", "hash"}, + "hash/fnv": {"L2", "hash"}, + "image": {"L2", "image/color"}, // interfaces + "image/color": {"L2"}, // interfaces + "image/color/palette": {"L2", "image/color"}, + "reflect": {"L2"}, + "sort": {"reflect"}, "L3": { "L2", "crypto", "crypto/cipher", - "crypto/internal/cipherhw", + "crypto/internal/subtle", "crypto/subtle", "encoding/base32", "encoding/base64", @@ -137,7 +140,8 @@ var pkgDeps = map[string][]string{ // End of linear dependency definitions. // Operating system access. - "syscall": {"L0", "internal/race", "internal/syscall/windows/sysdll", "unicode/utf16"}, + "syscall": {"L0", "internal/race", "internal/syscall/windows/sysdll", "syscall/js", "unicode/utf16"}, + "syscall/js": {"L0"}, "internal/syscall/unix": {"L0", "syscall"}, "internal/syscall/windows": {"L0", "syscall", "internal/syscall/windows/sysdll"}, "internal/syscall/windows/registry": {"L0", "syscall", "internal/syscall/windows/sysdll", "unicode/utf16"}, @@ -156,7 +160,7 @@ var pkgDeps = map[string][]string{ "internal/poll": {"L0", "internal/race", "syscall", "time", "unicode/utf16", "unicode/utf8", "internal/syscall/windows"}, "internal/testlog": {"L0"}, - "os": {"L1", "os", "syscall", "time", "internal/poll", "internal/syscall/windows", "internal/testlog"}, + "os": {"L1", "os", "syscall", "time", "internal/poll", "internal/syscall/windows", "internal/syscall/unix", "internal/testlog"}, "path/filepath": {"L2", "os", "syscall", "internal/syscall/windows"}, "io/ioutil": {"L2", "os", "path/filepath", "time"}, "os/exec": {"L2", "os", "context", "path/filepath", "syscall"}, @@ -181,7 +185,7 @@ var pkgDeps = map[string][]string{ "regexp/syntax": {"L2"}, "runtime/debug": {"L2", "fmt", "io/ioutil", "os", "time"}, "runtime/pprof": {"L2", "compress/gzip", "context", "encoding/binary", "fmt", "io/ioutil", "os", "text/tabwriter", "time"}, - "runtime/trace": {"L0"}, + "runtime/trace": {"L0", "context", "fmt"}, "text/tabwriter": {"L2"}, "testing": {"L2", "flag", "fmt", "internal/race", "os", "runtime/debug", "runtime/pprof", "runtime/trace", "time"}, @@ -223,54 +227,54 @@ var pkgDeps = map[string][]string{ "go/importer": {"L4", "go/build", "go/internal/gccgoimporter", "go/internal/gcimporter", "go/internal/srcimporter", "go/token", "go/types"}, "go/internal/gcimporter": {"L4", "OS", "go/build", "go/constant", "go/token", "go/types", "text/scanner"}, "go/internal/gccgoimporter": {"L4", "OS", "debug/elf", "debug/xcoff", "go/constant", "go/token", "go/types", "text/scanner"}, - "go/internal/srcimporter": {"L4", "fmt", "go/ast", "go/build", "go/parser", "go/token", "go/types", "path/filepath"}, + "go/internal/srcimporter": {"L4", "OS", "fmt", "go/ast", "go/build", "go/parser", "go/token", "go/types", "path/filepath"}, "go/types": {"L4", "GOPARSER", "container/heap", "go/constant"}, // One of a kind. - "archive/tar": {"L4", "OS", "syscall", "os/user"}, - "archive/zip": {"L4", "OS", "compress/flate"}, - "container/heap": {"sort"}, - "compress/bzip2": {"L4"}, - "compress/flate": {"L4"}, - "compress/gzip": {"L4", "compress/flate"}, - "compress/lzw": {"L4"}, - "compress/zlib": {"L4", "compress/flate"}, - "context": {"errors", "fmt", "reflect", "sync", "time"}, - "database/sql": {"L4", "container/list", "context", "database/sql/driver", "database/sql/internal"}, - "database/sql/driver": {"L4", "context", "time", "database/sql/internal"}, - "debug/dwarf": {"L4"}, - "debug/elf": {"L4", "OS", "debug/dwarf", "compress/zlib"}, - "debug/gosym": {"L4"}, - "debug/macho": {"L4", "OS", "debug/dwarf"}, - "debug/pe": {"L4", "OS", "debug/dwarf"}, - "debug/plan9obj": {"L4", "OS"}, - "debug/xcoff": {"L4", "OS", "debug/dwarf"}, - "encoding": {"L4"}, - "encoding/ascii85": {"L4"}, - "encoding/asn1": {"L4", "math/big"}, - "encoding/csv": {"L4"}, - "encoding/gob": {"L4", "OS", "encoding"}, - "encoding/hex": {"L4"}, - "encoding/json": {"L4", "encoding"}, - "encoding/pem": {"L4"}, - "encoding/xml": {"L4", "encoding"}, - "flag": {"L4", "OS"}, - "go/build": {"L4", "OS", "GOPARSER"}, - "html": {"L4"}, - "image/draw": {"L4", "image/internal/imageutil"}, - "image/gif": {"L4", "compress/lzw", "image/color/palette", "image/draw"}, - "image/internal/imageutil": {"L4"}, - "image/jpeg": {"L4", "image/internal/imageutil"}, - "image/png": {"L4", "compress/zlib"}, - "index/suffixarray": {"L4", "regexp"}, - "internal/singleflight": {"sync"}, - "internal/trace": {"L4", "OS"}, - "math/big": {"L4"}, - "mime": {"L4", "OS", "syscall", "internal/syscall/windows/registry"}, - "mime/quotedprintable": {"L4"}, - "net/internal/socktest": {"L4", "OS", "syscall", "internal/syscall/windows"}, - "net/url": {"L4"}, - "plugin": {"L0", "OS", "CGO"}, + "archive/tar": {"L4", "OS", "syscall", "os/user"}, + "archive/zip": {"L4", "OS", "compress/flate"}, + "container/heap": {"sort"}, + "compress/bzip2": {"L4"}, + "compress/flate": {"L4"}, + "compress/gzip": {"L4", "compress/flate"}, + "compress/lzw": {"L4"}, + "compress/zlib": {"L4", "compress/flate"}, + "context": {"errors", "fmt", "reflect", "sync", "time"}, + "database/sql": {"L4", "container/list", "context", "database/sql/driver", "database/sql/internal"}, + "database/sql/driver": {"L4", "context", "time", "database/sql/internal"}, + "debug/dwarf": {"L4"}, + "debug/elf": {"L4", "OS", "debug/dwarf", "compress/zlib"}, + "debug/gosym": {"L4"}, + "debug/macho": {"L4", "OS", "debug/dwarf", "compress/zlib"}, + "debug/pe": {"L4", "OS", "debug/dwarf", "compress/zlib"}, + "debug/plan9obj": {"L4", "OS"}, + "debug/xcoff": {"L4", "OS", "debug/dwarf"}, + "encoding": {"L4"}, + "encoding/ascii85": {"L4"}, + "encoding/asn1": {"L4", "math/big"}, + "encoding/csv": {"L4"}, + "encoding/gob": {"L4", "OS", "encoding"}, + "encoding/hex": {"L4"}, + "encoding/json": {"L4", "encoding"}, + "encoding/pem": {"L4"}, + "encoding/xml": {"L4", "encoding"}, + "flag": {"L4", "OS"}, + "go/build": {"L4", "OS", "GOPARSER"}, + "html": {"L4"}, + "image/draw": {"L4", "image/internal/imageutil"}, + "image/gif": {"L4", "compress/lzw", "image/color/palette", "image/draw"}, + "image/internal/imageutil": {"L4"}, + "image/jpeg": {"L4", "image/internal/imageutil"}, + "image/png": {"L4", "compress/zlib"}, + "index/suffixarray": {"L4", "regexp"}, + "internal/singleflight": {"sync"}, + "internal/trace": {"L4", "OS"}, + "math/big": {"L4"}, + "mime": {"L4", "OS", "syscall", "internal/syscall/windows/registry"}, + "mime/quotedprintable": {"L4"}, + "net/internal/socktest": {"L4", "OS", "syscall", "internal/syscall/windows"}, + "net/url": {"L4"}, + "plugin": {"L0", "OS", "CGO"}, "runtime/pprof/internal/profile": {"L4", "OS", "compress/gzip", "regexp"}, "testing/internal/testdeps": {"L4", "internal/testlog", "runtime/pprof", "regexp"}, "text/scanner": {"L4", "OS"}, @@ -299,7 +303,7 @@ var pkgDeps = map[string][]string{ "runtime/msan": {"C"}, // Plan 9 alone needs io/ioutil and os. - "os/user": {"L4", "CGO", "io/ioutil", "os", "syscall"}, + "os/user": {"L4", "CGO", "io/ioutil", "os", "syscall", "internal/syscall/windows", "internal/syscall/windows/registry"}, // Internal package used only for testing. "os/signal/internal/pty": {"CGO", "fmt", "os", "syscall"}, @@ -312,7 +316,7 @@ var pkgDeps = map[string][]string{ "context", "math/rand", "os", "reflect", "sort", "syscall", "time", "internal/nettrace", "internal/poll", "internal/syscall/windows", "internal/singleflight", "internal/race", - "golang_org/x/net/lif", "golang_org/x/net/route", + "golang_org/x/net/dns/dnsmessage", "golang_org/x/net/lif", "golang_org/x/net/route", }, // NET enables use of basic network-related packages. @@ -329,19 +333,21 @@ var pkgDeps = map[string][]string{ "net/textproto": {"L4", "OS", "net"}, // Core crypto. - "crypto/aes": {"L3"}, - "crypto/des": {"L3"}, - "crypto/hmac": {"L3"}, - "crypto/md5": {"L3"}, - "crypto/rc4": {"L3"}, - "crypto/sha1": {"L3"}, - "crypto/sha256": {"L3"}, - "crypto/sha512": {"L3"}, + "crypto/aes": {"L3"}, + "crypto/des": {"L3"}, + "crypto/hmac": {"L3"}, + "crypto/internal/randutil": {"io", "sync"}, + "crypto/md5": {"L3"}, + "crypto/rc4": {"L3"}, + "crypto/sha1": {"L3"}, + "crypto/sha256": {"L3"}, + "crypto/sha512": {"L3"}, "CRYPTO": { "crypto/aes", "crypto/des", "crypto/hmac", + "crypto/internal/randutil", "crypto/md5", "crypto/rc4", "crypto/sha1", @@ -355,7 +361,7 @@ var pkgDeps = map[string][]string{ // Random byte, number generation. // This would be part of core crypto except that it imports // math/big, which imports fmt. - "crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall", "internal/syscall/unix"}, + "crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall", "syscall/js", "internal/syscall/unix"}, // Mathematical crypto: dependencies on fmt (L4) and math/big. // We could avoid some of the fmt, but math/big imports fmt anyway. @@ -399,10 +405,10 @@ var pkgDeps = map[string][]string{ "context", "crypto/rand", "crypto/tls", + "golang_org/x/net/http/httpguts", + "golang_org/x/net/http/httpproxy", "golang_org/x/net/http2/hpack", "golang_org/x/net/idna", - "golang_org/x/net/lex/httplex", - "golang_org/x/net/proxy", "golang_org/x/text/unicode/norm", "golang_org/x/text/width", "internal/nettrace", @@ -410,20 +416,24 @@ var pkgDeps = map[string][]string{ "net/http/httptrace", "net/http/internal", "runtime/debug", + "syscall/js", }, "net/http/internal": {"L4"}, - "net/http/httptrace": {"context", "crypto/tls", "internal/nettrace", "net", "reflect", "time"}, + "net/http/httptrace": {"context", "crypto/tls", "internal/nettrace", "net", "net/textproto", "reflect", "time"}, // HTTP-using packages. "expvar": {"L4", "OS", "encoding/json", "net/http"}, "net/http/cgi": {"L4", "NET", "OS", "crypto/tls", "net/http", "regexp"}, "net/http/cookiejar": {"L4", "NET", "net/http"}, "net/http/fcgi": {"L4", "NET", "OS", "context", "net/http", "net/http/cgi"}, - "net/http/httptest": {"L4", "NET", "OS", "crypto/tls", "flag", "net/http", "net/http/internal", "crypto/x509"}, - "net/http/httputil": {"L4", "NET", "OS", "context", "net/http", "net/http/internal"}, - "net/http/pprof": {"L4", "OS", "html/template", "net/http", "runtime/pprof", "runtime/trace"}, - "net/rpc": {"L4", "NET", "encoding/gob", "html/template", "net/http"}, - "net/rpc/jsonrpc": {"L4", "NET", "encoding/json", "net/rpc"}, + "net/http/httptest": { + "L4", "NET", "OS", "crypto/tls", "flag", "net/http", "net/http/internal", "crypto/x509", + "golang_org/x/net/http/httpguts", + }, + "net/http/httputil": {"L4", "NET", "OS", "context", "net/http", "net/http/internal"}, + "net/http/pprof": {"L4", "OS", "html/template", "net/http", "runtime/pprof", "runtime/trace"}, + "net/rpc": {"L4", "NET", "encoding/gob", "html/template", "net/http"}, + "net/rpc/jsonrpc": {"L4", "NET", "encoding/json", "net/rpc"}, } // isMacro reports whether p is a package dependency macro diff --git a/libgo/go/go/build/doc.go b/libgo/go/go/build/doc.go index daa9a75..69613e3 100644 --- a/libgo/go/go/build/doc.go +++ b/libgo/go/go/build/doc.go @@ -107,6 +107,7 @@ // - "go1.8", from Go version 1.8 onward // - "go1.9", from Go version 1.9 onward // - "go1.10", from Go version 1.10 onward +// - "go1.11", from Go version 1.11 onward // - any additional words listed in ctxt.BuildTags // // If a file's name, after stripping the extension and a possible _test suffix, diff --git a/libgo/go/go/build/gc.go b/libgo/go/go/build/gc.go index e2be2cb..3025cd5 100644 --- a/libgo/go/go/build/gc.go +++ b/libgo/go/go/build/gc.go @@ -7,131 +7,11 @@ package build import ( - "os" - "os/exec" "path/filepath" "runtime" - "strings" - "sync" ) // getToolDir returns the default value of ToolDir. func getToolDir() string { return filepath.Join(runtime.GOROOT(), "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH) } - -// isStandardPackage is not used for the gc toolchain. -// However, this function may be called when using `go build -compiler=gccgo`. -func isStandardPackage(path string) bool { - return gccgoSearch.isStandard(path) -} - -// gccgoSearch holds the gccgo search directories. -type gccgoDirs struct { - once sync.Once - dirs []string -} - -// gccgoSearch is used to check whether a gccgo package exists in the -// standard library. -var gccgoSearch gccgoDirs - -// init finds the gccgo search directories. If this fails it leaves dirs == nil. -func (gd *gccgoDirs) init() { - gccgo := os.Getenv("GCCGO") - if gccgo == "" { - gccgo = "gccgo" - } - bin, err := exec.LookPath(gccgo) - if err != nil { - return - } - - allDirs, err := exec.Command(bin, "-print-search-dirs").Output() - if err != nil { - return - } - versionB, err := exec.Command(bin, "-dumpversion").Output() - if err != nil { - return - } - version := strings.TrimSpace(string(versionB)) - machineB, err := exec.Command(bin, "-dumpmachine").Output() - if err != nil { - return - } - machine := strings.TrimSpace(string(machineB)) - - dirsEntries := strings.Split(string(allDirs), "\n") - const prefix = "libraries: =" - var dirs []string - for _, dirEntry := range dirsEntries { - if strings.HasPrefix(dirEntry, prefix) { - dirs = filepath.SplitList(strings.TrimPrefix(dirEntry, prefix)) - break - } - } - if len(dirs) == 0 { - return - } - - var lastDirs []string - for _, dir := range dirs { - goDir := filepath.Join(dir, "go", version) - if fi, err := os.Stat(goDir); err == nil && fi.IsDir() { - gd.dirs = append(gd.dirs, goDir) - goDir = filepath.Join(goDir, machine) - if fi, err = os.Stat(goDir); err == nil && fi.IsDir() { - gd.dirs = append(gd.dirs, goDir) - } - } - if fi, err := os.Stat(dir); err == nil && fi.IsDir() { - lastDirs = append(lastDirs, dir) - } - } - gd.dirs = append(gd.dirs, lastDirs...) -} - -// isStandard returns whether path is a standard library for gccgo. -func (gd *gccgoDirs) isStandard(path string) bool { - // Quick check: if the first path component has a '.', it's not - // in the standard library. This skips most GOPATH directories. - i := strings.Index(path, "/") - if i < 0 { - i = len(path) - } - if strings.Contains(path[:i], ".") { - return false - } - - if path == "unsafe" { - // Special case. - return true - } - - gd.once.Do(gd.init) - if gd.dirs == nil { - // We couldn't find the gccgo search directories. - // Best guess, since the first component did not contain - // '.', is that this is a standard library package. - return true - } - - for _, dir := range gd.dirs { - full := filepath.Join(dir, path) - pkgdir, pkg := filepath.Split(full) - for _, p := range [...]string{ - full, - full + ".gox", - pkgdir + "lib" + pkg + ".so", - pkgdir + "lib" + pkg + ".a", - full + ".o", - } { - if fi, err := os.Stat(p); err == nil && !fi.IsDir() { - return true - } - } - } - - return false -} diff --git a/libgo/go/go/build/gccgo.go b/libgo/go/go/build/gccgo.go index 59e089d..c6aac9a 100644 --- a/libgo/go/go/build/gccgo.go +++ b/libgo/go/go/build/gccgo.go @@ -12,9 +12,3 @@ import "runtime" func getToolDir() string { return envOr("GCCGOTOOLDIR", runtime.GCCGOTOOLDIR) } - -// isStandardPackage returns whether path names a standard library package. -// This uses a list generated at build time. -func isStandardPackage(path string) bool { - return stdpkg[path] -} diff --git a/libgo/go/go/build/testdata/doc/a_test.go b/libgo/go/go/build/testdata/doc/a_test.go new file mode 100644 index 0000000..1c07b56 --- /dev/null +++ b/libgo/go/go/build/testdata/doc/a_test.go @@ -0,0 +1,2 @@ +// Doc from xtests +package doc_test diff --git a/libgo/go/go/build/testdata/doc/b_test.go b/libgo/go/go/build/testdata/doc/b_test.go new file mode 100644 index 0000000..0cf1605 --- /dev/null +++ b/libgo/go/go/build/testdata/doc/b_test.go @@ -0,0 +1 @@ +package doc_test diff --git a/libgo/go/go/build/testdata/doc/c_test.go b/libgo/go/go/build/testdata/doc/c_test.go new file mode 100644 index 0000000..1025707 --- /dev/null +++ b/libgo/go/go/build/testdata/doc/c_test.go @@ -0,0 +1 @@ +package doc diff --git a/libgo/go/go/build/testdata/doc/d_test.go b/libgo/go/go/build/testdata/doc/d_test.go new file mode 100644 index 0000000..ec19564 --- /dev/null +++ b/libgo/go/go/build/testdata/doc/d_test.go @@ -0,0 +1,2 @@ +// Doc from regular tests. +package doc diff --git a/libgo/go/go/build/testdata/doc/e.go b/libgo/go/go/build/testdata/doc/e.go new file mode 100644 index 0000000..1025707 --- /dev/null +++ b/libgo/go/go/build/testdata/doc/e.go @@ -0,0 +1 @@ +package doc diff --git a/libgo/go/go/build/testdata/doc/f.go b/libgo/go/go/build/testdata/doc/f.go new file mode 100644 index 0000000..ab1d0bc --- /dev/null +++ b/libgo/go/go/build/testdata/doc/f.go @@ -0,0 +1,2 @@ +// Correct +package doc diff --git a/libgo/go/go/constant/example_test.go b/libgo/go/go/constant/example_test.go new file mode 100644 index 0000000..21f9de7 --- /dev/null +++ b/libgo/go/go/constant/example_test.go @@ -0,0 +1,160 @@ +// Copyright 2018 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 gccgo_examples + +package constant_test + +import ( + "fmt" + "go/constant" + "go/token" + "sort" +) + +func Example_complexNumbers() { + // Create the complex number 2.3 + 5i. + ar := constant.MakeFloat64(2.3) + ai := constant.MakeImag(constant.MakeInt64(5)) + a := constant.BinaryOp(ar, token.ADD, ai) + + // Compute (2.3 + 5i) * 11. + b := constant.MakeUint64(11) + c := constant.BinaryOp(a, token.MUL, b) + + // Convert c into a complex128. + Ar, exact := constant.Float64Val(constant.Real(c)) + if !exact { + fmt.Printf("Could not represent real part %s exactly as float64\n", constant.Real(c)) + } + Ai, exact := constant.Float64Val(constant.Imag(c)) + if !exact { + fmt.Printf("Could not represent imaginary part %s as exactly as float64\n", constant.Imag(c)) + } + C := complex(Ar, Ai) + + fmt.Println("literal", 25.3+55i) + fmt.Println("go/constant", c) + fmt.Println("complex128", C) + + // Output: + // + // Could not represent real part 25.3 exactly as float64 + // literal (25.3+55i) + // go/constant (25.3 + 55i) + // complex128 (25.299999999999997+55i) +} + +func ExampleBinaryOp() { + // 11 / 0.5 + a := constant.MakeUint64(11) + b := constant.MakeFloat64(0.5) + c := constant.BinaryOp(a, token.QUO, b) + fmt.Println(c) + + // Output: 22 +} + +func ExampleUnaryOp() { + vs := []constant.Value{ + constant.MakeBool(true), + constant.MakeFloat64(2.7), + constant.MakeUint64(42), + } + + for i, v := range vs { + switch v.Kind() { + case constant.Bool: + vs[i] = constant.UnaryOp(token.NOT, v, 0) + + case constant.Float: + vs[i] = constant.UnaryOp(token.SUB, v, 0) + + case constant.Int: + // Use 16-bit precision. + // This would be equivalent to ^uint16(v). + vs[i] = constant.UnaryOp(token.XOR, v, 16) + } + } + + for _, v := range vs { + fmt.Println(v) + } + + // Output: + // + // false + // -2.7 + // 65493 +} + +func ExampleCompare() { + vs := []constant.Value{ + constant.MakeString("Z"), + constant.MakeString("bacon"), + constant.MakeString("go"), + constant.MakeString("Frame"), + constant.MakeString("defer"), + constant.MakeFromLiteral(`"a"`, token.STRING, 0), + } + + sort.Slice(vs, func(i, j int) bool { + // Equivalent to vs[i] <= vs[j]. + return constant.Compare(vs[i], token.LEQ, vs[j]) + }) + + for _, v := range vs { + fmt.Println(constant.StringVal(v)) + } + + // Output: + // + // Frame + // Z + // a + // bacon + // defer + // go +} + +func ExampleSign() { + zero := constant.MakeInt64(0) + one := constant.MakeInt64(1) + negOne := constant.MakeInt64(-1) + + mkComplex := func(a, b constant.Value) constant.Value { + b = constant.MakeImag(b) + return constant.BinaryOp(a, token.ADD, b) + } + + vs := []constant.Value{ + negOne, + mkComplex(zero, negOne), + mkComplex(one, negOne), + mkComplex(negOne, one), + mkComplex(negOne, negOne), + zero, + mkComplex(zero, zero), + one, + mkComplex(zero, one), + mkComplex(one, one), + } + + for _, v := range vs { + fmt.Printf("% d %s\n", constant.Sign(v), v) + } + + // Output: + // + // -1 -1 + // -1 (0 + -1i) + // -1 (1 + -1i) + // -1 (-1 + 1i) + // -1 (-1 + -1i) + // 0 0 + // 0 (0 + 0i) + // 1 1 + // 1 (0 + 1i) + // 1 (1 + 1i) +} diff --git a/libgo/go/go/constant/value_test.go b/libgo/go/go/constant/value_test.go index 5ec4f4c..e6fca76 100644 --- a/libgo/go/go/constant/value_test.go +++ b/libgo/go/go/constant/value_test.go @@ -431,6 +431,7 @@ func TestUnknown(t *testing.T) { MakeBool(false), // token.ADD ok below, operation is never considered MakeString(""), MakeInt64(1), + MakeFromLiteral("''", token.CHAR, 0), MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT, 0), MakeFloat64(1.2), MakeImag(MakeFloat64(1.2)), diff --git a/libgo/go/go/doc/comment.go b/libgo/go/go/doc/comment.go index 4228e8c..a7430f4 100644 --- a/libgo/go/go/doc/comment.go +++ b/libgo/go/go/doc/comment.go @@ -231,8 +231,8 @@ func heading(line string) string { return "" } - // exclude lines with illegal characters - if strings.ContainsAny(line, ",.;:!?+*/=()[]{}_^°&§~%#@<\">\\") { + // exclude lines with illegal characters. we allow "()," + if strings.ContainsAny(line, ";:!?+*/=[]{}_^°&§~%#@<\">\\") { return "" } @@ -248,6 +248,18 @@ func heading(line string) string { b = b[i+2:] } + // allow "." when followed by non-space + for b := line; ; { + i := strings.IndexRune(b, '.') + if i < 0 { + break + } + if i+1 >= len(b) || b[i+1] == ' ' { + return "" // not followed by non-space + } + b = b[i+1:] + } + return line } @@ -281,7 +293,7 @@ func anchorID(line string) string { // a single paragraph. There is one exception to the rule: a span that // consists of a single line, is followed by another paragraph span, // begins with a capital letter, and contains no punctuation -// is formatted as a heading. +// other than parentheses and commas is formatted as a heading. // // A span of indented lines is converted into a <pre> block, // with the common indent prefix removed. diff --git a/libgo/go/go/doc/example.go b/libgo/go/go/doc/example.go index a89f29b..5b40bb0 100644 --- a/libgo/go/go/doc/example.go +++ b/libgo/go/go/doc/example.go @@ -56,7 +56,7 @@ func Examples(files ...*ast.File) []*Example { continue } f, ok := decl.(*ast.FuncDecl) - if !ok { + if !ok || f.Recv != nil { continue } numDecl++ @@ -77,7 +77,7 @@ func Examples(files ...*ast.File) []*Example { Name: name[len("Example"):], Doc: doc, Code: f.Body, - Play: playExample(file, f.Body), + Play: playExample(file, f), Comments: file.Comments, Output: output, Unordered: unordered, @@ -140,27 +140,39 @@ func isTest(name, prefix string) bool { // playExample synthesizes a new *ast.File based on the provided // file with the provided function body as the body of main. -func playExample(file *ast.File, body *ast.BlockStmt) *ast.File { +func playExample(file *ast.File, f *ast.FuncDecl) *ast.File { + body := f.Body + if !strings.HasSuffix(file.Name.Name, "_test") { // We don't support examples that are part of the // greater package (yet). return nil } - // Find top-level declarations in the file. - topDecls := make(map[*ast.Object]bool) + // Collect top-level declarations in the file. + topDecls := make(map[*ast.Object]ast.Decl) + typMethods := make(map[string][]ast.Decl) + for _, decl := range file.Decls { switch d := decl.(type) { case *ast.FuncDecl: - topDecls[d.Name.Obj] = true + if d.Recv == nil { + topDecls[d.Name.Obj] = d + } else { + if len(d.Recv.List) == 1 { + t := d.Recv.List[0].Type + tname, _ := baseTypeName(t) + typMethods[tname] = append(typMethods[tname], d) + } + } case *ast.GenDecl: for _, spec := range d.Specs { switch s := spec.(type) { case *ast.TypeSpec: - topDecls[s.Name.Obj] = true + topDecls[s.Name.Obj] = d case *ast.ValueSpec: - for _, id := range s.Names { - topDecls[id.Obj] = true + for _, name := range s.Names { + topDecls[name.Obj] = d } } } @@ -169,36 +181,59 @@ func playExample(file *ast.File, body *ast.BlockStmt) *ast.File { // Find unresolved identifiers and uses of top-level declarations. unresolved := make(map[string]bool) - usesTopDecl := false + var depDecls []ast.Decl + hasDepDecls := make(map[ast.Decl]bool) + var inspectFunc func(ast.Node) bool inspectFunc = func(n ast.Node) bool { - // For selector expressions, only inspect the left hand side. - // (For an expression like fmt.Println, only add "fmt" to the - // set of unresolved names, not "Println".) - if e, ok := n.(*ast.SelectorExpr); ok { + switch e := n.(type) { + case *ast.Ident: + if e.Obj == nil && e.Name != "_" { + unresolved[e.Name] = true + } else if d := topDecls[e.Obj]; d != nil { + if !hasDepDecls[d] { + hasDepDecls[d] = true + depDecls = append(depDecls, d) + } + } + return true + case *ast.SelectorExpr: + // For selector expressions, only inspect the left hand side. + // (For an expression like fmt.Println, only add "fmt" to the + // set of unresolved names, not "Println".) ast.Inspect(e.X, inspectFunc) return false - } - // For key value expressions, only inspect the value - // as the key should be resolved by the type of the - // composite literal. - if e, ok := n.(*ast.KeyValueExpr); ok { + case *ast.KeyValueExpr: + // For key value expressions, only inspect the value + // as the key should be resolved by the type of the + // composite literal. ast.Inspect(e.Value, inspectFunc) return false } - if id, ok := n.(*ast.Ident); ok { - if id.Obj == nil { - unresolved[id.Name] = true - } else if topDecls[id.Obj] { - usesTopDecl = true - } - } return true } ast.Inspect(body, inspectFunc) - if usesTopDecl { - // We don't support examples that are not self-contained (yet). - return nil + for i := 0; i < len(depDecls); i++ { + switch d := depDecls[i].(type) { + case *ast.FuncDecl: + ast.Inspect(d.Body, inspectFunc) + case *ast.GenDecl: + for _, spec := range d.Specs { + switch s := spec.(type) { + case *ast.TypeSpec: + ast.Inspect(s.Type, inspectFunc) + + depDecls = append(depDecls, typMethods[s.Name.Name]...) + case *ast.ValueSpec: + if s.Type != nil { + ast.Inspect(s.Type, inspectFunc) + } + for _, val := range s.Values { + ast.Inspect(val, inspectFunc) + } + } + } + } } // Remove predeclared identifiers from unresolved list. @@ -261,6 +296,20 @@ func playExample(file *ast.File, body *ast.BlockStmt) *ast.File { // end position. body, comments = stripOutputComment(body, comments) + // Include documentation belonging to dependent declarations. + for _, d := range depDecls { + switch d := d.(type) { + case *ast.GenDecl: + if d.Doc != nil { + comments = append(comments, d.Doc) + } + case *ast.FuncDecl: + if d.Doc != nil { + comments = append(comments, d.Doc) + } + } + } + // Synthesize import declaration. importDecl := &ast.GenDecl{ Tok: token.IMPORT, @@ -279,14 +328,27 @@ func playExample(file *ast.File, body *ast.BlockStmt) *ast.File { // Synthesize main function. funcDecl := &ast.FuncDecl{ Name: ast.NewIdent("main"), - Type: &ast.FuncType{Params: &ast.FieldList{}}, // FuncType.Params must be non-nil + Type: f.Type, Body: body, } + decls := make([]ast.Decl, 0, 2+len(depDecls)) + decls = append(decls, importDecl) + decls = append(decls, depDecls...) + decls = append(decls, funcDecl) + + sort.Slice(decls, func(i, j int) bool { + return decls[i].Pos() < decls[j].Pos() + }) + + sort.Slice(comments, func(i, j int) bool { + return comments[i].Pos() < comments[j].Pos() + }) + // Synthesize file. return &ast.File{ Name: ast.NewIdent("main"), - Decls: []ast.Decl{importDecl, funcDecl}, + Decls: decls, Comments: comments, } } diff --git a/libgo/go/go/doc/example_test.go b/libgo/go/go/doc/example_test.go index e154ea8..552a51b 100644 --- a/libgo/go/go/doc/example_test.go +++ b/libgo/go/go/doc/example_test.go @@ -6,6 +6,7 @@ package doc_test import ( "bytes" + "go/ast" "go/doc" "go/format" "go/parser" @@ -21,6 +22,7 @@ import ( "flag" "fmt" "log" + "sort" "os/exec" ) @@ -67,6 +69,46 @@ var keyValueTopDecl = struct { func ExampleKeyValueTopDecl() { fmt.Print(keyValueTopDecl) + // Output: a: "B", b: 2 +} + +// Person represents a person by name and age. +type Person struct { + Name string + Age int +} + +// String returns a string representation of the Person. +func (p Person) String() string { + return fmt.Sprintf("%s: %d", p.Name, p.Age) +} + +// ByAge implements sort.Interface for []Person based on +// the Age field. +type ByAge []Person + +// Len returns the number of elements in ByAge. +func (a (ByAge)) Len() int { return len(a) } + +// Swap swaps the elements in ByAge. +func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age } + +// people is the array of Person +var people = []Person{ + {"Bob", 31}, + {"John", 42}, + {"Michael", 17}, + {"Jenny", 26}, +} + +func ExampleSort() { + fmt.Println(people) + sort.Sort(ByAge(people)) + fmt.Println(people) + // Output: + // [Bob: 31 John: 42 Michael: 17 Jenny: 26] + // [Michael: 17 Jenny: 26 Bob: 31 John: 42] } ` @@ -93,8 +135,14 @@ var exampleTestCases = []struct { Output: "Name: \"play\"\n", }, { - Name: "KeyValueTopDecl", - Play: "<nil>", + Name: "KeyValueTopDecl", + Play: exampleKeyValueTopDeclPlay, + Output: "a: \"B\", b: 2\n", + }, + { + Name: "Sort", + Play: exampleSortPlay, + Output: "[Bob: 31 John: 42 Michael: 17 Jenny: 26]\n[Michael: 17 Jenny: 26 Bob: 31 John: 42]\n", }, } @@ -158,6 +206,69 @@ func main() { } ` +const exampleKeyValueTopDeclPlay = `package main + +import ( + "fmt" +) + +var keyValueTopDecl = struct { + a string + b int +}{ + a: "B", + b: 2, +} + +func main() { + fmt.Print(keyValueTopDecl) +} +` + +const exampleSortPlay = `package main + +import ( + "fmt" + "sort" +) + +// Person represents a person by name and age. +type Person struct { + Name string + Age int +} + +// String returns a string representation of the Person. +func (p Person) String() string { + return fmt.Sprintf("%s: %d", p.Name, p.Age) +} + +// ByAge implements sort.Interface for []Person based on +// the Age field. +type ByAge []Person + +// Len returns the number of elements in ByAge. +func (a ByAge) Len() int { return len(a) } + +// Swap swaps the elements in ByAge. +func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age } + +// people is the array of Person +var people = []Person{ + {"Bob", 31}, + {"John", 42}, + {"Michael", 17}, + {"Jenny", 26}, +} + +func main() { + fmt.Println(people) + sort.Sort(ByAge(people)) + fmt.Println(people) +} +` + func TestExamples(t *testing.T) { fset := token.NewFileSet() file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleTestFile), parser.ParseComments) @@ -170,16 +281,7 @@ func TestExamples(t *testing.T) { t.Errorf("got Name == %q, want %q", e.Name, c.Name) } if w := c.Play; w != "" { - var g string // hah - if e.Play == nil { - g = "<nil>" - } else { - var buf bytes.Buffer - if err := format.Node(&buf, fset, e.Play); err != nil { - t.Fatal(err) - } - g = buf.String() - } + g := formatFile(t, fset, e.Play) if g != w { t.Errorf("%s: got Play == %q, want %q", c.Name, g, w) } @@ -189,3 +291,73 @@ func TestExamples(t *testing.T) { } } } + +const exampleWholeFile = `package foo_test + +type X int + +func (X) Foo() { +} + +func (X) TestBlah() { +} + +func (X) BenchmarkFoo() { +} + +func Example() { + fmt.Println("Hello, world!") + // Output: Hello, world! +} +` + +const exampleWholeFileOutput = `package main + +type X int + +func (X) Foo() { +} + +func (X) TestBlah() { +} + +func (X) BenchmarkFoo() { +} + +func main() { + fmt.Println("Hello, world!") +} +` + +func TestExamplesWholeFile(t *testing.T) { + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleWholeFile), parser.ParseComments) + if err != nil { + t.Fatal(err) + } + es := doc.Examples(file) + if len(es) != 1 { + t.Fatalf("wrong number of examples; got %d want 1", len(es)) + } + e := es[0] + if e.Name != "" { + t.Errorf("got Name == %q, want %q", e.Name, "") + } + if g, w := formatFile(t, fset, e.Play), exampleWholeFileOutput; g != w { + t.Errorf("got Play == %q, want %q", g, w) + } + if g, w := e.Output, "Hello, world!\n"; g != w { + t.Errorf("got Output == %q, want %q", g, w) + } +} + +func formatFile(t *testing.T, fset *token.FileSet, n *ast.File) string { + if n == nil { + return "<nil>" + } + var buf bytes.Buffer + if err := format.Node(&buf, fset, n); err != nil { + t.Fatal(err) + } + return buf.String() +} diff --git a/libgo/go/go/doc/exports.go b/libgo/go/go/doc/exports.go index 40cbb22..5f99bf7 100644 --- a/libgo/go/go/doc/exports.go +++ b/libgo/go/go/doc/exports.go @@ -25,6 +25,49 @@ func filterIdentList(list []*ast.Ident) []*ast.Ident { return list[0:j] } +var underscore = ast.NewIdent("_") + +func filterCompositeLit(lit *ast.CompositeLit, filter Filter, export bool) { + n := len(lit.Elts) + lit.Elts = filterExprList(lit.Elts, filter, export) + if len(lit.Elts) < n { + lit.Incomplete = true + } +} + +func filterExprList(list []ast.Expr, filter Filter, export bool) []ast.Expr { + j := 0 + for _, exp := range list { + switch x := exp.(type) { + case *ast.CompositeLit: + filterCompositeLit(x, filter, export) + case *ast.KeyValueExpr: + if x, ok := x.Key.(*ast.Ident); ok && !filter(x.Name) { + continue + } + if x, ok := x.Value.(*ast.CompositeLit); ok { + filterCompositeLit(x, filter, export) + } + } + list[j] = exp + j++ + } + return list[0:j] +} + +// updateIdentList replaces all unexported identifiers with underscore +// and reports whether at least one exported name exists. +func updateIdentList(list []*ast.Ident) (hasExported bool) { + for i, x := range list { + if ast.IsExported(x.Name) { + hasExported = true + } else { + list[i] = underscore + } + } + return hasExported +} + // hasExportedName reports whether list contains any exported names. // func hasExportedName(list []*ast.Ident) bool { @@ -156,10 +199,24 @@ func (r *reader) filterSpec(spec ast.Spec) bool { // always keep imports so we can collect them return true case *ast.ValueSpec: - s.Names = filterIdentList(s.Names) - if len(s.Names) > 0 { - r.filterType(nil, s.Type) - return true + s.Values = filterExprList(s.Values, ast.IsExported, true) + if len(s.Values) > 0 || s.Type == nil && len(s.Values) == 0 { + // If there are values declared on RHS, just replace the unexported + // identifiers on the LHS with underscore, so that it matches + // the sequence of expression on the RHS. + // + // Similarly, if there are no type and values, then this expression + // must be following an iota expression, where order matters. + if updateIdentList(s.Names) { + r.filterType(nil, s.Type) + return true + } + } else { + s.Names = filterIdentList(s.Names) + if len(s.Names) > 0 { + r.filterType(nil, s.Type) + return true + } } case *ast.TypeSpec: if name := s.Name.Name; ast.IsExported(name) { diff --git a/libgo/go/go/doc/reader.go b/libgo/go/go/doc/reader.go index 140f587..21c0292 100644 --- a/libgo/go/go/doc/reader.go +++ b/libgo/go/go/doc/reader.go @@ -104,6 +104,8 @@ func baseTypeName(x ast.Expr) (name string, imported bool) { // assume type is imported return t.Sel.Name, true } + case *ast.ParenExpr: + return baseTypeName(t.X) case *ast.StarExpr: return baseTypeName(t.X) } @@ -389,27 +391,33 @@ func (r *reader) readFunc(fun *ast.FuncDecl) { return } - // associate factory functions with the first visible result type, if any + // Associate factory functions with the first visible result type, if that + // is the only type returned. if fun.Type.Results.NumFields() >= 1 { - res := fun.Type.Results.List[0] - if len(res.Names) <= 1 { + var typ *namedType // type to associate the function with + numResultTypes := 0 + for _, res := range fun.Type.Results.List { // exactly one (named or anonymous) result associated // with the first type in result signature (there may // be more than one result) factoryType := res.Type - if t, ok := factoryType.(*ast.ArrayType); ok && t.Len == nil { - // We consider functions that return slices of type T (or - // pointers to T) as factory functions of T. + if t, ok := factoryType.(*ast.ArrayType); ok { + // We consider functions that return slices or arrays of type + // T (or pointers to T) as factory functions of T. factoryType = t.Elt } if n, imp := baseTypeName(factoryType); !imp && r.isVisible(n) { - if typ := r.lookupType(n); typ != nil { - // associate function with typ - typ.funcs.set(fun) - return + if t := r.lookupType(n); t != nil { + typ = t + numResultTypes++ } } } + // If there is exactly one result type, associate the function with that type. + if numResultTypes == 1 { + typ.funcs.set(fun) + return + } } // just an ordinary function diff --git a/libgo/go/go/doc/testdata/g.0.golden b/libgo/go/go/doc/testdata/g.0.golden new file mode 100644 index 0000000..487cf06 --- /dev/null +++ b/libgo/go/go/doc/testdata/g.0.golden @@ -0,0 +1,32 @@ +// The package g is a go/doc test for mixed exported/unexported ... +PACKAGE g + +IMPORTPATH + testdata/g + +FILENAMES + testdata/g.go + +CONSTANTS + // + const ( + A, _ = iota, iota + _, D + E, _ + G, H + ) + + +VARIABLES + // + var ( + _, C2, _ = 1, 2, 3 + C4, _, C6 = 4, 5, 6 + _, C8, _ = 7, 8, 9 + ) + + // + var ( + _, X = f() + ) + diff --git a/libgo/go/go/doc/testdata/g.1.golden b/libgo/go/go/doc/testdata/g.1.golden new file mode 100644 index 0000000..438441a --- /dev/null +++ b/libgo/go/go/doc/testdata/g.1.golden @@ -0,0 +1,34 @@ +// The package g is a go/doc test for mixed exported/unexported ... +PACKAGE g + +IMPORTPATH + testdata/g + +FILENAMES + testdata/g.go + +CONSTANTS + // + const ( + A, b = iota, iota + c, D + E, f + G, H + ) + + +VARIABLES + // + var ( + c1, C2, c3 = 1, 2, 3 + C4, c5, C6 = 4, 5, 6 + c7, C8, c9 = 7, 8, 9 + xx, yy, zz = 0, 0, 0 // all unexported and hidden + ) + + // + var ( + x, X = f() + y, z = f() + ) + diff --git a/libgo/go/go/doc/testdata/g.2.golden b/libgo/go/go/doc/testdata/g.2.golden new file mode 100644 index 0000000..487cf06 --- /dev/null +++ b/libgo/go/go/doc/testdata/g.2.golden @@ -0,0 +1,32 @@ +// The package g is a go/doc test for mixed exported/unexported ... +PACKAGE g + +IMPORTPATH + testdata/g + +FILENAMES + testdata/g.go + +CONSTANTS + // + const ( + A, _ = iota, iota + _, D + E, _ + G, H + ) + + +VARIABLES + // + var ( + _, C2, _ = 1, 2, 3 + C4, _, C6 = 4, 5, 6 + _, C8, _ = 7, 8, 9 + ) + + // + var ( + _, X = f() + ) + diff --git a/libgo/go/go/doc/testdata/g.go b/libgo/go/go/doc/testdata/g.go new file mode 100644 index 0000000..ceeb417 --- /dev/null +++ b/libgo/go/go/doc/testdata/g.go @@ -0,0 +1,25 @@ +// Copyright 2018 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 package g is a go/doc test for mixed exported/unexported values. +package g + +const ( + A, b = iota, iota + c, D + E, f + G, H +) + +var ( + c1, C2, c3 = 1, 2, 3 + C4, c5, C6 = 4, 5, 6 + c7, C8, c9 = 7, 8, 9 + xx, yy, zz = 0, 0, 0 // all unexported and hidden +) + +var ( + x, X = f() + y, z = f() +) diff --git a/libgo/go/go/doc/testdata/issue12839.0.golden b/libgo/go/go/doc/testdata/issue12839.0.golden new file mode 100644 index 0000000..76c2855 --- /dev/null +++ b/libgo/go/go/doc/testdata/issue12839.0.golden @@ -0,0 +1,33 @@ +// Package issue12839 is a go/doc test to test association of a ... +PACKAGE issue12839 + +IMPORTPATH + testdata/issue12839 + +IMPORTS + p + +FILENAMES + testdata/issue12839.go + +FUNCTIONS + // F1 should not be associated with T1 + func F1() (*T1, *T2) + + // F4 should not be associated with a type (same as F1) + func F4() (a T1, b T2) + + +TYPES + // + type T1 struct{} + + // F2 should be associated with T1 + func F2() (a, b, c T1) + + // F3 should be associated with T1 because b.T3 is from a ... + func F3() (a T1, b p.T3) + + // + type T2 struct{} + diff --git a/libgo/go/go/doc/testdata/issue12839.1.golden b/libgo/go/go/doc/testdata/issue12839.1.golden new file mode 100644 index 0000000..b0a327f --- /dev/null +++ b/libgo/go/go/doc/testdata/issue12839.1.golden @@ -0,0 +1,36 @@ +// Package issue12839 is a go/doc test to test association of a ... +PACKAGE issue12839 + +IMPORTPATH + testdata/issue12839 + +IMPORTS + p + +FILENAMES + testdata/issue12839.go + +FUNCTIONS + // F1 should not be associated with T1 + func F1() (*T1, *T2) + + // F4 should not be associated with a type (same as F1) + func F4() (a T1, b T2) + + +TYPES + // + type T1 struct{} + + // F2 should be associated with T1 + func F2() (a, b, c T1) + + // F3 should be associated with T1 because b.T3 is from a ... + func F3() (a T1, b p.T3) + + // + func (t T1) hello() string + + // + type T2 struct{} + diff --git a/libgo/go/go/doc/testdata/issue12839.2.golden b/libgo/go/go/doc/testdata/issue12839.2.golden new file mode 100644 index 0000000..76c2855 --- /dev/null +++ b/libgo/go/go/doc/testdata/issue12839.2.golden @@ -0,0 +1,33 @@ +// Package issue12839 is a go/doc test to test association of a ... +PACKAGE issue12839 + +IMPORTPATH + testdata/issue12839 + +IMPORTS + p + +FILENAMES + testdata/issue12839.go + +FUNCTIONS + // F1 should not be associated with T1 + func F1() (*T1, *T2) + + // F4 should not be associated with a type (same as F1) + func F4() (a T1, b T2) + + +TYPES + // + type T1 struct{} + + // F2 should be associated with T1 + func F2() (a, b, c T1) + + // F3 should be associated with T1 because b.T3 is from a ... + func F3() (a T1, b p.T3) + + // + type T2 struct{} + diff --git a/libgo/go/go/doc/testdata/issue12839.go b/libgo/go/go/doc/testdata/issue12839.go new file mode 100644 index 0000000..500d495 --- /dev/null +++ b/libgo/go/go/doc/testdata/issue12839.go @@ -0,0 +1,38 @@ +// Copyright 2018 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 issue12839 is a go/doc test to test association of a function +// that returns multiple types. +// See golang.org/issue/12839. +package issue12839 + +import "p" + +type T1 struct{} + +type T2 struct{} + +func (t T1) hello() string { + return "hello" +} + +// F1 should not be associated with T1 +func F1() (*T1, *T2) { + return &T1{}, &T2{} +} + +// F2 should be associated with T1 +func F2() (a, b, c T1) { + return T1{}, T1{}, T1{} +} + +// F3 should be associated with T1 because b.T3 is from a different package +func F3() (a T1, b p.T3) { + return T1{}, p.T3{} +} + +// F4 should not be associated with a type (same as F1) +func F4() (a T1, b T2) { + return T1{}, T2{} +} diff --git a/libgo/go/go/doc/testdata/issue18063.2.golden b/libgo/go/go/doc/testdata/issue22856.0.golden index 0afbc16..a88f43f 100644 --- a/libgo/go/go/doc/testdata/issue18063.2.golden +++ b/libgo/go/go/doc/testdata/issue22856.0.golden @@ -1,19 +1,13 @@ // -PACKAGE issue18063 +PACKAGE issue22856 IMPORTPATH - testdata/issue18063 + testdata/issue22856 FILENAMES - testdata/issue18063.go + testdata/issue22856.go FUNCTIONS - // NewArray is not a factory function because arrays of type T are ... - func NewArray() [1]T - - // NewPointerArray is not a factory function because arrays of ... - func NewPointerArray() [1]*T - // NewPointerSliceOfSlice is not a factory function because slices ... func NewPointerSliceOfSlice() [][]*T @@ -32,9 +26,15 @@ TYPES func New() T // + func NewArray() [1]T + + // func NewPointer() *T // + func NewPointerArray() [1]*T + + // func NewPointerOfPointer() **T // diff --git a/libgo/go/go/doc/testdata/issue18063.1.golden b/libgo/go/go/doc/testdata/issue22856.1.golden index 0afbc16..a88f43f 100644 --- a/libgo/go/go/doc/testdata/issue18063.1.golden +++ b/libgo/go/go/doc/testdata/issue22856.1.golden @@ -1,19 +1,13 @@ // -PACKAGE issue18063 +PACKAGE issue22856 IMPORTPATH - testdata/issue18063 + testdata/issue22856 FILENAMES - testdata/issue18063.go + testdata/issue22856.go FUNCTIONS - // NewArray is not a factory function because arrays of type T are ... - func NewArray() [1]T - - // NewPointerArray is not a factory function because arrays of ... - func NewPointerArray() [1]*T - // NewPointerSliceOfSlice is not a factory function because slices ... func NewPointerSliceOfSlice() [][]*T @@ -32,9 +26,15 @@ TYPES func New() T // + func NewArray() [1]T + + // func NewPointer() *T // + func NewPointerArray() [1]*T + + // func NewPointerOfPointer() **T // diff --git a/libgo/go/go/doc/testdata/issue18063.0.golden b/libgo/go/go/doc/testdata/issue22856.2.golden index 0afbc16..a88f43f 100644 --- a/libgo/go/go/doc/testdata/issue18063.0.golden +++ b/libgo/go/go/doc/testdata/issue22856.2.golden @@ -1,19 +1,13 @@ // -PACKAGE issue18063 +PACKAGE issue22856 IMPORTPATH - testdata/issue18063 + testdata/issue22856 FILENAMES - testdata/issue18063.go + testdata/issue22856.go FUNCTIONS - // NewArray is not a factory function because arrays of type T are ... - func NewArray() [1]T - - // NewPointerArray is not a factory function because arrays of ... - func NewPointerArray() [1]*T - // NewPointerSliceOfSlice is not a factory function because slices ... func NewPointerSliceOfSlice() [][]*T @@ -32,9 +26,15 @@ TYPES func New() T // + func NewArray() [1]T + + // func NewPointer() *T // + func NewPointerArray() [1]*T + + // func NewPointerOfPointer() **T // diff --git a/libgo/go/go/doc/testdata/issue18063.go b/libgo/go/go/doc/testdata/issue22856.go index 1193af5..f456998 100644 --- a/libgo/go/go/doc/testdata/issue18063.go +++ b/libgo/go/go/doc/testdata/issue22856.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 issue18063 +package issue22856 type T struct{} @@ -11,14 +11,8 @@ func NewPointer() *T { return &T{} } func NewPointerSlice() []*T { return []*T{&T{}} } func NewSlice() []T { return []T{T{}} } func NewPointerOfPointer() **T { x := &T{}; return &x } - -// NewArray is not a factory function because arrays of type T are not -// factory functions of type T. -func NewArray() [1]T { return [1]T{T{}} } - -// NewPointerArray is not a factory function because arrays of type *T are not -// factory functions of type T. -func NewPointerArray() [1]*T { return [1]*T{&T{}} } +func NewArray() [1]T { return [1]T{T{}} } +func NewPointerArray() [1]*T { return [1]*T{&T{}} } // NewSliceOfSlice is not a factory function because slices of a slice of // type *T are not factory functions of type T. diff --git a/libgo/go/go/format/example_test.go b/libgo/go/go/format/example_test.go new file mode 100644 index 0000000..3d72997 --- /dev/null +++ b/libgo/go/go/format/example_test.go @@ -0,0 +1,41 @@ +// Copyright 2018 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 gccgo_examples + +package format_test + +import ( + "bytes" + "fmt" + "go/format" + "go/parser" + "go/token" + "log" +) + +func ExampleNode() { + const expr = "(6+2*3)/4" + + // parser.ParseExpr parses the argument and returns the + // corresponding ast.Node. + node, err := parser.ParseExpr(expr) + if err != nil { + log.Fatal(err) + } + + // Create a FileSet for node. Since the node does not come + // from a real source file, fset will be empty. + fset := token.NewFileSet() + + var buf bytes.Buffer + err = format.Node(&buf, fset, node) + if err != nil { + log.Fatal(err) + } + + fmt.Println(buf.String()) + + // Output: (6 + 2*3) / 4 +} diff --git a/libgo/go/go/format/format.go b/libgo/go/go/format/format.go index cad5958..9aa28fc 100644 --- a/libgo/go/go/format/format.go +++ b/libgo/go/go/format/format.go @@ -3,6 +3,15 @@ // license that can be found in the LICENSE file. // Package format implements standard formatting of Go source. +// +// Note that formatting of Go source code changes over time, so tools relying on +// consistent formatting should execute a specific version of the gofmt binary +// instead of using this package. That way, the formatting will be stable, and +// the tools won't need to be recompiled each time gofmt changes. +// +// For example, pre-submit checks that use this package directly would behave +// differently depending on what Go version each developer uses, causing the +// check to be inherently fragile. package format import ( @@ -79,10 +88,6 @@ func Node(dst io.Writer, fset *token.FileSet, node interface{}) error { // space as src), and the result is indented by the same amount as the first // line of src containing code. Imports are not sorted for partial source files. // -// Caution: Tools relying on consistent formatting based on the installed -// version of gofmt (for instance, such as for presubmit checks) should -// execute that gofmt binary instead of calling Source. -// func Source(src []byte) ([]byte, error) { fset := token.NewFileSet() file, sourceAdj, indentAdj, err := parse(fset, "", src, true) diff --git a/libgo/go/go/format/format_test.go b/libgo/go/go/format/format_test.go index 72b8d5a..b5817a5 100644 --- a/libgo/go/go/format/format_test.go +++ b/libgo/go/go/format/format_test.go @@ -6,11 +6,9 @@ package format import ( "bytes" - "fmt" "go/parser" "go/token" "io/ioutil" - "log" "strings" "testing" ) @@ -145,28 +143,3 @@ func TestPartial(t *testing.T) { } } } - -func ExampleNode() { - const expr = "(6+2*3)/4" - - // parser.ParseExpr parses the argument and returns the - // corresponding ast.Node. - node, err := parser.ParseExpr(expr) - if err != nil { - log.Fatal(err) - } - - // Create a FileSet for node. Since the node does not come - // from a real source file, fset will be empty. - fset := token.NewFileSet() - - var buf bytes.Buffer - err = Node(&buf, fset, node) - if err != nil { - log.Fatal(err) - } - - fmt.Println(buf.String()) - - // Output: (6 + 2*3) / 4 -} diff --git a/libgo/go/go/internal/gccgoimporter/ar.go b/libgo/go/go/internal/gccgoimporter/ar.go new file mode 100644 index 0000000..ebd08b8 --- /dev/null +++ b/libgo/go/go/internal/gccgoimporter/ar.go @@ -0,0 +1,148 @@ +// Copyright 2018 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 gccgoimporter + +import ( + "bytes" + "debug/elf" + "errors" + "fmt" + "io" + "strconv" + "strings" +) + +// Magic strings for different archive file formats. +const ( + armag = "!<arch>\n" + armagt = "!<thin>\n" + armagb = "<bigaf>\n" +) + +// Offsets and sizes for fields in a standard archive header. +const ( + arNameOff = 0 + arNameSize = 16 + arDateOff = arNameOff + arNameSize + arDateSize = 12 + arUIDOff = arDateOff + arDateSize + arUIDSize = 6 + arGIDOff = arUIDOff + arUIDSize + arGIDSize = 6 + arModeOff = arGIDOff + arGIDSize + arModeSize = 8 + arSizeOff = arModeOff + arModeSize + arSizeSize = 10 + arFmagOff = arSizeOff + arSizeSize + arFmagSize = 2 + + arHdrSize = arFmagOff + arFmagSize +) + +// The contents of the fmag field of a standard archive header. +const arfmag = "`\n" + +// arExportData takes an archive file and returns a ReadSeeker for the +// export data in that file. This assumes that there is only one +// object in the archive containing export data, which is not quite +// what gccgo does; gccgo concatenates together all the export data +// for all the objects in the file. In practice that case does not arise. +func arExportData(archive io.ReadSeeker) (io.ReadSeeker, error) { + if _, err := archive.Seek(0, io.SeekStart); err != nil { + return nil, err + } + + var buf [len(armag)]byte + if _, err := archive.Read(buf[:]); err != nil { + return nil, err + } + + switch string(buf[:]) { + case armag: + return standardArExportData(archive) + case armagt: + return nil, errors.New("unsupported thin archive") + case armagb: + return nil, errors.New("unsupported AIX big archive") + default: + return nil, fmt.Errorf("unrecognized archive file format %q", buf[:]) + } +} + +// standardArExportData returns export data form a standard archive. +func standardArExportData(archive io.ReadSeeker) (io.ReadSeeker, error) { + off := int64(len(armag)) + for { + var hdrBuf [arHdrSize]byte + if _, err := archive.Read(hdrBuf[:]); err != nil { + return nil, err + } + off += arHdrSize + + if bytes.Compare(hdrBuf[arFmagOff:arFmagOff+arFmagSize], []byte(arfmag)) != 0 { + return nil, fmt.Errorf("archive header format header (%q)", hdrBuf[:]) + } + + size, err := strconv.ParseInt(strings.TrimSpace(string(hdrBuf[arSizeOff:arSizeOff+arSizeSize])), 10, 64) + if err != nil { + return nil, fmt.Errorf("error parsing size in archive header (%q): %v", hdrBuf[:], err) + } + + fn := hdrBuf[arNameOff : arNameOff+arNameSize] + if fn[0] == '/' && (fn[1] == ' ' || fn[1] == '/' || bytes.Compare(fn[:8], []byte("/SYM64/ ")) == 0) { + // Archive symbol table or extended name table, + // which we don't care about. + } else { + archiveAt := readerAtFromSeeker(archive) + ret, err := elfFromAr(io.NewSectionReader(archiveAt, off, size)) + if ret != nil || err != nil { + return ret, err + } + } + + if size&1 != 0 { + size++ + } + off += size + if _, err := archive.Seek(off, io.SeekStart); err != nil { + return nil, err + } + } +} + +// elfFromAr tries to get export data from an archive member as an ELF file. +// If there is no export data, this returns nil, nil. +func elfFromAr(member *io.SectionReader) (io.ReadSeeker, error) { + ef, err := elf.NewFile(member) + if err != nil { + return nil, err + } + sec := ef.Section(".go_export") + if sec == nil { + return nil, nil + } + return sec.Open(), nil +} + +// readerAtFromSeeker turns an io.ReadSeeker into an io.ReaderAt. +// This is only safe because there won't be any concurrent seeks +// while this code is executing. +func readerAtFromSeeker(rs io.ReadSeeker) io.ReaderAt { + if ret, ok := rs.(io.ReaderAt); ok { + return ret + } + return seekerReadAt{rs} +} + +type seekerReadAt struct { + seeker io.ReadSeeker +} + +func (sra seekerReadAt) ReadAt(p []byte, off int64) (int, error) { + if _, err := sra.seeker.Seek(off, io.SeekStart); err != nil { + return 0, err + } + return sra.seeker.Read(p) +} diff --git a/libgo/go/go/internal/gccgoimporter/importer.go b/libgo/go/go/internal/gccgoimporter/importer.go index ddaed40..7bc2afc 100644 --- a/libgo/go/go/internal/gccgoimporter/importer.go +++ b/libgo/go/go/internal/gccgoimporter/importer.go @@ -13,9 +13,7 @@ import ( "go/types" "io" "os" - "os/exec" "path/filepath" - "runtime" "strings" ) @@ -100,26 +98,9 @@ func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err e reader = f return - case archiveMagic, aixbigafMagic: - // TODO(pcc): Read the archive directly instead of using "ar". - f.Close() - closer = nil - var cmd *exec.Cmd - - if runtime.GOOS == "aix" && runtime.GOARCH == "ppc64" { - // AIX puts both 32-bit and 64-bit objects in the same archive. - // Tell the AIX "ar" command to only care about 64-bit objects. - cmd = exec.Command("ar", "-X64", "p", fpath) - } else { - cmd = exec.Command("ar", "p", fpath) - } - var out []byte - out, err = cmd.Output() - if err != nil { - return - } - - objreader = bytes.NewReader(out) + case archiveMagic: + reader, err = arExportData(f) + return default: objreader = f @@ -171,26 +152,29 @@ func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Impo var reader io.ReadSeeker var fpath string + var rc io.ReadCloser if lookup != nil { if p := imports[pkgpath]; p != nil && p.Complete() { return p, nil } - rc, err := lookup(pkgpath) - if err == nil && rc != nil { - defer rc.Close() - rs, ok := rc.(io.ReadSeeker) - if !ok { - return nil, fmt.Errorf("gccgo importer requires lookup to return an io.ReadSeeker, have %T", rc) - } - reader = rs - fpath = "<lookup " + pkgpath + ">" - // Take name from Name method (like on os.File) if present. - if n, ok := rc.(interface{ Name() string }); ok { - fpath = n.Name() - } + rc, err = lookup(pkgpath) + if err != nil { + return nil, err } } - if reader == nil { + if rc != nil { + defer rc.Close() + rs, ok := rc.(io.ReadSeeker) + if !ok { + return nil, fmt.Errorf("gccgo importer requires lookup to return an io.ReadSeeker, have %T", rc) + } + reader = rs + fpath = "<lookup " + pkgpath + ">" + // Take name from Name method (like on os.File) if present. + if n, ok := rc.(interface{ Name() string }); ok { + fpath = n.Name() + } + } else { fpath, err = findExportFile(searchpaths, pkgpath) if err != nil { return nil, err @@ -206,17 +190,24 @@ func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Impo reader = r } - var magic [4]byte - _, err = reader.Read(magic[:]) + var magics string + magics, err = readMagic(reader) if err != nil { return } - _, err = reader.Seek(0, io.SeekStart) - if err != nil { - return + + if magics == archiveMagic { + reader, err = arExportData(reader) + if err != nil { + return + } + magics, err = readMagic(reader) + if err != nil { + return + } } - switch string(magic[:]) { + switch magics { case gccgov1Magic, gccgov2Magic: var p parser p.init(fpath, reader, imports) @@ -247,9 +238,22 @@ func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Impo // } default: - err = fmt.Errorf("unrecognized magic string: %q", string(magic[:])) + err = fmt.Errorf("unrecognized magic string: %q", magics) } return } } + +// readMagic reads the four bytes at the start of a ReadSeeker and +// returns them as a string. +func readMagic(reader io.ReadSeeker) (string, error) { + var magic [4]byte + if _, err := reader.Read(magic[:]); err != nil { + return "", err + } + if _, err := reader.Seek(0, io.SeekStart); err != nil { + return "", err + } + return string(magic[:]), nil +} diff --git a/libgo/go/go/internal/gccgoimporter/importer_test.go b/libgo/go/go/internal/gccgoimporter/importer_test.go index 01ab47a..5a69968 100644 --- a/libgo/go/go/internal/gccgoimporter/importer_test.go +++ b/libgo/go/go/internal/gccgoimporter/importer_test.go @@ -101,6 +101,7 @@ var importerTests = [...]importerTest{ {pkgpath: "unicode", name: "IsUpper", want: "func IsUpper(r rune) bool"}, {pkgpath: "unicode", name: "MaxRune", want: "const MaxRune untyped rune", wantval: "1114111"}, {pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}}, + {pkgpath: "importsar", name: "Hello", want: "var Hello string"}, {pkgpath: "alias", name: "IntAlias2", want: "type IntAlias2 = Int"}, {pkgpath: "escapeinfo", name: "NewT", want: "func NewT(data []byte) *T"}, } diff --git a/libgo/go/go/internal/gccgoimporter/parser.go b/libgo/go/go/internal/gccgoimporter/parser.go index 4b3d899..9f8c19b 100644 --- a/libgo/go/go/internal/gccgoimporter/parser.go +++ b/libgo/go/go/internal/gccgoimporter/parser.go @@ -585,13 +585,13 @@ func (p *parser) parseInterfaceType(pkg *types.Package) types.Type { p.expectKeyword("interface") var methods []*types.Func - var typs []*types.Named + var embeddeds []types.Type p.expect('{') for p.tok != '}' && p.tok != scanner.EOF { if p.tok == '?' { p.next() - typs = append(typs, p.parseType(pkg).(*types.Named)) + embeddeds = append(embeddeds, p.parseType(pkg)) } else { method := p.parseFunc(pkg) methods = append(methods, method) @@ -600,7 +600,7 @@ func (p *parser) parseInterfaceType(pkg *types.Package) types.Type { } p.expect('}') - return types.NewInterface(methods, typs) + return types.NewInterfaceType(methods, embeddeds) } // PointerType = "*" ("any" | Type) . @@ -906,10 +906,3 @@ func (p *parser) parsePackage() *types.Package { p.pkg.MarkComplete() return p.pkg } - -// InitData = { InitDataDirective } . -func (p *parser) parseInitData() { - for p.tok != scanner.EOF { - p.parseInitDataDirective() - } -} diff --git a/libgo/go/go/internal/gcimporter/bimport.go b/libgo/go/go/internal/gcimporter/bimport.go index 23c1d2f..4e3023b 100644 --- a/libgo/go/go/internal/gcimporter/bimport.go +++ b/libgo/go/go/internal/gcimporter/bimport.go @@ -37,8 +37,7 @@ type importer struct { posInfoFormat bool prevFile string prevLine int - fset *token.FileSet - files map[string]*token.File + fake fakeFileSet // debugging support debugFormat bool @@ -51,12 +50,16 @@ type importer struct { // compromised, an error is returned. func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { // catch panics and return them as errors + const currentVersion = 6 + version := -1 // unknown version defer func() { if e := recover(); e != nil { - // The package (filename) causing the problem is added to this - // error by a wrapper in the caller (Import in gcimporter.go). // Return a (possibly nil or incomplete) package unchanged (see #16088). - err = fmt.Errorf("cannot import, possibly version skew (%v) - reinstall package", e) + if version > currentVersion { + err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e) + } else { + err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e) + } } }() @@ -64,11 +67,13 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data [] imports: imports, data: data, importpath: path, - version: -1, // unknown version + version: version, strList: []string{""}, // empty string is mapped to 0 pathList: []string{""}, // empty string is mapped to 0 - fset: fset, - files: make(map[string]*token.File), + fake: fakeFileSet{ + fset: fset, + files: make(map[string]*token.File), + }, } // read version info @@ -87,7 +92,7 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data [] p.posInfoFormat = p.int() != 0 versionstr = p.string() if versionstr == "v1" { - p.version = 0 + version = 0 } } else { // Go1.8 extensible encoding @@ -95,24 +100,25 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data [] versionstr = p.rawStringln(b) if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" { if v, err := strconv.Atoi(s[1]); err == nil && v > 0 { - p.version = v + version = v } } } + p.version = version // read version specific flags - extend as necessary switch p.version { - // case 6: + // case currentVersion: // ... // fallthrough - case 5, 4, 3, 2, 1: + case currentVersion, 5, 4, 3, 2, 1: p.debugFormat = p.rawStringln(p.rawByte()) == "debug" p.trackAllTypes = p.int() != 0 p.posInfoFormat = p.int() != 0 case 0: // Go1.7 encoding format - nothing to do here default: - errorf("unknown export format version %d (%q)", p.version, versionstr) + errorf("unknown bexport format version %d (%q)", p.version, versionstr) } // --- generic export data --- @@ -182,6 +188,9 @@ func (p *importer) pkg() *types.Package { } else { path = p.string() } + if p.version >= 6 { + p.int() // package height; unused by go/types + } // we should never see an empty package name if name == "" { @@ -257,7 +266,7 @@ func (p *importer) obj(tag int) { case constTag: pos := p.pos() pkg, name := p.qualifiedName() - typ := p.typ(nil) + typ := p.typ(nil, nil) val := p.value() p.declare(types.NewConst(pos, pkg, name, typ, val)) @@ -265,16 +274,16 @@ func (p *importer) obj(tag int) { // TODO(gri) verify type alias hookup is correct pos := p.pos() pkg, name := p.qualifiedName() - typ := p.typ(nil) + typ := p.typ(nil, nil) p.declare(types.NewTypeName(pos, pkg, name, typ)) case typeTag: - p.typ(nil) + p.typ(nil, nil) case varTag: pos := p.pos() pkg, name := p.qualifiedName() - typ := p.typ(nil) + typ := p.typ(nil, nil) p.declare(types.NewVar(pos, pkg, name, typ)) case funcTag: @@ -321,15 +330,23 @@ func (p *importer) pos() token.Pos { p.prevFile = file p.prevLine = line - // Synthesize a token.Pos + return p.fake.pos(file, line) +} +// Synthesize a token.Pos +type fakeFileSet struct { + fset *token.FileSet + files map[string]*token.File +} + +func (s *fakeFileSet) pos(file string, line int) token.Pos { // Since we don't know the set of needed file positions, we // reserve maxlines positions per file. const maxlines = 64 * 1024 - f := p.files[file] + f := s.files[file] if f == nil { - f = p.fset.AddFile(file, -1, maxlines) - p.files[file] = f + f = s.fset.AddFile(file, -1, maxlines) + s.files[file] = f // Allocate the fake linebreak indices on first use. // TODO(adonovan): opt: save ~512KB using a more complex scheme? fakeLinesOnce.Do(func() { @@ -379,7 +396,11 @@ func (t *dddSlice) String() string { return "..." + t.elem.String() } // the package currently imported. The parent package is needed for // exported struct fields and interface methods which don't contain // explicit package information in the export data. -func (p *importer) typ(parent *types.Package) types.Type { +// +// A non-nil tname is used as the "owner" of the result type; i.e., +// the result type is the underlying type of tname. tname is used +// to give interface methods a named receiver type where possible. +func (p *importer) typ(parent *types.Package, tname *types.Named) types.Type { // if the type was seen before, i is its index (>= 0) i := p.tagOrIndex() if i >= 0 { @@ -409,15 +430,15 @@ func (p *importer) typ(parent *types.Package) types.Type { t0 := types.NewNamed(obj.(*types.TypeName), nil, nil) // but record the existing type, if any - t := obj.Type().(*types.Named) - p.record(t) + tname := obj.Type().(*types.Named) // tname is either t0 or the existing type + p.record(tname) // read underlying type - t0.SetUnderlying(p.typ(parent)) + t0.SetUnderlying(p.typ(parent, t0)) // interfaces don't have associated methods if types.IsInterface(t0) { - return t + return tname } // read associated methods @@ -438,7 +459,7 @@ func (p *importer) typ(parent *types.Package) types.Type { t0.AddMethod(types.NewFunc(pos, parent, name, sig)) } - return t + return tname case arrayTag: t := new(types.Array) @@ -447,7 +468,7 @@ func (p *importer) typ(parent *types.Package) types.Type { } n := p.int64() - *t = *types.NewArray(p.typ(parent), n) + *t = *types.NewArray(p.typ(parent, nil), n) return t case sliceTag: @@ -456,7 +477,7 @@ func (p *importer) typ(parent *types.Package) types.Type { p.record(t) } - *t = *types.NewSlice(p.typ(parent)) + *t = *types.NewSlice(p.typ(parent, nil)) return t case dddTag: @@ -465,7 +486,7 @@ func (p *importer) typ(parent *types.Package) types.Type { p.record(t) } - t.elem = p.typ(parent) + t.elem = p.typ(parent, nil) return t case structTag: @@ -483,7 +504,7 @@ func (p *importer) typ(parent *types.Package) types.Type { p.record(t) } - *t = *types.NewPointer(p.typ(parent)) + *t = *types.NewPointer(p.typ(parent, nil)) return t case signatureTag: @@ -502,18 +523,20 @@ func (p *importer) typ(parent *types.Package) types.Type { // cannot expect the interface type to appear in a cycle, as any // such cycle must contain a named type which would have been // first defined earlier. + // TODO(gri) Is this still true now that we have type aliases? + // See issue #23225. n := len(p.typList) if p.trackAllTypes { p.record(nil) } - var embeddeds []*types.Named + var embeddeds []types.Type for n := p.int(); n > 0; n-- { p.pos() - embeddeds = append(embeddeds, p.typ(parent).(*types.Named)) + embeddeds = append(embeddeds, p.typ(parent, nil)) } - t := types.NewInterface(p.methodList(parent), embeddeds) + t := types.NewInterfaceType(p.methodList(parent, tname), embeddeds) p.interfaceList = append(p.interfaceList, t) if p.trackAllTypes { p.typList[n] = t @@ -526,8 +549,8 @@ func (p *importer) typ(parent *types.Package) types.Type { p.record(t) } - key := p.typ(parent) - val := p.typ(parent) + key := p.typ(parent, nil) + val := p.typ(parent, nil) *t = *types.NewMap(key, val) return t @@ -537,19 +560,8 @@ func (p *importer) typ(parent *types.Package) types.Type { p.record(t) } - var dir types.ChanDir - // tag values must match the constants in cmd/compile/internal/gc/go.go - switch d := p.int(); d { - case 1 /* Crecv */ : - dir = types.RecvOnly - case 2 /* Csend */ : - dir = types.SendOnly - case 3 /* Cboth */ : - dir = types.SendRecv - default: - errorf("unexpected channel dir %d", d) - } - val := p.typ(parent) + dir := chanDir(p.int()) + val := p.typ(parent, nil) *t = *types.NewChan(dir, val) return t @@ -559,6 +571,21 @@ func (p *importer) typ(parent *types.Package) types.Type { } } +func chanDir(d int) types.ChanDir { + // tag values must match the constants in cmd/compile/internal/gc/go.go + switch d { + case 1 /* Crecv */ : + return types.RecvOnly + case 2 /* Csend */ : + return types.SendOnly + case 3 /* Cboth */ : + return types.SendRecv + default: + errorf("unexpected channel dir %d", d) + return 0 + } +} + func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags []string) { if n := p.int(); n > 0 { fields = make([]*types.Var, n) @@ -573,7 +600,7 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [ func (p *importer) field(parent *types.Package) (*types.Var, string) { pos := p.pos() pkg, name, alias := p.fieldName(parent) - typ := p.typ(parent) + typ := p.typ(parent, nil) tag := p.string() anonymous := false @@ -597,22 +624,30 @@ func (p *importer) field(parent *types.Package) (*types.Var, string) { return types.NewField(pos, pkg, name, typ, anonymous), tag } -func (p *importer) methodList(parent *types.Package) (methods []*types.Func) { +func (p *importer) methodList(parent *types.Package, baseType *types.Named) (methods []*types.Func) { if n := p.int(); n > 0 { methods = make([]*types.Func, n) for i := range methods { - methods[i] = p.method(parent) + methods[i] = p.method(parent, baseType) } } return } -func (p *importer) method(parent *types.Package) *types.Func { +func (p *importer) method(parent *types.Package, baseType *types.Named) *types.Func { pos := p.pos() pkg, name, _ := p.fieldName(parent) + // If we don't have a baseType, use a nil receiver. + // A receiver using the actual interface type (which + // we don't know yet) will be filled in when we call + // types.Interface.Complete. + var recv *types.Var + if baseType != nil { + recv = types.NewVar(token.NoPos, parent, "", baseType) + } params, isddd := p.paramList() result, _ := p.paramList() - sig := types.NewSignature(nil, params, result, isddd) + sig := types.NewSignature(recv, params, result, isddd) return types.NewFunc(pos, pkg, name, sig) } @@ -668,7 +703,7 @@ func (p *importer) paramList() (*types.Tuple, bool) { } func (p *importer) param(named bool) (*types.Var, bool) { - t := p.typ(nil) + t := p.typ(nil, nil) td, isddd := t.(*dddSlice) if isddd { t = types.NewSlice(td.elem) diff --git a/libgo/go/go/internal/gcimporter/gcimporter.go b/libgo/go/go/internal/gcimporter/gcimporter.go index 2185f5b..d117f6f 100644 --- a/libgo/go/go/internal/gcimporter/gcimporter.go +++ b/libgo/go/go/internal/gcimporter/gcimporter.go @@ -133,9 +133,7 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func }() rc = f } - defer func() { - rc.Close() - }() + defer rc.Close() var hdr string buf := bufio.NewReader(rc) @@ -146,16 +144,27 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func switch hdr { case "$$\n": err = fmt.Errorf("import %q: old export format no longer supported (recompile library)", path) + case "$$B\n": var data []byte data, err = ioutil.ReadAll(buf) - if err == nil { - // TODO(gri): allow clients of go/importer to provide a FileSet. - // Or, define a new standard go/types/gcexportdata package. - fset := token.NewFileSet() + if err != nil { + break + } + + // TODO(gri): allow clients of go/importer to provide a FileSet. + // Or, define a new standard go/types/gcexportdata package. + fset := token.NewFileSet() + + // The indexed export format starts with an 'i'; the older + // binary export format starts with a 'c', 'd', or 'v' + // (from "version"). Select appropriate importer. + if len(data) > 0 && data[0] == 'i' { + _, pkg, err = iImportData(fset, packages, data[1:], id) + } else { _, pkg, err = BImportData(fset, packages, data, id) - return } + default: err = fmt.Errorf("unknown export data header: %q", hdr) } diff --git a/libgo/go/go/internal/gcimporter/gcimporter_test.go b/libgo/go/go/internal/gcimporter/gcimporter_test.go index 56870a1..d496f2e 100644 --- a/libgo/go/go/internal/gcimporter/gcimporter_test.go +++ b/libgo/go/go/internal/gcimporter/gcimporter_test.go @@ -141,9 +141,21 @@ func TestVersionHandling(t *testing.T) { } pkgpath := "./" + name[:len(name)-2] + if testing.Verbose() { + t.Logf("importing %s", name) + } + // test that export data can be imported _, err := Import(make(map[string]*types.Package), pkgpath, dir, nil) if err != nil { + // ok to fail if it fails with a newer version error for select files + if strings.Contains(err.Error(), "newer version") { + switch name { + case "test_go1.11_999b.a", "test_go1.11_999i.a": + continue + } + // fall through + } t.Errorf("import %q failed: %v", pkgpath, err) continue } @@ -200,11 +212,24 @@ var importedObjectTests = []struct { name string want string }{ + // non-interfaces + {"crypto.Hash", "type Hash uint"}, + {"go/ast.ObjKind", "type ObjKind int"}, + {"go/types.Qualifier", "type Qualifier func(*Package) string"}, + {"go/types.Comparable", "func Comparable(T Type) bool"}, {"math.Pi", "const Pi untyped float"}, + {"math.Sin", "func Sin(x float64) float64"}, + {"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"}, + {"go/internal/gcimporter.BImportData", "func BImportData(fset *go/token.FileSet, imports map[string]*go/types.Package, data []byte, path string) (_ int, pkg *go/types.Package, err error)"}, + + // interfaces + {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"}, + {"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"}, + {"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"}, {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"}, {"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"}, - {"math.Sin", "func Sin(x float64) float64"}, - // TODO(gri) add more tests + {"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"}, + {"go/types.Type", "type Type interface{String() string; Underlying() Type}"}, } func TestImportedTypes(t *testing.T) { @@ -239,6 +264,46 @@ func TestImportedTypes(t *testing.T) { if got != test.want { t.Errorf("%s: got %q; want %q", test.name, got, test.want) } + + if named, _ := obj.Type().(*types.Named); named != nil { + verifyInterfaceMethodRecvs(t, named, 0) + } + } +} + +// verifyInterfaceMethodRecvs verifies that method receiver types +// are named if the methods belong to a named interface type. +func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) { + // avoid endless recursion in case of an embedding bug that lead to a cycle + if level > 10 { + t.Errorf("%s: embeds itself", named) + return + } + + iface, _ := named.Underlying().(*types.Interface) + if iface == nil { + return // not an interface + } + + // check explicitly declared methods + for i := 0; i < iface.NumExplicitMethods(); i++ { + m := iface.ExplicitMethod(i) + recv := m.Type().(*types.Signature).Recv() + if recv == nil { + t.Errorf("%s: missing receiver type", m) + continue + } + if recv.Type() != named { + t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named) + } + } + + // check embedded interfaces (if they are named, too) + for i := 0; i < iface.NumEmbeddeds(); i++ { + // embedding of interfaces cannot have cycles; recursion will terminate + if etype, _ := iface.EmbeddedType(i).(*types.Named); etype != nil { + verifyInterfaceMethodRecvs(t, etype, level+1) + } } } @@ -456,6 +521,47 @@ func TestIssue20046(t *testing.T) { t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect) } } +func TestIssue25301(t *testing.T) { + skipSpecialPlatforms(t) + + // This package only handles gc export data. + if runtime.Compiler != "gc" { + t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) + } + + // On windows, we have to set the -D option for the compiler to avoid having a drive + // letter and an illegal ':' in the import path - just skip it (see also issue #3483). + if runtime.GOOS == "windows" { + t.Skip("avoid dealing with relative paths/drive letters on windows") + } + + if f := compile(t, "testdata", "issue25301.go"); f != "" { + defer os.Remove(f) + } + + importPkg(t, "./testdata/issue25301") +} + +func TestIssue25596(t *testing.T) { + skipSpecialPlatforms(t) + + // This package only handles gc export data. + if runtime.Compiler != "gc" { + t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) + } + + // On windows, we have to set the -D option for the compiler to avoid having a drive + // letter and an illegal ':' in the import path - just skip it (see also issue #3483). + if runtime.GOOS == "windows" { + t.Skip("avoid dealing with relative paths/drive letters on windows") + } + + if f := compile(t, "testdata", "issue25596.go"); f != "" { + defer os.Remove(f) + } + + importPkg(t, "./testdata/issue25596") +} func importPkg(t *testing.T, path string) *types.Package { pkg, err := Import(make(map[string]*types.Package), path, ".", nil) diff --git a/libgo/go/go/internal/gcimporter/iimport.go b/libgo/go/go/internal/gcimporter/iimport.go new file mode 100644 index 0000000..bf48064 --- /dev/null +++ b/libgo/go/go/internal/gcimporter/iimport.go @@ -0,0 +1,596 @@ +// Copyright 2018 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. + +// Indexed package import. +// See cmd/compile/internal/gc/iexport.go for the export data format. + +package gcimporter + +import ( + "bytes" + "encoding/binary" + "fmt" + "go/constant" + "go/token" + "go/types" + "io" + "sort" +) + +type intReader struct { + *bytes.Reader + path string +} + +func (r *intReader) int64() int64 { + i, err := binary.ReadVarint(r.Reader) + if err != nil { + errorf("import %q: read varint error: %v", r.path, err) + } + return i +} + +func (r *intReader) uint64() uint64 { + i, err := binary.ReadUvarint(r.Reader) + if err != nil { + errorf("import %q: read varint error: %v", r.path, err) + } + return i +} + +const predeclReserved = 32 + +type itag uint64 + +const ( + // Types + definedType itag = iota + pointerType + sliceType + arrayType + chanType + mapType + signatureType + structType + interfaceType +) + +// iImportData imports a package from the serialized package data +// and returns the number of bytes consumed and a reference to the package. +// If the export data version is not recognized or the format is otherwise +// compromised, an error is returned. +func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { + const currentVersion = 0 + version := -1 + defer func() { + if e := recover(); e != nil { + if version > currentVersion { + err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e) + } else { + err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e) + } + } + }() + + r := &intReader{bytes.NewReader(data), path} + + version = int(r.uint64()) + switch version { + case currentVersion: + default: + errorf("unknown iexport format version %d", version) + } + + sLen := int64(r.uint64()) + dLen := int64(r.uint64()) + + whence, _ := r.Seek(0, io.SeekCurrent) + stringData := data[whence : whence+sLen] + declData := data[whence+sLen : whence+sLen+dLen] + r.Seek(sLen+dLen, io.SeekCurrent) + + p := iimporter{ + ipath: path, + + stringData: stringData, + stringCache: make(map[uint64]string), + pkgCache: make(map[uint64]*types.Package), + + declData: declData, + pkgIndex: make(map[*types.Package]map[string]uint64), + typCache: make(map[uint64]types.Type), + + fake: fakeFileSet{ + fset: fset, + files: make(map[string]*token.File), + }, + } + + for i, pt := range predeclared { + p.typCache[uint64(i)] = pt + } + + pkgList := make([]*types.Package, r.uint64()) + for i := range pkgList { + pkgPathOff := r.uint64() + pkgPath := p.stringAt(pkgPathOff) + pkgName := p.stringAt(r.uint64()) + _ = r.uint64() // package height; unused by go/types + + if pkgPath == "" { + pkgPath = path + } + pkg := imports[pkgPath] + if pkg == nil { + pkg = types.NewPackage(pkgPath, pkgName) + imports[pkgPath] = pkg + } else if pkg.Name() != pkgName { + errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path) + } + + p.pkgCache[pkgPathOff] = pkg + + nameIndex := make(map[string]uint64) + for nSyms := r.uint64(); nSyms > 0; nSyms-- { + name := p.stringAt(r.uint64()) + nameIndex[name] = r.uint64() + } + + p.pkgIndex[pkg] = nameIndex + pkgList[i] = pkg + } + + localpkg := pkgList[0] + + names := make([]string, 0, len(p.pkgIndex[localpkg])) + for name := range p.pkgIndex[localpkg] { + names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + p.doDecl(localpkg, name) + } + + for _, typ := range p.interfaceList { + typ.Complete() + } + + // record all referenced packages as imports + list := append(([]*types.Package)(nil), pkgList[1:]...) + sort.Sort(byPath(list)) + localpkg.SetImports(list) + + // package was imported completely and without errors + localpkg.MarkComplete() + + consumed, _ := r.Seek(0, io.SeekCurrent) + return int(consumed), localpkg, nil +} + +type iimporter struct { + ipath string + + stringData []byte + stringCache map[uint64]string + pkgCache map[uint64]*types.Package + + declData []byte + pkgIndex map[*types.Package]map[string]uint64 + typCache map[uint64]types.Type + + fake fakeFileSet + interfaceList []*types.Interface +} + +func (p *iimporter) doDecl(pkg *types.Package, name string) { + // See if we've already imported this declaration. + if obj := pkg.Scope().Lookup(name); obj != nil { + return + } + + off, ok := p.pkgIndex[pkg][name] + if !ok { + errorf("%v.%v not in index", pkg, name) + } + + r := &importReader{p: p, currPkg: pkg} + r.declReader.Reset(p.declData[off:]) + + r.obj(name) +} + +func (p *iimporter) stringAt(off uint64) string { + if s, ok := p.stringCache[off]; ok { + return s + } + + slen, n := binary.Uvarint(p.stringData[off:]) + if n <= 0 { + errorf("varint failed") + } + spos := off + uint64(n) + s := string(p.stringData[spos : spos+slen]) + p.stringCache[off] = s + return s +} + +func (p *iimporter) pkgAt(off uint64) *types.Package { + if pkg, ok := p.pkgCache[off]; ok { + return pkg + } + path := p.stringAt(off) + errorf("missing package %q in %q", path, p.ipath) + return nil +} + +func (p *iimporter) typAt(off uint64, base *types.Named) types.Type { + if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) { + return t + } + + if off < predeclReserved { + errorf("predeclared type missing from cache: %v", off) + } + + r := &importReader{p: p} + r.declReader.Reset(p.declData[off-predeclReserved:]) + t := r.doType(base) + + if base == nil || !isInterface(t) { + p.typCache[off] = t + } + return t +} + +type importReader struct { + p *iimporter + declReader bytes.Reader + currPkg *types.Package + prevFile string + prevLine int64 +} + +func (r *importReader) obj(name string) { + tag := r.byte() + pos := r.pos() + + switch tag { + case 'A': + typ := r.typ() + + r.declare(types.NewTypeName(pos, r.currPkg, name, typ)) + + case 'C': + typ, val := r.value() + + r.declare(types.NewConst(pos, r.currPkg, name, typ, val)) + + case 'F': + sig := r.signature(nil) + + r.declare(types.NewFunc(pos, r.currPkg, name, sig)) + + case 'T': + // Types can be recursive. We need to setup a stub + // declaration before recursing. + obj := types.NewTypeName(pos, r.currPkg, name, nil) + named := types.NewNamed(obj, nil, nil) + r.declare(obj) + + underlying := r.p.typAt(r.uint64(), named).Underlying() + named.SetUnderlying(underlying) + + if !isInterface(underlying) { + for n := r.uint64(); n > 0; n-- { + mpos := r.pos() + mname := r.ident() + recv := r.param() + msig := r.signature(recv) + + named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig)) + } + } + + case 'V': + typ := r.typ() + + r.declare(types.NewVar(pos, r.currPkg, name, typ)) + + default: + errorf("unexpected tag: %v", tag) + } +} + +func (r *importReader) declare(obj types.Object) { + obj.Pkg().Scope().Insert(obj) +} + +func (r *importReader) value() (typ types.Type, val constant.Value) { + typ = r.typ() + + switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType { + case types.IsBoolean: + val = constant.MakeBool(r.bool()) + + case types.IsString: + val = constant.MakeString(r.string()) + + case types.IsInteger: + val = r.mpint(b) + + case types.IsFloat: + val = r.mpfloat(b) + + case types.IsComplex: + re := r.mpfloat(b) + im := r.mpfloat(b) + val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) + + default: + errorf("unexpected type %v", typ) // panics + panic("unreachable") + } + + return +} + +func intSize(b *types.Basic) (signed bool, maxBytes uint) { + if (b.Info() & types.IsUntyped) != 0 { + return true, 64 + } + + switch b.Kind() { + case types.Float32, types.Complex64: + return true, 3 + case types.Float64, types.Complex128: + return true, 7 + } + + signed = (b.Info() & types.IsUnsigned) == 0 + switch b.Kind() { + case types.Int8, types.Uint8: + maxBytes = 1 + case types.Int16, types.Uint16: + maxBytes = 2 + case types.Int32, types.Uint32: + maxBytes = 4 + default: + maxBytes = 8 + } + + return +} + +func (r *importReader) mpint(b *types.Basic) constant.Value { + signed, maxBytes := intSize(b) + + maxSmall := 256 - maxBytes + if signed { + maxSmall = 256 - 2*maxBytes + } + if maxBytes == 1 { + maxSmall = 256 + } + + n, _ := r.declReader.ReadByte() + if uint(n) < maxSmall { + v := int64(n) + if signed { + v >>= 1 + if n&1 != 0 { + v = ^v + } + } + return constant.MakeInt64(v) + } + + v := -n + if signed { + v = -(n &^ 1) >> 1 + } + if v < 1 || uint(v) > maxBytes { + errorf("weird decoding: %v, %v => %v", n, signed, v) + } + + buf := make([]byte, v) + io.ReadFull(&r.declReader, buf) + + // convert to little endian + // TODO(gri) go/constant should have a more direct conversion function + // (e.g., once it supports a big.Float based implementation) + for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 { + buf[i], buf[j] = buf[j], buf[i] + } + + x := constant.MakeFromBytes(buf) + if signed && n&1 != 0 { + x = constant.UnaryOp(token.SUB, x, 0) + } + return x +} + +func (r *importReader) mpfloat(b *types.Basic) constant.Value { + x := r.mpint(b) + if constant.Sign(x) == 0 { + return x + } + + exp := r.int64() + switch { + case exp > 0: + x = constant.Shift(x, token.SHL, uint(exp)) + case exp < 0: + d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp)) + x = constant.BinaryOp(x, token.QUO, d) + } + return x +} + +func (r *importReader) ident() string { + return r.string() +} + +func (r *importReader) qualifiedIdent() (*types.Package, string) { + name := r.string() + pkg := r.pkg() + return pkg, name +} + +func (r *importReader) pos() token.Pos { + delta := r.int64() + if delta != deltaNewFile { + r.prevLine += delta + } else if l := r.int64(); l == -1 { + r.prevLine += deltaNewFile + } else { + r.prevFile = r.string() + r.prevLine = l + } + + if r.prevFile == "" && r.prevLine == 0 { + return token.NoPos + } + + return r.p.fake.pos(r.prevFile, int(r.prevLine)) +} + +func (r *importReader) typ() types.Type { + return r.p.typAt(r.uint64(), nil) +} + +func isInterface(t types.Type) bool { + _, ok := t.(*types.Interface) + return ok +} + +func (r *importReader) pkg() *types.Package { return r.p.pkgAt(r.uint64()) } +func (r *importReader) string() string { return r.p.stringAt(r.uint64()) } + +func (r *importReader) doType(base *types.Named) types.Type { + switch k := r.kind(); k { + default: + errorf("unexpected kind tag in %q: %v", r.p.ipath, k) + return nil + + case definedType: + pkg, name := r.qualifiedIdent() + r.p.doDecl(pkg, name) + return pkg.Scope().Lookup(name).(*types.TypeName).Type() + case pointerType: + return types.NewPointer(r.typ()) + case sliceType: + return types.NewSlice(r.typ()) + case arrayType: + n := r.uint64() + return types.NewArray(r.typ(), int64(n)) + case chanType: + dir := chanDir(int(r.uint64())) + return types.NewChan(dir, r.typ()) + case mapType: + return types.NewMap(r.typ(), r.typ()) + case signatureType: + r.currPkg = r.pkg() + return r.signature(nil) + + case structType: + r.currPkg = r.pkg() + + fields := make([]*types.Var, r.uint64()) + tags := make([]string, len(fields)) + for i := range fields { + fpos := r.pos() + fname := r.ident() + ftyp := r.typ() + emb := r.bool() + tag := r.string() + + fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb) + tags[i] = tag + } + return types.NewStruct(fields, tags) + + case interfaceType: + r.currPkg = r.pkg() + + embeddeds := make([]types.Type, r.uint64()) + for i := range embeddeds { + _ = r.pos() + embeddeds[i] = r.typ() + } + + methods := make([]*types.Func, r.uint64()) + for i := range methods { + mpos := r.pos() + mname := r.ident() + + // TODO(mdempsky): Matches bimport.go, but I + // don't agree with this. + var recv *types.Var + if base != nil { + recv = types.NewVar(token.NoPos, r.currPkg, "", base) + } + + msig := r.signature(recv) + methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig) + } + + typ := types.NewInterfaceType(methods, embeddeds) + r.p.interfaceList = append(r.p.interfaceList, typ) + return typ + } +} + +func (r *importReader) kind() itag { + return itag(r.uint64()) +} + +func (r *importReader) signature(recv *types.Var) *types.Signature { + params := r.paramList() + results := r.paramList() + variadic := params.Len() > 0 && r.bool() + return types.NewSignature(recv, params, results, variadic) +} + +func (r *importReader) paramList() *types.Tuple { + xs := make([]*types.Var, r.uint64()) + for i := range xs { + xs[i] = r.param() + } + return types.NewTuple(xs...) +} + +func (r *importReader) param() *types.Var { + pos := r.pos() + name := r.ident() + typ := r.typ() + return types.NewParam(pos, r.currPkg, name, typ) +} + +func (r *importReader) bool() bool { + return r.uint64() != 0 +} + +func (r *importReader) int64() int64 { + n, err := binary.ReadVarint(&r.declReader) + if err != nil { + errorf("readVarint: %v", err) + } + return n +} + +func (r *importReader) uint64() uint64 { + n, err := binary.ReadUvarint(&r.declReader) + if err != nil { + errorf("readUvarint: %v", err) + } + return n +} + +func (r *importReader) byte() byte { + x, err := r.declReader.ReadByte() + if err != nil { + errorf("declReader.ReadByte: %v", err) + } + return x +} diff --git a/libgo/go/go/internal/gcimporter/testdata/issue25301.go b/libgo/go/go/internal/gcimporter/testdata/issue25301.go new file mode 100644 index 0000000..e3dc98b --- /dev/null +++ b/libgo/go/go/internal/gcimporter/testdata/issue25301.go @@ -0,0 +1,17 @@ +// Copyright 2018 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 issue25301 + +type ( + A = interface { + M() + } + T interface { + A + } + S struct{} +) + +func (S) M() { println("m") } diff --git a/libgo/go/go/internal/gcimporter/testdata/issue25596.go b/libgo/go/go/internal/gcimporter/testdata/issue25596.go new file mode 100644 index 0000000..8923373 --- /dev/null +++ b/libgo/go/go/internal/gcimporter/testdata/issue25596.go @@ -0,0 +1,13 @@ +// Copyright 2018 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 issue25596 + +type E interface { + M() T +} + +type T interface { + E +} diff --git a/libgo/go/go/internal/gcimporter/testdata/versions/test.go b/libgo/go/go/internal/gcimporter/testdata/versions/test.go index ac9c968..227fc09 100644 --- a/libgo/go/go/internal/gcimporter/testdata/versions/test.go +++ b/libgo/go/go/internal/gcimporter/testdata/versions/test.go @@ -11,7 +11,10 @@ // // go build -o test_go1.$X_$Y.a test.go // -// with $X = Go version and $Y = export format version. +// with $X = Go version and $Y = export format version +// (add 'b' or 'i' to distinguish between binary and +// indexed format starting with 1.11 as long as both +// formats are supported). // // Make sure this source is extended such that it exercises // whatever export format change has taken place. diff --git a/libgo/go/go/internal/srcimporter/srcimporter.go b/libgo/go/go/internal/srcimporter/srcimporter.go index 9ed7e5e..2a6c274 100644 --- a/libgo/go/go/internal/srcimporter/srcimporter.go +++ b/libgo/go/go/internal/srcimporter/srcimporter.go @@ -13,6 +13,8 @@ import ( "go/parser" "go/token" "go/types" + "io" + "os" "path/filepath" "sync" ) @@ -144,7 +146,11 @@ func (p *Importer) ImportFrom(path, srcDir string, mode types.ImportMode) (*type } func (p *Importer) parseFiles(dir string, filenames []string) ([]*ast.File, error) { - open := p.ctxt.OpenFile // possibly nil + // use build.Context's OpenFile if there is one + open := p.ctxt.OpenFile + if open == nil { + open = func(name string) (io.ReadCloser, error) { return os.Open(name) } + } files := make([]*ast.File, len(filenames)) errors := make([]error, len(filenames)) @@ -154,22 +160,13 @@ func (p *Importer) parseFiles(dir string, filenames []string) ([]*ast.File, erro for i, filename := range filenames { go func(i int, filepath string) { defer wg.Done() - if open != nil { - src, err := open(filepath) - if err != nil { - errors[i] = fmt.Errorf("opening package file %s failed (%v)", filepath, err) - return - } - files[i], errors[i] = parser.ParseFile(p.fset, filepath, src, 0) - src.Close() // ignore Close error - parsing may have succeeded which is all we need - } else { - // Special-case when ctxt doesn't provide a custom OpenFile and use the - // parser's file reading mechanism directly. This appears to be quite a - // bit faster than opening the file and providing an io.ReaderCloser in - // both cases. - // TODO(gri) investigate performance difference (issue #19281) - files[i], errors[i] = parser.ParseFile(p.fset, filepath, nil, 0) + src, err := open(filepath) + if err != nil { + errors[i] = err // open provides operation and filename in error + return } + files[i], errors[i] = parser.ParseFile(p.fset, filepath, src, 0) + src.Close() // ignore Close error - parsing may have succeeded which is all we need }(i, p.joinPath(dir, filename)) } wg.Wait() diff --git a/libgo/go/go/internal/srcimporter/srcimporter_test.go b/libgo/go/go/internal/srcimporter/srcimporter_test.go index dd4d56a..b9caa90 100644 --- a/libgo/go/go/internal/srcimporter/srcimporter_test.go +++ b/libgo/go/go/internal/srcimporter/srcimporter_test.go @@ -131,6 +131,44 @@ func TestImportedTypes(t *testing.T) { if got != test.want { t.Errorf("%s: got %q; want %q", test.name, got, test.want) } + + if named, _ := obj.Type().(*types.Named); named != nil { + verifyInterfaceMethodRecvs(t, named, 0) + } + } +} + +// verifyInterfaceMethodRecvs verifies that method receiver types +// are named if the methods belong to a named interface type. +func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) { + // avoid endless recursion in case of an embedding bug that lead to a cycle + if level > 10 { + t.Errorf("%s: embeds itself", named) + return + } + + iface, _ := named.Underlying().(*types.Interface) + if iface == nil { + return // not an interface + } + + // check explicitly declared methods + for i := 0; i < iface.NumExplicitMethods(); i++ { + m := iface.ExplicitMethod(i) + recv := m.Type().(*types.Signature).Recv() + if recv == nil { + t.Errorf("%s: missing receiver type", m) + continue + } + if recv.Type() != named { + t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named) + } + } + + // check embedded interfaces (they are named, too) + for i := 0; i < iface.NumEmbeddeds(); i++ { + // embedding of interfaces cannot have cycles; recursion will terminate + verifyInterfaceMethodRecvs(t, iface.Embedded(i), level+1) } } diff --git a/libgo/go/go/parser/error_test.go b/libgo/go/go/parser/error_test.go index ef91e1e..9b79097 100644 --- a/libgo/go/go/parser/error_test.go +++ b/libgo/go/go/parser/error_test.go @@ -91,6 +91,12 @@ func expectedErrors(fset *token.FileSet, filename string, src []byte) map[token. } errors[pos] = string(s[2]) } + case token.SEMICOLON: + // don't use the position of auto-inserted (invisible) semicolons + if lit != ";" { + break + } + fallthrough default: prev = pos var l int // token length diff --git a/libgo/go/go/parser/interface.go b/libgo/go/go/parser/interface.go index 724d865..9de160a 100644 --- a/libgo/go/go/parser/interface.go +++ b/libgo/go/go/parser/interface.go @@ -35,11 +35,7 @@ func readSource(filename string, src interface{}) ([]byte, error) { return s.Bytes(), nil } case io.Reader: - var buf bytes.Buffer - if _, err := io.Copy(&buf, s); err != nil { - return nil, err - } - return buf.Bytes(), nil + return ioutil.ReadAll(s) } return nil, errors.New("invalid source") } diff --git a/libgo/go/go/parser/parser.go b/libgo/go/go/parser/parser.go index 2b58724..189bfb4 100644 --- a/libgo/go/go/parser/parser.go +++ b/libgo/go/go/parser/parser.go @@ -48,11 +48,11 @@ type parser struct { lit string // token literal // Error recovery - // (used to limit the number of calls to syncXXX functions + // (used to limit the number of calls to parser.advance // w/o making scanning progress - avoids potential endless // loops across multiple parser functions during error recovery) syncPos token.Pos // last synchronization position - syncCnt int // number of calls to syncXXX without progress + syncCnt int // number of parser.advance calls without progress // Non-syntactic parser control exprLev int // < 0: in control clause, >= 0: in expression @@ -375,13 +375,14 @@ func (p *parser) errorExpected(pos token.Pos, msg string) { if pos == p.pos { // the error happened at the current position; // make the error message more specific - if p.tok == token.SEMICOLON && p.lit == "\n" { + switch { + case p.tok == token.SEMICOLON && p.lit == "\n": msg += ", found newline" - } else { + case p.tok.IsLiteral(): + // print 123 rather than 'INT', etc. + msg += ", found " + p.lit + default: msg += ", found '" + p.tok.String() + "'" - if p.tok.IsLiteral() { - msg += " " + p.lit - } } } p.error(pos, msg) @@ -419,7 +420,7 @@ func (p *parser) expectSemi() { p.next() default: p.errorExpected(p.pos, "';'") - syncStmt(p) + p.advance(stmtStart) } } } @@ -445,21 +446,16 @@ func assert(cond bool, msg string) { } } -// syncStmt advances to the next statement. -// Used for synchronization after an error. -// -func syncStmt(p *parser) { - for { - switch p.tok { - case token.BREAK, token.CONST, token.CONTINUE, token.DEFER, - token.FALLTHROUGH, token.FOR, token.GO, token.GOTO, - token.IF, token.RETURN, token.SELECT, token.SWITCH, - token.TYPE, token.VAR: +// advance consumes tokens until the current token p.tok +// is in the 'to' set, or token.EOF. For error recovery. +func (p *parser) advance(to map[token.Token]bool) { + for ; p.tok != token.EOF; p.next() { + if to[p.tok] { // Return only if parser made some progress since last - // sync or if it has not reached 10 sync calls without + // sync or if it has not reached 10 advance calls without // progress. Otherwise consume at least one token to // avoid an endless parser loop (it is possible that - // both parseOperand and parseStmt call syncStmt and + // both parseOperand and parseStmt call advance and // correctly do not advance, thus the need for the // invocation limit p.syncCnt). if p.pos == p.syncPos && p.syncCnt < 10 { @@ -476,35 +472,40 @@ func syncStmt(p *parser) { // leads to skipping of possibly correct code if a // previous error is present, and thus is preferred // over a non-terminating parse. - case token.EOF: - return } - p.next() } } -// syncDecl advances to the next declaration. -// Used for synchronization after an error. -// -func syncDecl(p *parser) { - for { - switch p.tok { - case token.CONST, token.TYPE, token.VAR: - // see comments in syncStmt - if p.pos == p.syncPos && p.syncCnt < 10 { - p.syncCnt++ - return - } - if p.pos > p.syncPos { - p.syncPos = p.pos - p.syncCnt = 0 - return - } - case token.EOF: - return - } - p.next() - } +var stmtStart = map[token.Token]bool{ + token.BREAK: true, + token.CONST: true, + token.CONTINUE: true, + token.DEFER: true, + token.FALLTHROUGH: true, + token.FOR: true, + token.GO: true, + token.GOTO: true, + token.IF: true, + token.RETURN: true, + token.SELECT: true, + token.SWITCH: true, + token.TYPE: true, + token.VAR: true, +} + +var declStart = map[token.Token]bool{ + token.CONST: true, + token.TYPE: true, + token.VAR: true, +} + +var exprEnd = map[token.Token]bool{ + token.COMMA: true, + token.COLON: true, + token.SEMICOLON: true, + token.RPAREN: true, + token.RBRACK: true, + token.RBRACE: true, } // safePos returns a valid file position for a given position: If pos @@ -623,7 +624,7 @@ func (p *parser) parseType() ast.Expr { if typ == nil { pos := p.pos p.errorExpected(pos, "type") - p.next() // make progress + p.advance(exprEnd) return &ast.BadExpr{From: pos, To: p.pos} } @@ -1166,7 +1167,7 @@ func (p *parser) parseOperand(lhs bool) ast.Expr { // we have an error pos := p.pos p.errorExpected(pos, "operand") - syncStmt(p) + p.advance(stmtStart) return &ast.BadExpr{From: pos, To: p.pos} } @@ -1808,48 +1809,96 @@ func (p *parser) parseBranchStmt(tok token.Token) *ast.BranchStmt { return &ast.BranchStmt{TokPos: pos, Tok: tok, Label: label} } -func (p *parser) makeExpr(s ast.Stmt, kind string) ast.Expr { +func (p *parser) makeExpr(s ast.Stmt, want string) ast.Expr { if s == nil { return nil } if es, isExpr := s.(*ast.ExprStmt); isExpr { return p.checkExpr(es.X) } - p.error(s.Pos(), fmt.Sprintf("expected %s, found simple statement (missing parentheses around composite literal?)", kind)) + found := "simple statement" + if _, isAss := s.(*ast.AssignStmt); isAss { + found = "assignment" + } + p.error(s.Pos(), fmt.Sprintf("expected %s, found %s (missing parentheses around composite literal?)", want, found)) return &ast.BadExpr{From: s.Pos(), To: p.safePos(s.End())} } -func (p *parser) parseIfStmt() *ast.IfStmt { - if p.trace { - defer un(trace(p, "IfStmt")) +// parseIfHeader is an adjusted version of parser.header +// in cmd/compile/internal/syntax/parser.go, which has +// been tuned for better error handling. +func (p *parser) parseIfHeader() (init ast.Stmt, cond ast.Expr) { + if p.tok == token.LBRACE { + p.error(p.pos, "missing condition in if statement") + cond = &ast.BadExpr{From: p.pos, To: p.pos} + return } + // p.tok != token.LBRACE - pos := p.expect(token.IF) - p.openScope() - defer p.closeScope() + outer := p.exprLev + p.exprLev = -1 - var s ast.Stmt - var x ast.Expr - { - prevLev := p.exprLev - p.exprLev = -1 + if p.tok != token.SEMICOLON { + // accept potential variable declaration but complain + if p.tok == token.VAR { + p.next() + p.error(p.pos, fmt.Sprintf("var declaration not allowed in 'IF' initializer")) + } + init, _ = p.parseSimpleStmt(basic) + } + + var condStmt ast.Stmt + var semi struct { + pos token.Pos + lit string // ";" or "\n"; valid if pos.IsValid() + } + if p.tok != token.LBRACE { if p.tok == token.SEMICOLON { + semi.pos = p.pos + semi.lit = p.lit p.next() - x = p.parseRhs() } else { - s, _ = p.parseSimpleStmt(basic) - if p.tok == token.SEMICOLON { - p.next() - x = p.parseRhs() - } else { - x = p.makeExpr(s, "boolean expression") - s = nil - } + p.expect(token.SEMICOLON) } - p.exprLev = prevLev + if p.tok != token.LBRACE { + condStmt, _ = p.parseSimpleStmt(basic) + } + } else { + condStmt = init + init = nil } + if condStmt != nil { + cond = p.makeExpr(condStmt, "boolean expression") + } else if semi.pos.IsValid() { + if semi.lit == "\n" { + p.error(semi.pos, "unexpected newline, expecting { after if clause") + } else { + p.error(semi.pos, "missing condition in if statement") + } + } + + // make sure we have a valid AST + if cond == nil { + cond = &ast.BadExpr{From: p.pos, To: p.pos} + } + + p.exprLev = outer + return +} + +func (p *parser) parseIfStmt() *ast.IfStmt { + if p.trace { + defer un(trace(p, "IfStmt")) + } + + pos := p.expect(token.IF) + p.openScope() + defer p.closeScope() + + init, cond := p.parseIfHeader() body := p.parseBlockStmt() + var else_ ast.Stmt if p.tok == token.ELSE { p.next() @@ -1867,7 +1916,7 @@ func (p *parser) parseIfStmt() *ast.IfStmt { p.expectSemi() } - return &ast.IfStmt{If: pos, Init: s, Cond: x, Body: body, Else: else_} + return &ast.IfStmt{If: pos, Init: init, Cond: cond, Body: body, Else: else_} } func (p *parser) parseTypeList() (list []ast.Expr) { @@ -2160,7 +2209,7 @@ func (p *parser) parseStmt() (s ast.Stmt) { switch p.tok { case token.CONST, token.TYPE, token.VAR: - s = &ast.DeclStmt{Decl: p.parseDecl(syncStmt)} + s = &ast.DeclStmt{Decl: p.parseDecl(stmtStart)} case // tokens that may start an expression token.IDENT, token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operands @@ -2205,7 +2254,7 @@ func (p *parser) parseStmt() (s ast.Stmt) { // no statement found pos := p.pos p.errorExpected(pos, "statement") - syncStmt(p) + p.advance(stmtStart) s = &ast.BadStmt{From: pos, To: p.pos} } @@ -2419,7 +2468,7 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl { return decl } -func (p *parser) parseDecl(sync func(*parser)) ast.Decl { +func (p *parser) parseDecl(sync map[token.Token]bool) ast.Decl { if p.trace { defer un(trace(p, "Declaration")) } @@ -2438,7 +2487,7 @@ func (p *parser) parseDecl(sync func(*parser)) ast.Decl { default: pos := p.pos p.errorExpected(pos, "declaration") - sync(p) + p.advance(sync) return &ast.BadDecl{From: pos, To: p.pos} } @@ -2488,7 +2537,7 @@ func (p *parser) parseFile() *ast.File { if p.mode&ImportsOnly == 0 { // rest of package body for p.tok != token.EOF { - decls = append(decls, p.parseDecl(syncDecl)) + decls = append(decls, p.parseDecl(declStart)) } } } diff --git a/libgo/go/go/parser/short_test.go b/libgo/go/go/parser/short_test.go index 6f8ef6b..49bb681 100644 --- a/libgo/go/go/parser/short_test.go +++ b/libgo/go/go/parser/short_test.go @@ -58,10 +58,10 @@ func TestValid(t *testing.T) { var invalids = []string{ `foo /* ERROR "expected 'package'" */ !`, - `package p; func f() { if { /* ERROR "expected operand" */ } };`, - `package p; func f() { if ; { /* ERROR "expected operand" */ } };`, - `package p; func f() { if f(); { /* ERROR "expected operand" */ } };`, - `package p; func f() { if _ /* ERROR "expected boolean expression" */ = range x; true {} };`, + `package p; func f() { if { /* ERROR "missing condition" */ } };`, + `package p; func f() { if ; /* ERROR "missing condition" */ {} };`, + `package p; func f() { if f(); /* ERROR "missing condition" */ {} };`, + `package p; func f() { if _ = range /* ERROR "expected operand" */ x; true {} };`, `package p; func f() { switch _ /* ERROR "expected switch expression" */ = range x; true {} };`, `package p; func f() { for _ = range x ; /* ERROR "expected '{'" */ ; {} };`, `package p; func f() { for ; ; _ = range /* ERROR "expected operand" */ x {} };`, @@ -85,7 +85,7 @@ var invalids = []string{ `package p; func f() { _ = (<-<- /* ERROR "expected 'chan'" */ chan int)(nil) };`, `package p; func f() { _ = (<-chan<-chan<-chan<-chan<-chan<- /* ERROR "expected channel type" */ int)(nil) };`, `package p; func f() { var t []int; t /* ERROR "expected identifier on left side of :=" */ [0] := 0 };`, - `package p; func f() { if x := g(); x = /* ERROR "expected '=='" */ 0 {}};`, + `package p; func f() { if x := g(); x /* ERROR "expected boolean expression" */ = 0 {}};`, `package p; func f() { _ = x = /* ERROR "expected '=='" */ 0 {}};`, `package p; func f() { _ = 1 == func()int { var x bool; x = x = /* ERROR "expected '=='" */ true; return x }() };`, `package p; func f() { var s []int; _ = s[] /* ERROR "expected operand" */ };`, diff --git a/libgo/go/go/parser/testdata/commas.src b/libgo/go/go/parser/testdata/commas.src index af6e706..e0603cf 100644 --- a/libgo/go/go/parser/testdata/commas.src +++ b/libgo/go/go/parser/testdata/commas.src @@ -8,12 +8,12 @@ package p var _ = []int{ - 0 /* ERROR "missing ','" */ + 0/* ERROR HERE "missing ','" */ } var _ = []int{ 0, 1, 2, - 3 /* ERROR "missing ','" */ + 3/* ERROR HERE "missing ','" */ } diff --git a/libgo/go/go/printer/nodes.go b/libgo/go/go/printer/nodes.go index 3e2ff4f..1de7cd8 100644 --- a/libgo/go/go/printer/nodes.go +++ b/libgo/go/go/printer/nodes.go @@ -12,6 +12,7 @@ import ( "bytes" "go/ast" "go/token" + "math" "strconv" "strings" "unicode" @@ -31,8 +32,10 @@ import ( // Print as many newlines as necessary (but at least min newlines) to get to // the current line. ws is printed before the first line break. If newSection -// is set, the first line break is printed as formfeed. Returns true if any -// line break was printed; returns false otherwise. +// is set, the first line break is printed as formfeed. Returns 0 if no line +// breaks were printed, returns 1 if there was exactly one newline printed, +// and returns a value > 1 if there was a formfeed or more than one newline +// printed. // // TODO(gri): linebreak may add too many lines if the next statement at "line" // is preceded by comments because the computation of n assumes @@ -42,7 +45,7 @@ import ( // linebreaks. At the moment there is no easy way to know about // future (not yet interspersed) comments in this function. // -func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (printedBreak bool) { +func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (nbreaks int) { n := nlimit(line - p.pos.Line) if n < min { n = min @@ -52,11 +55,12 @@ func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (prin if newSection { p.print(formfeed) n-- + nbreaks = 2 } + nbreaks += n for ; n > 0; n-- { p.print(newline) } - printedBreak = true } return } @@ -111,9 +115,11 @@ func (p *printer) identList(list []*ast.Ident, indent bool) { if !indent { mode = noIndent } - p.exprList(token.NoPos, xlist, 1, mode, token.NoPos) + p.exprList(token.NoPos, xlist, 1, mode, token.NoPos, false) } +const filteredMsg = "contains filtered or unexported fields" + // Print a list of expressions. If the list spans multiple // source lines, the original line breaks are respected between // expressions. @@ -121,8 +127,18 @@ func (p *printer) identList(list []*ast.Ident, indent bool) { // TODO(gri) Consider rewriting this to be independent of []ast.Expr // so that we can use the algorithm for any kind of list // (e.g., pass list via a channel over which to range). -func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos) { +func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos, isIncomplete bool) { if len(list) == 0 { + if isIncomplete { + prev := p.posFor(prev0) + next := p.posFor(next0) + if prev.IsValid() && prev.Line == next.Line { + p.print("/* " + filteredMsg + " */") + } else { + p.print(newline) + p.print(indent, "// "+filteredMsg, unindent, newline) + } + } return } @@ -141,23 +157,26 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp } p.expr0(x, depth) } + if isIncomplete { + p.print(token.COMMA, blank, "/* "+filteredMsg+" */") + } return } // list entries span multiple lines; // use source code positions to guide line breaks - // don't add extra indentation if noIndent is set; - // i.e., pretend that the first line is already indented + // Don't add extra indentation if noIndent is set; + // i.e., pretend that the first line is already indented. ws := ignore if mode&noIndent == 0 { ws = indent } - // the first linebreak is always a formfeed since this section must not - // depend on any previous formatting + // The first linebreak is always a formfeed since this section must not + // depend on any previous formatting. prevBreak := -1 // index of last expression that was followed by a linebreak - if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) { + if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) > 0 { ws = ignore prevBreak = 0 } @@ -165,22 +184,29 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp // initialize expression/key size: a zero value indicates expr/key doesn't fit on a single line size := 0 + // We use the ratio between the geometric mean of the previous key sizes and + // the current size to determine if there should be a break in the alignment. + // To compute the geometric mean we accumulate the ln(size) values (lnsum) + // and the number of sizes included (count). + lnsum := 0.0 + count := 0 + // print all list elements prevLine := prev.Line for i, x := range list { line = p.lineFor(x.Pos()) - // determine if the next linebreak, if any, needs to use formfeed: + // Determine if the next linebreak, if any, needs to use formfeed: // in general, use the entire node size to make the decision; for - // key:value expressions, use the key size + // key:value expressions, use the key size. // TODO(gri) for a better result, should probably incorporate both // the key and the node size into the decision process useFF := true - // determine element size: all bets are off if we don't have + // Determine element size: All bets are off if we don't have // position information for the previous and next token (likely // generated code - simply ignore the size in this case by setting - // it to 0) + // it to 0). prevSize := size const infinity = 1e6 // larger than any source line size = p.nodeSize(x, infinity) @@ -195,40 +221,51 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp size = 0 } - // if the previous line and the current line had single- + // If the previous line and the current line had single- // line-expressions and the key sizes are small or the - // the ratio between the key sizes does not exceed a - // threshold, align columns and do not use formfeed + // ratio between the current key and the geometric mean + // if the previous key sizes does not exceed a threshold, + // align columns and do not use formfeed. if prevSize > 0 && size > 0 { - const smallSize = 20 - if prevSize <= smallSize && size <= smallSize { + const smallSize = 40 + if count == 0 || prevSize <= smallSize && size <= smallSize { useFF = false } else { - const r = 4 // threshold - ratio := float64(size) / float64(prevSize) - useFF = ratio <= 1.0/r || r <= ratio + const r = 2.5 // threshold + geomean := math.Exp(lnsum / float64(count)) // count > 0 + ratio := float64(size) / geomean + useFF = r*ratio <= 1 || r <= ratio } } needsLinebreak := 0 < prevLine && prevLine < line if i > 0 { - // use position of expression following the comma as + // Use position of expression following the comma as // comma position for correct comment placement, but - // only if the expression is on the same line + // only if the expression is on the same line. if !needsLinebreak { p.print(x.Pos()) } p.print(token.COMMA) needsBlank := true if needsLinebreak { - // lines are broken using newlines so comments remain aligned - // unless forceFF is set or there are multiple expressions on - // the same line in which case formfeed is used - if p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) { + // Lines are broken using newlines so comments remain aligned + // unless useFF is set or there are multiple expressions on + // the same line in which case formfeed is used. + nbreaks := p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) + if nbreaks > 0 { ws = ignore prevBreak = i needsBlank = false // we got a line break instead } + // If there was a new section or more than one new line + // (which means that the tabwriter will implicitly break + // the section), reset the geomean variables since we are + // starting a new group of elements with the next element. + if nbreaks > 1 { + lnsum = 0 + count = 0 + } } if needsBlank { p.print(blank) @@ -236,10 +273,10 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp } if len(list) > 1 && isPair && size > 0 && needsLinebreak { - // we have a key:value expression that fits onto one line + // We have a key:value expression that fits onto one line // and it's not on the same line as the prior expression: - // use a column for the key such that consecutive entries - // can align if possible + // Use a column for the key such that consecutive entries + // can align if possible. // (needsLinebreak is set if we started a new line before) p.expr(pair.Key) p.print(pair.Colon, token.COLON, vtab) @@ -248,12 +285,21 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp p.expr0(x, depth) } + if size > 0 { + lnsum += math.Log(float64(size)) + count++ + } + prevLine = line } if mode&commaTerm != 0 && next.IsValid() && p.pos.Line < next.Line { - // print a terminating comma if the next token is on a new line + // Print a terminating comma if the next token is on a new line. p.print(token.COMMA) + if isIncomplete { + p.print(newline) + p.print("// " + filteredMsg) + } if ws == ignore && mode&noIndent == 0 { // unindent if we indented p.print(unindent) @@ -262,6 +308,11 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp return } + if isIncomplete { + p.print(token.COMMA, newline) + p.print("// "+filteredMsg, newline) + } + if ws == ignore && mode&noIndent == 0 { // unindent if we indented p.print(unindent) @@ -296,7 +347,7 @@ func (p *printer) parameters(fields *ast.FieldList) { p.print(token.COMMA) } // separator if needed (linebreak or blank) - if needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) { + if needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) > 0 { // break line if the opening "(" or previous parameter ended on a different line ws = ignore } else if i > 0 { @@ -481,7 +532,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) p.print(formfeed) } p.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment - p.setLineComment("// contains filtered or unexported fields") + p.setLineComment("// " + filteredMsg) } } else { // interface @@ -667,7 +718,7 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) { if xline != yline && xline > 0 && yline > 0 { // at least one line break, but respect an extra empty line // in the source - if p.linebreak(yline, 1, ws, true) { + if p.linebreak(yline, 1, ws, true) > 0 { ws = ignore printBlank = false // no blank after line break } @@ -833,13 +884,13 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { } p.print(x.Lparen, token.LPAREN) if x.Ellipsis.IsValid() { - p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis) + p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis, false) p.print(x.Ellipsis, token.ELLIPSIS) if x.Rparen.IsValid() && p.lineFor(x.Ellipsis) < p.lineFor(x.Rparen) { p.print(token.COMMA, formfeed) } } else { - p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen) + p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen, false) } p.print(x.Rparen, token.RPAREN) if wasIndented { @@ -853,7 +904,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { } p.level++ p.print(x.Lbrace, token.LBRACE) - p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace) + p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace, x.Incomplete) // do not insert extra line break following a /*-style comment // before the closing '}' as it might break the code if there // is no trailing ',' @@ -1163,9 +1214,9 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) { if len(s.Lhs) > 1 && len(s.Rhs) > 1 { depth++ } - p.exprList(s.Pos(), s.Lhs, depth, 0, s.TokPos) + p.exprList(s.Pos(), s.Lhs, depth, 0, s.TokPos, false) p.print(blank, s.TokPos, s.Tok, blank) - p.exprList(s.TokPos, s.Rhs, depth, 0, token.NoPos) + p.exprList(s.TokPos, s.Rhs, depth, 0, token.NoPos, false) case *ast.GoStmt: p.print(token.GO, blank) @@ -1186,10 +1237,10 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) { // lead to more nicely formatted code in general. if p.indentList(s.Results) { p.print(indent) - p.exprList(s.Pos(), s.Results, 1, noIndent, token.NoPos) + p.exprList(s.Pos(), s.Results, 1, noIndent, token.NoPos, false) p.print(unindent) } else { - p.exprList(s.Pos(), s.Results, 1, 0, token.NoPos) + p.exprList(s.Pos(), s.Results, 1, 0, token.NoPos, false) } } @@ -1225,7 +1276,7 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) { case *ast.CaseClause: if s.List != nil { p.print(token.CASE, blank) - p.exprList(s.Pos(), s.List, 1, 0, s.Colon) + p.exprList(s.Pos(), s.List, 1, 0, s.Colon, false) } else { p.print(token.DEFAULT) } @@ -1377,7 +1428,7 @@ func (p *printer) valueSpec(s *ast.ValueSpec, keepType bool) { } if s.Values != nil { p.print(vtab, token.ASSIGN, blank) - p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos) + p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos, false) extraTabs-- } if s.Comment != nil { @@ -1459,7 +1510,7 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool) { } if s.Values != nil { p.print(blank, token.ASSIGN, blank) - p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos) + p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos, false) } p.setComment(s.Comment) diff --git a/libgo/go/go/printer/printer.go b/libgo/go/go/printer/printer.go index dbb4bbd..9143442 100644 --- a/libgo/go/go/printer/printer.go +++ b/libgo/go/go/printer/printer.go @@ -11,7 +11,6 @@ import ( "go/token" "io" "os" - "strconv" "strings" "text/tabwriter" "unicode" @@ -56,14 +55,15 @@ type printer struct { fset *token.FileSet // Current state - output []byte // raw printer result - indent int // current indentation - level int // level == 0: outside composite literal; level > 0: inside composite literal - mode pmode // current printer mode - impliedSemi bool // if set, a linebreak implies a semicolon - lastTok token.Token // last token printed (token.ILLEGAL if it's whitespace) - prevOpen token.Token // previous non-brace "open" token (, [, or token.ILLEGAL - wsbuf []whiteSpace // delayed white space + output []byte // raw printer result + indent int // current indentation + level int // level == 0: outside composite literal; level > 0: inside composite literal + mode pmode // current printer mode + endAlignment bool // if set, terminate alignment immediately + impliedSemi bool // if set, a linebreak implies a semicolon + lastTok token.Token // last token printed (token.ILLEGAL if it's whitespace) + prevOpen token.Token // previous non-brace "open" token (, [, or token.ILLEGAL + wsbuf []whiteSpace // delayed white space // Positions // The out position differs from the pos position when the result @@ -192,13 +192,13 @@ func (p *printer) linesFrom(line int) int { func (p *printer) posFor(pos token.Pos) token.Position { // not used frequently enough to cache entire token.Position - return p.fset.Position(pos) + return p.fset.PositionFor(pos, false /* absolute position */) } func (p *printer) lineFor(pos token.Pos) int { if pos != p.cachedPos { p.cachedPos = pos - p.cachedLine = p.fset.Position(pos).Line + p.cachedLine = p.fset.PositionFor(pos, false /* absolute position */).Line } return p.cachedLine } @@ -233,6 +233,20 @@ func (p *printer) writeIndent() { // writeByte writes ch n times to p.output and updates p.pos. // Only used to write formatting (white space) characters. func (p *printer) writeByte(ch byte, n int) { + if p.endAlignment { + // Ignore any alignment control character; + // and at the end of the line, break with + // a formfeed to indicate termination of + // existing columns. + switch ch { + case '\t', '\v': + ch = ' ' + case '\n', '\f': + ch = '\f' + p.endAlignment = false + } + } + if p.out.Column == 1 { // no need to write line directives before white space p.writeIndent() @@ -299,10 +313,15 @@ func (p *printer) writeString(pos token.Position, s string, isLit bool) { nlines := 0 var li int // index of last newline; valid if nlines > 0 for i := 0; i < len(s); i++ { - // Go tokens cannot contain '\f' - no need to look for it - if s[i] == '\n' { + // Raw string literals may contain any character except back quote (`). + if ch := s[i]; ch == '\n' || ch == '\f' { + // account for line break nlines++ li = i + // A line break inside a literal will break whatever column + // formatting is in place; ignore any further alignment through + // the end of the line. + p.endAlignment = true } } p.pos.Offset += len(s) @@ -622,24 +641,10 @@ func (p *printer) writeComment(comment *ast.Comment) { const linePrefix = "//line " if strings.HasPrefix(text, linePrefix) && (!pos.IsValid() || pos.Column == 1) { - // possibly a line directive - ldir := strings.TrimSpace(text[len(linePrefix):]) - if i := strings.LastIndex(ldir, ":"); i >= 0 { - if line, err := strconv.Atoi(ldir[i+1:]); err == nil && line > 0 { - // The line directive we are about to print changed - // the Filename and Line number used for subsequent - // tokens. We have to update our AST-space position - // accordingly and suspend indentation temporarily. - indent := p.indent - p.indent = 0 - defer func() { - p.pos.Filename = ldir[:i] - p.pos.Line = line - p.pos.Column = 1 - p.indent = indent - }() - } - } + // Possibly a //-style line directive. + // Suspend indentation temporarily to keep line directive valid. + defer func(indent int) { p.indent = indent }(p.indent) + p.indent = 0 } // shortcut common case of //-style comments diff --git a/libgo/go/go/printer/printer_test.go b/libgo/go/go/printer/printer_test.go index 5984d2c..27d46df 100644 --- a/libgo/go/go/printer/printer_test.go +++ b/libgo/go/go/printer/printer_test.go @@ -188,12 +188,14 @@ var data = []entry{ {"comments.input", "comments.golden", 0}, {"comments.input", "comments.x", export}, {"comments2.input", "comments2.golden", idempotent}, + {"alignment.input", "alignment.golden", idempotent}, {"linebreaks.input", "linebreaks.golden", idempotent}, {"expressions.input", "expressions.golden", idempotent}, {"expressions.input", "expressions.raw", rawFormat | idempotent}, {"declarations.input", "declarations.golden", 0}, {"statements.input", "statements.golden", 0}, {"slow.input", "slow.golden", idempotent}, + {"complit.input", "complit.x", export}, } func TestFiles(t *testing.T) { @@ -325,7 +327,7 @@ func fibo(n int) { comment := f.Comments[0].List[0] pos := comment.Pos() - if fset.Position(pos).Offset != 1 { + if fset.PositionFor(pos, false /* absolute position */).Offset != 1 { t.Error("expected offset 1") // error in test } @@ -422,6 +424,7 @@ func (t *t) foo(a, b, c int) int { t.Errorf("got ident %s; want %s", i2.Name, i1.Name) } + // here we care about the relative (line-directive adjusted) positions l1 := fset.Position(i1.Pos()).Line l2 := fset.Position(i2.Pos()).Line if l2 != l1 { @@ -710,3 +713,26 @@ type bar int // comment2 t.Errorf("got %q, want %q", buf.String(), bar) } } + +func TestIssue11151(t *testing.T) { + const src = "package p\t/*\r/1\r*\r/2*\r\r\r\r/3*\r\r+\r\r/4*/\n" + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "", src, parser.ParseComments) + if err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + Fprint(&buf, fset, f) + got := buf.String() + const want = "package p\t/*/1*\r/2*\r/3*+/4*/\n" // \r following opening /* should be stripped + if got != want { + t.Errorf("\ngot : %q\nwant: %q", got, want) + } + + // the resulting program must be valid + _, err = parser.ParseFile(fset, "", got, 0) + if err != nil { + t.Errorf("%v\norig: %q\ngot : %q", err, src, got) + } +} diff --git a/libgo/go/go/printer/testdata/alignment.golden b/libgo/go/go/printer/testdata/alignment.golden new file mode 100644 index 0000000..96086ed --- /dev/null +++ b/libgo/go/go/printer/testdata/alignment.golden @@ -0,0 +1,172 @@ +// Copyright 2018 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 alignment + +// ---------------------------------------------------------------------------- +// Examples from issue #7335. + +func main() { + z := MyStruct{ + Foo: "foo", + Bar: "bar", + Name: "name", + LongName: "longname", + Baz: "baz", + } + y := MyStruct{ + Foo: "foo", + Bar: "bar", + NameXX: "name", + LongNameXXXXXXXXXXXXX: "longname", + Baz: "baz", + } + z := MyStruct{ + Foo: "foo", + Bar: "bar", + Name: "name", + LongNameXXXXXXXXXXXXX: "longname", + Baz: "baz", + } +} + +// ---------------------------------------------------------------------------- +// Examples from issue #10392. + +var kcfg = KubeletConfig{ + Address: s.Address, + AllowPrivileged: s.AllowPrivileged, + HostNetworkSources: hostNetworkSources, + HostnameOverride: s.HostnameOverride, + RootDirectory: s.RootDirectory, + ConfigFile: s.Config, + ManifestURL: s.ManifestURL, + FileCheckFrequency: s.FileCheckFrequency, + HTTPCheckFrequency: s.HTTPCheckFrequency, + PodInfraContainerImage: s.PodInfraContainerImage, + SyncFrequency: s.SyncFrequency, + RegistryPullQPS: s.RegistryPullQPS, + RegistryBurst: s.RegistryBurst, + MinimumGCAge: s.MinimumGCAge, + MaxPerPodContainerCount: s.MaxPerPodContainerCount, + MaxContainerCount: s.MaxContainerCount, + ClusterDomain: s.ClusterDomain, + ClusterDNS: s.ClusterDNS, + Runonce: s.RunOnce, + Port: s.Port, + ReadOnlyPort: s.ReadOnlyPort, + CadvisorInterface: cadvisorInterface, + EnableServer: s.EnableServer, + EnableDebuggingHandlers: s.EnableDebuggingHandlers, + DockerClient: dockertools.ConnectToDockerOrDie(s.DockerEndpoint), + KubeClient: client, + MasterServiceNamespace: s.MasterServiceNamespace, + VolumePlugins: ProbeVolumePlugins(), + NetworkPlugins: ProbeNetworkPlugins(), + NetworkPluginName: s.NetworkPluginName, + StreamingConnectionIdleTimeout: s.StreamingConnectionIdleTimeout, + TLSOptions: tlsOptions, + ImageGCPolicy: imageGCPolicy, imageGCPolicy, + Cloud: cloud, + NodeStatusUpdateFrequency: s.NodeStatusUpdateFrequency, +} + +var a = A{ + Long: 1, + LongLong: 1, + LongLongLong: 1, + LongLongLongLong: 1, + LongLongLongLongLong: 1, + LongLongLongLongLongLong: 1, + LongLongLongLongLongLongLong: 1, + LongLongLongLongLongLongLongLong: 1, + Short: 1, + LongLongLongLongLongLongLongLongLong: 3, +} + +// ---------------------------------------------------------------------------- +// Examples from issue #22852. + +var fmtMap = map[string]string{ + "1": "123", + "12": "123", + "123": "123", + "1234": "123", + "12345": "123", + "123456": "123", + "12345678901234567890123456789": "123", + "abcde": "123", + "123456789012345678901234567890": "123", + "1234567": "123", + "abcdefghijklmnopqrstuvwxyzabcd": "123", + "abcd": "123", +} + +type Fmt struct { + abcdefghijklmnopqrstuvwx string + abcdefghijklmnopqrstuvwxy string + abcdefghijklmnopqrstuvwxyz string + abcdefghijklmnopqrstuvwxyza string + abcdefghijklmnopqrstuvwxyzab string + abcdefghijklmnopqrstuvwxyzabc string + abcde string + abcdefghijklmnopqrstuvwxyzabcde string + abcdefg string +} + +func main() { + _ := Fmt{ + abcdefghijklmnopqrstuvwx: "foo", + abcdefghijklmnopqrstuvwxyza: "foo", + abcdefghijklmnopqrstuvwxyzab: "foo", + abcdefghijklmnopqrstuvwxyzabc: "foo", + abcde: "foo", + abcdefghijklmnopqrstuvwxyzabcde: "foo", + abcdefg: "foo", + abcdefghijklmnopqrstuvwxy: "foo", + abcdefghijklmnopqrstuvwxyz: "foo", + } +} + +// ---------------------------------------------------------------------------- +// Examples from issue #26352. + +var _ = map[int]string{ + 1: "", + + 12345678901234567890123456789: "", + 12345678901234567890123456789012345678: "", +} + +func f() { + _ = map[int]string{ + 1: "", + + 12345678901234567: "", + 12345678901234567890123456789012345678901: "", + } +} + +// ---------------------------------------------------------------------------- +// Examples from issue #26930. + +var _ = S{ + F1: []string{}, + F2____: []string{}, +} + +var _ = S{ + F1: []string{}, + F2____: []string{}, +} + +var _ = S{ + F1____: []string{}, + F2: []string{}, +} + +var _ = S{ + F1____: []string{}, + F2: []string{}, +} diff --git a/libgo/go/go/printer/testdata/alignment.input b/libgo/go/go/printer/testdata/alignment.input new file mode 100644 index 0000000..323d268 --- /dev/null +++ b/libgo/go/go/printer/testdata/alignment.input @@ -0,0 +1,179 @@ +// Copyright 2018 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 alignment + +// ---------------------------------------------------------------------------- +// Examples from issue #7335. + +func main() { + z := MyStruct{ + Foo: "foo", + Bar: "bar", + Name: "name", + LongName: "longname", + Baz: "baz", + } + y := MyStruct{ + Foo: "foo", + Bar: "bar", + NameXX: "name", + LongNameXXXXXXXXXXXXX: "longname", + Baz: "baz", + } + z := MyStruct{ + Foo: "foo", + Bar: "bar", + Name: "name", + LongNameXXXXXXXXXXXXX: "longname", + Baz: "baz", + } +} + +// ---------------------------------------------------------------------------- +// Examples from issue #10392. + +var kcfg = KubeletConfig{ + Address: s.Address, + AllowPrivileged: s.AllowPrivileged, + HostNetworkSources: hostNetworkSources, + HostnameOverride: s.HostnameOverride, + RootDirectory: s.RootDirectory, + ConfigFile: s.Config, + ManifestURL: s.ManifestURL, + FileCheckFrequency: s.FileCheckFrequency, + HTTPCheckFrequency: s.HTTPCheckFrequency, + PodInfraContainerImage: s.PodInfraContainerImage, + SyncFrequency: s.SyncFrequency, + RegistryPullQPS: s.RegistryPullQPS, + RegistryBurst: s.RegistryBurst, + MinimumGCAge: s.MinimumGCAge, + MaxPerPodContainerCount: s.MaxPerPodContainerCount, + MaxContainerCount: s.MaxContainerCount, + ClusterDomain: s.ClusterDomain, + ClusterDNS: s.ClusterDNS, + Runonce: s.RunOnce, + Port: s.Port, + ReadOnlyPort: s.ReadOnlyPort, + CadvisorInterface: cadvisorInterface, + EnableServer: s.EnableServer, + EnableDebuggingHandlers: s.EnableDebuggingHandlers, + DockerClient: dockertools.ConnectToDockerOrDie(s.DockerEndpoint), + KubeClient: client, + MasterServiceNamespace: s.MasterServiceNamespace, + VolumePlugins: ProbeVolumePlugins(), + NetworkPlugins: ProbeNetworkPlugins(), + NetworkPluginName: s.NetworkPluginName, + StreamingConnectionIdleTimeout: s.StreamingConnectionIdleTimeout, + TLSOptions: tlsOptions, + ImageGCPolicy: imageGCPolicy,imageGCPolicy, + Cloud: cloud, + NodeStatusUpdateFrequency: s.NodeStatusUpdateFrequency, +} + +var a = A{ + Long: 1, + LongLong: 1, + LongLongLong: 1, + LongLongLongLong: 1, + LongLongLongLongLong: 1, + LongLongLongLongLongLong: 1, + LongLongLongLongLongLongLong: 1, + LongLongLongLongLongLongLongLong: 1, + Short: 1, + LongLongLongLongLongLongLongLongLong: 3, +} + +// ---------------------------------------------------------------------------- +// Examples from issue #22852. + +var fmtMap = map[string]string{ + "1": "123", + "12": "123", + "123": "123", + "1234": "123", + "12345": "123", + "123456": "123", + "12345678901234567890123456789": "123", + "abcde": "123", + "123456789012345678901234567890": "123", + "1234567": "123", + "abcdefghijklmnopqrstuvwxyzabcd": "123", + "abcd": "123", +} + +type Fmt struct { + abcdefghijklmnopqrstuvwx string + abcdefghijklmnopqrstuvwxy string + abcdefghijklmnopqrstuvwxyz string + abcdefghijklmnopqrstuvwxyza string + abcdefghijklmnopqrstuvwxyzab string + abcdefghijklmnopqrstuvwxyzabc string + abcde string + abcdefghijklmnopqrstuvwxyzabcde string + abcdefg string +} + +func main() { + _ := Fmt{ + abcdefghijklmnopqrstuvwx: "foo", + abcdefghijklmnopqrstuvwxyza: "foo", + abcdefghijklmnopqrstuvwxyzab: "foo", + abcdefghijklmnopqrstuvwxyzabc: "foo", + abcde: "foo", + abcdefghijklmnopqrstuvwxyzabcde: "foo", + abcdefg: "foo", + abcdefghijklmnopqrstuvwxy: "foo", + abcdefghijklmnopqrstuvwxyz: "foo", + } +} + +// ---------------------------------------------------------------------------- +// Examples from issue #26352. + +var _ = map[int]string{ + 1: "", + + 12345678901234567890123456789: "", + 12345678901234567890123456789012345678: "", +} + +func f() { + _ = map[int]string{ + 1: "", + + 12345678901234567: "", + 12345678901234567890123456789012345678901: "", + } +} + +// ---------------------------------------------------------------------------- +// Examples from issue #26930. + +var _ = S{ + F1: []string{ + }, + F2____: []string{}, +} + +var _ = S{ + F1: []string{ + + + }, + F2____: []string{}, +} + +var _ = S{ + F1____: []string{ + }, + F2: []string{}, +} + +var _ = S{ + F1____: []string{ + + }, + F2: []string{}, +} diff --git a/libgo/go/go/printer/testdata/comments.golden b/libgo/go/go/printer/testdata/comments.golden index e1818e5..1a21fff 100644 --- a/libgo/go/go/printer/testdata/comments.golden +++ b/libgo/go/go/printer/testdata/comments.golden @@ -702,8 +702,8 @@ func _() { //line foo:2 _ = 2 - // The following is not a legal line directive (negative line number): - //line foo:-3 + // The following is not a legal line directive (missing colon): +//line foo -3 _ = 3 } diff --git a/libgo/go/go/printer/testdata/comments.input b/libgo/go/go/printer/testdata/comments.input index f3eda12..aa428a2 100644 --- a/libgo/go/go/printer/testdata/comments.input +++ b/libgo/go/go/printer/testdata/comments.input @@ -699,8 +699,8 @@ func _() { //line foo:2 _ = 2 -// The following is not a legal line directive (negative line number): -//line foo:-3 +// The following is not a legal line directive (missing colon): +//line foo -3 _ = 3 } diff --git a/libgo/go/go/printer/testdata/complit.input b/libgo/go/go/printer/testdata/complit.input new file mode 100644 index 0000000..82806a4 --- /dev/null +++ b/libgo/go/go/printer/testdata/complit.input @@ -0,0 +1,65 @@ +// Copyright 2018 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 complit + +var ( + // Multi-line declarations + V1 = T{ + F1: "hello", + f2: 1, + } + V2 = T{ + f2: 1, + F1: "hello", + } + V3 = T{ + F1: "hello", + F2: T2{ + A: "world", + b: "hidden", + }, + f3: T2{ + A: "world", + }, + } + V4 = T{ + f2: 1, + } + + // Single-line declarations + V5 = T{F1: "hello", f2: 1} + V6 = T{f2: 1, F1: "hello"} + V7 = T{f2: 1} + + // Mixed-mode declarations + V8 = T{ + F1: "hello", f2: 1, + F3: "world", + f4: 2} + V9 = T{ + f2: 1, F1: "hello",} + V10 = T{ + F1: "hello", f2: 1, + f3: 2, + F4: "world", f5: 3, + } + + // Other miscellaneous declarations + V11 = T{ + t{ + A: "world", + b: "hidden", + }, + f2: t{ + A: "world", + b: "hidden", + }, + } + V12 = T{ + F1: make(chan int), + f2: []int{}, + F3: make(map[int]string), f4: 1, + } +)
\ No newline at end of file diff --git a/libgo/go/go/printer/testdata/complit.x b/libgo/go/go/printer/testdata/complit.x new file mode 100644 index 0000000..458ac61 --- /dev/null +++ b/libgo/go/go/printer/testdata/complit.x @@ -0,0 +1,62 @@ +package complit + +var ( + // Multi-line declarations + V1 = T{ + F1: "hello", + // contains filtered or unexported fields + } + V2 = T{ + + F1: "hello", + // contains filtered or unexported fields + } + V3 = T{ + F1: "hello", + F2: T2{ + A: "world", + // contains filtered or unexported fields + }, + // contains filtered or unexported fields + } + V4 = T{ + // contains filtered or unexported fields + } + + // Single-line declarations + V5 = T{F1: "hello", /* contains filtered or unexported fields */} + V6 = T{F1: "hello", /* contains filtered or unexported fields */} + V7 = T{/* contains filtered or unexported fields */} + + // Mixed-mode declarations + V8 = T{ + F1: "hello", + F3: "world", + // contains filtered or unexported fields + } + V9 = T{ + F1: "hello", + // contains filtered or unexported fields + } + V10 = T{ + F1: "hello", + + F4: "world", + // contains filtered or unexported fields + } + + // Other miscellaneous declarations + V11 = T{ + t{ + A: "world", + // contains filtered or unexported fields + }, + // contains filtered or unexported fields + } + V12 = T{ + F1: make(chan int), + + F3: make(map[int]string), + // contains filtered or unexported fields + } +) diff --git a/libgo/go/go/printer/testdata/declarations.golden b/libgo/go/go/printer/testdata/declarations.golden index bebc0ea..fe0f783 100644 --- a/libgo/go/go/printer/testdata/declarations.golden +++ b/libgo/go/go/printer/testdata/declarations.golden @@ -688,8 +688,8 @@ var _ = T1{ // not aligned var _ = T2{ - a: x, - b: y, + a: x, + b: y, ccccccccccccccccccccc: z, } @@ -703,8 +703,8 @@ var _ = T3{ // not aligned var _ = T4{ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: x, - b: y, - c: z, + b: y, + c: z, } // no alignment of map composite entries if they are not the first entry on a line diff --git a/libgo/go/go/printer/testdata/linebreaks.golden b/libgo/go/go/printer/testdata/linebreaks.golden index 006cf17..17d2b5c 100644 --- a/libgo/go/go/printer/testdata/linebreaks.golden +++ b/libgo/go/go/printer/testdata/linebreaks.golden @@ -256,7 +256,7 @@ func _( ) { } -// Example from issue 2597. +// Example from issue #2597. func ManageStatus0( in <-chan *Status, req <-chan Request, @@ -272,4 +272,24 @@ func ManageStatus1( ) { } +// Example from issue #9064. +func (y *y) xerrors() error { + _ = "xerror.test" //TODO- + _ = []byte(` +foo bar foo bar foo bar +`) //TODO- +} + +func _() { + _ = "abc" // foo + _ = `abc_0123456789_` // foo +} + +func _() { + _ = "abc" // foo + _ = `abc +0123456789 +` // foo +} + // There should be exactly one linebreak after this comment. diff --git a/libgo/go/go/printer/testdata/linebreaks.input b/libgo/go/go/printer/testdata/linebreaks.input index e782bb0..9e714f3 100644 --- a/libgo/go/go/printer/testdata/linebreaks.input +++ b/libgo/go/go/printer/testdata/linebreaks.input @@ -252,7 +252,7 @@ func _( y T, ) {} -// Example from issue 2597. +// Example from issue #2597. func ManageStatus0( in <-chan *Status, req <-chan Request, @@ -267,5 +267,25 @@ func ManageStatus1( TargetHistorySize int, ) { } - + +// Example from issue #9064. +func (y *y) xerrors() error { + _ = "xerror.test" //TODO- + _ = []byte(` +foo bar foo bar foo bar +`) //TODO- +} + +func _() { + _ = "abc" // foo + _ = `abc_0123456789_` // foo +} + +func _() { + _ = "abc" // foo + _ = `abc +0123456789 +` // foo +} + // There should be exactly one linebreak after this comment. diff --git a/libgo/go/go/scanner/scanner.go b/libgo/go/go/scanner/scanner.go index a86e4eb..23bbb28 100644 --- a/libgo/go/go/scanner/scanner.go +++ b/libgo/go/go/scanner/scanner.go @@ -141,46 +141,26 @@ func (s *Scanner) error(offs int, msg string) { s.ErrorCount++ } -var prefix = []byte("//line ") - -func (s *Scanner) interpretLineComment(text []byte) { - if bytes.HasPrefix(text, prefix) { - // get filename and line number, if any - if i := bytes.LastIndex(text, []byte{':'}); i > 0 { - if line, err := strconv.Atoi(string(text[i+1:])); err == nil && line > 0 { - // valid //line filename:line comment - filename := string(bytes.TrimSpace(text[len(prefix):i])) - if filename != "" { - filename = filepath.Clean(filename) - if !filepath.IsAbs(filename) { - // make filename relative to current directory - filename = filepath.Join(s.dir, filename) - } - } - // update scanner position - s.file.AddLineInfo(s.lineOffset+len(text)+1, filename, line) // +len(text)+1 since comment applies to next line - } - } - } -} - func (s *Scanner) scanComment() string { // initial '/' already consumed; s.ch == '/' || s.ch == '*' offs := s.offset - 1 // position of initial '/' - hasCR := false + next := -1 // position immediately following the comment; < 0 means invalid comment + numCR := 0 if s.ch == '/' { //-style comment + // (the final '\n' is not considered part of the comment) s.next() for s.ch != '\n' && s.ch >= 0 { if s.ch == '\r' { - hasCR = true + numCR++ } s.next() } - if offs == s.lineOffset { - // comment starts at the beginning of the current line - s.interpretLineComment(s.src[offs:s.offset]) + // if we are at '\n', the position following the comment is afterwards + next = s.offset + if s.ch == '\n' { + next++ } goto exit } @@ -190,11 +170,12 @@ func (s *Scanner) scanComment() string { for s.ch >= 0 { ch := s.ch if ch == '\r' { - hasCR = true + numCR++ } s.next() if ch == '*' && s.ch == '/' { s.next() + next = s.offset goto exit } } @@ -203,13 +184,104 @@ func (s *Scanner) scanComment() string { exit: lit := s.src[offs:s.offset] - if hasCR { - lit = stripCR(lit) + + // On Windows, a (//-comment) line may end in "\r\n". + // Remove the final '\r' before analyzing the text for + // line directives (matching the compiler). Remove any + // other '\r' afterwards (matching the pre-existing be- + // havior of the scanner). + if numCR > 0 && len(lit) >= 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' { + lit = lit[:len(lit)-1] + numCR-- + } + + // interpret line directives + // (//line directives must start at the beginning of the current line) + if next >= 0 /* implies valid comment */ && (lit[1] == '*' || offs == s.lineOffset) && bytes.HasPrefix(lit[2:], prefix) { + s.updateLineInfo(next, offs, lit) + } + + if numCR > 0 { + lit = stripCR(lit, lit[1] == '*') } return string(lit) } +var prefix = []byte("line ") + +// updateLineInfo parses the incoming comment text at offset offs +// as a line directive. If successful, it updates the line info table +// for the position next per the line directive. +func (s *Scanner) updateLineInfo(next, offs int, text []byte) { + // extract comment text + if text[1] == '*' { + text = text[:len(text)-2] // lop off trailing "*/" + } + text = text[7:] // lop off leading "//line " or "/*line " + offs += 7 + + i, n, ok := trailingDigits(text) + if i == 0 { + return // ignore (not a line directive) + } + // i > 0 + + if !ok { + // text has a suffix :xxx but xxx is not a number + s.error(offs+i, "invalid line number: "+string(text[i:])) + return + } + + var line, col int + i2, n2, ok2 := trailingDigits(text[:i-1]) + if ok2 { + //line filename:line:col + i, i2 = i2, i + line, col = n2, n + if col == 0 { + s.error(offs+i2, "invalid column number: "+string(text[i2:])) + return + } + text = text[:i2-1] // lop off ":col" + } else { + //line filename:line + line = n + } + + if line == 0 { + s.error(offs+i, "invalid line number: "+string(text[i:])) + return + } + + // If we have a column (//line filename:line:col form), + // an empty filename means to use the previous filename. + filename := string(text[:i-1]) // lop off ":line", and trim white space + if filename == "" && ok2 { + filename = s.file.Position(s.file.Pos(offs)).Filename + } else if filename != "" { + // Put a relative filename in the current directory. + // This is for compatibility with earlier releases. + // See issue 26671. + filename = filepath.Clean(filename) + if !filepath.IsAbs(filename) { + filename = filepath.Join(s.dir, filename) + } + } + + s.file.AddLineColumnInfo(next, filename, line, col) +} + +func trailingDigits(text []byte) (int, int, bool) { + i := bytes.LastIndexByte(text, ':') // look from right (Windows filenames may contain ':') + if i < 0 { + return 0, 0, false // no ":" + } + // i >= 0 + n, err := strconv.ParseUint(string(text[i+1:]), 10, 0) + return i + 1, int(n), err == nil +} + func (s *Scanner) findLineEnd() bool { // initial '/' already consumed @@ -480,11 +552,16 @@ func (s *Scanner) scanString() string { return string(s.src[offs:s.offset]) } -func stripCR(b []byte) []byte { +func stripCR(b []byte, comment bool) []byte { c := make([]byte, len(b)) i := 0 - for _, ch := range b { - if ch != '\r' { + for j, ch := range b { + // In a /*-style comment, don't strip \r from *\r/ (incl. + // sequences of \r from *\r\r...\r/) since the resulting + // */ would terminate the comment too early unless the \r + // is immediately following the opening /* in which case + // it's ok because /*/ is not closed yet (issue #11151). + if ch != '\r' || comment && i > len("/*") && c[i-1] == '*' && j+1 < len(b) && b[j+1] == '/' { c[i] = ch i++ } @@ -514,7 +591,7 @@ func (s *Scanner) scanRawString() string { lit := s.src[offs:s.offset] if hasCR { - lit = stripCR(lit) + lit = stripCR(lit, false) } return string(lit) diff --git a/libgo/go/go/scanner/scanner_test.go b/libgo/go/go/scanner/scanner_test.go index ff41c03..0aad368 100644 --- a/libgo/go/go/scanner/scanner_test.go +++ b/libgo/go/go/scanner/scanner_test.go @@ -45,6 +45,8 @@ var tokens = [...]elt{ {token.COMMENT, "/* a comment */", special}, {token.COMMENT, "// a comment \n", special}, {token.COMMENT, "/*\r*/", special}, + {token.COMMENT, "/**\r/*/", special}, // issue 11151 + {token.COMMENT, "/**\r\r/*/", special}, {token.COMMENT, "//\r\n", special}, // Identifiers and basic type literals @@ -203,7 +205,9 @@ func newlineCount(s string) int { func checkPos(t *testing.T, lit string, p token.Pos, expected token.Position) { pos := fset.Position(p) - if pos.Filename != expected.Filename { + // Check cleaned filenames so that we don't have to worry about + // different os.PathSeparator values. + if pos.Filename != expected.Filename && filepath.Clean(pos.Filename) != filepath.Clean(expected.Filename) { t.Errorf("bad filename for %q: got %s, expected %s", lit, pos.Filename, expected.Filename) } if pos.Offset != expected.Offset { @@ -270,7 +274,7 @@ func TestScan(t *testing.T) { switch e.tok { case token.COMMENT: // no CRs in comments - elit = string(stripCR([]byte(e.lit))) + elit = string(stripCR([]byte(e.lit), e.lit[1] == '*')) //-style comment literal doesn't contain newline if elit[1] == '/' { elit = elit[0 : len(elit)-1] @@ -284,7 +288,7 @@ func TestScan(t *testing.T) { // no CRs in raw string literals elit = e.lit if elit[0] == '`' { - elit = string(stripCR([]byte(elit))) + elit = string(stripCR([]byte(elit), false)) } } else if e.tok.IsKeyword() { elit = e.lit @@ -309,6 +313,26 @@ func TestScan(t *testing.T) { } } +func TestStripCR(t *testing.T) { + for _, test := range []struct{ have, want string }{ + {"//\n", "//\n"}, + {"//\r\n", "//\n"}, + {"//\r\r\r\n", "//\n"}, + {"//\r*\r/\r\n", "//*/\n"}, + {"/**/", "/**/"}, + {"/*\r/*/", "/*/*/"}, + {"/*\r*/", "/**/"}, + {"/**\r/*/", "/**\r/*/"}, + {"/*\r/\r*\r/*/", "/*/*\r/*/"}, + {"/*\r\r\r\r*/", "/**/"}, + } { + got := string(stripCR([]byte(test.have), len(test.have) >= 2 && test.have[1] == '*')) + if got != test.want { + t.Errorf("stripCR(%q) = %q; want %q", test.have, got, test.want) + } + } +} + func checkSemi(t *testing.T, line string, mode Mode) { var S Scanner file := fset.AddFile("TestSemis", fset.Base(), len(line)) @@ -481,69 +505,130 @@ func TestSemis(t *testing.T) { } type segment struct { - srcline string // a line of source text - filename string // filename for current token - line int // line number for current token + srcline string // a line of source text + filename string // filename for current token; error message for invalid line directives + line, column int // line and column for current token; error position for invalid line directives } var segments = []segment{ // exactly one token per line since the test consumes one token per segment - {" line1", filepath.Join("dir", "TestLineComments"), 1}, - {"\nline2", filepath.Join("dir", "TestLineComments"), 2}, - {"\nline3 //line File1.go:100", filepath.Join("dir", "TestLineComments"), 3}, // bad line comment, ignored - {"\nline4", filepath.Join("dir", "TestLineComments"), 4}, - {"\n//line File1.go:100\n line100", filepath.Join("dir", "File1.go"), 100}, - {"\n//line \t :42\n line1", "", 42}, - {"\n//line File2.go:200\n line200", filepath.Join("dir", "File2.go"), 200}, - {"\n//line foo\t:42\n line42", filepath.Join("dir", "foo"), 42}, - {"\n //line foo:42\n line44", filepath.Join("dir", "foo"), 44}, // bad line comment, ignored - {"\n//line foo 42\n line46", filepath.Join("dir", "foo"), 46}, // bad line comment, ignored - {"\n//line foo:42 extra text\n line48", filepath.Join("dir", "foo"), 48}, // bad line comment, ignored - {"\n//line ./foo:42\n line42", filepath.Join("dir", "foo"), 42}, - {"\n//line a/b/c/File1.go:100\n line100", filepath.Join("dir", "a", "b", "c", "File1.go"), 100}, + {" line1", "TestLineDirectives", 1, 3}, + {"\nline2", "TestLineDirectives", 2, 1}, + {"\nline3 //line File1.go:100", "TestLineDirectives", 3, 1}, // bad line comment, ignored + {"\nline4", "TestLineDirectives", 4, 1}, + {"\n//line File1.go:100\n line100", "File1.go", 100, 0}, + {"\n//line \t :42\n line1", " \t ", 42, 0}, + {"\n//line File2.go:200\n line200", "File2.go", 200, 0}, + {"\n//line foo\t:42\n line42", "foo\t", 42, 0}, + {"\n //line foo:42\n line43", "foo\t", 44, 0}, // bad line comment, ignored (use existing, prior filename) + {"\n//line foo 42\n line44", "foo\t", 46, 0}, // bad line comment, ignored (use existing, prior filename) + {"\n//line /bar:42\n line45", "/bar", 42, 0}, + {"\n//line ./foo:42\n line46", "foo", 42, 0}, + {"\n//line a/b/c/File1.go:100\n line100", "a/b/c/File1.go", 100, 0}, + {"\n//line c:\\bar:42\n line200", "c:\\bar", 42, 0}, + {"\n//line c:\\dir\\File1.go:100\n line201", "c:\\dir\\File1.go", 100, 0}, + + // tests for new line directive syntax + {"\n//line :100\na1", "", 100, 0}, // missing filename means empty filename + {"\n//line bar:100\nb1", "bar", 100, 0}, + {"\n//line :100:10\nc1", "bar", 100, 10}, // missing filename means current filename + {"\n//line foo:100:10\nd1", "foo", 100, 10}, + + {"\n/*line :100*/a2", "", 100, 0}, // missing filename means empty filename + {"\n/*line bar:100*/b2", "bar", 100, 0}, + {"\n/*line :100:10*/c2", "bar", 100, 10}, // missing filename means current filename + {"\n/*line foo:100:10*/d2", "foo", 100, 10}, + {"\n/*line foo:100:10*/ e2", "foo", 100, 14}, // line-directive relative column + {"\n/*line foo:100:10*/\n\nf2", "foo", 102, 1}, // absolute column since on new line +} + +var dirsegments = []segment{ + // exactly one token per line since the test consumes one token per segment + {" line1", "TestLineDir/TestLineDirectives", 1, 3}, + {"\n//line File1.go:100\n line100", "TestLineDir/File1.go", 100, 0}, } -var unixsegments = []segment{ - {"\n//line /bar:42\n line42", "/bar", 42}, +var dirUnixSegments = []segment{ + {"\n//line /bar:42\n line42", "/bar", 42, 0}, } -var winsegments = []segment{ - {"\n//line c:\\bar:42\n line42", "c:\\bar", 42}, - {"\n//line c:\\dir\\File1.go:100\n line100", "c:\\dir\\File1.go", 100}, +var dirWindowsSegments = []segment{ + {"\n//line c:\\bar:42\n line42", "c:\\bar", 42, 0}, } -// Verify that comments of the form "//line filename:line" are interpreted correctly. -func TestLineComments(t *testing.T) { - segs := segments +// Verify that line directives are interpreted correctly. +func TestLineDirectives(t *testing.T) { + testSegments(t, segments, "TestLineDirectives") + testSegments(t, dirsegments, "TestLineDir/TestLineDirectives") if runtime.GOOS == "windows" { - segs = append(segs, winsegments...) + testSegments(t, dirWindowsSegments, "TestLineDir/TestLineDirectives") } else { - segs = append(segs, unixsegments...) + testSegments(t, dirUnixSegments, "TestLineDir/TestLineDirectives") } +} - // make source +func testSegments(t *testing.T, segments []segment, filename string) { var src string - for _, e := range segs { + for _, e := range segments { src += e.srcline } // verify scan var S Scanner - file := fset.AddFile(filepath.Join("dir", "TestLineComments"), fset.Base(), len(src)) - S.Init(file, []byte(src), nil, dontInsertSemis) - for _, s := range segs { + file := fset.AddFile(filename, fset.Base(), len(src)) + S.Init(file, []byte(src), func(pos token.Position, msg string) { t.Error(Error{pos, msg}) }, dontInsertSemis) + for _, s := range segments { p, _, lit := S.Scan() pos := file.Position(p) checkPos(t, lit, p, token.Position{ Filename: s.filename, Offset: pos.Offset, Line: s.line, - Column: pos.Column, + Column: s.column, }) } if S.ErrorCount != 0 { - t.Errorf("found %d errors", S.ErrorCount) + t.Errorf("got %d errors", S.ErrorCount) + } +} + +// The filename is used for the error message in these test cases. +// The first line directive is valid and used to control the expected error line. +var invalidSegments = []segment{ + {"\n//line :1:1\n//line foo:42 extra text\ndummy", "invalid line number: 42 extra text", 1, 12}, + {"\n//line :2:1\n//line foobar:\ndummy", "invalid line number: ", 2, 15}, + {"\n//line :5:1\n//line :0\ndummy", "invalid line number: 0", 5, 9}, + {"\n//line :10:1\n//line :1:0\ndummy", "invalid column number: 0", 10, 11}, + {"\n//line :1:1\n//line :foo:0\ndummy", "invalid line number: 0", 1, 13}, // foo is considered part of the filename +} + +// Verify that invalid line directives get the correct error message. +func TestInvalidLineDirectives(t *testing.T) { + // make source + var src string + for _, e := range invalidSegments { + src += e.srcline + } + + // verify scan + var S Scanner + var s segment // current segment + file := fset.AddFile(filepath.Join("dir", "TestInvalidLineDirectives"), fset.Base(), len(src)) + S.Init(file, []byte(src), func(pos token.Position, msg string) { + if msg != s.filename { + t.Errorf("got error %q; want %q", msg, s.filename) + } + if pos.Line != s.line || pos.Column != s.column { + t.Errorf("got position %d:%d; want %d:%d", pos.Line, pos.Column, s.line, s.column) + } + }, dontInsertSemis) + for _, s = range invalidSegments { + S.Scan() + } + + if S.ErrorCount != len(invalidSegments) { + t.Errorf("go %d errors; want %d", S.ErrorCount, len(invalidSegments)) } } diff --git a/libgo/go/go/token/example_test.go b/libgo/go/go/token/example_test.go new file mode 100644 index 0000000..8c0d081 --- /dev/null +++ b/libgo/go/go/token/example_test.go @@ -0,0 +1,79 @@ +// Copyright 2018 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 gccgo_examples + +package token_test + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" +) + +func Example_retrievePositionInfo() { + fset := token.NewFileSet() + + const src = `package main + +import "fmt" + +import "go/token" + +//line :1:5 +type p = token.Pos + +const bad = token.NoPos + +//line fake.go:42:11 +func ok(pos p) bool { + return pos != bad +} + +/*line :7:9*/func main() { + fmt.Println(ok(bad) == bad.IsValid()) +} +` + + f, err := parser.ParseFile(fset, "main.go", src, 0) + if err != nil { + fmt.Println(err) + return + } + + // Print the location and kind of each declaration in f. + for _, decl := range f.Decls { + // Get the filename, line, and column back via the file set. + // We get both the relative and absolute position. + // The relative position is relative to the last line directive. + // The absolute position is the exact position in the source. + pos := decl.Pos() + relPosition := fset.Position(pos) + absPosition := fset.PositionFor(pos, false) + + // Either a FuncDecl or GenDecl, since we exit on error. + kind := "func" + if gen, ok := decl.(*ast.GenDecl); ok { + kind = gen.Tok.String() + } + + // If the relative and absolute positions differ, show both. + fmtPosition := relPosition.String() + if relPosition != absPosition { + fmtPosition += "[" + absPosition.String() + "]" + } + + fmt.Printf("%s: %s\n", fmtPosition, kind) + } + + //Output: + // + // main.go:3:1: import + // main.go:5:1: import + // main.go:1:5[main.go:8:1]: type + // main.go:3:1[main.go:10:1]: const + // fake.go:42:11[main.go:13:1]: func + // fake.go:7:9[main.go:17:14]: func +} diff --git a/libgo/go/go/token/position.go b/libgo/go/go/token/position.go index 88d7416..241133f 100644 --- a/libgo/go/go/token/position.go +++ b/libgo/go/go/token/position.go @@ -30,7 +30,9 @@ func (pos *Position) IsValid() bool { return pos.Line > 0 } // String returns a string in one of several forms: // // file:line:column valid position with file name +// file:line valid position with file name but no column (column == 0) // line:column valid position without file name +// line valid position without file name and no column (column == 0) // file invalid position with file name // - invalid position without file name // @@ -40,7 +42,10 @@ func (pos Position) String() string { if s != "" { s += ":" } - s += fmt.Sprintf("%d:%d", pos.Line, pos.Column) + s += fmt.Sprintf("%d", pos.Line) + if pos.Column != 0 { + s += fmt.Sprintf(":%d", pos.Column) + } } if s == "" { s = "-" @@ -204,28 +209,36 @@ func (f *File) SetLinesForContent(content []byte) { f.mutex.Unlock() } -// A lineInfo object describes alternative file and line number -// information (such as provided via a //line comment in a .go -// file) for a given file offset. +// A lineInfo object describes alternative file, line, and column +// number information (such as provided via a //line directive) +// for a given file offset. type lineInfo struct { // fields are exported to make them accessible to gob - Offset int - Filename string - Line int + Offset int + Filename string + Line, Column int } -// AddLineInfo adds alternative file and line number information for -// a given file offset. The offset must be larger than the offset for -// the previously added alternative line info and smaller than the -// file size; otherwise the information is ignored. -// -// AddLineInfo is typically used to register alternative position -// information for //line filename:line comments in source files. +// AddLineInfo is like AddLineColumnInfo with a column = 1 argument. +// It is here for backward-compatibility for code prior to Go 1.11. // func (f *File) AddLineInfo(offset int, filename string, line int) { + f.AddLineColumnInfo(offset, filename, line, 1) +} + +// AddLineColumnInfo adds alternative file, line, and column number +// information for a given file offset. The offset must be larger +// than the offset for the previously added alternative line info +// and smaller than the file size; otherwise the information is +// ignored. +// +// AddLineColumnInfo is typically used to register alternative position +// information for line directives such as //line filename:line:column. +// +func (f *File) AddLineColumnInfo(offset int, filename string, line, column int) { f.mutex.Lock() if i := len(f.infos); i == 0 || f.infos[i-1].Offset < offset && offset < f.size { - f.infos = append(f.infos, lineInfo{offset, filename, line}) + f.infos = append(f.infos, lineInfo{offset, filename, line, column}) } f.mutex.Unlock() } @@ -275,12 +288,25 @@ func (f *File) unpack(offset int, adjusted bool) (filename string, line, column line, column = i+1, offset-f.lines[i]+1 } if adjusted && len(f.infos) > 0 { - // almost no files have extra line infos + // few files have extra line infos if i := searchLineInfos(f.infos, offset); i >= 0 { alt := &f.infos[i] filename = alt.Filename if i := searchInts(f.lines, alt.Offset); i >= 0 { - line += alt.Line - i - 1 + // i+1 is the line at which the alternative position was recorded + d := line - (i + 1) // line distance from alternative position base + line = alt.Line + d + if alt.Column == 0 { + // alternative column is unknown => relative column is unknown + // (the current specification for line directives requires + // this to apply until the next PosBase/line directive, + // not just until the new newline) + column = 0 + } else if d == 0 { + // the alternative position base is on the current line + // => column is relative to alternative column + column = alt.Column + (offset - alt.Offset) + } } } } diff --git a/libgo/go/go/types/api.go b/libgo/go/go/types/api.go index 9908f5c..fcefddf 100644 --- a/libgo/go/go/types/api.go +++ b/libgo/go/go/types/api.go @@ -161,14 +161,14 @@ type Info struct { // in package clauses, or symbolic variables t in t := x.(type) of // type switch headers), the corresponding objects are nil. // - // For an anonymous field, Defs returns the field *Var it defines. + // For an embedded field, Defs returns the field *Var it defines. // // Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos() Defs map[*ast.Ident]Object // Uses maps identifiers to the objects they denote. // - // For an anonymous field, Uses returns the *TypeName it denotes. + // For an embedded field, Uses returns the *TypeName it denotes. // // Invariant: Uses[id].Pos() != id.Pos() Uses map[*ast.Ident]Object @@ -239,7 +239,7 @@ func (info *Info) TypeOf(e ast.Expr) Type { // ObjectOf returns the object denoted by the specified id, // or nil if not found. // -// If id is an anonymous struct field, ObjectOf returns the field (*Var) +// If id is an embedded struct field, ObjectOf returns the field (*Var) // it uses, not the type (*TypeName) it defines. // // Precondition: the Uses and Defs maps are populated. @@ -309,7 +309,7 @@ func (tv TypeAndValue) Assignable() bool { } // HasOk reports whether the corresponding expression may be -// used on the lhs of a comma-ok assignment. +// used on the rhs of a comma-ok assignment. func (tv TypeAndValue) HasOk() bool { return tv.mode == commaok || tv.mode == mapindex } diff --git a/libgo/go/go/types/api_test.go b/libgo/go/go/types/api_test.go index 8a4f1ef..d4ed400 100644 --- a/libgo/go/go/types/api_test.go +++ b/libgo/go/go/types/api_test.go @@ -26,7 +26,6 @@ func pkgFor(path, source string, info *Info) (*Package, error) { if err != nil { return nil, err } - conf := Config{Importer: importer.Default()} return conf.Check(f.Name.Name, fset, []*ast.File{f}, info) } @@ -44,6 +43,20 @@ func mustTypecheck(t *testing.T, path, source string, info *Info) string { return pkg.Name() } +func mayTypecheck(t *testing.T, path, source string, info *Info) string { + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, path, source, 0) + if f == nil { // ignore errors unless f is nil + t.Fatalf("%s: unable to parse: %s", path, err) + } + conf := Config{ + Error: func(err error) {}, + Importer: importer.Default(), + } + pkg, _ := conf.Check(f.Name.Name, fset, []*ast.File{f}, info) + return pkg.Name() +} + func TestValuesInfo(t *testing.T) { var tests = []struct { src string @@ -244,11 +257,16 @@ func TestTypesInfo(t *testing.T) { `<-ch`, `(string, bool)`, }, + + // tests for broken code that doesn't parse or type-check + {`package x0; func _() { var x struct {f string}; x.f := 0 }`, `x.f`, `string`}, + {`package x1; func _() { var z string; type x struct {f string}; y := &x{q: z}}`, `z`, `string`}, + {`package x2; func _() { var a, b string; type x struct {f string}; z := &x{f: a; f: b;}}`, `b`, `string`}, } for _, test := range tests { info := Info{Types: make(map[ast.Expr]TypeAndValue)} - name := mustTypecheck(t, "TypesInfo", test.src, &info) + name := mayTypecheck(t, "TypesInfo", test.src, &info) // look for expression type var typ Type diff --git a/libgo/go/go/types/assignments.go b/libgo/go/go/types/assignments.go index 98c9e12..27002f6 100644 --- a/libgo/go/go/types/assignments.go +++ b/libgo/go/go/types/assignments.go @@ -153,7 +153,7 @@ func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type { var v *Var var v_used bool if ident != nil { - if _, obj := check.scope.LookupParent(ident.Name, token.NoPos); obj != nil { + if obj := check.lookup(ident.Name); obj != nil { // It's ok to mark non-local variables, but ignore variables // from other packages to avoid potential race conditions with // dot-imported variables. @@ -279,6 +279,7 @@ func (check *Checker) assignVars(lhs, rhs []ast.Expr) { } func (check *Checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) { + top := len(check.delayed) scope := check.scope // collect lhs variables @@ -309,6 +310,7 @@ func (check *Checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) { check.recordDef(ident, obj) } } else { + check.useLHS(lhs) check.errorf(lhs.Pos(), "cannot declare %s", lhs) } if obj == nil { @@ -319,6 +321,9 @@ func (check *Checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) { check.initVars(lhsVars, rhs, token.NoPos) + // process function literals in rhs expressions before scope changes + check.processDelayed(top) + // declare new variables if len(newVars) > 0 { // spec: "The scope of a constant or variable identifier declared inside diff --git a/libgo/go/go/types/builtins.go b/libgo/go/go/types/builtins.go index 6654823..05e0324 100644 --- a/libgo/go/go/types/builtins.go +++ b/libgo/go/go/types/builtins.go @@ -158,7 +158,11 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b // function calls; in this case s is not evaluated." if !check.hasCallOrRecv { mode = constant_ - val = constant.MakeInt64(t.len) + if t.len >= 0 { + val = constant.MakeInt64(t.len) + } else { + val = constant.MakeUnknown() + } } case *Slice, *Chan: @@ -170,7 +174,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b } } - if mode == invalid { + if mode == invalid && typ != Typ[Invalid] { check.invalidArg(x.pos(), "%s for %s", x, bin.name) return } @@ -470,6 +474,19 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b case _Panic: // panic(x) + // record panic call if inside a function with result parameters + // (for use in Checker.isTerminating) + if check.sig.results.Len() > 0 { + // function has result parameters + p := check.isPanic + if p == nil { + // allocate lazily + p = make(map[*ast.CallExpr]bool) + check.isPanic = p + } + p[call] = true + } + check.assignment(x, &emptyInterface, "argument to panic") if x.mode == invalid { return @@ -606,7 +623,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b // Note: trace is only available in self-test mode. // (no argument evaluated yet) if nargs == 0 { - check.dump("%s: trace() without arguments", call.Pos()) + check.dump("%v: trace() without arguments", call.Pos()) x.mode = novalue break } @@ -614,7 +631,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b x1 := x for _, arg := range call.Args { check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T)) - check.dump("%s: %s", x1.pos(), x1) + check.dump("%v: %s", x1.pos(), x1) x1 = &t // use incoming x only for first argument } // trace is only available in test mode - no need to record signature diff --git a/libgo/go/go/types/call.go b/libgo/go/go/types/call.go index 8fe65e4..d5c196a 100644 --- a/libgo/go/go/types/call.go +++ b/libgo/go/go/types/call.go @@ -34,6 +34,7 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind { check.conversion(x, T) } default: + check.use(e.Args...) check.errorf(e.Args[n-1].Pos(), "too many arguments in conversion to %s", T) } x.expr = e @@ -274,7 +275,7 @@ func (check *Checker) argument(fun ast.Expr, sig *Signature, i int, x *operand, typ = sig.params.vars[n-1].typ if debug { if _, ok := typ.(*Slice); !ok { - check.dump("%s: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), typ) + check.dump("%v: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), typ) } } default: @@ -314,7 +315,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { // can only appear in qualified identifiers which are mapped to // selector expressions. if ident, ok := e.X.(*ast.Ident); ok { - _, obj := check.scope.LookupParent(ident.Name, check.pos) + obj := check.lookup(ident.Name) if pname, _ := obj.(*PkgName); pname != nil { assert(pname.pkg == check.pkg) check.recordUse(ident, pname) @@ -323,12 +324,12 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { exp := pkg.scope.Lookup(sel) if exp == nil { if !pkg.fake { - check.errorf(e.Pos(), "%s not declared by package %s", sel, pkg.name) + check.errorf(e.Sel.Pos(), "%s not declared by package %s", sel, pkg.name) } goto Error } if !exp.Exported() { - check.errorf(e.Pos(), "%s not exported by package %s", sel, pkg.name) + check.errorf(e.Sel.Pos(), "%s not exported by package %s", sel, pkg.name) // ok to continue } check.recordUse(e.Sel, exp) @@ -373,11 +374,11 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { switch { case index != nil: // TODO(gri) should provide actual type where the conflict happens - check.invalidOp(e.Pos(), "ambiguous selector %s", sel) + check.invalidOp(e.Sel.Pos(), "ambiguous selector %s", sel) case indirect: - check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ) + check.invalidOp(e.Sel.Pos(), "%s is not in method set of %s", sel, x.typ) default: - check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel) + check.invalidOp(e.Sel.Pos(), "%s has no field or method %s", x, sel) } goto Error } @@ -386,7 +387,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { // method expression m, _ := obj.(*Func) if m == nil { - check.invalidOp(e.Pos(), "%s has no method %s", x, sel) + check.invalidOp(e.Sel.Pos(), "%s has no method %s", x, sel) goto Error } @@ -448,7 +449,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { // lookup. mset := NewMethodSet(typ) if m := mset.Lookup(check.pkg, sel); m == nil || m.obj != obj { - check.dump("%s: (%s).%v -> %s", e.Pos(), typ, obj.name, m) + check.dump("%v: (%s).%v -> %s", e.Pos(), typ, obj.name, m) check.dump("%s\n", mset) panic("method sets and lookup don't agree") } diff --git a/libgo/go/go/types/check.go b/libgo/go/go/types/check.go index 26db576..76d9c89 100644 --- a/libgo/go/go/types/check.go +++ b/libgo/go/go/types/check.go @@ -39,22 +39,22 @@ type exprInfo struct { val constant.Value // constant value; or nil (if not a constant) } -// funcInfo stores the information required for type-checking a function. -type funcInfo struct { - name string // for debugging/tracing only - decl *declInfo // for cycle detection - sig *Signature - body *ast.BlockStmt -} - // A context represents the context within which an object is type-checked. type context struct { - decl *declInfo // package-level declaration whose init expression/function body is checked - scope *Scope // top-most scope for lookups - iota constant.Value // value of iota in a constant declaration; nil otherwise - sig *Signature // function signature if inside a function; nil otherwise - hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions - hasCallOrRecv bool // set if an expression contains a function call or channel receive operation + decl *declInfo // package-level declaration whose init expression/function body is checked + scope *Scope // top-most scope for lookups + pos token.Pos // if valid, identifiers are looked up as if at position pos (used by Eval) + iota constant.Value // value of iota in a constant declaration; nil otherwise + sig *Signature // function signature if inside a function; nil otherwise + isPanic map[*ast.CallExpr]bool // set of panic call expressions (used for termination check) + hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions + hasCallOrRecv bool // set if an expression contains a function call or channel receive operation +} + +// lookup looks up name in the current context and returns the matching object, or nil. +func (ctxt *context) lookup(name string) Object { + _, obj := ctxt.scope.LookupParent(name, ctxt.pos) + return obj } // An importKey identifies an imported package by import path and source directory @@ -85,16 +85,16 @@ type Checker struct { files []*ast.File // package files unusedDotImports map[*Scope]map[*Package]token.Pos // positions of unused dot-imported packages for each file scope - firstErr error // first error encountered - methods map[string][]*Func // maps type names to associated methods - untyped map[ast.Expr]exprInfo // map of expressions without final type - funcs []funcInfo // list of functions to type-check - delayed []func() // delayed checks requiring fully setup types + firstErr error // first error encountered + methods map[*TypeName][]*Func // maps package scope type names to associated non-blank, non-interface methods + interfaces map[*TypeName]*ifaceInfo // maps interface type names to corresponding interface infos + untyped map[ast.Expr]exprInfo // map of expressions without final type + delayed []func() // stack of delayed actions + objPath []Object // path of object dependencies during type inference (for cycle reporting) // context within which the current object is type-checked // (valid only for the duration of type-checking a specific object) context - pos token.Pos // if valid, identifiers are looked up as if at position pos (used by Eval) // debugging indent int // indentation for tracing @@ -128,15 +128,6 @@ func (check *Checker) addDeclDep(to Object) { from.addDep(to) } -func (check *Checker) assocMethod(tname string, meth *Func) { - m := check.methods - if m == nil { - m = make(map[string][]*Func) - check.methods = m - } - m[tname] = append(m[tname], meth) -} - func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) { m := check.untyped if m == nil { @@ -146,12 +137,32 @@ func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, ty m[e] = exprInfo{lhs, mode, typ, val} } -func (check *Checker) later(name string, decl *declInfo, sig *Signature, body *ast.BlockStmt) { - check.funcs = append(check.funcs, funcInfo{name, decl, sig, body}) +// later pushes f on to the stack of actions that will be processed later; +// either at the end of the current statement, or in case of a local constant +// or variable declaration, before the constant or variable is in scope +// (so that f still sees the scope before any new declarations). +func (check *Checker) later(f func()) { + check.delayed = append(check.delayed, f) } -func (check *Checker) delay(f func()) { - check.delayed = append(check.delayed, f) +// push pushes obj onto the object path and returns its index in the path. +func (check *Checker) push(obj Object) int { + check.objPath = append(check.objPath, obj) + return len(check.objPath) - 1 +} + +// pop pops and returns the topmost object from the object path. +func (check *Checker) pop() Object { + i := len(check.objPath) - 1 + obj := check.objPath[i] + check.objPath[i] = nil + check.objPath = check.objPath[:i] + return obj +} + +// pathString returns a string of the form a->b-> ... ->g for an object path [a, b, ... g]. +func (check *Checker) pathString() string { + return objPathString(check.objPath) } // NewChecker returns a new Checker instance for a given package. @@ -186,8 +197,8 @@ func (check *Checker) initFiles(files []*ast.File) { check.firstErr = nil check.methods = nil + check.interfaces = nil check.untyped = nil - check.funcs = nil check.delayed = nil // determine package name and collect valid files @@ -236,9 +247,9 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) { check.collectObjects() - check.packageObjects(check.resolveOrder()) + check.packageObjects() - check.functionBodies() + check.processDelayed(0) // incl. all functions check.initOrder() @@ -246,11 +257,6 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) { check.unusedImports() } - // perform delayed checks - for _, f := range check.delayed { - f() - } - check.recordUntyped() check.pkg.complete = true @@ -264,7 +270,7 @@ func (check *Checker) recordUntyped() { for x, info := range check.untyped { if debug && isTyped(info.typ) { - check.dump("%s: %s (type %s) is typed", x.Pos(), x, info.typ) + check.dump("%v: %s (type %s) is typed", x.Pos(), x, info.typ) unreachable() } check.recordTypeAndValue(x, info.mode, info.typ, info.val) diff --git a/libgo/go/go/types/check_test.go b/libgo/go/go/types/check_test.go index e3ca90a..2bdfc15 100644 --- a/libgo/go/go/types/check_test.go +++ b/libgo/go/go/types/check_test.go @@ -61,6 +61,7 @@ var tests = [][]string{ {"testdata/cycles2.src"}, {"testdata/cycles3.src"}, {"testdata/cycles4.src"}, + {"testdata/cycles5.src"}, {"testdata/init0.src"}, {"testdata/init1.src"}, {"testdata/init2.src"}, @@ -89,6 +90,8 @@ var tests = [][]string{ {"testdata/labels.src"}, {"testdata/issues.src"}, {"testdata/blank.src"}, + {"testdata/issue25008b.src", "testdata/issue25008a.src"}, // order (b before a) is crucial! + {"testdata/issue26390.src"}, // stand-alone test to ensure case is triggered } var fset = token.NewFileSet() diff --git a/libgo/go/go/types/decl.go b/libgo/go/go/types/decl.go index 9b250b3..11b6858 100644 --- a/libgo/go/go/types/decl.go +++ b/libgo/go/go/types/decl.go @@ -37,15 +37,45 @@ func (check *Checker) declare(scope *Scope, id *ast.Ident, obj Object, pos token } } +// pathString returns a string of the form a->b-> ... ->g for a path [a, b, ... g]. +// TODO(gri) remove once we don't need the old cycle detection (explicitly passed +// []*TypeName path) anymore +func pathString(path []*TypeName) string { + var s string + for i, p := range path { + if i > 0 { + s += "->" + } + s += p.Name() + } + return s +} + +// objPathString returns a string of the form a->b-> ... ->g for a path [a, b, ... g]. +// TODO(gri) s/objPathString/pathString/ once we got rid of pathString above +func objPathString(path []Object) string { + var s string + for i, p := range path { + if i > 0 { + s += "->" + } + s += p.Name() + } + return s +} + +// useCycleMarking enables the new coloring-based cycle marking scheme +// for package-level objects. Set this flag to false to disable this +// code quickly and revert to the existing mechanism (and comment out +// some of the new tests in cycles5.src that will fail again). +// TODO(gri) remove this for Go 1.12 +const useCycleMarking = true + // objDecl type-checks the declaration of obj in its respective (file) context. // See check.typ for the details on def and path. func (check *Checker) objDecl(obj Object, def *Named, path []*TypeName) { - if obj.Type() != nil { - return // already checked - nothing to do - } - if trace { - check.trace(obj.Pos(), "-- declaring %s", obj.Name()) + check.trace(obj.Pos(), "-- checking %s %s (path = %s, objPath = %s)", obj.color(), obj, pathString(path), check.pathString()) check.indent++ defer func() { check.indent-- @@ -53,9 +83,142 @@ func (check *Checker) objDecl(obj Object, def *Named, path []*TypeName) { }() } + // Checking the declaration of obj means inferring its type + // (and possibly its value, for constants). + // An object's type (and thus the object) may be in one of + // three states which are expressed by colors: + // + // - an object whose type is not yet known is painted white (initial color) + // - an object whose type is in the process of being inferred is painted grey + // - an object whose type is fully inferred is painted black + // + // During type inference, an object's color changes from white to grey + // to black (pre-declared objects are painted black from the start). + // A black object (i.e., its type) can only depend on (refer to) other black + // ones. White and grey objects may depend on white and black objects. + // A dependency on a grey object indicates a cycle which may or may not be + // valid. + // + // When objects turn grey, they are pushed on the object path (a stack); + // they are popped again when they turn black. Thus, if a grey object (a + // cycle) is encountered, it is on the object path, and all the objects + // it depends on are the remaining objects on that path. Color encoding + // is such that the color value of a grey object indicates the index of + // that object in the object path. + + // During type-checking, white objects may be assigned a type without + // traversing through objDecl; e.g., when initializing constants and + // variables. Update the colors of those objects here (rather than + // everywhere where we set the type) to satisfy the color invariants. + if obj.color() == white && obj.Type() != nil { + obj.setColor(black) + return + } + + switch obj.color() { + case white: + assert(obj.Type() == nil) + // All color values other than white and black are considered grey. + // Because black and white are < grey, all values >= grey are grey. + // Use those values to encode the object's index into the object path. + obj.setColor(grey + color(check.push(obj))) + defer func() { + check.pop().setColor(black) + }() + + case black: + assert(obj.Type() != nil) + return + + default: + // Color values other than white or black are considered grey. + fallthrough + + case grey: + // We have a cycle. + // In the existing code, this is marked by a non-nil type + // for the object except for constants and variables whose + // type may be non-nil (known), or nil if it depends on the + // not-yet known initialization value. + // In the former case, set the type to Typ[Invalid] because + // we have an initialization cycle. The cycle error will be + // reported later, when determining initialization order. + // TODO(gri) Report cycle here and simplify initialization + // order code. + switch obj := obj.(type) { + case *Const: + if useCycleMarking && check.typeCycle(obj) { + obj.typ = Typ[Invalid] + break + } + if obj.typ == nil { + obj.typ = Typ[Invalid] + } + + case *Var: + if useCycleMarking && check.typeCycle(obj) { + obj.typ = Typ[Invalid] + break + } + if obj.typ == nil { + obj.typ = Typ[Invalid] + } + + case *TypeName: + // fixFor26390 enables a temporary work-around to handle alias type names + // that have not been given a type yet even though the underlying type + // is already known. See testdata/issue26390.src for a simple example. + // Set this flag to false to disable this code quickly (and comment + // out the new test in decls4.src that will fail again). + // TODO(gri) remove this for Go 1.12 in favor of a more comprehensive fix + const fixFor26390 = true + if fixFor26390 { + // If we have a package-level alias type name that has not been + // given a type yet but the underlying type is a type name that + // has been given a type already, don't report a cycle but use + // the underlying type name's type instead. The cycle shouldn't + // exist in the first place in this case and is due to the way + // methods are type-checked at the moment. See also the comment + // at the end of Checker.typeDecl below. + if d := check.objMap[obj]; d != nil && d.alias && obj.typ == Typ[Invalid] { + // If we can find the underlying type name syntactically + // and it has a type, use that type. + if tname := check.resolveBaseTypeName(ast.NewIdent(obj.name)); tname != nil && tname.typ != nil { + obj.typ = tname.typ + break + } + } + } + + if useCycleMarking && check.typeCycle(obj) { + // break cycle + // (without this, calling underlying() + // below may lead to an endless loop + // if we have a cycle for a defined + // (*Named) type) + obj.typ = Typ[Invalid] + } + + case *Func: + if useCycleMarking && check.typeCycle(obj) { + // Don't set obj.typ to Typ[Invalid] here + // because plenty of code type-asserts that + // functions have a *Signature type. Grey + // functions have their type set to an empty + // signature which makes it impossible to + // initialize a variable with the function. + } + + default: + unreachable() + } + assert(obj.Type() != nil) + return + } + d := check.objMap[obj] if d == nil { - check.dump("%s: %s should have been declared", obj.Pos(), obj.Name()) + check.dump("%v: %s should have been declared", obj.Pos(), obj) unreachable() } @@ -90,17 +253,118 @@ func (check *Checker) objDecl(obj Object, def *Named, path []*TypeName) { } } -func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) { - assert(obj.typ == nil) +// indir is a sentinel type name that is pushed onto the object path +// to indicate an "indirection" in the dependency from one type name +// to the next. For instance, for "type p *p" the object path contains +// p followed by indir, indicating that there's an indirection *p. +// Indirections are used to break type cycles. +var indir = NewTypeName(token.NoPos, nil, "*", nil) + +// cutCycle is a sentinel type name that is pushed onto the object path +// to indicate that a cycle doesn't actually exist. This is currently +// needed to break cycles formed via method declarations because they +// are type-checked together with their receiver base types. Once methods +// are type-checked separately (see also TODO in Checker.typeDecl), we +// can get rid of this. +var cutCycle = NewTypeName(token.NoPos, nil, "!", nil) + +// typeCycle checks if the cycle starting with obj is valid and +// reports an error if it is not. +// TODO(gri) rename s/typeCycle/cycle/ once we don't need the other +// cycle method anymore. +func (check *Checker) typeCycle(obj Object) (isCycle bool) { + d := check.objMap[obj] + if d == nil { + check.dump("%v: %s should have been declared", obj.Pos(), obj) + unreachable() + } - if obj.visited { - obj.typ = Typ[Invalid] - return + // Given the number of constants and variables (nval) in the cycle + // and the cycle length (ncycle = number of named objects in the cycle), + // we distinguish between cycles involving only constants and variables + // (nval = ncycle), cycles involving types (and functions) only + // (nval == 0), and mixed cycles (nval != 0 && nval != ncycle). + // We ignore functions at the moment (taking them into account correctly + // is complicated and it doesn't improve error reporting significantly). + // + // A cycle must have at least one indirection and one type definition + // to be permitted: If there is no indirection, the size of the type + // cannot be computed (it's either infinite or 0); if there is no type + // definition, we have a sequence of alias type names which will expand + // ad infinitum. + var nval, ncycle int + var hasIndir, hasTDef bool + assert(obj.color() >= grey) + start := obj.color() - grey // index of obj in objPath + cycle := check.objPath[start:] + ncycle = len(cycle) // including indirections + for _, obj := range cycle { + switch obj := obj.(type) { + case *Const, *Var: + nval++ + case *TypeName: + switch { + case obj == indir: + ncycle-- // don't count (indirections are not objects) + hasIndir = true + case obj == cutCycle: + // The cycle is not real and only caused by the fact + // that we type-check methods when we type-check their + // receiver base types. + return false + case !check.objMap[obj].alias: + hasTDef = true + } + case *Func: + // ignored for now + default: + unreachable() + } + } + + if trace { + check.trace(obj.Pos(), "## cycle detected: objPath = %s->%s (len = %d)", objPathString(cycle), obj.Name(), ncycle) + check.trace(obj.Pos(), "## cycle contains: %d values, has indirection = %v, has type definition = %v", nval, hasIndir, hasTDef) + defer func() { + if isCycle { + check.trace(obj.Pos(), "=> error: cycle is invalid") + } + }() + } + + // A cycle involving only constants and variables is invalid but we + // ignore them here because they are reported via the initialization + // cycle check. + if nval == ncycle { + return false } - obj.visited = true + + // A cycle involving only types (and possibly functions) must have at + // least one indirection and one type definition to be permitted: If + // there is no indirection, the size of the type cannot be computed + // (it's either infinite or 0); if there is no type definition, we + // have a sequence of alias type names which will expand ad infinitum. + if nval == 0 && hasIndir && hasTDef { + return false // cycle is permitted + } + + // report cycle + check.errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name()) + for _, obj := range cycle { + if obj == indir { + continue // don't print indir sentinels + } + check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented + } + check.errorf(obj.Pos(), "\t%s", obj.Name()) + + return true +} + +func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) { + assert(obj.typ == nil) // use the correct value of iota - assert(check.iota == nil) check.iota = obj.val defer func() { check.iota = nil }() @@ -109,7 +373,7 @@ func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) { // determine type, if any if typ != nil { - t := check.typ(typ) + t := check.typExpr(typ, nil, nil) if !isConstType(t) { // don't report an error if the type is an invalid C (defined) type // (issue #22090) @@ -133,18 +397,9 @@ func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) { func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) { assert(obj.typ == nil) - if obj.visited { - obj.typ = Typ[Invalid] - return - } - obj.visited = true - - // var declarations cannot use iota - assert(check.iota == nil) - // determine type, if any if typ != nil { - obj.typ = check.typ(typ) + obj.typ = check.typExpr(typ, nil, nil) // We cannot spread the type to all lhs variables if there // are more than one since that would mark them as checked // (see Checker.objDecl) and the assignment of init exprs, @@ -222,9 +477,6 @@ func (n *Named) setUnderlying(typ Type) { func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, path []*TypeName, alias bool) { assert(obj.typ == nil) - // type declarations cannot use iota - assert(check.iota == nil) - if alias { obj.typ = Typ[Invalid] @@ -266,18 +518,22 @@ func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, path []* func (check *Checker) addMethodDecls(obj *TypeName) { // get associated methods - methods := check.methods[obj.name] - if len(methods) == 0 { - return // no methods + // (Checker.collectObjects only collects methods with non-blank names; + // Checker.resolveBaseTypeName ensures that obj is not an alias name + // if it has attached methods.) + methods := check.methods[obj] + if methods == nil { + return } - delete(check.methods, obj.name) + delete(check.methods, obj) + assert(!check.objMap[obj].alias) // don't use TypeName.IsAlias (requires fully set up object) // use an objset to check for name conflicts var mset objset // spec: "If the base type is a struct type, the non-blank method // and field names must be distinct." - base, _ := obj.typ.(*Named) // nil if receiver base type is type alias + base, _ := obj.typ.(*Named) // shouldn't fail but be conservative if base != nil { if t, _ := base.underlying.(*Struct); t != nil { for _, fld := range t.fields { @@ -296,30 +552,38 @@ func (check *Checker) addMethodDecls(obj *TypeName) { } } + if useCycleMarking { + // Suppress detection of type cycles occurring through method + // declarations - they wouldn't exist if methods were type- + // checked separately from their receiver base types. See also + // comment at the end of Checker.typeDecl. + // TODO(gri) Remove this once methods are type-checked separately. + check.push(cutCycle) + defer check.pop() + } + // type-check methods for _, m := range methods { // spec: "For a base type, the non-blank names of methods bound // to it must be unique." - if m.name != "_" { - if alt := mset.insert(m); alt != nil { - switch alt.(type) { - case *Var: - check.errorf(m.pos, "field and method with the same name %s", m.name) - case *Func: - check.errorf(m.pos, "method %s already declared for %s", m.name, obj) - default: - unreachable() - } - check.reportAltDecl(alt) - continue + assert(m.name != "_") + if alt := mset.insert(m); alt != nil { + switch alt.(type) { + case *Var: + check.errorf(m.pos, "field and method with the same name %s", m.name) + case *Func: + check.errorf(m.pos, "method %s already declared for %s", m.name, obj) + default: + unreachable() } + check.reportAltDecl(alt) + continue } // type-check check.objDecl(m, nil, nil) - // methods with blank _ names cannot be found - don't keep them - if base != nil && m.name != "_" { + if base != nil { base.methods = append(base.methods, m) } } @@ -343,7 +607,9 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) { // function body must be type-checked after global declarations // (functions implemented elsewhere have no body) if !check.conf.IgnoreFuncBodies && fdecl.Body != nil { - check.later(obj.name, decl, sig, fdecl.Body) + check.later(func() { + check.funcBody(decl, obj.name, sig, fdecl.Body, nil) + }) } } @@ -361,6 +627,8 @@ func (check *Checker) declStmt(decl ast.Decl) { case *ast.ValueSpec: switch d.Tok { case token.CONST: + top := len(check.delayed) + // determine which init exprs to use switch { case s.Type != nil || len(s.Values) > 0: @@ -385,6 +653,9 @@ func (check *Checker) declStmt(decl ast.Decl) { check.arityMatch(s, last) + // process function literals in init expressions before scope changes + check.processDelayed(top) + // spec: "The scope of a constant or variable identifier declared // inside a function begins at the end of the ConstSpec or VarSpec // (ShortVarDecl for short variable declarations) and ends at the @@ -395,6 +666,8 @@ func (check *Checker) declStmt(decl ast.Decl) { } case token.VAR: + top := len(check.delayed) + lhs0 := make([]*Var, len(s.Names)) for i, name := range s.Names { lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil) @@ -435,6 +708,9 @@ func (check *Checker) declStmt(decl ast.Decl) { check.arityMatch(s, nil) + // process function literals in init expressions before scope changes + check.processDelayed(top) + // declare all variables // (only at this point are the variable scopes (parents) set) scopePos := s.End() // see constant declarations diff --git a/libgo/go/go/types/errors.go b/libgo/go/go/types/errors.go index 0c0049b..4c8d853 100644 --- a/libgo/go/go/types/errors.go +++ b/libgo/go/go/types/errors.go @@ -67,10 +67,20 @@ func (check *Checker) dump(format string, args ...interface{}) { } func (check *Checker) err(pos token.Pos, msg string, soft bool) { + // Cheap trick: Don't report errors with messages containing + // "invalid operand" or "invalid type" as those tend to be + // follow-on errors which don't add useful information. Only + // exclude them if these strings are not at the beginning, + // and only if we have at least one error already reported. + if check.firstErr != nil && (strings.Index(msg, "invalid operand") > 0 || strings.Index(msg, "invalid type") > 0) { + return + } + err := Error{check.fset, pos, msg, soft} if check.firstErr == nil { check.firstErr = err } + f := check.conf.Error if f == nil { panic(bailout{}) // report only first error diff --git a/libgo/go/go/types/eval.go b/libgo/go/go/types/eval.go index 831d771..8d4db48 100644 --- a/libgo/go/go/types/eval.go +++ b/libgo/go/go/types/eval.go @@ -16,9 +16,6 @@ import ( // complete position information relative to the provided file // set. // -// If the expression contains function literals, their bodies -// are ignored (i.e., the bodies are not type-checked). -// // If pkg == nil, the Universe scope is used and the provided // position pos is ignored. If pkg != nil, and pos is invalid, // the package scope is used. Otherwise, pos must belong to the @@ -34,7 +31,7 @@ import ( // level untyped constants will return an untyped type rather then the // respective context-specific type. // -func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (TypeAndValue, error) { +func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (_ TypeAndValue, err error) { // determine scope var scope *Scope if pkg == nil { @@ -79,5 +76,7 @@ func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (TypeAn // evaluate node var x operand check.rawExpr(&x, node, nil) - return TypeAndValue{x.mode, x.typ, x.val}, err + check.processDelayed(0) // incl. all functions + + return TypeAndValue{x.mode, x.typ, x.val}, nil } diff --git a/libgo/go/go/types/eval_test.go b/libgo/go/go/types/eval_test.go index 6032112..d3b3fec 100644 --- a/libgo/go/go/types/eval_test.go +++ b/libgo/go/go/types/eval_test.go @@ -149,6 +149,19 @@ func TestEvalPos(t *testing.T) { package p /* T => , p.T */ `, + ` + package p + import "io" + type R = io.Reader + func _() { + /* interface{R}.Read => , func(interface{io.Reader}, p []byte) (n int, err error) */ + _ = func() { + /* interface{io.Writer}.Write => , func(interface{io.Writer}, p []byte) (n int, err error) */ + type io interface {} // must not shadow io in line above + } + type R interface {} // must not shadow R in first line of this function body + } + `, } fset := token.NewFileSet() diff --git a/libgo/go/go/types/expr.go b/libgo/go/go/types/expr.go index 59534c7..c1deaf8 100644 --- a/libgo/go/go/types/expr.go +++ b/libgo/go/go/types/expr.go @@ -382,7 +382,7 @@ func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) { // The respective sub-expressions got their final types // upon assignment or use. if debug { - check.dump("%s: found old type(%s): %s (new: %s)", x.Pos(), x, old.typ, typ) + check.dump("%v: found old type(%s): %s (new: %s)", x.Pos(), x, old.typ, typ) unreachable() } return @@ -1030,16 +1030,15 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { // Anonymous functions are considered part of the // init expression/func declaration which contains // them: use existing package-level declaration info. - // - // TODO(gri) We delay type-checking of regular (top-level) - // function bodies until later. Why don't we do - // it for closures of top-level expressions? - // (We can't easily do it for local closures - // because the surrounding scopes must reflect - // the exact position where the closure appears - // in the source; e.g., variables declared below - // must not be visible). - check.funcBody(check.decl, "", sig, e.Body) + decl := check.decl // capture for use in closure below + iota := check.iota // capture for use in closure below (#22345) + // Don't type-check right away because the function may + // be part of a type definition to which the function + // body refers. Instead, type-check as soon as possible, + // but before the enclosing scope contents changes (#22992). + check.later(func() { + check.funcBody(decl, "<function literal>", sig, e.Body, iota) + }) x.mode = value x.typ = sig } else { @@ -1065,7 +1064,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { break } } - typ = check.typ(e.Type) + typ = check.typExpr(e.Type, nil, nil) base = typ case hint != nil: @@ -1095,6 +1094,9 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { continue } key, _ := kv.Key.(*ast.Ident) + // do all possible checks early (before exiting due to errors) + // so we don't drop information on the floor + check.expr(x, kv.Value) if key == nil { check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key) continue @@ -1106,15 +1108,14 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { } fld := fields[i] check.recordUse(key, fld) + etyp := fld.typ + check.assignment(x, etyp, "struct literal") // 0 <= i < len(fields) if visited[i] { check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name) continue } visited[i] = true - check.expr(x, kv.Value) - etyp := fld.typ - check.assignment(x, etyp, "struct literal") } } else { // no element must have a key @@ -1432,7 +1433,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { check.invalidAST(e.Pos(), "use of .(type) outside type switch") goto Error } - T := check.typ(e.Type) + T := check.typExpr(e.Type, nil, nil) if T == Typ[Invalid] { goto Error } diff --git a/libgo/go/go/types/gotype.go b/libgo/go/go/types/gotype.go index 196fc9b..cde373f 100644 --- a/libgo/go/go/types/gotype.go +++ b/libgo/go/go/types/gotype.go @@ -53,6 +53,8 @@ Flags controlling additional output: print parse trace (forces -seq) -comments parse comments (ignored unless -ast or -trace is provided) + -panic + panic on first error Examples: @@ -105,6 +107,7 @@ var ( printAST = flag.Bool("ast", false, "print AST (forces -seq)") printTrace = flag.Bool("trace", false, "print parse trace (forces -seq)") parseComments = flag.Bool("comments", false, "parse comments (ignored unless -ast or -trace is provided)") + panicOnError = flag.Bool("panic", false, "panic on first error") ) var ( @@ -164,6 +167,9 @@ func usage() { } func report(err error) { + if *panicOnError { + panic(err) + } scanner.PrintError(os.Stderr, err) if list, ok := err.(scanner.ErrorList); ok { errorCount += len(list) @@ -209,14 +215,30 @@ func parseFiles(dir string, filenames []string) ([]*ast.File, error) { } wg.Wait() - // if there are errors, return the first one for deterministic results + // If there are errors, return the first one for deterministic results. + var first error for _, err := range errors { if err != nil { - return nil, err + first = err + // If we have an error, some files may be nil. + // Remove them. (The go/parser always returns + // a possibly partial AST even in the presence + // of errors, except if the file doesn't exist + // in the first place, in which case it cannot + // matter.) + i := 0 + for _, f := range files { + if f != nil { + files[i] = f + i++ + } + } + files = files[:i] + break } } - return files, nil + return files, first } func parseDir(dir string) ([]*ast.File, error) { @@ -318,7 +340,7 @@ func main() { files, err := getPkgFiles(flag.Args()) if err != nil { report(err) - os.Exit(2) + // ok to continue (files may be empty, but not nil) } checkPkgFiles(files) diff --git a/libgo/go/go/types/interfaces.go b/libgo/go/go/types/interfaces.go new file mode 100644 index 0000000..e4b42dc --- /dev/null +++ b/libgo/go/go/types/interfaces.go @@ -0,0 +1,443 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this src code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "bytes" + "fmt" + "go/ast" + "go/token" +) + +// This file implements the collection of an interface's methods +// without relying on partially computed types of methods or interfaces +// for interface types declared at the package level. +// +// Because interfaces must not embed themselves, directly or indirectly, +// the method set of a valid interface can always be computed independent +// of any cycles that might exist via method signatures (see also issue #18395). +// +// Except for blank method name and interface cycle errors, no errors +// are reported. Affected methods or embedded interfaces are silently +// dropped. Subsequent type-checking of the interface will check +// signatures and embedded interfaces and report errors at that time. +// +// Only infoFromTypeLit should be called directly from code outside this file +// to compute an ifaceInfo. + +// ifaceInfo describes the method set for an interface. +// The zero value for an ifaceInfo is a ready-to-use ifaceInfo representing +// the empty interface. +type ifaceInfo struct { + explicits int // number of explicitly declared methods + methods []*methodInfo // all methods, starting with explicitly declared ones in source order +} + +// emptyIfaceInfo represents the ifaceInfo for the empty interface. +var emptyIfaceInfo ifaceInfo + +func (info *ifaceInfo) String() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "interface{") + for i, m := range info.methods { + if i > 0 { + fmt.Fprint(&buf, " ") + } + fmt.Fprint(&buf, m) + } + fmt.Fprintf(&buf, "}") + return buf.String() +} + +// methodInfo represents an interface method. +// At least one of src or fun must be non-nil. +// (Methods declared in the current package have a non-nil scope +// and src, and eventually a non-nil fun field; imported and pre- +// declared methods have a nil scope and src, and only a non-nil +// fun field.) +type methodInfo struct { + scope *Scope // scope of interface method; or nil + src *ast.Field // syntax tree representation of interface method; or nil + fun *Func // corresponding fully type-checked method type; or nil +} + +func (info *methodInfo) String() string { + if info.fun != nil { + return info.fun.name + } + return info.src.Names[0].Name +} + +func (info *methodInfo) Pos() token.Pos { + if info.fun != nil { + return info.fun.Pos() + } + return info.src.Pos() +} + +func (info *methodInfo) id(pkg *Package) string { + if info.fun != nil { + return info.fun.Id() + } + return Id(pkg, info.src.Names[0].Name) +} + +// A methodInfoSet maps method ids to methodInfos. +// It is used to determine duplicate declarations. +// (A methodInfo set is the equivalent of an objset +// but for methodInfos rather than Objects.) +type methodInfoSet map[string]*methodInfo + +// insert attempts to insert an method m into the method set s. +// If s already contains an alternative method alt with +// the same name, insert leaves s unchanged and returns alt. +// Otherwise it inserts m and returns nil. +func (s *methodInfoSet) insert(pkg *Package, m *methodInfo) *methodInfo { + id := m.id(pkg) + if alt := (*s)[id]; alt != nil { + return alt + } + if *s == nil { + *s = make(methodInfoSet) + } + (*s)[id] = m + return nil +} + +// like Checker.declareInSet but for method infos. +func (check *Checker) declareInMethodSet(mset *methodInfoSet, pos token.Pos, m *methodInfo) bool { + if alt := mset.insert(check.pkg, m); alt != nil { + check.errorf(pos, "%s redeclared", m) + check.reportAltMethod(alt) + return false + } + return true +} + +// like Checker.reportAltDecl but for method infos. +func (check *Checker) reportAltMethod(m *methodInfo) { + if pos := m.Pos(); pos.IsValid() { + // We use "other" rather than "previous" here because + // the first declaration seen may not be textually + // earlier in the source. + check.errorf(pos, "\tother declaration of %s", m) // secondary error, \t indented + } +} + +// infoFromTypeLit computes the method set for the given interface iface +// declared in scope. +// If a corresponding type name exists (tname != nil), it is used for +// cycle detection and to cache the method set. +// The result is the method set, or nil if there is a cycle via embedded +// interfaces. A non-nil result doesn't mean that there were no errors, +// but they were either reported (e.g., blank methods), or will be found +// (again) when computing the interface's type. +// If tname is not nil it must be the last element in path. +func (check *Checker) infoFromTypeLit(scope *Scope, iface *ast.InterfaceType, tname *TypeName, path []*TypeName) (info *ifaceInfo) { + assert(iface != nil) + + // lazy-allocate interfaces map + if check.interfaces == nil { + check.interfaces = make(map[*TypeName]*ifaceInfo) + } + + if trace { + check.trace(iface.Pos(), "-- collect methods for %v (path = %s, objPath = %s)", iface, pathString(path), check.pathString()) + check.indent++ + defer func() { + check.indent-- + check.trace(iface.Pos(), "=> %s", info) + }() + } + + // If the interface is named, check if we computed info already. + // + // This is not simply an optimization; we may run into stack + // overflow with recursive interface declarations. Example: + // + // type T interface { + // m() interface { T } + // } + // + // (Since recursive definitions can only be expressed via names, + // it is sufficient to track named interfaces here.) + // + // While at it, use the same mechanism to detect cycles. (We still + // have the path-based cycle check because we want to report the + // entire cycle if present.) + if tname != nil { + assert(path[len(path)-1] == tname) // tname must be last path element + var found bool + if info, found = check.interfaces[tname]; found { + if info == nil { + // We have a cycle and use check.cycle to report it. + // We are guaranteed that check.cycle also finds the + // cycle because when infoFromTypeLit is called, any + // tname that's already in check.interfaces was also + // added to the path. (But the converse is not true: + // A non-nil tname is always the last element in path.) + ok := check.cycle(tname, path, true) + assert(ok) + } + return + } + check.interfaces[tname] = nil // computation started but not complete + } + + if iface.Methods.List == nil { + // fast track for empty interface + info = &emptyIfaceInfo + } else { + // (syntactically) non-empty interface + info = new(ifaceInfo) + + // collect explicitly declared methods and embedded interfaces + var mset methodInfoSet + var embeddeds []*ifaceInfo + var positions []token.Pos // entries correspond to positions of embeddeds; used for error reporting + for _, f := range iface.Methods.List { + if len(f.Names) > 0 { + // We have a method with name f.Names[0]. + // (The parser ensures that there's only one method + // and we don't care if a constructed AST has more.) + + // spec: "As with all method sets, in an interface type, + // each method must have a unique non-blank name." + if name := f.Names[0]; name.Name == "_" { + check.errorf(name.Pos(), "invalid method name _") + continue // ignore + } + + m := &methodInfo{scope: scope, src: f} + if check.declareInMethodSet(&mset, f.Pos(), m) { + info.methods = append(info.methods, m) + } + } else { + // We have an embedded interface and f.Type is its + // (possibly qualified) embedded type name. Collect + // it if it's a valid interface. + var e *ifaceInfo + switch ename := f.Type.(type) { + case *ast.Ident: + e = check.infoFromTypeName(scope, ename, path) + case *ast.SelectorExpr: + e = check.infoFromQualifiedTypeName(scope, ename) + default: + // The parser makes sure we only see one of the above. + // Constructed ASTs may contain other (invalid) nodes; + // we simply ignore them. The full type-checking pass + // will report those as errors later. + } + if e != nil { + embeddeds = append(embeddeds, e) + positions = append(positions, f.Type.Pos()) + } + } + } + info.explicits = len(info.methods) + + // collect methods of embedded interfaces + for i, e := range embeddeds { + pos := positions[i] // position of type name of embedded interface + for _, m := range e.methods { + if check.declareInMethodSet(&mset, pos, m) { + info.methods = append(info.methods, m) + } + } + } + } + + // mark check.interfaces as complete + assert(info != nil) + if tname != nil { + check.interfaces[tname] = info + } + + return +} + +// infoFromTypeName computes the method set for the given type name +// which must denote a type whose underlying type is an interface. +// The same result qualifications apply as for infoFromTypeLit. +// infoFromTypeName should only be called from infoFromTypeLit. +func (check *Checker) infoFromTypeName(scope *Scope, name *ast.Ident, path []*TypeName) *ifaceInfo { + // A single call of infoFromTypeName handles a sequence of (possibly + // recursive) type declarations connected via unqualified type names. + // Each type declaration leading to another typename causes a "tail call" + // (goto) of this function. The general scenario looks like this: + // + // ... + // type Pn T // previous declarations leading to T, path = [..., Pn] + // type T interface { T0; ... } // T0 leads to call of infoFromTypeName + // + // // infoFromTypeName(name = T0, path = [..., Pn, T]) + // type T0 T1 // path = [..., Pn, T, T0] + // type T1 T2 <-+ // path = [..., Pn, T, T0, T1] + // type T2 ... | // path = [..., Pn, T, T0, T1, T2] + // type Tn T1 --+ // path = [..., Pn, T, T0, T1, T2, Tn] and T1 is in path => cycle + // + // infoFromTypeName returns nil when such a cycle is detected. But in + // contrast to cycles involving interfaces, we must not report the + // error for "type name only" cycles because they will be found again + // during type-checking of embedded interfaces. Reporting those cycles + // here would lead to double reporting. Cycles involving embedding are + // not reported again later because type-checking of interfaces relies + // on the ifaceInfos computed here which are cycle-free by design. + // + // Remember the path length to detect "type name only" cycles. + start := len(path) + +typenameLoop: + // name must be a type name denoting a type whose underlying type is an interface + _, obj := scope.LookupParent(name.Name, check.pos) + if obj == nil { + return nil + } + tname, _ := obj.(*TypeName) + if tname == nil { + return nil + } + + // We have a type name. It may be predeclared (error type), + // imported (dot import), or declared by a type declaration. + // It may not be an interface (e.g., predeclared type int). + // Resolve it by analyzing each possible case. + + // Abort but don't report an error if we have a "type name only" + // cycle (see big function comment). + if check.cycle(tname, path[start:], false) { + return nil + } + + // Abort and report an error if we have a general cycle. + if check.cycle(tname, path, true) { + return nil + } + + path = append(path, tname) + + // If tname is a package-level type declaration, it must be + // in the objMap. Follow the RHS of that declaration if so. + // The RHS may be a literal type (likely case), or another + // (possibly parenthesized and/or qualified) type name. + // (The declaration may be an alias declaration, but it + // doesn't matter for the purpose of determining the under- + // lying interface.) + if decl := check.objMap[tname]; decl != nil { + switch typ := unparen(decl.typ).(type) { + case *ast.Ident: + // type tname T + name = typ + goto typenameLoop + case *ast.SelectorExpr: + // type tname p.T + return check.infoFromQualifiedTypeName(decl.file, typ) + case *ast.InterfaceType: + // type tname interface{...} + return check.infoFromTypeLit(decl.file, typ, tname, path) + } + // type tname X // and X is not an interface type + return nil + } + + // If tname is not a package-level declaration, in a well-typed + // program it should be a predeclared (error type), imported (dot + // import), or function local declaration. Either way, it should + // have been fully declared before use, except if there is a direct + // cycle, and direct cycles will be caught above. Also, the denoted + // type should be an interface (e.g., int is not an interface). + if typ := tname.typ; typ != nil { + // typ should be an interface + if ityp, _ := typ.Underlying().(*Interface); ityp != nil { + return infoFromType(ityp) + } + } + + // In all other cases we have some error. + return nil +} + +// infoFromQualifiedTypeName computes the method set for the given qualified type name, or nil. +func (check *Checker) infoFromQualifiedTypeName(scope *Scope, qname *ast.SelectorExpr) *ifaceInfo { + // see also Checker.selector + name, _ := qname.X.(*ast.Ident) + if name == nil { + return nil + } + _, obj1 := scope.LookupParent(name.Name, check.pos) + if obj1 == nil { + return nil + } + pname, _ := obj1.(*PkgName) + if pname == nil { + return nil + } + assert(pname.pkg == check.pkg) + obj2 := pname.imported.scope.Lookup(qname.Sel.Name) + if obj2 == nil || !obj2.Exported() { + return nil + } + tname, _ := obj2.(*TypeName) + if tname == nil { + return nil + } + ityp, _ := tname.typ.Underlying().(*Interface) + if ityp == nil { + return nil + } + return infoFromType(ityp) +} + +// infoFromType computes the method set for the given interface type. +// The result is never nil. +func infoFromType(typ *Interface) *ifaceInfo { + assert(typ.allMethods != nil) // typ must be completely set up + + // fast track for empty interface + n := len(typ.allMethods) + if n == 0 { + return &emptyIfaceInfo + } + + info := new(ifaceInfo) + info.explicits = len(typ.methods) + info.methods = make([]*methodInfo, n) + + // If there are no embedded interfaces, simply collect the + // explicitly declared methods (optimization of common case). + if len(typ.methods) == n { + for i, m := range typ.methods { + info.methods[i] = &methodInfo{fun: m} + } + return info + } + + // Interface types have a separate list for explicitly declared methods + // which shares its methods with the list of all (explicitly declared or + // embedded) methods. Collect all methods in a set so we can separate + // the embedded methods from the explicitly declared ones. + all := make(map[*Func]bool, n) + for _, m := range typ.allMethods { + all[m] = true + } + assert(len(all) == n) // methods must be unique + + // collect explicitly declared methods + info.methods = make([]*methodInfo, n) + for i, m := range typ.methods { + info.methods[i] = &methodInfo{fun: m} + delete(all, m) + } + + // collect remaining (embedded) methods + i := len(typ.methods) + for m := range all { + info.methods[i] = &methodInfo{fun: m} + i++ + } + assert(i == n) + + return info +} diff --git a/libgo/go/go/types/issues_test.go b/libgo/go/go/types/issues_test.go index 81c00f9..6e0d406 100644 --- a/libgo/go/go/types/issues_test.go +++ b/libgo/go/go/types/issues_test.go @@ -316,3 +316,44 @@ func TestIssue22525(t *testing.T) { t.Errorf("got: %swant: %s", got, want) } } + +func TestIssue25627(t *testing.T) { + const prefix = `package p; import "unsafe"; type P *struct{}; type I interface{}; type T ` + // The src strings (without prefix) are constructed such that the number of semicolons + // plus one corresponds to the number of fields expected in the respective struct. + for _, src := range []string{ + `struct { x Missing }`, + `struct { Missing }`, + `struct { *Missing }`, + `struct { unsafe.Pointer }`, + `struct { P }`, + `struct { *I }`, + `struct { a int; b Missing; *Missing }`, + } { + f, err := parser.ParseFile(fset, "", prefix+src, 0) + if err != nil { + t.Fatal(err) + } + + cfg := Config{Importer: importer.Default(), Error: func(err error) {}} + info := &Info{Types: make(map[ast.Expr]TypeAndValue)} + _, err = cfg.Check(f.Name.Name, fset, []*ast.File{f}, info) + if err != nil { + if _, ok := err.(Error); !ok { + t.Fatal(err) + } + } + + ast.Inspect(f, func(n ast.Node) bool { + if spec, _ := n.(*ast.TypeSpec); spec != nil { + if tv, ok := info.Types[spec.Type]; ok && spec.Name.Name == "T" { + want := strings.Count(src, ";") + 1 + if got := tv.Type.(*Struct).NumFields(); got != want { + t.Errorf("%s: got %d fields; want %d", src, got, want) + } + } + } + return true + }) + } +} diff --git a/libgo/go/go/types/lookup.go b/libgo/go/go/types/lookup.go index ee8202d..f31ef9c 100644 --- a/libgo/go/go/types/lookup.go +++ b/libgo/go/go/types/lookup.go @@ -19,7 +19,7 @@ package types // 2) the list of all methods (method set) of an interface type; or // 3) the list of fields of a struct type. // -// The earlier index entries are the indices of the anonymous struct fields +// The earlier index entries are the indices of the embedded struct fields // traversed to get to the found entry, starting at depth 0. // // If no entry is found, a nil object is returned. In this case, the returned @@ -149,7 +149,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o // T is a type name. If e.typ appeared multiple times at // this depth, f.typ appears multiple times at the next // depth. - if obj == nil && f.anonymous { + if obj == nil && f.embedded { typ, isPtr := deref(f.typ) // TODO(gri) optimization: ignore types that can't // have fields or methods (only Named, Struct, and diff --git a/libgo/go/go/types/methodset.go b/libgo/go/go/types/methodset.go index 2a8b1c2..2b810da 100644 --- a/libgo/go/go/types/methodset.go +++ b/libgo/go/go/types/methodset.go @@ -7,9 +7,9 @@ package types import ( - "bytes" "fmt" "sort" + "strings" ) // A MethodSet is an ordered set of concrete or abstract (interface) methods; @@ -24,7 +24,7 @@ func (s *MethodSet) String() string { return "MethodSet {}" } - var buf bytes.Buffer + var buf strings.Builder fmt.Fprintln(&buf, "MethodSet {") for _, f := range s.list { fmt.Fprintf(&buf, "\t%s\n", f) @@ -132,7 +132,7 @@ func NewMethodSet(T Type) *MethodSet { // T is a type name. If typ appeared multiple times at // this depth, f.Type appears multiple times at the next // depth. - if f.anonymous { + if f.embedded { typ, isPtr := deref(f.typ) // TODO(gri) optimization: ignore types that can't // have fields or methods (only Named, Struct, and diff --git a/libgo/go/go/types/object.go b/libgo/go/go/types/object.go index 633d327..07adfbc 100644 --- a/libgo/go/go/types/object.go +++ b/libgo/go/go/types/object.go @@ -34,9 +34,15 @@ type Object interface { // 0 for all other objects (including objects in file scopes). order() uint32 + // color returns the object's color. + color() color + // setOrder sets the order number of the object. It must be > 0. setOrder(uint32) + // setColor sets the object's color. It must not be white. + setColor(color color) + // setParent sets the parent scope of the object. setParent(*Scope) @@ -78,22 +84,74 @@ type object struct { name string typ Type order_ uint32 + color_ color scopePos_ token.Pos } -func (obj *object) Parent() *Scope { return obj.parent } -func (obj *object) Pos() token.Pos { return obj.pos } -func (obj *object) Pkg() *Package { return obj.pkg } -func (obj *object) Name() string { return obj.name } -func (obj *object) Type() Type { return obj.typ } -func (obj *object) Exported() bool { return ast.IsExported(obj.name) } -func (obj *object) Id() string { return Id(obj.pkg, obj.name) } +// color encodes the color of an object (see Checker.objDecl for details). +type color uint32 + +// An object may be painted in one of three colors. +// Color values other than white or black are considered grey. +const ( + white color = iota + black + grey // must be > white and black +) + +func (c color) String() string { + switch c { + case white: + return "white" + case black: + return "black" + default: + return "grey" + } +} + +// colorFor returns the (initial) color for an object depending on +// whether its type t is known or not. +func colorFor(t Type) color { + if t != nil { + return black + } + return white +} + +// Parent returns the scope in which the object is declared. +// The result is nil for methods and struct fields. +func (obj *object) Parent() *Scope { return obj.parent } + +// Pos returns the declaration position of the object's identifier. +func (obj *object) Pos() token.Pos { return obj.pos } + +// Pkg returns the package to which the object belongs. +// The result is nil for labels and objects in the Universe scope. +func (obj *object) Pkg() *Package { return obj.pkg } + +// Name returns the object's (package-local, unqualified) name. +func (obj *object) Name() string { return obj.name } + +// Type returns the object's type. +func (obj *object) Type() Type { return obj.typ } + +// Exported reports whether the object is exported (starts with a capital letter). +// It doesn't take into account whether the object is in a local (function) scope +// or not. +func (obj *object) Exported() bool { return ast.IsExported(obj.name) } + +// Id is a wrapper for Id(obj.Pkg(), obj.Name()). +func (obj *object) Id() string { return Id(obj.pkg, obj.name) } + func (obj *object) String() string { panic("abstract") } func (obj *object) order() uint32 { return obj.order_ } +func (obj *object) color() color { return obj.color_ } func (obj *object) scopePos() token.Pos { return obj.scopePos_ } func (obj *object) setParent(parent *Scope) { obj.parent = parent } func (obj *object) setOrder(order uint32) { assert(order > 0); obj.order_ = order } +func (obj *object) setColor(color color) { assert(color != white); obj.color_ = color } func (obj *object) setScopePos(pos token.Pos) { obj.scopePos_ = pos } func (obj *object) sameId(pkg *Package, name string) bool { @@ -129,7 +187,7 @@ type PkgName struct { // NewPkgName returns a new PkgName object representing an imported package. // The remaining arguments set the attributes found with all Objects. func NewPkgName(pos token.Pos, pkg *Package, name string, imported *Package) *PkgName { - return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, token.NoPos}, imported, false} + return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, black, token.NoPos}, imported, false} } // Imported returns the package that was imported. @@ -139,20 +197,21 @@ func (obj *PkgName) Imported() *Package { return obj.imported } // A Const represents a declared constant. type Const struct { object - val constant.Value - visited bool // for initialization cycle detection + val constant.Value } // NewConst returns a new constant with value val. // The remaining arguments set the attributes found with all Objects. func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val constant.Value) *Const { - return &Const{object{nil, pos, pkg, name, typ, 0, token.NoPos}, val, false} + return &Const{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, val} } +// Val returns the constant's value. func (obj *Const) Val() constant.Value { return obj.val } -func (*Const) isDependency() {} // a constant may be a dependency of an initialization expression -// A TypeName represents a name for a (named or alias) type. +func (*Const) isDependency() {} // a constant may be a dependency of an initialization expression + +// A TypeName represents a name for a (defined or alias) type. type TypeName struct { object } @@ -165,7 +224,7 @@ type TypeName struct { // argument for NewNamed, which will set the TypeName's type as a side- // effect. func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName { - return &TypeName{object{nil, pos, pkg, name, typ, 0, token.NoPos}} + return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}} } // IsAlias reports whether obj is an alias name for a type. @@ -195,32 +254,35 @@ func (obj *TypeName) IsAlias() bool { // A Variable represents a declared variable (including function parameters and results, and struct fields). type Var struct { object - anonymous bool // if set, the variable is an anonymous struct field, and name is the type name - visited bool // for initialization cycle detection - isField bool // var is struct field - used bool // set if the variable was used + embedded bool // if set, the variable is an embedded struct field, and name is the type name + isField bool // var is struct field + used bool // set if the variable was used } // NewVar returns a new variable. // The arguments set the attributes found with all Objects. func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var { - return &Var{object: object{nil, pos, pkg, name, typ, 0, token.NoPos}} + return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}} } // NewParam returns a new variable representing a function parameter. func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var { - return &Var{object: object{nil, pos, pkg, name, typ, 0, token.NoPos}, used: true} // parameters are always 'used' + return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, used: true} // parameters are always 'used' } // NewField returns a new variable representing a struct field. -// For anonymous (embedded) fields, the name is the unqualified -// type name under which the field is accessible. -func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool) *Var { - return &Var{object: object{nil, pos, pkg, name, typ, 0, token.NoPos}, anonymous: anonymous, isField: true} +// For embedded fields, the name is the unqualified type name +/// under which the field is accessible. +func NewField(pos token.Pos, pkg *Package, name string, typ Type, embedded bool) *Var { + return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, embedded: embedded, isField: true} } -// Anonymous reports whether the variable is an anonymous field. -func (obj *Var) Anonymous() bool { return obj.anonymous } +// Anonymous reports whether the variable is an embedded field. +// Same as Embedded; only present for backward-compatibility. +func (obj *Var) Anonymous() bool { return obj.embedded } + +// Embedded reports whether the variable is an embedded field. +func (obj *Var) Embedded() bool { return obj.embedded } // IsField reports whether the variable is a struct field. func (obj *Var) IsField() bool { return obj.isField } @@ -242,7 +304,7 @@ func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func { if sig != nil { typ = sig } - return &Func{object{nil, pos, pkg, name, typ, 0, token.NoPos}} + return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}} } // FullName returns the package- or receiver-type-qualified name of @@ -267,7 +329,7 @@ type Label struct { // NewLabel returns a new label. func NewLabel(pos token.Pos, pkg *Package, name string) *Label { - return &Label{object{pos: pos, pkg: pkg, name: name, typ: Typ[Invalid]}, false} + return &Label{object{pos: pos, pkg: pkg, name: name, typ: Typ[Invalid], color_: black}, false} } // A Builtin represents a built-in function. @@ -278,7 +340,7 @@ type Builtin struct { } func newBuiltin(id builtinId) *Builtin { - return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid]}, id} + return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid], color_: black}, id} } // Nil represents the predeclared value nil. diff --git a/libgo/go/go/types/object_test.go b/libgo/go/go/types/object_test.go index 9f073944..88cd875 100644 --- a/libgo/go/go/types/object_test.go +++ b/libgo/go/go/types/object_test.go @@ -29,9 +29,9 @@ func TestIsAlias(t *testing.T) { name *TypeName alias bool }{ - {NewTypeName(0, nil, "t0", nil), false}, // no type yet - {NewTypeName(0, pkg, "t0", nil), false}, // no type yet - {t1, false}, // type name refers to named type and vice versa + {NewTypeName(0, nil, "t0", nil), false}, // no type yet + {NewTypeName(0, pkg, "t0", nil), false}, // no type yet + {t1, false}, // type name refers to named type and vice versa {NewTypeName(0, nil, "t2", &emptyInterface), true}, // type name refers to unnamed type {NewTypeName(0, pkg, "t3", n1), true}, // type name refers to named type with different type name {NewTypeName(0, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name diff --git a/libgo/go/go/types/ordering.go b/libgo/go/go/types/ordering.go deleted file mode 100644 index 3579abf..0000000 --- a/libgo/go/go/types/ordering.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file implements resolveOrder. - -package types - -import ( - "go/ast" - "sort" -) - -// resolveOrder computes the order in which package-level objects -// must be type-checked. -// -// Interface types appear first in the list, sorted topologically -// by dependencies on embedded interfaces that are also declared -// in this package, followed by all other objects sorted in source -// order. -// -// TODO(gri) Consider sorting all types by dependencies here, and -// in the process check _and_ report type cycles. This may simplify -// the full type-checking phase. -// -func (check *Checker) resolveOrder() []Object { - var ifaces, others []Object - - // collect interface types with their dependencies, and all other objects - for obj := range check.objMap { - if ityp := check.interfaceFor(obj); ityp != nil { - ifaces = append(ifaces, obj) - // determine dependencies on embedded interfaces - for _, f := range ityp.Methods.List { - if len(f.Names) == 0 { - // Embedded interface: The type must be a (possibly - // qualified) identifier denoting another interface. - // Imported interfaces are already fully resolved, - // so we can ignore qualified identifiers. - if ident, _ := f.Type.(*ast.Ident); ident != nil { - embedded := check.pkg.scope.Lookup(ident.Name) - if check.interfaceFor(embedded) != nil { - check.objMap[obj].addDep(embedded) - } - } - } - } - } else { - others = append(others, obj) - } - } - - // final object order - var order []Object - - // sort interface types topologically by dependencies, - // and in source order if there are no dependencies - sort.Sort(inSourceOrder(ifaces)) - visited := make(objSet) - for _, obj := range ifaces { - check.appendInPostOrder(&order, obj, visited) - } - - // sort everything else in source order - sort.Sort(inSourceOrder(others)) - - return append(order, others...) -} - -// interfaceFor returns the AST interface denoted by obj, or nil. -func (check *Checker) interfaceFor(obj Object) *ast.InterfaceType { - tname, _ := obj.(*TypeName) - if tname == nil { - return nil // not a type - } - d := check.objMap[obj] - if d == nil { - check.dump("%s: %s should have been declared", obj.Pos(), obj.Name()) - unreachable() - } - if d.typ == nil { - return nil // invalid AST - ignore (will be handled later) - } - ityp, _ := d.typ.(*ast.InterfaceType) - return ityp -} - -func (check *Checker) appendInPostOrder(order *[]Object, obj Object, visited objSet) { - if visited[obj] { - // We've already seen this object; either because it's - // already added to order, or because we have a cycle. - // In both cases we stop. Cycle errors are reported - // when type-checking types. - return - } - visited[obj] = true - - d := check.objMap[obj] - for _, obj := range orderedSetObjects(d.deps) { - check.appendInPostOrder(order, obj, visited) - } - - *order = append(*order, obj) -} - -func orderedSetObjects(set objSet) []Object { - list := make([]Object, len(set)) - i := 0 - for obj := range set { - // we don't care about the map element value - list[i] = obj - i++ - } - sort.Sort(inSourceOrder(list)) - return list -} - -// inSourceOrder implements the sort.Sort interface. -type inSourceOrder []Object - -func (a inSourceOrder) Len() int { return len(a) } -func (a inSourceOrder) Less(i, j int) bool { return a[i].order() < a[j].order() } -func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } diff --git a/libgo/go/go/types/predicates.go b/libgo/go/go/types/predicates.go index 3aa4878..46ad4e2 100644 --- a/libgo/go/go/types/predicates.go +++ b/libgo/go/go/types/predicates.go @@ -150,7 +150,9 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { // Two array types are identical if they have identical element types // and the same array length. if y, ok := y.(*Array); ok { - return x.len == y.len && identical(x.elem, y.elem, cmpTags, p) + // If one or both array lengths are unknown (< 0) due to some error, + // assume they are the same to avoid spurious follow-on errors. + return (x.len < 0 || y.len < 0 || x.len == y.len) && identical(x.elem, y.elem, cmpTags, p) } case *Slice: @@ -162,13 +164,13 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { case *Struct: // Two struct types are identical if they have the same sequence of fields, // and if corresponding fields have the same names, and identical types, - // and identical tags. Two anonymous fields are considered to have the same + // and identical tags. Two embedded fields are considered to have the same // name. Lower-case field names from different packages are always different. if y, ok := y.(*Struct); ok { if x.NumFields() == y.NumFields() { for i, f := range x.fields { g := y.fields[i] - if f.anonymous != g.anonymous || + if f.embedded != g.embedded || cmpTags && x.Tag(i) != y.Tag(i) || !f.sameId(g.pkg, g.name) || !identical(f.typ, g.typ, cmpTags, p) { diff --git a/libgo/go/go/types/resolver.go b/libgo/go/go/types/resolver.go index d03c179..5cbaba1 100644 --- a/libgo/go/go/types/resolver.go +++ b/libgo/go/go/types/resolver.go @@ -9,6 +9,7 @@ import ( "go/ast" "go/constant" "go/token" + "sort" "strconv" "strings" "unicode" @@ -24,8 +25,6 @@ type declInfo struct { alias bool // type alias declaration // The deps field tracks initialization expression dependencies. - // As a special (overloaded) case, it also tracks dependencies of - // interface types on embedded interfaces (see ordering.go). deps objSet // lazily initialized } @@ -216,6 +215,7 @@ func (check *Checker) collectObjects() { pkgImports[imp] = true } + var methods []*Func // list of methods with non-blank _ names for fileNo, file := range check.files { // The package identifier denotes the current package, // but there is no corresponding package object. @@ -411,20 +411,13 @@ func (check *Checker) collectObjects() { } } else { // method - check.recordDef(d.Name, obj) - // Associate method with receiver base type name, if possible. - // Ignore methods that have an invalid receiver, or a blank _ - // receiver name. They will be type-checked later, with regular - // functions. - if list := d.Recv.List; len(list) > 0 { - typ := unparen(list[0].Type) - if ptr, _ := typ.(*ast.StarExpr); ptr != nil { - typ = unparen(ptr.X) - } - if base, _ := typ.(*ast.Ident); base != nil && base.Name != "_" { - check.assocMethod(base.Name, obj) - } + // (Methods with blank _ names are never found; no need to collect + // them for later type association. They will still be type-checked + // with all the other functions.) + if name != "_" { + methods = append(methods, obj) } + check.recordDef(d.Name, obj) } info := &declInfo{file: fileScope, fdecl: d} check.objMap[obj] = info @@ -451,10 +444,90 @@ func (check *Checker) collectObjects() { } } } + + // Now that we have all package scope objects and all methods, + // associate methods with receiver base type name where possible. + // Ignore methods that have an invalid receiver. They will be + // type-checked later, with regular functions. + if methods == nil { + return // nothing to do + } + check.methods = make(map[*TypeName][]*Func) + for _, f := range methods { + fdecl := check.objMap[f].fdecl + if list := fdecl.Recv.List; len(list) > 0 { + // f is a method + // receiver may be of the form T or *T, possibly with parentheses + typ := unparen(list[0].Type) + if ptr, _ := typ.(*ast.StarExpr); ptr != nil { + typ = unparen(ptr.X) + } + if base, _ := typ.(*ast.Ident); base != nil { + // base is a potential base type name; determine + // "underlying" defined type and associate f with it + if tname := check.resolveBaseTypeName(base); tname != nil { + check.methods[tname] = append(check.methods[tname], f) + } + } + } + } } -// packageObjects typechecks all package objects in objList, but not function bodies. -func (check *Checker) packageObjects(objList []Object) { +// resolveBaseTypeName returns the non-alias receiver base type name, +// explicitly declared in the package scope, for the given receiver +// type name; or nil. +func (check *Checker) resolveBaseTypeName(name *ast.Ident) *TypeName { + var path []*TypeName + for { + // name must denote an object found in the current package scope + // (note that dot-imported objects are not in the package scope!) + obj := check.pkg.scope.Lookup(name.Name) + if obj == nil { + return nil + } + // the object must be a type name... + tname, _ := obj.(*TypeName) + if tname == nil { + return nil + } + + // ... which we have not seen before + if check.cycle(tname, path, false) { + return nil + } + + // we're done if tdecl defined tname as a new type + // (rather than an alias) + tdecl := check.objMap[tname] // must exist for objects in package scope + if !tdecl.alias { + return tname + } + + // Otherwise, if tdecl defined an alias for a (possibly parenthesized) + // type which is not an (unqualified) named type, we're done because + // receiver base types must be named types declared in this package. + typ := unparen(tdecl.typ) // a type may be parenthesized + name, _ = typ.(*ast.Ident) + if name == nil { + return nil + } + + // continue resolving name + path = append(path, tname) + } +} + +// packageObjects typechecks all package objects, but not function bodies. +func (check *Checker) packageObjects() { + // process package objects in source order for reproducible results + objList := make([]Object, len(check.objMap)) + i := 0 + for obj := range check.objMap { + objList[i] = obj + i++ + } + sort.Sort(inSourceOrder(objList)) + // add new methods to already type-checked types (from a prior Checker.Files call) for _, obj := range objList { if obj, _ := obj.(*TypeName); obj != nil && obj.typ != nil { @@ -476,10 +549,20 @@ func (check *Checker) packageObjects(objList []Object) { check.methods = nil } -// functionBodies typechecks all function bodies. -func (check *Checker) functionBodies() { - for _, f := range check.funcs { - check.funcBody(f.decl, f.name, f.sig, f.body) +// inSourceOrder implements the sort.Sort interface. +type inSourceOrder []Object + +func (a inSourceOrder) Len() int { return len(a) } +func (a inSourceOrder) Less(i, j int) bool { return a[i].order() < a[j].order() } +func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// processDelayed processes all delayed actions pushed after top. +func (check *Checker) processDelayed(top int) { + for len(check.delayed) > top { + i := len(check.delayed) - 1 + f := check.delayed[i] + check.delayed = check.delayed[:i] + f() // may append to check.delayed } } diff --git a/libgo/go/go/types/return.go b/libgo/go/go/types/return.go index 0c1447f..2d34a70 100644 --- a/libgo/go/go/types/return.go +++ b/libgo/go/go/types/return.go @@ -28,15 +28,9 @@ func (check *Checker) isTerminating(s ast.Stmt, label string) bool { return check.isTerminating(s.Stmt, s.Label.Name) case *ast.ExprStmt: - // the predeclared (possibly parenthesized) panic() function is terminating - if call, _ := unparen(s.X).(*ast.CallExpr); call != nil { - if id, _ := call.Fun.(*ast.Ident); id != nil { - if _, obj := check.scope.LookupParent(id.Name, token.NoPos); obj != nil { - if b, _ := obj.(*Builtin); b != nil && b.id == _Panic { - return true - } - } - } + // calling the predeclared (possibly parenthesized) panic() function is terminating + if call, ok := unparen(s.X).(*ast.CallExpr); ok && check.isPanic[call] { + return true } case *ast.ReturnStmt: diff --git a/libgo/go/go/types/sizes.go b/libgo/go/go/types/sizes.go index 13e2abb..300f521 100644 --- a/libgo/go/go/types/sizes.go +++ b/libgo/go/go/types/sizes.go @@ -132,9 +132,10 @@ func (s *StdSizes) Sizeof(T Type) int64 { } case *Array: n := t.len - if n == 0 { + if n <= 0 { return 0 } + // n > 0 a := s.Alignof(t.elem) z := s.Sizeof(t.elem) return align(z, a)*(n-1) + z @@ -166,6 +167,7 @@ var gcArchSizes = map[string]*StdSizes{ "mips64le": {8, 8}, "ppc64": {8, 8}, "ppc64le": {8, 8}, + "riscv64": {8, 8}, "s390x": {8, 8}, "wasm": {8, 8}, // When adding more architectures here, @@ -177,7 +179,7 @@ var gcArchSizes = map[string]*StdSizes{ // // Supported architectures for compiler "gc": // "386", "arm", "arm64", "amd64", "amd64p32", "mips", "mipsle", -// "mips64", "mips64le", "ppc64", "ppc64le", "s390x", "wasm". +// "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "wasm". func SizesFor(compiler, arch string) Sizes { var m map[string]*StdSizes switch compiler { diff --git a/libgo/go/go/types/stdlib_test.go b/libgo/go/go/types/stdlib_test.go index 39a3be8..b2f31ef 100644 --- a/libgo/go/go/types/stdlib_test.go +++ b/libgo/go/go/types/stdlib_test.go @@ -179,6 +179,8 @@ func TestStdFixed(t *testing.T) { "issue20529.go", // go/types does not have constraints on stack size "issue22200.go", // go/types does not have constraints on stack size "issue22200b.go", // go/types does not have constraints on stack size + "issue25507.go", // go/types does not have constraints on stack size + "issue20780.go", // go/types does not have constraints on stack size ) } diff --git a/libgo/go/go/types/stmt.go b/libgo/go/go/types/stmt.go index 5221bcc..abd9d05 100644 --- a/libgo/go/go/types/stmt.go +++ b/libgo/go/go/types/stmt.go @@ -7,20 +7,18 @@ package types import ( - "fmt" "go/ast" "go/constant" "go/token" "sort" ) -func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt) { +func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt, iota constant.Value) { if trace { - if name == "" { - name = "<function literal>" - } - fmt.Printf("--- %s: %s {\n", name, sig) - defer fmt.Println("--- <end>") + check.trace(body.Pos(), "--- %s: %s", name, sig) + defer func() { + check.trace(body.End(), "--- <end>") + }() } // set function scope extent @@ -36,6 +34,7 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body check.context = context{ decl: decl, scope: sig.scope, + iota: iota, sig: sig, } check.indent = 0 @@ -52,8 +51,6 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body // spec: "Implementation restriction: A compiler may make it illegal to // declare a variable inside a function body if the variable is never used." - // (One could check each scope after use, but that distributes this check - // over several places because CloseScope is not always called explicitly.) check.usage(sig.scope) } @@ -72,7 +69,7 @@ func (check *Checker) usage(scope *Scope) { } for _, scope := range scope.children { - // Don't go inside closure scopes a second time; + // Don't go inside function literal scopes a second time; // they are handled explicitly by funcBody. if !scope.isFunc { check.usage(scope) @@ -294,10 +291,6 @@ L: // stmt typechecks statement s. func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { - // statements cannot use iota in general - // (constant declarations set it explicitly) - assert(check.iota == nil) - // statements must end with the same top scope as they started with if debug { defer func(scope *Scope) { @@ -309,6 +302,9 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { }(check.scope) } + // process collected function literals before scope changes + defer check.processDelayed(len(check.delayed)) + inner := ctxt &^ (fallthroughOk | finalSwitchCase) switch s := s.(type) { case *ast.BadStmt, *ast.EmptyStmt: @@ -440,7 +436,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { // list in a "return" statement if a different entity (constant, type, or variable) // with the same name as a result parameter is in scope at the place of the return." for _, obj := range res.vars { - if _, alt := check.scope.LookupParent(obj.name, check.pos); alt != nil && alt != obj { + if alt := check.lookup(obj.name); alt != nil && alt != obj { check.errorf(s.Pos(), "result parameter %s not in scope at return", obj.name) check.errorf(alt.Pos(), "\tinner declaration of %s", obj) // ok to continue diff --git a/libgo/go/go/types/testdata/const0.src b/libgo/go/go/types/testdata/const0.src index a617178..19fb1bd 100644 --- a/libgo/go/go/types/testdata/const0.src +++ b/libgo/go/go/types/testdata/const0.src @@ -6,6 +6,8 @@ package const0 +import "unsafe" + // constants declarations must be initialized by constants var x = 0 const c0 = x /* ERROR "not constant" */ @@ -281,6 +283,45 @@ func _() { _ = y } +// iotas are usable inside closures in constant declarations (#22345) +const ( + _ = iota + _ = len([iota]byte{}) + _ = unsafe.Sizeof(iota) + _ = unsafe.Sizeof(func() { _ = iota }) + _ = unsafe.Sizeof(func() { var _ = iota }) + _ = unsafe.Sizeof(func() { const _ = iota }) + _ = unsafe.Sizeof(func() { type _ [iota]byte }) + _ = unsafe.Sizeof(func() { func() int { return iota }() }) +) + +// verify inner and outer const declarations have distinct iotas +const ( + zero = iota + one = iota + _ = unsafe.Sizeof(func() { + var x [iota]int // [2]int + const ( + Zero = iota + One + Two + _ = unsafe.Sizeof([iota-1]int{} == x) // assert types are equal + _ = unsafe.Sizeof([Two]int{} == x) // assert types are equal + ) + }) + three = iota // the sequence continues +) +var _ [three]int = [3]int{} // assert 'three' has correct value + +var ( + _ = iota /* ERROR "iota outside constant decl" */ + _ = unsafe.Sizeof(iota /* ERROR "iota outside constant decl" */ ) + _ = unsafe.Sizeof(func() { _ = iota /* ERROR "iota outside constant decl" */ }) + _ = unsafe.Sizeof(func() { var _ = iota /* ERROR "iota outside constant decl" */ }) + _ = unsafe.Sizeof(func() { type _ [iota /* ERROR "iota outside constant decl" */ ]byte }) + _ = unsafe.Sizeof(func() { func() int { return iota /* ERROR "iota outside constant decl" */ }() }) +) + // constant arithmetic precision and rounding must lead to expected (integer) results var _ = []int64{ 0.0005 * 1e9, diff --git a/libgo/go/go/types/testdata/constdecl.src b/libgo/go/go/types/testdata/constdecl.src index 6de9b13..c2f40ed 100644 --- a/libgo/go/go/types/testdata/constdecl.src +++ b/libgo/go/go/types/testdata/constdecl.src @@ -5,6 +5,7 @@ package constdecl import "math" +import "unsafe" var v int @@ -94,4 +95,16 @@ func _() { ) } +// Test case for constants depending on function literals (see also #22992). +const A /* ERROR initialization cycle */ = unsafe.Sizeof(func() { _ = A }) + +func _() { + // The function literal below must not see a. + const a = unsafe.Sizeof(func() { _ = a /* ERROR "undeclared name" */ }) + const b = unsafe.Sizeof(func() { _ = a }) + + // The function literal below must not see x, y, or z. + const x, y, z = 0, 1, unsafe.Sizeof(func() { _ = x /* ERROR "undeclared name" */ + y /* ERROR "undeclared name" */ + z /* ERROR "undeclared name" */ }) +} + // TODO(gri) move extra tests from testdata/const0.src into here diff --git a/libgo/go/go/types/testdata/cycles.src b/libgo/go/go/types/testdata/cycles.src index b4bd5d8b..59f112d 100644 --- a/libgo/go/go/types/testdata/cycles.src +++ b/libgo/go/go/types/testdata/cycles.src @@ -4,6 +4,8 @@ package cycles +import "unsafe" + type ( T0 int T1 /* ERROR cycle */ T1 @@ -52,9 +54,9 @@ type ( // interfaces I0 /* ERROR cycle */ interface{ I0 } - I1 interface{ I2 } + I1 /* ERROR cycle */ interface{ I2 } I2 interface{ I3 } - I3 /* ERROR cycle */ interface{ I1 } + I3 interface{ I1 } I4 interface{ f(I4) } @@ -145,8 +147,17 @@ type ( // test cases for issue 18643 // (type cycle detection when non-type expressions are involved) type ( - T14 [len(T14 /* ERROR cycle */ {})]int + T14 /* ERROR cycle */ [len(T14{})]int T15 [][len(T15 /* ERROR cycle */ {})]int T16 map[[len(T16 /* ERROR cycle */ {1:2})]int]int T17 map[int][len(T17 /* ERROR cycle */ {1:2})]int ) + +// Test case for types depending on function literals (see also #22992). +type T20 chan [unsafe.Sizeof(func(ch T20){ _ = <-ch })]byte +type T22 = chan [unsafe.Sizeof(func(ch T20){ _ = <-ch })]byte + +func _() { + type T1 chan [unsafe.Sizeof(func(ch T1){ _ = <-ch })]byte + type T2 = chan [unsafe.Sizeof(func(ch T2){ _ = <-ch })]byte +} diff --git a/libgo/go/go/types/testdata/cycles2.src b/libgo/go/go/types/testdata/cycles2.src index 345ab56..a7f4bc6 100644 --- a/libgo/go/go/types/testdata/cycles2.src +++ b/libgo/go/go/types/testdata/cycles2.src @@ -69,47 +69,38 @@ type T interface { // Variations of this test case. -type T1 interface { - m() [x1 /* ERROR no value */ .m()[0]]int +type T1 /* ERROR cycle */ interface { + m() [x1.m()[0]]int } var x1 T1 -type T2 interface { - m() [len(x2 /* ERROR no value */ .m())]int +type T2 /* ERROR cycle */ interface { + m() [len(x2.m())]int } var x2 T2 -type T3 interface { +type T3 /* ERROR cycle */ interface { m() [unsafe.Sizeof(x3.m)]int } var x3 T3 -// The test case below should also report an error for -// the cast inside the T4 interface (like it does for the -// variable initialization). The reason why it does not is -// that inside T4, the method x4.m depends on T4 which is not -// fully set up yet. The x4.m method happens to have an empty -// signature which is why the cast is permitted. -// TODO(gri) Consider marking methods as incomplete and provide -// a better error message in that case. - -type T4 interface { +type T4 /* ERROR cycle */ interface { m() [unsafe.Sizeof(cast4(x4.m))]int } var x4 T4 -var _ = cast4(x4 /* ERROR cannot convert */.m) +var _ = cast4(x4.m) type cast4 func() // This test is symmetric to the T4 case: Here the cast is // "correct", but it doesn't work inside the T5 interface. -type T5 interface { - m() [unsafe.Sizeof(cast5(x5 /* ERROR cannot convert */ .m))]int +type T5 /* ERROR cycle */ interface { + m() [unsafe.Sizeof(cast5(x5.m))]int } var x5 T5 diff --git a/libgo/go/go/types/testdata/cycles3.src b/libgo/go/go/types/testdata/cycles3.src index 3da4fb5..5e89b62 100644 --- a/libgo/go/go/types/testdata/cycles3.src +++ b/libgo/go/go/types/testdata/cycles3.src @@ -48,7 +48,7 @@ type ( ) type ( - U interface { + U /* ERROR cycle */ interface { V } diff --git a/libgo/go/go/types/testdata/cycles4.src b/libgo/go/go/types/testdata/cycles4.src index 3f6304b..445babc 100644 --- a/libgo/go/go/types/testdata/cycles4.src +++ b/libgo/go/go/types/testdata/cycles4.src @@ -108,15 +108,3 @@ type Element interface { type Event interface { Target() Element } - -// Recognize issue #13895. - -type ( - _ interface{ m(B1) } - A1 interface{ a(D1) } - B1 interface{ A1 } - C1 interface{ B1 /* ERROR issue #18395 */ } - D1 interface{ C1 } -) - -var _ A1 = C1 /* ERROR cannot use C1 */ (nil)
\ No newline at end of file diff --git a/libgo/go/go/types/testdata/decls0.src b/libgo/go/go/types/testdata/decls0.src index d4df386..162dfed 100644 --- a/libgo/go/go/types/testdata/decls0.src +++ b/libgo/go/go/types/testdata/decls0.src @@ -61,7 +61,7 @@ type ( type ( - p1 pi /* ERROR "no field or method foo" */ .foo + p1 pi.foo /* ERROR "no field or method foo" */ p2 unsafe.Pointer ) @@ -97,7 +97,7 @@ type ( u, v, a /* ERROR "redeclared" */ float32 } S2 struct { - S0 // anonymous field + S0 // embedded field S0 /* ERROR "redeclared" */ int } S3 struct { @@ -159,13 +159,13 @@ type ( I8 /* ERROR "illegal cycle" */ interface { I8 } - I9 interface { + I9 /* ERROR "illegal cycle" */ interface { I10 } I10 interface { I11 } - I11 /* ERROR "illegal cycle" */ interface { + I11 interface { I9 } @@ -184,15 +184,15 @@ type ( // cycles in function/method declarations // (test cases for issue 5217 and variants) -func f1(x f1 /* ERROR "not a type" */ ) {} -func f2(x *f2 /* ERROR "not a type" */ ) {} -func f3() (x f3 /* ERROR "not a type" */ ) { return } -func f4() (x *f4 /* ERROR "not a type" */ ) { return } - -func (S0) m1(x S0 /* ERROR "field or method" */ .m1) {} -func (S0) m2(x *S0 /* ERROR "field or method" */ .m2) {} -func (S0) m3() (x S0 /* ERROR "field or method" */ .m3) { return } -func (S0) m4() (x *S0 /* ERROR "field or method" */ .m4) { return } +func f1 /* ERROR cycle */ (x f1 /* ERROR "not a type" */ ) {} +func f2 /* ERROR cycle */ (x *f2 /* ERROR "not a type" */ ) {} +func f3 /* ERROR cycle */ () (x f3 /* ERROR "not a type" */ ) { return } +func f4 /* ERROR cycle */ () (x *f4 /* ERROR "not a type" */ ) { return } + +func (S0) m1(x S0.m1 /* ERROR "field or method" */ ) {} +func (S0) m2(x *S0.m2 /* ERROR "field or method" */ ) {} +func (S0) m3() (x S0.m3 /* ERROR "field or method" */ ) { return } +func (S0) m4() (x *S0.m4 /* ERROR "field or method" */ ) { return } // interfaces may not have any blank methods type BlankI interface { diff --git a/libgo/go/go/types/testdata/decls1.src b/libgo/go/go/types/testdata/decls1.src index 1ef2806..0740546 100644 --- a/libgo/go/go/types/testdata/decls1.src +++ b/libgo/go/go/types/testdata/decls1.src @@ -64,7 +64,7 @@ var ( t13 int = a /* ERROR "shifted operand" */ << d t14 int = i << j /* ERROR "must be unsigned" */ t15 math /* ERROR "not in selector" */ - t16 math /* ERROR "not declared" */ .xxx + t16 math.xxx /* ERROR "not declared" */ t17 math /* ERROR "not a type" */ .Pi t18 float64 = math.Pi * 10.0 t19 int = t1 /* ERROR "cannot call" */ () diff --git a/libgo/go/go/types/testdata/decls3.src b/libgo/go/go/types/testdata/decls3.src index 80d2bc8..18ddf58 100644 --- a/libgo/go/go/types/testdata/decls3.src +++ b/libgo/go/go/types/testdata/decls3.src @@ -19,7 +19,7 @@ func _() { ) var t T3 - _ = t /* ERROR "ambiguous selector" */ .X + _ = t.X /* ERROR "ambiguous selector" */ } func _() { @@ -31,7 +31,7 @@ func _() { ) var t T4 - _ = t /* ERROR "ambiguous selector" */ .X + _ = t.X /* ERROR "ambiguous selector" */ } func issue4355() { @@ -41,10 +41,10 @@ func issue4355() { T3 struct {T2} T4 struct {T2} T5 struct {T3; T4} // X is embedded twice at the same level via T3->T2->T1->X, T4->T2->T1->X - ) + ) var t T5 - _ = t /* ERROR "ambiguous selector" */ .X + _ = t.X /* ERROR "ambiguous selector" */ } func _() { @@ -54,7 +54,7 @@ func _() { type T struct{ A; B } var t T - _ = t /* ERROR "ambiguous selector" */ .State + _ = t.State /* ERROR "ambiguous selector" */ } // Embedded fields can be predeclared types. @@ -99,9 +99,9 @@ func _() { // unsafe.Pointers are treated like regular pointers when embedded type T2 struct { unsafe /* ERROR "cannot be unsafe.Pointer" */ .Pointer - */* ERROR "cannot be unsafe.Pointer" */ unsafe.Pointer + */* ERROR "cannot be unsafe.Pointer" */ /* ERROR "Pointer redeclared" */ unsafe.Pointer UP /* ERROR "cannot be unsafe.Pointer" */ - * /* ERROR "cannot be unsafe.Pointer" */ UP + * /* ERROR "cannot be unsafe.Pointer" */ /* ERROR "UP redeclared" */ UP } } @@ -118,8 +118,8 @@ func _() { var p P _ = p.x - _ = p /* ERROR "no field or method" */ .m - _ = P /* ERROR "no field or method" */ .m + _ = p.m /* ERROR "no field or method" */ + _ = P.m /* ERROR "no field or method" */ } // Borrowed from the FieldByName test cases in reflect/all_test.go. @@ -209,9 +209,9 @@ type S13 struct { } func _() { - _ = struct /* ERROR "no field or method" */ {}{}.Foo + _ = struct{}{}.Foo /* ERROR "no field or method" */ _ = S0{}.A - _ = S0 /* ERROR "no field or method" */ {}.D + _ = S0{}.D /* ERROR "no field or method" */ _ = S1{}.A _ = S1{}.B _ = S1{}.S0 @@ -220,17 +220,17 @@ func _() { _ = S2{}.S1 _ = S2{}.B _ = S2{}.C - _ = S2 /* ERROR "no field or method" */ {}.D - _ = S3 /* ERROR "ambiguous selector" */ {}.S1 + _ = S2{}.D /* ERROR "no field or method" */ + _ = S3{}.S1 /* ERROR "ambiguous selector" */ _ = S3{}.A - _ = S3 /* ERROR "ambiguous selector" */ {}.B + _ = S3{}.B /* ERROR "ambiguous selector" */ _ = S3{}.D _ = S3{}.E _ = S4{}.A - _ = S4 /* ERROR "no field or method" */ {}.B - _ = S5 /* ERROR "ambiguous selector" */ {}.X + _ = S4{}.B /* ERROR "no field or method" */ + _ = S5{}.X /* ERROR "ambiguous selector" */ _ = S5{}.Y - _ = S10 /* ERROR "ambiguous selector" */ {}.X + _ = S10{}.X /* ERROR "ambiguous selector" */ _ = S10{}.Y } @@ -306,4 +306,4 @@ type R22 R21 type R23 R21 type R24 R21 -var _ = R0 /* ERROR "ambiguous selector" */ {}.X
\ No newline at end of file +var _ = R0{}.X /* ERROR "ambiguous selector" */
\ No newline at end of file diff --git a/libgo/go/go/types/testdata/errors.src b/libgo/go/go/types/testdata/errors.src index 29fcd8f..ff92921 100644 --- a/libgo/go/go/types/testdata/errors.src +++ b/libgo/go/go/types/testdata/errors.src @@ -53,3 +53,8 @@ func _() { // Use unqualified names for package-local objects. type T struct{} var _ int = T /* ERROR value of type T */ {} // use T in error message rather then errors.T + +// Don't report errors containing "invalid type" (issue #24182). +func _(x *missing /* ERROR undeclared name: missing */ ) { + x.m() // there shouldn't be an error here referring to *invalid type +} diff --git a/libgo/go/go/types/testdata/expr3.src b/libgo/go/go/types/testdata/expr3.src index b7ab9b3..b4c8163 100644 --- a/libgo/go/go/types/testdata/expr3.src +++ b/libgo/go/go/types/testdata/expr3.src @@ -153,17 +153,17 @@ type T struct { func (*T) m() {} func method_expressions() { - _ = T /* ERROR "no field or method" */ .a - _ = T /* ERROR "has no method" */ .x - _ = T /* ERROR "not in method set" */ .m + _ = T.a /* ERROR "no field or method" */ + _ = T.x /* ERROR "has no method" */ + _ = T.m /* ERROR "not in method set" */ _ = (*T).m - var f func(*T) = T /* ERROR "not in method set" */ .m + var f func(*T) = T.m /* ERROR "not in method set" */ var g func(*T) = (*T).m _, _ = f, g - _ = T /* ERROR "has no method" */ .y - _ = ( /* ERROR "has no method" */ *T).y + _ = T.y /* ERROR "has no method" */ + _ = (*T).y /* ERROR "has no method" */ } func struct_literals() { diff --git a/libgo/go/go/types/testdata/importC.src b/libgo/go/go/types/testdata/importC.src index f50f7f3..f55be2d 100644 --- a/libgo/go/go/types/testdata/importC.src +++ b/libgo/go/go/types/testdata/importC.src @@ -20,7 +20,7 @@ type T struct { Ordinal int } -func f(args []T) { +func _(args []T) { var s string for i, v := range args { cname := C.CString(v.Name) @@ -33,3 +33,22 @@ type CType C.Type const _ CType = C.X // no error due to invalid constant type const _ = C.X + +// Test cases extracted from issue #23712. + +func _() { + var a [C.ArrayLength]byte + _ = a[0] // no index out of bounds error here +} + +// Additional tests to verify fix for #23712. + +func _() { + var a [C.ArrayLength1]byte + _ = 1 / len(a) // no division by zero error here and below + _ = 1 / cap(a) + _ = uint(unsafe.Sizeof(a)) // must not be negative + + var b [C.ArrayLength2]byte + a = b // should be valid +} diff --git a/libgo/go/go/types/testdata/importdecl0a.src b/libgo/go/go/types/testdata/importdecl0a.src index 463dcd0..e96fca3 100644 --- a/libgo/go/go/types/testdata/importdecl0a.src +++ b/libgo/go/go/types/testdata/importdecl0a.src @@ -32,7 +32,7 @@ import f2 "fmt" // reflect.flag must not be visible in this package type flag int -type _ reflect /* ERROR "not exported" */ .flag +type _ reflect.flag /* ERROR "not exported" */ // imported package name may conflict with local objects type reflect /* ERROR "reflect already declared" */ int diff --git a/libgo/go/go/types/testdata/importdecl1a.src b/libgo/go/go/types/testdata/importdecl1a.src index 8301820..d377c01 100644 --- a/libgo/go/go/types/testdata/importdecl1a.src +++ b/libgo/go/go/types/testdata/importdecl1a.src @@ -6,6 +6,17 @@ package importdecl1 +import "go/ast" import . "unsafe" var _ Pointer // use dot-imported package unsafe + +// Test cases for issue 23914. + +type A interface { + // Methods m1, m2 must be type-checked in this file scope + // even when embedded in an interface in a different + // file of the same package. + m1() ast.Node + m2() Pointer +} diff --git a/libgo/go/go/types/testdata/importdecl1b.src b/libgo/go/go/types/testdata/importdecl1b.src index f24bb9a..ee70bbd 100644 --- a/libgo/go/go/types/testdata/importdecl1b.src +++ b/libgo/go/go/types/testdata/importdecl1b.src @@ -5,3 +5,7 @@ package importdecl1 import . /* ERROR "imported but not used" */ "unsafe" + +type B interface { + A +} diff --git a/libgo/go/go/types/testdata/init0.src b/libgo/go/go/types/testdata/init0.src index ef0349c..6e8746a 100644 --- a/libgo/go/go/types/testdata/init0.src +++ b/libgo/go/go/types/testdata/init0.src @@ -75,7 +75,7 @@ var x8 = x7 func f2() (int, int) { return f3() + f3(), 0 } func f3() int { return x8 } -// cycles via closures +// cycles via function literals var x9 /* ERROR initialization cycle */ = func() int { return x9 }() diff --git a/libgo/go/go/types/testdata/issues.src b/libgo/go/go/types/testdata/issues.src index 8729555..d85e04e 100644 --- a/libgo/go/go/types/testdata/issues.src +++ b/libgo/go/go/types/testdata/issues.src @@ -84,7 +84,7 @@ func issue10979() { nosuchtype /* ERROR undeclared name: nosuchtype */ } type _ interface { - fmt /* ERROR Nosuchtype not declared by package fmt */ .Nosuchtype + fmt.Nosuchtype /* ERROR Nosuchtype not declared by package fmt */ } type _ interface { nosuchpkg /* ERROR undeclared name: nosuchpkg */ .Nosuchtype @@ -207,3 +207,90 @@ func issue20358() { _ = T{t} _ = P{f: p} } + +// Test that we don't declare lhs variables in short variable +// declarations before we type-check function literals on the +// rhs. +func issue24026() { + f := func() int { f(0) /* must refer to outer f */; return 0 } + _ = f + + _ = func() { + f := func() { _ = f() /* must refer to outer f */ } + _ = f + } + + // b and c must not be visible inside function literal + a := 0 + a, b, c := func() (int, int, int) { + return a, b /* ERROR undeclared */ , c /* ERROR undeclared */ + }() + _, _ = b, c +} + +func f(int) {} // for issue24026 + +// Test that we don't report a "missing return statement" error +// (due to incorrect context when type-checking interfaces). +func issue24140(x interface{}) int { + switch x.(type) { + case interface{}: + return 0 + default: + panic(0) + } +} + +// Test that we don't crash when the 'if' condition is missing. +func issue25438() { + if { /* ERROR missing condition */ } + if x := 0; /* ERROR missing condition */ { _ = x } + if + { /* ERROR missing condition */ } +} + +// Test that we can embed alias type names in interfaces. +type issue25301 interface { + E +} + +type E = interface { + m() +} + +// Test case from issue. Eventually we may disallow this due +// to the cycle via the alias type name. But for now we make +// sure this is accepted. +type issue25301b = interface { + m() interface{ issue25301b } +} + +type issue25301c interface { + notE // ERROR struct\{\} is not an interface +} + +type notE = struct{} + +// Test that method declarations don't introduce artificial cycles +// (issue #26124). +const CC TT = 1 +type TT int +func (TT) MM() [CC]TT + +// Reduced test case from issue #26124. +const preloadLimit LNumber = 128 +type LNumber float64 +func (LNumber) assertFunction() *LFunction +type LFunction struct { + GFunction LGFunction +} +type LGFunction func(*LState) +type LState struct { + reg *registry +} +type registry struct { + alloc *allocator +} +type allocator struct { + _ [int(preloadLimit)]int +} diff --git a/libgo/go/go/types/testdata/methodsets.src b/libgo/go/go/types/testdata/methodsets.src index 8921146..2f21faf 100644 --- a/libgo/go/go/types/testdata/methodsets.src +++ b/libgo/go/go/types/testdata/methodsets.src @@ -29,7 +29,7 @@ type T3 struct { func _() { var ( _ func(T0) = T0.v0 - _ = T0 /* ERROR "not in method set" */ .p0 + _ = T0.p0 /* ERROR "not in method set" */ _ func (*T0) = (*T0).v0 _ func (*T0) = (*T0).p0 @@ -40,7 +40,7 @@ func _() { _ func(T2) = T2.p2 _ func(T3) = T3.v0 - _ func(T3) = T3 /* ERROR "not in method set" */ .p0 + _ func(T3) = T3.p0 /* ERROR "not in method set" */ _ func(T3) = T3.v1 _ func(T3) = T3.p1 _ func(T3) = T3.v2 @@ -135,7 +135,7 @@ func _() { func _() { var ( _ func() = T0{}.v0 - _ func() = T0 /* ERROR "not in method set" */ {}.p0 + _ func() = T0{}.p0 /* ERROR "not in method set" */ _ func() = (&T0{}).v0 _ func() = (&T0{}).p0 @@ -145,7 +145,7 @@ func _() { // no values for T2 _ func() = T3{}.v0 - _ func() = T3 /* ERROR "not in method set" */ {}.p0 + _ func() = T3{}.p0 /* ERROR "not in method set" */ _ func() = T3{}.v1 _ func() = T3{}.p1 _ func() = T3{}.v2 @@ -163,7 +163,7 @@ func _() { // Method calls with value receivers func _() { T0{}.v0() - T0 /* ERROR "not in method set" */ {}.p0() + T0{}.p0 /* ERROR "not in method set" */ () (&T0{}).v0() (&T0{}).p0() @@ -173,7 +173,7 @@ func _() { // no values for T2 T3{}.v0() - T3 /* ERROR "not in method set" */ {}.p0() + T3{}.p0 /* ERROR "not in method set" */ () T3{}.v1() T3{}.p1() T3{}.v2() @@ -196,9 +196,9 @@ func issue5918() { _ func(error) string = error.Error perr = &err - _ = perr /* ERROR "no field or method" */ .Error() - _ func() string = perr /* ERROR "no field or method" */ .Error - _ func(*error) string = ( /* ERROR "no field or method" */ *error).Error + _ = perr.Error /* ERROR "no field or method" */ () + _ func() string = perr.Error /* ERROR "no field or method" */ + _ func(*error) string = (*error).Error /* ERROR "no field or method" */ ) type T *interface{ m() int } @@ -207,8 +207,8 @@ func issue5918() { _ = (*x).m() _ = (*x).m - _ = x /* ERROR "no field or method" */ .m() - _ = x /* ERROR "no field or method" */ .m - _ = T /* ERROR "no field or method" */ .m + _ = x.m /* ERROR "no field or method" */ () + _ = x.m /* ERROR "no field or method" */ + _ = T.m /* ERROR "no field or method" */ ) } diff --git a/libgo/go/go/types/testdata/stmt1.src b/libgo/go/go/types/testdata/stmt1.src index 24ad6eb..f79f920 100644 --- a/libgo/go/go/types/testdata/stmt1.src +++ b/libgo/go/go/types/testdata/stmt1.src @@ -239,3 +239,21 @@ L: select { } ; ; ; } /* ERROR "missing return" */ + +func parenPanic() int { + ((((((panic)))(0)))) +} + +func issue23218a() int { + { + panic := func(interface{}){} + panic(0) + } +} /* ERROR "missing return" */ + +func issue23218b() int { + { + panic := func(interface{}){} + ((((panic))))(0) + } +} /* ERROR "missing return" */ diff --git a/libgo/go/go/types/testdata/vardecl.src b/libgo/go/go/types/testdata/vardecl.src index 197dec2..54f5ef1 100644 --- a/libgo/go/go/types/testdata/vardecl.src +++ b/libgo/go/go/types/testdata/vardecl.src @@ -151,7 +151,7 @@ func (r T) _(a, b, c int) (u, v, w int) { return } -// Unused variables in closures must lead to only one error (issue #22524). +// Unused variables in function literals must lead to only one error (issue #22524). func _() { _ = func() { var x /* ERROR declared but not used */ int @@ -190,4 +190,17 @@ func _() { _ = b } +// Test case for variables depending on function literals (see also #22992). +var A /* ERROR initialization cycle */ = func() int { return A }() + +func _() { + // The function literal below must not see a. + var a = func() int { return a /* ERROR "undeclared name" */ }() + var _ = func() int { return a }() + + // The function literal below must not see x, y, or z. + var x, y, z = 0, 1, func() int { return x /* ERROR "undeclared name" */ + y /* ERROR "undeclared name" */ + z /* ERROR "undeclared name" */ }() + _, _, _ = x, y, z +} + // TODO(gri) consolidate other var decl checks in this file
\ No newline at end of file diff --git a/libgo/go/go/types/type.go b/libgo/go/go/types/type.go index a58684a..d9399a6 100644 --- a/libgo/go/go/types/type.go +++ b/libgo/go/go/types/type.go @@ -97,9 +97,11 @@ type Array struct { } // NewArray returns a new array type for the given element type and length. +// A negative length indicates an unknown length. func NewArray(elem Type, len int64) *Array { return &Array{len, elem} } // Len returns the length of array a. +// A negative result indicates an unknown length. func (a *Array) Len() int64 { return a.len } // Elem returns element type of array a. @@ -139,7 +141,7 @@ func NewStruct(fields []*Var, tags []string) *Struct { return &Struct{fields: fields, tags: tags} } -// NumFields returns the number of fields in the struct (including blank and anonymous fields). +// NumFields returns the number of fields in the struct (including blank and embedded fields). func (s *Struct) NumFields() int { return len(s.fields) } // Field returns the i'th field for 0 <= i < NumFields(). @@ -240,8 +242,8 @@ func (s *Signature) Variadic() bool { return s.variadic } // An Interface represents an interface type. type Interface struct { - methods []*Func // ordered list of explicitly declared methods - embeddeds []*Named // ordered list of explicitly embedded types + methods []*Func // ordered list of explicitly declared methods + embeddeds []Type // ordered list of explicitly embedded types allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset) } @@ -254,8 +256,28 @@ var emptyInterface = Interface{allMethods: markComplete} var markComplete = make([]*Func, 0) // NewInterface returns a new (incomplete) interface for the given methods and embedded types. -// To compute the method set of the interface, Complete must be called. +// Each embedded type must have an underlying type of interface type. +// NewInterface takes ownership of the provided methods and may modify their types by setting +// missing receivers. To compute the method set of the interface, Complete must be called. +// +// Deprecated: Use NewInterfaceType instead which allows any (even non-defined) interface types +// to be embedded. This is necessary for interfaces that embed alias type names referring to +// non-defined (literal) interface types. func NewInterface(methods []*Func, embeddeds []*Named) *Interface { + tnames := make([]Type, len(embeddeds)) + for i, t := range embeddeds { + tnames[i] = t + } + return NewInterfaceType(methods, tnames) +} + +// NewInterfaceType returns a new (incomplete) interface for the given methods and embedded types. +// Each embedded type must have an underlying type of interface type (this property is not +// verified for defined types, which may be in the process of being set up and which don't +// have a valid underlying type yet). +// NewInterfaceType takes ownership of the provided methods and may modify their types by setting +// missing receivers. To compute the method set of the interface, Complete must be called. +func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface { typ := new(Interface) if len(methods) == 0 && len(embeddeds) == 0 { @@ -267,15 +289,24 @@ func NewInterface(methods []*Func, embeddeds []*Named) *Interface { if mset.insert(m) != nil { panic("multiple methods with the same name") } - // set receiver - // TODO(gri) Ideally, we should use a named type here instead of - // typ, for less verbose printing of interface method signatures. - m.typ.(*Signature).recv = NewVar(m.pos, m.pkg, "", typ) + // set receiver if we don't have one + if sig := m.typ.(*Signature); sig.recv == nil { + sig.recv = NewVar(m.pos, m.pkg, "", typ) + } } sort.Sort(byUniqueMethodName(methods)) - if embeddeds != nil { - sort.Sort(byUniqueTypeName(embeddeds)) + if len(embeddeds) > 0 { + // All embedded types should be interfaces; however, defined types + // may not yet be fully resolved. Only verify that non-defined types + // are interfaces. This matches the behavior of the code before the + // fix for #25301 (issue #25596). + for _, t := range embeddeds { + if _, ok := t.(*Named); !ok && !IsInterface(t) { + panic("embedded type is not an interface") + } + } + sort.Stable(byUniqueTypeName(embeddeds)) } typ.methods = methods @@ -293,9 +324,14 @@ func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] } // NumEmbeddeds returns the number of embedded types in interface t. func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) } -// Embedded returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds(). -// The types are ordered by the corresponding TypeName's unique Id. -func (t *Interface) Embedded(i int) *Named { return t.embeddeds[i] } +// Embedded returns the i'th embedded defined (*Named) type of interface t for 0 <= i < t.NumEmbeddeds(). +// The result is nil if the i'th embedded type is not a defined type. +// +// Deprecated: Use EmbeddedType which is not restricted to defined (*Named) types. +func (t *Interface) Embedded(i int) *Named { tname, _ := t.embeddeds[i].(*Named); return tname } + +// EmbeddedType returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds(). +func (t *Interface) EmbeddedType(i int) Type { return t.embeddeds[i] } // NumMethods returns the total number of methods of interface t. func (t *Interface) NumMethods() int { return len(t.allMethods) } @@ -308,36 +344,33 @@ func (t *Interface) Method(i int) *Func { return t.allMethods[i] } func (t *Interface) Empty() bool { return len(t.allMethods) == 0 } // Complete computes the interface's method set. It must be called by users of -// NewInterface after the interface's embedded types are fully defined and -// before using the interface type in any way other than to form other types. -// Complete returns the receiver. +// NewInterfaceType and NewInterface after the interface's embedded types are +// fully defined and before using the interface type in any way other than to +// form other types. Complete returns the receiver. func (t *Interface) Complete() *Interface { if t.allMethods != nil { return t } var allMethods []*Func - if t.embeddeds == nil { - if t.methods == nil { - allMethods = make([]*Func, 0, 1) - } else { - allMethods = t.methods - } - } else { - allMethods = append(allMethods, t.methods...) - for _, et := range t.embeddeds { - it := et.Underlying().(*Interface) - it.Complete() - for _, tm := range it.allMethods { - // Make a copy of the method and adjust its receiver type. - newm := *tm - newmtyp := *tm.typ.(*Signature) - newm.typ = &newmtyp - newmtyp.recv = NewVar(newm.pos, newm.pkg, "", t) - allMethods = append(allMethods, &newm) - } + allMethods = append(allMethods, t.methods...) + for _, et := range t.embeddeds { + it := et.Underlying().(*Interface) + it.Complete() + for _, tm := range it.allMethods { + // Make a copy of the method and adjust its receiver type. + newm := *tm + newmtyp := *tm.typ.(*Signature) + newm.typ = &newmtyp + newmtyp.recv = NewVar(newm.pos, newm.pkg, "", t) + allMethods = append(allMethods, &newm) } - sort.Sort(byUniqueMethodName(allMethods)) + } + sort.Sort(byUniqueMethodName(allMethods)) + + // t.methods and/or t.embeddeds may have been empty + if allMethods == nil { + allMethods = markComplete } t.allMethods = allMethods @@ -437,26 +470,26 @@ func (t *Named) AddMethod(m *Func) { // Implementations for Type methods. -func (t *Basic) Underlying() Type { return t } -func (t *Array) Underlying() Type { return t } -func (t *Slice) Underlying() Type { return t } -func (t *Struct) Underlying() Type { return t } -func (t *Pointer) Underlying() Type { return t } +func (b *Basic) Underlying() Type { return b } +func (a *Array) Underlying() Type { return a } +func (s *Slice) Underlying() Type { return s } +func (s *Struct) Underlying() Type { return s } +func (p *Pointer) Underlying() Type { return p } func (t *Tuple) Underlying() Type { return t } -func (t *Signature) Underlying() Type { return t } +func (s *Signature) Underlying() Type { return s } func (t *Interface) Underlying() Type { return t } -func (t *Map) Underlying() Type { return t } -func (t *Chan) Underlying() Type { return t } +func (m *Map) Underlying() Type { return m } +func (c *Chan) Underlying() Type { return c } func (t *Named) Underlying() Type { return t.underlying } -func (t *Basic) String() string { return TypeString(t, nil) } -func (t *Array) String() string { return TypeString(t, nil) } -func (t *Slice) String() string { return TypeString(t, nil) } -func (t *Struct) String() string { return TypeString(t, nil) } -func (t *Pointer) String() string { return TypeString(t, nil) } +func (b *Basic) String() string { return TypeString(b, nil) } +func (a *Array) String() string { return TypeString(a, nil) } +func (s *Slice) String() string { return TypeString(s, nil) } +func (s *Struct) String() string { return TypeString(s, nil) } +func (p *Pointer) String() string { return TypeString(p, nil) } func (t *Tuple) String() string { return TypeString(t, nil) } -func (t *Signature) String() string { return TypeString(t, nil) } +func (s *Signature) String() string { return TypeString(s, nil) } func (t *Interface) String() string { return TypeString(t, nil) } -func (t *Map) String() string { return TypeString(t, nil) } -func (t *Chan) String() string { return TypeString(t, nil) } +func (m *Map) String() string { return TypeString(m, nil) } +func (c *Chan) String() string { return TypeString(c, nil) } func (t *Named) String() string { return TypeString(t, nil) } diff --git a/libgo/go/go/types/typestring.go b/libgo/go/go/types/typestring.go index a9c0bfd..0c007f6 100644 --- a/libgo/go/go/types/typestring.go +++ b/libgo/go/go/types/typestring.go @@ -121,7 +121,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { if i > 0 { buf.WriteString("; ") } - if !f.anonymous { + if !f.embedded { buf.WriteString(f.name) buf.WriteByte(' ') } @@ -146,7 +146,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { case *Interface: // We write the source-level methods and embedded types rather // than the actual method set since resolved method signatures - // may have non-printable cycles if parameters have anonymous + // may have non-printable cycles if parameters have embedded // interface types that (directly or indirectly) embed the // current interface. For instance, consider the result type // of m: diff --git a/libgo/go/go/types/typestring_test.go b/libgo/go/go/types/typestring_test.go index 640ffb1..3dd89d6 100644 --- a/libgo/go/go/types/typestring_test.go +++ b/libgo/go/go/types/typestring_test.go @@ -140,16 +140,41 @@ func TestTypeString(t *testing.T) { func TestIncompleteInterfaces(t *testing.T) { sig := NewSignature(nil, nil, nil, false) + m := NewFunc(token.NoPos, nil, "m", sig) for _, test := range []struct { typ *Interface want string }{ {new(Interface), "interface{/* incomplete */}"}, {new(Interface).Complete(), "interface{}"}, + {NewInterface(nil, nil), "interface{/* incomplete */}"}, {NewInterface(nil, nil).Complete(), "interface{}"}, - {NewInterface([]*Func{NewFunc(token.NoPos, nil, "m", sig)}, nil), "interface{m() /* incomplete */}"}, - {NewInterface([]*Func{NewFunc(token.NoPos, nil, "m", sig)}, nil).Complete(), "interface{m()}"}, + {NewInterface([]*Func{}, nil), "interface{/* incomplete */}"}, + {NewInterface([]*Func{}, nil).Complete(), "interface{}"}, + {NewInterface(nil, []*Named{}), "interface{/* incomplete */}"}, + {NewInterface(nil, []*Named{}).Complete(), "interface{}"}, + {NewInterface([]*Func{m}, nil), "interface{m() /* incomplete */}"}, + {NewInterface([]*Func{m}, nil).Complete(), "interface{m()}"}, + {NewInterface(nil, []*Named{newDefined(new(Interface).Complete())}), "interface{T /* incomplete */}"}, + {NewInterface(nil, []*Named{newDefined(new(Interface).Complete())}).Complete(), "interface{T}"}, + {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil))}), "interface{T /* incomplete */}"}, + {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil).Complete())}), "interface{T /* incomplete */}"}, + {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil).Complete())}).Complete(), "interface{T}"}, + + {NewInterfaceType(nil, nil), "interface{/* incomplete */}"}, + {NewInterfaceType(nil, nil).Complete(), "interface{}"}, + {NewInterfaceType([]*Func{}, nil), "interface{/* incomplete */}"}, + {NewInterfaceType([]*Func{}, nil).Complete(), "interface{}"}, + {NewInterfaceType(nil, []Type{}), "interface{/* incomplete */}"}, + {NewInterfaceType(nil, []Type{}).Complete(), "interface{}"}, + {NewInterfaceType([]*Func{m}, nil), "interface{m() /* incomplete */}"}, + {NewInterfaceType([]*Func{m}, nil).Complete(), "interface{m()}"}, + {NewInterfaceType(nil, []Type{new(Interface).Complete()}), "interface{interface{} /* incomplete */}"}, + {NewInterfaceType(nil, []Type{new(Interface).Complete()}).Complete(), "interface{interface{}}"}, + {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil)}), "interface{interface{m() /* incomplete */} /* incomplete */}"}, + {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil).Complete()}), "interface{interface{m()} /* incomplete */}"}, + {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil).Complete()}).Complete(), "interface{interface{m()}}"}, } { got := test.typ.String() if got != test.want { @@ -158,6 +183,13 @@ func TestIncompleteInterfaces(t *testing.T) { } } +// newDefined creates a new defined type named T with the given underlying type. +// Helper function for use with TestIncompleteInterfaces only. +func newDefined(underlying Type) *Named { + tname := NewTypeName(token.NoPos, nil, "T", nil) + return NewNamed(tname, underlying, nil) +} + func TestQualifiedTypeString(t *testing.T) { t.Skip("skipping for gccgo--no importer") p, _ := pkgFor("p.go", "package p; type T int", nil) diff --git a/libgo/go/go/types/typexpr.go b/libgo/go/go/types/typexpr.go index 92ab06b..45ada58 100644 --- a/libgo/go/go/types/typexpr.go +++ b/libgo/go/go/types/typexpr.go @@ -22,6 +22,8 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeNa x.mode = invalid x.expr = e + // Note that we cannot use check.lookup here because the returned scope + // may be different from obj.Parent(). See also Scope.LookupParent doc. scope, obj := check.scope.LookupParent(e.Name, check.pos) if obj == nil { if e.Name == "_" { @@ -69,21 +71,17 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeNa case *TypeName: x.mode = typexpr - // check for cycle - // (it's ok to iterate forward because each named type appears at most once in path) - for i, prev := range path { - if prev == obj { - check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name) - // print cycle - for _, obj := range path[i:] { - check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented - } - check.errorf(obj.Pos(), "\t%s", obj.Name()) - // maintain x.mode == typexpr despite error - typ = Typ[Invalid] + // package-level alias cycles are now checked by Checker.objDecl + if useCycleMarking { + if check.objMap[obj] != nil { break } } + if check.cycle(obj, path, true) { + // maintain x.mode == typexpr despite error + typ = Typ[Invalid] + break + } case *Var: // It's ok to mark non-local variables, but ignore variables @@ -116,11 +114,35 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeNa x.typ = typ } +// cycle reports whether obj appears in path or not. +// If it does, and report is set, it also reports a cycle error. +func (check *Checker) cycle(obj *TypeName, path []*TypeName, report bool) bool { + // (it's ok to iterate forward because each named type appears at most once in path) + for i, prev := range path { + if prev == obj { + if report { + check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name) + // print cycle + for _, obj := range path[i:] { + check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented + } + check.errorf(obj.Pos(), "\t%s", obj.Name()) + } + return true + } + } + return false +} + // typExpr type-checks the type expression e and returns its type, or Typ[Invalid]. // If def != nil, e is the type specification for the named type def, declared // in a type declaration, and def.underlying will be set to the type of e before // any components of e are type-checked. Path contains the path of named types -// referring to this type. +// referring to this type; i.e. it is the path of named types directly containing +// each other and leading to the current type e. Indirect containment (e.g. via +// pointer indirection, function parameter, etc.) breaks the path (leads to a new +// path, and usually via calling Checker.typ below) and those types are not found +// in the path. // func (check *Checker) typExpr(e ast.Expr, def *Named, path []*TypeName) (T Type) { if trace { @@ -139,7 +161,18 @@ func (check *Checker) typExpr(e ast.Expr, def *Named, path []*TypeName) (T Type) return } +// typ is like typExpr (with a nil argument for the def parameter), +// but typ breaks type cycles. It should be called for components of +// types that break cycles, such as pointer base types, slice or map +// element types, etc. See the comment in typExpr for details. +// func (check *Checker) typ(e ast.Expr) Type { + // typExpr is called with a nil path indicating an indirection: + // push indir sentinel on object path + if useCycleMarking { + check.push(indir) + defer check.pop() + } return check.typExpr(e, nil, nil) } @@ -305,7 +338,7 @@ func (check *Checker) typExprInternal(e ast.Expr, def *Named, path []*TypeName) // // Delay this check because it requires fully setup types; // it is safe to continue in any case (was issue 6667). - check.delay(func() { + check.later(func() { if !Comparable(typ.key) { check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key) } @@ -368,6 +401,9 @@ func (check *Checker) typOrNil(e ast.Expr) Type { return Typ[Invalid] } +// arrayLength type-checks the array length expression e +// and returns the constant length >= 0, or a value < 0 +// to indicate an error (and thus an unknown length). func (check *Checker) arrayLength(e ast.Expr) int64 { var x operand check.expr(&x, e) @@ -375,7 +411,7 @@ func (check *Checker) arrayLength(e ast.Expr) int64 { if x.mode != invalid { check.errorf(x.pos(), "array length %s must be constant", &x) } - return 0 + return -1 } if isUntyped(x.typ) || isInteger(x.typ) { if val := constant.ToInt(x.val); val.Kind() == constant.Int { @@ -384,12 +420,12 @@ func (check *Checker) arrayLength(e ast.Expr) int64 { return n } check.errorf(x.pos(), "invalid array length %s", &x) - return 0 + return -1 } } } check.errorf(x.pos(), "array length %s must be integer", &x) - return 0 + return -1 } func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) { @@ -456,44 +492,92 @@ func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool return true } -func (check *Checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, def *Named, path []*TypeName) { - // empty interface: common case - if ityp.Methods == nil { +func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, def *Named, path []*TypeName) { + // fast-track empty interface + if iface.Methods.List == nil { + ityp.allMethods = markComplete return } - // The parser ensures that field tags are nil and we don't - // care if a constructed AST contains non-nil tags. + // collect embedded interfaces + // Only needed for printing and API. Delay collection + // to end of type-checking (for package-global interfaces) + // when all types are complete. Local interfaces are handled + // after each statement (as each statement processes delayed + // functions). + interfaceContext := check.context // capture for use in closure below + check.later(func() { + if trace { + check.trace(iface.Pos(), "-- delayed checking embedded interfaces of %v", iface) + check.indent++ + defer func() { + check.indent-- + }() + } + + // The context must be restored since for local interfaces + // delayed functions are processed after each statement + // (was issue #24140). + defer func(ctxt context) { + check.context = ctxt + }(check.context) + check.context = interfaceContext + + for _, f := range iface.Methods.List { + if len(f.Names) == 0 { + typ := check.typ(f.Type) + // typ should be a named type denoting an interface + // (the parser will make sure it's a named type but + // constructed ASTs may be wrong). + if typ == Typ[Invalid] { + continue // error reported before + } + embed, _ := typ.Underlying().(*Interface) + if embed == nil { + check.errorf(f.Type.Pos(), "%s is not an interface", typ) + continue + } + // Correct embedded interfaces must be complete - + // don't just assert, but report error since this + // used to be the underlying cause for issue #18395. + if embed.allMethods == nil { + check.dump("%v: incomplete embedded interface %s", f.Type.Pos(), typ) + unreachable() + } + // collect interface + ityp.embeddeds = append(ityp.embeddeds, typ) + } + } + // sort to match NewInterface/NewInterface2 + // TODO(gri) we may be able to switch to source order + sort.Stable(byUniqueTypeName(ityp.embeddeds)) + }) + + // compute method set + var tname *TypeName + if def != nil { + tname = def.obj + } + info := check.infoFromTypeLit(check.scope, iface, tname, path) + if info == nil || info == &emptyIfaceInfo { + // error or empty interface - exit early + ityp.allMethods = markComplete + return + } // use named receiver type if available (for better error messages) - var recvTyp Type = iface + var recvTyp Type = ityp if def != nil { recvTyp = def } - // Phase 1: Collect explicitly declared methods, the corresponding - // signature (AST) expressions, and the list of embedded - // type (AST) expressions. Do not resolve signatures or - // embedded types yet to avoid cycles referring to this - // interface. - - var ( - mset objset - signatures []ast.Expr // list of corresponding method signatures - embedded []ast.Expr // list of embedded types - ) - for _, f := range ityp.Methods.List { - if len(f.Names) > 0 { - // The parser ensures that there's only one method - // and we don't care if a constructed AST has more. - name := f.Names[0] + // collect methods + var sigfix []*methodInfo + for i, minfo := range info.methods { + fun := minfo.fun + if fun == nil { + name := minfo.src.Names[0] pos := name.Pos() - // spec: "As with all method sets, in an interface type, - // each method must have a unique non-blank name." - if name.Name == "_" { - check.errorf(pos, "invalid method name _") - continue - } // Don't type-check signature yet - use an // empty signature now and update it later. // Since we know the receiver, set it up now @@ -504,101 +588,64 @@ func (check *Checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, d // also the T4 and T5 tests in testdata/cycles2.src. sig := new(Signature) sig.recv = NewVar(pos, check.pkg, "", recvTyp) - m := NewFunc(pos, check.pkg, name.Name, sig) - if check.declareInSet(&mset, pos, m) { - iface.methods = append(iface.methods, m) - iface.allMethods = append(iface.allMethods, m) - signatures = append(signatures, f.Type) - check.recordDef(name, m) - } - } else { - // embedded type - embedded = append(embedded, f.Type) - } - } - - // Phase 2: Resolve embedded interfaces. Because an interface must not - // embed itself (directly or indirectly), each embedded interface - // can be fully resolved without depending on any method of this - // interface (if there is a cycle or another error, the embedded - // type resolves to an invalid type and is ignored). - // In particular, the list of methods for each embedded interface - // must be complete (it cannot depend on this interface), and so - // those methods can be added to the list of all methods of this - // interface. - - for _, e := range embedded { - pos := e.Pos() - typ := check.typExpr(e, nil, path) - // Determine underlying embedded (possibly incomplete) type - // by following its forward chain. - named, _ := typ.(*Named) - under := underlying(named) - embed, _ := under.(*Interface) - if embed == nil { - if typ != Typ[Invalid] { - check.errorf(pos, "%s is not an interface", typ) - } - continue + fun = NewFunc(pos, check.pkg, name.Name, sig) + minfo.fun = fun + check.recordDef(name, fun) + sigfix = append(sigfix, minfo) } - iface.embeddeds = append(iface.embeddeds, named) - // collect embedded methods - if embed.allMethods == nil { - check.errorf(pos, "internal error: incomplete embedded interface %s (issue #18395)", named) - } - for _, m := range embed.allMethods { - if check.declareInSet(&mset, pos, m) { - iface.allMethods = append(iface.allMethods, m) - } + // fun != nil + if i < info.explicits { + ityp.methods = append(ityp.methods, fun) } + ityp.allMethods = append(ityp.allMethods, fun) } - // Phase 3: At this point all methods have been collected for this interface. - // It is now safe to type-check the signatures of all explicitly - // declared methods, even if they refer to this interface via a cycle - // and embed the methods of this interface in a parameter of interface - // type. - - for i, m := range iface.methods { - expr := signatures[i] - typ := check.typ(expr) + // fix signatures now that we have collected all methods + savedContext := check.context + for _, minfo := range sigfix { + // (possibly embedded) methods must be type-checked within their scope and + // type-checking them must not affect the current context (was issue #23914) + check.context = context{scope: minfo.scope} + typ := check.typ(minfo.src.Type) sig, _ := typ.(*Signature) if sig == nil { if typ != Typ[Invalid] { - check.invalidAST(expr.Pos(), "%s is not a method signature", typ) + check.invalidAST(minfo.src.Type.Pos(), "%s is not a method signature", typ) } continue // keep method with empty method signature } // update signature, but keep recv that was set up before - old := m.typ.(*Signature) + old := minfo.fun.typ.(*Signature) sig.recv = old.recv - *old = *sig // update signature (don't replace it!) + *old = *sig // update signature (don't replace pointer!) } + check.context = savedContext - // TODO(gri) The list of explicit methods is only sorted for now to - // produce the same Interface as NewInterface. We may be able to - // claim source order in the future. Revisit. - sort.Sort(byUniqueMethodName(iface.methods)) - - // TODO(gri) The list of embedded types is only sorted for now to - // produce the same Interface as NewInterface. We may be able to - // claim source order in the future. Revisit. - sort.Sort(byUniqueTypeName(iface.embeddeds)) + // sort to match NewInterface/NewInterface2 + // TODO(gri) we may be able to switch to source order + sort.Sort(byUniqueMethodName(ityp.methods)) - if iface.allMethods == nil { - iface.allMethods = make([]*Func, 0) // mark interface as complete + if ityp.allMethods == nil { + ityp.allMethods = markComplete } else { - sort.Sort(byUniqueMethodName(iface.allMethods)) + sort.Sort(byUniqueMethodName(ityp.allMethods)) } } // byUniqueTypeName named type lists can be sorted by their unique type names. -type byUniqueTypeName []*Named +type byUniqueTypeName []Type func (a byUniqueTypeName) Len() int { return len(a) } -func (a byUniqueTypeName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() } +func (a byUniqueTypeName) Less(i, j int) bool { return sortName(a[i]) < sortName(a[j]) } func (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func sortName(t Type) string { + if named, _ := t.(*Named); named != nil { + return named.obj.Id() + } + return "" +} + // byUniqueMethodName method lists can be sorted by their unique method names. type byUniqueMethodName []*Func @@ -634,7 +681,7 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa // current field typ and tag var typ Type var tag string - add := func(ident *ast.Ident, anonymous bool, pos token.Pos) { + add := func(ident *ast.Ident, embedded bool, pos token.Pos) { if tag != "" && tags == nil { tags = make([]string, len(fields)) } @@ -643,7 +690,7 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa } name := ident.Name - fld := NewField(pos, check.pkg, name, typ, anonymous) + fld := NewField(pos, check.pkg, name, typ, embedded) // spec: "Within a struct, non-blank field names must be unique." if name == "_" || check.declareInSet(&fset, pos, fld) { fields = append(fields, fld) @@ -651,6 +698,16 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa } } + // addInvalid adds an embedded field of invalid type to the struct for + // fields with errors; this keeps the number of struct fields in sync + // with the source as long as the fields are _ or have different names + // (issue #25627). + addInvalid := func(ident *ast.Ident, pos token.Pos) { + typ = Typ[Invalid] + tag = "" + add(ident, true, pos) + } + for _, f := range list.List { typ = check.typExpr(f.Type, nil, path) tag = check.tag(f.Tag) @@ -660,13 +717,16 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa add(name, false, name.Pos()) } } else { - // anonymous field + // embedded field // spec: "An embedded type must be specified as a type name T or as a pointer // to a non-interface type name *T, and T itself may not be a pointer type." pos := f.Type.Pos() - name := anonymousFieldIdent(f.Type) + name := embeddedFieldIdent(f.Type) if name == nil { - check.invalidAST(pos, "anonymous field type %s has no name", f.Type) + check.invalidAST(pos, "embedded field type %s has no name", f.Type) + name = ast.NewIdent("_") + name.NamePos = pos + addInvalid(name, pos) continue } t, isPtr := deref(typ) @@ -676,22 +736,26 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa case *Basic: if t == Typ[Invalid] { // error was reported before + addInvalid(name, pos) continue } // unsafe.Pointer is treated like a regular pointer if t.kind == UnsafePointer { - check.errorf(pos, "anonymous field type cannot be unsafe.Pointer") + check.errorf(pos, "embedded field type cannot be unsafe.Pointer") + addInvalid(name, pos) continue } case *Pointer: - check.errorf(pos, "anonymous field type cannot be a pointer") + check.errorf(pos, "embedded field type cannot be a pointer") + addInvalid(name, pos) continue case *Interface: if isPtr { - check.errorf(pos, "anonymous field type cannot be a pointer to an interface") + check.errorf(pos, "embedded field type cannot be a pointer to an interface") + addInvalid(name, pos) continue } } @@ -703,17 +767,17 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa styp.tags = tags } -func anonymousFieldIdent(e ast.Expr) *ast.Ident { +func embeddedFieldIdent(e ast.Expr) *ast.Ident { switch e := e.(type) { case *ast.Ident: return e case *ast.StarExpr: // *T is valid, but **T is not if _, ok := e.X.(*ast.StarExpr); !ok { - return anonymousFieldIdent(e.X) + return embeddedFieldIdent(e.X) } case *ast.SelectorExpr: return e.Sel } - return nil // invalid anonymous field + return nil // invalid embedded field } diff --git a/libgo/go/go/types/universe.go b/libgo/go/go/types/universe.go index 07d7078..7af6dab 100644 --- a/libgo/go/go/types/universe.go +++ b/libgo/go/go/types/universe.go @@ -12,9 +12,15 @@ import ( "strings" ) +// The Universe scope contains all predeclared objects of Go. +// It is the outermost scope of any chain of nested scopes. +var Universe *Scope + +// The Unsafe package is the package returned by an importer +// for the import path "unsafe". +var Unsafe *Package + var ( - Universe *Scope - Unsafe *Package universeIota *Const universeByte *Basic // uint8 alias, but has name "byte" universeRune *Basic // int32 alias, but has name "rune" @@ -74,7 +80,7 @@ func defPredeclaredTypes() { res := NewVar(token.NoPos, nil, "", Typ[String]) sig := &Signature{results: NewTuple(res)} err := NewFunc(token.NoPos, nil, "Error", sig) - typ := &Named{underlying: NewInterface([]*Func{err}, nil).Complete()} + typ := &Named{underlying: NewInterfaceType([]*Func{err}, nil).Complete()} sig.recv = NewVar(token.NoPos, nil, "", typ) def(NewTypeName(token.NoPos, nil, "error", typ)) } @@ -96,7 +102,7 @@ func defPredeclaredConsts() { } func defPredeclaredNil() { - def(&Nil{object{name: "nil", typ: Typ[UntypedNil]}}) + def(&Nil{object{name: "nil", typ: Typ[UntypedNil], color_: black}}) } // A builtinId is the id of a builtin function. @@ -201,6 +207,7 @@ func init() { // scope; other objects are inserted in the universe scope. // func def(obj Object) { + assert(obj.color() == black) name := obj.Name() if strings.Contains(name, " ") { return // nothing to do diff --git a/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305.go b/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305.go index 3f0dcb9..e28f49d 100644 --- a/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305.go +++ b/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305.go @@ -7,6 +7,7 @@ package chacha20poly1305 // import "golang.org/x/crypto/chacha20poly1305" import ( "crypto/cipher" + "encoding/binary" "errors" ) @@ -18,7 +19,7 @@ const ( ) type chacha20poly1305 struct { - key [32]byte + key [8]uint32 } // New returns a ChaCha20-Poly1305 AEAD that uses the given, 256-bit key. @@ -27,7 +28,14 @@ func New(key []byte) (cipher.AEAD, error) { return nil, errors.New("chacha20poly1305: bad key length") } ret := new(chacha20poly1305) - copy(ret.key[:], key) + ret.key[0] = binary.LittleEndian.Uint32(key[0:4]) + ret.key[1] = binary.LittleEndian.Uint32(key[4:8]) + ret.key[2] = binary.LittleEndian.Uint32(key[8:12]) + ret.key[3] = binary.LittleEndian.Uint32(key[12:16]) + ret.key[4] = binary.LittleEndian.Uint32(key[16:20]) + ret.key[5] = binary.LittleEndian.Uint32(key[20:24]) + ret.key[6] = binary.LittleEndian.Uint32(key[24:28]) + ret.key[7] = binary.LittleEndian.Uint32(key[28:32]) return ret, nil } diff --git a/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go b/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go index 1e523b9..4e7b6ea 100644 --- a/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go +++ b/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go @@ -6,7 +6,11 @@ package chacha20poly1305 -import "encoding/binary" +import ( + "encoding/binary" + + "internal/cpu" +) //go:noescape func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool @@ -14,78 +18,27 @@ func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool //go:noescape func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte) -// cpuid is implemented in chacha20poly1305_amd64.s. -func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) - -// xgetbv with ecx = 0 is implemented in chacha20poly1305_amd64.s. -func xgetbv() (eax, edx uint32) - var ( - useASM bool - useAVX2 bool + useASM = cpu.X86.HasSSSE3 + useAVX2 = cpu.X86.HasAVX2 && cpu.X86.HasBMI2 ) -func init() { - detectCpuFeatures() -} - -// detectCpuFeatures is used to detect if cpu instructions -// used by the functions implemented in assembler in -// chacha20poly1305_amd64.s are supported. -func detectCpuFeatures() { - maxId, _, _, _ := cpuid(0, 0) - if maxId < 1 { - return - } - - _, _, ecx1, _ := cpuid(1, 0) - - haveSSSE3 := isSet(9, ecx1) - useASM = haveSSSE3 - - haveOSXSAVE := isSet(27, ecx1) - - osSupportsAVX := false - // For XGETBV, OSXSAVE bit is required and sufficient. - if haveOSXSAVE { - eax, _ := xgetbv() - // Check if XMM and YMM registers have OS support. - osSupportsAVX = isSet(1, eax) && isSet(2, eax) - } - haveAVX := isSet(28, ecx1) && osSupportsAVX - - if maxId < 7 { - return - } - - _, ebx7, _, _ := cpuid(7, 0) - haveAVX2 := isSet(5, ebx7) && haveAVX - haveBMI2 := isSet(8, ebx7) - - useAVX2 = haveAVX2 && haveBMI2 -} - -// isSet checks if bit at bitpos is set in value. -func isSet(bitpos uint, value uint32) bool { - return value&(1<<bitpos) != 0 -} - // setupState writes a ChaCha20 input matrix to state. See // https://tools.ietf.org/html/rfc7539#section-2.3. -func setupState(state *[16]uint32, key *[32]byte, nonce []byte) { +func setupState(state *[16]uint32, key *[8]uint32, nonce []byte) { state[0] = 0x61707865 state[1] = 0x3320646e state[2] = 0x79622d32 state[3] = 0x6b206574 - state[4] = binary.LittleEndian.Uint32(key[:4]) - state[5] = binary.LittleEndian.Uint32(key[4:8]) - state[6] = binary.LittleEndian.Uint32(key[8:12]) - state[7] = binary.LittleEndian.Uint32(key[12:16]) - state[8] = binary.LittleEndian.Uint32(key[16:20]) - state[9] = binary.LittleEndian.Uint32(key[20:24]) - state[10] = binary.LittleEndian.Uint32(key[24:28]) - state[11] = binary.LittleEndian.Uint32(key[28:32]) + state[4] = key[0] + state[5] = key[1] + state[6] = key[2] + state[7] = key[3] + state[8] = key[4] + state[9] = key[5] + state[10] = key[6] + state[11] = key[7] state[12] = 0 state[13] = binary.LittleEndian.Uint32(nonce[:4]) diff --git a/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go b/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go index b9a55ea..56e4f0e 100644 --- a/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go +++ b/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go @@ -7,7 +7,7 @@ package chacha20poly1305 import ( "encoding/binary" - "golang_org/x/crypto/chacha20poly1305/internal/chacha20" + "golang_org/x/crypto/internal/chacha20" "golang_org/x/crypto/poly1305" ) @@ -16,15 +16,17 @@ func roundTo16(n int) int { } func (c *chacha20poly1305) sealGeneric(dst, nonce, plaintext, additionalData []byte) []byte { - var counter [16]byte - copy(counter[4:], nonce) + ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize) var polyKey [32]byte - chacha20.XORKeyStream(polyKey[:], polyKey[:], &counter, &c.key) - - ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize) - counter[0] = 1 - chacha20.XORKeyStream(out, plaintext, &counter, &c.key) + s := chacha20.New(c.key, [3]uint32{ + binary.LittleEndian.Uint32(nonce[0:4]), + binary.LittleEndian.Uint32(nonce[4:8]), + binary.LittleEndian.Uint32(nonce[8:12]), + }) + s.XORKeyStream(polyKey[:], polyKey[:]) + s.Advance() // skip the next 32 bytes + s.XORKeyStream(out, plaintext) polyInput := make([]byte, roundTo16(len(additionalData))+roundTo16(len(plaintext))+8+8) copy(polyInput, additionalData) @@ -44,11 +46,14 @@ func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData [] copy(tag[:], ciphertext[len(ciphertext)-16:]) ciphertext = ciphertext[:len(ciphertext)-16] - var counter [16]byte - copy(counter[4:], nonce) - var polyKey [32]byte - chacha20.XORKeyStream(polyKey[:], polyKey[:], &counter, &c.key) + s := chacha20.New(c.key, [3]uint32{ + binary.LittleEndian.Uint32(nonce[0:4]), + binary.LittleEndian.Uint32(nonce[4:8]), + binary.LittleEndian.Uint32(nonce[8:12]), + }) + s.XORKeyStream(polyKey[:], polyKey[:]) + s.Advance() // skip the next 32 bytes polyInput := make([]byte, roundTo16(len(additionalData))+roundTo16(len(ciphertext))+8+8) copy(polyInput, additionalData) @@ -64,7 +69,6 @@ func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData [] return nil, errOpen } - counter[0] = 1 - chacha20.XORKeyStream(out, ciphertext, &counter, &c.key) + s.XORKeyStream(out, ciphertext) return ret, nil } diff --git a/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_vectors_test.go b/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_vectors_test.go index 49f0da6b7..64f6a72 100644 --- a/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_vectors_test.go +++ b/libgo/go/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_vectors_test.go @@ -8,6 +8,13 @@ var chacha20Poly1305Tests = []struct { plaintext, aad, key, nonce, out string }{ { + "", + "", + "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "070000004041424344454647", + "a0784d7a4716f3feb4f64e7f4b39bf04", + }, + { "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e", "50515253c0c1c2c3c4c5c6c7", "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", diff --git a/libgo/go/golang_org/x/crypto/cryptobyte/asn1.go b/libgo/go/golang_org/x/crypto/cryptobyte/asn1.go index 225a49f..08314b4 100644 --- a/libgo/go/golang_org/x/crypto/cryptobyte/asn1.go +++ b/libgo/go/golang_org/x/crypto/cryptobyte/asn1.go @@ -23,6 +23,12 @@ func (b *Builder) AddASN1Int64(v int64) { b.addASN1Signed(asn1.INTEGER, v) } +// AddASN1Int64WithTag appends a DER-encoded ASN.1 INTEGER with the +// given tag. +func (b *Builder) AddASN1Int64WithTag(v int64, tag asn1.Tag) { + b.addASN1Signed(tag, v) +} + // AddASN1Enum appends a DER-encoded ASN.1 ENUMERATION. func (b *Builder) AddASN1Enum(v int64) { b.addASN1Signed(asn1.ENUM, v) @@ -224,6 +230,9 @@ func (b *Builder) AddASN1(tag asn1.Tag, f BuilderContinuation) { // String +// ReadASN1Boolean decodes an ASN.1 INTEGER and converts it to a boolean +// representation into out and advances. It reports whether the read +// was successful. func (s *String) ReadASN1Boolean(out *bool) bool { var bytes String if !s.ReadASN1(&bytes, asn1.INTEGER) || len(bytes) != 1 { @@ -245,8 +254,8 @@ func (s *String) ReadASN1Boolean(out *bool) bool { var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem() // ReadASN1Integer decodes an ASN.1 INTEGER into out and advances. If out does -// not point to an integer or to a big.Int, it panics. It returns true on -// success and false on error. +// not point to an integer or to a big.Int, it panics. It reports whether the +// read was successful. func (s *String) ReadASN1Integer(out interface{}) bool { if reflect.TypeOf(out).Kind() != reflect.Ptr { panic("out is not a pointer") @@ -359,8 +368,16 @@ func asn1Unsigned(out *uint64, n []byte) bool { return true } -// ReadASN1Enum decodes an ASN.1 ENUMERATION into out and advances. It returns -// true on success and false on error. +// ReadASN1Int64WithTag decodes an ASN.1 INTEGER with the given tag into out +// and advances. It reports whether the read was successful and resulted in a +// value that can be represented in an int64. +func (s *String) ReadASN1Int64WithTag(out *int64, tag asn1.Tag) bool { + var bytes String + return s.ReadASN1(&bytes, tag) && checkASN1Integer(bytes) && asn1Signed(out, bytes) +} + +// ReadASN1Enum decodes an ASN.1 ENUMERATION into out and advances. It reports +// whether the read was successful. func (s *String) ReadASN1Enum(out *int) bool { var bytes String var i int64 @@ -392,7 +409,7 @@ func (s *String) readBase128Int(out *int) bool { } // ReadASN1ObjectIdentifier decodes an ASN.1 OBJECT IDENTIFIER into out and -// advances. It returns true on success and false on error. +// advances. It reports whether the read was successful. func (s *String) ReadASN1ObjectIdentifier(out *encoding_asn1.ObjectIdentifier) bool { var bytes String if !s.ReadASN1(&bytes, asn1.OBJECT_IDENTIFIER) || len(bytes) == 0 { @@ -431,7 +448,7 @@ func (s *String) ReadASN1ObjectIdentifier(out *encoding_asn1.ObjectIdentifier) b } // ReadASN1GeneralizedTime decodes an ASN.1 GENERALIZEDTIME into out and -// advances. It returns true on success and false on error. +// advances. It reports whether the read was successful. func (s *String) ReadASN1GeneralizedTime(out *time.Time) bool { var bytes String if !s.ReadASN1(&bytes, asn1.GeneralizedTime) { @@ -449,8 +466,8 @@ func (s *String) ReadASN1GeneralizedTime(out *time.Time) bool { return true } -// ReadASN1BitString decodes an ASN.1 BIT STRING into out and advances. It -// returns true on success and false on error. +// ReadASN1BitString decodes an ASN.1 BIT STRING into out and advances. +// It reports whether the read was successful. func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool { var bytes String if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 { @@ -471,8 +488,8 @@ func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool { } // ReadASN1BitString decodes an ASN.1 BIT STRING into out and advances. It is -// an error if the BIT STRING is not a whole number of bytes. This function -// returns true on success and false on error. +// an error if the BIT STRING is not a whole number of bytes. It reports +// whether the read was successful. func (s *String) ReadASN1BitStringAsBytes(out *[]byte) bool { var bytes String if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 { @@ -489,14 +506,14 @@ func (s *String) ReadASN1BitStringAsBytes(out *[]byte) bool { // ReadASN1Bytes reads the contents of a DER-encoded ASN.1 element (not including // tag and length bytes) into out, and advances. The element must match the -// given tag. It returns true on success and false on error. +// given tag. It reports whether the read was successful. func (s *String) ReadASN1Bytes(out *[]byte, tag asn1.Tag) bool { return s.ReadASN1((*String)(out), tag) } // ReadASN1 reads the contents of a DER-encoded ASN.1 element (not including // tag and length bytes) into out, and advances. The element must match the -// given tag. It returns true on success and false on error. +// given tag. It reports whether the read was successful. // // Tags greater than 30 are not supported (i.e. low-tag-number format only). func (s *String) ReadASN1(out *String, tag asn1.Tag) bool { @@ -509,7 +526,7 @@ func (s *String) ReadASN1(out *String, tag asn1.Tag) bool { // ReadASN1Element reads the contents of a DER-encoded ASN.1 element (including // tag and length bytes) into out, and advances. The element must match the -// given tag. It returns true on success and false on error. +// given tag. It reports whether the read was successful. // // Tags greater than 30 are not supported (i.e. low-tag-number format only). func (s *String) ReadASN1Element(out *String, tag asn1.Tag) bool { @@ -521,8 +538,8 @@ func (s *String) ReadASN1Element(out *String, tag asn1.Tag) bool { } // ReadAnyASN1 reads the contents of a DER-encoded ASN.1 element (not including -// tag and length bytes) into out, sets outTag to its tag, and advances. It -// returns true on success and false on error. +// tag and length bytes) into out, sets outTag to its tag, and advances. +// It reports whether the read was successful. // // Tags greater than 30 are not supported (i.e. low-tag-number format only). func (s *String) ReadAnyASN1(out *String, outTag *asn1.Tag) bool { @@ -531,14 +548,14 @@ func (s *String) ReadAnyASN1(out *String, outTag *asn1.Tag) bool { // ReadAnyASN1Element reads the contents of a DER-encoded ASN.1 element // (including tag and length bytes) into out, sets outTag to is tag, and -// advances. It returns true on success and false on error. +// advances. It reports whether the read was successful. // // Tags greater than 30 are not supported (i.e. low-tag-number format only). func (s *String) ReadAnyASN1Element(out *String, outTag *asn1.Tag) bool { return s.readASN1(out, outTag, false /* include header */) } -// PeekASN1Tag returns true if the next ASN.1 value on the string starts with +// PeekASN1Tag reports whether the next ASN.1 value on the string starts with // the given tag. func (s String) PeekASN1Tag(tag asn1.Tag) bool { if len(s) == 0 { @@ -547,7 +564,8 @@ func (s String) PeekASN1Tag(tag asn1.Tag) bool { return asn1.Tag(s[0]) == tag } -// SkipASN1 reads and discards an ASN.1 element with the given tag. +// SkipASN1 reads and discards an ASN.1 element with the given tag. It +// reports whether the operation was successful. func (s *String) SkipASN1(tag asn1.Tag) bool { var unused String return s.ReadASN1(&unused, tag) @@ -556,7 +574,7 @@ func (s *String) SkipASN1(tag asn1.Tag) bool { // ReadOptionalASN1 attempts to read the contents of a DER-encoded ASN.1 // element (not including tag and length bytes) tagged with the given tag into // out. It stores whether an element with the tag was found in outPresent, -// unless outPresent is nil. It returns true on success and false on error. +// unless outPresent is nil. It reports whether the read was successful. func (s *String) ReadOptionalASN1(out *String, outPresent *bool, tag asn1.Tag) bool { present := s.PeekASN1Tag(tag) if outPresent != nil { @@ -569,7 +587,7 @@ func (s *String) ReadOptionalASN1(out *String, outPresent *bool, tag asn1.Tag) b } // SkipOptionalASN1 advances s over an ASN.1 element with the given tag, or -// else leaves s unchanged. +// else leaves s unchanged. It reports whether the operation was successful. func (s *String) SkipOptionalASN1(tag asn1.Tag) bool { if !s.PeekASN1Tag(tag) { return true @@ -581,8 +599,8 @@ func (s *String) SkipOptionalASN1(tag asn1.Tag) bool { // ReadOptionalASN1Integer attempts to read an optional ASN.1 INTEGER // explicitly tagged with tag into out and advances. If no element with a // matching tag is present, it writes defaultValue into out instead. If out -// does not point to an integer or to a big.Int, it panics. It returns true on -// success and false on error. +// does not point to an integer or to a big.Int, it panics. It reports +// whether the read was successful. func (s *String) ReadOptionalASN1Integer(out interface{}, tag asn1.Tag, defaultValue interface{}) bool { if reflect.TypeOf(out).Kind() != reflect.Ptr { panic("out is not a pointer") @@ -619,8 +637,8 @@ func (s *String) ReadOptionalASN1Integer(out interface{}, tag asn1.Tag, defaultV // ReadOptionalASN1OctetString attempts to read an optional ASN.1 OCTET STRING // explicitly tagged with tag into out and advances. If no element with a -// matching tag is present, it writes defaultValue into out instead. It returns -// true on success and false on error. +// matching tag is present, it sets "out" to nil instead. It reports +// whether the read was successful. func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag asn1.Tag) bool { var present bool var child String @@ -644,6 +662,7 @@ func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag // ReadOptionalASN1Boolean sets *out to the value of the next ASN.1 BOOLEAN or, // if the next bytes are not an ASN.1 BOOLEAN, to the value of defaultValue. +// It reports whether the operation was successful. func (s *String) ReadOptionalASN1Boolean(out *bool, defaultValue bool) bool { var present bool var child String diff --git a/libgo/go/golang_org/x/crypto/cryptobyte/asn1_test.go b/libgo/go/golang_org/x/crypto/cryptobyte/asn1_test.go index 59e562d..f776288 100644 --- a/libgo/go/golang_org/x/crypto/cryptobyte/asn1_test.go +++ b/libgo/go/golang_org/x/crypto/cryptobyte/asn1_test.go @@ -149,6 +149,39 @@ func TestReadASN1IntegerSigned(t *testing.T) { } } }) + + // Repeat with the implicit-tagging functions + t.Run("WithTag", func(t *testing.T) { + for i, test := range testData64 { + tag := asn1.Tag((i * 3) % 32).ContextSpecific() + + testData := make([]byte, len(test.in)) + copy(testData, test.in) + + // Alter the tag of the test case. + testData[0] = uint8(tag) + + in := String(testData) + var out int64 + ok := in.ReadASN1Int64WithTag(&out, tag) + if !ok || out != test.out { + t.Errorf("#%d: in.ReadASN1Int64WithTag() = %v, want true; out = %d, want %d", i, ok, out, test.out) + } + + var b Builder + b.AddASN1Int64WithTag(test.out, tag) + result, err := b.Bytes() + + if err != nil { + t.Errorf("#%d: AddASN1Int64WithTag failed: %s", i, err) + continue + } + + if !bytes.Equal(result, testData) { + t.Errorf("#%d: AddASN1Int64WithTag: got %x, want %x", i, result, testData) + } + } + }) } func TestReadASN1IntegerUnsigned(t *testing.T) { diff --git a/libgo/go/golang_org/x/crypto/cryptobyte/string.go b/libgo/go/golang_org/x/crypto/cryptobyte/string.go index 7636fb9..39bf98a 100644 --- a/libgo/go/golang_org/x/crypto/cryptobyte/string.go +++ b/libgo/go/golang_org/x/crypto/cryptobyte/string.go @@ -37,8 +37,8 @@ func (s *String) Skip(n int) bool { return s.read(n) != nil } -// ReadUint8 decodes an 8-bit value into out and advances over it. It -// returns true on success and false on error. +// ReadUint8 decodes an 8-bit value into out and advances over it. +// It reports whether the read was successful. func (s *String) ReadUint8(out *uint8) bool { v := s.read(1) if v == nil { @@ -49,7 +49,7 @@ func (s *String) ReadUint8(out *uint8) bool { } // ReadUint16 decodes a big-endian, 16-bit value into out and advances over it. -// It returns true on success and false on error. +// It reports whether the read was successful. func (s *String) ReadUint16(out *uint16) bool { v := s.read(2) if v == nil { @@ -60,7 +60,7 @@ func (s *String) ReadUint16(out *uint16) bool { } // ReadUint24 decodes a big-endian, 24-bit value into out and advances over it. -// It returns true on success and false on error. +// It reports whether the read was successful. func (s *String) ReadUint24(out *uint32) bool { v := s.read(3) if v == nil { @@ -71,7 +71,7 @@ func (s *String) ReadUint24(out *uint32) bool { } // ReadUint32 decodes a big-endian, 32-bit value into out and advances over it. -// It returns true on success and false on error. +// It reports whether the read was successful. func (s *String) ReadUint32(out *uint32) bool { v := s.read(4) if v == nil { @@ -119,28 +119,27 @@ func (s *String) readLengthPrefixed(lenLen int, outChild *String) bool { } // ReadUint8LengthPrefixed reads the content of an 8-bit length-prefixed value -// into out and advances over it. It returns true on success and false on -// error. +// into out and advances over it. It reports whether the read was successful. func (s *String) ReadUint8LengthPrefixed(out *String) bool { return s.readLengthPrefixed(1, out) } // ReadUint16LengthPrefixed reads the content of a big-endian, 16-bit -// length-prefixed value into out and advances over it. It returns true on -// success and false on error. +// length-prefixed value into out and advances over it. It reports whether the +// read was successful. func (s *String) ReadUint16LengthPrefixed(out *String) bool { return s.readLengthPrefixed(2, out) } // ReadUint24LengthPrefixed reads the content of a big-endian, 24-bit -// length-prefixed value into out and advances over it. It returns true on -// success and false on error. +// length-prefixed value into out and advances over it. It reports whether +// the read was successful. func (s *String) ReadUint24LengthPrefixed(out *String) bool { return s.readLengthPrefixed(3, out) } -// ReadBytes reads n bytes into out and advances over them. It returns true on -// success and false and error. +// ReadBytes reads n bytes into out and advances over them. It reports +// whether the read was successful. func (s *String) ReadBytes(out *[]byte, n int) bool { v := s.read(n) if v == nil { @@ -150,8 +149,8 @@ func (s *String) ReadBytes(out *[]byte, n int) bool { return true } -// CopyBytes copies len(out) bytes into out and advances over them. It returns -// true on success and false on error. +// CopyBytes copies len(out) bytes into out and advances over them. It reports +// whether the copy operation was successful func (s *String) CopyBytes(out []byte) bool { n := len(out) v := s.read(n) diff --git a/libgo/go/golang_org/x/crypto/curve25519/curve25519.go b/libgo/go/golang_org/x/crypto/curve25519/curve25519.go index 2d14c2a..cb8fbc5 100644 --- a/libgo/go/golang_org/x/crypto/curve25519/curve25519.go +++ b/libgo/go/golang_org/x/crypto/curve25519/curve25519.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. -// We have a implementation in amd64 assembly so this code is only run on +// We have an implementation in amd64 assembly so this code is only run on // non-amd64 platforms. The amd64 assembly does not support gccgo. // +build !amd64 gccgo appengine diff --git a/libgo/go/golang_org/x/crypto/curve25519/doc.go b/libgo/go/golang_org/x/crypto/curve25519/doc.go index ebeea3c..da9b10d 100644 --- a/libgo/go/golang_org/x/crypto/curve25519/doc.go +++ b/libgo/go/golang_org/x/crypto/curve25519/doc.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package curve25519 provides an implementation of scalar multiplication on -// the elliptic curve known as curve25519. See http://cr.yp.to/ecdh.html +// the elliptic curve known as curve25519. See https://cr.yp.to/ecdh.html package curve25519 // import "golang.org/x/crypto/curve25519" // basePoint is the x coordinate of the generator of the curve. diff --git a/libgo/go/golang_org/x/crypto/internal/chacha20/chacha_generic.go b/libgo/go/golang_org/x/crypto/internal/chacha20/chacha_generic.go new file mode 100644 index 0000000..7ed1cd9b --- /dev/null +++ b/libgo/go/golang_org/x/crypto/internal/chacha20/chacha_generic.go @@ -0,0 +1,227 @@ +// Copyright 2016 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 ChaCha20 implements the core ChaCha20 function as specified +// in https://tools.ietf.org/html/rfc7539#section-2.3. +package chacha20 + +import ( + "crypto/cipher" + "encoding/binary" +) + +// assert that *Cipher implements cipher.Stream +var _ cipher.Stream = (*Cipher)(nil) + +// Cipher is a stateful instance of ChaCha20 using a particular key +// and nonce. A *Cipher implements the cipher.Stream interface. +type Cipher struct { + key [8]uint32 + counter uint32 // incremented after each block + nonce [3]uint32 + buf [bufSize]byte // buffer for unused keystream bytes + len int // number of unused keystream bytes at end of buf +} + +// New creates a new ChaCha20 stream cipher with the given key and nonce. +// The initial counter value is set to 0. +func New(key [8]uint32, nonce [3]uint32) *Cipher { + return &Cipher{key: key, nonce: nonce} +} + +// XORKeyStream XORs each byte in the given slice with a byte from the +// cipher's key stream. Dst and src must overlap entirely or not at all. +// +// If len(dst) < len(src), XORKeyStream will panic. It is acceptable +// to pass a dst bigger than src, and in that case, XORKeyStream will +// only update dst[:len(src)] and will not touch the rest of dst. +// +// Multiple calls to XORKeyStream behave as if the concatenation of +// the src buffers was passed in a single run. That is, Cipher +// maintains state and does not reset at each XORKeyStream call. +func (s *Cipher) XORKeyStream(dst, src []byte) { + // xor src with buffered keystream first + if s.len != 0 { + buf := s.buf[len(s.buf)-s.len:] + if len(src) < len(buf) { + buf = buf[:len(src)] + } + td, ts := dst[:len(buf)], src[:len(buf)] // BCE hint + for i, b := range buf { + td[i] = ts[i] ^ b + } + s.len -= len(buf) + if s.len != 0 { + return + } + s.buf = [len(s.buf)]byte{} // zero the empty buffer + src = src[len(buf):] + dst = dst[len(buf):] + } + + if len(src) == 0 { + return + } + if haveAsm { + s.xorKeyStreamAsm(dst, src) + return + } + + // set up a 64-byte buffer to pad out the final block if needed + // (hoisted out of the main loop to avoid spills) + rem := len(src) % 64 // length of final block + fin := len(src) - rem // index of final block + if rem > 0 { + copy(s.buf[len(s.buf)-64:], src[fin:]) + } + + // qr calculates a quarter round + qr := func(a, b, c, d uint32) (uint32, uint32, uint32, uint32) { + a += b + d ^= a + d = (d << 16) | (d >> 16) + c += d + b ^= c + b = (b << 12) | (b >> 20) + a += b + d ^= a + d = (d << 8) | (d >> 24) + c += d + b ^= c + b = (b << 7) | (b >> 25) + return a, b, c, d + } + + // ChaCha20 constants + const ( + j0 = 0x61707865 + j1 = 0x3320646e + j2 = 0x79622d32 + j3 = 0x6b206574 + ) + + // pre-calculate most of the first round + s1, s5, s9, s13 := qr(j1, s.key[1], s.key[5], s.nonce[0]) + s2, s6, s10, s14 := qr(j2, s.key[2], s.key[6], s.nonce[1]) + s3, s7, s11, s15 := qr(j3, s.key[3], s.key[7], s.nonce[2]) + + n := len(src) + src, dst = src[:n:n], dst[:n:n] // BCE hint + for i := 0; i < n; i += 64 { + // calculate the remainder of the first round + s0, s4, s8, s12 := qr(j0, s.key[0], s.key[4], s.counter) + + // execute the second round + x0, x5, x10, x15 := qr(s0, s5, s10, s15) + x1, x6, x11, x12 := qr(s1, s6, s11, s12) + x2, x7, x8, x13 := qr(s2, s7, s8, s13) + x3, x4, x9, x14 := qr(s3, s4, s9, s14) + + // execute the remaining 18 rounds + for i := 0; i < 9; i++ { + x0, x4, x8, x12 = qr(x0, x4, x8, x12) + x1, x5, x9, x13 = qr(x1, x5, x9, x13) + x2, x6, x10, x14 = qr(x2, x6, x10, x14) + x3, x7, x11, x15 = qr(x3, x7, x11, x15) + + x0, x5, x10, x15 = qr(x0, x5, x10, x15) + x1, x6, x11, x12 = qr(x1, x6, x11, x12) + x2, x7, x8, x13 = qr(x2, x7, x8, x13) + x3, x4, x9, x14 = qr(x3, x4, x9, x14) + } + + x0 += j0 + x1 += j1 + x2 += j2 + x3 += j3 + + x4 += s.key[0] + x5 += s.key[1] + x6 += s.key[2] + x7 += s.key[3] + x8 += s.key[4] + x9 += s.key[5] + x10 += s.key[6] + x11 += s.key[7] + + x12 += s.counter + x13 += s.nonce[0] + x14 += s.nonce[1] + x15 += s.nonce[2] + + // increment the counter + s.counter += 1 + if s.counter == 0 { + panic("chacha20: counter overflow") + } + + // pad to 64 bytes if needed + in, out := src[i:], dst[i:] + if i == fin { + // src[fin:] has already been copied into s.buf before + // the main loop + in, out = s.buf[len(s.buf)-64:], s.buf[len(s.buf)-64:] + } + in, out = in[:64], out[:64] // BCE hint + + // XOR the key stream with the source and write out the result + xor(out[0:], in[0:], x0) + xor(out[4:], in[4:], x1) + xor(out[8:], in[8:], x2) + xor(out[12:], in[12:], x3) + xor(out[16:], in[16:], x4) + xor(out[20:], in[20:], x5) + xor(out[24:], in[24:], x6) + xor(out[28:], in[28:], x7) + xor(out[32:], in[32:], x8) + xor(out[36:], in[36:], x9) + xor(out[40:], in[40:], x10) + xor(out[44:], in[44:], x11) + xor(out[48:], in[48:], x12) + xor(out[52:], in[52:], x13) + xor(out[56:], in[56:], x14) + xor(out[60:], in[60:], x15) + } + // copy any trailing bytes out of the buffer and into dst + if rem != 0 { + s.len = 64 - rem + copy(dst[fin:], s.buf[len(s.buf)-64:]) + } +} + +// Advance discards bytes in the key stream until the next 64 byte block +// boundary is reached and updates the counter accordingly. If the key +// stream is already at a block boundary no bytes will be discarded and +// the counter will be unchanged. +func (s *Cipher) Advance() { + s.len -= s.len % 64 + if s.len == 0 { + s.buf = [len(s.buf)]byte{} + } +} + +// XORKeyStream crypts bytes from in to out using the given key and counters. +// In and out must overlap entirely or not at all. Counter contains the raw +// ChaCha20 counter bytes (i.e. block counter followed by nonce). +func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) { + s := Cipher{ + key: [8]uint32{ + binary.LittleEndian.Uint32(key[0:4]), + binary.LittleEndian.Uint32(key[4:8]), + binary.LittleEndian.Uint32(key[8:12]), + binary.LittleEndian.Uint32(key[12:16]), + binary.LittleEndian.Uint32(key[16:20]), + binary.LittleEndian.Uint32(key[20:24]), + binary.LittleEndian.Uint32(key[24:28]), + binary.LittleEndian.Uint32(key[28:32]), + }, + nonce: [3]uint32{ + binary.LittleEndian.Uint32(counter[4:8]), + binary.LittleEndian.Uint32(counter[8:12]), + binary.LittleEndian.Uint32(counter[12:16]), + }, + counter: binary.LittleEndian.Uint32(counter[0:4]), + } + s.XORKeyStream(out, in) +} diff --git a/libgo/go/golang_org/x/crypto/internal/chacha20/chacha_noasm.go b/libgo/go/golang_org/x/crypto/internal/chacha20/chacha_noasm.go new file mode 100644 index 0000000..91520d1 --- /dev/null +++ b/libgo/go/golang_org/x/crypto/internal/chacha20/chacha_noasm.go @@ -0,0 +1,16 @@ +// Copyright 2018 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 !s390x gccgo appengine + +package chacha20 + +const ( + bufSize = 64 + haveAsm = false +) + +func (*Cipher) xorKeyStreamAsm(dst, src []byte) { + panic("not implemented") +} diff --git a/libgo/go/golang_org/x/crypto/internal/chacha20/chacha_s390x.go b/libgo/go/golang_org/x/crypto/internal/chacha20/chacha_s390x.go new file mode 100644 index 0000000..0c1c671 --- /dev/null +++ b/libgo/go/golang_org/x/crypto/internal/chacha20/chacha_s390x.go @@ -0,0 +1,30 @@ +// Copyright 2018 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 s390x,!gccgo,!appengine + +package chacha20 + +var haveAsm = hasVectorFacility() + +const bufSize = 256 + +// hasVectorFacility reports whether the machine supports the vector +// facility (vx). +// Implementation in asm_s390x.s. +func hasVectorFacility() bool + +// xorKeyStreamVX is an assembly implementation of XORKeyStream. It must only +// be called when the vector facility is available. +// Implementation in asm_s390x.s. +//go:noescape +func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32, buf *[256]byte, len *int) + +func (c *Cipher) xorKeyStreamAsm(dst, src []byte) { + xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter, &c.buf, &c.len) +} + +// EXRL targets, DO NOT CALL! +func mvcSrcToBuf() +func mvcBufToDst() diff --git a/libgo/go/golang_org/x/crypto/internal/chacha20/chacha_test.go b/libgo/go/golang_org/x/crypto/internal/chacha20/chacha_test.go new file mode 100644 index 0000000..bf99330 --- /dev/null +++ b/libgo/go/golang_org/x/crypto/internal/chacha20/chacha_test.go @@ -0,0 +1,188 @@ +// Copyright 2016 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 chacha20 + +import ( + "encoding/hex" + "fmt" + "math/rand" + "testing" +) + +func TestCore(t *testing.T) { + // This is just a smoke test that checks the example from + // https://tools.ietf.org/html/rfc7539#section-2.3.2. The + // chacha20poly1305 package contains much more extensive tests of this + // code. + var key [32]byte + for i := range key { + key[i] = byte(i) + } + + var input [16]byte + input[0] = 1 + input[7] = 9 + input[11] = 0x4a + + var out [64]byte + XORKeyStream(out[:], out[:], &input, &key) + const expected = "10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4ed2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e" + if result := hex.EncodeToString(out[:]); result != expected { + t.Errorf("wanted %x but got %x", expected, result) + } +} + +// Run the test cases with the input and output in different buffers. +func TestNoOverlap(t *testing.T) { + for _, c := range testVectors { + s := New(c.key, c.nonce) + input, err := hex.DecodeString(c.input) + if err != nil { + t.Fatalf("cannot decode input %#v: %v", c.input, err) + } + output := make([]byte, c.length) + s.XORKeyStream(output, input) + got := hex.EncodeToString(output) + if got != c.output { + t.Errorf("length=%v: got %#v, want %#v", c.length, got, c.output) + } + } +} + +// Run the test cases with the input and output overlapping entirely. +func TestOverlap(t *testing.T) { + for _, c := range testVectors { + s := New(c.key, c.nonce) + data, err := hex.DecodeString(c.input) + if err != nil { + t.Fatalf("cannot decode input %#v: %v", c.input, err) + } + s.XORKeyStream(data, data) + got := hex.EncodeToString(data) + if got != c.output { + t.Errorf("length=%v: got %#v, want %#v", c.length, got, c.output) + } + } +} + +// Run the test cases with various source and destination offsets. +func TestUnaligned(t *testing.T) { + const max = 8 // max offset (+1) to test + for _, c := range testVectors { + input := make([]byte, c.length+max) + output := make([]byte, c.length+max) + for i := 0; i < max; i++ { // input offsets + for j := 0; j < max; j++ { // output offsets + s := New(c.key, c.nonce) + + input := input[i : i+c.length] + output := output[j : j+c.length] + + data, err := hex.DecodeString(c.input) + if err != nil { + t.Fatalf("cannot decode input %#v: %v", c.input, err) + } + copy(input, data) + s.XORKeyStream(output, input) + got := hex.EncodeToString(output) + if got != c.output { + t.Errorf("length=%v: got %#v, want %#v", c.length, got, c.output) + } + } + } + } +} + +// Run the test cases by calling XORKeyStream multiple times. +func TestStep(t *testing.T) { + // wide range of step sizes to try and hit edge cases + steps := [...]int{1, 3, 4, 7, 8, 17, 24, 30, 64, 256} + rnd := rand.New(rand.NewSource(123)) + for _, c := range testVectors { + s := New(c.key, c.nonce) + input, err := hex.DecodeString(c.input) + if err != nil { + t.Fatalf("cannot decode input %#v: %v", c.input, err) + } + output := make([]byte, c.length) + + // step through the buffers + i, step := 0, steps[rnd.Intn(len(steps))] + for i+step < c.length { + s.XORKeyStream(output[i:i+step], input[i:i+step]) + if i+step < c.length && output[i+step] != 0 { + t.Errorf("length=%v, i=%v, step=%v: output overwritten", c.length, i, step) + } + i += step + step = steps[rnd.Intn(len(steps))] + } + // finish the encryption + s.XORKeyStream(output[i:], input[i:]) + + got := hex.EncodeToString(output) + if got != c.output { + t.Errorf("length=%v: got %#v, want %#v", c.length, got, c.output) + } + } +} + +// Test that Advance() discards bytes until a block boundary is hit. +func TestAdvance(t *testing.T) { + for _, c := range testVectors { + for i := 0; i < 63; i++ { + s := New(c.key, c.nonce) + z := New(c.key, c.nonce) + input, err := hex.DecodeString(c.input) + if err != nil { + t.Fatalf("cannot decode input %#v: %v", c.input, err) + } + zeros, discard := make([]byte, 64), make([]byte, 64) + so, zo := make([]byte, c.length), make([]byte, c.length) + for j := 0; j < c.length; j += 64 { + lim := j + i + if lim > c.length { + lim = c.length + } + s.XORKeyStream(so[j:lim], input[j:lim]) + // calling s.Advance() multiple times should have no effect + for k := 0; k < i%3+1; k++ { + s.Advance() + } + z.XORKeyStream(zo[j:lim], input[j:lim]) + if lim < c.length { + end := 64 - i + if c.length-lim < end { + end = c.length - lim + } + z.XORKeyStream(discard[:], zeros[:end]) + } + } + + got := hex.EncodeToString(so) + want := hex.EncodeToString(zo) + if got != want { + t.Errorf("length=%v: got %#v, want %#v", c.length, got, want) + } + } + } +} + +func BenchmarkChaCha20(b *testing.B) { + sizes := []int{32, 63, 64, 256, 1024, 1350, 65536} + for _, size := range sizes { + s := size + b.Run(fmt.Sprint(s), func(b *testing.B) { + k := [32]byte{} + c := [16]byte{} + src := make([]byte, s) + dst := make([]byte, s) + b.SetBytes(int64(s)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + XORKeyStream(dst, src, &c, &k) + } + }) + } +} diff --git a/libgo/go/golang_org/x/crypto/internal/chacha20/vectors_test.go b/libgo/go/golang_org/x/crypto/internal/chacha20/vectors_test.go new file mode 100644 index 0000000..b441fbd --- /dev/null +++ b/libgo/go/golang_org/x/crypto/internal/chacha20/vectors_test.go @@ -0,0 +1,578 @@ +// Copyright 2018 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 chacha20 + +// Test vectors for ChaCha20 implementations. + +type testCase struct { + length int + nonce [3]uint32 + key [8]uint32 + input string + output string +} + +var testVectors = [...]testCase{ + { + length: 0, + nonce: [3]uint32{0x94d13317, 0x6b6a2b3, 0x3ffe0036}, + key: [8]uint32{0x9da8a3b6, 0x3abf4ae6, 0xa2f19cae, 0x1068c707, 0x72e4801e, 0xce165d92, 0x61e7028f, 0x82ac3d57}, + input: "", + output: "", + }, + { + length: 5, + nonce: [3]uint32{0x469fadd, 0xee3fcc1e, 0x45cf77b0}, + key: [8]uint32{0x3477e02b, 0x45bf809f, 0x27f4a1fa, 0xdb901de8, 0xd8a190dc, 0x1d2c21d4, 0x87bdf2ac, 0xdfbf0000}, + input: "23dbad0780", + output: "415a3e498d", + }, + { + length: 9, + nonce: [3]uint32{0x512a6b49, 0x8df9af6d, 0x5336a2a5}, + key: [8]uint32{0xe9124c25, 0x4fd1a373, 0x7945f7bb, 0xeed5f064, 0x29c4185d, 0x3c9acf13, 0x4c94a367, 0x7c2c2c53}, + input: "f518831fab69c054a6", + output: "cfe40f63f81391484b", + }, + { + length: 12, + nonce: [3]uint32{0xca697a9e, 0x6b2f6717, 0xb7859220}, + key: [8]uint32{0xfc825020, 0x5ca4410b, 0x7d5285d0, 0x160a1c9d, 0x15470b41, 0x3634742a, 0xe64aa7fa, 0xca0be67a}, + input: "805fad1d62951537aeed9859", + output: "47bd303f93c3ce04bce44710", + }, + { + length: 14, + nonce: [3]uint32{0xcded3db3, 0x35770a7f, 0x6aede9b}, + key: [8]uint32{0x44632def, 0xa5e420a7, 0xfc12a8f, 0x63b79a15, 0x337de314, 0xb82fbf16, 0x3104bc57, 0x677c9227}, + input: "f4e8a7577affb841cf48392cf5df", + output: "f445c0fb7e3d5bfdab47090ddee6", + }, + { + length: 15, + nonce: [3]uint32{0x348a50b1, 0x4acc9280, 0x8d6014ce}, + key: [8]uint32{0x34bd31a8, 0x2808f47e, 0x9d8b19f9, 0x4df59683, 0x31584348, 0x34a74a45, 0xde174a2, 0x29d4c7dc}, + input: "1179b71ec4dc34bd812f742b5a0b27", + output: "cc7f80f333c647d6e592e4f7ecc834", + }, + { + length: 20, + nonce: [3]uint32{0xc8754703, 0x9188c521, 0xac8ce8a6}, + key: [8]uint32{0xe93c79ed, 0xce89162b, 0x116a8366, 0xecdc657f, 0x5bc81d98, 0xff5d2f52, 0x171f3ebb, 0x50773f2f}, + input: "7bd94943d55392d0311c413ac755ce0347872ba3", + output: "c43665de15136af232675d9d5dbbeca77f3c542a", + }, + { + length: 21, + nonce: [3]uint32{0x9a8655cb, 0x6e9d6ea5, 0x5dad705e}, + key: [8]uint32{0x3542d5b3, 0x1f7bfd8f, 0x1038abf8, 0x7214e8ec, 0xedd05693, 0x60e663bd, 0xe8e5d506, 0xeea923a2}, + input: "1505f669acc5ad9aaa0e993ba8c24e744d13655e1f", + output: "26cad1ccf4cf4c49b267ab7be10bc2ffa3ba66bc86", + }, + { + length: 25, + nonce: [3]uint32{0x3f202ca4, 0x63fc86, 0x7260a10e}, + key: [8]uint32{0xe28ab1d6, 0xe83b3d47, 0x671271ca, 0xb977bcff, 0xa2f64476, 0x311d79b4, 0x180d91d0, 0xec1a6e0c}, + input: "20070523ddb4ebf0d5f20fd95aacf47fb269ebadda6879638a", + output: "5ce972624cb2b7e7c28f5b865ba08c887911b4f5e361830a4b", + }, + { + length: 31, + nonce: [3]uint32{0xcf8671ea, 0x8d72df2f, 0x8b5a538a}, + key: [8]uint32{0xe46ca2bb, 0xd06ab5ef, 0xb0e2966b, 0x54dd0c2d, 0x8815d89a, 0x426c30a9, 0x15b0f1e, 0x254bae75}, + input: "d10f8050c1186f92e26f351db36490d82ea677498562d8d4f487a0a4058adf", + output: "f30c11bc553b2baf6870760d735680897c9fee168f976b2a33ef395fdbd4fc", + }, + { + length: 34, + nonce: [3]uint32{0xd1be983a, 0xf5aa389, 0xfa26c7e1}, + key: [8]uint32{0x795c6da7, 0x8cb1aadc, 0xa042359a, 0x95ea2e27, 0x128253c4, 0xaabc592f, 0x391e810, 0xf641d971}, + input: "e88dc380b7d45a4a762c34f310199587867516fac4a2634022b96a9f862e17714d17", + output: "aac98ba3821399e55a5eab5862f7f1bfc63637d700125878c2b17151f306c9aec80e", + }, + { + length: 34, + nonce: [3]uint32{0x98f5f4b8, 0x3f181d73, 0x5bf4572e}, + key: [8]uint32{0xa86f8cf7, 0x8db41a2b, 0xe0e03156, 0x3dad8a59, 0xb3e4d1ba, 0x75f6fb38, 0xdb94709d, 0xc3db34f3}, + input: "b0fcf0a731e2902787309697db2384e1cda07b60002c95355a4e261fb601f034b2b3", + output: "b6c8c40ddda029a70a21c25f724cc90c43f6edc407055683572a9f5e9690a1d571bb", + }, + { + length: 40, + nonce: [3]uint32{0x7289ae18, 0x7ebe7e50, 0x7d819176}, + key: [8]uint32{0x336c07a0, 0x4a2ea22b, 0xa8872f46, 0xa47b5e28, 0xbe645e3f, 0x371c6591, 0xd2dc237a, 0x92c59580}, + input: "cf9ec6fa3f0a67488adb5598a48ed916729a1e416d206f9675dfa9fd6585793f274f363bbca348b3", + output: "bb7ed8a199aa329dcd18736ce705804ffae8c3e2ba341ae907f94f4672d57175df25d28e16962fd6", + }, + { + length: 47, + nonce: [3]uint32{0xfd3181de, 0x8b193e26, 0xbebc799}, + key: [8]uint32{0x781a4c2e, 0x27ab55e2, 0x814aaf43, 0xa0bab01, 0x9de62ce0, 0x472b03d2, 0xdfee18e8, 0x8b855b93}, + input: "be9a8211d68642310724eda3dd02f63fcc03a101d9564b0ecee6f4ecececcb0099bb26aabee46b1a2c0416b4ac269e", + output: "3152f317cf3626e26d02cff9392619ea02e22115b6d43d6dd2e1177c6bb3cb71c4a90c3d13b63c43e03605ec98d9a1", + }, + { + length: 51, + nonce: [3]uint32{0x27b02ff6, 0xa510613e, 0x218b22d8}, + key: [8]uint32{0x62fc7732, 0xcef06cf4, 0xa4f45ed5, 0x2f96654f, 0x9f2b956e, 0x42b572f4, 0x5bb59c86, 0x35e4784f}, + input: "495343a257250f8970f791f493b89d10edba89806b88aaaeb3b5aefd078ba7b765746164bce653f5e6c096dd8499fb76d97d77", + output: "62c01f426581551b5b16e8b1a3a23c86bcdd189ab695dbea4bf811a14741e6ebbb0261ef8ae47778a6be7e0ef11697b891412c", + }, + { + length: 52, + nonce: [3]uint32{0x9db97a63, 0xff50248, 0xf2b6df56}, + key: [8]uint32{0x2b657a8f, 0xfe67575d, 0xaa56d261, 0x30179a97, 0xaefcfff1, 0x9b8eb698, 0x1efe3756, 0xb4ea450c}, + input: "e37fbbd3fe37ce5a99d18e5dcb0dafe7adf8b596528708f7d310569ab44c251377f7363a390c653965e0cb8dd217464b3d8f79c1", + output: "b07d4c56fb83a49e8d9fc992e1230bb5086fecbd828cdbc7353f61b1a3cec0baf9c5bf67c9da06b49469a999ba3b37916ec125be", + }, + { + length: 56, + nonce: [3]uint32{0xc1dfec38, 0x7d7503d3, 0x9a3e3c66}, + key: [8]uint32{0x8614d8e7, 0xde9b0413, 0x2a48b4fa, 0xcbbde744, 0xad5ddc5e, 0x9144d83e, 0x74d9d617, 0x230bdb45}, + input: "9efab614388a7d99102bcc901e3623d31fd9dd9d3c3338d086f69c13e7aa8653f9ce76e722e5a6a8cbbbee067a6cb9c59aa9b4b4c518bbed", + output: "829d9fe74b7a4b3aeb04580b41d38a156ffbebba5d49ad55d1b0370f25abcd41221304941ad8e0d5095e15fbd839295bf1e7a509a807c005", + }, + { + length: 63, + nonce: [3]uint32{0xc7e2521c, 0x795499b4, 0xc7946cd7}, + key: [8]uint32{0x53fce774, 0x9a4b53bf, 0x5f614134, 0xa3c39414, 0xa8a07c72, 0x93242311, 0x43aeec99, 0x216deb5a}, + input: "03b5d7ab4bd8c9a4f47ec122cbeb595bd1a0d58de3bb3dcc66c4e288f29622d6863e846fdfb27a90740feb03a4761c6017250bc0f129cc65d19680ab9d6970", + output: "83db55d9eb441a909268311da67d432c732ad6bda0a0dae710d1bce040b91269deb558a68ced4aa5760ca0b9c5efc84e725f297bdbdadbc368bea4e20261c5", + }, + { + length: 66, + nonce: [3]uint32{0x1d41f0a1, 0x7c3b7778, 0x6991eea5}, + key: [8]uint32{0x1f213e39, 0x56261d14, 0x15fc7c2c, 0x21feccc5, 0xa95684c5, 0x26600506, 0xdadcc06b, 0xf2c810b0}, + input: "2f4da518578a2a82c8c855155645838ca431cdf35d9f8562f256746150580ca1c74f79b3e9ae78224573da8b47a4b3cc63fbed8d4e831a6b4d796c124d87c78a66e5", + output: "6fc086ded3d1d5566577ccd9971e713c1126ec52d3894f09ab701116c7b5abda959cbb207f4468eb7b6a6b7e1b6d2bc6047f337499d63522f256ee751b91f84f70b6", + }, + { + length: 72, + nonce: [3]uint32{0x749f022c, 0xa021dab0, 0x648c2252}, + key: [8]uint32{0xa1ace7b0, 0x567a0ea1, 0x52af13b9, 0xcba30c08, 0xe07a6d74, 0x5c3bca39, 0x85b2ac07, 0x3b5afc0}, + input: "55739a1738b4a4028021b21549e2661b050e3d830ad9a56f57bfcaca3e0f72051b9ca92411840061083e5e45124d8425061ab26c632ac7852118411ac80026da946357c630f27225", + output: "8051bf98f8f2617e159ba205a9342ab700973dd045e09321805eed89e419f37f3211c5aa82666b9a097270babc26d3bfe0c990fe245ae982a31f23cfbf6156b5c8cfb77f340e2bf5", + }, + { + length: 74, + nonce: [3]uint32{0x23c16ba8, 0x9fd1cd4e, 0xcb224ecb}, + key: [8]uint32{0xb694404a, 0x86b5f198, 0x10fd1bff, 0x13a84e54, 0xab21e509, 0x7443d764, 0x931b3f1, 0x686e87f2}, + input: "7ffd8d5970fdee613eeae531d1c673fd379d64b0b6bfedd010433b080b561038f7f266fa7e15d7d8e10d23f21b9d7724bb200b0f58b9250483e784f4a6555d09c234e8d1c549ebb76a8e", + output: "c173617e36ea20ce04c490803b2098bd4f1ff4b31fdca1c51c6475ade83892c5f12731652d5774631d55ae2938617a5e9462bb6083328a23a4fba52de50ca9075586f2efc22aae56e3a8", + }, + { + length: 81, + nonce: [3]uint32{0xd65f6f29, 0xf3f76219, 0x9a033c9e}, + key: [8]uint32{0xeba017c4, 0x69e0421a, 0x449e2317, 0x29858a11, 0xd0c8523a, 0xa8b0c9a2, 0xab2ca84, 0xaf011a45}, + input: "7a5766097562361cfaeac5b8a6175e1ceeeda30aec5e354df4302e7700ea48c505da9fdc57874da879480ecfea9c6c8904f330cbac5e27f296b33b667fea483348f031bef761d0b8e318a8132caa7a5943", + output: "5e9fbf427c4f0fcf44db3180ea47d923f52bee933a985543622eff70e2b3f5c673be8e05cd7acbcadd8593da454c60d5f19131e61730a73b9c0f87e3921ee5a591a086446b2a0fadd8a4bc7b49a8e83764", + }, + { + length: 88, + nonce: [3]uint32{0xc70ee56e, 0xe58ec41, 0xafd96f61}, + key: [8]uint32{0x172af2bb, 0x9085d27c, 0x8ca2c44d, 0x8aa148da, 0x290c88b0, 0x88187439, 0x18d54781, 0x633f2cce}, + input: "0777c02a2900052d9b79f38387d2c234108a2ad066cbf7df6ea6acc5a3f86b3d6156abb5b18ad4ecf79e171383a1897e64a95ecdbba6aa3f1c7c12fe31283629ff547cb113a826cb348a7c10507cc645fa2eb97b5f22e44d", + output: "368c90db3464ba488340b1960e9f75d2c3b5b392bdd5622ff70e85e6d00b1e6a996ba3978ce64f8f2b5a9a90576c8f32b908233e15d2f443cccc98af87745c93c8056603407a3fb37ce0c1f8ab6384cc37c69c98bfecf337", + }, + { + length: 92, + nonce: [3]uint32{0x3006da79, 0x2748051d, 0x72c17cdc}, + key: [8]uint32{0x60cdb7e8, 0xcecbe928, 0xe19b7ab9, 0x30d61537, 0xa0fbc199, 0x897738bf, 0xdd7705a9, 0x3e5c1763}, + input: "cf2dccbcfd781c030376f9019d841ca701cb54a1791f50f50bee0c2bf178182603a4712b5916eebd5001595c3f48283f1ba097ce2e7bf94f2b7fa957ce776e14a7a570093be2de386ececbd6525e72c5970c3e7d35974b8f0b831fbc", + output: "7c92b8c75e6eb8675229660cedcb10334965a7737cde7336512d9eff846c670d1fa8f8a427ea4f43e66be609466711fd241ccff7d3f049bda3a2394e5aa2108abc80e859611dbd3c7ba2d044a3ececa4980dd65e823dd110fea7a548", + }, + { + length: 96, + nonce: [3]uint32{0xfc0fb1ee, 0x414cc60a, 0x4144bd67}, + key: [8]uint32{0x103291c6, 0x822b03b6, 0xd29ab548, 0xc88f3efe, 0x6936056a, 0x28aaa61f, 0xa0df7858, 0xdaa23519}, + input: "e08a8949a1bfd6a8c1186b431b6ad59b106ae5552821db69b66dc03fbc4a2b970dcf9c7da4f5082572bc978f8ee27c554c8884b5a450b36d70453348cd6cac9b80c9900cf98a4088803f564bb1281d24507b2f61ba737c8145c71b50eb0f6dfc", + output: "73d043acf9dcd758c7299bd1fd1f4100d61ff77d339e279bfbe6f9233b0d9afa24992a9c1c7a19545d469fdfb369c201322f6fe8c633fcdcffef31032bfb41b9fb55506e301d049fd447d61f974a713debeaed886f486a98efd3d6c3f25fbb30", + }, + { + length: 103, + nonce: [3]uint32{0xc2030c57, 0x1e3b59e1, 0x607ede1a}, + key: [8]uint32{0xd1bac2b5, 0x56a94583, 0x628b479b, 0x3056a51e, 0x69bf8f8f, 0x2df1e03d, 0x4b9d48d2, 0x7df5c379}, + input: "a0c302120111f00c99cff7d839cdf43207a7e2f73d5dd888daa00d84254db0e621a72493480420c9c61ce1cfc54188ff525bb7a0e6c1cd298f598973a1de9fd2d79a21401588775b0adbe261ba4e4f79a894d1bd5835b5924d09ba32ef03cb4bc0bd6eb4ee4274", + output: "bc714bd7d8399beedc238f7ddeb0b99d94ad6bf8bf54548a3e4b90a76aa5673c91db6482591e8ff9126e1412bce56d52a4c2d89f22c29858e24482f177abacef428d0ae1779f0ae0778c44f9f02fe474da93c35c615b5fad29eca697978891f426714441317f2b", + }, + { + length: 109, + nonce: [3]uint32{0xf44dc81f, 0xcf6e03e7, 0xf4966796}, + key: [8]uint32{0xd7b12f4, 0x683f4789, 0xc7828fb4, 0x820fc6a0, 0xc51231eb, 0xe46716d7, 0x4036ef93, 0x26afb96c}, + input: "ebce290c03c7cb65d053918ba2da0256dc700b337b8c124c43d5da4746888ca78387feea1a3a72c5e249d3d93a1907977dd4009699a15be5da2ca89c60e971c8df5d4553b61b710d92d3453dea595a0e45ae1e093f02ea70608b7b32f9c6aadc661a052f9b14c03ea0117a3192", + output: "cbb8c4ec827a1123c1141327c594d4a8b0b4a74b0008115bb9ec4275db3a8e5529a4f145551af29c473764cbaa0794b2d1eb1066f32a07fd39f5f3fe51498c46fba5310ae7c3664571d6a851e673ded3badc25e426f9c6038724779aa6d2d8ec3f54865f7df612e25575635ab5", + }, + { + length: 115, + nonce: [3]uint32{0x8d3e461b, 0x7e05c360, 0x3bbbafdd}, + key: [8]uint32{0xf9b917c9, 0x9af89bf7, 0x7decbbc9, 0xe7e5ea7b, 0x9b4aab55, 0x90eff6be, 0xa19b6d90, 0xb9f69b1a}, + input: "275c97de985aa265332065ccce437770b110737a77dea62137a5d6cb62e9cb8b504d34334a58a71aba153d9b86f21377467b2fafaf54829331bf2ce0009acb37842b7a4b5f152aab650a393153f1ed479abc21f7a6fe205b9852ff2f7f3a0e3bfe76ca9770efada4e29e06db0569a99d08648e", + output: "b225aa01d5c438d572deaea51ac12c0c694e0f9dc0ed2884a98e5e2943d52bb4692d7d8f12486de12d0559087e8c09e4f2d5b74e350838aa2bd36023032ccbcae56be75c6a17c59583d81a1fd60e305af5053ac89f753c9347f3040e48405232dc8428c49dcb3d9b899145f5b3bc955f34dbbe", + }, + { + length: 119, + nonce: [3]uint32{0x871f33f5, 0xe4fee3ba, 0xcb8c1e93}, + key: [8]uint32{0x33124903, 0x7e0287e5, 0xe9d6988f, 0x1962405f, 0x5f21c1b5, 0x2ac695e6, 0x46b200c9, 0x9fda98ba}, + input: "ceda15cfffd53ccebe31b5886facd863f6166e02ec65f46f54148860a5c2702e34fd204d881af6055952690cd1ffa8ba4d0e297cc165d981b371932adb935398c987baff335108c5e77f2e5dd5e1ca9a017bc376cbdbe3c0f45e079c212e8986b438444e79cd37927c1479f45c9e75b0076cc9f8679011", + output: "a3f1c3f885583b999c85cd118e2ababfa5a2de0c8eb28aacc161b1efee89d8de36ddeb584174c0e92011b8d667cb64009049976082072e6262933dbf7b14839805e1face375b7cbb54f9828ba1ed8aa55634ec5d72b6351feff4d77a3a22b34203b02e096f5e5f9ae9ad6a9dd16c57ce6d94dcc8873d18", + }, + { + length: 120, + nonce: [3]uint32{0xef553ce8, 0xdfe120ea, 0x9a047e3a}, + key: [8]uint32{0xbef479c1, 0x59554f8b, 0xbf97f089, 0x52316f1e, 0x141e428, 0xff26dc04, 0xe10c8f57, 0xa7568a59}, + input: "799bb2d634406753416b3a2b67513293a0b3496ef5b2d019758dedaaac2edd72502fc4a375b3f0d4237bc16b0e3d47e7ddc315c6aef3a23fcae2eb3a6083bc7ac4fd1b5bf0025cc1cb266b40234b77db762c747d3a7b27956cf3a4cf72320fb60c0d0713fa60b37a6cb5b21a599e79d0f06a5b7201aeb5d2", + output: "e84dfb3dbaac364085497aeabd197db852d3140c0c07f5f10e5c144c1fe26a50a9877649e88c6fe04283f4b7590a8d0d042ef577693f76f706e31c4979437590fe0ab03d89afb089d1be50ae173ea5458810372838eceac53bf4bac792735d8149e548efb432e236da92bf3168bbcf36f644c23efb478a4e", + }, + { + length: 123, + nonce: [3]uint32{0xd98124a0, 0x78cd80aa, 0x3dc55cfc}, + key: [8]uint32{0x2286e41, 0xf13e38e3, 0xf735476b, 0x33c44bfc, 0xd7978797, 0x4a9c4595, 0x6080413, 0x1299fdd8}, + input: "b2d060bd173955f44ee01b8bfcf0a6fad017c3517e4e8c8da728379f6d54471c955615e2b1effe4ce3d0139df225223c361be1cac416ade10a749c5da324563696dae8272577e44e8588cd5306bff0bfbdb32af3ac7cbc78be24b51baf4d5e47cf8f1d6b0a63ed9359da45c3e7297b2314028848f5816feab885e2", + output: "ffa4aa66dd5d39694ae64696bfa96f771accef68f195456ad815751e25c47ed4f27b436f1b3e3fcaa3e0d04133b53559c100cd633ced3d4321fc56225c85d2443727bce40434455aa4c1f3e6768c0fe58ad88b3a928313d41a7629f1ce874d2c8bcf822ebdaebfd9d95a31bb62daab5385eb8eefe026e8cbf1ff7a", + }, + { + length: 127, + nonce: [3]uint32{0x53106b0f, 0xdf11fd81, 0x69d1b6f3}, + key: [8]uint32{0x736b138, 0x55cde194, 0xf8273c1, 0xf7c268e6, 0x61362bd5, 0xbb3cb455, 0x44d3c9fc, 0x7d56d3fd}, + input: "4f0171d7309493a349530940feece3c6200693f9cff38924114d53f723d090fffa3c80731b5ca989d3e924d1fa14266632cb9ab879e1a36df22dc9f8d1dadea229db72fded0c42187c38b9fa263c20e5fb5b4aa80eb90e8616e36d9b8c613b371c402343823184ecad3532058a46cf9e7ea5a9ecad043ac3028cbcc3f36d32", + output: "88c773ff34b23e691e14018ba1b2bd48a4a6979b377eb0d68336ce6192dcd5177e6b4f1c4bea2df90af56b35fe2a1d6279d253c0194dcbca9bf136f92d69165b216e4c9d1ce6b3fbe40c71e32c3f4088de352732d0e2bad9c16fd0bb9bde3d6c30257ce063432d09f19da79d49aa7641124a6c9e3f09449e911edbae11a053", + }, + { + length: 130, + nonce: [3]uint32{0x5e90ffbd, 0xa898f173, 0x269f9a88}, + key: [8]uint32{0x5244e05f, 0xf9adbe9b, 0x9e9f54ac, 0x23460046, 0x6782cdea, 0xba982c96, 0xc721506b, 0xed10f7e3}, + input: "8f8d9e18d3212bd20b96d75c06d1a63622fd83d13f79d542e45996135368772ea81511302a0d87e246dd346314cfe019bae8a5c97f567f12d82aca98dfea397c6a47dd0c419f1c609d9c52dcfcbe7eee68b2635954206ed592b7081442ce9ce3187d10ccd41cc856fb924b011f817c676c9419f52a2938c7af5f76755a75eb065411", + output: "4e130c5df384b9c3c84aa38a744260735e93783da0337ade99f777e692c5ea276ac4cc65880b4ae9c3b96888760fdddb74bc2e2694bedf1ee6f14619c8015f951ba81b274b466e318d09defe80bdbed57bc213ac4631d2eb14c8e348181d60f6295ceee1e9231ae047830ef4778ff66146621b76974773b5d11c8e17a476450f46ef", + }, + { + length: 130, + nonce: [3]uint32{0x308e39e8, 0x9aa4f14f, 0xf511db96}, + key: [8]uint32{0x833b5219, 0x4b82e588, 0x4b2d652c, 0x7c8f6ed7, 0xfe4be863, 0x9d3a50e5, 0xb888099b, 0x9f8d1968}, + input: "30d2379dd3ceae612182576f9acf6de505ab5a9445fe1a86ae75c5c29429e11c50fd9ec657b29b173a3763b1e171b5a7da1803ba5d64fccb2d32cb7788be194dbca00c3c91774c4c4c8ede48c1027d7cc8b387101a4fe5e44a1d9693b2f627626025072806083aadbced91c9711a0171f52ffb8ed5596cf34130022398c8a1da99c7", + output: "b1e8da34ad0189038ee24673979b405ef73fdbdd6f376f800031d64005a4ebed51a37f2180571223848decbea6dd22b198ab9560d7edc047c5d69183dc69b5fca346911d25cb2a1a9f830dc6382ad0024e8c3eef3aa2d155abcfe43bff01956a5e20a862fbed5c5e8df8eed0601a120caac634b068314e221f175baa11ae29002bb9", + }, + { + length: 135, + nonce: [3]uint32{0xa5feca5a, 0x753ac1b4, 0xc5a46609}, + key: [8]uint32{0xabbf4859, 0x828d9bf6, 0xf7f7aa6d, 0x25208ca2, 0xd7a4c0ad, 0x2fdd3282, 0x2bfcb8c2, 0x8389d84b}, + input: "d9404ccdcc8ef128a1b1ace4f9f1669d274ec82aa914cac34b83ac00b236478fd6167e96ec658850c6c139eb0f6fc0dd7191ba9a39828032008f7f37eb9a8df9d6cdd54240e600efe7fc49a674000c5030d825b2c5c96d0f19b8ecdbf4eeb86d3e569c5e3131abc7d6359dd4255284ccacf150d42e7a899536d51ee6db329654a4581c5ac6e419", + output: "c5534b5fb40b4834300e9577a9d87440c5272263d06e6aee84aa92cdf5d1b033145d336f26e5fe55c09a7e75753af93d0786dfc1cb435e86c67bd3ec8e766d0801b99e68691e2c3c3ffec539cf62e68285ea9027daa2716cd6f97e8eb7b9e266357a25eb2d4839a829508a6b7228f2832b3cd998f77597ae530430e6e4ecb53eb9efe456863a04", + }, + { + length: 135, + nonce: [3]uint32{0x12aa5846, 0x88604f6c, 0xc10d9585}, + key: [8]uint32{0x1491ccd6, 0x602f559d, 0xd4080c06, 0x202fabd, 0xffd3f4f8, 0xbf144c17, 0x88bf3f3c, 0x8083375}, + input: "231765f832927461f338aceb0f4cf51fd8469348c69c549c1dec7333d4aa4968c1ed58b65ab3fe3d0562600a2b076d56fd9ef91f589752e0455dd1d2e614cacfc0d757a11a4a2264bd38f23d3cca108632201b4f6c3b06477467726dde0c2f3aee01d66d788247663f1d0e66b044da9393ede27b9905b44115b067914961bdade85a2eca2844e1", + output: "1dd35f3f774f66d88cb7c2b23820ee078a093d0d85f86c4f103d869f93e2dbdd8a7cb8f101084fe1d7281a71754ec9aac5eb4fca8c365b24ed80e695caace1a8781a5a225938b50b8be96d0499752fdabd4f50d0b6ce396c6e2ca45308d1f2cc5a2a2361a8ca7a334e6ee62d466d74a1b0bf5b352f4ef6d8f8c589b733748bd3d7cda593243fab", + }, + { + length: 140, + nonce: [3]uint32{0x1c9d70f0, 0xa088a367, 0x4ec24d2b}, + key: [8]uint32{0x494e9775, 0xd07a852, 0xaf8af24a, 0xc65b825c, 0xc5e06780, 0x17fbbace, 0x651d71b5, 0xf548d8ef}, + input: "e46841f12d98aeb7710b9162d342895a971b0e3a499886bbb6aa74dc744a28d89a54542b628acdc2f693cb7c03f73fc3b74069bc3f2d000a145fb8a806cdc7d6fa971da09a33b92851cc3d1f6f5646d7fa2b1d564876feefeb63b6e66dba1c0b86ca345235bb822e0f93132346840d2a3d6eb1b541178ea51affc7b31f8da02732cc4e5bcb5d8683ae0a91c9", + output: "1dcbfd0bb2b905656c52bd7b1bcdad9b4d434ae9ac221a0d3a316115cdd4a463fa9b3444d2612a4e277d0dcd881fa6e80e59e5a54e35e1a14747aed31edf4ac24214f9d9c329ebe2157620b64efaded9976549bc4aa100d5c15be3f85f700f8a21dfe77590dfee2de9a23cc1ed1e44f32ebf68ca289b097bc13b42802dc7c75309c4afc25b5741839f7db3d5", + }, + { + length: 144, + nonce: [3]uint32{0x23067b8b, 0x5b276c6d, 0xaeca6c60}, + key: [8]uint32{0x29d64488, 0x893a2973, 0x32e3b4ef, 0x2af3d5d4, 0x95ec01b, 0xc805b64c, 0x884e8b7d, 0x798d7062}, + input: "e98e4a9550bdd29e4106f0cc8669dcc646a69438408e9a72c7cdb9b9d437b5f7a13fcb197629541c55bca1f8972a80cd1c1f591a0e24f977cdeb84763eab2648e42286e6473ea95e3a6a43b07a32b6a6cd80fe007ba0cf7f5ac7e651431f5e72690ec52a7134f9757daf0d8eff6b831a229db4ab8288f6bbf81e16fedebe621fd1737c8792cfd15fb3040f4f6a4cbc1e", + output: "5c69cf522c058790a3bc38979e172b60e71f7896d362d754edc1668d4f388b3fc0acdf40786d2f34886e107a142b1e724b9b9b171cb0e38fd78b35f8ac5269d74296c39c9f8628d848f57af9d8525a33f19021db2b9c64ba113171ebb3882075019ec7e77b51ce80b063ed41d48dad481d9536c030002a75d15c1c10ce0ec3ff17bc483f8416055a99b53035f4b6ea60", + }, + { + length: 148, + nonce: [3]uint32{0x2b079658, 0xbdf5da85, 0x8a75450d}, + key: [8]uint32{0x49c9eaa3, 0x62048819, 0x9baacfa5, 0x3870addc, 0x5c682e1, 0xf4f9fff3, 0xa3848e4b, 0xac1ebc1}, + input: "ce0f0d900dd0d31749d08631ec59f216a1391f66a73bae81d3b0e2919a461bc9a14d6a01b827e3bcb55bbccf27c1ed574157e6becd5cf47181a73c9d3e865ab48a20551027e560e965876b0e1a256bfa5cb5179bf54bd8ec65e5570e374b853b37bf4b3ef1ec612d288ebc19275fa88da9419e012f957f9b6a7e375b3377db0eb3619c731aebfeb0930772b4020d3a3e90723e72", + output: "b06981b57fe184091ef9f8ccf522a5bcdb59bf9a68a3ddb817fdd999a6ecf81053a602141cf1b17017bae592b6b6e64756631a2b29a9e1b4f877c8b2ae30f71bc921e4f34b6f9cd8e587c57a30245f80e95005d0f18f5114400785140e6743da352d921fb4a74632a9c40115ad7706263ac9b41a11609fa0c42fc00f8d60931976162598df63ebad9496dd8943d25a03fa47475c", + }, + { + length: 148, + nonce: [3]uint32{0x98e8ab8, 0x84d8e77b, 0xbb305841}, + key: [8]uint32{0x46b5f93c, 0xc8b2778d, 0x2cc5278f, 0xd2a3904c, 0x6ce5d4f, 0xc4459e8, 0x4a35c30, 0x2feadc02}, + input: "eccfd66bdc691478f354b8423d6a3f20932a1f591d8e6cefa734975fb8ee6881b6dc92c0d1d5ed54fd1999efd7f11ac697a1f130587dd895eb498c9a8fc7d1714c385ec156ecae3bdea2a3462834245e724531d0fedda2b77693a53ed7354b758e875b23cfc83219a091fb2076e7a88cd77f779ed96f8d81ffa3fe5059303ac706086494b9f2982f4f88a0c6fadc3748625004db", + output: "925529047d4177b72bf50905ba77e47608815522c1829b24046e439d5451901257903a5409fb910373167e8b7f4fdfa543a477608ddfc11bbd1efc138366961463b9915b302a346b795dd593f6fcf4fa73529b6fe83079552aabbe99474a72806f59688d826675fa7f6649b9f5307e5028853c9821b8c4a1a0fc4bfdc7c8c78b25aeaba2b5821d17b36317381a3bd578917d2504", + }, + { + length: 152, + nonce: [3]uint32{0x2e2a6e4a, 0x9a6d488a, 0xf9966cb6}, + key: [8]uint32{0x58903bff, 0xc2be173f, 0xe26128b5, 0xb6b6af53, 0x92f8eeb, 0x38cf3336, 0x7fdf90fb, 0x7ae24b37}, + input: "f0c7139c69413869bca980d7f192b2bc3f57e34ca4f26164e1a54a234e84e1aa285cc02cfbaef3dfba2dbb52a555ec1f6ef0e89d0b2f0bd1846e65b74444b5f003a7308965e67bed558689be2668ca10ca368fac072e0e4535a031af23b3c37c561e185872b86c9bceddb5c1199e43fb5f735384766d33710460b541b52d3f5b6c108c08e76724bcac7ad2d866a8bbeeea92a3d867660d2e", + output: "d2c16c7a242b493038203daec65960de384c030eb698ef6a53c36eabb7556cbfa4770eaa8bc0a2b385ad97495eeb1c03ff4e6efcb804aefa81c177dc62700a9eefe6e8dd10cff5d43a2f47463cab5eb1ee260c3826cac9bfa070f1e0435541a89ebd224d13cc43f8fff12f38091c2b3f2102d5c20d8b1c3ae4f129364bbe9f9ce2147dcf0639668ddb90dffe6a50f939f53fa7ba358e913f", + }, + { + length: 155, + nonce: [3]uint32{0x243e0198, 0x884448c, 0x9a31e760}, + key: [8]uint32{0x37e017bc, 0x9b1e2e90, 0x15679daa, 0xf94a23ee, 0xda86dfe, 0xc3eea84c, 0xdd199799, 0x6eeffb92}, + input: "7024974ebf3f66e25631c0699bcc057be0af06bc60d81a7131acaa620a998e15f385c4eaf51ff1e0a81ae5c6a7442d28a3cdc8aeb9701055e75d39ecac35f1e0ac9f9affb6f9197c0066bf39338a2286316e9d1bb7464398e411da1507c470d64f88d11d86d09e6958fa856583ace697f4ee4edc82618662cb3c5380cb4ce7f01c770aab3467d6367c409a83e447c36768a92fc78f9cbe5698c11e", + output: "ff56a3a6e3867588c753260b320c301ce80de8c406545fdd69025abc21ce7430cba6b4f4a08ad3d95dc09be50e67beeff20d1983a98b9cb544b91165f9a0a5b803a66c4e21bd3a10b463b7c1f565e66064f7019362290c77238d72b0ea1e264c0939d76799843439b9f09e220982eb1dc075d449412f838709428a6b8975db25163c58f40bf320514abf7a685150d37a98bac8b34ccb5245edb551", + }, + { + length: 160, + nonce: [3]uint32{0xd24e866d, 0xc59d25d8, 0xfcf623f1}, + key: [8]uint32{0x5f32cca0, 0x4167cac5, 0xc04943ee, 0x507fa1ec, 0xad8fdfc0, 0x6266fa2d, 0x22f05341, 0x8074143e}, + input: "8d79329cf647e966fde65a57fc959223c745801c55312046b791671773cca0af4cd48ead1f316eba0da44aa5d18025eced0c9ed97abaabb24570d89b5b00c179dca15dbae89c0b12bb9e67028e3ae4d6065041b76e508706bec36517a135554d8e6ef7cf3b613cbf894bec65d4dc4e8cb5ca8734ad397238e1e5f528fa11181a57dc71cc3d8c29f3aba45f746b1e8c7faace119c9ba23a05fffd9022c6c85260", + output: "60aea840869f7be6fcc5584b87f43d7ba91ed2d246a8f0a58e82c5153772a9561bdf08e31a0a974f8a057b04a238feb014403cd5ffe9cf231db292199198271f9793c9202387f0835a1e1dc24f85dd86cb34608923783fd38226244a2dd745071b27d49cbffebea80d9dacad1578c09852406aa15250de58d6d09cf50c3fcfff3313fac92c8dad5cb0a61ccc02c91cecee3f628e30c666698edecf81831e55ec", + }, + { + length: 167, + nonce: [3]uint32{0x30b61047, 0x810cf901, 0x4d681524}, + key: [8]uint32{0xe51476d0, 0xdf98008d, 0x59dfe69e, 0xdb39166, 0x6c1e4a4a, 0xfb76165e, 0x5180f185, 0x7359fb35}, + input: "85484293a843d2d80b72924b7972dfa97cbe5b8c6bcc096f4d5b38956eb3f13f47b02b0f759ea37014ecdecfb55f2707ef6d7e81fd4973c92b0043eac160aaf90a4f32b83067b708a08b48db7c5900d87e4f2f62b932cf0981de72b4feea50a5eb00e39429c374698cbe5b86cf3e1fc313a6156a1559f73c5bac146ceaaaf3ccf81917c3fdd0b639d57cf19ab5bc98295fff3c779242f8be486ba348bd757ba920ca6579be2156", + output: "bb1650260ef2e86d96d39170f355411b6561082dcc763df0e018fdea8f10e9dc48489fb7a075f7f84260aecc10abcfadbc6e1cd26924b25dedb1cc887ada49bb4e3e02006bdd39098ef404c1c320fb3b294ded3e82b3920c8798727badfb0d63853138c29cf1ebf1759423a1457b3d2c252acf0d1cde8165f01c0b2266297e688ff03756d1b06cb79a2cc3ba649d161b8d9ef1f8fb792bd823c4eabb7fb799393f4106ab324d98", + }, + { + length: 172, + nonce: [3]uint32{0x42020cbe, 0xad62af90, 0x29e53cd}, + key: [8]uint32{0xabad2095, 0x601ec477, 0x3bc923a1, 0x1edede1a, 0x33612355, 0x285b4858, 0xd3fd6714, 0xe0f4bcc3}, + input: "a2fc6e1b5281a4e0330eecd1ab4c41670570423173255979953142b78733b2910fa5540e8294208df6ae4f18672d5ac65acf851bcd394e1932db13c81b21e6f165e5538aff862e46126c650bbe055e54b31c78f2f0221d2631d66ef6d3f4c5ae25eada043b74d8770e2c29799c0954d8ccbd17766b79e6e94e88f478db3566a20cb890846917591a07738328d5c05f7ed4695a82607660f1239661faa9af0368aeb89726f13c2aaecf0deaf7", + output: "d8fe402a641c388522842385de98be60f87d922c318215947d4b7562d4ca1e2dbc7ee86494e65fb0bfddfdebdb2ae6469312f95b32c722b2720d64bb8d7cc3dd82f9055b1d89f05b77984f91f94ba4ac79c5129cd7c91cc751b0defc3f2799518e372d27aa683f1e7bbd4f55414c48fe8a3a37ac1f179a1a329cda775aec0d31d75a5a38addb1de67c06bddbedf4c8d87abc18c9f9dd072d457ea29ad4dfb109ce7e99a4a82fbe330b0afbb5", + }, + { + length: 176, + nonce: [3]uint32{0xa8021c8f, 0x667a02c4, 0x7a68b693}, + key: [8]uint32{0xece401c8, 0xfa805a47, 0x6d572fca, 0x9c1c780c, 0x647545e5, 0xd7ef4c11, 0x91dc1e46, 0xba2a694e}, + input: "480387bc6d2bbc9e4ced2448d9ec39a4f27abe8cfb46752d773552ad7808a794058962b49e005fef4e403e6a391d1d3f59025eeb5fb8fbbe920f5361862c205d430eac613cd66108f2f2f0bd4d95a8f6ca7bd1f917eaeb388be87d8b7084a2eb98c575034578edf1b3dafff051a59313873a7be78908599e7e1c442d883d3fd3d26787eb7467eed3a3fb2d40046a4460d5d14215565606bcf8b6270af8500e3504d6d27dacf45bace32214472d525fdc", + output: "ab81a9c28358dfe12e35a21e96f5f4190afb59214f3cf310c092ab273c63cd73a783d080c7d4db2faccd70d1180b954cd700c0a56b086691e2c2cd735c88e765e2266cd9ebe1830d63df4b34e2611a8abeeca9c8c4fac71135dafb1cb3569540ed1362ddeb744ed62f6fd21de87b836ec2980f165c02506e0c316ae3cf3d18a862954d9781f726ecc1723af4a730ccc6d6de82553450a52499acb58fb2008969401c45b2f20e12b58f308db1d199b4ff", + }, + { + length: 176, + nonce: [3]uint32{0x414e687c, 0xc6fc69c2, 0xd3ca12d3}, + key: [8]uint32{0x1b51cca, 0xbc8455af, 0x3f904842, 0x6042b452, 0xcd4dd164, 0xda83f3f0, 0xff04b972, 0xf972dd0e}, + input: "b274e61059f3215173ae226e30a92ee4b4f8a3da95f2e768e3fac2e54ddac92c200c525f190403a6ef9d13c0661c6a7e52ed14c73b821c9680f1f29711f28a6f3163cf762742ed9474dbea51ff94503a5a404adbbdfbf4c6041e57cb14ea90945dc6cb095a52a1c57c69c5f62ac1a91cd8784b925666335bbfee331820b5f7470bc566f8bbb303366aafe75d77c4df5de2649ed55b2e5e514c3cb9f632b567594a0cf02ec6089a950dbe00554ee4dfb9", + output: "a0969730d48ee881792a3927b2f5d279aba9f2ed01e6b31b92d0e1fb8ba7f35a236d838e0ce5f8654957167de864f324c870864b4e7450a6050cd4950aa35e5a1a34a595e88dd6f6396300aff285de369691b6e0e894106dc5b31525e4539c1e56df3ceedbbab1e85da8c0914e816270a4bae3af294b04a3ea6e9ef7e2aab4da5f5370df2706b5e3f000d88179ac756deaa652a1cc85e80ad9622f1bf91a2776262eb7289846d44f7f8192e763cb37aa", + }, + { + length: 183, + nonce: [3]uint32{0xdd315c1d, 0x2335da98, 0xe0a0da0f}, + key: [8]uint32{0x6419c7d6, 0xd340f42, 0x7af2f4b8, 0x3536cf42, 0x2f68c6fb, 0xac9d855f, 0x7c4d490, 0x9711b1b1}, + input: "ee849039c6cd972dc943d2a4468844d130c0150276f4e0889047e2300c3ecc6792c4527bfe9437dad877eb986e6b1aa9b867d1798c9d314243f0a87ec9ee5b601c2554876c87cbf50df3334a077c4152f8b8fef4a2d301ddbfa90c887ece757c3eb6c4fc1e0212d6b5a8bb038acaec28cba064c9b34f5364cb7f0fc2ac4ef2c7ddde0f5ba17014459eaa78f08a46a01882ebf7c6e409dadda250bb899dc8b3b70e160bbcb4412a9963b174d0fc6bc16383a46ffaacb6e0", + output: "3e272ded9c0a5cebe7cf17ac03f69eb20f62996e047501b6cc3c8691ddb2780ea72c21a81888bfea96e4373a412c55ca95648390de740102d661143043baec3976230e024477d134b8504a223c36a215b34164c9e9e1fa99a49fdc56f2f04ea525a6b82997d9bbc95c4b5baeab4dec50061efb7c1a757887acb8b47b142e0a2e61885a2c14c4642d83d718a0546b90699adc545a48129603862a1c89d8e665cde54b3ba487754db6d6f5acf6a4b95693cc569577a2dc48", + }, + { + length: 185, + nonce: [3]uint32{0xebb44f7c, 0xaf14c7dd, 0x4543cd7a}, + key: [8]uint32{0xce71977, 0x99790e86, 0x6510d6dc, 0x37968ae7, 0x2917fb9a, 0x19ef25f, 0xd282d085, 0x6128d043}, + input: "0992396a6f29b861dd0bc256e1d1b7dce88435733506a6aa20c62e43afa542d1c46e28b2e6d8e2eacb7c08db05e356fe404684b0e3a9849596db82eb788aa09258c28eb19e9838f757425b4edef12deeca56e30cf030272e325d4246d6e083219b2f965124963ca91f066d47bf5a8282a011a78b0155aa70038259a4a59135f241fd2f88c908b9f4eef7b7df0f3a1c16a52c009b522f89dabd52601bbf6e3ce68732e1a6d444469480f06da218786cf6c9666362e7a7f7be12", + output: "545c05a84b5a4fffd1dd623c8f2b11443818560bdb0c26dadd3b694d4790d294b99059f4127b7cca122c4000954d745af96094ff4623f60db33e994bb6903263d775f48d7047427b3a498c2ecde65bd37bcb8ee7e240a1e08c884c0079cab518f4e1c38ba5ea547f4da83b7c6036e4259bee91c42e8fae895df07781cc166f1d50e1550a88ee0244bb2950070714dd80a891aa8a9f0580a67a35cb44609b82a5cc7235f16deea2c4f3667f2c2b33e8eeef944e1abdc25e48fa", + }, + { + length: 187, + nonce: [3]uint32{0x35cb7190, 0x212e9a86, 0xbc423ce4}, + key: [8]uint32{0xfa19cede, 0x576ae8f2, 0x58055dab, 0x91b3355d, 0x69d2501a, 0x736323c2, 0x266c1385, 0x134f4557}, + input: "3b9efcbbb607fad5e9f1263dad014cc5c2617d439fcd980408f4f9a93acb1a33d1c3a22f38c037e4603dfbbfb5571bc08c4a1958cbbf510e3e4dd19007fe15fad7808369149a9c4db7ca0496f7a600a6f2454ee1cffd5a68d45c270e4b53ac9b77f33a1ffbb1804244f57d2b05b8036fe2cda9efead3d4eff074ea5c07128e0b354a4a11ffa179163933bc6bd10d200804cc93b64575746e94e975f990bddcc8a4335e99e2459fbe9bc0e004ffcd6cac52f48ef55cc0637a75c1dc", + output: "631ba7301e33236da2477506ea98d3b732447389e849b68e1f09bd5fd814f40dc3247a1012fa654f08e3dda0c104ee2dff12ecf5cb018644de50d70dfb6c8cc1f5f552e5f1e50466bbb538ad6b98fd37f33fe615c326efc9cc97899b829b007f91569fa9b28ce0076c53daedf9cc0f838e22cf1125b86a6a2c2eb4a45dadea45ad00fb4f054e7d6b09c13ab1dd5328debfbf4f1b70af2b8a5b1d02df8a87d7661473e0c180ba4c815f14db87c5bdc15f11a29d8e0ce3d747d5dcd4", + }, + { + length: 191, + nonce: [3]uint32{0xccc941ac, 0xdba45b02, 0xab0d7ad6}, + key: [8]uint32{0x9b750752, 0xa627090a, 0x967c95f0, 0xf8ff2c3f, 0x69beb97e, 0xa30b99c1, 0xadddc83, 0x443f9baf}, + input: "f28a71efd95e963e5e0bc0fcf04d8768ce93cb55dc73c32e6496022e214596314b7f843f5c7b136a371c2776a0bfbdd534dccbe7f55e9d3d3b5e938f2d7e74393e4caf6c38fa4b05c948e31dc6a9126817fa3d7892c478f75ab9f6ab85c0e12091bd06e89c7d3ca8d9dcdd4c21fead3d769a253919c2c72dd068474ea322b7e71cafa31684e05a63e179e6432fb70661792cc626a5060cec9e506b35d9286f15dc53cc220b1826314eec337dd8e7af688e5950b2316c30516620569ea65aab", + output: "1bcea54b1bf4e6e17f87e0d16388abe49b988b9c785b31f67f49f2ca4011ecd2ad5283d52ef707dd3b803e73a17663b5bfa9027710e045a0da4237f77a725cf92792b178575456de731b2971718937dd0e9ea12558c3fa06e80bbf769e9799f7470db5b91476d6175f1a6d8e974fd505854c1230b252bb892a318e6d0c24dcc9ecb4861769cd746abab58805bc41c6086a6d22b951fba57b00c5b78f6dcb2831715b9d4d788b11c06086f1d6e6279cd130bc752218d7836abc77d255a9e7a1", + }, + { + length: 198, + nonce: [3]uint32{0x987e7c58, 0xcc839a94, 0x30952e60}, + key: [8]uint32{0xe34a286f, 0x4adcd996, 0x97168712, 0xa82dde8, 0x14249e5, 0x5e82810b, 0xb4a445e8, 0x9579adb0}, + input: "c1d1ede73bd89b7c3d4ea43b7d49c065a99f789c57452670d1f92f04f2e26f4f5325c825f545016c854f2db2b3448f3dc00afe37c547d0740223515de57fd7a0861b00acfb39931dc9b1681035d69702183e4b9c6559fb8196acbf80b45e8cc5348b638c6d12cea11f6ef3cc370073c5467d0e077d2fb75e6bf89cea9e93e5cf9612862219ca743ef1696783140d833cd2147d8821a33310e3a49360cb26e393b3fee6dba08fcda38d1b7e2310ec1f715e3d8fa0c6b5e291eea07c25afd5c82759a834a89cc5", + output: "11a8493cdc495c179f0d29c2b4672997205a9080f596ee3c80d79b55162b1c875ac18eb94bf2a9e05b08024f524a1e9665912394a330c593d23260e6bdf87620c10a48f678693196fb744c49054182fba667c601e7b7ebf0f068e8d69ba004b804fda616a4a0d5350e1a3bd424b8266462be282308219c578569aefc1ccd09ecdf5da283356c9e524e14e69d25b0e19643dab26f54373a7272b43755c3f1ddaee6c5fb9e8e093110c41697e95f73a68c75454e050239197c9fbd8cec76698bd11894ebf6e2b2", + }, + { + length: 204, + nonce: [3]uint32{0x851f025a, 0xe6f3c800, 0x85ae7530}, + key: [8]uint32{0x2d0dbe47, 0xda05e465, 0x42f6b3b2, 0x7026e79e, 0x9e446680, 0x691df976, 0xf7b23da2, 0xbb3421fa}, + input: "37b2dc4b6a5203d3a753d2aeffcdaed5a7c1741ed04d755dd6325902128f63b6981f93c8cc540f678987f0ddb13aae6965abb975a565f0769528e2bc8c6c19d66b8934f2a39f1234f5a5e16f8f0e47789cd3042ca24d7e1d4ddb9f69d6a96e4fd648673a3a7e988a0730229512382caaded327b6bbbbd00a35df681aca21b186bc7ac3356d50889bbf891839a22bb85db4c00bfa43717b26699c485892eb5e16d1034b08d3afa61f3b5f798af502bba33d7281f2f1942b18fb733ca983244e57963615a43b64184f00a5e220", + output: "b68c7a2a1c8d8c8a03fc33495199c432726b9a1500bc5b0f8034ce32c3e3a78c42c1078e087665bd93c72a41df6bfa4e5beb63e3d3226aeeba686128229a584fab0c8c074a65cef417ad06ab1565675a41cf06bb0fb38f51204eccccb75edd724cdd16b1d65a272f939c01508f0385ca55ac68a0e145806317cc12e6848b1124943a6b2d99a8c92083fc5f31ab2e7354db3f8f2d783dbf1cfec9c54f8bfcb93d6f28ef66f18f19b0fab8836458e9b09bee742ba936cb2b747dd9dcf97ca7f6c82bf0af6f1b433592d65143fe", + }, + { + length: 210, + nonce: [3]uint32{0xaebfd97f, 0xf583442d, 0x15ab2f1f}, + key: [8]uint32{0xd3d1cf9b, 0xe43187e6, 0x5071a757, 0x412a83b4, 0x3f27716f, 0x17fdc488, 0x271f77ed, 0x6c4bb056}, + input: "68c2c5612912b5f994172720130dff092ee85a2c1395111efa64d5a281ca864d3db9600e685854d81c6de7e8747b92fb7c4c2efa829d3d4c0c9fc9d689e2e5c84c9eae8ba4ab536fb6c7523124b9e9f2997f0b36e05fb16163d6952eee066dd22fb7585925ffded0204cc76818bcead0d1f8095ca2cf9cd1ddcd0361b9c9451940e14332dac4e870e8b2af57f8c55996447e2a8c9d548255fe3ed6c08aedaf05bb599743ecb0df8655152bbb162a52e3f21bea51cb8bf29f6df8525eb1aa9f2dd73cd3d99f4cca31f90c05316a146aab2b5b", + output: "d0ae327fa3c4d6270a2750b1125145bdeef8ab5d0a11662c25372e56f368c82c6f5fc99115a06a5968f22ffe1e4c3034c231614dd6304e6853090c5940b4d1f7905ef4588356d16d903199186167fec57e3d5ce72c900fe1330a389200ed61eec0bdc3672554f1588ec342961bf4be874139b95df66431178d1d10b178e11fcbd26963ff589d5d5faf301b7774a56bbfa836112a6ea9c3026ebdd051085f9131132c2700674bef6e6c2c5b96aace94eb2ba6c0e0aef0eefa88995e742ca51ac50af83683b801b7c2c5af4880e2b344cc5564", + }, + { + length: 216, + nonce: [3]uint32{0xf9e973b8, 0x2485a6a7, 0x2ea7dee6}, + key: [8]uint32{0x96edef11, 0x8cf57f26, 0xb6e3a83c, 0x9ef434c6, 0x4607ea48, 0xace87e4d, 0xa0d87475, 0x3a9c9458}, + input: "fed3d1efa309c8b50cb9da02b95167f3b77c76e0f213490a404f049270a9c105158160357b7922e6be78bc014053360534add61c2052265d9d1985022af6c2327cf2d565e9cef25a13202577948c01edc22337dc4c45defe6adbfb36385b2766e4fa7e9059b23754b1bad52e42fce76c87782918c5911f57a9394a565620d4b2d46716aa6b2ba73e9c4001298c77bfdca6e9f7df8c20807fa71278bd11d6c318ed323584978ad345c9d383b9186db3bd9cec6d128f43ff89998f315dd07fa56e2230c89d803c1c000a1b749107a3159a54398dac37487d9a", + output: "6a95fba06be8147a269599bccda0ce8f5c693398a83738512e972808ec2f25bc72402d4bcd1bc808cc7772b6e863b0e49d1d70c58fcf4fcaa442215eeb3a4648ade085177b4e7a0b0e2198f0acf5465c97bd63f93781db3f0b9a0a184c3e06a76c4793a13923f83b2242b62511c2edff00b5304584cbe317c538de23785d2504fae8faabee81c5315298186ce3dcbf63370d1ccd9efec718cbc90b3d2e0b0b6aefb3a9b31e4311f8f518be22fdc2b0f00e79a283701c53f6936dd63734ecb24480d5365d1a81392498faf9a1ddee577007acc5f8c87895be", + }, + { + length: 217, + nonce: [3]uint32{0xe3bd4c44, 0xa3b75a31, 0xfe92010f}, + key: [8]uint32{0xdd05ab8b, 0x5ac7cd1, 0xb8113720, 0x53524706, 0x8e0ceea1, 0x52eb23e7, 0x1c85730b, 0xb33914d5}, + input: "d776bee5625d29a2ebf6fec4df94d2b9ac62e8e7c56704fd38a87ee932b787cbc555621535e76ea30183cb0ee30604f485b541f45feb8c01b9750d37fded5cdffbbc34fb90fdc9c7c7ddf949a1d50b796f1ea5db437238c7fb83c4b22c9e491f75b33d84746f1cd10bfda56851b8514ff0ded0adfd5092a66a85202d06bd967485d06a2c56011110da74bf40b6e59f61b0273164744da02ce2b285d5c3f03aee79eea4d4503e517177692ed3bb035071d77fc1b95c97a4d6cc0d41462ae4a357edf478d457c4805fa586515614697e647e19271091d5734d90", + output: "60e9b2dd15da511770162345251edfb15cea929fb79285a42f6c616dfde6befc77f252e653b2d7902a403032fc4ce4934620931a2ec952a8d0f14bf1c0b65cc287b23c2300999ed993446eb416749bf0c9c7dfe60181903e5d78a92d85e7a46b5e1f824c6004d851810b0875ec7b4083e7d861aabdd251b255b3f1fd1ee64619a17d97fde45c5704ab1ef28242d607d9501709a3ac28ee7d91a3aac00cd7f27eb9e7feaf7279962b9d3468bb4367e8e725ecf168a2e1af0b0dc5ca3f5a205b8a7a2aae6534edd224efa2cf1a9cd113b372577decaaf83c1afd", + }, + { + length: 218, + nonce: [3]uint32{0xcdabfd50, 0xd10d5b99, 0x9e160a85}, + key: [8]uint32{0x8231a4e9, 0x89f33c8b, 0xf96b11b, 0x853cae9d, 0xf6624a33, 0xee9523ee, 0x28bb7853, 0x688ac6f8}, + input: "4f57848ff5398e61bcafd4d4609bcd616ef109c0f5aa826c84f0e5055d475c6a3a90f978a38d0bd773df153179465ab6402b2c03a4bf43de1f7516eb8626d057ae1ab455316dd87f7636b15762a9e46a332645648b707b139e609b377165207bb501b8bccfa05f1bf0084631c648279afdf51c26798899777812de520f6a6f0d3c7f3ef866982f5d57f9c8d81c9a4eabb036651e8055a43c23a7f558b893dd66d8534bf8d179d8aa7d9e8987cfdaaa7b5a9381ba9c79d5c1161b1bdbd30defdd304ee07f19b7ba829a0d5b40a04b42edd6407b68399caac69069", + output: "e096cc68956ed16d2dea1154a259e01647913eeea488be0b54bd1816c781a35e161772ae1f7a26b82e864ade297a51cc9be518641b2e5f195b557ec6fc183e4e5c1fc01d84fe6ca75e5b073af8339427569b1b8ee7fcff0ffa5e7e6237987c40deec0abf091c06a3b28469c8f955fc72e4f3727557f78e8606123e0639dff782e954d55e236448f4223ff6301accda9f8fa6cd43a8d6381e5dde61851a5aec0f23aeca7262659bc793ce71fa7992f80e44611ae080b7d36066e5c75c30851306f0af514591d4d5034ecdf0d6c704bfdf85473f86141c9eb59377", + }, + { + length: 219, + nonce: [3]uint32{0x67de323f, 0xa0442ac9, 0x9d77b1d9}, + key: [8]uint32{0xca8d33d4, 0x834349d9, 0x5e68d581, 0x99a7c30e, 0xdc7f6038, 0x697e8b63, 0x284c2ece, 0xee3e3bfa}, + input: "046a61c0f09dcbf3e3af52fab8bbcded365092fad817b66ed8ca6603b649780ed812af0150adbc8b988c43a6ada564a70df677661aff7b9f380d62977d8180d2506c63637c0585dcef6fe3f7a2cf3bbb7b3d0df7769f04bf0f2e3af9439ab7615c304b32055aea0fc060890beb34fa9f90084814b6ed7363b400dfc52ee87925c5b4a14a98e3b50c7f65adc48c89ddd6414626c5e0bdefabab85c4a0e012243e682d4931be413af62fd7123ab7e7774fcae7e423bf1d3a31d036195437e9ea8f38aa40182daa9aacf3c9f3d90cc0050977c6065c9a46bcca6ba745", + output: "cd5a6a263e3ee50dda0e34c614b94c3ec1b14b99a2f4095a6b5715fdfc3449fcdf8a09d1ae02d4c52e5e638f1ee87a4a629f99f15a23dd06718792f24285f5a415e40f698752c697ee81f2f9248da1506ce04a7f489f8e2b02e6834671a2da79acc1cdfb78ea01822d09a1c4a87ffa44e56c4f85f97507044cf946ccb6a2e06e2917bac013f608d75ee78fa422a5efc9c569226bf7068d4705fde3a9fad2030256db0acf9a1d12666e0acf9f5346ad62e5af4c01a008d67ab1224b3e98278d073116ff966cdc779fb3aff985ec9411a3eefa042d71dd4ae5b15d5e", + }, + { + length: 221, + nonce: [3]uint32{0xa36a3d5a, 0x1747a05f, 0x5440eb4}, + key: [8]uint32{0x2d701ee6, 0x143d5a1a, 0xbb67b9ab, 0xabc88ccc, 0x20baad8f, 0x6507e48b, 0xdb1e1b39, 0x9e521d80}, + input: "af516216f74a6344cbe458cbba820f7e25c0b10aa84b790da2ee6317e059171076d7246c2878be83fc00c200d546c007f849e4c163d52c7b0da31beff4abff481be3266b92e668cf4dd1c84d9d7b3e5191dcd6ddb51d17d337621046e83e9ac035fccfb239648bc3c6fd340fbb50707e5a33b3ef439d292192d0e4bc727690c61450e5a28789e5ea50e746bc66d039493e080fb70e9ae06d89004cb71de8178941c422f1e9862492fc9149a4864ff52b1277b9f5a63c2f16e9adb5263cf65a034a62ebb0f1a385d2681c87a35f1c45670b4edef1c68fe9544fcf411d95", + output: "b22ffd8f0e549bd3e0206d7f01ff222f92d39b41cf995a331d5ef0cf5c24bcc3ddb36e64d351b5755400246fe4989b5f912e18daa46cdd33e52dafbd2872f16e94220b56315d72c1dbb1525fd34831d7202970c11711ff36de3fc479407c34fef0aea86e172f9beb0f393194355b9dd59625639f4a6bf72ba571c229f2fb053c1114e82793deb2dfe8232f1a327949689d2fb2820662dcd2a39a2546c7df12b3ff7e87e58c74badf568cddebd3c558f0f7874c834c4b8aa988653f138ec79620f5e3ed737690928a30f981dca9f2920ac7307607063b40f87c204de47c", + }, + { + length: 223, + nonce: [3]uint32{0xb92be022, 0x1e1257c7, 0xad7c01e}, + key: [8]uint32{0xca1dbb9c, 0xaadb9504, 0x77b8a95c, 0xc50deb5e, 0x2dbc0fb8, 0x9e654bc2, 0x94d8925a, 0xfe9cfb66}, + input: "a3d70bdb509f10bb28a8caab96db61652467cf4d8e608ee365699d6148d4e84d5d93bdabe29aa4f0bc8ee155f0b1fb73293c5293929eaacdd070e770c7cccfb2de120b0c3811abeeddaf77b7214a375ca67d618a5d169bb274a477421d71a651cfb9370bcf7e0d38f913754c11002089cf6cd6a8de1c8a937fb216591d57b37efdf3797f280773950f7eddeb9c3385c8315ff5ff581c64610a86ada7ff6a1657e262df94892dff9fdfb6e958d101f4c26296470c138dc4e1ca4bb565b3ff877a7f78b3d11d64b7c24e27ba6f6b06f6e368f5ac218cd5d11b815ab0987678eb", + output: "646314264896a6e25601e536f6e783d465b2ead1e0be4422bc9cc8eacabae4a749ad533eb28091be8397328dcfb34c92006bbda930ab070ed7b806095bb1c8f476350e7b08ffbd4d7d6055c8defaa8deff9d54f5215c2d7db27ce09e08f5d87a859145ea3126e2a01882921c3fddef3985bd451bca44063258390aec8ec725b07d064314fe43a9c83e9287b47616dfefbf539b82da209aa08a6d3176b7e3b4be4a17d44e581280a684e4a64414649bfcea82b541729f8178b580e8b972a89f5b8c4f9b68205e9396d8ae5e81873b61da074080fd44c52d50fb0880ee9c35da", + }, + { + length: 224, + nonce: [3]uint32{0x5091927, 0x661c75ba, 0xc23dad}, + key: [8]uint32{0x2e00499d, 0xafdc63db, 0xc3c62efb, 0xb4157a19, 0x84ce8b13, 0x85326279, 0x2ee71e9d, 0x318721e4}, + input: "f48b5ae62f9968baa9ba0754276cd8e9dcfa8a88e4571856d483ee857b1e7bc98b4732e81f1b4421a3bf05ab9020d56c573474b2a2ac4a2daf0a7e0c3a692a097e746d12507ba6c47bec1d91d4c7cfc8993c6700c65a0e5f11b1ccd07a04eac41f59b15b085c1e2a38b7d3be9eb7d08984782753ae23acdafbd01ae0065ab9c6d2a2d157c1fc9c49c2444f2e5f9b0f0bbfb055cc04e29b2658b85d414b448a5b62d32af9a1e115d3d396387d4bb97ba656a9202f868b32353cc05f15ae46cbe983d47b78ba73d2578a94d149e2c64a48d0c1a04fc68baf34c24b641ea0b7a800", + output: "b9af1016275eaff9905356292944168c3fe5fdffd9e4494eb33d539b34546680936c664420769204e91ead32c2bb33a8b4868b563174d1a46108b9dfe6d9ac6cc1e975f9662c8473b14950cbc9bc2c08de19d5d0653bb460bea37b4c20a9ab118a9550bfeb1b4892a3ff774e8efe3656adcdf48239f96e844d242525ee9f9559f6a469e920dcb3eaa283a0f31f5dfac3c4fac7befa586ac31bd17f8406f5c4379ba8c3e03a6992a1915afa526d5ed8cc7d5a2605423ece9f4a44f0c41d6dc35a5d2085916ca8cabd85ac257421eb78d73451f69aaedeb4ec57840231436654ce", + }, + { + length: 227, + nonce: [3]uint32{0x5d6d997c, 0x9d623987, 0x5742de36}, + key: [8]uint32{0x57b2a5ea, 0xc5bdd68b, 0x99c7b0c6, 0x26aea960, 0xba5c75f1, 0xa904cf6b, 0x685031de, 0xa0f0e99}, + input: "b39101601efa2ecdf41878b0fd920a3005ce709e4ec2970abb76e32c232ea21069f81b246eda75aace7555ce8ae203455d3723e684bd671389300e353eec0d2f499d10654fafda2e7a69bfca7198eb172249167ca8864b5d5f58d28723090ec86e251a1bac0346d52fd81f06e0c05429e0b2b895588290b7d00878a4da3378eb6c7e61487de2b318fedf68fa7ad7c88ee746827c1f60d98c7716f3f9695c5ffd4670f71a0fa78a1fb554ba482c5de83feaed7c65fc71acc9f541342eb8f7622b12bb2cfa222fa2ddd8b3ed210ce442275afa3132c8a0e17dd504ecbc92525c118952be", + output: "50eb5b21c179a03b9a822f0075906a3ce4acc32486139f92635c7d834f69071d5a6dc0e15ed06a5cee37147071d59641d140a82ad5815b954e7f28e080c3dbbeaf13943d7b7c66d49d51ba1132eeadd4cb7a7e7d726d08d95f1578d55519f267f753f3e16ff39504a87b2286d8bfba0fe6bc28887b466bf276453a82cdd0abbbbf08db0e1c26c317d50ad9b8dc09cd621bc566d362024e8404739df6468869d2125c58b25d70e392f5e75924c4341be81c263915bb514ad436fb24c2c67450e84f6d1b72d1a02a3310c07a7814d930264fdbbf5ddca7067e18e8a44faa87169b7f2e35", + }, + { + length: 233, + nonce: [3]uint32{0x75bca707, 0x89f6d1f4, 0x2a6f657a}, + key: [8]uint32{0x949f42cc, 0x2b5d3c48, 0xfe0be473, 0x17ac92aa, 0xbdc9d9dd, 0x74f9df26, 0x26487508, 0x7c7b41a2}, + input: "0a42f63b975ad0e12a1e32615813dfd6f79e53ce011e2a2f0534dd054689f8df73a8326fecfd517ff7fe530d78081af66c3a8c7c189eb9d9efed1e5577b5512d42ef1fe273f670ce380c64bc62e217a7e410a8ed89998344e29301e4e053a3a3cf7e71587fd056a6bd976f16e157476a06997dfaaff32172dd84190570621f2221420c0a0ea607ea756e9792c8c0e7157c95b89c9490e20b750ee85e4c27c9b8f409e848ec90afcad33342010bb9808358afbcb3d9b094127c38c243a204e76899677079758e7cbada9a5c18363449eebc07bab516a16372722403a046df85c7dd2ffc804c54d38aab", + output: "87a47bcaa1c1eb8e55151011c4f39af4b9e108a55a7124cdcf66d0dee727306e6971f783b038bd6b215f530cdbb53e17975742ec304fdb3792a88b674504396978c6a5e4a9c87a7c3ca430d61165c1a3f6162eeaf38c93e18b6ccb6a595ad428cdc98efef8f84463eed757a72ffd827b71c0579fcc1f4baa11812be2bc5a2a95df8e41d04b33343df09ce628c367d1f88488f7a2787f013c8e76f0b9257cee777ec4adc6df8c5790e41ea02da85142b777a0d4e7c7157a48118046935f8888b5352d1750bf00b92843027a349cf5685e8a2a2efde16dcf5e1c1ed8c779bb38cabfb42ec4dd87d58273", + }, + { + length: 234, + nonce: [3]uint32{0x5003a4f7, 0x40bd8cde, 0xfe35fb25}, + key: [8]uint32{0x576e49d9, 0xe84e9df, 0x9f227a3, 0x437c9de0, 0xc46ac8de, 0x1a6a2d2b, 0x42ab7684, 0x4253fbb6}, + input: "abeff48fa082dfe78cac33636c421991b0d94c3bc9e5bd6d22763601a55201fa47b09ce60cb959ba107020213c28ae31d54923d1e74ab1d9ddc2762b2d23d8c6961d81068230884a39682fa4b30676ffec19319362c075df0b879a0f083a67b23597bf95c4bb997fae4736479cb8a9c00520ba2f6e5962d54c313c576180d17779ff239ad60f1f1373627770d50a1c49718b2b2e536846299e052f8c1a5d3079e91cb1b8eac4661daac32d73b3b99e2051f8f694a61d1e9d3935f802921a4d979b6ade453cf30d73a4a498a6a2c5395c60fcf271d50b4967ac12b0d7bf818c2679d552e9b3b963f9f789", + output: "a0d11e732984ad575570ed51031b8ac2d7b4c536f7e85f6fce9ef5d2b946cefe2ee009227d6747c7d133ba69609f4a1e2253d0eb59d1f930611e0c26a7c0cf2d2ce7ccea6e079eadf2eb1acf0463d90fb4b3269faae3febfc88cb9fb0873d8b74894506199394c8e44a96e6b479bd3e045749cce1c3f57243abdb37e67084eb573cd820c6cee424227019592a027e9da8f7b8997bfb292627a986f83c8fb8d156a91a12a8b52659cf9272924631745ed3a2453a4c2d87a167faa9104e799c715ed597bcb66949ab15dae29a86ba147507e8d8af66e96c09c53caa053ad3b79d9ed3c0c6c00169eaec3a3", + }, + { + length: 237, + nonce: [3]uint32{0xc6ae48ce, 0x26f0906f, 0xfd8ab8bf}, + key: [8]uint32{0x42b82c50, 0x7f519e0d, 0xcbb95098, 0x6f75e532, 0xe2c9f61b, 0x5a4af942, 0x2679777b, 0x6a8e1c9c}, + input: "a77b7a5870335b9145fd2e08ec898ba2f158fda16e8a2661a7a416857b6ba6937b4843ecaa79d3635d28383af80290842de9ca0bb621ee22b7fd6bf379922741e812b1739c33dd6923d0607826fc84d46bbdbd1fe9d1255f56a167779a560a6eed1b9c9579b8f771147df467e67a070d9e9ce8ad92dc0543d1c28216c1dec82614ac5e853ed49b6abac7eb3426ef0c749febce2ca4e589d06ccfc8f9f622ede388282d69ceb2fd5122ba024b7a194da9dffc7acb481eabfcd127e9b854be1da727483452a83d1ca14238a496db89958afd7140dd057773ea9a1eee412875b552d464ba0fab31239c752d7dd3d9", + output: "b330c33a511d9809436ab0c4b84253eeda63b095d5e8dc74803de5f070444a0256d21d6c1cf82054a231b43648c3547aa37919b32cfd9893e265b55545be6d7cd11d3f238ef66c3c278fcccb7dd0dc59f57750562cb28da05d86ee30265ff6a3991a466ba7e6208c56fc8862e19ac332e5fb3cbcc84e83a6205dee61a71acd363a3c9de96d54070a69860c152d4ceb9c4b4cc3b878547b6116699885654b11f888dc3c23483a4b24fbe27c52545c06dd80ab7223d4578ab89bff5f9cbf5d55b19611a5251031df5da5060a1f198226c638ab5e8ec5db459e9cd8210f64b2521a2329d79228cc484c5065ef8a1d", + }, + { + length: 244, + nonce: [3]uint32{0xea38678b, 0xc41eada, 0x3381147b}, + key: [8]uint32{0x268fc2ac, 0x21297e86, 0xdf9ef8cf, 0xd4b45234, 0x2a95c4f2, 0xcec36ce3, 0xd5fa38c9, 0x7dc43790}, + input: "322d634bc180458123e10d0509870b54e0f0a3a72a2bd9e9cf44324c7a1ca37dd6adf9db1fcc8dadabd881f91d47d93b58382802b42ee936802fac8612ea4dd9eca5f215935ea9ba6233b9c8bddba3385861de669d95c888c8977851cb305db577a4eb2360f362fa459d61ffc8fcaa1502905b073bd8e9567ac7cff8e5fb1002c55641a3af5fc47ac0131fae372f073e19721ffcce9821e0241d7fa67bfc499c8f100e050d39bd4d7cae4557d208629603ec4a007852762ec1905d0e81b873510fd334dedcd9c288eb8415db505913af06bea94d197ab627d58f6a9944f6c56247595fc54ae3f8604aa37c3466f74561131e11dc", + output: "edbfb1090987762f75eba2439d746cdbefe8605b8ebad59e075d28b54edfe48813ccae891f6ed655c5ab5211ba896fff0c8e09bd1554aad987dc53f355d0822e9b0f524a99a79c68a9f3b4e30506cd725b07be135e4540078be88dac64fc545c433837b96a924452f6b844291c4c3fb5f8cc94f06d9f19dad7fc945f093020e82ed19f9eb3ddff68b813629991d1a460e5455e1cb41cf23bb3d96fdb6b96581c3bf9ef72814406329bbbba5b835e7724c728cebe88efcd996dea71d0fd5c53e081c21ce8b3764738d693e390fbf8e0137a716760fc9cd2014cd9bf3fd706bc3464d1f15803606976e96b1077cda0a62921ff7c32", + }, + { + length: 250, + nonce: [3]uint32{0x883ac584, 0x8fb8e7d5, 0xdf07de66}, + key: [8]uint32{0xc7747e47, 0x853d88c6, 0xbf9aa631, 0x78f16480, 0x7c248080, 0x15ff973b, 0x31528a40, 0x629686e5}, + input: "e6b8a9012cdfd2041ab2b65b4e4f1442794fdf1c3685e6622ce70f80b9c2252ba6d9e6384d474a7622053d35df946a3b19408b3e1712da00525070279ce381359b542a9ad7c07750e393e0834593777352c1f7dbc84cc1a2b1eba787377d2cb1d08a7d20e1393d44022107acac5d765be37f9075af02e4bbf8e60ceb262aa34e2b870cc7adcf54329a667249cb4958393bff4f4333338cae45cbca419d59e605aa0cecb1241080339198b9b283e4201afc07360b8ae2a57b0b9b97167c315f03fd7a87a00ae73f91ca560a1505f3cdf04576b9aee5ea775f719916f1e1942ad5311c7f87153f8e62855ace3f34afb08d4d7c7f4fd2bf83e42f76", + output: "fc2673c80812d101bca7a2e0e105fa449550e695a016596f5c3cde11fb7dc518b94fdb74058e634546a726c37896110e1d1f9cdeccba1c89958041061ded8e8bc2751ec6dad76a305e70c57f9c81a5a65b5116390af4f7bf7053a03ec13f5d60a58cc5ba61f8c46ef6d2d291de490082dcfdf294aeb3a9414d64e4bd6497d4625acfa591627bfd98f0aec7e7def71515c09942db6911d73b96b4bd2d6df03bb729e945d71549d40e4bc401e1f73baf263a74280537692240638619f92645a5ade1eb8151191c7ff8bd715b3c1cd667e69745b806e16d46d9aa680a7367b8fb45a1598631cf3d44c1f5cfcd95bc8dafdb65a2083905a6937fcf21", + }, + { + length: 256, + nonce: [3]uint32{0x79cd7a62, 0xae619be, 0x7d96d236}, + key: [8]uint32{0x7dec8e64, 0x9f12b14, 0x6c70df2a, 0xeae0aa0d, 0x27b1ac14, 0x7a00d833, 0xe63c0aca, 0x189438e2}, + input: "0cfd93b195e37dd15dfae83132c24ed5bfce7fe6fad4064b213b2c31a39e39ddad2f977e904c9c5b055ed03db46fcdd845bbb6ff0ab5a8c92e89295b6801f36ae63eba61fba24a3858aeb36f2da226b23b24d7b2c7d2670f23a9a1b60db85c0ecee584bef1b00e42d10ca17432a74bbb220d88356d82c850da4c09dd5baf413caf8f9479e02a330065fb865489c0f59605d56146ec8434182345de2d15e2a1dceeeee2fe94871d41913f6788738947ed9849ca0ae985e3e19a97bee82b96feeddceb196c9b6012264661945981c279f43db9599a4ef01116f592478619690daa64387290484d21e8d2444751194e1f361fb37f04014a3c7e4b409e5c828d8990", + output: "0502848571d1472ff10bec06c1299fad23a2cb824d88bf91b5447c5139500bd837a2fddc629e4a964e84907c1e6740263f1fef4f5ed41062982c150d9e77a1047b7d86c0e191945e8db00ca3845a39560857fc9e0e4a394eea4ba80a689cb5714c4bab7124ffdbfa8bbb91c3eb3caa1621f49dba1eea3ebf1d547ee337f9085638a12317b86c11aa1525813445107038942fc519eebdc1b98d313ad822bf0b94a054259aa8cf1be4b3a68f974269729941747f9a23fa5d83453071b431dac62274c24f6a32248b0785ff90aad5840fadc89af0aef7553d9352cfb00d3999ffbe28cd9fde7854e95710f4532b8bf5011e518c93361e58d22a2302182e00e8bccd", + }, + { + length: 268, + nonce: [3]uint32{0xb7581e00, 0x9a1bba92, 0x64356674}, + key: [8]uint32{0xdc2c9fcd, 0x5e50de1a, 0x8546466b, 0xc1b49b21, 0x36a670cd, 0x2887f367, 0x2fbf4300, 0xf90a0374}, + input: "0d8d864010ce8df1c0179cf0236dce1c100f9c115eaa5294c24a2e1afa27f9d57ebc18f00482be0218d44262bd4db73002ff53c6388f5e333470aced2a42a73b376686c8d02e05ece27cdd8b1e3f675c715981f8b656d68d0e16227b529cf881d2433e4371fbcd933eaa72346e77e688ac80ee95324512c66a4c16338cf38c941b72c21c3d01e005a07c0eb436014fb1ee61806de7e96842ca3217ab8c7607d609dd2f637f9fda8a85cb0549f262c9e4a955c384319a6ad2b696e2593d7d174f5ddb98e2a8d5d12558c18ab67571e9a0202e91ce26d720cbe41a3a6a4f309296ca4d9d9a59a9043dd2e5a707ed7d5034023d5ea06ab14b39b7852e5c984848d5670c6f2f0b189c2a8a4a4bca", + output: "d2a5693c9d503a8821751d085a0837579233e65b691366e4a7464481d22800e786939349f721a815f28b4e47c8889f0814fb95d592d1185e45d6dbcac14ffa4f1d6c79194f2f7eb7323439d9607edf80f01e3a968b483eb93c01d9cb9d3625d21d66927e7aeedc1d9bd589560ed2b61cbed5ad0e0310c8ebe140c64c67d4909c010902d5386efa359ab60a9573493d3e5d8761cfd4023eba23de48372032d4673b5f6ad66cd0dfab02a73aa81f269ae88fcabb3ae9cb09f6bf60fd3575a3046bc6843f444e1e9fb9ff9b991620344fb99da68df09496b40f8b9dfc34e830a87f65710940603ebab554d36e8b4c9228bc9c26c07b828f34cdfdd40b161717236ba325e8c20bd018b324345e09", + }, + { + length: 305, + nonce: [3]uint32{0x2c641fcb, 0x5170c7e2, 0x62a23688}, + key: [8]uint32{0x5aed5915, 0xc5c4cc18, 0xf0e51574, 0x75d894c6, 0x1b7082d1, 0x5d2ea1db, 0x709fd24, 0xf5f69898}, + input: "07c50a69e168e388caf6f91471cf436886a3de58ef2c44795d94fba6538add8d414d84f3ef0ac9377fd5bed6aa6805a695f3a711025550bb6f014893c664e09bd05f4d3b850771991fc02f41c7353cd062156243b67fce9c1f0c21eb73087a5de0db0578923eb49bf87a583351e8441c7b121645bcb64ef5960fdca85af863dca7ebb56662e9707d541513bc91bf9b301431423b552e2c148e66ecfd48045ecb3a940dd65694d7fc8bf511e691b9cfd7547fe7bca6465b72ff9f1748723c4eb14f8bc1efb2fbc6726115c597a3881e0d5019335daf2e5ea8796c2a8b893ca798c4ef2639465505c4bd492bf7e934bb35be9b66c9f35730736c65fa4c1a2485378b9d71912cb924634a8e0db2802b75728818dc00fc28effdf1d8a05e4de4608bb6a78bb19c377d5ec77dca1b5ad38fded7", + output: "3dff5fde2ca24bf419e13cb7d12368e70449d41f2aa22e4b567f5cbdbcf3257975e44097deb180f2621ec36acf375dad3b7a19234b9856dc6c7842a7f86be00304b41a8c1662a02e8390346cbd0ff6be7bc1ceb821dbd805ab5c93c9c6ea5093249b5dc52081cbbbe1b326e831ef3c6c42fb791790086d1586f7daf031e70a71b54e9134f942e9ce229fc77980eb80c985ee0c5965eaba375d156f9b423b0615f4ca6fd77de28e28f35aba327e4f1b75725730155b7b4d6c5c264bf3d9dc9a16e7ededcc261add8c666278bac5cf0b3275d6d6678060eae30bbf2ce5f63e6a53a450b65aa0adbd1c90cf045f5ddd9700c2a99c80586c5244cf4c08035b6ff630c82cec3a4fcc83860e987898b42fe746939f8b37c814f8dab65de276e9784fb90f0751d3ba0826889e1e7e4fdbf8a90942", + }, + { + length: 430, + nonce: [3]uint32{0x99b172cc, 0x91056d0, 0x48057533}, + key: [8]uint32{0xe6cf398e, 0xc3c56066, 0xc5ff194c, 0xf6d2d8c4, 0x6d1d8908, 0x63e62065, 0xcca485cb, 0x1eb03dd6}, + input: "3ddcd3c00014747903c95e49f64258615455a0b26c5070a9532382a9bbd18eeb19c9fe1a902f5c6baf544c5938fc256d310a9332223dc3c54a6eb79a4b4091c3b01c798d2800418863f2865c1cd8add760e445588576d4a6c945e1d6d50dc913674daa4737ac94d84eb0ff57cda95df915989c75adc97c4e3c1c837c798a432ba4803a246bb274b032db77e5c1bb554a5342ef2e5d3ff7f102adb5d4e282ad800ccae83f68c4bfd3b6046786a8cfaa2b63c62d64c938189b1039ae1a81ce5c91530772cca0f4a3470ba68e4e0548a221eb4addf91554e603155a4592dc5c338aa0f75a8cc2822b318fbfba4a8f73fa08512132705dae792eed6b809c251d35cca60c476406d964187b63cd59333771e37367671d0ccb393f5b8bde77bebc133485ec5c66bdd631d98cdbee78a3cf435d2f824fa2f9e91e89af28b2e155df4fb04bbe4ce0b6162dcd8e81ee8d5922ebf9c957b26c343a0396d91f6287a4af9e11b7fbb5a5a5c1fcdb186365a20617d4ff5037b0bfa97b6213a6ebcf0b78b81c65737378787b255cba03d715fed4addc2c70c1fb4d3ab16f2bff287186c26a164dae2fe9dbe3c4a2e1617f01cae79f", + output: "ecea5fc18dc4aed23359cacb8f79a457512e0a27d9816f353e315519d2b2faf74d14ae8ae5e227b203823998a47a050c363a807f45f610942fed4518b8091b88dff8b2af8fb6552eb654c85d2b6a918bcf56fb898392941d983b1afd867ef840e12313059ed3e4d217498dd511563a939c3c536fbbf8e019deed29262f0a655fc680b15939475e0cee0ce2e8bab5834f7354b93e2e0958a5bc608fab369b6aee3c9d73a6898e402484eac7300150517bbd137bf55762897696a3dc4be74b0c141755ac8f2f6e59f707b1690c451a774c46bbe195d826a6784f8d807b78f8ebc343ecacf37cb9b1b2fdbff6a1237b5098853d783e77515c419894c2628f8b5117042294ee2ed58a33746f9e79b13fdfaa25a75fc95340a89076e786e0ecad7de437a9a3fb3092146d255005b22895310b1252a3e34572cf74665b97f4adc30dd0f34e3216c7757953a4b618a775bbe68f9e0922d75afc80a1379aaf1745f2263afb6f0b37553d9c984f1ef781ea75b1980c559c77565c83f3e0bd7a3cd7cdb594658beb7e5eb940633dbc6ae2f50383beea676cb6c814b17b1d73dd133f544da88ab371415889ead21803c1ffe3f2", + }, + { + length: 449, + nonce: [3]uint32{0x2adb4a6d, 0x33d00c1c, 0x10a0193c}, + key: [8]uint32{0x8bd707df, 0x70212019, 0xdb685581, 0x9cdbd1a3, 0x7db9ff1a, 0x1af119ee, 0xb1d8c0ff, 0x3c4a22cb}, + input: "93ce72a518ae892e00c271a08ead720cc4a32b676016612b5bf2b45d9ae9a27da52e664dbbdf709d9a69ba0506e2c988bb5a587400bca8ae4773bf1f315a8f383826741bfd36afeae5219796f5ce34b229cac71c066988dbcae2cbcfcdbb49efcf335380519669aaf3058e9df7f364bfd66c84703d3faaf8747442bdd35ac98acdc719011d27beba39f62eab8656060df02fab7039223f2a96caac8649bc34da45f6f224f928d69c18b281a9b3065f376858c9fd10f26658ae21f5166a50fe9a0d20739402eec84f5240ee05e61268f34408089e264e7006a59bb63eeaa629ba72603e65718d48e94e244e7b39d21d85848d5f6f417631f3876f51b76b6c264356d7d7b1b27bbac78316c5167b689eff236078cf9e2e4626a4ae8bedeecbcaf6883e2e6e9304969b4fc7a4280dcdc5196267e9bb980e225fcbf7a9b2f7098f7f5c9edd06f50c8791edaf387ff3e85ff7bee1f61e4660fddd4eaf5ab0320508e3ccaa9823ae5a71faa86bd76e16d862d83ed57bf6a13de046a3095a74a10c4da952b3c9b8fbde36048537f76eef631a83d55d3a13096e48f02b96a5a8da74c287a9164ce03ddf2f868e9ca3119ec41f0233792e64086c903eb9247dbae80e923eae", + output: "bcf49d62dcd1cff9dc37d7096df0c39031e64ccaeea3830fa485edb71b7fcf2ec709a4b327ef9c7d4ea2b35f113a8485d4c236e06b3baccee30e79c6c08739fe5fbed59db30479b56dfbe584a5d79b169b200430ed27072137e940a34170606b31f22095f2151b4d9b901f6337f991a23e4c8997a1ebf5105361fdade1c889b8dc9565e3b33e0bd608c39d725becbb60da8a797186fe0986736112da3d09906442364d6e253e5b27fd5ad72e877c120ea7a11d42b19948f0df5ddabf9cf661c5ce14b81adc2a95b6b0009ece48922b6a2b6efffdf961be8f8ec1b51ad7cfc5c1bca371f42cdac2389cbddcdc5373b6507cdf3ffc7bfb7e81487a778fcf380b934f7326b131cb568bbaa14c8f427920aa78cc0b323d6ea65260022113e2febfb93dcfce791ab6a18489e9b38de281169f1cd3b35eee0a57ed30533d7411a7e50641a78d2e80db1f872398e4ae49938b8d5aa930c0c0da2182bd176e3df56ab90af3e46cdb862cfc12070bc3bd62d6b0387e4eee66d90c50972427b34acaf2baff9d8a76002a20f43c22ac93686defc68b98b7b707d78d0e7265aabadde32507a67f425cbd16c22a426d56b9892bac3a73dd2d2c03efdb22ecc6483f8d1ca67fc7d5", + }, + { + length: 487, + nonce: [3]uint32{0xecf15215, 0x45e31add, 0x56499d31}, + key: [8]uint32{0xf5988496, 0x49bcc2df, 0x7b4ba3c3, 0x5d5138be, 0xd6cb466b, 0xe98c82f8, 0x147d3f27, 0xc82389f0}, + input: "f72bec13b0f0b6f2317118f14c2a0d8e963b1bd49ae7584e710dbde75bb1e30c79281847cb822a5f3ae4fa56825e511212f17f0d293cfe80f872e6992d304e9283d08ce65ceeacb003b36a862c91282a22536e0b9c19953512a1bf9e20d3e7a8f1a2dff45dec0b9b04c592e88a7814540cf636a024d10008463d0b3aafbc4c9359889149433ef173124866aa6f53526ef3b3f2c630860ecdd08ffd9fc050e95da512cc87f812f9391085cdec5cc87258b8560806a52336d612da7ab05e0f60566b950904aa27c975a48c7d78455728c87f9b53aa4978374ab9592e12c22d9a760e26eb527133534ac5bbf969596b71cde8b4ef3587fa7ffa7116834348c275ad4dce68ab3397521ddc8e54380129cc81b981f9b32db20dddb0ecaa0f1ff7b06495a42b4a800a207b8e9ca38794e2fa9f40546e0e3aef7b5236d7fdadd72b1158714a5ad8d6264df1e75120088e449b9e911eddac59f1f19a795205ab7532783a93159876133b3fe3a518475a545fbe8dd2ac143f33c35d98e3ee13b63606b1e671917ac3ff9412773a3ac47b8c6627b8ba9dde6820f4f16c2ed9cb7d7086cfbb0cf2d7533eff253d14f634ab2aad3fb4289b9a0bb667a6fdd0acd5949185d53f1dd2b96ff060bb44f872a67259100669e6eaf1a7e2b11dd5fc35792db0c44a1127765934a068bf", + output: "bb618ae6b7739a4dedde1dbacf864b0892b93dea3007237d2f6f23be0718bdd29321e6b0fcb6a44dacf0f5c53d91e16165997e2302ae7ebc2dbd02c0fd8e8606a4ad13e409a4e807f331cf4174171c5fff23ca232192906b4eefdf2ffb4c65af78be01b0ba7d15b4341dd5a2edd49b17db2812358c8af0a4a9724e0169f50d1d331936bc2400012a60849876c3ead52cc9fe60173c9992f83f3e41ebd24fe3961835109612994c7620280539d483f91ef9a64c16032a35612a119589efe6357fa35b19531274576e304be75bc7e91d58015792095bb00ce4de251a52b946554366ea7ed9ce9317020ec155ae0071e022af36ad10eda5d671e5090c136e381cecdb8bc179474fabc7dab2d8a134772976cf0791b6cebe2333d34b4b8e2b6b2eab2b5dc7c6a08a583d091df64328cbcde36bc1b81095d82c741a1503c55d833d551a855e098166c5efffb8e4146e32e54abcaa85076ca6660abdfca9e82824217b5d3f23f7ff3455872bc76751480c1a8e3e725365c82fc135cd3713cc0f1ea733754142f8c37716a2a4fa8a6b898215c287565325774c2510df6b49e78cb986853ac5ca532c9a7e2bceb7c0157f60433f29fe29009343d6035d7b5892c77f821b644590615dc505604501dd218dcab789e6f0525387919cf25c7c6d62a8979e39d346decbed2657", + }, + { + length: 511, + nonce: [3]uint32{0xba68c47, 0xbc020097, 0xbf7d14a7}, + key: [8]uint32{0x3bbeedde, 0x6e8f4d6c, 0x6e27cd72, 0x140ff360, 0xc891efa0, 0x4aaa227f, 0x733cfef2, 0x2b51f1f3}, + input: "96eb94e1adbcc0646440c8824a2fc0f2c4b17d9cbddbb8ba8d9dbd6482fbf7201c74eb923153e0138b2f6f182f9c3d5656ee40bb7c26a01740b5c7d125261d4e4197614800aa152b402ba581bfbf4288e73c9ef7e7e37491212b921420eaaff880eeb458e3d0aa108b01b53492c97e328e9d10e3220b924351d583c00e76aee9325d6b89b1f162ffa30b386b37b5eaf4dfc25d22987dde4496158818c4d8f19ea300fe140be921d3f1abdaf9ab8946833a57cda5f41f995ff80e98b0f10f7afd736dd33438dfd395547f11563056078ff8f7c202aac262955f0ca5dae2365472de40f069028104ac552ea5a45ff2773335e5d3242f1e62e0e98003333dc51a3c8abbaf368f284536672e55d005b24b7aeba8e4cef23289adc12db2213aa037c797e7e753ae985568199cfe14cf1704fbca443e6036bdd05859e3583897cbefe7a0cf268b75d554b2da6e503ee04b126fbf74eaac0ebca37e84ab9c726973af780fe2bc9869fe67b7d9e4a04062ee535b2c1740d1347224e211b5cd37ee14c3325f40abee930eb6a1634986e756b3a1f86a3d7ee7184d95ea948506d8ab8b23f92ecf3eb0586f7a8b1bc227e08a0e32ca75ca4eeffc5c0a2a623547788bca66f3dc2c48671e462544d52a87d34307a7f111aeacb7da50262deab33d9f29dd6b47c3bb555be598d619cc66be8c4b74b01772725268a43d467f39bc565e5efcd0", + output: "590965d18ebdf1a89689662cfae1b8c8a73db8b26941313006b9b9bd6afa6a57149d09a27390b8883069e4fc2dfcf75035def1f8b865e24c21b1a1ed3e9f220d7b48046577b661bc92d9888a912984ad415ea2fc92c9e37da0bef5c7dab11495c612c27b5babe6eee28fd26482272fce69ca7f11bac95251735ad808365ac587830ec04105304f8e440a4da47d30e788718da4282941c9c76f18de4f954b8be750b54cb1145489edf273625a0df9a694a23fe7bfea12579b53c3b2a3de85705568cd7e603f3b8beba9a14cad9979ea283a8a291d3e1105b7f890e2a569804d9b7dd4c7e50bd0dcd11223fd7247af77f04212ece1b98c238d2fa0386a994bc502f83dcdd2e5a0d45b185155e1a395d91726d383c2c198fff1590e983c65ee041638510787c8c59c2e96f31678226a033e027bb40c416b73c3dbef31affc93a659c8ec7ffeca313fd5283a80533b2d63941c8f245d22b160c5fe57c5fa4b759c407b9acd6d9c4f80f244360b9acd11e2b43d4af757e16a6ef9d6756df39ca3a8a235e74351f50b2ebf54df633c8c400fd80b41b07117676d486377095660f2f20f62c034563b4560b473a8f4d6a740306d2a822fd8bd98012a840ba9b1709df9a0d61ecc305f7180fd764e334045d9a8ca23cb8036c05616a8b21fc488429ba4168c59dfa231f0ffa668a3be7b16583df1a55bb9c15d51660ddeca730d66f7a9", + }, + { + length: 607, + nonce: [3]uint32{0x9419df54, 0x4593f2a, 0x71c06dd6}, + key: [8]uint32{0x7b517740, 0x41e86353, 0xed629408, 0x5fe32cea, 0xb06bc5df, 0xaec9b350, 0xc00c2a6f, 0xb3aaf44f}, + input: "be3f309c6e7b89e1ec4a855cf161156d09f8a04d5630534ee19e9e071e3f4603f23f0c59a7b7f8a32c4c203ec8c129a268faba09abde7b61135c6c37fd091e2d695f0e242488098ebed30c7d321f4dcef0bdd23fa85a53569868cf2008bf4d2ee7a12a6673298c7e797321b9f4559748223b590e6fcf17aa72251586b01181cefcd32c6a1a20a0fc27143426f6572b1aab0e7301e390cb857f912d78d5153906c698ee140b36cdc72693cc019cb7add747ca3a07b2b82a2332bfa76c962b186ad94209fcf590ed0f6a73b08a771a58eb9649f2f1da4f7c385da83d50c939231f745514d14b0920deedd9c4dc6d2e547f83643d13541870875e52c610372b14b602e7a47f0b3721cfca60ec68e2eee91f40ceba2d0fdb4ebe19cb1d1ab170726c9e600030454ef355f9a40033672be520e528937f38e7a862a5ae50cd94f667cd015a72ee3f91b1a09031bf4c207e0c516b2e7a4baedf373f1ee71843e560741ed3a3094d2b513e2248caf27ce135716f6887d9f1fe5b11e02c12c989d29054ab183a3f55d9b40d78e12ff56edf936ab966c7c3130bea472b71fd69e70165a76afbf720e2c1587a77943b35acfd81b2ab6f39476623edf3663024fb84da8057ed3a361e9533caf9fc58a5e4897e4bf84f58ed063b5c353bdca3792952eec0a1404149ebeb5b17cd6350ab3e27e44e40fbcb00780d001a48d0365d534ff830553409919608881e665f83bb5cf0736d728c41cc4e985c377f89ee1186303d0d76bc634875ab3ebd87059969f24b0464ae11967bcc47f300a34e3b917b1affceea716c5ad9abf1aa3a1106e2f4d006514dc62cfd2a52426968f2f3991c9f9d8fcd", + output: "e4032c01bcece73fde73961ed216820dcb44ce20134678c98afb674bb03afec2f4aacbade7f87a32fff57ae9213eaf0509e9d9db1313b06fd1df53561f85896ba627cccd2d0e2ae4f24f5579bf02f6599f5e63412ba084cf53a5bc9a8061b5c029b755329fcd73f629fadd3bcf6cb4c572fea86466cb5159d19eaaf0f44c3471d0323bc7206bb514ed8117a61c6d98d44faff6a83716657531d965ba3efbcf067c452e0d2807db3423958d9a4421886fe132d7c47e82086db9507616b67f0051dffc1a49ecce3ca8e4d5f5af15684cd8837a471430ddd333ea0b6ee603b7d9e702692f857fab060ccf26f2a8e61dfd3b12923acca78b83a6004e4ff09113becf6bdd0bec3a449a195559dfeafd4e2a79ead5ae3c993a15ad9b1a2ce818e18edb010b7fece9aa437d85ba9841d89026d6aac1a3a6ab6dad932a26d7db6f3664b06d51584cf4d22a75c06e2840db7292798306e4d39379af85a6bc8dcaebb5246e07fadd5e336f122de0ecb99ca24a971701a1f43bd69933beef6e52d299b132e7510caf27b99739e32bd272afc36755ea80cc7ed3957d91325584b338d15b19fe554ee70bee903babe21d0cbecd49235c70a3a4f516ce16761d1cfcd70bb4b9c7c73c359f3fdd0753d6c1ac1a1463142f18266b6a9c84675f247d56563646fb2c8c3b6b81944c2ba2b76b685ba5ea40cf539bcf3850a8af3e0a69c0b38164de520a3bea82b91f67d36bbd87877b5be7f06c2d26b2dc747a26a51f51fe293197db0e91e6ac617c71ddc6edfeb7db8f067ac2012268deb7e5f00a640c1bbec5c4c71f10f921071308cadededad5c90e72d744d0bf790b043fd35729570889ebe5", + }, + { + length: 682, + nonce: [3]uint32{0x17cebe90, 0xeffe259b, 0xbdf9d4ca}, + key: [8]uint32{0x172d51e8, 0x5b80f5c6, 0xb9c9e438, 0xa56119c0, 0x62212323, 0xf5386589, 0xde7079a3, 0x669e643}, + input: "0aa4fbce7e1774f0607e7ea01fc0e6d210bb283964ae75e180a9f6ff3d2c4d50914bfc32bca6d243eb33551521d54d66f377fdc1d31974ece79b157905ff7e7a9b064f349727ce37c83c15ae13df635c3e6b4baf994d9aa0bb90b06c6cda51deefda72c97a2993448e654b746b216d2b949bff1af5238558205cfc3162f1d7a020a919db4d4eb44bcf7b269d4df57e24133d1e540694b9148444cee16e64035ef006a6079dff449949c1b342991f2a27f21c8bd74ccf4bc944284a46e9fd9f9bfd4b95f80c05553950fabbf5e5aed6babb8427832266aa4d175114de9127ff6ee848534d6dd5aa6d2dc361319863cdf32cfb1b074faed17d368964393352df01fe8d86af0e994bc9dac315f7d9efa7bef47a16676cdf17a535ae71d399c4c11a3a3ba0491e8d41f419685258a4ec7d1ae588b3ca341719c0827ce5f5a653959a8671844f2d0293c09bc7d35497ed18c160fc7b6d073a311b621a7a37f7ded1df3d73dcba1821278c9e17a191997fa4dab0802e1ee1b468e91e4272c4569a17dc0b2805b980bde798640aa328a3605abea1865083d7446e960c27f69d32882a2a2295efc9c440dc203872373411925f8839715e9441d31dd9cc14bab09a3e03b4a63e14db3039d58725796326ea6327f189beecd63955f1409467c81f4691ecfe9f0ac5234f23dfb84e3199e415ee7b4f67189e8857ff6cb3f64c2ac1b554bfbd679a6ea8491cfd69d96d08ee2744d9103e0b044212560ff707974b1a9043e1f2c3592828fde8ab5e993652c00e2b3fdb19082611b67866ece6c4a2635f87e04d2136d679f632416b03ece4d7e9406f3437163f4fe0c8cc7b87d487f6de3b3022665bcafa847c2b9199e1ba9af7deb0e29b66ad41688d03a8369416dfbee6d03526adb3ebc4b4f8531d73589499a3010b5309e9d9d2f5a9cf347983a92722dbf6c4f0bae8aba57b37d322", + output: "a31f9a532f35f20ba604a9ab9989260e5a4ed04e6ecfa1cb9e0e1d16943906acbbb4e761a2bebc86cad0ce8b3f26d98b455e4b0835eb8b43791cea29fe8fa6e5187b60198142059bbce98917aa2957ae2555bee70e6e9e21ff6197a51ac2ca2952c413efec4d9903a2f6883e88aebe7ca8316831f6a8f2cd0e486319b58dc8db862779adff98b7f35c33faa53d56acd7a81e0feffc286b728f3a11afab7cace4c30b1a45780276b1f0ab89242410d07cb1191c7b9da5d09db7c9a729d91ac3ed82f4350f2871a12d125ba672861d1b0af7219c360a0e023a8b7c23fb9d72631c72e032c097118d90e5db0576586d8224165a8376fe8d04de93516848e7c2653cb4f7d24a971ccf4f16c527ea5b4153fad5fd5bf473b15806671854507bf1a6d9e5fe4a6f6ec977197d21d69a041dd955e199031f895adefd850c8b0ae327ba0c18ca1783560e1ff0feb2f659137e34a91e9e9ff04fe3375b7db6e4326986e6265e5fef00297f6ae627c7563846e531762748fe8d0b6baff17acf1e6c5cfefa35a95ef634ff96f83f16342a6c62311fc653d314f8a6de109356ab7801316e69a48834cb6325816b1f66d5c67d6e9c9cbc8e1a0521fd6e4bf77a7d2609f99c9579e143f530677b99d198a97620d087f058edf35eb7271701ecebb8bfde5671641ed21aeee9e7db06b932e0def91be93cf2955159e9666c770cdffa03886eb6e98dfca8f91ff5cef1927c0f82b9226d65c68d011416cbef802c264e34244ead7a6ebbe28a510a37e1276f4f3cf27a3944a08aaa23bd321092761627dae20dc269b6150545c75e995cfee0a9bcedb1ad8b364beb8839fd5c9f7984fa0a08a1a354aebe18f62acf6d6664978fcfda2ce6fc16eaa2cda5b835339001b3b98d3a407a3e18e0ec2da6ee3d1448c1ece2ed67c3f51f01e76ed59f0e61102b103a3c65aea94275e8d1f0d331538efe", + }, + { + length: 768, + nonce: [3]uint32{0xb1c9bd09, 0xdbe6497d, 0x16c73b95}, + key: [8]uint32{0xbf9d9e5, 0x2eede668, 0x631dca95, 0x4233e36d, 0xd83fe644, 0x99b11f89, 0xef055717, 0x1ae9695f}, + input: "e097b1e8dea40f63714e63ab3ad9bdd518ac3e188926d1086a9850a5580affb592f6e421abc617c103479ba39a3924eea1c0bbbb051614c4b5003bbd5fcbb8093864fc1c130748194d6b560e203b889b98b574a98ec3e0e07cb2d9f271ba7794e5419123b4f2ebc7e0d65cd404104868905ff2c38d30c967fe9d77ebdd4b8fa836c3b0ad15e3e70e9a28236d5593e761e694b047f63bc62c7b0d493c3e2528c8af78f56725172ac9416ec2bdc54de92b92a63f9ccb61e686f9249c7cc337d99b2160400bb5535eb8f8eb1e3cafcbceaa821c1088edbacb3b01b5bed977e702de747ad00268ffe72e3d877dd75816db65b5459607cd1b963fe43bf2405ec223ddc0de514d59cde74f7522dc72285caa3eeb7eae527a7723b33d21ce91c91c8d26bf36eeb1dcdfc1e9e475c1565ed9c7e64ef601874a4f277280a5ceec26717e9385aee8b159379e3feed7952b87240c942970d63351259aa7a286ddb4a2620fa67565c92f592902e49422f1eecea2f44d1c0bbbf54a9e5612b86a9549aa3e6639a924c7bbe2d3c1b5669da73c0e2c6f6f6084f54a912ad2635d0141c2f5ac925414dce0da09ab8f86eae2a7b7e48741253189e5fd554d5c04d9807ac6ffd8a4f8229a3e8ab75ca5c778bd7ec5a5c02085faba9792cbc47f9e9311f3444e6544359769e1b3eb4d42ac8923ec94536e1a44497766b5da523f5763749dbc2738dfa8e13c191dfeac56c7614a96bd3ae23e4e6e5ac00be851ac9831108989b491eaade62113c531385ef3e964ce817c8ed0857adca946467682c2f4387fab2f31ce71b58370853171720268459588d5d216faca58d0bebbd7cd83a78445d9b49e83ec2cdb59b5d760880bf60532178d60372752b47d52562b316c7de5c74af9cd588643002d66bc6260595a540d2f82cf2c07fa64e0cdd1f79877b6a25b0608c735a7d35ca10852da441fcfb31061fd7e482a0989866f9eea8b0b39c3d519715c1c2766c3ad99f041143cdb36557ed647403458155dccbb80c3a365f0a85b1135695648ab67ac76b3d219c7b77e49d735c72ac947b1d7eeb279beb9d2602aba7b36ca", + output: "7b6e07e6415660affba56047b988f4548b308e7a642c76791f5c3742cc4cb744cde48fc30e50d458084e06c6dd29a52cb4c306a69a493a17c0838d14b107d07b81c983a2dbad09b80f087ba48465a8beaae5b16e8093e17cfb9e84ea3bdb9af00889268a5c01ddf25af434de56f65882322432aa275fac8519e317ef4d89478f29182143f97350983050f5d37c4b518611da6fa2aed7bb73e614231a194fe17c9073e377fc6ea0aa491e15ca54808e0536c8c3f1bf657283f807ebfc89b55049ac8fb86f89f17974fcf0afc1a2c690c0442842d0f4af9ee29dd960e499d1077bfdad4c0c9189a6e83799bb585acdb853c1e99da7ce9c7eeb9bf431f8d364d0ea80b0a95a7807f196c6ee69fe90e6d1f5d23e5cb256e37e65826d7a111f2272884d6319f968580b3164b2697ea6556816cea3ca316651fe2fd68dfa905d080c28622606f7d24da216289fa2c54c6f42dc244ecb047512ace62f0801f2dfad8f0218f45e2b3bbac97c2176c842398b16dfa1fdfc9a68b7b5a1e785d2a0cc592bc491f5a69c81127b758ee02c66b81674d3135c5882d1dc89dadcffa06f4b0644df5c7fd65c72611d79be7ad637edd6fc38b39946aa2a2c6d08ca9d3ff9a8ffe2e7989546489539b1a623fa937c468e59e0978602526b4367de277526895aa222fbaeae2084f418c5745d8ee844da0baa47f592970c14cf710f49539c12104a62baddb3382f5773dd18c83ecb238ae2e749a51584a38e394ebadd175bf5c3cec787907abb1d94af70ae63d3b7d8d5ff254da90b78ec8fe2ea95dfbc6e3e69ecad856c9e54906df8fe39859f2014b74dc3ca0ee2a957001939d37a6c0b489bd3f1658b835a57b24aa282c23e875c9e67e6eb8b32fe44e7d7d8e285d85da0ce1b53990f9fdb5e2e74728e433ed2c1044df9e89cb9bb316c39fc6fc8bcc74a382093926a288170e857d6b7f47858a4c2d05c74263dc9e8199332d0179687f4a4cdfc80ee6737300cefba75905b22d21e897f887b67aa3051877fff11d98bf96ca5091bb225bddd5eae697f3dfb0efcdb788ebf6694b5b39dbb0d4bf9427382a3a58f0b", + }, + { + length: 828, + nonce: [3]uint32{0xc7e503e, 0xf8110ddf, 0x83316c8c}, + key: [8]uint32{0xfa2d1cd, 0x4fe7f905, 0x2b9e4c1b, 0x115bc881, 0x2922bcc5, 0x3f60aa25, 0x13c26d31, 0x2096af63}, + input: "0a1064714f20d9e47fe53250ecfec759f4137e60afaf65755f4709a483504c3855833b6dcaf7aa0180fd735fa9a73d46697f6c45004adf12452ea4c04a720fd7c20b9783b74b8b3ea0c8b1563d5a85f44af8afd7d91ca6298ca22642a684f66e365edd6f6bdb2dd32dfa13c62dc497fb341b86f65d40655931171416e23e3b2623c0b4a67d448877b6e3d4e0fe284034a10162b2b5e21639047036874f4bcde22b145b5f18aa8ff32dec81e6a5ac68b3c30c24bd8fd3b8e098a1cf202e2ab2a3bb66a9393222b9f7384653cda7707f00bc3c81e9591fd040a07d3629410c2db78781a4c9db3df5f9d648162f1b087974f56a89db07aa21ba827e3864a1618945b2fba06853a13c35da2909f5013feb313bae09870b8eab904024adab0d6ac46c1a1499791b47413139dee59db676949b9e9ab8d3d6abaa954ec2a9fc83953c91b483c3b6bd6700b96484850734e72e3710a1b379c0d0698aeaf68f13a0d317bfd689471e3299288e7a383a58522f0daaff210cc4917fa05f0b8ceefc2afc46148a05a100d30787accfb4da094e61ea6b58f132692aedcabae928e53c2594b01507b8fc2d0a85a1d111d1f4de0b95258281ae01873a72606753b6f878ecd8c4f613fb3477710d260f0bca0d4c06f675ab7113eded395f88755a98a0ad22b4a002cfe9447c4e39eda13738f4eccb9c13367ebc2878257c4647d31b67e5e32b6a77f23e9593658d19c0a40e8a7228767afba1cf23072b013b2d76ee66e42b57bec2797ce3619c695a661004c8129cb5c5d6a2836be22483f3b7e40bf8ac5535bf6cd065c4821a87829948c88163cfe3c0f60cea4e7ff59df4cdbf80064b2d664b39487413039999b5e86f1d467b12682d0cd355e9f7cd980e87d584ddbda89f68632d3b8fd6bc3b80205d7feb97a46842b093f74aa14bb21accda7474247b5e39ac76ef75e9b5b52b6a829a7e2297ab88fb0eb690d54ab1af2d7437149a6202035ce15f1e6c6267458d62677c263d83d3f8119af191b7d766582620e0f08b411c996c25ba6a32c2d73f592e789ed662e94103329bfa5e6573f1116ec04438997f3e4ad91b4123b570743455020d914bde2d8417fb24671e6db261732fb89dda1a36614b095529e4f97374c9bc0e55aa577bfffa663c816ca9fae3472e0a", + output: "b00a7caf5359c5bcebe590e6bab9aa03370050c55cbd45a257f4869937e922a15f2d38121b1493d6b5dd4a8a47d7b4e5cb049d396ad84ed421df774b0408b6939f18ebf5cf83f48c540affcc2a885967bf4bd222c42904b8a73c4185bde3f97e874fad25b46714235e60c9ff53ed2975c9c85ebad0752249e4b627ffa41555eb9074f63a5f7d61d207d2ce11b2a9fa23a13a0832eccb91efa2afd8d9acfee94ac78a733fa156bfea5006da1d0127c32aadbb75c015b68c627903e1c85bf3a1a9f99c6cfbdbb5c871f7f9661b78cf5e16d819f53e9930e201d4f58e69bcdce77ec5b9b1d2cf206a71f744342273c26b9abc71303c20df3d51f52222893d803fc8e0e0afcd99ee1c7f95b48680403566f7f9e296d7ccc0ec348b6ad515af58d11fd82c628ea29ee6a5d67aaeabd8823addc01a078b04313af73105d4ce4abef8e6ee8ce649640a19678292d4f1017d121549fd2c19ba6cdc0b613e512bc9551d759c6d38aea7e35c0847a142e273a16bb1495e652f9668b97801ba3f6d9931c0a1efaa4452e15732dca1ca9cb45ed289e0fd08d1cee1cdcc9dfba8d0b2562b0b1a180f4ee69d63573222c8d4789bf0d63d2a201a70c7b27c84e620e33e8a863cf49b784269a51ead3d4ad26f044d5859988d5485a11533ea805f5a8f6313caa6b421071a34f57170fdd8e4663e9a4cdcdcc1ddaa9f6e651fb365cf827667b018ae7d028c7f96295b2b4f9eeb4b361b48af86463a79f50b107ab0935e3cec3f4f203cea801ff95fb870d2c2f0e315dc8a6a547dd3c390a1f5403917315164bd2d40362489b389a54e8dc0ddb83e6a43a26c65923e6f76ee0ee0e3a33b0a9066620a01f0319e20b9f1beb3910ad962a3000e6aacb0ae57f3f6c5e0315be5de93edcf0e45e0e47332f9daf7f33e6e8bf1929910b78b8f88ca12bf5519a3217b7554c8c8350cc314561d580bf67a3878e3979430d070121a5e070a3458742e8549bda972f603222e2b30eb8a49a955805307e6e02f8c60a08188f69340e116422458d4a8841f46a78c833b1a822e3f6c9c97422c918f17c36175ca4b3d1c081ee4b175b4b07bf101c3836eb5b9e3cbd08a89b4a1c50edcb41ea8ea6ceb1532f5b842715d50dc21e2499e08c373d3dedb96bb477c8802ab7aa957e0b5810f38", + }, + { + length: 859, + nonce: [3]uint32{0xeb02dac9, 0xa7cba06c, 0xc24764c}, + key: [8]uint32{0xe9414a57, 0xd5e29546, 0x1a5e2f4c, 0x806e4c46, 0x48098d1f, 0x4351ca1a, 0x53ed97c, 0xa6a495ca}, + input: "00fa3b13b5cfa9b5d65a41cc2d3c420518802c22c4582873f1ad52a22032d2cef7c975078b199787e852fb1f914529f60d1cc854e5d6d547216dce043e0fc94866bb2193343c3a07fde60e668266d1cee3067c6f2ce0f9f63456ad08094b6c7f515f7ca90caa96494e2a6835ba1f3f166012ad1ff6af6b5f8455d5c26e72402966af9066ca70ad027eed23b0eb02c751195064a62283975efeb29bc5993f83360d012a2f5275ac758a9e8fe458fc7cc0673e6b9e338678f0faff60a67fff3784c3054dcbd95d1b00ed4c6156b3831cc42a2ccdeee55541f228b88e6c318e2d797c6fc035ae12868c4a4e3843b5b25a530b1477dec3f5ac27644476b5766e0ee132d833f9a63200eb0980bf72c3666150e567e01e3e1f469cf36beea65946fce714a3f354983e54ca4315b57ea35c5f48bd5eada05f49db1004cbb39888ebab3afad62f6509abad77ca8c4ff28731c7ae545e6876c8f4a80b6cc26928ee05001a9764694b52edd605e182d5a3a5fd192bff58aba90f57e4debe612d02cf6f08af33a78ebf8823bb3eb46d4da25b7dfa15ad436c380633d3db3d0dc4dfec6c2324d105e7090e65342b554854e777b40b5dab8125a58e8b212364ff88459a8466ff5ae661034abc8286a78ad5aa582e2dabbcd7a0b0cedcb9fd5f0bb8c3bef9117f2ca6520a72b94e528c1a4a464398e654995d5f4c77cbabf2b204b96a058cf1b38284b34e41ac37b05a003ed51be9602050f21c6b9326714bc425c1e22833da95a6e77571691d4dcab4ef9056c4c7f85d5b445b902eb375b5164c6bdf629ccfd4127a6c024bb6c4da0b6b08350432e58f8229e04e2e76f704be17d36e0c04fcc7a98f721d4572aa7f66ae8e9664300a189bc3862da47b60c8b33424f6d577cc10f4755f36c2a6decc30ba81bf48f96616ccfcfb74965d6bdcab82728bb224c560d1cfd7a175413ad1c14c734746be3b062b4e7514e9075c688103515e32e3335dbd272a315024d56f4ecd354264da9bc712080657b2b51b06dc7c4c441d9858935a4c3e6b207bde38ea83bba4c6854b2bcf914d758e0a174c0528e0e385c7cff355c38db1c22440369141e91266824c59f1ed23e7d4b99d31b0baa7bed4526e24259dbef5c9ae275e97267b756645f804c274d65ac7ab0f7683435bc2e4f24075cd1b790aa2b53fbf044e8f2092bdf0dbe88a582ff8f8de291e8220", + output: "bea32587095caac661c3ac49e65654b282192b2addf5b9a403aea6c8bd0096291a0a66ca4062acf1da91fb5749952096ec63ab652ecf94c29807f0aaac939b6896edcd6f0cd8dd8d208b906ef4d7a8766831fecd6ce98f4ea0c34fa9a5114dbeb23c2cd6d3aa962e39b18cb343c24e65d49fad0a0fb50736f8d2b24b011108932484399f4c4510ac9a5e6bc78ff0b450e67f87b49f253b99d95d6294e15a9934fc8b89a5913c08f75d3516766fb0f60f82e2b2647b4619991c78adbcf548c07c0dda30c629349d84f298313c3e629e03760b1cf860264205a950d6fd86732a6513827f72c0dff5aff96f7203464f60849c1065beb70f282cca1334f6f6c767dfff94f063361f592e85597de5d313eaed17bd533db24818d9ba9aea2afa797721fbd19eea7b8d46bbc4b9dc0164636d2e754f5e9e8c04e2a381096331731c645ea1f613a37bfa9a6fb2c6307e9bacacbeab7f5672163ff9742a8115049bce0269d7d5f6f35787be031dbee1535b0516ec0b46d12f5833cde5f2cc569edcdd20993e9776aacf48ace7bfadf79065f2803fba6b2b27aa622abb7ae023ff2b27b727f509f313f92026392485a5ed4fd53b2e22b2d2dc1538ce158d34921214638be30ae054a0f5f1d4f9c590a2d215ac2a5b23ed33871ab26c8bb6db7fe9d6f51e527c9547248a4e9734c64658b22893f4f6867a35f18e2bbfd7d62142025955cb51af8e40b6fcb91c7e959cea2c92022c87c29dae107a306f41b00e73c7bceef8cb070e8f9e830caeee463170e919cba6eee63092a5a7ee33b74db09cdd022fdafbcd5d524253a29a103ba6f4d668d31d18f867557871c0e0258221c3050d57c18bdae4cc4ff8da0daddb5c08619be127ee76a317b59a9d8e67808603a1bfce6b4e0d070082b283bf9c0e6ef8256208e482f3e2d1a40d30807f60a868e2279dfbc3586d44ee25fdca3505cd39fd469c2cd03bc2f921d22a8346750f346c919e7247301c1c8a4a3ddb8eabc6e80d85cd2459afe1cbb4851ea2c86b8075e0fef3177cb074894410ecf681242fac62b5fa4ed3a10ddaa595427851d376cf69e350207b667f7aa26d003f1ec739a8792532ebd93f3cafb1fea40d227bcadda2fb6da794cea3371240f257f80b1b8a857ea453b46938397c1f4b303a46257750003a60666a11d03bf2afb5c71e059933d617288891733b63784bd9c662234f", + }, + { + length: 985, + nonce: [3]uint32{0x3c2b47a4, 0xf614c813, 0xa26f7014}, + key: [8]uint32{0x39bd3d18, 0xc9aacd67, 0xcb5485b5, 0x20536a22, 0xbb22ac87, 0x1c9da580, 0x7d996b2e, 0x456fe461}, + input: "01847d8a97d56e55e12f89adb13c8c0f9dea5555e8dc61171fbb8e181f6cf846a4dd68b2c75335c0896fa215bf7f9eb7e398e4520aaaf33461ecfb61051f43d43569fb75fabd79d319bf39469f951e4da7932a74624c46d8d26a9499c701c00d3dea57a6f65b4c0f33b568d13989340294d17cd005b26d89cf6fa1c88e7b6ef4d074291fa8c117ae05d7c785459ef4561c45af63a811e9aa1c31b69a5bdac2356d955a0f579791247a757a691b3de447a53619878397cd82a74053f06da3574045bc856500ec01fd2afbc64d8dd283ac876a50e9396f78c424ab157f481316fd9c90cd899f5aca46dad32c68f1d64ea7f1c4bdb994ad847072609bd89adc2fa8382a5d573b680533640b8321b6adf27926274660b2cbaf04fbc9a4fb17ce8957c38c7bab1aafd5bf7263171e47d2e1ae5cf0494815642209d303dba561754479c24ea01a573c9083b68acc49907b1748924d3c6a82feb9417ca932578c123f9db35521c0d992565f7396f0c23e436289c1720e4e7c6e285c04a8159f93e06801334e523b18fe188355cc6a155febe64ba053e6b5d1cc87787fd5ae68fa86d8c51868b9f6a9664cf0d56aa6cb8463362bb671e6b8423bcbefe2a1a0acba3f135496736b5cec5e329494af46aba322bf5d1cc108c98298459558773a316e09b0bb960a26f4b0bfbaa493b5f98a0e522b6203c471b10e662abe9b9e60de2a1517843933add02017fadd62608383ad53796159f3d21b2c8ed7295802ca79ea65d550114ca2bcc7f7c3b4c6709fffc3c2de00da06e83d8f0cf04b8c8edd21c0fc11a0b2aa7f6adad255fef25e5c0a9d59546e97446e1fbf6a51a8ea6cad54cabfdd19cd10d7d33ff0549b710557e3931821dd8809ab0a9d3aaa761a01ae0f2e378906672924d6a1b12fb1cca7bed41f31974b9917a05de60c32796f502e7035a2c01cb49bc8e1734b9fa138b81b4dfe19d37f5942dd1b42f03e1e5a6a046ecd457174150e17dd148e4bfea44b72da35ef42a7251244700e59e702033677d42611168fd246e1b18b9a464b6c20fc7fcf6360cd00466ece059a69d7d54a4f5565d08799f85dd3c849a08ba43415077c1c0e5dbdba52bb3167ee99a11db551f0260493be1dde58d2072e8c02251f4f574b6e115cbb6136dc2c3fbce75fdcefe812d9c07a91a89088985a52cb1fb9f6cef80fa30364706414175e42c75e8e37f6e7cd028c99f59caa88c49db5b46e8d6301bc39034013718a9eeef5506415016fb21d70e46a03b4c5ba72f91dd9321ff5e210e5e5f7b0723a3bc4bb02b5a74c1f4a63aa5a993a31f79a768fe8033c9abfeb4deb536af1054be02d8d1c4a6a0fa75f3eb787d57a03b7ae994fb1b54b2c43b230ce32e6245d944b3cea4fa6", + output: "785dbea5d1e50af4743ed5fd2209e441fc7c50bc7f6fd9cc7f24654c619e2606178dcbbd81a1f94c1b3176837024098bd31326145be326b32fd9277a55a6fb38780c8dc8b471a3184379d90da4aa87d80b889b1f4d8d0755c1704a526b99ac829b8ad157ca54b2b05ff8b2917e27b0c147ab54add9a89fdcad7b93ba1fe2d5be9de88b68a5324f1b42943e45ee31c4ef783ec9e2337b3f2834b10cf452b313fafdf0c03719140f64060da0a565e185cb8e544e1c185ca230ff2321739a285abe8be4be0ce76678a7b0902a77a645194de49fef8ff64cc464ea25e1f1d72c775e450f08ddd7680d27a4142879787b198583d93b84cd87fd5b4063d92d13d9c9cb580c01fac0174686a18f64e6fa0b3589624cfae04aad74950559bdf92b2b199c60cb04013aa0ef56d1f9ec5b7e968f6a83756ecc9cee7dd8b433f64649f948df5474a64549e71e46fd8bb16568d21f5fb67f5ed555f2b8aec4709383e8cbc45b9fe47c0434178ad4c6d0d42606d6eef0e21d0370898d1d5d646830a88d5f024094fe9c7a2003ca13d20ab7cd748dc11a22f578ddab416f3500eff3d89fc177b46436108e2e2c7973910cb8454a01c9e9b98f966848325444b2ac205b1ed6919fa76aaf63717574761b7f62b10649357df49f85a845a30b6acd57fa202fe58673930ec59399f537e9682b1f5f6f409988789a8e0c1f803478dded14b40d3b6eb3109758efeb6a7fe21f41c4dcc8027258da27ad74010839dbfdf8fe55050511f85c321e653f76e55f22248f46da529a380c6b1a16a19ce73af9715545c2cae098dc42dd61248dbcf7b295f4dc6b8930b41baeef677156c534869be65e723e1aa0336e8be8a3b138f840c9cd63bab6d9d61f239a47d8cf56258544e6ef65edca27069f7a57f087a7cc021fa1294b75c0c0f1093c025e426e4f041ed5187f358402676d5da5fb6ceba76a178f65c8c3046f258531c165b8808bdd221c59ff56e3e06247576e144aac01ea96a07f1be15d7a2b0b3b6c259a9133f8a50b56154ecf9f61022f470027247e6e70e6eaf7ece5e324ec8f95667ffed10337652b119e7cb8d197e306e81ea251340b9fb2c33aa230c0a16e1ca783f9344b3acbf413acd96616e6d477dba90e39326089934bc5ca6620855cdc442e25bf8b8debf335e16e7e25cceb68659cc81b13a507fbd9f30b347126beeb57016bd348fe3df592d4778011664a218227e70d7360d139480500b7f6f84153e61ca4dea105875e19ce3d11a3dfd0ad0074035ff6a9fac0ece91afd8be74c168da20c8baafcc14632eb0e774db758a3d90709cddf0266c27963788c35a842beea8ba2d916234431efde4bb32fd7e1cef51dcf580f4697206bbc3f991f4046360aea6e88ec", + }, +} diff --git a/libgo/go/golang_org/x/crypto/internal/chacha20/xor.go b/libgo/go/golang_org/x/crypto/internal/chacha20/xor.go new file mode 100644 index 0000000..9c5ba0b --- /dev/null +++ b/libgo/go/golang_org/x/crypto/internal/chacha20/xor.go @@ -0,0 +1,43 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found src the LICENSE file. + +package chacha20 + +import ( + "runtime" +) + +// Platforms that have fast unaligned 32-bit little endian accesses. +const unaligned = runtime.GOARCH == "386" || + runtime.GOARCH == "amd64" || + runtime.GOARCH == "arm64" || + runtime.GOARCH == "ppc64le" || + runtime.GOARCH == "s390x" + +// xor reads a little endian uint32 from src, XORs it with u and +// places the result in little endian byte order in dst. +func xor(dst, src []byte, u uint32) { + _, _ = src[3], dst[3] // eliminate bounds checks + if unaligned { + // The compiler should optimize this code into + // 32-bit unaligned little endian loads and stores. + // TODO: delete once the compiler does a reliably + // good job with the generic code below. + // See issue #25111 for more details. + v := uint32(src[0]) + v |= uint32(src[1]) << 8 + v |= uint32(src[2]) << 16 + v |= uint32(src[3]) << 24 + v ^= u + dst[0] = byte(v) + dst[1] = byte(v >> 8) + dst[2] = byte(v >> 16) + dst[3] = byte(v >> 24) + } else { + dst[0] = src[0] ^ byte(u) + dst[1] = src[1] ^ byte(u>>8) + dst[2] = src[2] ^ byte(u>>16) + dst[3] = src[3] ^ byte(u>>24) + } +} diff --git a/libgo/go/golang_org/x/crypto/poly1305/poly1305.go b/libgo/go/golang_org/x/crypto/poly1305/poly1305.go index 4a5f826..f562fa5 100644 --- a/libgo/go/golang_org/x/crypto/poly1305/poly1305.go +++ b/libgo/go/golang_org/x/crypto/poly1305/poly1305.go @@ -3,7 +3,8 @@ // license that can be found in the LICENSE file. /* -Package poly1305 implements Poly1305 one-time message authentication code as specified in http://cr.yp.to/mac/poly1305-20050329.pdf. +Package poly1305 implements Poly1305 one-time message authentication code as +specified in https://cr.yp.to/mac/poly1305-20050329.pdf. Poly1305 is a fast, one-time authentication function. It is infeasible for an attacker to generate an authenticator for a message without the key. However, a diff --git a/libgo/go/golang_org/x/crypto/poly1305/poly1305_test.go b/libgo/go/golang_org/x/crypto/poly1305/poly1305_test.go index 017027f..256bdbb 100644 --- a/libgo/go/golang_org/x/crypto/poly1305/poly1305_test.go +++ b/libgo/go/golang_org/x/crypto/poly1305/poly1305_test.go @@ -5,7 +5,6 @@ package poly1305 import ( - "bytes" "encoding/hex" "flag" "testing" @@ -14,80 +13,51 @@ import ( var stressFlag = flag.Bool("stress", false, "run slow stress tests") -var testData = []struct { - in, k, correct []byte -}{ - { - []byte("Hello world!"), - []byte("this is 32-byte key for Poly1305"), - []byte{0xa6, 0xf7, 0x45, 0x00, 0x8f, 0x81, 0xc9, 0x16, 0xa2, 0x0d, 0xcc, 0x74, 0xee, 0xf2, 0xb2, 0xf0}, - }, - { - make([]byte, 32), - []byte("this is 32-byte key for Poly1305"), - []byte{0x49, 0xec, 0x78, 0x09, 0x0e, 0x48, 0x1e, 0xc6, 0xc2, 0x6b, 0x33, 0xb9, 0x1c, 0xcc, 0x03, 0x07}, - }, - { - make([]byte, 2007), - []byte("this is 32-byte key for Poly1305"), - []byte{0xda, 0x84, 0xbc, 0xab, 0x02, 0x67, 0x6c, 0x38, 0xcd, 0xb0, 0x15, 0x60, 0x42, 0x74, 0xc2, 0xaa}, - }, - { - make([]byte, 2007), - make([]byte, 32), - make([]byte, 16), - }, - { - // This test triggers an edge-case. See https://go-review.googlesource.com/#/c/30101/. - []byte{0x81, 0xd8, 0xb2, 0xe4, 0x6a, 0x25, 0x21, 0x3b, 0x58, 0xfe, 0xe4, 0x21, 0x3a, 0x2a, 0x28, 0xe9, 0x21, 0xc1, 0x2a, 0x96, 0x32, 0x51, 0x6d, 0x3b, 0x73, 0x27, 0x27, 0x27, 0xbe, 0xcf, 0x21, 0x29}, - []byte{0x3b, 0x3a, 0x29, 0xe9, 0x3b, 0x21, 0x3a, 0x5c, 0x5c, 0x3b, 0x3b, 0x05, 0x3a, 0x3a, 0x8c, 0x0d}, - []byte{0x6d, 0xc1, 0x8b, 0x8c, 0x34, 0x4c, 0xd7, 0x99, 0x27, 0x11, 0x8b, 0xbe, 0x84, 0xb7, 0xf3, 0x14}, - }, - { - // This test generates a result of (2^130-1) % (2^130-5). - []byte{ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }, - []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - []byte{4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - }, - { - // This test generates a result of (2^130-6) % (2^130-5). - []byte{ - 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }, - []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - []byte{0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - }, - { - // This test generates a result of (2^130-5) % (2^130-5). - []byte{ - 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }, - []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - }, +type test struct { + in string + key string + tag string } -func testSum(t *testing.T, unaligned bool) { - var out [16]byte +func (t *test) Input() []byte { + in, err := hex.DecodeString(t.in) + if err != nil { + panic(err) + } + return in +} + +func (t *test) Key() [32]byte { + buf, err := hex.DecodeString(t.key) + if err != nil { + panic(err) + } var key [32]byte + copy(key[:], buf[:32]) + return key +} +func (t *test) Tag() [16]byte { + buf, err := hex.DecodeString(t.tag) + if err != nil { + panic(err) + } + var tag [16]byte + copy(tag[:], buf[:16]) + return tag +} + +func testSum(t *testing.T, unaligned bool, sumImpl func(tag *[TagSize]byte, msg []byte, key *[32]byte)) { + var tag [16]byte for i, v := range testData { - in := v.in + in := v.Input() if unaligned { in = unalignBytes(in) } - copy(key[:], v.k) - Sum(&out, in, &key) - if !bytes.Equal(out[:], v.correct) { - t.Errorf("%d: expected %x, got %x", i, v.correct, out[:]) + key := v.Key() + sumImpl(&tag, in, &key) + if tag != v.Tag() { + t.Errorf("%d: expected %x, got %x", i, v.Tag(), tag[:]) } } } @@ -125,8 +95,10 @@ func TestBurnin(t *testing.T) { } } -func TestSum(t *testing.T) { testSum(t, false) } -func TestSumUnaligned(t *testing.T) { testSum(t, true) } +func TestSum(t *testing.T) { testSum(t, false, Sum) } +func TestSumUnaligned(t *testing.T) { testSum(t, true, Sum) } +func TestSumGeneric(t *testing.T) { testSum(t, false, sumGeneric) } +func TestSumGenericUnaligned(t *testing.T) { testSum(t, true, sumGeneric) } func benchmark(b *testing.B, size int, unaligned bool) { var out [16]byte @@ -146,6 +118,7 @@ func Benchmark64(b *testing.B) { benchmark(b, 64, false) } func Benchmark1K(b *testing.B) { benchmark(b, 1024, false) } func Benchmark64Unaligned(b *testing.B) { benchmark(b, 64, true) } func Benchmark1KUnaligned(b *testing.B) { benchmark(b, 1024, true) } +func Benchmark2M(b *testing.B) { benchmark(b, 2097152, true) } func unalignBytes(in []byte) []byte { out := make([]byte, len(in)+1) diff --git a/libgo/go/golang_org/x/crypto/poly1305/sum_noasm.go b/libgo/go/golang_org/x/crypto/poly1305/sum_noasm.go new file mode 100644 index 0000000..751eec5 --- /dev/null +++ b/libgo/go/golang_org/x/crypto/poly1305/sum_noasm.go @@ -0,0 +1,14 @@ +// Copyright 2018 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 s390x,!go1.11 !arm,!amd64,!s390x gccgo appengine nacl + +package poly1305 + +// Sum generates an authenticator for msg using a one-time key and puts the +// 16-byte result into out. Authenticating two different messages with the same +// key allows an attacker to forge messages at will. +func Sum(out *[TagSize]byte, msg []byte, key *[32]byte) { + sumGeneric(out, msg, key) +} diff --git a/libgo/go/golang_org/x/crypto/poly1305/sum_ref.go b/libgo/go/golang_org/x/crypto/poly1305/sum_ref.go index b2805a5..c4d59bd 100644 --- a/libgo/go/golang_org/x/crypto/poly1305/sum_ref.go +++ b/libgo/go/golang_org/x/crypto/poly1305/sum_ref.go @@ -2,16 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !amd64,!arm gccgo appengine nacl - package poly1305 import "encoding/binary" -// Sum generates an authenticator for msg using a one-time key and puts the -// 16-byte result into out. Authenticating two different messages with the same -// key allows an attacker to forge messages at will. -func Sum(out *[TagSize]byte, msg []byte, key *[32]byte) { +// sumGeneric generates an authenticator for msg using a one-time key and +// puts the 16-byte result into out. This is the generic implementation of +// Sum and should be called if no assembly implementation is available. +func sumGeneric(out *[TagSize]byte, msg []byte, key *[32]byte) { var ( h0, h1, h2, h3, h4 uint32 // the hash accumulators r0, r1, r2, r3, r4 uint64 // the r part of the key diff --git a/libgo/go/golang_org/x/crypto/poly1305/sum_s390x.go b/libgo/go/golang_org/x/crypto/poly1305/sum_s390x.go new file mode 100644 index 0000000..7a266ce --- /dev/null +++ b/libgo/go/golang_org/x/crypto/poly1305/sum_s390x.go @@ -0,0 +1,49 @@ +// Copyright 2018 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 s390x,go1.11,!gccgo,!appengine + +package poly1305 + +// hasVectorFacility reports whether the machine supports +// the vector facility (vx). +func hasVectorFacility() bool + +// hasVMSLFacility reports whether the machine supports +// Vector Multiply Sum Logical (VMSL). +func hasVMSLFacility() bool + +var hasVX = hasVectorFacility() +var hasVMSL = hasVMSLFacility() + +// poly1305vx is an assembly implementation of Poly1305 that uses vector +// instructions. It must only be called if the vector facility (vx) is +// available. +//go:noescape +func poly1305vx(out *[16]byte, m *byte, mlen uint64, key *[32]byte) + +// poly1305vmsl is an assembly implementation of Poly1305 that uses vector +// instructions, including VMSL. It must only be called if the vector facility (vx) is +// available and if VMSL is supported. +//go:noescape +func poly1305vmsl(out *[16]byte, m *byte, mlen uint64, key *[32]byte) + +// Sum generates an authenticator for m using a one-time key and puts the +// 16-byte result into out. Authenticating two different messages with the same +// key allows an attacker to forge messages at will. +func Sum(out *[16]byte, m []byte, key *[32]byte) { + if hasVX { + var mPtr *byte + if len(m) > 0 { + mPtr = &m[0] + } + if hasVMSL && len(m) > 256 { + poly1305vmsl(out, mPtr, uint64(len(m)), key) + } else { + poly1305vx(out, mPtr, uint64(len(m)), key) + } + } else { + sumGeneric(out, m, key) + } +} diff --git a/libgo/go/golang_org/x/crypto/poly1305/vectors_test.go b/libgo/go/golang_org/x/crypto/poly1305/vectors_test.go new file mode 100644 index 0000000..18d7ff8 --- /dev/null +++ b/libgo/go/golang_org/x/crypto/poly1305/vectors_test.go @@ -0,0 +1,2943 @@ +// Copyright 2018 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 poly1305 + +var testData = [...]test{ + // edge cases + { + // see https://go-review.googlesource.com/#/c/30101/ + key: "3b3a29e93b213a5c5c3b3b053a3a8c0d00000000000000000000000000000000", + tag: "6dc18b8c344cd79927118bbe84b7f314", + in: "81d8b2e46a25213b58fee4213a2a28e921c12a9632516d3b73272727becf2129", + }, + { + key: "0100000000000000000000000000000000000000000000000000000000000000", + tag: "04000000000000000000000000000000", // (2¹³⁰-1) % (2¹³⁰-5) + in: "ffffffffffffffffffffffffffffffff" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000", + }, + { + key: "0100000000000000000000000000000000000000000000000000000000000000", + tag: "faffffffffffffffffffffffffffffff", // (2¹³⁰-6) % (2¹³⁰-5) + in: "faffffffffffffffffffffffffffffff" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000", + }, + { + key: "0100000000000000000000000000000000000000000000000000000000000000", + tag: "00000000000000000000000000000000", // (2¹³⁰-5) % (2¹³⁰-5) + in: "fbffffffffffffffffffffffffffffff" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000", + }, + { + key: "0100000000000000000000000000000000000000000000000000000000000000", + tag: "f9ffffffffffffffffffffffffffffff", // (2*(2¹³⁰-6)) % (2¹³⁰-5) + in: "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000", + }, + { + key: "0100000000000000000000000000000000000000000000000000000000000000", + tag: "00000000000000000000000000000000", // (2*(2¹³⁰-5)) % (2¹³⁰-5) + in: "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000", + }, + { + key: "0100000000000000000000000000000000000000000000000000000000000000", + tag: "f8ffffffffffffffffffffffffffffff", // (3*(2¹³⁰-6)) % (2¹³⁰-5) + in: "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000", + }, + { + key: "0100000000000000000000000000000000000000000000000000000000000000", + tag: "00000000000000000000000000000000", // (3*(2¹³⁰-5)) % (2¹³⁰-5) + in: "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000", + }, + { + key: "0100000000000000000000000000000000000000000000000000000000000000", + tag: "f7ffffffffffffffffffffffffffffff", // (4*(2¹³⁰-6)) % (2¹³⁰-5) + in: "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000", + }, + { + key: "0100000000000000000000000000000000000000000000000000000000000000", + tag: "00000000000000000000000000000000", // (4*(2¹³⁰-5)) % (2¹³⁰-5) + in: "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000", + }, + { + key: "0100000000000000000000000000000000000000000000000000000000000000", + tag: "f3ffffffffffffffffffffffffffffff", // (8*(2¹³⁰-6)) % (2¹³⁰-5) + in: "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000", + }, + { + key: "0100000000000000000000000000000000000000000000000000000000000000", + tag: "00000000000000000000000000000000", // (8*(2¹³⁰-5)) % (2¹³⁰-5) + in: "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000", + }, + { + key: "0100000000000000000000000000000000000000000000000000000000000000", + tag: "ebffffffffffffffffffffffffffffff", // (16*(2¹³⁰-6)) % (2¹³⁰-5) + in: "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "faffffffffffffffffffffffffffffff" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000", + }, + { + key: "0100000000000000000000000000000000000000000000000000000000000000", + tag: "00000000000000000000000000000000", // (16*(2¹³⁰-5)) % (2¹³⁰-5) + in: "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "fbffffffffffffffffffffffffffffff" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000", + }, + // original smoke tests + { + key: "746869732069732033322d62797465206b657920666f7220506f6c7931333035", + tag: "a6f745008f81c916a20dcc74eef2b2f0", + in: "48656c6c6f20776f726c6421", + }, + { + key: "746869732069732033322d62797465206b657920666f7220506f6c7931333035", + tag: "49ec78090e481ec6c26b33b91ccc0307", + in: "0000000000000000000000000000000000000000000000000000000000000000", + }, + { + key: "746869732069732033322d62797465206b657920666f7220506f6c7931333035", + tag: "da84bcab02676c38cdb015604274c2aa", + in: "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000", + }, + { + key: "0000000000000000000000000000000000000000000000000000000000000000", + tag: "00000000000000000000000000000000", + in: "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000", + }, + // randomly generated + { + key: "52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649", + tag: "9566c74d10037c4d7bbb0407d1e2c649", + in: "", + }, + { + key: "81855ad8681d0d86d1e91e00167939cb6694d2c422acd208a0072939487f6999", + tag: "eaa270caaa12faa39b797374a4b8a420", + in: "eb", + }, + { + key: "9d18a44784045d87f3c67cf22746e995af5a25367951baa2ff6cd471c483f15f", + tag: "dbea66e1da48a8f822887c6162c2acf1", + in: "b90b", + }, + { + key: "adb37c5821b6d95526a41a9504680b4e7c8b763a1b1d49d4955c848621632525", + tag: "6ac09aaa88c32ee95a7198376f16abdb", + in: "3fec73", + }, + { + key: "8dd7a9e28bf921119c160f0702448615bbda08313f6a8eb668d20bf505987592", + tag: "b1443487f97fe340b04a74719ed4de68", + in: "1e668a5b", + }, + { + key: "df2c7fc4844592d2572bcd0668d2d6c52f5054e2d0836bf84c7174cb7476364c", + tag: "7463be0f9d99a5348039e4afcbf4019c", + in: "c3dbd968b0", + }, + { + key: "f7172ed85794bb358b0c3b525da1786f9fff094279db1944ebd7a19d0f7bbacb", + tag: "2edaee3bcf303fd05609e131716f8157", + in: "e0255aa5b7d4", + }, + { + key: "4bec40f84c892b9bffd43629b0223beea5f4f74391f445d15afd4294040374f6", + tag: "965f18767420c1d94a4ef657e8d15e1e", + in: "924b98cbf8713f", + }, + { + key: "8d962d7c8d019192c24224e2cafccae3a61fb586b14323a6bc8f9e7df1d92933", + tag: "2bf4a33287dd6d87e1ed4282f7342b6a", + in: "3ff993933bea6f5b", + }, + { + key: "3af6de0374366c4719e43a1b067d89bc7f01f1f573981659a44ff17a4c7215a3", + tag: "c5e987b60373a48893c5af30acf2471f", + in: "b539eb1e5849c6077d", + }, + { + key: "bb5722f5717a289a266f97647981998ebea89c0b4b373970115e82ed6f4125c8", + tag: "19f0f640b309d168ea1b480e6a4faee5", + in: "fa7311e4d7defa922daa", + }, + { + key: "e7786667f7e936cd4f24abf7df866baa56038367ad6145de1ee8f4a8b0993ebd", + tag: "de75e5565d97834b9fa84ad568d31359", + in: "f8883a0ad8be9c3978b048", + }, + { + key: "83e56a156a8de563afa467d49dec6a40e9a1d007f033c2823061bdd0eaa59f8e", + tag: "de184a5a9b826aa203c5c017986d6690", + in: "4da6430105220d0b29688b73", + }, + { + key: "4b8ea0f3ca9936e8461f10d77c96ea80a7a665f606f6a63b7f3dfd2567c18979", + tag: "7478f18d9684905aa5d1a34ee67e4c84", + in: "e4d60f26686d9bf2fb26c901ff", + }, + { + key: "354cde1607ee294b39f32b7c7822ba64f84ab43ca0c6e6b91c1fd3be89904341", + tag: "3b2008a9c52b5308f5538b789ab5506f", + in: "79d3af4491a369012db92d184fc3", + }, + { + key: "9d1734ff5716428953bb6865fcf92b0c3a17c9028be9914eb7649c6c93478009", + tag: "71c8e76a67a505b7370b562ba15ba032", + in: "79d1830356f2a54c3deab2a4b4475d", + }, + { + key: "63afbe8fb56987c77f5818526f1814be823350eab13935f31d84484517e924ae", + tag: "1dc895f74f866bdb3edf6c4430829c1c", + in: "f78ae151c00755925836b7075885650c", + }, + { + key: "30ec29a3703934bf50a28da102975deda77e758579ea3dfe4136abf752b3b827", + tag: "afca2b3ba7b0e1a928001966883e9b16", + in: "1d03e944b3c9db366b75045f8efd69d22ae5411947cb553d7694267aef4e" + + "bcea406b32d6108bd68584f57e37caac6e33feaa3263a399437024ba9c9b" + + "14678a274f01a910ae295f6efbfe5f5abf44ccde263b5606633e2bf0006f" + + "28295d7d39069f01a239c4365854c3af7f6b41d631f92b9a8d12f4125732" + + "5fff332f7576b0620556304a3e3eae14c28d0cea39d2901a52720da85ca1" + + "e4b38eaf3f", + }, + { + key: "44c6c6ef8362f2f54fc00e09d6fc25640854c15dfcacaa8a2cecce5a3aba53ab", + tag: "6f2a09aa76c9b76774e31ec02dcf7991", + in: "705b18db94b4d338a5143e63408d8724b0cf3fae17a3f79be1072fb63c35" + + "d6042c4160f38ee9e2a9f3fb4ffb0019b454d522b5ffa17604193fb89667" + + "10a7960732ca52cf53c3f520c889b79bf504cfb57c7601232d589baccea9" + + "d6e263e25c27741d3f6c62cbbb15d9afbcbf7f7da41ab0408e3969c2e2cd" + + "cf233438bf1774ace7709a4f091e9a83fdeae0ec55eb233a9b5394cb3c78" + + "56b546d313c8a3b4c1c0e05447f4ba370eb36dbcfdec90b302dcdc3b9ef5" + + "22e2a6f1ed0afec1f8e20faabedf6b162e717d3a748a58677a0c56348f89" + + "21a266b11d0f334c62fe52ba53af19779cb2948b6570ffa0b773963c130a" + + "d797ddea", + }, + { + key: "fe4e3ad29b5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd", + tag: "27381e3fc2a356103fb796f107d826e7", + in: "58b00ce73bff706f7ff4b6f44090a32711f3208e4e4b89cb5165ce64002c" + + "bd9c2887aa113df2468928d5a23b9ca740f80c9382d9c6034ad2960c7965" + + "03e1ce221725f50caf1fbfe831b10b7bf5b15c47a53dbf8e7dcafc9e1386" + + "47a4b44ed4bce964ed47f74aa594468ced323cb76f0d3fac476c9fb03fc9" + + "228fbae88fd580663a0454b68312207f0a3b584c62316492b49753b5d502" + + "7ce15a4f0a58250d8fb50e77f2bf4f0152e5d49435807f9d4b97be6fb779" + + "70466a5626fe33408cf9e88e2c797408a32d29416baf206a329cfffd4a75" + + "e498320982c85aad70384859c05a4b13a1d5b2f5bfef5a6ed92da482caa9" + + "568e5b6fe9d8a9ddd9eb09277b92cef9046efa18500944cbe800a0b1527e" + + "a6", + }, + { + key: "4729a861d2f6497a3235c37f4192779ec1d96b3b1c5424fce0b727b03072e641", + tag: "0173965669fb9de88d38a827a0271271", + in: "5a761f03abaa40abc9448fddeb2191d945c04767af847afd0edb5d8857b7" + + "99acb18e4affabe3037ffe7fa68aa8af5e39cc416e734d373c5ebebc9cdc" + + "c595bcce3c7bd3d8df93fab7e125ddebafe65a31bd5d41e2d2ce9c2b1789" + + "2f0fea1931a290220777a93143dfdcbfa68406e877073ff08834e197a403" + + "4aa48afa3f85b8a62708caebbac880b5b89b93da53810164402104e648b6" + + "226a1b78021851f5d9ac0f313a89ddfc454c5f8f72ac89b38b19f53784c1" + + "9e9beac03c875a27db029de37ae37a42318813487685929359ca8c5eb94e" + + "152dc1af42ea3d1676c1bdd19ab8e2925c6daee4de5ef9f9dcf08dfcbd02" + + "b80809398585928a0f7de50be1a6dc1d5768e8537988fddce562e9b948c9" + + "18bba3e933e5c400cde5e60c5ead6fc7ae77ba1d259b188a4b21c86fbc23" + + "d728b45347eada650af24c56d0800a8691332088a805bd55c446e25eb075" + + "90bafcccbec6177536401d9a2b7f512b54bfc9d00532adf5aaa7c3a96bc5" + + "9b489f77d9042c5bce26b163defde5ee6a0fbb3e9346cef81f0ae9515ef3" + + "0fa47a364e75aea9e111d596e685a591121966e031650d510354aa845580" + + "ff560760fd36514ca197c875f1d02d9216eba7627e2398322eb5cf43d72b" + + "d2e5b887d4630fb8d4747ead6eb82acd1c5b078143ee26a586ad23139d50" + + "41723470bf24a865837c", + }, + { + key: "9123461c41f5ff99aa99ce24eb4d788576e3336e65491622558fdf297b9fa007", + tag: "1eb0cdad9237905250d30a24fe172a34", + in: "864bafd7cd4ca1b2fb5766ab431a032b72b9a7e937ed648d0801f29055d3" + + "090d2463718254f9442483c7b98b938045da519843854b0ed3f7ba951a49" + + "3f321f0966603022c1dfc579b99ed9d20d573ad53171c8fef7f1f4e4613b" + + "b365b2ebb44f0ffb6907136385cdc838f0bdd4c812f042577410aca008c2" + + "afbc4c79c62572e20f8ed94ee62b4de7aa1cc84c887e1f7c31e927dfe52a" + + "5f8f46627eb5d3a4fe16fafce23623e196c9dfff7fbaff4ffe94f4589733" + + "e563e19d3045aad3e226488ac02cca4291aed169dce5039d6ab00e40f67a" + + "ab29332de1448b35507c7c8a09c4db07105dc31003620405da3b2169f5a9" + + "10c9d0096e5e3ef1b570680746acd0cc7760331b663138d6d342b051b5df" + + "410637cf7aee9b0c8c10a8f9980630f34ce001c0ab7ac65e502d39b216cb" + + "c50e73a32eaf936401e2506bd8b82c30d346bc4b2fa319f245a8657ec122" + + "eaf4ad5425c249ee160e17b95541c2aee5df820ac85de3f8e784870fd87a" + + "36cc0d163833df636613a9cc947437b6592835b9f6f4f8c0e70dbeebae7b" + + "14cdb9bc41033aa5baf40d45e24d72eac4a28e3ca030c9937ab8409a7cbf" + + "05ae21f97425254543d94d115900b90ae703b97d9856d2441d14ba49a677" + + "de8b18cb454b99ddd9daa7ccbb7500dae4e2e5df8cf3859ebddada6745fb" + + "a6a04c5c37c7ca35036f11732ce8bc27b48868611fc73c82a491bfabd7a1" + + "9df50fdc78a55dbbc2fd37f9296566557fab885b039f30e706f0cd5961e1" + + "9b642221db44a69497b8ad99408fe1e037c68bf7c5e5de1d2c68192348ec" + + "1189fb2e36973cef09ff14be23922801f6eaee41409158b45f2dec82d17c" + + "aaba160cd6", + }, + { + key: "40ff73495fe4a05ce1202ca7287ed3235b95e69f571fa5e656aaa51fae1ebdd7", + tag: "2e619d8ea81b77484e4fddeb29844e4b", + in: "aa6269c2ec7f4057b33593bc84888c970fd528d4a99a1eab9d2420134537" + + "cd6d02282e0981e140232a4a87383a21d1845c408ad757043813032a0bd5" + + "a30dcca6e3aa2df04715d879279a96879a4f3690ac2025a60c7db15e0501" + + "ebc34b734355fe4a059bd3899d920e95f1c46d432f9b08e64d7f9b38965d" + + "5a77a7ac183c3833e1a3425ead69d4f975012fd1a49ed832f69e6e9c63b4" + + "53ec049c9e7a5cf944232d10353f64434abae060f6506ad3fdb1f4415b0a" + + "f9ce8c208bc20ee526741539fa3203c77ecba410fd6718f227e0b430f9bc" + + "b049a3d38540dc222969120ce80f2007cd42a708a721aa29987b45d4e428" + + "811984ecad349cc35dd93515cefe0b002cee5e71c47935e281ebfc4b8b65" + + "2b69ccb092e55a20f1b9f97d046296124621928739a86671cc180152b953" + + "e3bf9d19f825c3dd54ae1688e49efb5efe65dcdad34bc860010e7c8c997c" + + "d5f9e320ca7d39d4ba801a175b1c76f057832f3f36d7d893e216e4c7bbdb" + + "548d0ba48449330027368b34f9c69776b4591532da1c5be68ef4eebe8cb8" + + "fa7dc5483fb70c2c896334cb1f9cb5dfe044fa086197ff5dfd02f2ba3884" + + "c53dd718c8560da743a8e9d4aeae20ccef002d82ca352592b8d8f2a8df3b" + + "0c35f15b9b370dca80d4ca8e9a133eb52094f2dd5c08731f52315d828846" + + "e37df68fd10658b480f2ac84233633957e688e924ffe3713b52c76fd8a56" + + "da8bb07daa8eb4eb8f7334f99256e2766a4109150eed424f0f743543cdea" + + "66e5baaa03edc918e8305bb19fc0c6b4ddb4aa3886cb5090940fc6d4cabe" + + "2153809e4ed60a0e2af07f1b2a6bb5a6017a578a27cbdc20a1759f76b088" + + "9a83ce25ce3ca91a4eb5c2f8580819da04d02c41770c01746de44f3db6e3" + + "402e7873db7635516e87b33e4b412ba3df68544920f5ea27ec097710954f" + + "42158bdba66d4814c064b4112538676095467c89ba98e6a543758d7093a4" + + "94df", + }, + { + key: "5cc36d09c7a6472a41f29c380a987b1ecdcf84765f4e5d3ceefc1c02181f570f", + tag: "0d57b8cbea8090df0541354673dcb4e0", + in: "44fcd629f08dc1ef53c9ae0d8869fe67fdc7a2c67b425f13c5be8d9f630c" + + "1d063c02fd75cf64c1aec9d2e2ef6e6431d5f5ad0489078dc61f46494dcc" + + "f403dad7f094170d2c3e29c198b0f341e284c4be8fa60c1a478d6bd55dd2" + + "c04dad86d2053d5d25b014e3d8b64322cdcb5004faa46cfa2d6ad2ff933b" + + "c3bd9a5a74660af3d048a9a43634c0250427d9a6219197a3f3633f841753" + + "ba7c27f3619f387b6b1a6cb9c1dc227674aa020724d137da2cb87b1615d5" + + "12974fa4747dd1e17d02c9462a44fec150ca3a8f99cc1e4953365e429956" + + "5e108535b1f62e1d4ba18e17a52164418bfd1a933f7fb3a126c860830a87" + + "293d9271da736e4398c1e37fb75c4bf02786e1faf4b610cd1377fbb9ae18" + + "0655a0abefbad700c09473469f1eca5a66d53fa3dc7cd3e7c3b0411d7e14" + + "5f96eb9654ab94913dda503a50f9e773842f4d2a5faa60869bf365830511" + + "f2ededd03e0a73000edb60c9a29a5f5e194cf3b5667a694690384599d116" + + "f8d2fd93b2aed55b7d44b5b054f3f38e788e4fdf36e591568c41d1052cad" + + "0fcb68ca4c4bf5090d57df9db6f0d91dd8b11b804f331adb7efb087a5604" + + "e9e22b4d54db40bcbc6e272ff5eaddfc1471459e59f0554c58251342134a" + + "8daaef1498069ba581ef1da2510be92843487a4eb8111c79a6f0195fc38a" + + "d6aee93c1df2b5897eaa38ad8f47ab2fe0e3aa3e6accbfd4c16d46843318" + + "5fc61c861b96ca65e34d31f24d6f56ee85092314a4d7656205c15322f1c9" + + "7613c079eae292ba966e10d1e700164e518b243f424c46f9ea63db1c2c34" + + "b512c403c128ee19030a6226517b805a072512a5e4cd274b7fd1fa23f830" + + "058208ff1a063b41039c74036b5b3da8b1a0b93135a710352da0f6c31203" + + "a09d1f2329651bb3ab3984ab591f2247e71cd44835e7a1a1b66d8595f7ae" + + "f9bf39d1417d2d31ea3599d405ff4b5999a86f52f3259b452909b57937d8" + + "5364d6c23deb4f14e0d9fcee9184df5994fdc11f045c025c8d561adb0e7d" + + "fd4748fd4b20f84e53322471a410cdb3fd88e48b2e7eb7ae5dae994cb5ea" + + "e3eaf21cf9005db560d6d22e4d9b97d7e9e488751afcd72aa176c0fcde93" + + "16f676fd527d9c42105b851639f09ea70533d26fc60cbeb4b76ed554fc99" + + "177620b28ca6f56a716f8cb384", + }, + { + key: "811c3e356e7c793acf114c624dc86ace38e67bff2a60e5b2a6c20723c1b9f003", + tag: "c6e59044cefc43ee681c3eed872d02b3", + in: "e115b304c023792448794546a2474f04294d7a616215e5dd6c40a65bb6ed" + + "b508c3680b14c176c327fdfb1ee21962c0006b7deb4e5de87db21989d13c" + + "3ab0462d5d2a52ef4ca0d366ae06a314f50e3a21d9247f814037798cc5e1" + + "0a63de027477decdeb8a8e0c279299272490106ddf8683126f60d35772c6" + + "dfc744b0adbfd5dcf118c4f2b06cfaf077881d733a5e643b7c46976647d1" + + "c1d3f8f6237c6218fa86fb47080b1f7966137667bd6661660c43b75b6339" + + "0b514bbe491aa46b524bde1c5b7456255fb214c3f74907b7ce1cba94210b" + + "78b5e68f049fcb002b96a5d38d59df6e977d587abb42d0972d5f3ffc898b" + + "3cbec26f104255761aee1b8a232d703585dd276ee1f43c8cd7e92a993eb1" + + "5107d02f59ba75f8dd1442ee37786ddb902deb88dd0ebdbf229fb25a9dca" + + "86d0ce46a278a45f5517bff2c049cc959a227dcdd3aca677e96ce84390e9" + + "b9a28e0988777331847a59f1225b027a66c1421422683dd6081af95e16f2" + + "48ab03da494112449ce7bdace6c988292f95699bb5e4d9c8d250aa28a6df" + + "44c0c265156deb27e9476a0a4af44f34bdf631b4af1146afe34ea988fc95" + + "3e71fc21ce60b3962313000fe46d757109281f6e55bc950200d0834ceb5c" + + "41553afd12576f3fbb9a8e05883ccc51c9a1269b6d8e9d27123dce5d0bd6" + + "db649c6fea06b4e4e9dea8d2d17709dc50ae8aa38231fd409e9580e255fe" + + "2bf59e6e1b6e310610ea4881206262be76120d6c97db969e003947f08bad" + + "8fa731f149397c47d2c964e84f090e77e19046277e18cd8917c48a776c9d" + + "e627b6656203b522c60e97cc61914621c564243913ae643f1c9c9e0ad00a" + + "14f66eaa45844229ecc35abb2637317ae5d5e338c68691bea8fa1fd469b7" + + "b54d0fccd730c1284ec7e6fccdec800b8fa67e6e55ac574f1e53a65ab976" + + "4c218a404184793cc9892308e296b334c85f7097edc16927c2451c4cd7e5" + + "3f239aa4f4c83241bde178f692898b1ece2dbcb19a97e64c4710326528f2" + + "4b099d0b674bd614fad307d9b9440adab32117f0f15b1450277b00eb366e" + + "0260fca84c1d27e50a1116d2ce16c8f5eb212c77c1a84425744ea3195edb" + + "b54c970b77e090b644942d43fe8c4546a158bad7620217a40e34b9bb84d1" + + "89eff32b20ef3f015714dbb1f150015d6eeb84cbccbd3fffa63bde89", + }, + { + key: "f33691f5db2dea41e1e608af3ff39f3a6988dba204ce1b09214475ae0ea864b8", + tag: "6e50e70411201378c8d67857d7b631d2", + in: "439bc9ea10db4d2b08c7fcf2e8bd89fa9844f8061d462e28f174489e7514" + + "0f84e842040141cc59ce38f9551850cfbdfac2d75337d155090d70d0d930" + + "04340bdfe60062f17c53f3c9005b9995a0feb49f6bef8eaff80f4feb7ef3" + + "f2181733a4b43b6ac43a5130a73a9b3c2cbc93bd296cd5f48c9df022b6c8" + + "2bb752bc21e3d8379be31328aa32edc11efc8a4b4b3f370ee8c870cd281d" + + "614e6bc2c0a5ca303bc48696a3bd574ee34738de4c4c29910f8feb7557bf" + + "ffcfe7428b4703144bd6d7fe5b3f5de748918553df5453b3c6001696f3de" + + "0137e454aadf30cedfb6be36b0b908a38409f1a2dc202fc285610765e4c8" + + "6414692bf4bde20ed899e97727b7ea1d95d7c621717c560f1d260ab3624e" + + "d6168d77c483dd5ce0d234049017795f2e5a7569d7ad323c50a5b1170337" + + "4174a9977026c20cd52c10b72f14e0569a684a3dcf2ccbc148fd3db506e2" + + "8d24f6c55544cb3980a36e86747adc89ebad78d1630618d113fa445f8625" + + "b583cd7be33913c30c419d047cf3baf40fd05219a1fcec717b87a65fa022" + + "1a3aa8143062d77588168019454240ae3d37640996f2967810459bc658df" + + "e556de4d07263dc3d9158ec242008226d1c6aea7f0846e12ce2d316e80da" + + "522343264ec9451ec23aaaa367d640faad4af3d44d6d86544ade34c93518" + + "2843f6b4d1c934996778affa9ee962e7dfef5e70d933d4309f0f343e9606" + + "1b91b11ac380a9675e17a96099fe411bedc28a298cd78d5496e28fbbd4f5" + + "b0a27735d1144348e22be5b75724d8f125e99c4cb4e9c3a1f0b4e9da5146" + + "e6afaa33d02fda74bf58a8badee2b634b989c01755afa6ab20ee494c6ae4" + + "c2c6f17af6b53b61d2947d83a18eb3b8a1612aad5d3ea7e8e35f325c9168" + + "ac490f22cb713ddb61fbd96011c5849ac8e2fcd42db820349bdf9157dcc0" + + "0d9f9ed9c099b10c7194d48b623b0df43759734b2a2e5f8a35e7192bf9a0" + + "03dcb9d16a54bd84d922f85b6021b28aacc5264fe9e83deb48f18f864cbd" + + "367eb163d39c45b0eb907311a2a4b09fb26109088df782ce031b02f3caff" + + "d2dbe25b1cbde9f35ba7c47292a4fd49e7def7a28824f3dfda259a86c3de" + + "59257c255c712686ee47d128a55c7b9e8c546035eab7e2da420f32ed5c94" + + "bc12a34dc68eb99257a7ea03b69d6c760b0681fa24e4ca97b7c377182ab5" + + "fee30a278b08c44c988a8f925af2997883111c750d176b432735868208f4" + + "0de7137331b544f2d28040a3581d195e82811c945c3f9fde68fc21b36a44" + + "e1cfa2d8eb625f3102461539b3f13c660936a5ddb29a0ae791fbf52c2f69" + + "7bd334653f3605b362d91cd78569b41dbd09b2a5892440b5097fa08d0b4b" + + "291fc5b934585dd8d5adc80d573fdd194b2eae26dfc49f5e51c1f1607d7e" + + "87740702f244bf39ca1d52423e0ae84891dfdf4f43ef984c7a5f293a2007" + + "a1e00e39c757f064518953f55621f955986f63", + }, + { + key: "d115b6ac998a65b48b3dae5977abaf985258d3d1cfe1616cec3d6a77f7a75785", + tag: "b431c9318ec2769fc8ee8f5fc3c079c3", + in: "7e7eb43839a6d7616b8a7b1fb7144817904342a9bd34167051162941a6b1" + + "b85db5e587f76e4a53211755d5ab29c11822d7711a97b3f1ff5b21f2485d" + + "9c86241fb56cdd6796245d3112df11ad9a7344db44d09934c4efb280ed65" + + "80cfcafb5c97a32993cbbf4917183e0b7bb38f2ce2479c28e1d39f673962" + + "17a7010448dfd39a4e7f406c8bd2d804f993bb410fffa4eb57518a531ecf" + + "259a8af068230acb826d9ffc20ee0fc43885221a321e3928971bb28615f0" + + "d9f099f5b68a80503a910fdba0bc643c60b64837900be38770b6b30c362c" + + "4580722b5dbb1b9c8cd02a18fd7b5661d2c4d28aa941c50af6655c826690" + + "37312fbf9f1cf4adb0b9400532755011b40e8252bd0e3c7a22efb0ef9122" + + "1e04b4aa8316d4a4ffeaa11909d38cc264650e7ca416835ded0953f39e29" + + "b01d3a33bba454760fb0a96d9fe50b3e42c95271e57840380d1fd39a375b" + + "3e5513a31a4b80a2dad8731d4fd1ced5ff61e1fbe8ff3ff90a277e6b5631" + + "f99f046c4c3c66158554f61af2ede73aede97e94b1d1f129aaadf9b53548" + + "553cc2304103e245b77701f134d94d2a3658f2b41108c5a519c2c8f450db" + + "027824f1c0ab94010589a4139ff521938b4f0c7bf0986585f535b6e292e5" + + "b3ded23bf81cec17c8420fe67a449e508864e4cbb7eaf335975668f013e9" + + "da70b33bd52a72094a8f03762ea7440ce9fcd10e251837cfc9ccc1a8cc47" + + "0c67379f6a32f16cf70ea8c19d1a67779a9b2d2b379665e0e908a88b26e7" + + "8c9f94f17acefa6d5feb70a7095e0297c53e091cf98df132a23a5ce5aa72" + + "59f1154b92e079f0b6f95d2a38aa5d62a2fd97c12ee7b085e57cc4652863" + + "8defacc1e70c3aceab82a9fa04e6aa70f5fbfd19de075bee4e3aac4a87d0" + + "ad0226a463a554816f1ebac08f30f4c3a93fa85d79b92f0da06348b4f008" + + "880fac2df0f768d8f9d082f5a747afb0f62eb29c89d926de9fc491921474" + + "1d8647c67d57ac55f94751389ee466bbd44dbe186f2f38abbc61a0425613" + + "e9b6a64e6bcb45a2e2bb783b9103483643d5610a7e2dcdb10b5d78423285" + + "506b42a99b00a4fb7b619b4526bb4ec78299dd01ad894fde2f053e18c55b" + + "6047f86333f2690c2cb8e87d9834ab8a5e339aa346e4d9952ed62dc083e3" + + "b11a823a67f23fec099a033f127ebe8626a89fa1a5a6b3520aa0d215a8e7" + + "dea3af37907686c16521739a95d6c532cc259c497bf397fceaea49cd46b9" + + "ad5c1b39a36fdd2f0d2225fef1b6ca2bb73fe604646c10ba4c572ab13a26" + + "559ededc98f5a34c874cc25621e65ba4852529b5a4e9c1b2bf8e1a8f8ff0" + + "5a31095b84696c6381eb9ad37ac0db184fe5fccf3554e514946a33cabe6f" + + "4d617b549d28ad1cc4642dac96e0215ee1596481600d3619e8f45e2c9ae1" + + "da834d44aca216bba0efef6254503ca90339f2d7ca508b2722d50c08def8" + + "a736590fa44855cd9eb9979c743783aa26e633696739f2ae25ff7b72ceb2" + + "4dff4455b85bbd675c8cb71ad18386dc58c371bdf37b4b3875b98a9423ff" + + "3becfc0d0ba2aacab3ee7683cb3b345095fefcaca5751ca793da63c89428", + }, + { + key: "f3717306b9729be998cdb2c9d856306c5ae3d89da2cdcef12f86f6110c98d873", + tag: "907dba0f4849c7cf4570b5128b5f31d5", + in: "079572187d4559f24d8e48dc366441acf226a4db79e214ec3ee288acc349" + + "887e2e377419bcafa377d0151497b52e4d9cf2a02b0fc91ad9516482bdf6" + + "eccd1497954b53241bfb0bc5c04cc45045c6251f23a510060fee32721872" + + "bbc95cd8d400dff00bcac2ecce6229c7d73d8f85ed5a87afdccf6dedd299" + + "2d5c7b5b8090c47c737ded036ff0e9aedf02a2242fd9820be618b9601e73" + + "d3ba5d8f1ae9805cfd2306251704bc74e3546997f109f1dfae20c03ff31f" + + "17564769aa49f01233c9c4b79f90fa3d1433d18cdc497914046ad77d2792" + + "2588a7d0e61d4258d7d80cdab8503e3111ddca22cf7f39c1f80f1e16a68d" + + "9e21db8b53dd316dfa4233cb453a39a90101c60efc08514a3057db007e96" + + "507745bd4a0764ed8717a250bffb5fd1ea58474bdfb5b869681939693926" + + "40d832a3387ed4ac9cdab0d2af8fcb51b86e4d927097f1e79b5af96574ec" + + "d59d0dd150a0208978c41de28ad6cadf72a49279cffd6dc281c640f2e294" + + "4cde49a13ed390da1dd92e3011ce0f4a0863375a9db3f67fca1e3b8288a0" + + "78611161d7cb668ecdb932e1ff3733982c8c460eeeff2bca46c96e8a02cf" + + "b55d770940de556373a4dd676e3a0dd66f1280c8cb77a85136b3f003fab4" + + "887dad548de7bfe6488ae55e7a71da4097db03900d4b94e776a939530328" + + "83492da900b2a6c3e73d7a6f12ee30c9dd06cc34e5a3893976eb1de5864d" + + "32e792ac02e68d052d9d0cfc7cfb40b77728422f6c26cf68987c6b40fcfe" + + "9d660abc657360eb129de11bd70af5eb8fe350af2c27a6ece2cdf81b94c8" + + "0e68e8c51106497cfa5171236efe2d71d76b5dff3352af9b407dc5aab60f" + + "46b5683646f5b28732b7c750d351a08a507243d8e437cc4bef13a3edaa20" + + "5fc4e9968b4e563fa0dc965ba20b8e48bc188a321b16d3213bed69647512" + + "7a20afc1a3680ef261df6d37b017dee05cfc3a42e4130216e5540cf715c4" + + "e638d7d615c50bef576eeb19b3b15b2c2b454dfcef2b18161a143ddf52fc" + + "8e88fa71cbe34c92cd4b5a0adc81e5c33e11d2721bc1b95a9e693ac3cabc" + + "490889a8a42bf7e22375b679e8598c8faef22a006ed2da8ab1c08aaed2f5" + + "6d6f26649036335c0881bfec1e3a5346335c3b3707ee92173f1a7a3305c2" + + "933f78e995da8f1df64daf12b81ce23c8813c27fd4551103dc33561c2e80" + + "45b6b6770fa03498fd359a104884699d628020173edbcc4398b977e456e4" + + "885964840466176a490e7c513ba5d66090277c1ab1632a995a54f555a452" + + "1170a000507865b6650730aa6d6050a55959102836fff3d37e4773340e59" + + "2e56951ff9652519de4421d9c5b63edbeb30a3852a1ea110a9a29721aee3" + + "23d5a306de1624cecc87badc47aa87f489635d2fb60bff62ba67f5257999" + + "6af0a1f1a6fbcd8704e119196fcc289a6db6a4170a2cae31a1d30744b702" + + "2536d1526d41659c2dcc8b39c26aecfc0f8a707136d81b2827a158fd7386" + + "a537514471c213a8c859016748e0264cf3fbde10f40c620840ec4df99432" + + "e2b9e1e368e33f126ec40c572e841c2618d49d4eb098b9533b1f4ae00b46" + + "8d15de8c8ab6d0b650e599576f2bd90a124c9c6a0f911fd1bd8253bac272" + + "942cbdf8864f3747ff7f09d8a5a9d8599be7ee1744e5f1faf3e526cd2a06" + + "b157527272af9d38565957c9ce663c295766c0e0e464971c6282b70d4c0c" + + "1fb3b69856b34c089ad2b2c745f5a033cee1429c5b855581ee285278893c" + + "43a5968d9c28384b7abe8d072ba69089c938685cb1eab461f05314ad6d06" + + "eaa58512f8738bde35b7b15ef359dd2e8753cb1ed6", + }, + { + key: "9772c1a4b74cbf53586e5df04369b35f1fdca390565872251bc6844bc81bda88", + tag: "68eb7fc459ecc3be819485001ab438dc", + in: "e115cc2f33e367cb85c01a914b3a512404ad6a98b5b0c3a211d4bffd5802" + + "ee43b3fb07451c74524ec8b4eddbb41ca33dd6e49791875d716a44bec97b" + + "7c2d4546616939ffa3b1ab9b8ba1d1a637e7c985cc922606caa0453085e3" + + "5f2fe0bd2de129d1d1856ade975a3281a62965927d8bb695e54514e69558" + + "89361a2a00a1b24e62bda78d0b71a0d40147016fcdaf1a702331dda8e678" + + "d8f476dcc91698da1688c610ec0cb1d9b8fbcd45dfde6d1503ba60a01337" + + "ae5b2f5c854a82c3087779babd2e522dd92f4718cd9f8c649ac226745ca2" + + "fa1696442764758f67cd926369578ae87612790dc56ed9cda935281a490e" + + "5c984950ec7a4e930520d273a69da4ed3a330e532508e26f942961fed0e3" + + "efeed52a7b96250d723155aa39a8ae85131c255c32bf406b647de1a37fba" + + "dc61e302bb5b70adec4505ee66b3a1d1b7bfe9c58b11e53ad556d56e5807" + + "017bb30b71be94e8f86aaf1496e8b8d6db75ec0afbe1cd336c23963c745d" + + "7b4ba1787ceb30728f1762b46f6eaad5064c8029d29b86266b87f93142a2" + + "74f519f3281d8c1cb43c23eb184ae41f3f625cf624b05a48d73cd7783fdf" + + "14954a03ec1a930e9a954424eff030e3f15357de4c19983f484619a0e9e2" + + "b67221cf965e9aa8d8926595c793adfe0181050df8b845ce648a66df532f" + + "78b10c83ecc86374a4f8abf8edcc303654bafd3dcc7de9c77a0a9d1d98fb" + + "121534b47d16f75b55fdc2a5e2e6799f8a2f8000d4292282e56863ae422a" + + "5779900ad6881b78946e750d7777f33f2f013a75c19615632c0e40b98338" + + "1e9b8d35a26abe30242c45662eebb157e6d7a8a5519de60268ac289b8295" + + "5d4feb47b9eef6da65031c6f52c2c4f5baa36fce3618b6a331f1e8bdd621" + + "48954fcf0846afeeb0a6cadb495c909a7fe671b021d5b0b4669961052187" + + "d01b67d44218471bfb04c1a3d82bf7b776208013fc8adabaefb11719f7a7" + + "e6cb0b92d4cc39b403ceb56bd806cbdcc9ee75362ab4aaeb760e170fdc6a" + + "23c038d45f465d8ec8519af8b0aad2eb5fae2972c603ed35ff8e46644803" + + "fc042ff8044540280766e35d8aaddcaa81e7c0c7eba28674f710492924c6" + + "1743da4d241e12b0c519910d4e31de332c2672ea77c9a3d5c60cd78a35d7" + + "924fda105b6f0a7cc11523157982418405be0bacf554b6398aeb9a1a3b12" + + "fe411c09e9bfb66416a47dd51cbd29abf8fbbd264dd57ba21a388c7e19e8" + + "12e66768b2584ad8471bef36245881fc04a22d9900a246668592ca35cfc3" + + "a8faf77da494df65f7d5c3daa129b7c98cef57e0826dee394eb927b3d6b3" + + "a3c42fa2576dcc6efd1259b6819da9544c82728276b324a36121a519aee5" + + "ae850738a44349cdec1220a6a933808aee44ba48ce46ec8fb7d897bd9e6b" + + "c4c325a27d1b457eb6be5c1806cd301c5d874d2e863fb0a01cbd3e1f5b0f" + + "8e0c771fca0c0b14042a7b0f3ae6264294a82212119b73821dcfbbfd85bb" + + "625b6f75e4dc0ee0292ab4f17daf1d507e6c97364260480d406bd43b7d8e" + + "8c2f26672a916321b482d5fa7166e282bfeed9b3598c8f8c19d2f8c8b98d" + + "f24c2500c8ad41cd6ed3f2835737916d846f1a6406cda1125ed7740fe301" + + "d1144559b7c95fa407599ae40a795226513153f86c9b8abe7d8aa6963c99" + + "5646ec586cbf20a03a698cc0681b7bd333402d00fa8e15cb32300b5a24ea" + + "316c5e1df67de78891846cb9183a4b112c3bcc17bcaa5fecd6c1dbbf6ef8" + + "272d9269e7f0ba9f17050a6aa5f11cb28874360396ab647941f2c9a85cb0" + + "6a969919b16997b0827af8f909c614545f1ad638ebb23109f6bab6b49b22" + + "b2285cabbb998b3e1bf42771b4d4e52330b224e5a1d63169ec85fe1c7dd2" + + "46dbafa6138448420f463d547a41c2b26026d4621b854bc7786ab3a0a93a" + + "e5390dd840f2454028b7c3bb87680f04f084089bbc8786ee42cf06904d01" + + "7e405144d2fae141599e2babe71abfbe7644fb25ec8a8a44a8928ff77a59" + + "a3e235de6bd7c7b803cf3cf60435e473e3315f02d7292b1c3f5a19c93646" + + "3cc4ccd6b24961083756f86ffa107322c5c7dd8d2e4ca0466f6725e8a35b" + + "574f0439f34ca52a393b2f017d2503ba2018fb4a0991fddc1949832d370a" + + "27c42e", + }, + { + key: "d18a328b63a1d0f34e987682fe6ca3d48b4834b4312a17e99b3d88827b8d2238", + tag: "938b43b80cb3935e39b21dd8ba133cf8", + in: "bc2b0baf92580ee6c5efe640f2a029a791a3c77bec459be74cbc30931508" + + "d9f312c3a0944212831cbe4fc92e8f107f2f750c91bcc09f7624fa9a09b4" + + "9b7712cf5d619ea9da100fc23068ae2f4e353047e3956b215884bdb12235" + + "3f06b8ee98f36c3212493d61ae9ce151cd0453f3075b18a12d7d73da3de7" + + "dc2d98376cfb420069ca8148c511ca6bbae57572394a3c615a6fefb30c5f" + + "d727f964b4065ac9ee252bdd2bcae3e70162fe0e8069974e073f0a093d45" + + "be52d7de16a8f5f65c548aa6525822ffb00dc642530fedf355f7188ef017" + + "56384760c80afb61ad903d10119a7d615ec4fbdc79c490160bdeaf200915" + + "e405f2a921a2380c0ab9d2ac1e4fdc8ec4b907368c004458598efac13dc7" + + "2751e7faded538e3dc8b16590cac9b7ec294da0ad53e22cb9c05d8ef494f" + + "a04f6ab7c843c867fbe3cf1b4eb146d65339b0b03392259f12627a8e98e8" + + "0f4896c30b8ecd210acb2365539a872541921dcd8e1e54caf4936dfc7e1f" + + "68f3bbce61d325b447a8cce7f0fcad28494f2e47dae46b136594b5dfca7a" + + "bdafd6856f91496c05b21079aa55aa8c41628220a2cf0cdd755893375b7b" + + "b13d914c9a1d1db4a18f8fa36c55e52d0342352052032fb62d32fcd51cb1" + + "ac46f44b06e682db5d96d583cda03b966c650c03ae53542e8da1066b6884" + + "4a7e2280c664415e413f270b1fdcfbb40b9daa6131d071ee7eb1553dc5b1" + + "a50677971223dc316d2d326d57cbd529c88698facdca425e2d5c6b10d7ae" + + "cae28b8890aa44ede9b9193dbe8d1d8aa1fa580ca384b57eadcbefc96dd8" + + "bfccbe3b855a96f1fd4913035f817b75954ef1827c7718aab24d353e41cb" + + "a73748e14e0c2750d5b6a9752125708cc7ee7a498c7fbadf4186e7f8fa93" + + "bfdf281a49400f877621651b8ba87edda5231e80b758564e75139b61b1a9" + + "9fb9ec694f928ab1f47c6c4287bd4182d1b2be053380616e98da06f3ef57" + + "b570ade17c51da1d602b6ebc5a638ebde30d99bf4f91d0e01557c7dcd8f7" + + "9e5120143c935fc699eb5616ccd3cac56b5f8a53ed9e6c47ba896bfefe71" + + "2004ad908c12cf6d954b83bec8fb0e641cc261ff8f542b86e62d90e227f2" + + "a5bd59c9d390c0dd857f6da2b7624787a0bb31908bae84896890b283da61" + + "d8ec4f56eea38b22b438d6374b42243f9c1d94288874e53ab90c554cc1f1" + + "d736acde67aff55007fd4b3becc4d0f3ddd96f10dc75255cb0327aa47076" + + "2b3a3a656e33c87b02a682658b6cd2a75d9c0462803c9bbffa51441501a0" + + "3a2fbb2344aa13d27ffb9e98704ea6720b6a9992e53449688cd74d0648fa" + + "e8e776b0ea6bf048b2ec05341e5948cab0af015328b284ae7bd89a5f763c" + + "eaf5ca3e647a9f5bff7197e4d357e4359fa5fe30709545453149be510e3b" + + "ff86beeba5110c79c0215fbe9ac9339a8ac7d41f7488588ab14ac657aaf7" + + "d5c03a353932bbb2b261f0e83f3526c5e8e0c2348a10ab4eed6ecdcf9014" + + "7550abcb0a722f257e01d38bad47cdd5a64eef43ef4e741bf50da275720a" + + "0aee47adfc5cd2534b911dc269197c3c396820b303f6941e3fd85b5ed21d" + + "6d8136745c3eeb9f36b1f226434e334dc94be8a5606079cb7643136aacd2" + + "da9c38b2eb7e2b898bd8632003767bf0c87d00a3c2fcee48bbbcdd949af3" + + "3455128216709df25879b0ce894ac4f121dfca6b8c7865002b828696641d" + + "14ffc59924fbda50866fded0afaea545c8008c564a3a0b023f519a9980ea" + + "d541d91d1c07a739fd02286ea5660e473f80494236a68e84ea31aad71348" + + "e45055ded69c39941e31d51df257a4d0b0d8f025dbedee093f2b91795bc1" + + "533dc472020769a157a187abd6d8d52e1693e2ef56b2212759d0c0120e54" + + "c425d0084fdb3925e296dd6cdd8e677043a90674904057d88ebdea5998aa" + + "03562a790adecc4399352df43e5179cf8c584d95ef8e4b37295946b1d37f" + + "faf4b3b7b98869184e42ea8b304fe1059f180ff83d14a0861ca7c0682c34" + + "b48a70df8653bd8d9a26f9489e1271fa44e41b392e648d0e619ecdad2c53" + + "952094802eeb70ade4ffe096e3049867de93a824217e31364b18204e9681" + + "dd8e84ae2678aad155b238f59dd9bf9ce07e97183a690b2a46a8f3624843" + + "5b2f713e7d8dcda4dea1e3c4cf9692dda082322c51f7bb1f63d92aa987ec" + + "cf1355a043e21a7b8d60a2b97f18487f6fff4c77df92dbfdc9837540c518" + + "9fd9585731bc6e726a34ca21154b0499522c9d1016953dd0fa2eb6a92b6d" + + "14d6e3da5c12fabe92bd639e253983fc91041091791643", + }, + { + key: "46e8eb27acfdc8f4be622d8741c7bc414464c149e21da97ab4afbf3e07b98b0e", + tag: "56b5f49be824c7a19b19faabf0787a87", + in: "ced52b76c057872a60107194b432cf04b7be05e65209045d2952ea0284d8" + + "3e2ed5a15cfdc58071204573c18ab03765b4d5e63a601419e039c42075b2" + + "7ebb2827de9c6233d6632e6d3db9140bdb4a9291d53f33734c2dc8e24df9" + + "0764dc10e0d321d20fdf659bfa2a81bc9e04fd0f83448143276647c08bfa" + + "dcfe3bc23898eda655c9353693ed7b022f43eefa23c21db7660c5029ca64" + + "a6085d93029ea6c43197356f56b7624d4819f5008d053357d981ffbe7f40" + + "96d6c55d8417002d36189b04bbb2c637339d90f4910a400833a8d422d88d" + + "c816c1636e8d9f7f926c244a28d9e0a956cec11e81d0fd81d4b2b5d4904a" + + "d1a5f55b5ec078dcb5c2bc1112bbfd5efc8c2577fe6d9872a985ee129e5b" + + "953e9cebf28cf23c6f9c6a5e09cb09ab586c6a50e4389cd3110777591d7f" + + "0608a3fd95b99f6ba03984fb0e13c6bbbde3668c59f2f2b69d7caadffa94" + + "6f67e725d56280e59e66dca025a18d4616e81abd9801835bd94485bb2025" + + "dee81fba440005b181ee81dc1d7796cbec92e4ec1c9016c8e8073cf281ce" + + "f749993f09a618a4671d58b476feffa454600f82955c591882715148a826" + + "586f68bb50059914dce1c1c85e5e3951647c9964ec9316005209a58baeb5" + + "2c6d01e6b4c275c0050a7e2bdc52133e433b050a700b556d4314e5c041d1" + + "93ee47f47adc971aed1b63259dd5cd4f95854a71a947eae3d3d12d0d7b52" + + "c6cd2fef2d2e892607a9681d73ac3236fad21ee30a4f857010bc95c00d5f" + + "6f0c6b3fe50cd6452be6eec4f5f01542dc2cb5e2db1f52224f11348fe2a0" + + "5d1e5885f1317f2d06ce2813dc4c723008e836a2ee95d0aac66855fe4c3b" + + "1b2e02ba0700be759b1ef1c2a3123ee4ccf9200d8d4de5e0d503f04c2053" + + "66393d1e91b648392ca28389d976aa618b4796acbfe8aa356ecdce1f7786" + + "bf09af226bb9402317b6fa319bbb9248d8ce00b1f49f066c69d4df93266b" + + "938342cd7fd4b07c320c2409ef72d8a57c21d0c6d6d493f7ca94d01b9852" + + "e4fca6a9291e9060154bc38af6c86932645f53914709fc90e11db56ec471" + + "6d600ee6452041248ea8244f79534f793bfc1f2020855d817cb4ca3c48ea" + + "7f6441ce9af9bda61936c226d810086c04a35e8654fdc30d4b35701adccc" + + "016d5895b2121ba4066e44d694f6371d97911786edb73dc3020ba186a01f" + + "ee3dd6036c0e205a8d05979bad228fd12c0fd2fded6c7f1e4c11354d266e" + + "d9c2f706269c43cd90504997d93a17b39b10dab0ff083ab3bd06540ce612" + + "d08f46ce75a16ef330525737410a0d98fb3d484968f9c12edcaf50103fdc" + + "c14128ea4ad6c30b56247eab28197fe617e5f88afa5cbe003c63d423647a" + + "d3042626fafd2084a0582ff1b1efdb5baa162662048019546234e2f6b6a1" + + "d8bb971114aae41df7795b4f3598f2af9e8921a9aadc7fab6c780aaa32a3" + + "84865a4ccb02351dbc55ec92a3152d1e66ec9d478be5dca17b4a131b4a0d" + + "3d4420fc6123fef80fd56ca266407d58a7880d6b7e5ce2b6bdc9a3721071" + + "7feec573d83c83a2e3f7d4023f2f68e785cde728fdbf5054060e4c89faa6" + + "1c9dd10524a08811d15c627b3b4ada549a3fa1d8dd77c005daaf2addeb10" + + "0abf694da8dd692f113965cd6366a5a7b0c17e1f2a320243e2c90b01418e" + + "22426d0401a2c8fd02cb3129a14fdfa6cbcaa1f1c2f17706e9ac374a3458" + + "777761e986ee4c358d26f8e420d33230d198fd86704e77298dd4c40c5205" + + "7566ac0cd92993b21937c3a3b4a8b89110a97cf38c781ad758bdc28f3565" + + "60cf3acbedfa8e05b396d226ef619746e8e4fa84c8e00a7f0e6d652808c8" + + "9c9b123d9bd802624cfa949eb68af85ca459b9aa85b81dbc0b630856cb9d" + + "7e18cdc96b3c069a006dd5b716e218a5ed1f580be3e3ccf0083017607902" + + "a7967a02d0a439e7c54b3b7ca4cc9d94a7754efba0bb5e192e8d1a6e7c79" + + "4aa59e410869b21009d9443204213f7bceb880ccf1f61edb6a67c395a361" + + "ff14144262b4d90c0e715dbefce92339ff704cc4065d56118624a7e429e4" + + "cadf0b9d2e7ffc4eb31c6078474a5265beba0774209c79bf81a930b302bd" + + "0f142534a6ae402da6d355a010d8c82dc379ea16d49b9d859a7de4db6e62" + + "40f6976ae0f47bc583b327df7ec88f5bd68f713b5d53796e72e28c29e843" + + "6c64cd411d335623ff4f5d167f3c7b8cba411e82f03714662425c8e1bc1e" + + "fbf435d28df541a914a55317de0ded8c744a1c3a6e047590244b207bcdcb" + + "f4bd1f9f81210deddd629192c58e6fd73e83812f084ef52f21c67bea98ee" + + "17554437d9642e2e", + }, + { + key: "b41210e5ef845bd5a8128455c4e67b533e3e2b19dffc1fb754caa528c234d6a0", + tag: "72c9534aec8c1d883eef899f04e1c65e", + in: "7eeca180bb20d99635e36b9208221b2b8ef073fbf5a57f5190e19cb86c49" + + "89b0e8150d22ec3aaf56f6ed9cb6720284d13a4b0a34cd3d7f7fc7089326" + + "6d1893fa4185269fb806677ff490aec8f889896fca50d6c80d295875b1d5" + + "4a779b6d49305360b31011b48537157d0f323ff4e865d46fba6bd23a06c1" + + "46878cf9404360d325432312ff08ce495edca63a3c93c44d79c050e3f1de" + + "4b6ca5fedbbd43dbdef9ceb26d440a59c7e0be3a8e461c4f15b6b1e1dc36" + + "a71fc723ad593fb903e83d0804ce497fc49bfc6b6a602b9dc6e9891010b1" + + "4ca066cb1c68044c1ad837c638076dd3708078509cba49fdc54922cdf5d7" + + "715fb43e9b5a5942cb8950eade143577bc9dcedde58d51deddc70075e452" + + "bbceab1e95b5d003eb96bea69687faa6d50d9c605769cb4287b5d9924dd6" + + "8881c699abaa6f93e41dac7639cdbbbd0259099a3ed096f482a1fa322b15" + + "ffc379812c74e09e95f1bd3706347eac421fe56895e738a47fcd3e118773" + + "c3a7e7e264cc7ff5a53a80e436df058265dab9756fdf6913786a47e98bbc" + + "411052d58ffec9ee948e28cbaadaae471c5d828eaf3b3c87d3bfd495477b" + + "403da54f1418a15ace0d4d0df68f6a8f2b0457b127d5eae1f45ae055afa1" + + "8f058d5dd7eea559de3ae9378ca53f7d6dc9a9465ea1f945295f16ee0404" + + "7fc9dd3deda8ee32631d7af70c20edc1e12c5f8abd2e78f43dbd4cd6407f" + + "038efab144a24ea8a090a7ba3e6499345a60106220c2959a388e1a73d070" + + "1d854bfaaa86165a5aee934b615ac7f45da7c43a1e8f74613917ed10dcd2" + + "27e4b070414412e77851db5bc053e5f502bb4e2b2645bca074c18643e814" + + "4caeccb58be49ea9a552913c0616382c899635eea79a166988c206b9aaa0" + + "977c7ced89c4c7aaeaa8fb89b38030c44530a97187fda592b088198b63a5" + + "2dfad59a0a4c1aadf812bdf1881924e8b51b8fd4dbca8e73b2986b3ab484" + + "171e9d0cbb08be40ae60de8818bd7f400191b42c7b3200c27643f06720a7" + + "e0a17441f34131629388ac43955b78c31ea6602a70dd665f872e7669e865" + + "f6f40e634e8772d747608cd3a570e1726eb1ddca64f08582b022bb026eda" + + "6a913dc83f174ce3c18b9fc0503d3ac74e2fe45691d6dfb4af8c86d752a1" + + "6d6664fab4de08afe8858392fcc35cb9ea82fc42c42d48c0c0556267ea0d" + + "cc19b10f05e0318c4488ffe704b5036908f5cb938eebd3163503acaa874f" + + "592d945448fbeb93a877a26a72306a36e181745ba300afdc30cb7986919f" + + "3dbdc5c47ef1fa052a9e4aeeda3955f61ce2f30a0593a81dbaffebac5a49" + + "e5a8d1308352701d1ca9e620a67a89abdf5f0f8b1a0acfde5819981d4b77" + + "58799c0fe41030b86754837712af821c315301aa8dd50d1387b9fb92ee63" + + "10777e08229edd54e5e86b086ac281bd321082ef46ce298a6211aaa3aa4f" + + "6e55b5a4641220ec94cca73087760da1b1ac3e0da3f438214e691aa184b0" + + "535950b715a64d11485940dcaa3f72e0aa521002b1443f5e7880e2a85b83" + + "40d32db0fc4c4702e10f0fa24a35da9307850e945f608ad34d6cfdf6f2b9" + + "ff4f6b8e9eb5a883546578e2ff3cc5787322e4384640f42dc5bd05f432d9" + + "610dcf7c06cdf34762dd2a5e805e24aee8cebb3b4db9e4d1471da995bba9" + + "a72cf59ea8a040671b1d8ce24a3dce4fc86d2df85c8ab5e1eb2b0567c186" + + "4fb464f48c3ca72c7df2749542ed4d4be51b63769012ce3d06356856b2a4" + + "24995a2429a156ad93bc79c705e7b163149ce53a42c34a19680dfe4fd0f7" + + "fce38c30dffe9da9bc941d131f435c1398f8284a230e9d6e3992710074c3" + + "881d03aa309a9edd0fde7a39c33f6455dfcc5ae3fa20ea0e0d6549a43536" + + "b4cd8a2991a135b7d7a4265fb840318813091274414108f13fe191db7774" + + "6a5f4270f6d51a29ff523954f84cb76131d4abee79161dcbd97dc1ef24cf" + + "db1fade057dddee00a1e0de0db1afaeed1b535f7bb402afa3b297551fd14" + + "8c8f3e05f1351d3a8ee2948daaf14e7fc448c4670c906ae076eac5a7c656" + + "fd5f9cd937b91e26c9e5adb43c138f8d65e447b0022a524e059f879c6e27" + + "4ff7e671f75717233aae70853d5bd7bbb41b43c47bb08d6dc2f54f9ec606" + + "9487d1267add72403d01552a3d138abab9ca8a0d2dc32439759aa5695f70" + + "1a17d28dfb85850fdb55fddadcdde4d220e4b05821e5736d346e7dc9c945" + + "72743366488b1de8975184771361894b6520e3407c5c2e38473430969e35" + + "b106024da8618665d58c9d084824a28991a33658d6ec702139e01b65b7d0" + + "cc537a644caeee880657803d95f5f67816948d5ab362922f8ffbd531473e" + + "b0ff8fde2afc37a4abfa28dbed0be1b3d4ed48a1d02358e8403905d33b12" + + "3066e7a9fe2491ee9eb24fc9de7dbd322c8ddbc5ebcd0d92cd102ebac96b" + + "90e2fd784fd6d4b699304df23b17d963080a013794322690456be525c071" + + "b78fcd2d1148026e44ff14c4d0f942cd44d2b3263f4a93b79ec7a618b4b0" + + "d77ae7a1f6e6c7c7e2f498b825bf1954df348bae45ae1d7c87b6787f1212" + + "60c9a724429a4a2491ef989f65acfdc72fa717486dcf1984905218e11cc3" + + "970a09d71061e6df751f100abfbf", + }, + { + key: "d9b0dc303188756312c12d08488c29f43a72e78714560fe476703c1d9d3e20c1", + tag: "6b9782f2a09b59653aa448348a49291b", + in: "dbde1820035997dc8a8ff3015b4e0674e7ce7bf0c2d994b7977f2d91b49b" + + "f200995040daeb1218a0f4307b6b8211913992b070d321bdb947b4ba5017" + + "a0885e7e5502710a75cbbcb56d49e1bdc2bc2afa5a0e83851162dec41340" + + "bafc41c5e11fcbf4ea2ac45bc57def4742281bbf734777f83c9ae1ea3d5e" + + "d42380230570f59c40d5dd9a2d89b75fa3c92664f12a274d965ed8de79a8" + + "b37f3763939ad21d1703ad794f617c8b32b20cc4dd7c1b7f969a65e1bafa" + + "f6c43f30c9eba256f10201910e2cc31a9b13a46ad29257024ef8f2ee29b2" + + "ee63cc5b6230ab9f87cd5cb534f4b0bb08a790466e0d57b849fffa1ed21b" + + "fb0b27804e3ff9df7bebf14e100cf91691a493e53870abfad6321f6711c5" + + "0fbcf1f0b2c1e5231d6c0a08e710525176355f6f82bedc1f787f0d3cb41f" + + "a11e91ebf9f4cbae46035a371232d63ef0d8bda0355af8cd0a2f7d1327d8" + + "0ab769ea0f1da0f76ec99cc737b5ce84675fa8a9ac0c98342bb82b5848bf" + + "656d35327ea01a1b09d84ab974c307511af68a30cd6978b529a8f58c68a5" + + "9d476062ace8897ec0d1a90d5d167e29ebaa6f46d93d697760c8771417ce" + + "94c0f3698985a98702833d1b68641b811840ca3d935386dbd4600fbc81c8" + + "728c4fd0e4588be739a048f03bd4ac651ceecd7e2fb120fe7190011f957f" + + "cbbfdc025f1ca0b356208db8cad87fcd53c5d3a30a7c2a48140ccd4cdb49" + + "f3961cef742caedd1e848bf3cacafb0da030416bf3177877aa0bc5f9d1cc" + + "41fafcb829d5e3ace9394028683d712552579e024084a6b855830ad9f567" + + "ff58f05d3ec263eddd6f56adec378f167e8dabbeaf7d0a9e65c71660314d" + + "6c8d54beeca2711113fbc32a2ff8c0daa8373278d10085d2a0660ad53f4e" + + "1ade74a483be180180acf9e9ad3ea5bdd9162ccd69599163a451c6837d5e" + + "a5e115bd9a560f395128ea002ee739009a44fa46078b18959933fb6e866f" + + "eb4612a56ce93b1affcb95fccaa18d71a148582ba1412a5daa07404fcb39" + + "c3cb4a2519cc506c1172c6c326016ae2e5410f6a438569f35a50d45cbf3c" + + "c46188651aa22c257858f60649cee8c05c75953ce49358dfe5980445fce9" + + "614ccd16d333ad236e29d204691ca0bf46f29da954bcaae52e41016556d2" + + "f4cae1d37565bcbe84de1b49f344d0200478a38187da29c155cc98184d9d" + + "33dca088d70054e0fce321f7a90c48a14963d0ace2b4e7a24b21c14a5e67" + + "1994fe1f7d22d1135d4df9268dd18d323fde3603288735626a5449582d35" + + "30e2c2225414e05a8c7b987c873a82e272a5d83e59b90f3d7264631d6ad0" + + "4a0cf3b5e96596a66ed5bfbc24ab6e4870aeec0acbad2cc5affaee06de32" + + "dca06f175bf763cf8e7fdf95941a177e934f0078be7dbaa4c9b6f5c16b4a" + + "5607bab5d56144a6ba3c7d9a084b8d1f4b24b6f9754ed207b230d3a2cc26" + + "259ccc725e1f8a44c4df8143e13edb5ebf073e2c9d2da5f1562df4feece2" + + "f6480987f093f642eb7afa3aa92dce2a8b60bb925cd2d11cf6c2ae7d2153" + + "1a9c8f068d71d0e682023932fe64e956a49347aed22b21084c4a84480491" + + "244ac6b337b6d12d5551ad5684766c68bacca62bdcafab6603c81bdbd8e6" + + "80d9d8b3825eaea4df023142e840f98ee251466a0422d810a54726a9f03a" + + "7e0afeb0043e60e2ba4908f951d2e87fcbc372096f2a9f4f2a95ad5faede" + + "3796b11ecf4401c3ee3d268bd8c46476c61e0ffc5c43c0f3c58c79e20f75" + + "520c102aa3c260972a870fc50f8841fa0553a9e30bf37ad282fb51b34adc" + + "7a933ca1691a8a706605ce0b906fdccbe954f8e5f2f63c42599a483c4be7" + + "3a041ef90ad930fe60e7e6d44bab29eebde5abb111e433447825c8a46ef7" + + "070d1f65862b30418efd93bfea9c2b601a994354a2ff1fc11c383e7bc555" + + "9e7546b8bf8d44358b1ce8cb63978dd194260e00a88a8fd17df06373aa80" + + "04a89172a6051bd5b8cea41bdaf3f23fc0612197f5573f3f72bce39c9f89" + + "faf3fb48d8ca918586d4feaea7e0f2a0d7a6afca096a081af462ea5318cc" + + "898a9cc09e8258a837559570cbd5eb901e8c0e04ee88ba31c81a76b000b8" + + "0e544feba576b3eb5272b53e46e96a0b35b9c759caadcec61444f8ec47c3" + + "45a1d2304e2708eeddfbfa75a98eab3493889047d690e84431d445407fdd" + + "99560c0bdd287e0944116f8ac62ab992ed3f1e2b415aea784b03c6904795" + + "f4326ff60bc839615f2894570dc9c27cf928ef192047528a1a19ec990978" + + "3b0d1a13dd4baf4a19e49bf798975abe2ad167dd574b32b3d0c22aa4d9b5" + + "2761e8f56cf2100fe5a39fceae3d865f3724d4f299d07ff899fed6baf7fc" + + "eb7189357bf56cf94a6493e61301b43e3ed158cb9c7a0e615fd9888c2db0" + + "7f7689762f62ef6b3ad4125e06b07a422f5040c3aa8b8f205d68356c9225" + + "56fc4c976165fed9599daeb297498ecf744bf6c7dc5e30604c461ad99402" + + "2eea0fb6fe33f82a97b5c272fd24162a94b761ec7e52173e7bb42e88b343" + + "64f5fa2c141ed04a86b8d00fd9c25bf77a8dc3e63f5543331405be6bf421" + + "6a891089b316aa4f887cb4aff0dfb4e80c2ccd65ddd9daa74b17b4411c0f" + + "c849dc748d9b138279dcd9ebfc6e6759a53f5c28a41bb82107d71cc161fa" + + "81291a8290", + }, + { + key: "fb70ae7ec12264ff9f51124da188e5b11dbf53cae2671363f6054b575b1ddcc1", + tag: "d9ab81fab28b3be96fa3331714e78c9a", + in: "c62edf20b1d53962b42386eb570b10378f9764421ecbd7c4802853332747" + + "19ff4c89c06005050fa9ba6579a844060eb7ece6c43bab520e683e0f36ba" + + "49cba259edc6ae35d41e0d7812a7d5edbe4d90cd5e0504d16f4c3f70d01f" + + "5a0313de55934b661ce1ec317968c2c4de60f45c66cded8c10565a1ca6d2" + + "3a84bf182df2fcb05956ed4d46b49fc0fe3bd23961d9466fde070341ce41" + + "bc6e148449360a31634fe10e91082d82def90d9da2c250ea72c58add2058" + + "d046b4392b78bc3af5b3936ed568733e8ad5672dabbfa3130a6a535ec73b" + + "da8e7223535f49f96cd35d56ed4792c5cb7076720d5461d96a2692b2ada5" + + "2be08fb7bad15d15a0108143790024f0f15f5adc275e783aa56b70844061" + + "e30952a040e4cb9650f2a010417812790105d8f58bd25d99b0db3cb16229" + + "3f6322e86cd5b0bb1505a7b998fb0f81d1e1915faca3c2c8ddea39115507" + + "80339430a7955521839deff5b301f3fad54edd5ebd2ac4ec9b1795cb4dc0" + + "e2eb62ebca8e886c3f1e507d10a0228c3027b472a7104b815f5ec8dae55e" + + "0783ff7ae9a3e6b99e381ad788206b135520cb870ba0cdbe876feea843b8" + + "5a82adc95a6d71c555f798da92b82daf0abfcdbc82ec30b1f12d78490b06" + + "7315735017a94ac150b44dfaace151896f873923310ffcd41e91bac04de6" + + "d70ea71565948c907ab21c4a23703fbbd2a8de6d3095f3d8f901538968e3" + + "60e7bfddb9d22036b1c23f4f5f1b2ee22623426a2d5de68c1e1a38e38e08" + + "e2b5670aac1edff69e9c73c2ca56cb69c709009ef1d541aff1fdb2b40c92" + + "9b87f162f394b76cdbba1f5605993e4dd9c312321d59b0aa5c6e33be1b10" + + "bfd00b92d4c02db064d0e4a98f2913c89051b0f0ead163deb5087b6466d9" + + "84f57553b0fa53850eaa142e072fd91802eb9f0d2eb7318dd620555e6ce1" + + "86706b866d41cf6ba81f100342faa14d801dc6f3d522db38fab17a879fcb" + + "b6acfe922163505bd23a6842f6ef6397ae5fb6e6016421998bd43b0142b0" + + "3ca3b16d6ccb7a47891c75c687d791a930b26aaa2e3412e7aa16e2cf1501" + + "7bf6df6d2e1c289af0d7ce03954a60c1dfcee5e4b3da51eb43ddd14faf59" + + "082005d0c8b104561f66c002ff426be60be769282fc5685cfd1968df1941" + + "73667e48e9ad681d35757f1199f1d93377bbad093c8cc3efa2bcb6ecb703" + + "694422772d15aaa58cab9e9ab277ed510f684114cc4a44ccadb3eb1c9a76" + + "d8619a9b7743106df6fb6f927ac49b22ae5bb9a9a4d231e340a2cd0e3282" + + "53f6d75df694826f60e4b3e758398793eaf73ef5d4b56cd1471e16400f40" + + "4a947e9737f4f874fe09a29ad799f4525156e3abbf0585c3c3c0a3744c86" + + "5d56db3d2ecba6bcbb1adcc8bf5f3b2a2d46d3eba18cda55201598a8112f" + + "d8f14e205f0e615f081b8ff6c5aa6669da776bfc7c34d5af4d0b26d0d819" + + "f6aacc53cf3c6653138b9a962acee9d6ea01d280c35bb1f05d1509238ccf" + + "004c5013167f804d1780d9f4ef9d45742fccac346b0472bde24ff5db9ae0" + + "16455a3c02256358fcd8e6a9aae94f8a37a1a3da58a889bbe3d295e16544" + + "2e580f59bdd31c92ffcab40c49c1cdbb4db1dd4882b66edc10fcb1704203" + + "c518c1d8d4c268588ce13fc38e0210aeb47d11d2603d4b3de5c6ff5e969b" + + "9d5904abb282b699bd04a6e9f1cb323679e30400d725aab128a032745dc0" + + "be05a46b02b34b93bff02523cd8498c021fc35a488f164a70ef1ceb873d9" + + "14a681d3a3a34cc76bfd5a547e2630d7741a284511bae5897d9f7a197fc2" + + "456af5c6cd7e1a93d3388c7a990b5feacd7749cf39fdecdc20adfdd540c6" + + "9d330195db7cc0d4555ea5f5356a3647e2265399f153c34ed1e217c5dafd" + + "c2c5dd3d566c332c7ddacb0d76ecd3a0ad505a4165443aa81b0f43cabfb4" + + "62942fe74a77c22b8f68a8b1a6d712d1e9b86e6a750005a3796ba1545396" + + "13170906d228dabf572ab969c762f8b296054f23d5d4a37bff64bf9cc46f" + + "43b491b41101256018376d487fe8097f1653a7a9e99e1ef2492600598fb0" + + "bbb7df8270be8b9106126d6f491f8b342a96ab95df6133e883d3db4c6a99" + + "402aeb58d371263a32dcf76d33c8904395b9cf0016fdfc15608eb43e20b0" + + "99cbe7455f7a76f69bba058ef96f83ae752587485657f89c7f26fde7fbeb" + + "a82ede581ee92821dc13b8202930aa58bd4f1c86f68926baca0d06fee642" + + "ea8c652d226af91a9638a0244f1a03c7ce56969b87cd5c1f86110d192e0b" + + "98dd979d74acca6c1956b1127d9a1f456053d17974081ed8ced0faa4293a" + + "319e5b25ba285c1151214f52c283e39c35af51c4572c8e395b7856697bfe" + + "dfc4145ab4ed0bdbe43ba509c06a196ae6bf30d7582550cb546c63b51833" + + "cb0dfff7196d83f6a1c6d6d712cce2ec1989fd9ff5a0a22ac5022b49d566" + + "58f196703e4809e7624fe7cfa6c13b378f5aac7e66e657ed7eaa942d1a00" + + "544a947199f24d736b8976ec2cfb563433c49ba131bd08b63636854219d4" + + "c45100c98e3092773ef492dd9210bfd8f54cfe2cddafcf5c05468d90e620" + + "0c2ef99d17fa6992cc45eff3072b7cfd51cabb07ea3019582c245b3ff758" + + "0302e88edc2c13fc43646ba34de37338568baa66ecff3accfebad88d143a" + + "fd1c3b09ae39c501e3f116af33b0b720d6c2baf5acd7f31220788b2f9017" + + "3ed7a51f400054e174d3b692273fcab263eb87bc38b1f486e707d399fe8d" + + "5a3f0a7ed4f5e443d477d1ab30bc0b312b7d85754cb886e9", + }, + { + key: "f7e7affceb80a0127d9ce2f27693f447be80efc695d2e3ee9ca37c3f1b4120f4", + tag: "41c32ced08a16bb35ac8c23868f58ac9", + in: "5a3607fb98eaea52e4d642e98aa35719bfce5b7d7902950995f4a87c3dc6" + + "ad6238aadc71b7884318c2b93cd24139eed13d68773f901307a90189e272" + + "6471e4bf9e786b2e4cf144764f33c3ac3e66521f845f6f0688f09eaa227f" + + "e71033b0f74295f6ddb91fe741323f2b54f420cb9b774d4291b06219f1fb" + + "4410b55900425c5e6fcabec76a5c2424d637a1641db6f0f6cad564a36a91" + + "0f49894bfd598e91f38ceea65e8253c1284f210cf7b50a96e664e562f3cc" + + "01c4fc490fa6d4679fd63fbb3ed8995a8a05166b573e92d22ef4370c6aac" + + "74ae94c94177e5f71143c6f340efceefda679ae76f6ed7f26eaa4848a8de" + + "8c40894316efbb06400f9695b18ba279e8947c032a84a40ca647d9ace457" + + "6dd0082494d6bd7be4e7928e749c78110af8774a5d43e9c9479964e2fddc" + + "ee51146460eac734311225d08c60706e40f298a7cb97f369ef599be097ac" + + "3bf1c275497bbd68968a235fdf8a61bc7cfeef0fe451bb04e662ca39f34e" + + "a8e3acdd0befe9762f9eeb275c0cdd43c80fc91131d1e0e790020975ab65" + + "afbea81f303ebd86760821efb4cad7cc01fd6d6fd194ac5ffe7703d890d0" + + "169e21b444cdbaf691fc741a5d99bd47357c37785755fa72582ca4754a03" + + "b4def86ded39aa6d9eb3f38801077e6d17e3cee3fb57ae83f30c79c3cf29" + + "0e2739c6b7323612cec3a561ebeadb4faa642f150323aaa9d270658c907c" + + "4c1610a5e1834730c08be3379cf1abc50c30e2bf01ce903927c27d85e135" + + "3db9e216dda8860c45925e2bb791abe5c8281ee6d16607bdca87f60662dc" + + "bd6e20224e7f009a86db66fadd8e37e0a59559328385090c6953cd20bb61" + + "f28a734fb056714f5159977f18e5c5f11de75f7a00ba807e47a29e4da32d" + + "5c67ec76ce4d7b669b5e6ee17e1df7c673dd8a7c87fce665cda8adb9547d" + + "1dccbdbe7be44846b4b121b0bfa65e4ed530789510d79bc4477e50178060" + + "f2668ac8956f39ef422ecb0e4cf90b8ce508552eedeeefa6c7d1bccc077e" + + "8088bd7e0e6aaf0bda9f11c412c270ee2ad6912f9808f9344a4bb137bdac" + + "b5b9372b00b0de026a8f5d1fb13972e1290b5005689f7636c43aee2fd443" + + "93d390371ae573f0e064b2d7df552b9adf04bf173d71c621795b9fb503dc" + + "5e918536c6ad25ce4a76f70e6b752b6d44be321187269a19bcf33ec899ca" + + "40e88b4eb23217095a85057bf95d8a54812cae4a7d32e0c2966a21376110" + + "74c6c8c3dd45a553c43c675d23308709f91be0b235d0222aa5e1e1ce08f9" + + "c6b45ceb5b47bcd7d7b2d4380bcdbd6eced452d93e6d8cbe18123277889c" + + "7f86b15fb991364a501fbf5d8244f2e3332ea0ab49e833c6f765017a4006" + + "cc7cd1a0365945a8d8873cb21832b210c83e451c01ac949de2fb0f7a420e" + + "405bf64eb251c6f022181595d68174b91e503187d3b3f49b60c23e44ea40" + + "ca20311305b413047bb22e89672758b74d6bd1a06decf09e9556421087a4" + + "0c1d2c44c5fb13d4d9625581ac4ccef1a1b5eeb5689aac5c0291aebda276" + + "50daf9d4396a64d02c6d58bcbd609d9a0017880ae0cbaf02ad0f1fc8d1b3" + + "ec987ffe13102d77352690c9b761bf13ea0b3a8ebad4a0823817fcaab4d0" + + "9b0bf03486620761dc77a6ba007ba07153b17425c4026597473e78863cbf" + + "430c0e5e9b04a83ad11506b61b8d9be3aeb06b5114e0d53d4724863eba12" + + "4f3b974bdb0d02743520409910621cd730c97ca984fe2921c38055f83ee8" + + "c4611db92e52d8ea51d89203e89df7586c574df15f3a96ed5a10bf04cb27" + + "f9656b5b11cf35fd21360b029ab26e9a741c6b3e6357aa1a41de2cac6e85" + + "f9a49e3441e60a60e74f434e1b8cd4454b11962e5507ebf904e9d6c52a7d" + + "9722300517c434758fbd6191f4550108b143eb16c0b60094fdc29327492c" + + "18a3f36737e506fda2ae48cd48691533f525acfffb619d356bf8347a8bbb" + + "4babdc2ac866e497f192e65a694d620687cfb4f631fbd6ae5d20ac2e3a12" + + "4d85f9391a240b616d829ac2adceedf8f3451ee77e4835639b13c622ef8c" + + "48a181fc7598eacb419fa438d4046aa971942c86b36eb8e16eab67105783" + + "d27fc56f5b66f35451b2a407d4648a87ae70807e45bccf14983b3abcb198" + + "d661d562dfcb00ffc569ca967171746e4e36f839946bc7d2ea9a0eda85b5" + + "a5594f6a9c1b179f7230eaa7797a6aaf8628d67fd538050cf47aa654778c" + + "11dbdc149458c1ec2233c7ca5cb172356424eb79479b6a3eed1deb9f3278" + + "5282a1034ba165032b0d30733912e7cd775cdb7e0f2616b05d521dc407a2" + + "ae7dfcf46fbae30547b56f14dbb0ead11b3666666c45d345cd5dbfa200ae" + + "24d5d0b747cdc29dfe7d9029a3e8c94d205c0b78b56d5e18613b3169bd44" + + "1b3c31513528fe102f9bac588c400f29c515d59bbcb0725a62c2e5bfb32b" + + "5cf291d737e67f923080f52d8a79f2324e45a3bd051bd51bac2816c501af" + + "873b27f253ef9b92ba4d7a422e2fb26a35c1e99eca605acc10d2a60369d0" + + "1f52bca5850299a522b3aa126f470675fa2ec84793a31e9ac0d11beab08e" + + "2c66d989a1e1b89db8d11439ad0d0e79617eafe0160e88384f936c15eb15" + + "ece4ff00e1ba80b0f9fb7a7d6138bdf0bf48d5d2ad494deae0ccf448c4bd" + + "60f0788d3f2b76de8ad1456f7572bd0ffd27bc2836d704d95e9c0df34571" + + "9dab267dd805577fafda03b834dd225ad9714d2bd182b4103faa5975180f" + + "90d5d6cac1825a19b9d4c87cc825512ae9dbeb33d2759c990905050f960c" + + "db3eb364c15b593524c882902b2a1d7fe40ea3f54fb0202fd8821463c7e3" + + "4b02a1209ba0048a9805f0468a13e03d18009318ecd92042959be263a51a" + + "407f1e660632c4247419659a4e073a8e9cd4a226763a7daea464d5427270" + + "7efd053cb4efc0504602c4f63e7d247b55db2ce1c07138f585d16cec97a3" + + "0731d5aec2166cb4de41695feb76280cbae1af8a2e67c2d5a3ac5487ffe8" + + "640f308ace6137e83576b79d586b663122221c20aba7a6bf60f73958f436" + + "59f087f850ba6e2d7fd862249c5fa6b20e3e43d4f2aa10d4c9cebfcbdf02" + + "6b8d103e4f89b93dd8af172f421001c8b162bd6d0b847a58ac108b6d6cc4" + + "9c7a9ba069deee", + }, + { + key: "e3d21f9674f72ae65661aebe726a8a6496dd3cc4b3319f797e75ccbc98125caa", + tag: "3c95668130de728d24f7bca0c91588bc", + in: "baaea2b4b4cbe9dbc4fa193c376271f40a9e216836dc35ac8012476e9abd" + + "43dac6b9ce67dc6815904e6c84a5730cea0f9b4c6900a04ae2f7344fd846" + + "58a99513ffb268c6899dfe98d605c11e7dc77de77b0d30986f3051754503" + + "7c26be7b719aa9ca1140cfdf4c586b7fe726a8bc403249396a11cfee0a6a" + + "f6c5e72259785cfd13c2897384fe527100170001ea19106aed38f7d5d9a7" + + "ad43f0b41451e19989192a46b4f9734a774b6304cb74feb7d83822044a24" + + "2e51d55c0b8318e0439493bd1a57cc13f6079166cabc46877d003dcd39b2" + + "c0b90f6b32fc77acf04a6c125e11b35d91e2b18401cd53df4aff804e3c67" + + "a8bb3894b27c6e9b0070b53a85aafab0c0a253f9cfd4d3cd3be52428385b" + + "24a3f9f71660ca2c38474d14a0309e2f400e2c21af6e379099283ff241d7" + + "51da5a96a8dcbfdc43b913b29cc8cf8020eebb4a67f5bed31f2e383f8656" + + "8c815ff172382b425e95902e80f5fc219eccb51b656d37b56660f749e5b1" + + "4976a23648680a472d02ba71476e0afb29a0e084984f4eac3befbf8dd802" + + "2b7dca4dadd18bbe58e49c49ce48a06a71557a9a620c51e2623f818e4d62" + + "c2564c7ba04595cc109685869b183faeff2ac7a65049fc57cb10fb01951e" + + "a525332782d691f9759ec2ecd68bebb9c7aece5d522a08ce7830be520db4" + + "c9d60a2e490eaa0c91e37b256a97f84b39fe3c77953748c3b86fd84e9547" + + "a298c049cb28b8c85d59548b8dce635d59487c9de615802d16a8adc4c0e7" + + "80f35b9f10588a431b39b499dca929ab9d225f26e5721820627fe62427fe" + + "06d5773a50878b6effe840dc55bd3ea0c35168f6b6a972d57e8f88c5993d" + + "1ae33e0b7e9459c123753b518c184de7aaf429df078c9a18a29af77c727b" + + "796f5c1a501fa8105ee873c4e78c907142eb19690638a182fddb413adb06" + + "d66db19c7f6f46dac582bd72a6347b4427a576eb769d233febaf7be8f768" + + "337273c12253924f15653f9f3602b783703a81454a1dd7a8772a9ab1eeb8" + + "51be33e0c6c0708f3cc2012cabe8e2f0c38e35372abe27bc148fc4e1054d" + + "9d151f80aec0232a3a92dd77928a3678ebd7d09ba7b4e1d83227257292c0" + + "b8bc4a76de36bff6c9deb383029afaf4f37d5b935dc080a18665545e4acc" + + "195da0b9545d8902408886204b64f8548b32d012e0cdc520c17d9fb3be97" + + "800c2e2b945cb09a75a0a49e5d4d81c4194d91e839333b2b9b9e34d588e4" + + "e20cc1e911ca0a1429fa70ff063f0090fd842f89dfc5cc44affcce4e1e1b" + + "8b11c612f66b074c03ac2a055fd8f51ac9ed4f2e624589ff5730721d077a" + + "fb4c19e43abf8cf3ffa698362be8be51e92c2c91a4a56be64d9ac6d3fbaf" + + "5536a24c7fd0adaf74ca84c508e5e8c8bf7d4254e0c44158bd26acdf3f64" + + "e78438b3aaff89ac9986cef1e3a88d5bf2016340367a1cacd01ec167ec6d" + + "185d93a2a220d718b43ce1d429d2cb598605660b030e51e8d75fdbdd5b8f" + + "8677675e196a40a88285b18b24c5d2d594bab3d457e6f9e503e38cd470a6" + + "9ff8037c9a0a0f110a434335d954fa856a3721e0edcfb14287c3dd9639ba" + + "4db32b7da0670dd0a872e468e3819741d0d4ecf0a4f7a011bbae1493c01e" + + "642757491189f8664be3ec6437c4f3c76abfb0276e44a4d28871d3487c2c" + + "ce2f230452cb06184bb8620919659a7ba0a3d5c12ec25678b03403715ee4" + + "acb6a53d281036d8f3a085143cf5ecc3a0c6c92129caa7ac1f645c7bb95e" + + "4f63da38dc319e2ccff4a9006f9b9b1a38c4c39f6dc686bb82d43fb9fce4" + + "0c767d3ff22f52c5f9900130c65bb6a9cc7408a777d49b70946665f4a733" + + "5099376b276a43dc9a6382bb2d40425f6481b1846148434c672b84dd7a20" + + "33deb5140d43ba39e04ffe83659b6deb48629e1abf51e68748deffb756a3" + + "ed9e0807506b248a024cd509f539f4161366547c62c72933584e851599b6" + + "82ec16f1d79e9c6a01cff6f51ba7f46b67cdca09f3ab8496322b990a6116" + + "8d7574854a1cb1cb8f30a303dbd13a095df56dbb940dd16ce79879cd2d73" + + "80a419842fa1b34da668286de4c1ff5917b7aaa64713c349dc8f855d04ae" + + "de9a3a4d0739dfc36510b1e7bb1695418164285c44631b4b1a7c5798ecb2" + + "d976c1a3679a827bf0e8c662567e402bcc1354222036ad5959a6f0b8508c" + + "6a8c7d4a63e7dde154d778fc80a011592771d55801c7e1297b00b77f80d6" + + "314ebd1f5b3057398d1943599897cfabb65e7568d8fbdfcbecfd4b8a83ca" + + "0a7bed08ab9a656424831e0d7718c15727af7c83b2ef5eb5684aa044eca2" + + "ba896811246766248b20a325094a4b4159f9cde1ee349be6dc3c9a190453" + + "0349212a9537f65ae333c288753cd2bef6c5beb2f4164168d965a2c0fb9c" + + "c8c73d9e776e23d53ddcfb83bb7dfe2a1b8c781280f449d6f310faf8b53e" + + "89e6a611d6d3f42f2aaed5259730d149b3e7dabdc9f865bc1555374738c8" + + "456abe112e9628fb31efc2ecdc972da05987aafce728ccaed246cfcdf518" + + "3fe5dae528bbfb99d33194167e0f84d462d3d0da83e92227cf57922c7956" + + "4fe44648d87c69ad708e797972c44c4a5183fd5d1150a1182e3d39c3cd16" + + "3920f1d7ed83992bc4116d9351ae1c6c4827d1374242e374310409f32d5f" + + "0f38c78b6489c568b791c70394d29ea2516dcb10e51bdad862ce3339d5e6" + + "14fe14f150961809c36e0a2c8eb872e9f7a1c0956fbc9194cb63ff9993e5" + + "d0dcf62c0f49e81dbe99f3656c4dea57b766ae9a11254f9970618f1b33c8" + + "f339f440de240170f7a21f03ff2da42102b323ce2b9b7d0de5aae324d1ba" + + "c87b1e4c5279a566bf659778f8b03882aded57377a0f1b063af2897060e4" + + "23be7cefd4aa9a28479c16773944d254fc21d3e1acdf508b7972372b5991" + + "3b8b088e93471a7d54c6ae4c52ba465ef07f19f269677fc2f64d3fb3d7f1" + + "9069d6c7001d4b002ed6683c59bd5651a450503b68a4a00820b8c17e3263" + + "18f32c21dfbcb2a02a104edaeff67ec09533aaf3d1a7fb41aa5d506ccdbb" + + "e6e35fa0a263c0aad3acc91182addf8c5bdfbd0626702694b8d652a63c65" + + "8d6b2b7c75d015630de508195e1fca9573b61bc549ca017c4bd888194d44" + + "3e031f36170215a301f922736a819f3ffda69117170d1933300366c5f2ae" + + "1052446ef7c3b82c5868be158a881597132f51c91c80c24ebf621393dc45" + + "05fe057364a76ae67494a8a5f67acb551cfe89f447df272ed9c1509fc330" + + "2c3e16541452d4d68438f26858724012ad3b72c094b9f166c6bedb8336a3" + + "41e032988f39cf53535789b320b5424d07b6bf5f8792e3aceb0e868765b8" + + "611d7905089949e0c273e2410c72a146cd63981f420405bd883e5390e985" + + "8214a8db714e8400a21d0636d7e5d9671a3582ab9ff032170b8dd6b9d5a2" + + "144d065228fa54aea9a22654df67f3f62c5fc59d68914d8b219829b536cd" + + "2ae937ecccdb6031d94cb3", + }, + { + key: "84373472e362a356bd5c9b50f55c588d067b939009944f02564f136c62dac36b", + tag: "12dd5297cfcec53deae1dd5f9325d894", + in: "860d9b2954c3daf18fd67eb8bd9e6e3de2e4988ad9b04b1987219204dee2" + + "388db1c59a935de27bce29e7cd3ebdf038785efb35eabd4c3785a62b1d9c" + + "3ffa25e2273cfe5eb10b4ec6152cd8f21dea415421b452efc7cc4ea6bf1a" + + "b85fa6614e7f6d650125424865386ff8ab53247a63ff023b2d0753a9e5bd" + + "458d6ab0156fd3cf2d5002f902f927a847e8c4a8426b0a5191f5e237d590" + + "2659ce9be9024750d1d618a6b8dd57efb6c2bbac2930858f1132639391aa" + + "9e8a620a2a7d64bb7e943c77753401b5b619d95ef857df25a52b4eb97372" + + "a05416706b2644e2687bf1d42c0cf06e5eef8a1fc7e178440bfebb85c44a" + + "4837f69e43a1789728a999c5e04291576e757510f22bca11583a4e93688b" + + "442f2b2dab8d5ea9441ff09b8287862ca538ad979297cc75510a3d9ef36a" + + "662b4b7c373f184202befa5bf3f315642e6210763d033b7e2c59731cb356" + + "045e9470bf2f83cd62f11b3e904b0c0b1be99bcb805150ba7ef12b8df3ca" + + "bfc5055640687d710ab88e0fa8034b26112ebfd044a4b290b1c6f6d18c31" + + "ba9880b1cf2d81b5d02f00d6d351da5dbf47b6a5cb7b53eaf6de52c8a68d" + + "053602ccffa37ccb44a7683ab4f8a58c4bbc9e140e4e6f3cc10a5c07ebd6" + + "070818db983f9f415168606011efab6b8d7b4e61e8eadd8bfd8d028b89bf" + + "b0a16996252d7b4ee4f9ab50fc9d6e482ecf99beeabc38d70efbb9a0d4b7" + + "9a1c5d2835adf8e25111352eabd24d562644efc97637f695e4792f2049c6" + + "00f4d889ceb951cfe289adf159865d013046985d7fe2598014bf2dbbc528" + + "b4166fc2180e724ded8e7ea1c8d66338ec50d955d5594a0a7b4655338b70" + + "e8978485a722df814fdc6fd2436dbc060121fcb575672b2a5e454c1209bc" + + "2bb21a99d39dcb3c697306dbc2104d60fd8051c43ea2fce268987d0ec249" + + "a5c02f91d3b0dfee181b3cf8ef1ba9665daf7ea1f1d3b216e378943b78b6" + + "bb41e5dba095748bc776f8df6383033a1f5504955da3f42153b1c7ea83e2" + + "f90b990ea0c5bd3906b5c4060b19f447ec7762916b8766e5a23bc4d39cdf" + + "8e27752df8129b60ccee1731e47383b589d4fcad865eed4041a186df206e" + + "9fb69ab6ea092e36f186a6fea8d77bd7f3ab0fa0e29404d617317c75c832" + + "854427848237cfc18486c95f7213b9d53f324da036e8d298133b5003984a" + + "b9d71836f9f1b059db90005a9067c261bd85aaeed4d623df2220eb52b73d" + + "d683abcdee5cebd411996f853752f638bd28df6d78bec2ed3e00d7beea06" + + "2b81c19682ffb2f6abe3a3623a2e0570650c1384f1818d76fbefe3a7ef3f" + + "46138160ef897f9934e00e066e215230e719c23905dc60d7fa4d666fa52f" + + "e7737db15126d3262c3a4c385cdb23ff3b56c131e43b241f4a6062a1a248" + + "de9f13eb82c11f7b6a22c28904a1eb6513cdb11179067b13c7b5f83a58c1" + + "4f2753f19fdb356f124f52923249d6e4a2c8dadc8bb0fc91e360155a14c5" + + "c194334b9f0a566d51fad98592b59c1cc4b40eeddb34e64f337f83874884" + + "0583f853398c343dabc29b9444be1e316309fb8d81304d654b3d4bc4cff3" + + "55fc31278fe22e649324ef10acd247c0b72397edf96a1c16bbbef0640296" + + "4d219575fd23c36efc1fb8f8a34b510ba9bdfb3b478e236777ef7c6c47f5" + + "5a2bd0383d8eed3759456ffcffb15e61985b08c022658a5ffc875821bdf8" + + "83f69f096dcc72a96888c3af76db57a54be701759670bf05cc9015f5bf1a" + + "745cf755a25b1403a870875701427f820c4b29eccc260f30113629ba03e2" + + "785014bdcbf34d0c67aa6aca20d2dece811788686d5a45820d2980bf7d69" + + "d5c820a09bad7bd95166f63dcfbe8652565c285e60e2704955d69b3037d8" + + "7f5e6567d95b8891276d5cf7c59047d10a02ae4a28794405e2524ec2d595" + + "1b36ad1b9d5265fa098a033b88aa66cd9eaf01eea49c7dc4cc51c486f624" + + "507a2be23f152f43709b2cfecee44945ca506950e90e70164b77e12e1c13" + + "0b4d1021c2afa20038f190096276cd22e89b6e7dd10fd58fa033c9d42536" + + "98de3f4908203be8dbf259112f840c76726d982b4a837cae7139e27182b6" + + "1b4dfbcc50e42d5ab8532edfbd30f668879824e9ebc34b63ff1526cda81a" + + "e38352a774d79f73219500e57f0159a32326195d8895d965071834876a45" + + "c1a3c0bc4b1638535f7d40011cd5b23343fc27fa318c1aa3f9d8c43351c6" + + "6148dc2175e0e620813266da3000954dfa22048f305244629d512e852376" + + "6248a897a3ec3e2983aaa8a0f025f18feea57a5153a59b02604ebfcc7a9f" + + "b03e62443df88ead9dee955e23bcf6528c278a353f254c9484a67a7b263d" + + "a301923a4efb6866aeaaafd428e6da48781365bc49e90cd16b2388220d08" + + "bb9f79d14012b5a8299a651917b6a829488753b6ca449a14e8dd8c5fd5ef" + + "657d627b8e7773475b802655dc033694f24376e3b01e519d1aa8365d0e55" + + "92d0a4adbf555639b6d75d7ee59a7d12c6c11317b7927f11bbe75ed90508" + + "b0698420e231206704d22dd1f1740edbdcaf19a47d66ace4eecbcefb77b0" + + "85cfcfaced4d2d6048ce76434eb79990f0898adb4af2c377b581ebab3f3a" + + "150f40dcae002d4caa60050591c0de4ba83bfd59a08670beaa4641aa9829" + + "bdbb720d6eb8b2f3e864a98676a67271a82cffdca2b3590a0b5f97efa5d4" + + "ba062b4798707159782bedc75e5363d5f5d55ec2bef70db22955adf401fa" + + "c3b7af937816eb25d54d9f2a92e5a2a04bd8b8d7568204fd289f5ed2e033" + + "a76209d288e11e8a4dbb06b9029e90cb186446746853f02d738e06bba538" + + "894e03e2658ab3d7f9ac861d2cffdf12396004d1cd15f18812d3803ab9e0" + + "6f41c9b374d6a0678bb82ce06d9e3b9dbc8d2e90b8f64d0d040f3fa8a3fa" + + "8be71d2b3183cceae1bcbfa2353689d842f7d7052e5699dcc70ab2b58761" + + "7041e5aa1e2f41911d525505f061d3ca45152f5a7a1fab50c674e4597a52" + + "b46aafb4ba57413879cad1308321843abb7c39696fc2f2e225878bb1191e" + + "e151cc76f1a1b8d491c1672fecbf710db82dcd32554361967fc839c8e5d4" + + "e488856e1b9382eb3fc3bdc3b6886a3cd79761b02bafa080a745ef6afa26" + + "822f1d10d5e8eefb842837d82c9986e78fc3390caa142b7643de8f613e5a" + + "890a57f5883409549537f8139534f4ca1b60f33e42be25433f1d82add530" + + "6a4cfce258c0d4f1f3c9148ffb5c4b626d51f78ac20bff0393b7fdb4b9cd" + + "70fee7f69892c8a9ee089c6c5c7bee0a1b825e5b9517f2c82d6c149735fe" + + "45a8839812c2deb2a355b6230697053092eca450b7b0d3242b2689efe364" + + "09e820d91fa4932034d96495d9dd3baa4b385da815a7cb69438ff648b326" + + "e7efe8d688e88570ba59df7c439faf72c95317a10c984c5ec0043407e9fc" + + "9b46487810eac19d2bb40e0a654935f76e7d8861480c5f48419eb33084d4" + + "0e1070e5ad542c94f58b49e67dd05b6637a2c67d41451b7e00ba30eff221" + + "755d6d427ec634a2b95980d274a89579feccf1c7df3787a9435e588f2496" + + "06a93b7ac41c8aaa84b91c95cad9463d4881de7353d95b13bbde4c9da90b" + + "f1fe96257309a416407c64368b5564f022c4a493f2a39df1696f45801e42" + + "a5", + }, + { + key: "2d0035a30d19b9cbc7a27561f3ab474c01115c4499b4adec660ea06ebaa1a14c", + tag: "a2c77b55cb0c076d8ea83cfe0e64f293", + in: "4e667580ba4f38f64e5cb5566bffb486dcae10cd17acb3754251e837767f" + + "16429bba2b832f29ba538f97f3556548d163be25e69f88fff0743150623b" + + "e0a1d82af9384ca335927a0e9cacc3dadbdf1e24fa5c81f2602d109e1400" + + "33929e409b9a0fa4f2653944edcb8b3ef963ba7f8806196c73bff0ded670" + + "c6def5d240c5f3daa121f8d5bec9b2a0b0f1d62d54b013dc742d6bd46325" + + "460f692b76d4991f0796820ddebf150c7d33829795784dd2759b334d2706" + + "70a7264941be5d99d460d078a9eedc3660cb3176ad302f9365f0bd698e46" + + "9f3e63511abc81109995dba17be1abe8bcd28407c7fc8d02c14794bb033e" + + "178a94f6dc73719d5bc235f980a16eccb4121ca83b13c4e165931ae4f192" + + "4292f8cfdf1c3ed40feb71e13d919b48fa296dddb4d23114a3d86ec10f16" + + "f314de4cef813ed24b49f4c7bc44cb8424df1f70e8d77366161c7cdd709e" + + "97610aca3a24fb2202ffe15eaaa25d711cb5179212a2c6497a13e5d7c365" + + "7bc502b3d2ebde2e57b714dd9bc21e73795f3d35d620613918c4c9aa0e89" + + "031481c97a5a4c15ec6abe42d40498c33d71c823bf1d5bb5fee457e2fff0" + + "bf777c80c6e3336ab3ce793440e74b336a8f7034f6ea2e4ff5ea4ea7c350" + + "65cf2ccd2da1d6df29bde10f4cc0202b5e4cf7ed097da49b970a6db41e5e" + + "98f3845b42f46663b1d1ff01da71389a8737ba8f51eac1ef357ba5ac9a80" + + "dd2c7f9476111dcd651fc33f4c86dc8658656f3f02a8878bc38ff0d0a1af" + + "2e31fb92eaef08c50195490818661feaf90e8b6f5daa1ebedb2cdbc8d5dc" + + "16db3505f9611ac46bc37931e02c1fd6aad6e4b7e187d5e6f990fddc9563" + + "2b33f55bf68b0db3890b11113ecc839a4fa4de25160e574289aabe4d8fb7" + + "9cecf9d2fa75ac8d0195beefbdfe0815f8d7d9751c1280a29b547149ec7c" + + "2295f5afa53cfb516158086bf203357eec2a5db71143f996c81555a47f92" + + "209719a71570a5553f1ff9b4b41827dd74657b463f36623565f0c9f4d2ee" + + "8735d6af56ceb3b3d0ec516b22f0ddafbc24647481f61ab169e2616c91c0" + + "e1f6a35436598ed801670e1dba76226cbd0544959ebe70f836c8a7df575c" + + "b907d780ed5aa0d6e4e8e0d2f457efe89a777374aa49d4961db96dbb787f" + + "021d99231001360d532a70ee1fb94bd6f26524dd4b7556c6d40e08723d7f" + + "9905aca66c4743f2bf8b34493bdabcfca617809a867bfe0a4f94c756a6a3" + + "dcd04ffc0a3ac671a0afefe0d5d447efcec48c6368998760db6a572676d4" + + "29b6d3d6e0c815650447748c4b27541c5447acfb8f7261b6378f3fc0fdd7" + + "375eb9d458648c7fe9cd96344f11aca912cc5098e9ee39e0b6794cc1dc2d" + + "f1b10f927102705efa20e667b63a91f935c17764650b287f5289d5790766" + + "555f31985c5aad94c652ba41fa9c0195d15405f1fcce9e23054a42c8a252" + + "da83bf6268782ba44edec5d8f94a20b1830cd1c5894cc6b9b52ad0b12a5e" + + "cf3195a32a0b02483ae3b954ac6f3af1e0f334221279d03a72138f3a2cb2" + + "1e706427c4d604674dab88d429f28a67be7a996126e077a1dcf8989d90d0" + + "8b08f4abb9a546b3c64ecaa287bf3468c59add86365b885f52afe13ed8d2" + + "69ea61832a7ecbb96ff3336f58a1eeaa6dde3611f3ff7c2cc8c9b745b0e8" + + "b5919914245a49ac192cd77d10deb9a249623f696065a532c20eef9e9b0f" + + "e706579566a9eeb14d4e8251a7750e29eaa60f034c1a7a1d51aa03a45fff" + + "89acf41080deec5506128b06f003fa46bc4021a82fad6a8052a49744ed69" + + "45bd9331b5ae80d873cd042bff079b2b9d8af8065a22c449c32a56dbbe7a" + + "80d0f3e30b9167532506915883dce0aa9cb749e4368c595c5bd33b57e36d" + + "98cc9bf91cbfa47331d69b5cbe9c92bc66c0fc9ca8717bfc108e1f710333" + + "14dba02a28b9aa05890cb01ae9175806c3c4215bd446f6cc96ec5d08982b" + + "4f83cd1646160e1d306b3cdec02d251f0901b03e8c3c35464eaa5082586b" + + "b55482db97599d513ed8d7a82e32fae302684b7ede058474c1fac7893444" + + "16fec93fb982accd162dd956ba2f31a894e9366eca00e6e997fbbf9a2980" + + "8b83a139f6432147a717381bb8baa2205715f735c1e0db273cdda6897c9f" + + "39bf0d7eb7caf93f657ef4d3fecea28baf69cf36d3cf347081df3114455e" + + "b4fe3e49ad3c3f14435e0b39b6c0d16db0fbcfd7ba8da8760d5952c03667" + + "251e7a4c3008cfb0904225e55c23b884bb09d26631650460c4240bd5a165" + + "b531ee76ba5749b3bc60adad35de519321c1672b47bc35fb59f7792a3495" + + "11b2bb3504ba4a28717823a27a1f99ce6970290b26efcf1e7a0399b10eb1" + + "0c1299c09b80f4520d00e7908d004d5b6a72a411759cfa9523f6b2912234" + + "481b1d8fe4c2365961c0528bd593d42bebb398b5836ae6ca013fe440adbb" + + "0090e8ea274f4d8bcae483e3663051a328f7c12870b40e4973a9797a2336" + + "3d3c53e1b0d1a9159bfb26158f44734b3c34b571be641bba2db937d4ae1e" + + "edc807b95b1c2a7d44804885536316ad38aedf0d83b1519661f2bb5283cb" + + "9c50dd61c3753433e988189f26962d1f4befd444257d0b6d5b819d5fd572" + + "22c9fdff032e07a4d8686d451e71de4748965309c0a2d7c422ab7cf3d96a" + + "8c0a1b0afb229debd1c9421cb828b9f2be96bb9d6b5be7ef8134bd9ccf81" + + "51620937d720d83dbdddbfaba8ecd2eab6f1974090efde0ca963e9fdd691" + + "ed0cc5e074c5780779222552fa46ddcd951763a32aa3a044ff4a73cbab41" + + "dabb3c2c03fcda68303477f0dc26f35bdb5c9bde721fba1a2db732a89629" + + "a8de3cfebc3918df1a9d5053d09da5b7316e3285bf62156ca28cb64d343e" + + "72445fd66757bf4ab374fe7932a65f3d7fb6e42cb12e5b67ddf8530383a4" + + "6c1ee7ec8883e454a467df1aa7e468a6e7035515f473901efca5d46ff358" + + "70e0cc2575bbd7f8866c8e73cb157903a1694ff3051424f28de826984dcd" + + "065dc3658df144ae3a6d37b88c367e3cf7c58169dfdedda4a2821ce22188" + + "40472ff72f0dd1a6b0100555ff188b80f835259a634405e3dad61fc299f9" + + "307e27503b2cb7714bf3b636cc64b61d2e374119c8ef8adb21f1516c7fe2" + + "38c807818065bf312003c12e02525d69d9629a99e4ac66ad2e792f302cd2" + + "a6f5f702dd28040738a084a7052f2c3ed0924c33b7a5d357b7c9a29cebd8" + + "621a4bfb7bb34676ff210d59f7f9d4eafb7c5c490c9ea48402af5bb072c4" + + "731bdebcbed4e8e08a67931b6d7342d4ef7bc4a75ca1dfbd32ed6027d8fc" + + "b71e3f55565c02e06daa8c579b69774889181291c470576a99e11f2c5acf" + + "77e091ef65ed243d4287176f7f6ac7aba6908c9ff1fa43b894a499b642ad" + + "c01b2fa1c4b58801411941bb448f1f7a04794d2cfe5db1be61f7b86d6eca" + + "c547ee51d4c9050f9e9f318dae958c150acc21c878f0c7df6065294eb1d9" + + "a278c920838a0db752b080a32e67ac312fa76b589a385f31847196076ed8" + + "1021fcc375bfcc8e1361878e2693860eb21ff0595e4eaaf7897f2b79367f" + + "7c4f711279bf0c93a97dcb1cd8d87e444ad5f4cb5c1de44e37868c6743f1" + + "cd72cec376726f26c8bd4836f9a9f9c68042f95ca6f9d7cde493e531c553" + + "8bf7ace6dd768db69ac7b41ce93e8ca27ff20a83ff2148ec5b89e05d8b8f" + + "5d78d0fe16b96f6eb8d3b20126a186085c6825df81aa16b3dbf57eabc360" + + "71299ccdda60e250c652408d9cd1da94d73c728440ae08fddb901aec0fac" + + "1050a778b10f94f84883bee158bc53b1c001807c43a3151fbf581b18dda2" + + "527430872834e5c380575c54b7aa50f817cf3249fb943d46933cad32092e" + + "bfc575bd31cc744b7405580a5f2eabe27a02eec31e0d7306750adbbb9f08" + + "c78cb2d4c738b2274c7310cbf8dd0e59138b6a91b8253ae9512fe3d7367e" + + "a965ac44d54a7ed664e5e5c3c6c2d942eac388cd32beffb38f", + }, + { + key: "2f29d71d73f7af98f96b34e939e1a21e2789ec6271b878bbebd14d7942d30080", + tag: "ec02f4953a9a63ab6f2bfc3501e4fab8", + in: "0e0950987f3508239063e26a13727fefcdfd2cea6a903615c64bf12d9ed3" + + "887f9b2cf7ccaa196ccc7756b09471475b9daefd4261e69abd23b9faf9c5" + + "1fd5d5788bb39d3c068fa6807d30f6201d3f6dfd31715d08b1733440cde1" + + "049608d23c4e45c5ed61f863350232f85827e7c292dc5f1eced1cbc912e3" + + "f5c420bd945911d3881ede5153d3b2cc85371fff98d2caf97cad6ef59001" + + "4017f9690cab08989851c2647e77e81401714a93ed9f938b79f8f54e3133" + + "fc2cdef259df2ba0d48f37bf9e43792e3a777214cf4aab6dde6deeb543a8" + + "813b71b5974136c1220d6218a252881f0f5677ff5b6aba127f19a5f3c5aa" + + "c988543d7839a90a3f947c4e4d5c6ae1ab48dbd40456d1aa65339a4c15eb" + + "520e8ff9f965ac4c37735937cf09942e7958f8a6cddee41707423f715903" + + "ffe0d15af8c3140d3a736d23be7485fceb9f07c6509f2c506eda4ec9d30c" + + "cc133708f48d8828e332808c84a745d337296d871b9794de1c5d06534aaf" + + "65587526a84e2521f8b332645e0e72564bb308ecf99b7bc69608474389d1" + + "686ffab8c49b7f04dadc28d2ecdd0f508dad2135843304e378b3bc7a4f25" + + "7fa4316be956e0a021edb8045f39fa9f002087f067199bd6001acaadd261" + + "4bf6aefd3f098f92a959685f24bb2206c347359d9c6adc6847117bb434ac" + + "6c40ec618f6ae8b75a5e2e4d44c332b7b06c8b4d521493b9b0bde8894209" + + "717a24b320214297b62dec741cea018ea681c9b56702068528b3726953e8" + + "c5e4ccd5029e4183e772d9834a56a88d45bf87603dfda40e03f7e894766a" + + "7623ab4dcc0dfc3086d17566945069173935916f772e2a5f8e1547348f28" + + "782400fc069ac0e2b94242e9e0f1ba2d0e76898f9b986540e61ea64d7f69" + + "1006b86ce61565da75eb16a8b4c5865ca4eebdde2190e354734bda94fe7e" + + "12ff47dcb5d5e6ad93cfadcc491cb350b09ffe391a157e14b65e3a211b5d" + + "4e447c3ff95571dbab33a83126d68dfddf9383b4359d4103ca64af1e6963" + + "d09e17eb944aa71e76711dca33168586bfc44ebe9fdc55497d83f238c66d" + + "bcb16063bc85635f0f1a6280563bca49ef971db96a41b6ac5e0642643262" + + "61eb4662f3d6ad4cac826db895de22c9b8aa35e6464a7f44e1ae7238e355" + + "068d68754ffcca76c50b7ce7ef9bfebac9eeab32c87d059cc7ef2adb5d57" + + "c7419adb394eef48441952253e8391e555730e29789d6293c3696f441449" + + "0aebe2bbe541e191a6652ffbec1192f0f9395b7ea370aefc1f1cc8438035" + + "d7681f12f1e11d6e334da188b10c302fc0f4bcf1de448090510a8f1d5683" + + "0c943a3c388b33a038c26741a4cf3487313f755fe7a28e25e44b5383c5f4" + + "cd6ef34d7dd73462226281899dc3f2e69809a0150f694673f31addc89888" + + "072a7d4ecd63d6b90540f9522ec05829a7f17d48728345ad808fb0203883" + + "3cbd018d612992a88df944b8e34a70920b3f26cda2e8bb16c3aa38b12b33" + + "b395c9ba5e809f60ff05f087112151af1b5987403cff8bb2dce79093f431" + + "2c744f911a6f3091e4f9ef9375c4dce4c241d2f6024a1797321851ca316c" + + "4e460fc060e7839deaff8ab5e8bf682c0f21ab6952eb793cffe690db911f" + + "50b11f56ea352942c43bfff51d4360882754faeb7cf28b6b32bf7fc9ca71" + + "fbfe1d72be05b8bac9ba513d731e2c9d13d6f2f10eb926edaaf0e3996656" + + "da8718a8e103c59326529e91ebac6ed52657c9690ccbf81028cd9fb189ec" + + "4de94fc0771e53302c8d9082835a68780cccd772660a110a1b40c57bef3a" + + "c1d69428aea549ed17663a96895a66a3bb5ff6ff61dc64908df49b760caf" + + "a5aff05e2766a418dbaa1e7d189a9edd55a04fee8c9d6e506d299abc36a9" + + "d67be035fea5d220f41d081af67615fe627c4dd04bd8659c7fa4f57f35d0" + + "db40d9684aa178d7483ed5d86f04eaea412e0ea05a4698377dbff4fc3a39" + + "1f6ce0cb833d3118d6c69319b511cce65fdc74928e270da0c537f8201eff" + + "77416155d4a39c7ad38c22cdbf7d2b7ff7d85383c178a835ec604c3f9ee3" + + "7399f7dd826e34f1a35ab75da44ba56f86097ddc0f3658ef5bd65a24f4de" + + "4255d0b03411a9d7f0ddc29e33cb865da23393471aa94e6c9e72e789206d" + + "3ba118aecd39727068f528f01b25fae2280d70033e4ee46b41b864bb922e" + + "001d8bf46d6fbaa5a594e926f45eb3a4d2f074506d7834b606f43c89699a" + + "6db00b374658d9333700894d440a712a1f25f5538f9e7c8ee57ae7e612df" + + "13292c8ba9dbede4fb77cc6c8944aaef59ea6ad3b36db398f4bb0f82d40b" + + "44879835f224d6e05992b1b8a68dd58c3dbda2fd73786492ee48c7a25f87" + + "264b766930fe9427487504fad17f8d230934f044e49ba219f26ead728856" + + "cb30eecc33a3946d3b1b781061f2458c7c46f6d96f3e06f369f97be91835" + + "f23b38347d1e381ad5be4419275772c2abd549522a0203c1ee9c96faefe1" + + "df413c4b7b2624417890e0716854b7092b3b3b368cb674035d3e6bab2357" + + "e7c262b606f7141b6dad2f6145ebc1deb7597814719784f3c17848a90ffb" + + "cb0289e2f3cc7da12442b837c4e47f468bca3eb4e944a31c48562c2f144e" + + "9e920ab5e4cf90a14ccadbae29af13db38cda911e3c8f6f525e6722809b5" + + "31a4de1926ab12f643d25af87eb8610df59eded6ec278242247dc69a4213" + + "13f7c2b26ae7a917c1bdaf66c56876e9104d40b59e6ca1431ddb77fc89f3" + + "14b46a154cf127688564a4f9e120d7b5816cd24a6e095dc8ab8b43bc3639" + + "329719f0e0f723e2f5136d82638e2249e648ebca67cf0306741e9e8d45cb" + + "903bca85485c4007397c88a1ce07266f4f611b96b7e0ace3074247a7dfb1" + + "cdbbdd66e25e172fd2bda74abde7f3b4cb5cc7ee7859f053b2f04f9de03b" + + "a8e96264117f502087c3ddbee8d850bf3618b4de90f7b3e562dfa57e4426" + + "5357236e35e71d1669226d63bca50b1b944ac07a1f794e73e80985689b25" + + "f18fc709367d63b8639d71865cee667536040be827145c08cf3e57a66678" + + "4c81115706a146eccadc7aa1a9f074b47e95bcba7db8108a13279077bef2" + + "64699fb87e5abf5b05ff3879d7c7c5169c7cae817c13f0859d4e9c05db0f" + + "74c045ecc30a51e515feea627da387ff780719395b5b9ad93179b16fad10" + + "5856049169dcebd43a7f39c549762405f807378e854b1654a1179d895ef0" + + "85aafc72c7fe1e0e1cd3abf8e20935e331145bbcece4f17ad24ebb6c64ea" + + "73bd98a7494c134859206c9422f7c4a057db0ae0770c4bcb08c1a6b9ca4b" + + "7dd8c1cdb3e4977c7ce6c1e79b9d6ad98e27d2759b53cee73ec037a8b686" + + "f1ff78eb8421f41c74ce9c62a90d38b75159ec925f232e0db71362f31e29" + + "4336f5580a34b26c5a01ee3454cba227c7f400f6889a319d7121dcea27b9" + + "584f33ac796d48a9a24cc5b6799ee12f10725fbc10d7cf83e4b87d9c444b" + + "f43e2f5ee49d8f3b531ebb58fed4234cb8bcab1b8b18bf50956506baae8b" + + "c1b7492250f3adf64294310387f1d4bcac12652895d4f2dce26f380733ce" + + "0b5820e9fcd8512a1585a49940a32fc8875ac3c9542a4270602e5e97e720" + + "90ed71b51badb775340429fdbe45b887fb9ee61cf9e091c06092cf0a2129" + + "b26572574c46910cb458bca7c63eddd29d89753d57e568323e380065794d" + + "3fa1ffb874543f5b0ddc702b087e91e22604d9600d37fa0dd90d7acb2458" + + "4cd408a4e66bb781dde5f39efda6a8fc26be0d08ffdf851e422ab1500c28" + + "bf6b4c85bdfa94e8aef5cda22870c39ad49c3c6acdbb3b0d58cd05424c65" + + "20740b5c2bce4336545eda12716317df58e6fb764fcb3004f5248c5ccd84" + + "f63abdc0dd2a64e447c0de4da4a1082a729d8ebe14810d396933085cde18" + + "318278481fdb9a748b637cacb491f5234bfe16b53a35da6677336baeedb7" + + "4a28c19a412e7812dace251446d40ec07afd63854c3dffbd5c0f6a9a3cac" + + "ee3bab07fba94800fd1fa0fe44f5f2ecb2b4a188cd02b8a2df0728347c50" + + "7d0cc58fcd5d54dffdbda11dd1bcc59758396ed8db77498fbe13238d3d8a" + + "0040194dfe66811542ddaa658094a9580d4e4b4e29", + }, + { + key: "1285f117bd90b70ef078ae62f37d2218419e894b7d334759ddb2d88833b287b5", + tag: "429b2b39195a10357043c9601590a277", + in: "00ef065a1adb4ce7108b497813ccc748933fa8442689a7cb8dc7c1ffdbf6" + + "c09adfe05ca2cc5ec3acb7493f3497ee8f9cd9bb8a4b332c18e33f78114a" + + "c8f9a72ddb9f13494e934ad711818909831013ba195b53f5e9e5b4689399" + + "6d0b669f3860958a32b85a21009d47fddbc8697b7c9b92dc75d5060eb4fb" + + "40aed7a1dbe69dbbeb6296f5467ea2426cd17d323671fa408855bc53e5c2" + + "d111203ae38cecac7719c0bd7f21f6bd6a1588187b3b513983627b80ac0b" + + "300b7fa038af1cc8512403ac2cea6e406595202ec3e74014d94cf8780ed0" + + "33c570e887ca7fb35ee4768202aa52427d02c24e63f7f2cede95ca9909e9" + + "dfa86246a27db757750667c198c9aff4ce348f7ac51864b36ef5695df713" + + "d17b8f561a972d0136bd9ee9aa16079c2ab5d29ac9ab472255ade05dc49c" + + "b966e0c1c04258ef9ec59ded01f402d9fdcd9a2020a2038a8c78892ca218" + + "30136069485527069132959dab2b81c73ca590fde2a7ecff761d95a54d63" + + "a2664aa5a6deec163e46b5225bc98976a4f363063b0f42e29f792d138af8" + + "eae68d3854b5c1985d5cd1c9f49f529b0b4d2c936887b5b92cdebacef992" + + "c35e0b7bbd52114aff8c6b261852e28e451b02099814f809b0289cba0586" + + "04a363e3f969aad3d982f645ec4c549f943fb360fb8fa0d5a597bf89842f" + + "8ced6014a5b2590ef71524a7ad50fe0ef0e2f81b6e26b99f9ebbc8036549" + + "f7eacbf6ab884710c6406ff59788e03ede35c30d4781ad5af171e0623e8f" + + "cf5344d71165f0475e256e9159040f702b359a2963116ed135dd6c1d111d" + + "2a1e33e15c178ca4f02c5fb15593c50cf9a8a492f01e04778dbb81d26c99" + + "0c58cf50a9bcf4fe38fbfc0fc0685d8bd422a773c7bce649f7a86c59118e" + + "f5f857b2c72508cd1ef05e1a0c0b7ab4687fdd57437092eb49bf41a9ae8b" + + "bd98272ea2f8ee2515ff267fa6ae892c266a7effe61ed54984924aefc461" + + "6cf483dec024ad666bc797beaa429a742d1b8806f67d451b6d3a85b4d474" + + "003cfe9e9dd906df47da5559c41f15afabecc3e6af279cca0f2a200eb2e8" + + "31437e034d457fc880f60f5ae635690bce82bf6d1ad6b4f5344ec042bf25" + + "7d010273c861e3ac516e9ee2bab3a255f570baa32298467bf704bf6d9076" + + "a4c0b08a528a05cd1fcbdf51f3885fbaba7891a144fc058919903b269b4a" + + "29f43926eda32c38853b814a7d528156c223748d674d8f7f5448350f011b" + + "bfab1511001b8014e20fee37ccd4a0456f638c197c86dc116b34f955c0b7" + + "dee10bac5ea0c2fec8a780ac05098b51b902ca6afff4db3c6fb4f761df79" + + "b2039dc5f16d9402442a6fcf6c4297769e6c36824d908beba8e584ea0b3a" + + "91b9017baeefac651d0307bd89f517789236c0693c65a5a20f244d39684c" + + "eb810cd2ffd3c78fe9285d2eb9f55d133b86113efb8dffcbc6d258e84c38" + + "2dd8f4d7d63b65672516d9bfcc3310a79ce244b60d380128d529487f99b7" + + "d532d5f5c28fad8b9a071fd2fab8fd98f6d7ed9dadbd2fc4396476eba6e2" + + "1a1b1cc594a31fbd3418d98e4aa736cab285a2786fbbd4650e49f9b080ed" + + "3fda34941c28d25545395e1408fc3e60730d0696061f821a4d24123cadf2" + + "3af3d37ba7ce1ba3cde1368d468f136df82c02f9be9210022192aa02117a" + + "ef5ff70bcfeffd47bc37b920826a4d3db001f956939abc0df520f3ec1613" + + "ba1c4b3385cad97e42bfd15a3150711fe86ba4562f17780cee1cdf198615" + + "ca06270db84986f33e1d53d552b0da82397c496a23c7a78ca7641a908e71" + + "89249cc657c0431f1e09ae0213f28a27e6267e9d17b5bba0ea4f3c21f266" + + "fe538e215ec62f85517ae6bd87799ac5ce68453f09cbbc50d6e2a168f0cf" + + "7166ad50cb65b6c76406c326573c00e04a3186251c6181933828c58f4198" + + "f8208c4484805639b0d428fd05b57e4356239638f458a84000c7a7a8de62" + + "ec25b54d1e39d2579ec9c512fec475f243576f35efc02a1cd6b0478e2dc8" + + "be5f17aa4e3849cd42e76fbffe6e7d6f912d6edf80f718f94a7e48e1fc10" + + "6cac29627d9d4b82f05a30cd7c739f7f3ef7ea368d22612f189da450e274" + + "de7b61c6361521e684d639be5af4cb11fefa5fce6f8a5065c90873e504c1" + + "2c940571ea7bd7e9221129b83039d2edb069e8b5bb68567d8fcae34c6ee0" + + "cb94474d8b056cc3c7403873f2fe6db3b567a44e702e4f4813b2a264231b" + + "0a998207b41916715ef94e5eec281589d0a711f8e74be32bc60f43d693de" + + "77f21d5f7eef892abe87725f3d2b01d9ddb6dee15f40735a8fb67766dbcd" + + "020a93b8eef4361dc3a891d521551f65dbe6e3f68c60819b0a540b0991c6" + + "4449d207cf5b1c198c17ad6caf3adc628d09fa0baae7a696d84e1879577c" + + "ffe9b3f62669d4ea5ebab6364f08c66d170ee4a94d61fb77d60b33dd6b60" + + "650f034c5c9879243d5c16f853dd7a89885a9047a341b076912d47872b3b" + + "3de49edf7451b435698ac4e182d16c339be83e18531a34aebad36c5c7c93" + + "aaf121cf99ff92d3844d40740fe001eeca9ee71300d826bc3cfc87a29d39" + + "ea108a3cf259657ec4b967fbb534e7513ef3a96bffb35abc5ce0e890696e" + + "54fab515af3d2c0be6e003747504e486c0ec6e30fa4ca79d6596ae0425f3" + + "396e40fd37432e52c74f812250dad603b3502f97ada48a26e39fd4d44584" + + "6591bfa5ffb3770d95d3dbd49e9c3a38c6305796b8f7d79bd0845170925d" + + "575774445299bdf9d3f8ad3dc2dc5cfd3ef0293b84d6e11370851af05ebf" + + "b3510a22edd930797dcb76b759a9b5a77ed8dd5130e79ff5ac44b01901bb" + + "79603cecf674202bc5d84076ff41b3c806454ce80cb9e5fa9db77294d20e" + + "6d3008ae3017aba712862ecd4b32daafef1b8cc8b19ee8f8bc3835e2372b" + + "5cec66222ad5ea9df753c033508ec43c8b5995e88c36c13ea3465c8bc462" + + "ae0a659d9767db34499e9d01fb1588410257d6f588b3fdb766a66bce28b5" + + "e0880f8cf988a2e5eb5bf80cd7d83192b7392fbb2e3a07d51aea2b6bfac0" + + "d74d304f56d5af3598a0712cb09c04c5dc14194eca8e1b9b29f88344c0ea" + + "55638c0f8ebb70b6242b797fe2525fa1bde76293dbc0a66ab4715e6f9b11" + + "f7ecd8f35a20ee4ff3552caf01bb307e257ec0576023d624d6094d43d25a" + + "aadfce939a6808f8baacb2109c3de50a1cfada9e384cdba3e97d2c9025a3" + + "2377bb195fce68c5569d2d1267e1bc68fcd925ddb4acf567fb29ea80517a" + + "7e4056fb014cdee597333ac2408157ff60cfa1afdc363a11fd4883308cab" + + "d9a8fe56c2b41c95eaef854f20bf5941ed23156d86de3bd413465a3bc74d" + + "5acffcd15722879849c261c1bbe987f89a1f00b3069453841b7da667d566" + + "e41fd894d94de44c23fed08d9bdffb723aa8449bf236261240d865efd7b1" + + "74a4460e5004ff77f4196d1d421227dff7c78f1726df7b5eebddb4bb5f57" + + "5ade25296dda2e71ab87ea2b44ef2ce8742a7ad5c1e7a40e097eb336561e" + + "865515f7ee0efbe01d5a928f208f7c9f2f58974d1c11af0e737c673dc446" + + "1795da9757010cefc6e7f2784658717938735ed8cbcbd7981a1bb8f31cab" + + "b901c87a3218dd1195c59f64d0bc3ce8b72580fe38e6dbf1181e0090e5c6" + + "d162df9f31cc52fa6a8ac61897e9b4b3cb0ca2bfb38a38d9b78e46d775d5" + + "7645d2d6da16bda8edd8675e2ba121f7f85400cf7cacb9ffcdfae583fb93" + + "753d07985a00afc3a4e26c9939a5116d9b61196502f5d774ab4c7fb6cfa6" + + "01bcfddcfabfcd28055e858d7d3c19feb6bd7c02565add3a3af61bfba8b6" + + "f4b52c072a8613e878368318383143059a98a85ba521f781a8983c2486ba" + + "b83f5b91fce02acee0be8d0dda7489975f0506c8f363b5adc48ba971adeb" + + "4e1c830b5f264ed42da36d2b5ce2fdab1e63333b1061ec5a44ec1b6e99da" + + "0f25e7f7250e788fe3f1b8e64467d3d709aeb7360720f854afe38e190cc0" + + "925c6cbd77fbfccc07d8beeb0ce68e47442fadaf13b53c30a03ce317cf79" + + "dc9155ddf96814583695f15c970fd0b6cea0b04b1825eb26e65ea9351bf2" + + "f7a841ddaa8c9f8e885b7c30b9985bac23d3ce777b", + }, + { + key: "491ebd0dddefc9f0117176772f9bab61b92a1f1de13796176091c56d1e53dfbe", + tag: "fbd3f884a3dc2a8be06ce03883282e1e", + in: "953b9a40789b206fb507ec2c5e9c88ca1baf25ad24c11a62f664db1da8bf" + + "dbe9b54f8e93b0bfb4adb12f8873096b8960fd91eb92a8ddb53232ac9141" + + "57caced33424cff943a8db129049af7e7b733afbec6637d8ee4f39d063e2" + + "be241cca6a339e48d72372efabceac57220692c40856532d95529adfae87" + + "a71c72f30244126d01a875375ad8836ef8db929bc81027935042a05c346f" + + "bc94dcc057db015e55c56064d2b11154596b813ee64b73bcac05d2688bf6" + + "f1fbb0cf3f8307b3df44c3e2dd1d226a4d0e9dc5f7482bada9611970f887" + + "f656dcb19ce1f8c5c86f4cbd1e4f49b18f170ecfd184028e769e79d7424f" + + "d01cb315897c21111f53f4d41c3b71402eea695272cb5b4e5f33abb9df50" + + "cbdaa55ed629d3ed7d93b43e550295502db1f2ed884afc320518e88be4c6" + + "b62a13f8d3636ba091d07dbc6c20c7e7fda016c05b2fadcfc9ea32f4ee2c" + + "4893de78ad8a1771aacf6efdbd8fb1f6ee9b0572ced3edc6313185b5d398" + + "88ce77950aa4c5201a256e3ae3e74f05b70faada14124b35b105a70e7769" + + "7184576b69708eaabd36e0ba885fc6bafd5738a67307a1181792333cddfd" + + "a4ef19c88497c82fccff05a8f9f732fc7505f0467a14e135288ee018aef3" + + "d0412f6b0760573d8ee4ab455d2789b4d22a42eebdf60616fe403627cfca" + + "fea672bd0a49e8e7b80e7b7b8feebce3381f2fc16819a8996a99ea230c3a" + + "84b510cf2e0d914610d646a2f45a14268ec1d6fca03d0aea5c9ae1c8d519" + + "b0e8b0f6fb8ad176b5d6aa620b253cc492b5e5645353fbd9b6c02bea48f0" + + "286e2c669782b5ffefa4d8f3f1037151026d9cca78e7808dfbe61df29e82" + + "951d7154f3c97606cd1e99300012578ea6a776dcef0811338b56606b51a6" + + "9893fe68f762af6c9c26066b1d503e64877d8cd988b443af66a36af8bdfa" + + "41b4dfb3721d1d81895884755b9c52527030afdfaecd66d4638fab1d1786" + + "3d5517ef7ee7d081b5555d24991810f1edde30930fd392f817cfe632b4ca" + + "6fb0460c36bde4a5620b9c369bf51c7d870c43998b8171a553d2f643fe8a" + + "58aabfce8cf7363ea978ff4d53f58284db822ca95b80306ec02a64d26a29" + + "c98520f1924c70d161682c54d08a2c48f54bb72980a8cf5babd0aaf0fd72" + + "7d5b1b9d9b731dc49bad228fe83f7347750e277a4fbd526983c206e075d6" + + "a03d68957b3e925a71bc1ea7304c77660d112a5d19fd21a785d4a8d7f2eb" + + "dc4183376d8125341eb28b2df5be0b4e04bbf95c47d2fe2aed939619cb97" + + "79548b752f57b723cf8295dfce69c9b7486b75a4e900f91926636f3fc78f" + + "7b7720a5151abdf5868fecf1e1a1d830cd6a4c5e3cd739da4432cf1fe2af" + + "a1090d6a1eeb32e7236ecfddb9d07b97220ab8e23edcc93d91abc11b0c30" + + "460d2027869d1c2487070cf60b85ad0b8bc5df566f6fdb0e58fd044da530" + + "6d277e564ca6cbfa820ca73fb6201b240a5a94c4ecd11d466cdc44046a66" + + "32478221bfa69b3a2cebd16baa302a573c90895d7f4cab453b11e3a4d8bb" + + "b5a9bf264781ce5b9796e3c47d0fa57f46b923889af4d073270a360dae8d" + + "51d85ea916f14787c6500d2d906ccaaa92d20d93edd09139f79bfeb5fcd9" + + "8c1cdbcbe9f2587e9c9094e3c4a32ab9ba56f400b929e80c0551f953896b" + + "e8eda6ecf22e6d4a541957dec21d6a9cf388ff0ba58169ab934902892a58" + + "86e1126b16118e965a271495ffa339c49466209ed3875b568a4290b7b949" + + "69d0465744a3c2a75c599c3a04ab1a3fd09125fe8f45724b2f48c7822b9f" + + "ef95af4b758ae66a8b6646df7a0a1aabe2a24c052fd6d30561cae0389263" + + "e3388c4c1effe431a04356c334aac64f36593544885c4b7295b57dc39638" + + "b665b22dcbf7dd6da867615de38c6a575cc66391135d47f8e1f0c73c6129" + + "17ada4099723933a758d83311b384364263cad5fe14bdd7c825d9601c400" + + "3537a5aca7f9da4710c132ce8b0f1464cee625633ef57f507739a0ab1cd2" + + "21ae634d4d0b3ff07e9ecb1baaef0a82a97279d46543a0464855cd62c07d" + + "5e890265612906a9eac88bec07b1dea5f67054c31ae40f8c673296cc5df7" + + "f0dd8cc9e643b44fd90dc2d1e870ad8acdbe165237642fd04c00965837cf" + + "bd2344ae830887a5719a3c16dc8ec08bd9131d055bfb959b64ff4cb638a1" + + "002a4fe02e369871cc4e3ffda17dd85343e679fab43e11970e60198b424b" + + "676ab17fb0dee10cc9c2e92b32b68d5b05b7a559176f822850c0557ed98b" + + "7454916e32af549a0027db95f02b88cfc5e7e05f28f53757dd97cc0f0594" + + "212f8801e58043cb17b040413c226dfce2104a172d218caa4353890de17d" + + "be1f53af6ceda24b8781801516cc51de9ca459e469b3c322be13d8c9541f" + + "755c518ca41a0ed42e44b9f87faa2a968b0292216e9f3d3e8987282103e5" + + "016fe9f7681496e1e8d663eb2d8bc30b41d735465527f19e336a98d2dc54" + + "d7c020bfab30fe6c62cbae7d09f84af69bc2c51a1839ffba15015d381ba0" + + "a44a3758771c4f18d13827f518f30bb74f4bff29a87d4b9e949f1063f63f" + + "662721cfd64ffe1dab3761852387f78fa83fb48ae2c75fc567475b673da6" + + "fa8f53770b6e5a3c9fad951ec099c6bc1e72d1c489e1ae620e7f12ddc29f" + + "ed65f29c65cef75014b999d739e2e6e015f928a30f2fee3f2e59bf65b54d" + + "89948bf2bfde98b076e5460643952befd02fc1b0f472a8b75195c53ea296" + + "6403b9028db529cd04b97231bac3068855fa211f4d976a88bc27a0088f04" + + "576e2487ac0467992066ef7667ca8429faee92db38003728e5c219c751f6" + + "6f011b5d679fdd957f4575a0cfb6b54693a9624f2c7e66c578f5f0367005" + + "c66addd1e3ab7ea1ac404e357cbdab9438b9b4f80b3a6761b864b006f1df" + + "689ae4c0434b06b686d5353d3e421b57381ea24fdcf6199195ccdb3d5cf4" + + "623a6bb1f9eba9b22fa15395f65f8093b5f90455061c1cbf8128b44a31e3" + + "910862a59e187aa7f4d22e0317ae6c177cef24eebc44171f70c25efac73b" + + "38ada0cba0b74f72d1c171277a734819c1111ebe46d5db20a6ff20e2c1a9" + + "a57edae95a3c1f80ddf2b12c86d3df0078a7bf68695b16ccf92053c727a4" + + "80586b8d87d0d1772e456fde0c20a7927f351a641bff5f22f9ee2217b6a2" + + "d0983c8102d7d5356dea60a19e105ce366b9d000987c8c33396569f97c56" + + "2d0fc0bc5859779aa10efd1f8df0909c307a9110083cc6d9748456c9bddf" + + "16dccee52b7974867cec718bb0b76b3353379a621257094277a30148ac38" + + "e5cf67ed7cc9c1bae12dbdeb99d7d880ce98e17f0dc93c5330d1824a3c9e" + + "ffd86f89e15b59a4bee5a48d4f674766896e187abaa39917b83f8d2f3265" + + "bbe7aac44c9f8d92f775fe6493e85ab44e6e28f79f28eff156c21e1abdae" + + "d10a291b88c4020b1ae8be001080870847a852d073e82bfc751028ac62d5" + + "6aeac1b18f2cff1c0c7d336bf08f8cd5099d9d3b28f9e16077e9caabab49" + + "f2d234616a7522a6bde1a3b3c608df4cc74a6c633d4c8068138abda8d26b" + + "4ca70f95d152888fb32bdee5dfad8ff4a5b002a0a327c873656db8d6fdd8" + + "ed882e47ce8e47c729e1292db9122ce2e9fa275f9bb986eb7e0a1dccb7cf" + + "abd0449c92fd35e2aedc4aa89caf53bcd28170cae85e93f93988e723a896" + + "10cefb4edb6fa545835fba3107e21dceb272c5a32da26fa77df070f41d7c" + + "ad1d68b836199ff0f1221e36b9b976b5e69bed54b5bfec67fe9cbb383484" + + "696265204797634594bc335150daea92dbc1004f613b4c27bf5c699debf9" + + "4365041b5a894701da68a93bcb61f4e546c553fe61f14ab0322b45915da6" + + "ecacaa093b0071f2516ca8c3fef2f1e3c403993d734403c47bfe5f4379e9" + + "cb5b613fde3c0d880cecef4101aad8b8b1c60a92ac5185f6c243fdf1711b" + + "0b56f0fd8e5ed6cc0f99da888e4f156455a0f0eb365b8964347eedd15d80" + + "2f297977af667ed1376dfcc610f5152421b97afaaf16f9db57a435328595" + + "b9aa00b5ed9ff106c66970fafef379f4d2f98f2c5984ea05aad64651fbf7" + + "7968c8cbc4e959859b85302a88a3c2faed37765f3f6ced59d8feb6c72e71" + + "f9d4497d98bccf95fcb650f29131e1df1bf06a5443f8af844aa1a7b5a68e" + + "bb250c7de3a65ae9b1086cf83f832050e55030d0f67c6a54ea2a1dbe18e2" + + "8a96c9e0dea2966997bfc5c5afd4244e3c8477c4f5e8bee8fc8ca9a5cde4" + + "d9c5a2c7f3d2e811b1de7ce4279229319e432674c609b4c8b70dc6172e9e" + + "653fe1969bbc2cb3685e64fd81d96d33", + }, + { + key: "b41db44465a0f0d70093f0303bbd7776017bca8461c92116595ae89f1da1e95f", + tag: "d8a111a09db22b841fa28367ce35438b", + in: "b074b0984fb83749586881e8ec2c5ce9e086cfb2aad17b42b2429d4cf43a" + + "0400fd15352d182e6c51e9338da892f886f460d40bd178d81c52e9ab9c1c" + + "bdd812594e6fe7a9bb7fb729c11328d3288604097600a0c151fa3d9e4268" + + "de75866558e9f47d8dd331994bf69f826fd4a6cb475ae5e18365f59a477a" + + "dde7fbcf7e40b4e3dee020a115830b86f0faae561751e9b596c07491c42d" + + "e02fc979e69071113953729d7b99f1867116d058a90f1b8c0f9ba12c6322" + + "4ebd1b563a87734f5d6e2d4e6715d5f0213e33316500cc4b23784f78a9bf" + + "13fdf99bfe149cf47aeaaeb9df1cee140c3c1264fe89bcde8acda6bde16c" + + "e3d770ba51950b67ad2c5232ae0cff048ddfda8540cf18e673582dc96987" + + "4b127f655e7d4e08859f2c6b95403cd5b4e2c21f72bb872e49e592306286" + + "48ba1b16fc9637709636b198f9a297aec364d4c3bc869dcad32b1830e434" + + "b556b429136f0012a0a0b6fb3797bc8668014b010ea51674ef8865348dcc" + + "197672047fcf72e6b6910a0e32a4f110d85e28db0e338d9cfdec715a8800" + + "b4f007a7951d09e41620815848c89f8768344c50bd522c46f64ac6c98e53" + + "92176651961c7a70b62f3d1819bfda674e2ecd3167415edc4b97419e8ae4" + + "9974b56cd8d52e1d05b82610b59606a750b34844ca33bfc9b21fb970738d" + + "b66f48928df79cf67730a30b0b612f8c15c22892120548ab460a6b9bb3ac" + + "e30554c86c9681c797821a1b1ce91d0e87fe90ad4097c974cfbdfd5c4c24" + + "a5f808f388e1b1473e858f48a387614501c8c39d6973ded69b1764663cd5" + + "166be02b596a49e392d637e3d8afc91323f7450318b79d5488c040e346cf" + + "0cee512044514b570aa66bb98d639a9ee23a7cebe28474592623d082873b" + + "73efb3eaa4721fc4761e15a390497cb13cce181107e8b1a0186b9e47a5a4" + + "b67a5be3cd88a43d341ef63f10af6970aaf56035db938655020809033a92" + + "8d4fe6d2f5424fbde2fe82adfd991d388edf293cb4e3eb68d876f225a5f1" + + "58208bcb1aaefcbc28d6763d267406aa8d6ecb413d18cff7a318ba031ba6" + + "0ac4560748c248de64eec56dd4540124b38581604f502d94a2004f9eb1d6" + + "edb009e16af6c6d3ccbea79b10743da98aee7ace407a90c6cfdde694f36b" + + "e0271e722618a457be68619b980754795f4ac95ebf4f1820b85ca8e3fbff" + + "a2430f8e01ab422d7140751f7741f2c921400dac404b04e049736738a87b" + + "6f49bd54b1b447b922c473831a65f224ab84fc96e4551a0333bc6187e15c" + + "c0f0ad628068bcd7c043bd1e3036ec01e7fdc3d157476149917baafaced0" + + "15d09fafb92181a0ec65b00c9c13631e65de184377416e04d3d93b847e0e" + + "286c1d88245d4d550d30d4fbfcb416ff26a39a94275631c2deafc7cb6780" + + "f149e4d0e9c4515b708fcd62be5252485407a6ceeb9247de34e0266ef384" + + "976f6d31284c97468b3b03e951d87a5a00836ea303a266147a79ff3431b4" + + "b382e86c74d92661e0f65e266b7d569c03994b667a8137f3080eda2ff542" + + "0f0b52b427558dc26932a22a615c9e6b1834a251c6b68fdfc0bbe0e8781e" + + "36adf669f2d78bd23509ef7e086634e526258e8d11a1e0be0a678ac09c7b" + + "b4e3c5758504011e701dc85997fe2a3e40c7af83f032bdbe7adc10ef1e4a" + + "666946c2bf31dd8e3a383211c9684d5302f89dafcf77976d5a02c14e2462" + + "09d2d99918e82402cb0eacaa12032ad8316315af1b3d3bd5058f7c935d35" + + "ef0d4e71373958fd5e4140a9a586d89c53e4144c00148a4706a524896eb0" + + "5b1479a0de5d3f57be46b3f5fa4e49bffe027c81a33e37abc01a4cafe08b" + + "8e21fa86b42be52d75d6407e6cdf399de7aedb9b61a6917b2677b211c979" + + "33536664c637a57ce2234e3319fe8b4a77d7285ae6347464dfd0aab3e6f1" + + "178e0029686770d3b0dd541490b097f001e95f27efe8eb16e4747937d643" + + "cdefd49e586ecad541270cedc3064bdb7c79f086bf1fa8c666304d977a15" + + "54ae268881e17d8bc3fe51fa9969f7e560e3d3e050424febec0998b35f2a" + + "7378b2c3e384cbfc80c4987734d76c78224cb81cc5376f88f0ceda28aa50" + + "44e956537c3ee209071d84a66173384e0aa466d989759fb1f2f17fe627a0" + + "ffeaae7c5a3884b237f5151278a07117c2e833f1815c7e0e0b1611f25058" + + "ca338d21deb1a571faf1d0486667cb7c58e2814c3722d24fb77ce1b7e018" + + "2ae5746442b5ad00208b17c0a68bab4df8a8f36edead4fbe79b4c9220dd6" + + "acea6d23c7caaf6ce7cabeeca677a1c764d610ea6c7e994d6a9c88f57fda" + + "ef160b251e7595578ea2cc1441d480c14b8b6945e76a001891b1f214979b" + + "c52ec15e9480d706a40cb6e3b259ee99a9e84e63a738f1b52cf71c8ecb04" + + "fc833c2c680bfed587aa1541e5ffe8bbd7b21302bbf745011e559f94f952" + + "8b7fad8a37f6d855306a5be22725859cc950bcc334179d49564af3b9c78c" + + "e1de59a9cb45086a33856ba7195c17cef573950155bea73ed16645768bf0" + + "a5cefce78ba3ff98a54a8e8afc5dfcb0d422bd811ba9b7770a663b081dbb" + + "40aefffbeabca955a9638830f0c5d70663cbf5b26067cd061c4a3f5cf8fa" + + "4b6678d82d9a2aa33f8538b7499a3466f6b0ae2a1daf280ab91a6c220684" + + "12705245f353b4b83db50bedd3bf99d42bde6363fd6212cb745467acb007" + + "b678128f6580629a06171f7f3af272f8900b801af3bf47439167871e7b0c" + + "33f198333992a6c52c32be46071738cfbf245937d48f816ebb88ff0e726a" + + "dc41de4c771ff0bd320a4c0b1fcccd9fd6c42ec9c5185943c70e9a4b7c26" + + "a980afe104bb1f99576671a254704c7d4233eaf9915e1d56c103ba9f6e8a" + + "46aff466933bf58c9842796ae9cd21f7ac6aa96ef42ca54e390203bac354" + + "b7c1de7d1887c48255201335f819020e2782a2ee8af92ceb206b651ae92b" + + "3f4fdefed05e08974aee0a353d104b1be9a5e75c7f958f1981271b0a6928" + + "05a7a2f28a0448d86102b4fadf9ab4ec2f98e31e64fcfdf2b524780b3342" + + "7a2a3100c2032fc93199f3ea7a9e8063fe73282dcb1fafaa9496c7da868f" + + "dcf33bbb761df0bfc6fef30fadd2b6efef4fd3216a8aee48a2ef28102491" + + "cf7278b567c272d1064a277eb193b3f6f01df641ddb729f72454943cbd3b" + + "671ec077f9e3548f5f57d063c653ebee4f228a78f8a128d26f7f4b44160a" + + "07e942bab87b2d043c77ecdf10c1a419e0a1c4162a99c21d4abae0558b8f" + + "4dc0b7f1ca3892a6babf71f2f70aaca26bb813ac884ee5d71abd273ff1c4" + + "add230a771b678afbb12a1ca7fbcb2c0f5589c9ce67fe8f78a8db87825b3" + + "09ca34f48ac35aa7ac69c2fb2423807650fcf47ee5529e9d79dd2628718e" + + "230ffe5b83f9d5bdfd9c5d211282e71cbcacf972995bf1b13d21419f7fa2" + + "8829ed1dcc459da35883b9269a474f7fceff01d44ab78caf1ef7d8117f50" + + "cc83eb624062b149a6ed06ddd1cd1feafccdee7122353e7b3eb82978ca69" + + "247fde52d2d6cfe7324f04af5259e1b5c2460889da4541b431ba342a1c25" + + "3a1b1b65fce7120829e5466e7ad2fe4e0f773c7c13954a9c92d906c91aa1" + + "de211f40916596bfa8245344e257e5907a2c49ebcc864cfbe28663e700d8" + + "472c50355313d5cf088e9e8a19cdd85bcfc483520498c6386050e53a3ff8" + + "1e2b77b55b116a853d71f60d621265166cd7e95ff5cb4466226d7cef68ff" + + "d0a35b61e76a43cdcfa8da7fff9558e2f89b981ec6be632b126303ca1fe8" + + "53d5c628d967d39317b60ac904d6a882beb0746f6925a86693aff4deaac2" + + "e5b64b611de86767d55a6e11221605508b1c5cc828251539b1b6f65c2c04" + + "8e65be5422c1b11194eb687d906c559068c0a810713b23b30d8b17f10df7" + + "0962c5e7e782aff7bb95adfe4cba9d90b0ebc975fa56822025100b5cb8b3" + + "8bdc8928c1a2a8034dd66e2a763696d7ce6cef4dd586b83f7d01749d37fc" + + "4fe8d7abd324d4ff1efdbdbfeb0a2fbb8b266fc2bce8e5e5b95d0089e7c5" + + "d7de4db837d1822ac8db8198889d6bfe778d0b19e842f12b5afd740aaecd" + + "e36e2cefc2cf0b082aa0c4f75684d024b8d828d8f2911fe1aae270251f62" + + "4f49584e40bb193577c9d8e04eb16c094653cdf9a15fe9210f724c7a7c73" + + "74cfd1a74abb5ceae88ea54f7e7569f8eb674529cbec965ed05bb62f1968" + + "8fdaa97297268bfeefd06eb21f700cc56f9bf7f6cecbbbe7278ada8399fb" + + "960371a2d5cdb852b11c9fa17650e614c5297bf46cb7889d52bcf49d2560" + + "720852822b75bb16524d88273cb366b84b88282da91875562e5a1fe73973" + + "afe90e5cdd3f5381612d3ba7bfa058d023a9326e403ec474d8938313fb32" + + "bdb5bf899b900c3818c43c8a0af6a061bd26e847ed75983402ee8a9cf4ef" + + "85bba5545a0d329ba81495157eda0286f1917de512fe448251697dea406d" + + "a510adcb05", + }, + { + key: "b78d5b3019688e6ef5980c17d28d7f543ca5b8f9f360f805ee459717ca0d85a1", + tag: "f01babc4901e957d0c2032a7279321e1", + in: "ba7d35b2ef8af1118bce1e78018c9314b0c8c320591e103d23f715acb05e" + + "dc98fbc618de06627661df5842dbba9f604c2d20d664e5db06e949b11d49" + + "665088dbafdb0d39d20beaca7d723f8dcdc57e9c5583d303b6cdfdbecf95" + + "7d8daf2f1c72b2a6fa27e3d18841f4841abafd334c110cd2b74efb6191db" + + "ab9b8fc8427ee17664082f31db98d30bf15dda967e20730a9ef525abe9f3" + + "f620e559ed22bf74d347c9869f0311f33da7f1a3dc858b3a8aa73a35989d" + + "b055a4a2c269c95e352259c57de8b94d8de48984ecde426d3ef60ec1c7b4" + + "41cc950f7764f55bd0cf52d069b9ad446d1f765f35d02ec104ffcc00bf1e" + + "dc1b951ef953acd19984ff1b41041bea0e9f5326a7c9ed97e6aab42174ee" + + "971ea1dbe2fd1c1f67f977ab215962b0195417170f6b7748fd57262424d6" + + "cf7c235b34425f4047191232722932213b3eb73904cadd6a2e9c7571d7c6" + + "6c2f705b5039ff75e5e71c5aa738bf4177653e6eb0b49303a4bc0e641e91" + + "2691f217296a3325431d578d615afddf47784e4618a2ca40ccecb05d621d" + + "a52f272b8cf84f7fd8177c83af1580d25a764cc06436d67171cb5d1e3b39" + + "367b46d9a59d849d87ab6bfcf3fb9bac2b1ebfcd1cef4459e74b0e1b7080" + + "dabd2dea79f75581a55de63c4b23ff67d986ad060102933fc6cce8d614c9" + + "c86dc84068828dd9e21ffc5665c809d83b09432fd315dfce5d7a4ebd8143" + + "181953e3f8716e47b0b30cc1f753e31a7d509f2dbd4177b6da310cf3cd02" + + "5db270adf98e96259a5ae1b81f5be4d5c76f502a612ca73c76b91e0ca695" + + "aa921f9489948619482c2956205ae71fffc3aba4476ff754e4878e36c763" + + "2c935c076857c5b90cd63ea4764efbcee53e2ddc9bdce54b1cbbcf0e7544" + + "d023e7c2b79419ad92221a1f76abe31a8236e370d38e2493cc9ca2aaa811" + + "30fc713d11f500fd071d6eba6861e8b0859b372e62fe60b627a96c377f66" + + "236aedf307e1d148a61bdad072b93d7d2a73367c595b1e048f7023e72729" + + "1ec508326f5424a5bbf4e010d0240b71fa9137e6642ab40c5e4fff79877d" + + "b3253c663a221b49b3e77ea307c7b9f3f72a0f3a54d0112c45c64a0c0034" + + "baf2b55ae36ea6f811bbb480cee663136474dacac174c73b1e8be817916c" + + "fd4eb1876582bb3a36cfbabad91776aa676305ddf568a86e3a5eb687fa81" + + "67771fca7b5ca00e974b3cc3e322b4bd9bcee2a87d0ae7976da5e04fa18c" + + "219fa988d4f6fce62f194b05c26ed3ae1b066cd9751a2d916d53426a454d" + + "58f9c3b2fb49374e5791b412fdee1b6029144f1ca787f56fece4f64f4fac" + + "bfe4cfd8ba7c807a83cf44008fe5126a283ab2631a87acd8e2a3bd10979c" + + "4b07a84a49b0687a45a4798ded0b5e9b2acce30e714d78395bfa8f33ca91" + + "e68b2138bd67d8a694cd87c88dcefcd101a3b408d7a9095cc6a4b38898ec" + + "c8b375f5a67deaaf73eb7e99b10314ca6bba824658bee85dd731d9a1475f" + + "976b7c0aed4b67b088f0db5ca5091273217f724969dff6cf184181377c45" + + "5722beb23fd9d097a82ea2d8d527ba6284acc20cb30f2e52af28800c61fd" + + "1faf9f4f619550e0162a1a63758e202533889b27420fe7d0eac9a47a6e11" + + "1d80054412340e0426cdddbb3c7b9b823b8db3ef58230fad7a3ac21a7805" + + "d30878d4ea78dda95c951b7a5dc552e9434c35e03e1dd88652d3714f8fbe" + + "a39936cc0717c2e0335371f2a751204f5d9386baaec853f019325edfd1b0" + + "719d1fdac3fbd774a64bf957fc54039501f66df94b5b9b82c2076c597065" + + "dfcfe58b2e215a3734066aeb685ef97759c704b5f32dd672ba59b74806cf" + + "ad5daeeb98d16f7332ff0ca713d541c84e4aef0750bab7477ea707e2e497" + + "e12882dbc0765106070ec6a722d08fe5c84a677817b28fa3a41a6117f2f5" + + "465c2a2f0eb2b8be4f36e676b4115008bade3573c86cfb1370c03b6b0dc4" + + "bbbb0ada4dedac10a593655068a26febc2bf10d869cac84e046c9c846ce7" + + "927431f606f07b92abdfd81260199ae05ed01dfa07088c56a6a8de9c6d51" + + "d61d6a6d3f9904c216ea8329467a006a3d2495a768a39ef99a21827d2def" + + "909bb743fed7209f7fe59ff1c1e710095b05f166c6173deef5c6ec4105c5" + + "fc3b87c8269c786bebd999af4acbf12d20453b125f338aee87e9509ee405" + + "9c9e568e336304d7be9ffe81d1700555b0800242d9b7450d7256f2b17f6e" + + "d46a39f67bb2980572ce73169e352070dbafd4c7fa5a6be78cf9b72981c0" + + "a01f1e1e30ee3736c59828b791d2373799854497a28a44bbe0e074925723" + + "4986696fbb06ef9ea83fbd49c45a583ce12ff10258ba06127c67b0f66dd1" + + "09f1366d8036853973d8884f93de54fb2a12949eefc020717eff47898cef" + + "306b5de068411f1e113ffdfe2556e0faedc3e27d95a45b8afc15ba0eeeff" + + "eb86da7b4324e20af80c62bf0ceb4aee1515f5912f71c6bf2febf20123e3" + + "dd3a82dc1e58a108f1039942dcdacdeb1f0ad0b2ef34488d98d6a52311ae" + + "acbd03c12f6e775e375d5979c7c295bb049f2cfd3580e3da3841ddd8e6af" + + "4de5e6512ca79cebcab9280554524881da37984d340e8f0163fe10a02ed0" + + "88682560bc6d3c4dbcf1a542ffb3dcc2ed16a2eb96896e8269697ffeb50b" + + "73f2cc354092e782a0072fc12e1eaff117c2cc8a5a1ad8b47802ac9e23fb" + + "91a0cef9e4027595e0885464e61563093ee2b1dc5f22dfd04af7de6a70d5" + + "977d3751a4b3cc0c71a71c59c0534cb1f8c0eeddcf1c0e1b3e5ad0d083b6" + + "6e8b998ddf9ae9d3b365c851d42e995b9afdf8d66b2ac40bf514ce32e456" + + "0880afd38c42c08926067eb243c4b1184e667ba756c14ace5f525eb48df7" + + "ebb429d0a23d159664f8021d27dc7167081de331c7114c9c6456e1ffdb42" + + "2172a81c06d8deca995e158c48df27261a83f83e0127f5e056a139be9b76" + + "e25dadf534d3d1ed6ebc0b5d77d51e5b90ff86f30d4023066115bc11b33c" + + "c827b1103098826d0bf8777176b2da6f1e5b580e407ccf7e614fdf4f5b53" + + "3ef6d30b20c1bee61eab90e983b1a97173a62720ffd27abb8976a948d532" + + "d06596c23b0ef31c79831bead8f8e99ad209af3658cac0cb3c3f9c88379b" + + "9bc871d8e84171d53400902da1243f664afeaff60bd96ba2639a7644676c" + + "a79f43130af12ba2c877d67f7ec030a4217a72f5368af7c9f24e643db6ac" + + "97a04adaf57dbc53762d8dfa1afd49667c4041adcb5ec303e191b786273b" + + "bb065cd9f16a3a4a399c6a7aab9c1a6604998264e8b3dbd13d8f2228b13b" + + "2c2b9fec5055d8e9f2df1d9a25e4bfe2029776389877bbef7e2c7621f06b" + + "c0b7fc0786e2b2d042483ccd4a59d2872a6c5ac73e217123e5c8401580a8" + + "d967e0895aaa28f4d25ce68c90b4394d8113bc423e9fae46ac47bc2ac191" + + "fb97b80b5a85feb2bb54f84c493235c1408662fe253c6786fcf6fdb8be87" + + "dc66a72cc847f94dfb5214af5905b7039a7363a1b23a07853daa26862783" + + "ba08a80846fbb93ce98700a4f9961115128dd67bd7d19e0c588fdf6196c1" + + "1cb0154002ae862f11421f5dc3a57b6c0870b452272be556a1d14eab1af0" + + "a91ff5b89de6bbeed6e03bc64f5efddf9e54da71c594bc5ef78e0192cfde" + + "da36e4ad1a6b0b51110c1b24d20dea1f19e18cb1184d80189f842d4f07ac" + + "834744dd009aa3771b1e5502fe4b65a403a4bb319e1880ff6ba852e90a8f" + + "4fcb52cf374c88408428cdb1255291b04ed58c992310955198d61fa1fd9d" + + "762d48f2f65a287773efc67d549981c291b427889d3e3dfc0cc6cd68415c" + + "dbed81b516786dacf431472a7dfc99688d15bb6c1b85b1a2015a106e5de8" + + "cb9eec4c80b17d00fdcf4a9c64de4643a95dade8fa9f1bc5c839037d86c1" + + "3800a244188e3b18561a74912ed72f99f2365f0126732d037dd54a3ab77f" + + "9a9f6a1c1469ea92eb707482066bd4990dec4d7614ccb4ea6dd4deb8bee2" + + "2c4dc0b9b4d4cc70a500d2c8a5ac3ef88a38439b7dc254a6d920cfd317a8" + + "4d7747148c65b6730709e43369d4c995b03c58b9df444f77f216944e70f6" + + "6446554d8d513b8f7f28ef0a2d7ad5ca2f6110304196953247a7ac184f68" + + "61fba896c2d5a59007ec2b2c8e263957e54cdc1f3b4a145228823fdf0960" + + "c33a28f59b03ee4be21001d2f56fd49ed14db33b2c4eec2c3f41b250a624" + + "99a9b6602c1e838526a54cdcd058af1c252d56009d4c7769deace53bdb66" + + "543f5a081cdde775e61efa70956fe2a7a6019a164c6e413ded314bc928b4" + + "aebccb946ffdf3eb33e187bf421febe26112b3262a526de65678cd1fa03b" + + "83513705108fe0bb87aa99aceb28af3641c46a2c4427cc1063de01aedaea" + + "fba68155d4de494a27ff6b7fcc8f5c5c3f7d3a115c397a1a295bc55aec8f" + + "7f150cbce2a8aa4706d54ec863877bb966ad441c57e612a1b5d438b98d9e" + + "fcdfe6d4f66e885f96407e038015cf974ae5a3540692b054d2ddfde59b28" + + "ede7e2f581eeb56c5b88e2779aea60c1d8ca6107b0cdda1ac93e6c7520da" + + "edc66afeed12f980e20e1e1c327d15ade4bb90de30b011a9cb33855ca3ca" + + "e2", + }, + { + key: "2b0b0fd3347e73c2fa3a9234e2787e690a11aec97a1c6d555ff7b4047b36f372", + tag: "81b1a6633f849ab0aa7baafa58a5d9b8", + in: "427f3a7a5f1142ffa68e83df5f917e07b2bc454f3adce068a8ae9e0908e1" + + "3e0099aaa9074697593c6d8c2528fedddeca05e3888be1a0a201c389a72d" + + "20cb661017544d95a431e70e7c6580d8fb46ea4495bc59db6ae2cd69510a" + + "02426c50de1b6110120f759960605aca718d4d0a497e003e1ea2b8ae9a53" + + "df3c1eb4f704eb32f8f05eb08cecba0fd4a94f0daa3b0984c30a38f94b7a" + + "10cde723182d30588bc40f1f9d38a3bab4800fdd5148e34e396144763696" + + "c9b3e9b8adfdb337123d54237c7413f98bb2056152b256e37a27bb947c67" + + "240fa3ce8da62ab367db540bcdd9eb873d6c71c75a08fe99b5c11ec8e6af" + + "f926d2adfcf073479de394d4aac5fdc6241824d944b8773db604c59afc01" + + "495ee755905e5616f256c8a64321d743a1c9368d46418826d99b762e2f6b" + + "f998d37a995969cdc1de85f0ce3987c6550459f5e5bfd9173bfcb9e0112a" + + "d91f092de446beba14fb3b8ce3fb2f9c941815b2cb5a3b406e2d887b7912" + + "bba07c8dc7caab9836827da93ca71fa5ada810da1e5e9b09738524564d8c" + + "923746d19c78dc9107b9f20f653e05d7f2eb6bd90cf5eb30fdd7b587eb46" + + "74a1064c70ef0af2e75373044d32b78d96eb1db3112342d38dca0e47b96e" + + "9307fcdd711b1c66355186369a28481cb47ef6bf6651c2ff7ee4665247cb" + + "12b573933d3b626d1c6264c88bd77873c2e73e73ee649216bf0b6d6615ab" + + "245c43569d0b8096596f25ceca8667661de1cd60dd575697370ebd63f7e9" + + "5333e8a2cdb829b75ea83d72cd246d50358f7c094c8a515805fda03165d5" + + "21391617c9f9a2ea562b419632df611a67912d2b369e5e505dbd5c719253" + + "16d66cd608cc4a9583a8eaa4661b7279870345fac3031631c1a220551527" + + "5be7d8d89b71960e687aace3a0e8f206e475053d6fbf97717b154c75406f" + + "2caa97d1ab66048f1c99281c188a2f37b8bfc736c25840a9130ef2031c05" + + "6acd9dc10592eddf94f5bac85319b10ae46cc136a0738aa803837287ed7e" + + "dafe08d1fcf31d5e63763e39a5e1f4d7d0edab368d44e63fdb33c28905ff" + + "d6be406a024c017081b4f2d70860776e9d2556cd008fa5017b58733da13c" + + "634938407a118827a80baa28d4e605db59430f65862b90cd8356baa287b8" + + "4e6d9199fd80abb9fa697e2c2c4c760128e4ec0438388cf407e2a2fe0f57" + + "908187ed8efd4c5cb83cc91dbe6a11444eede85099149ca82921bc28bdd6" + + "b9999594a41d97307f8854b1bf77b697e8cdd4daead2aa49fbc571aa44c0" + + "bc84a57cb5fd85f06847ad897ceaf449eec45bddd4e4eb1e1e119d15d5e7" + + "90957e686acbdda1bbe47ea935ebc4b8c2e3cf9b7157cc6dc03bcb19508d" + + "a9e19cb76d166da55559ec7e0995d9b50c6c45932d5b46eee400c56d9dee" + + "618977dcf6f76e3e86bc5207493afbc2aae9f569ec9277f33d9f61c03d59" + + "dd6d8250ee8cb3e54e5e941afb74f0735c41d52ef967610c9f55b2b52868" + + "4b549a99ae3392a7237bb52ff5f8d97327e2837268e767bed0bea51f76bf" + + "88bf0286bf22b881f93f1d54fab5cd4e3c148c96c39e7aeef375de249df0" + + "4d89d1bd97a7afb2be0cbfd3380cb861d31e4ad1ea8627721e4518b9db3c" + + "cda20273ec23549c4adc3c027e3ac9558de2010a0263c1225a77dac8be60" + + "d498b913f91391d8b2656ffddb06e748cb454dc2b7226745f11030a6b9ae" + + "09ac8ac428d9c6500801fb540650c94610ab70465b1210c6db2064dc84dd" + + "7f52573f8f40c281470e85176c85ec6de3c718663d30ad6b3dfc1a3a9606" + + "1936744357ca62fb8bb066aa1fcac6d7a2adf0a635cd546bef39fbd3ee0a" + + "8802ab0466ec9b049b5892a9befa4377cd199a887c34569b6f90852139a7" + + "86babc0049ee2b527aa96b988237a52eae8b4b49d2ee15ee5294118cee62" + + "3c3e11cecb836b21af88555f10be2eff8379beb615b7b3d6c01d545cacf6" + + "61be8ebbf7a3c58ac5e0e7b17997659a2bf15f2b2e3d680d142fd29d23a7" + + "aea9890f3ff7c337fce49ecedaf38573edfae07810ba9806723e576d687e" + + "a11700b8ccb96a6559259c367cef4e3999a05a373ab00a5672ce8b3d1dec" + + "a414187f383e449d10021b73c1f7e39ce01516b7af96193f9993036049fc" + + "72ac059ef36b2bcfbe13acf140d41592880fb8294ebffb98eb428ce9e65e" + + "1094521bcf8ecd71b84c7064539a7a1aac1ad2a8a22558fb3febe8a44b87" + + "72fc00c735773d4ce2868a0b478ee574b4f2e2ceb189221d36780b66212c" + + "dd8fd3627cf2faaa23a3d0b3cd7779b4d2b7f5b01eb8f1d78f5b6549c32a" + + "cc27945b5209f2dc82979324aebb5a80ab8a3b02129d358a7a98003e701c" + + "788a64de89726da470010eda8fdcf3da58b020fadc8970fafb08a29bef20" + + "2bd0707e994015258b08958fc2af4c86c3a570443fe6e1d786d7617b0c66" + + "29a6d9a97740c487622b5b8186c529d7f8af04d9f0a9f883043f08103ca4" + + "d70057ee76639f3b1046d86928d54cd79fb5bb7b46defdf15d2f8578568f" + + "1d7b73e475e798ec6812586700e038ed4791b23ac9439d679a1a4bc04cea" + + "e328330c24b065c9cdcdcedfbaf58e5299779e6f48783d29ec3b1643bc8f" + + "1095c724dea75770583b15797fc666f787510d91e65a8e2090cc1ed2013f" + + "e63ab17bc7640ee817487f4eac8326e9c4698cb4df05d01bae8c0d00fc00" + + "08919484d5e386c8f60b8ac097c93c025d74faa56e8cb688d1f0c554fc95" + + "aae30873e09aae39b2b53b1fd330b8546e82d9e09bbb80132d794c46263f" + + "4fd7b45fda61f86576dec52c49f2373e4dca31f276d033e155bbcdda82af" + + "8f823948498f4949bf23a08f4c8ca5fcc8598b89c7691a13e5aba3299ee0" + + "0b479b031463a11b97a9d0ed3189d60a6b6c2390fa5c27ce27e28384e4fb" + + "04291b476f01689292ace4db14abcb22a1a37556675c3497ac08098dfd94" + + "d682401cabec239377dff592c91aca7eb86634e9d5a2848161dc9f8c0c3a" + + "f7b6a728371fac9be057107b32634478476a34cbc8b95f83e5b7c08d28f6" + + "fb793e557513ca4c5342b124ad7808c7de9ecd2ac22d35d6d3c9ce2f8418" + + "7f16103879ed1f4827d1537f7a92b5bbd7cd12d1ecc13b91b2257ad073b7" + + "a9b1ea8f56b781bea1bddf19b3d7b5973f1065fb72105bb4aeecca5b7513" + + "ffd44d62bf41751e58490f171eb9e9eb6d57ffebedd4f77dd32f4016b769" + + "fed08dd96929e8efb39774d3c694b0d30c58610541dcfab3c1cd34970195" + + "7bf50204acd498da7e83947815e40f42338204392563a7b9039c8583a4dc" + + "faba5eaf2d0c27ada3b357b4fccd1595b9de09c607ebf20c537eb5b214b8" + + "e358cd97992fa5487bc1572c8459c583116a71e87c45c0ba2ca801931a47" + + "a18ef0785ebbe420790a30278d2d0d42a0225d211900618438d1a0b2d5be" + + "d14f8b4be850dc8cb08d775a011683a69ee1970bb114d8d5017de492f672" + + "09062d9ba3616e256d24078536f30489e4dacd6429ed37aab9b73c53fdd8" + + "a8a7aff1b914b9d82d75a46d0ccf85f48d3ce9a8d3f959b596ae9994ac3e" + + "3b4af137d0c8e07ece1b21fd8aa05522ba98f85a7ab24ed8c1e265fadf4e" + + "9a18c5ab5684d8ba8d3382ad53b415c73ebfaba35abeebaf973b6f18e0d8" + + "7f019420eb34e09bbb12afc5b149f1e9e9b6ae36ebde429d437ada1a2d52" + + "b998f7c75ef731132aafc3bb106a2ad3ae11223a355804d4869ebaa47166" + + "2df261d95d48ac6eb17c1781e81c0027ccf8f05c39e1eda7793cb16622be" + + "ce7a1ad5d2f72f8bf4bdb2f4f4dcadac3db3bf727f0d447adddad4500360" + + "09ee011bf4155e5e46c74b00d72e8e6a88de9a81a5a4685651b90e874dfe" + + "eba41698c98370fd9e99619ce59ebb8342417d03fc724f9c910ae36ac5e5" + + "b46c424141073199aaac34232a8e17ebbfdd80eb75e82290de92968f3893" + + "0ab53dc83ac433833576e86fbabfb9d7cd792c7e062811f4cb017710f841" + + "1e0fb65ea4b3cd68b0af132cb08330aa13579196ec632091476f268b44ba" + + "8f2e64b482427dfc535d40d3f58b4dee99053b35a3fed1cb245c711fa16f" + + "c141974c8db04f4c525205dad6ca23ccaebde585cd3bc91f5874452ed473" + + "08de95cb6164102744f90b3007e511e091653c97d364fe0cbd7f4cd3249c" + + "1f5c452becd722ccc8c6b4e371e2631337dff78efd903a8fc195a90ca5a2" + + "aa4513bc63cd43794ff06c5337329055c43d4fb547e63d6e4d14fbe37b52" + + "1411caf2f1b0df51a68f677db59aa227c725cf494ccb7f8cacc5a06ac5bd" + + "f135a2603175a5fd5e5af615fd2e7cea61934e6d938b9e672290aaccd99a" + + "7e26dc55efe928e56ae6354168264e61668a61f842a581cd0c4b39e0e429" + + "04631c01320857b4d7e260a39c7fbed0593875b495a76aa782b51fee4f88" + + "84ca8ddb8dda560b695323cdde78f82dd85757cadea12ef7cf205138c7ba" + + "db6a7361a8d7868c7aefa7aaf15f212f5f5ab090fd40113e5e3ad1ab04f9" + + "b7f68a12ad0c6db642d4efb3d9f54070cc80d05842272991bcdae54cd484" + + "9a017d2879fd2f6d6ebce27469dda28ad5c345c7f3c9738038667cc9a5bf" + + "97f8f3bc", + }, + { + key: "aa3a83a6843cec16ab9a02db3725654cb177e55ec9c0c4abd03ada0fbafca99a", + tag: "719dbe5a028d634398ce98e6702a164b", + in: "643883153c215352a4ff2bb2d6c857bafa6444f910653cacd2bbdb50ffdb" + + "cae23cc297a66e3afefbd85ab885e8ccf8d8f4930e403662fb4db5121aca" + + "82dfcc3069bd5f90be4f5bfd3c10f8038272021f155e5de0a381d1716abe" + + "0b64b6d0f73c30baf6ddfe0e6a700483cad0fa14f637afb2f72361e84915" + + "78ba117e1c03f01fd61aa8f31da6464f3d0c529524d12dc53b68f4d4b326" + + "db7fc45c63f75244002b8f9a185556f8aab85948647818f1486d32c73614" + + "b8c4763e2645bdb457721ff3901327588da01622a37ccbbd0374fec6fd1b" + + "cce62157e64c4cde22c3a5f14c54cd6db63db0bd77e14579989f1dd46461" + + "4c8691ef26406984b3f794bb7b612e8b160374be11586ec91e3dbb3d2ccc" + + "dbfd9c4b52f0069df27f04853e7cc8b2e382323345b82ce19473c30296cc" + + "453f479af9a09ec759597337221e37e395b5ef958d91767eeb2df37069a4" + + "f3a530399961b6bf01a88ce9dfcc21c573e899b7951723d76d3993666b7e" + + "24dc2570afe738cbe215272ccedb9d752e1a2da00d76adb4bc0bd05b52c3" + + "fa08445671c7c99981a1b535582e9b3228ce61662a1d90a9c79afbdcfcd4" + + "74def2b7880cac6533ba0a73fa0ba595e81fd9a72ec26965acc0f4159ba5" + + "08cd42553c23540bc582e6e9ac996a95a63309f3fa012eac14128818a377" + + "4d39936338827bbaafad7316e500a89ed0df7af81be99e2f6aae6bb62568" + + "1dfa7e100ebca5c8d70f67be3c1e534f25446738d990ee821c195c98d19c" + + "fd901e7722b4e388da90b95ac0b5b5dc5d052ad6b54f6ea34a824bcf0cd8" + + "7f1fc9a07e8f5b8aa0793e3c9c1022109a7c7ae97ee2a2867fd0cf0f8971" + + "34b3d150d3b24fcf8323de929b73cca01244df02510393f0b3905caa0268" + + "7fe35f64391e7d4b30be1cc98319716528ca4f35bb75d7e55cf7749968c5" + + "37136eddb149a9f91c456fde51937c0f35e7e524647311077e6fbe7f3c12" + + "37b9584fcf3b0f78744c7b2d3b452823aca06d144e4463eb5b01014201cc" + + "bfed1adf3414427072135d48e705b1b36ab602cae69428e7c19d39cbb4e0" + + "ca26a871d607ed4daa158b5c58a0a9f4aa935c18a66bdeff42f3dc44166b" + + "a299d71a2141877f23213b11c52d068b5afadc1fad76387cf1e76571e334" + + "0b066ade8da02fe3b0bdc575b1d9ec5d5f5a5f78599f14b62db0bef7ccc6" + + "1711482dfa4787957d42a58fdc2f99525c32962b06492229399980601bd2" + + "ee252306b1464914424de9aa414a0a6e5dadf8ffbf789e6d18a761035d3e" + + "f2ff0753becbd2dd19fc1c28f9acebec86f934f20b608a9ef735ac91f6b7" + + "83d9327cce7f4870d39bbbfb0100838dee83e6baf2b40cfc98415dd174ed" + + "72e393ad0459e8035dce7eb18eb3af2f39d2712846b9e1852cd61d06dfc3" + + "5e34fb761b67e2a711ceb4a82557371ed32ca8db2e4cd7fea0b6bd026177" + + "4057b9abc45dae6869cab1097459473a389a80a4523e5de696554f8b0bec" + + "0ca605e6acfaa00386fb5a48e0f5893860a29f35e680be979cf3bf81ee7e" + + "ed88262dc80af042b8cfe6359cf8b475560bb704728034e2bd67e590bd76" + + "1632e516e3292b564c7265d7a6dc15c75ba6f6a447b1c98c25315ac7de59" + + "9edc4993e4dc7d1dbfcea7e50ebd0b226e096500216c42de3abe352e5b09" + + "a3c9754aa35d00883906599c90a80284d172a90abbeaf7e156fe2166ada1" + + "794420fe55b1a166d752d0eb7f04e822d021c615e84777101e7c9f9dd12e" + + "565b7d093fe978f85e6142c1ca26798b45f4b8d23ecff6be836e810e314f" + + "ebd2ea66f2ac95bad84b39b7a6bac41448f237b45e9ec579235ba2bf5fa1" + + "f00286379ec107c743f06ae0d11b57a2f5b32e3bc5f1697aae812d7ca303" + + "b196a8a43259257f7697bae67adc7f121be561b2d0725982532ffc06cb22" + + "839d9066dce0e4d683d9348899089f6732de62751ca77f1c439e43054468" + + "2c531b9c61977bc221b66030f7571dfb3ddfb91d9838529dbc99612f650a" + + "d72bb78de061192068941a81d6ac341101aeb745b61bd7a87a35a2714d50" + + "c3eb2c3ea148fb9ebed948307f8b491aec277ac01903ba36e6ad54f89fe4" + + "280a17f8e7ae639e75aec16d56576f03c2a1efe4af995eb825ccaa6efe0f" + + "d6d878299a351591d791c286cac5cb049834580d47a9bb7720d0603e3141" + + "ad7c1ec2dd23d3002e15d73c1828a7f08062848b1b6fcf816bd954743547" + + "6f0d6f882125bd03095eb1b1a846d535730e258fc279f7095de7c2d3fcca" + + "a4640a2e2d5ce0974c1e073c60bb78171c1c88ae62c7213a95d36ea9ab17" + + "59093813b85d17ff106e69100bd739ede9656388bf47cc52730766a8a186" + + "9dcc623e09e43cfba1f83ae1d9f16789064ec73504c29686760ea02c6634" + + "a929ca10c6d334b1751494c6d143671ce8e1e7dcc9bcda25af895a193032" + + "ce27c1016ccc4d85507fd2265ebf280d3419f54f66ba2a161c068491578f" + + "be056f02f97be745db443e25ed2647c5348f278f4ad8bf5b2a2c2d56e795" + + "532e25585984a3a94f435ef2742a0413abed7230ff2e9724187c91f73a7a" + + "726ebf36bc8d0d959418dd586452664990889358c56720c1001c004ff768" + + "54b9850890ce1b31735fd9f4a3640622ef0b25c659e8a937daa0df7a21f1" + + "77be13dfdb8f729da1f48e39a05f592d8c98da416b022fd8edab8e6132eb" + + "a80c00501f5cc1e0243b6b096c8dbe7f8c6ffa2f8bcc7f309fb80b489b92" + + "c4878fabad42d91876e10ee64ccd415124461cdc7d86c7bb6bcd9133f3c0" + + "dfa8f629ddb43ab914c0ac5ecddf4398052229876fd838b9ae72523946cb" + + "bba0906a6b3ef26672c78cb24cbf691a5ec869d9fc912009d840772b7da0" + + "c7f47856037c7608705cd533918c207a744f75fdfac618a6981778e09332" + + "5c7d22170da85bdc61044b4c397919d601a30746cefefa798c58f02cb827" + + "0d130c813cbeb67b77fe67da37a1b04bf3f1e9ee95b104939220fb8a0394" + + "86ab8954b2a1468016f546406d1946d531966eadce8af3e02a1f59043ff6" + + "e1efc237dbf4dfd482c876531d131c9b120af8b8fd9662cef1a47a32da40" + + "da96c57dc4efad707a4e86d0b84262d850b451bda48e630c482ef7ede5bd" + + "c55147f69e2ff8d49262d9fe66368d1e38ecdb5c1d4e4042effff0670e69" + + "04e47d7d3047a971d65372126ff5d0426d82b12b253bb4b55005e7a22de5" + + "6fa54f1dfcce30b1e4b4f12b1e3c0de27cea30ce79b08c8c1aceb1ffa285" + + "c317d203a9f2e01d542874fc8035b7670f3648eec79561d6ff2fc20d114f" + + "ba4fbed462f1cd975ee78763c41663849b44cb2827ee875e500b445193e1" + + "4556bcccfaba833bb4ea331d24a6a3bd8ec09906c7b75598b44ce1820a49" + + "fca4a0c1501e6c67515d4fa7f88f6aa3cd7fbc6802131a7b14b219e154db" + + "9ed241133e10ace40e4d963f904dd9f3bdaaade99f19de1ddfe8af2b3cc4" + + "0a48374dd8eb559782bea5410f8f9a1cd128523c0157b6baad9ea331c273" + + "311492fa65c032d0d3b513d23b13b86201840d51759021e4133f873f2781" + + "8f54f34ba73b4f33107d49c8de1533856ec37bb440f3c67d42148765610c" + + "3296bce932c839fd866bec3762a38406ac2b39d0d93730d0c88cb8f765dc" + + "d8ee71263fc96068b538da06fc49e25dbeaa10a5111a9af8e8f8d78e6ed1" + + "3752ad021d9f2c6b5ff18a859fee9651d23a7237bd5a5c29029db3882c47" + + "0470de59fd19fb3bfbd25d116f2f13ef5c534bf3a84284ae03e3cf9cf01d" + + "9e984af9a2e63de54e030857b1a071267cc33d22843b28b64b66e4e02803" + + "c6ab5635291aefa69cfeb3958c09d0b37176842b902da26caae3f0d305e7" + + "c6ab550414e862e1d13d9bb9dc6122cb90ddb1a7bc6d31c55f146659baa9" + + "6cca4ea283e5e1639967889543ecb6849e355b6c0227572097221dd46c1d" + + "f8600b230e9644ba611ba45cd83fa4ac7df647b3be57387b6db12682018a" + + "de9be50a8ea7d5f7c743bf0c6382964bb385b3c207c0cdd63279c16130b3" + + "73ba974125291673344b35c8ef9a33be5a8a394e28dc1448f54d46af675a" + + "edc88ce85a11ad7e50058df4f3f2364abd243683d58a2b13fcb0dc0eed21" + + "380b666eb87f4be75e7f2842bae916c15af3e9658c55408537b2301faa6e" + + "42af4d94e3eda6a41d6d302be281e2a9299e9d0fb1f20cf4ca978e66bdd7" + + "4c8bea0f15c84d6513cdea787dacbd4bb529ed03528284cb12f6ecd841d3" + + "c58c3a57c6bc19b65d6d10692f4e1ad63b091137c8acacc6bc1496953f81" + + "2972bf6362cf883bb75a2d10614029596bf9f35e92addbb50315b30161b7" + + "de8867a1393d9583887a292cadceb54078c9c846ec30882e6ff987494060" + + "721d3c761940b91a126e8d1e0118617bdae01a7f9c1aa96bdd6c78ca06f2" + + "6c8d85664a8705334f4997c724ef98fe265985593d5a9c30798714e6de1e" + + "bd04b648be47a6b5d986a3103e738a5cd114b19b7ba99d2e2eec6181bf3d" + + "ff0fec8c54ae6118be8702c3e775d493a6fafb509712a43ee66c3f4b75b0" + + "194c88937cffa5fa17b284d2556f2b0eebf876e05f92c065515198bd5e83" + + "00d0db432cb256a4a0f9963a05694ffce3ecbd182209e0b7bb50120f6be4" + + "eeb9d268b17790ee14a2c887dc5753e0086630b3123734053aa37595aa8f" + + "31968ddae4991af4ab970c1e3cfa1146a2efd9dc42abd6af14777b8a0455" + + "3865691cbac4b4417b3fa13c154d581b498f3b8cb77adf0e42dc2f2fb521" + + "732447de97271e542c6cf8cad3ba0148cc3ba1f2983ead836a25a2c022d0" + + "43ba18fcd009d518d07b53344a5bc4d626b3b38405a114471f75dc70e015" + + "d11e8f6f57d087fa72909785573008b1", + }, + { + key: "1793bfda9c8666f0839b4b983776735a927bdaa3da99b13c9f3d1cc57d4d6b03", + tag: "bc89cfec34ab2f4f2d5308b8c1a5e70a", + in: "a09f661aa125471417d88912f0a4a14115df9a3a19c1de184878291acb0e" + + "89ee1f9d8213f62df442f8969a9a5a7c402fea09bdbe236fb832544e1f93" + + "9cdd4873802b2bb8fc35ba06b7ff96da6dc7efddfeeda84116bc525a7fc5" + + "2d84d2e63cbac00b122dc64f2d15b36595259d81a1d2a09f204c54072751" + + "dd812259df1104bb2d2ee58baee917c5d0aa2649c8a1503114501e6ed6fe" + + "239847d3d88dccd63d5f842426b600079c6bf06e80a2813b2208181163b8" + + "61dca07fa4d88254e84dac1c78c38397a016b5ad55a6b58878f99036db56" + + "89871ab3c321f6ed5895f218f8fd976c348b3f1269fcdf4d38c9492b4721" + + "6c45f499f5705830b33114d721f9731acf6c69fca681b74c2d82c92e145b" + + "7bab77110821d3a12cc818d7595a5c60c4b5e5219376c38a4dd52d435d41" + + "562802ff65ba2bba5c331c333d5adf194d29b2cd9ebb55927bb4ec17681a" + + "3f5574ad34fb4e964f2c756f6dbbb7a6876a21579a515263444de7a30a33" + + "15005458bc137ccfdff18a3892fc9f58f1de10d4de20bbcf860f5f036d8e" + + "8a188f18e5cf7ea3cd260710e7491befcb131d49a28dfb1ef688fd021a1e" + + "e4420d32fbfb03b47f5e85c37d91e49a1b0db85d966eb5434c4197433eb4" + + "9d56f2ff999c9a72230447032dc949202468261b48b6ac212e3f651d6c63" + + "03a06c90bb2d3a755ed91ba73bcdc28e1c5b0936e51e0a9f69c3ebabd3db" + + "add7abab6d8f6a44daeb3126429a01815f57444fb7022a4a510f8b564ae2" + + "dd9779b3a273fef15859a33e233724846c30d89fb78a595b6ff6c834812c" + + "00a991e405806aafd0c26a788895ad00a5e43c5426197aa8247207077548" + + "ee67db4cd6f878431a2e36e952d84b5fb89d681f553198e2c066310ea6ac" + + "3a31f5b1792620616f6c41d486fb844eeacc7fd36971abf416e8d6d50985" + + "c83cc92ea46ac37da8f0026aba30c945d8bb15080d2d95e4081bad626199" + + "3f95f57ed3252822a7caa035ae22a36c35e280cbbc82d729346cacdb1794" + + "ae9a9bb2793fd1d5c47121b135c2836063367339c5151b4e35278e97f62a" + + "fdd2f231d4b47812d083a829ebb9c374ff2ae8479cc4b76d55f9cef3ec6c" + + "4894f53e8caaeb0d8cd072960cedaf758e48e3640590d4f728626e0a08ee" + + "ebf719c96bf8ed4d0c283be09c0ae67b609e22d3b9aa6b03642854909de0" + + "5ed52b39673867bf586a632ab8072de15c637cc212cba8387515c9c9c433" + + "abd7ba6b02abd09da06a34694ad34f88515b65c0c9c247fdf9819fb05a1a" + + "ea4728c1182f8a08a64b7581cd0fb2131265edcb3d4874b009aede0e87ed" + + "463a2e4392aefd55e008eb7ba931788262f56e53193122a3555d4c08133b" + + "66020154b15643fa7f4f5e9f17621d350ede3dc70be02c59e40fea74dbbd" + + "7919d1a8d4e22ef07c916fa65e7d4b89fb11a7c24ddc4ca5f43344c753b6" + + "1331c3fa4558738ba7832b5b2a275bc9b7989b6e6888865793329806cd3b" + + "f0ba57c941d4428623e062f4ac05e7cd79ad5446f8838f2b247b66bddadf" + + "540845a1bb304a04b7edbbff579c8d37e2f6718f8690abd5231822c7e565" + + "69365ce532449a41ae963ec23a2a75e88307dc6b59cbb3fab913e43ed74d" + + "841ca9f6e4ef96dfd9f04e29e89361aece439c0b2e1943b30410a63d495c" + + "522ac3ec1b04ec4cb345f7f86969957ad750e5bd7dbf1d6a22eed02f70b8" + + "1cb5b2b020c0694d7f63044f9de0c3de1ede52009c858992d01ebb92ff19" + + "a9e0fbea18942fbafb77746c8e9e687dd58ccc569e767528bde43b62c7c1" + + "270a5721f1212de2b29a7aae2d6ba6cd173d7fbc78aec4356ce2e8ba9164" + + "d97dec061dd0c3a0e3c520a7611ac99739049dd5825537c70b7ef660046c" + + "1785546cd99aa400da848eb7c3c91247415c8e245d0f14c30d482c5849ae" + + "aaeab2568288229b08267818dae8f76fc674c684c99eb5faf88a0783813d" + + "f7298e0b50cb233f78471e5ca9cc3b04927c26a3871cf253798cc49aa717" + + "d8f18a1ddcbdc26497d188f15f86ec494dcf8f942c3e07e572385c6fa0ef" + + "40c0b625f1737543074a747a369482a0b342a08b3eccac9f9209be31aefe" + + "5a7794974f71ac0bc9a58026397ea3dd4f5e40511d58d2a3b45925c194ef" + + "13987037d736dd48b509d003a86471d5f161e0e5dd168b4f1ce32f703b89" + + "15004d8dfc708a5bb02b2e6fb67424b2cbcb31ddaa0114c4016b0917382d" + + "aad11815ff5b6e37d5af48daa5ef67cee3439283712bc51b5adf2356cb2a" + + "5181b8941fd78945c7c9d61497683e44fee456ad345e12b4258f15945d45" + + "b6ca4369ee792d849112d583fdb39cd4d333ee057355f0abc8d1eea4640c" + + "128cc1617982db0394233dbd416102eec1874081247d2982bbf9fed1b1b3" + + "8f4da923d68c8975c698f189a4d7840fd7aca9dceb7d91c076f85e1c546f" + + "4d5de4f60c91348455aaea30cac134c844dad93d583c139dd52b3be6346c" + + "4d2e6864125c5a2d0aed8f67930e1ebf8700ca88aacc914ea76ff17148f0" + + "777738cc126e75a2c81110faf02fefc47c91edbab7814599000ce55fe20e" + + "f313566e9b62457acf2f22e1141e220bd9d4747417d03e703d4e39282803" + + "386327fc65dd597f723ee28185c78d9195fc70a75706c36287ab9c6e00e8" + + "5cecbbd6043c6af8d30df6cdd8777be0686853b7c8a55a5b1e03e4431d39" + + "1725ff99875a85cae6926998723b36d13ad458220712209bfc5e8d2ca5d4" + + "4ea044d5ba846b4035e7ac7e9885f55d3f85c0c1b3d09fe929a74450f5d2" + + "9c9672e42d3f59be4ca9d864a4322cc454c2578493bd498a51bbe960e657" + + "3e5dd02c4a3a386d4f29e4578a39e9184024cd28d0e86ecac893b8e271bf" + + "ce3f944d130817378c74d471bd20a4086f2429ed66c5c99969fd8da358ff" + + "5c3be72bf356ae49a385aa0a631b588ddb63628fd162673e915cfc4de56e" + + "ae6ff7101df3b33125c9bab95928f6e61c60039b6cc07a66f9c733251447" + + "ef9c1ffefa2158a8ddf89dc08686a4cf9b86ea09914e79842d72a3236afc" + + "98a3afa0a1cac5590ab6a923e35a2ab8db6410a9d33cb84d1c48a054377e" + + "549774b25f50fbb343ecd5db095155cce9fb0c77d09752f62d4bbf16a770" + + "30452a75f6bdf73f7807d8f3a6bae16ad06b22175fee60549c22548de9c1" + + "3df35ef4e7bf7b66491a62b93c2c3fb0c5edc51f60f5704b56af30f1079d" + + "7c385b99f958ef8209e030e381d1ee8d67d3cb84f32e030e8ea2c1d0c77f" + + "d6b242a9f48707557c8682a08e1127f51221a55c733ab1edd00a9c2912cb" + + "36dde85f73b524e1a4f4da6414c5e4c18d9537722b2becc8a91bcc63f2b0" + + "9f32409c53c2beee0de6726dabcd6bf33118a5c23fb9c5c1810476efe658" + + "4bb6109c516b45e16b2f79f96755680374d82b91f2c519639a1815fd485b" + + "a3c00b46fbefeafcf25554ec5a6a5ae2da07c85b8a0f9fcde50263d9ed85" + + "038b2f7aadb9de765655bd201235218bfc74bcad6a9ddf4506167a649afa" + + "df400b85752d68a92b7a97f26b334dd77fce824862046b286a7c8e0adc36" + + "f713a252a673d4d995b268badf4bec8b8eefe85c25b823b6728582d35c4a" + + "60041114dab72b0623b99e2758f6a1e97365279bfba0eb1fc8952ca4f2c6" + + "fbffd9f5fd7dcad1125b18a796981b5ead0b6431141315898ace96f0d38f" + + "865698df8822ca7b65644b6b1f0a0f0d2e5850d4c93ec48ca3eba1b919e2" + + "4413a46d595ffa427715e499db3b7b9ab53c64abec7302bc737a5bd124bc" + + "da756abbca132f7f67e6989e09bfb23b497da31bf156bb9c69ae54588df1" + + "7420e8fe989f0472c8893b2bfe57cdae265a8cc7aeb39624167a567a6fbe" + + "bb1aa30c3dcfd14f2808a070994085e6e1fa79021e77c399f90ab1f995a7" + + "baff672cb693bd39b798b4c890b7d0a57978d6b9bcdc5bf3f4d205f8f24b" + + "2b43d3ae300a96971c9182be297618b9adceebedba1ab0f324b01d23d7e6" + + "35f009db3dbbc643c2d787567594bc639bfd78c4f3e6d948caf06f013423" + + "eb3c764666b58f886d5d28137c053c2a28535efcea400147e92ac6753574" + + "3b47f9cb48852abed1d057647d5b1c6f334eab1a813401fccd3dae332738" + + "776bb223e359f3c459b5c573ba64fa945bdd66c5ac0fcbd53b67032a7b80" + + "25f551e8d1fd2a4291bdb7941cbabe3a09765dc263e2bbb6db7077cc8fe6" + + "790d4bed5e36bd976d1e37dfdba36aafcdaa10c5f3ed51ba973379bcb8fd" + + "203d8b7282abbd271ecf947e54486e8653b7712c9df996a8ad035f41f29c" + + "ab81509f922c67dacb03f25f8f120cb1365ab3c1c286849c2722448ba9bc" + + "ff42a6b8a7a52f2c79b2bfcbdd22ef8a5651c18879a9575dac35f57d8107" + + "d6bece37b15d7dfff480c01f4461ef11f22228792accda4f7936d29d4c56" + + "cbba103b6d3e6db86e39e5f1bb9e9fd955df65b8a6e44a148620f02b5b90" + + "b2be9e5bb526d0ec75b1e723e94da933a356d7ca42d0ce8349699f730b8e" + + "59bac24a6b633759c88041d29399ce60a2ca2261c7eec1acb9a56e0e65bd" + + "e37653ce2cf7eb83a4d019c755bdc5d685b6394ecddb9006823182dd8138" + + "a1bf79a32d07a8e5e8ab221995c714e571b40bb255b79e328ab883542c16" + + "4899fffa16eb3296f310e302512352a864fd809beaab4169113027c6ccca" + + "99a92c6ce35c30f9449a3add70f10db1ed08078e8e6cbaafef630aab7e9f" + + "c8adb09c18e33fe1af3620d1e4d069ac11325e23cc18e5519a1ed249caf8" + + "ddba871c701f1287cc160019766988f63e089bd9bf1af7e6f5b9002e3b6c" + + "264d69a8bac16914ab55c418d3a8e974677cdcbea36c912e90386a839a37" + + "77b878e680c07c7cc99f42a7dd71924babf7fb0627d1f2cc60d9d390d1e1" + + "50d47386be6eefec9ddbb83b28fa7e2fd28cc3867cbe42d13b00545af8a0" + + "48cc07016ec79808b180e0b258c564739185da754f2e", + }, + { + key: "0d41cb4ac25217feb20e86fc2490e8d2ea2e8225c051252a9395cc4f56e1ae5a", + tag: "42df9f9a59d6dc05c98fd9e9577f7176", + in: "01caba7a19cdb09dc0ec6c522c61c628eacf17ef15485aa5710fed723875" + + "2e4e8e93dd4bbc414e4c5620bab596876dfbea33987e568ddabf7814b318" + + "8210a5f8d70041351e4d8410840642a29cc8d901c25fa67cc8f9664ea5e1" + + "9e433eaff7c722d0258ae112b7aca47120aa8af4420d4412a10732551db2" + + "cd3e0af6e5855d5eea61035af15a4d0d898d04033809e995706eba750a7c" + + "ac07aaa0dc71477d3020f778d0347f1a8e37c18540deb9ae967e734c0264" + + "df0e1f52b0b5334805579ea744c8784c3ae0c3ff8217cd3f53cb747f6996" + + "f3d2147699799e649061b205f97f7992e147fb20f21ff862c6c512e95534" + + "f03075e8e52f162e0d70d7a259e3618474427f400f44f75198edebae6e40" + + "a2173257d114e1bb5a13cf419c821eb124d90e89a938d91f4d2e70dfd1ab" + + "60446f1b602614930a329e98a0c30f107d342281db25b8f8259933e14d20" + + "8bbd991e42969e8b0600272f9bd408483cddfc4cb8dfe7bc19be1989c7fa" + + "129d38e1078d094b82e0a845040ddd69f220dc4aa2b236c44101d7da7779" + + "9827a7b037561b51e50fa033a045571c7267af93b96192df3bf6180c9a30" + + "7e8c8f2b1d6b9391767369625015da02730ad6070df4595eb8099bd8e484" + + "59214310cb62c3a91a4fa8ac3b3d7b2017d4254fb465f0a248e1bf45819b" + + "4f0360f37c9a79d405e2bb72e5c25a1b4df192cfd524d61e1e8b274f2fe0" + + "634c73f0653c7c9e9062c9d081f22a8b0327897eed7c6e870f2815bbac8f" + + "585c1bd868759a98dcb5c3db2f6c53244b9cc494a56f28a9ba673167cea8" + + "b799f37049ee7b0772972b3a6603f0b80eddb58ef03f916106814d72f000" + + "250b3573c97c5c105910d79b2f85ad9d56002a76a1f43d9d1c244ef56d3e" + + "032a9bab95fe3bd5dd830ad7d7e341f28b58c0440658f7fc2ca98f157708" + + "1c647e91432cb0739d9acdbf973ceb9b0047634d695279e8837b04dc5357" + + "f013fde3c55c9c53bf1d817ec59a1b18ed0ac0081ed9bbb3bcd1a5d3634f" + + "50f7506f79dc6a4ebfa640bf65682fe9aeca68088e276937669250064de1" + + "c19ad6d5c697f862114d0f81d2cc52be831ed20d3aab1e41fe6f476b5392" + + "af4799392464c51394c2d1a8325ee2e84f1635d295ee663490e538eb338c" + + "7126a8e731ad5c0becf144c7a9cae5c6493350b589385de29e1a0ad6716c" + + "346ec4f0a31ca5ea35c59ab6b099f65d7f0b3d00925a1da1b5777c029aea" + + "9679e895d7100645dc83f81d82a6174beab2357f7888ea640900cf3ee67a" + + "e0724a123919d78e70e05288f67e5e69ffa6f345be8a96e58bbe260184b5" + + "ec5c0c1354cfd516ebdb8d420029137d41b029641959cc07fa7b4e16b39d" + + "17f36b2367057410a42e0550e9ec1dcd2df4604d52d4f9dd1140d57af08d" + + "50e1527dad793b6d649324de799754f755818bf10e6d1ab614958dbb24ac" + + "8e2c01270a90ec3df4379c3f509b5ef721b0fd4f91a1bdb8127ae4dc74d0" + + "75f6cd8bb28319d6f8e8d8ff64fb4a42d646e9365156c6bc72cc46e9cd1c" + + "f9e735549e3df9a8e6b5fe541948b126190117db71fd1d61ad84be0f725f" + + "20b99eb141b240326d399976c4f2ce5823d94649a9580e1e8820bf49184d" + + "fc34378a60bea89b12aca69cb996c17847b7fb517cf2d51f16d78e3875ce" + + "aa33be15f6a154004f0e1134c6652c815c705efc34bcf35bd7743d28f0a2" + + "77d82dea4709dab41fbfb4e0cbc118c17aa00808872f0edc6437c357cd31" + + "74a02aee61890464e03e9458853189431bf5df6a0ad5d69951e24be7f266" + + "5bb3c904aa03f799fe7edc7bc6779d621cab7e520b5994f81505d0f01e55" + + "96e14b4c1efdf3e8aadee866c5337c1e50066b3acc039c84567b29b7d957" + + "683cadfb04fb35402acaba631e46ca83dbdd8adf28e377ec147e4d555a21" + + "e6d779d7c5a3078ab72702234d36ca65f68bd01221c9411f68f32e16ef04" + + "99a20c2d945fa31b79d9965853d38ada9d48eead9084d868c6bad974b0f4" + + "0956aa0fcbce6dac905858e46c4b62c0ee576b8db7d484a524e951f4c179" + + "decfc7d6f619e86dee808f246dd71c7e0b51d28bc958110d122fa2717148" + + "77823242711632f6e1c7c15248655ced8e451a107707cec8c84929beece4" + + "efe5503d3c1763d0ab7f139f043e26027d5e52a00d5414dd98a324a8fc2a" + + "06a1345cbde747f41099c3377b86bbdc5a17c8f6e5b773a761f78573832e" + + "4359b143810361dedc79142fffc49ddc0b32f225d50d360ceec3920fb0ba" + + "0693b644ee07fbd1ce829e223a02794b197614061c4bfa46112d105c2b7b" + + "4efea448501d146dece44f6640d674d5749db498b32969de6e165e705a18" + + "2aa1f3d8e16892b0120337640d52c9bee35e5b4b17f03eaeb31205c8ecbe" + + "1ae1b110023016e40ee87370a65c5c20bfb00f100d3c6c1de6e4a1c90162" + + "f25bddbf300ed637330206788a4ff96903f971c9618493ad074412af625c" + + "ff9e0f8f183bbd5e96c1f28307e6cae8b50cc0eb1a3a8154e44e9de947af" + + "002e4d1098d6b0ee3f2e71a10d03eb444729c42461283f37be8af2ce81ba" + + "bac246a05c2c94efacc43f0cf9ff3df38ab6fc1648c796ae7026ea95752e" + + "b70873a6da59da10d8b5316126431c4a17289466e95dc739c061d7a4b13a" + + "450809479eef421bddcdade77a6df133410328c754af8999a09b1a5c056b" + + "ecbb6fc2c339586ab92100f46d2fa1fa689994b36aa70703d76bf7738adc" + + "f0589fdfa6bd215339ad69ed983f62efce0add5a63fe7dfe4bfa006ff16e" + + "0cc06d39199ad60adcae12b75ca98d764502a783373da3a41281e03c2037" + + "e1b3ca7f7eb60e2b67427e97ec72d36670db7662c6daa505701fd279f116" + + "ac0ef569471f204e1531c25a4ac3ce19b6f68a8994b6f89b5abf034a6507" + + "32c7fad4206eb4eaa7cd9a710d866bf3c3f13c16faa268ae0cf4f69be909" + + "bb9b79aab80dd25101d4cc813a48d3f38d870f10ac0b6768005aa0e69e87" + + "dfc0424deef06414c9ba6f498c93c41c692a7a6221fb5595b390a32c70e0" + + "2cd64471c797ee8a143725849c1e054ee2043dcfc0b4cb1c00be21a14be9" + + "2d9a07f1b4e975d4c86b8a5c1387e6c42bf393e078fe86d24612d497e14b" + + "874485a3cc922b5b6d91295d7b79ab8bfa1c7f64b51e761d19bb9da82a5a" + + "a34aa469699036b6b2c55e2b84f84942f10585027ab07e2e0e562e0fc3dd" + + "36047850ded84be4416e22aa41c7a2f7d4a4d8e3dd420d746a1d8d56d87e" + + "5133a1b4380bd9a89500fd6d7e68a1ec02eb9e79e4a13edfdde1273466e4" + + "6b0e6a75f59ff6175716629da52463ad21de27f40fa2e25a566eec4b2696" + + "4af3a717dfb0170a73144c0bd9b00bed67ad8c0a146eb5a055812d071209" + + "c9d530cd4f50a41488c2238898dea8bb36b0f1496d3ea8c4ff8e263b367f" + + "64977679e697d88e5295bd97ac16a0420850d1ead9621e25a3f58925c266" + + "ef5246488b1c15a8fe0d8ec4291864faa5a67b2388b7786f47b6d27e8fe8" + + "46f85f85163e54155ef95cea4901e712a44404a4d3f27f28dd961ce36b84" + + "f3856770f07f20a2ebd34d77405beab04ddfc09770167d7d6340f494dc6b" + + "7e4c3df896bd974730193b1e862b58d4a5938e6e4ae8897dba8812924379" + + "e54f51a71364d39f76e24fdf2c6c704479ce85b456558ca6947b8fd76f03" + + "78273f0a7bcd1d860ef1defe4eea8fdb81c73eda028d82fdcb2248582ac4" + + "59eb7698a811e6c5823be886410f6b8577ff2e8252343b6ea890016ae846" + + "01c5894cfb988121059fd9c8fbc1596da470a149404fc67baa15383d38cb" + + "d17ac107b4ff3c1ca4c76b7930de02b240e7547d39f4978e0cc1fa37f8c1" + + "012b677f07bb4df4486196e9b0beb823a3827585475b878e3f6f0a2d3836" + + "2c7d34f9f3c91ed46c39cec95c2a0b6f0279a03a00ed5035b0725c393849" + + "cdb1ed3c0ecbcf3c2ce108017f468e1c3d469c03e8231d4195344ced70cf" + + "daa667252cc1554dce8d0c54eb4cf4da62367d77d7dcc02f81e788ce9f8d" + + "d306ba1b48192359cfe92bdbea9980f87ea0677d7d2082205a436cf514e6" + + "fde5eadd21b13dc836ce33b5dfb6118bcac79ae00fbb16d61f00a923b145" + + "f9caa9f3a2c7f0104f8b052e390987e57c8dc80cd5f0358afb0111af1fc4" + + "e31f92bd832ad35fd2e0bdf768272de52ce0b152f74d43a8973ad516b3ea" + + "f5937ec8a236ebc86adeba610de0cf7168453111f3c983b64df07678cae0" + + "a75466ae15adfb127328e716448cdbd2c1b73424cc29d93df11a765441e0" + + "0eeed72228e1099bd20569d9d0e9e5a0b3c11d0002e2896631186483db61" + + "c1a0cb407951f9b1ea6d3ebc79b37afb5a7037e957985e4955979b91fb85" + + "61ca7d5e8b9cdd5b7ce0130a880d9241027b011fea7696b0c695d4949ca2" + + "d0cf22d44b9fee073ecaef66d4981e172e03ea71a6edc7144393bfea5071" + + "2afac137f091bae2f5700bfb073a6d57fddcba674a899d7349044a10aadb" + + "2e7f547887dd2f765f394de5dc9ef5dbf1eab4d869be8cb68aad8e2614ac" + + "37bbf21ccd5a832ee09fdd07ce50a580a2af36256b1046e646fe3dff6d20" + + "0c5110f1ad1311bc39b8114cd11ecdb87f94df43d4f6468932fc0ed892d0" + + "3d8f3db3f8323ebb29776ab7d260493a36700bcda668abd62126a8189e91" + + "df2d2970ef688d4e8172fc942e69ba63941a36b79ac546fff38f5f7d1176" + + "57612a662ea38134e1090c3e903c9adacdeefd3ac2a0467e9f5125058c19" + + "7b2260d2afad2b0e627a9ae52cd579ee27168065658089e1b83a2d8cdb47" + + "e08966e4ec0018e78c4d267f9575b8fea2a42de5c2d25356fe4b8c9cb1ac" + + "daf0d1af4bf58b9704cd4bc08471e3b9a0e45a5693433ede2eb1374bce44" + + "1f1811cdc7612d7bb61f4f34aea0a44757bbcc12a55c1ba41a7901eb004e" + + "689587a38e5b4df4574ddcc7b2eda97f6e480d7d39f45247ea3b03c90a93" + + "0dd168b65d52a59ce9c2cb4e860cc6aaa0ee02a58d0c8ba990194bce80fe" + + "8c34ba5693fb0943ec2cbfc919e534cc47c04f502b6c217c2f860d1d482a" + + "a016aa02adfc2bea3171fc4e27e2a262fd37b824099aa227fccca508f778" + + "b8c6ec7aaff1d15f6497753f439daa9e52060fd6e9e056e6843d770fb057" + + "6d9e2e782db4843c0c2c7f408a17376719a3c5cf9fa08f04f8a779885a16" + + "5cf93ce404be", + }, + { + key: "ddbd5d6c5ebd61fa72b453dd849dc302c98a0f3e300f4768bf1dc698a3827dd2", + tag: "af608b71a353e63c64911558baa122f3", + in: "c67e2524b0de16483158a0232078fadcf611e4fbdb9e642e397b21222423" + + "cc2ed42ed34ffcb178448919ee337eff9d7d691f622e70fd3317cfd271df" + + "fe6a9d9b7e07db0d20813e2331164a654386db2ab06ae2983bf2460eaaa6" + + "3aa0171fb87afb82e85b40d95c8993b2039d32e9d38473dd13f41fb1ff1e" + + "261752ab004b221a4472b9b1a0e139f0c999f826a26a7e7df362b0611aac" + + "fa83c55cca2f7c0138d2c30313c2f6eb357278328ea6ebd6a5077947e18a" + + "a97c34b9dde3b6f2de4b83778ffcebc8c9cb58756691d5e2a3d15a759a2e" + + "5050b6da937a6f5551aec069a08027d60dd870d175d2a5b5f0b4f3143904" + + "7445c368a5c866370e9426abbc1a1c5a272b96731c4128aedeee93e8e00b" + + "b450601a6d31ea279b9450e738b4a47c0dc22d2d8ed5d44257f6318e0c59" + + "b951fb6b57746062ab95cd73c23ef0a5c000a7d14c18bfff172e59b6f6de" + + "aa61b81009e803eb05e24fb0b706870e18889a9180ac16a042d12dfff9d9" + + "1b88130f045d2342fd5ddc5f443681c31090459f262d1a65654c55251fc7" + + "d5a67bd2e62940ccd606f3e50700e4d1e992a3fdf0388b9ce3df9de6dda1" + + "5c1cd6b70622ac062dcb7ed7058872c00ff3df94032853927126cf6fa4cd" + + "c468d91c9b52dcbc272fd7ba920dcd3ea1e048af9c3286dba74d988ce9ce" + + "77174e25a87935352721dc23b60a9549322fadbe6a00dd1197dfa25b33fd" + + "9e5713afcfd0fae6dbcf27147fa58d995580d7e0a903c895752fe9819f5b" + + "b002ed752719552d0f3575312f2e618173a8ae7c147ca64a709053e5d2e1" + + "2f4d1ea337afa9ac4f9ba62760046ec1e48f4ed8f6df66786c9fd9f5bc7f" + + "9ca2526e1327b042f4657c405757690e190c91f260dee2dd3d2e6616b721" + + "e489c7c3cb828478a3d953b88f09904e7927cdf6dbd6a5419eeeb83c0be2" + + "51934a80dfe61e09442f0761aa2d013e10aeec3a32df204571ce8984a430" + + "9bbe30ccc91977790bf0305d2651ee450b749c3e7761534e45970e70a0a8" + + "473cadbc88f096970c275f188c9d2644e237fd50c2e24c1eabbf7578e80e" + + "6500762ac513fcd68cf6f8bb7a9d9eedadca059d9ecec07fe6fe7792b468" + + "9311861728dd482f087c28374cf9c5ea20b2c8630029e8485fa6fe518c74" + + "ef77d44eb7526ca764e50b5f34ed0f253a91fb2af6e59338e2af6e041e01" + + "084e1efade1aebb7d1b698ccdb8b4248ac89cd40d9517d840960c08f5e86" + + "88d8ba2b54889c1870d315498b70e0e9720f2c8c53a3377a8c0bd2d6a1c6" + + "f17c6ff847eb14def6855dc3886b99039e528b421ccbf6064e39263f8f3d" + + "340d5d20b1b14c264ac2310b5f3a0c6f0c1006d0d4f1a69af68d28ab447f" + + "cd17387e1fc98f164982a6d05dd32d6b4f0f1b04e40c6c6e0fb4467dd6b1" + + "0c5a9c92cc8c2bc97ef669b6d55cdd0aa8a15c46af954359165949012713" + + "4ea9f74181d54a300d3172c9f01db73288ef6a709c763a4891666d0baf88" + + "8531dcc77f0911412d096aef9033fa36b5c1ed283b8b5c109e45b5cde911" + + "6f3da2533fa0ab81929bd5783271d5501a9e4fce2aff9eb5a70a4215b253" + + "46885d7e4225fe34bb55b309a114a312693d60ccc61267359a8c2dd28141" + + "226e7cfd99f0f12c69df57d75dd790dbabfe3145f7fd1a24fa58e03bc2e2" + + "6ea19288af4929e5acc517d8f52a074745ff4644d94179eae6ba7d267292" + + "bbd2053167a0da9be5e4b6cd0a4200fcac5182d9957dffbefa857e662b82" + + "fc3a7cc32506e78030ed5c5d448d7f1b4fd854a735a0c50016bb85e6e716" + + "0f87527bca0de235f4b7dacb75be84919c15a5b8cf6bec035795cb67061b" + + "7855c2134c1b1bfa6affe04b7db239f73af6ea9c02bc9f7972b7f6400b6b" + + "838f4653aefc42179c21765e3ca7a5e96b4402ff544d4bc2332756a23500" + + "11241dc42ec6848afe127c00b9c333e69bb5a54ea5c7193e59ea22bd6d32" + + "af4f56b1bd2d5982ef7d9c1b02d7668525e4e81b68a400f7afc2653f0f41" + + "a03e11c7a02bd094830093481afbab96397245b9f37a568ea1c4ae248cdf" + + "afc87f88b1fb5dc300d8e9039af4e6e701b458ed3f32d693f2e869b76bb5" + + "1358cbbe5b5089013bf452734388a176cccfc1ae9b7cff603631ca48e129" + + "b5c9573d4e379547272cce8aeeeb407d3fc57f782a0eb5fcbd41e6fb13be" + + "7e4f1067cd407b42a6121b2969c384916ba2b32563e659f52aae09c8ce2e" + + "3c500fbb7e58be74cc1592dcfacd9f0d4cea1a90a18658147c81cccf6fb3" + + "078ed27f369e7646f551386a74e1b07074d93e0c1f298c761af46cdaae9f" + + "f4be86808b66d0e228016d27a3a77c843365cb847fddccb0bbcfb3b9008a" + + "1bacac59ffb0aa759a0568c72c556caf0ac1091431b574687c5fc7bd486e" + + "963e0fc3bdc828d988734a21070747c955cf8dba2df1c3a0ba8146cd58b5" + + "91b6d54712db67a9851b1607c8445bc97406eeb7488f5f85e547850d619c" + + "407f97632ca1801f52c09c2b314b4ab0f8e7fb5851fd60852f4666913ca6" + + "bc840c1ec8f8f06caefdbfbf02ce00f20b87b14ba9e651c80f40a31d0306" + + "403f541776075fbf23733a6b19e3b44d04b455b29ef8effa70cce0c59331" + + "7119abc07aa8c8d0246a760b0b36a3d87b244e83bae8a745b8277a531298" + + "f5d0283498a509c89898ddf0f7a7455be1f8a6889c46d323f1dd18c3babe" + + "1751a05f871f0639f50967afa46c19cb93d9c2a79c81e2436a7a62f225bc" + + "37c90698640f5b43673e1dc276de05ff1e29acdb4ace5121659db5f23c49" + + "57aae22f53e6f2cc935824fbd07c2ac87672eeeab895c3f06e09e178560e" + + "2fcfa7097f10201dfb8b1ebac08ca806c1b3ba3aff9284846a1a3beada53" + + "e9f7ade12eb89b5591f462b2543bb4090e081fee9fb53bbf821dc92d6b16" + + "fe820ab2ee4b1f6c0b6a6f19edb0bf6479e257fc73bcd60dc2261d0a4752" + + "e23a0be18abf355f3065177d8c3c14e21edc178d0abd1b39f703e6335131" + + "ec90cba3d9846cee7354a06c320a3f61b8a269abc7138831614f57ca6c19" + + "a4a621142889cd924bf4ffb82b57f871b854f3157e8874c22d43a5726900" + + "bafbb8f2260a1eba3a462e23d4def2ccf68ebaae8e52739a1ce67c039eaf" + + "9a6c3232fbb5a91d1e59a8dcd3798ba71345fbf83d09b83b41cc49d5ff5f" + + "2e809d2b1d5fbc1e7001ea76b9b2d8f896eb6609e2e1c5c562d2a6e74960" + + "2d67a0f6b43a201d5087509b8dc7b0440144e308c18ff8b96b607de2f20c" + + "6ee99bb05367a8b25947011889f724965a2b5c52c9db1e0622df9343c548" + + "d054699badeb15fc41055af0d79a2bfc1a5b4574634fa0dd9dd10a6213ed" + + "b6991187dc560facdc27440456a0a209fd7f5ee4fb350ae71f869723e5eb" + + "5338e3d1448bc993afca6957f4cc7b047a2c7c9593b7234725e66cc0eb23" + + "3824eb4cb905701cc522ec210950b871397c6c0bb3d0b839f2eb1a120f70" + + "36107246df4dfb2c24891bef0bd1dc131f2c9d7c295ee967e3184d963037" + + "fcc9e0b8c7011c8e04b4e70038150d34caab4f8c0230418cd2d8a91146e4" + + "4e11cf6707452ddc03d9b4e6380658135dfb48f62c0690ebad75167f4dd1" + + "c0df3ed555b5081a7b82616d9e501757c83c2193d0f640236d59f9c97a4a" + + "5c8bf532aea2cf5964ed2dbd8a70c01ca5c7677224cf2a37f3b24d8fe4ba" + + "91cd3b5033715de227de51deed15afb8eda9d2b9615d197b8f98322d7096" + + "79c5131eed48050fbe0145a9284e236605c25a4876e2adba42f4e35a8949" + + "3d59bbf44b3338d9d2e65a7d7ec6c863cd47cae9e23181b07298078a5e9b" + + "06a5c7e1059f474eb1a4247e8f02cdd4efdca67d22035b12abecf9b15982" + + "de4932a28e797bc4de38442cff2cba263eeddba0ab14fc706dbca04eaca1" + + "b4cc13000a10e35b32461424809b299798e4d8e66c92aa3181c5df16ab65" + + "9611cb625e895a8021af8c60960227d6f2ebeacb17b13536a5ff139734ef" + + "37cb67018ef9a410b856e6f6eddbe3f59b088d538c50a8f3f0912d06e47b" + + "88d773069aa759cc614e1f53cf6e572c127123d1ab56b79ee753a921cb22" + + "a60e4e6cae768c9966de4e2625484f2e990154da7fca84b6e6c0b59201e7" + + "fb8a729cb20b4c774381e84f1bd6e304543d952dc76ef741b72f3a4ca7a6" + + "ea7958b8b6337994ed82dcf988eb70f509610b9a279ab4d0f28cc2b2dd99" + + "3b8637a6be0cb4b5f67c79654c6b15e1b61120374ba9b974a628c547f11e" + + "52d72d39f8f9c5dbfc23a89f22d38984dd8d5c3ca72cd54e6adfe2b3d163" + + "86afdb50967846a4c311351a51e5fd322757bdb061d44c8796a61fa4db36" + + "793bc11984eac83bbcefb40d0bc7bab0ca81e7df3a7f58c6fe800396716d" + + "832acaddff6d72c8e19dc9ea838294ead800deadb6bc18d3e399fa76c46c" + + "5d88ee72a86a87399423b0578eb6e27d78156ea2abf6f08b5cbf747f2f74" + + "5301b694bfba84bfe3c5527acd50660eea5105a2644c1aa92f954a604fb6" + + "a1b3b2d0331497deafc3aaadc7040b9188a36cf607ee85a0655ae963fd32" + + "91dd58f8bb50b4e46dcf7c2957639bffa6b12d895660dc0323b7a092f999" + + "813380b820e1873c60d3e3038129c66d507862100a5d5842150869e7873d" + + "6bb6ad022350ffa3813aca26c80ccae72692bed9c77c9d4da23178c57153" + + "90b5f4505240a796ec9d10a7f280bd60a570b1b693453807707651fc0464" + + "03e4768965a6f42f112152942134f0a38c84137c7a6e086ef1ab9ad20d24" + + "3b93356b305c0996ab7d02c02c44cbaf8f7e60b8c0b8c9fece3f189b099d" + + "dbd126b7357c1c4ea1c8bc1ad93db91ea9bf043a4320acb60b502bec37b8" + + "6b2a5004b8225e549e613c6f83b97b7e4aeda1b013e0a442d7ce2f14e78e" + + "a94bab700c9ac0abba945e28f39fdadff223c4498cb204f01ddfcb450a41" + + "f32ae47f99a49114c6646a5cb103e9cd75f9d81dba417e48c4053e3b0295" + + "2267cd30589b0f5d993a5485a6ead1ffab9f2f4294c5853ba76383a326a6" + + "a42fb8b78948aa49f0f1f614bd0a3fbd2a58a3197daf2094605bd838285a" + + "1260f1265dca74aadd95652632335fd17cafcb73b202c3f0e5da836c2dcf" + + "2934f005935dca80154af43fa34c8ba440d1581b74ff17dfaca369dc9aa6" + + "734c03916d78e1b952691cef918fe033d33f7f4323cf724ffb8cd6c219bd" + + "046e9f268eb0601098e93daa59dde370e46269dd7c54891f71bee2829a53" + + "df86a2c7fb1046cd7c98fa21cd83597be554997a70acebe0b6e60f1f7098" + + "6f65adcae24385cb7102bdd3e01300ffd15d00f9764b3a5c51e35e5c9cdd" + + "da84f4b656fe514ec4ff8dcd774373f8a9103cf36abefe875f7084b9bbd9" + + "42e0c997ec2d860a4b622ff1a39a628582fd81f237d3d8f6843d26ac77cf" + + "bd48003e8e8c591ff813a9a897e3149ff0297ff476299d717e54d885cdd4" + + "4c3ba6ebf54bc7a1", + }, + { + key: "b15578da1020f662ada0ad4f33a180d9f8ad4991b3720bc42a22b52625c7414a", + tag: "b0e4ad4a010afd6dd41ed82868cda555", + in: "6d2afb7a9154064341bdbb533f11990d4987e7c90fbfc0167c1e58d6efff" + + "6010f7ed569dac62ad37183b0d384519ebed0bf9c6e05a070b4858e6b846" + + "547ab5e45619c866f83cce83dcdab6a8a6c36b115ac832de1c6d433b94fa" + + "35803fa1a36f1ee114f8632402a027a74ac110394f32ec4006beb0057f09" + + "a94dada8bd0d1ca9a14b1f2efb8f526d79d6438bbbaac0ca1a43935627e5" + + "d129d52c06bf6413af07513bc579447eccc3a9406645c94dae59dab98d6a" + + "f92fa90fd4efaaa4bec466806ed401d2083cda587139ad7e9ee2adbb1dfe" + + "a88b59dd788b954a0f52c3854a3fffecb4bea83debbb2f5f8883e6415d3b" + + "ac1b872df1afe185468adc59364c173082f1dd6da9d348f5f5ba2d216243" + + "23de1f623eeec875bf31d12acec40dc0c1b9562826f3105cdad4c43cf45d" + + "829aa8b14012c47847aef7a2a6e3935fd972235f5d3a7ce4ad3582785393" + + "602e2e27329914021eff38ed2926c88acec1551f17a1b818fc1c3ed4b3b6" + + "6825d55bea269d710123b52e12ca9520a069d9c6a21df3a0253b3a4a6a8c" + + "dc226d667541548834da6bdbbdc165f39e40047d4b647c507d981be17b3a" + + "836063436241a8bb46b11a2867b621413c42d838e4578b72cc1982e34bde" + + "c303b5575ef4b8dd9fea8ed5bf69539413909d03461d3853b5fbf714a61c" + + "769569f42b38fac4b849104e2f2ac1dad0e388646278789f83e0b0511571" + + "019d3bfc5b03ca4cb5564e4e75e103ea1b6000be6588e27105d7cdc2d2f1" + + "f680ad34ef823ac4bd4068146e9997834665aec7dcc7a82ff28d85d52dd6" + + "9c18dd35f326bcf709f74df5981bb90ca8e765fef9f0698a19e12220b287" + + "24a6d9e4f4c7ce93f8ca9a126689ad1df820072557ce3db246cdf41599dd" + + "44ca841bece6c7869358005536e1189aa86b764e890ef90970d6e3831def" + + "fa890bf8692381123924e7d9df804fd770a0a30ee97d5dcdca302833efe8" + + "1d4b2505b17382f0b3429b38c41269ac95e36e9f5a1dbc6e6c8963741917" + + "02a23198decb4efe6809fcbeb5d0c9098a4c300155dc841610e55c8a6e27" + + "2a38a39de3d8ebf38a750af25836ffb1bb7822bb98886280f0cab6838c01" + + "cec57961bdc2e1bf158248309ff9294adcb962252b1c24646d132a3be2c9" + + "1ff82e8e101facbdb807826cc9d1840a90874ba08692e808c336c9d280ee" + + "f36a43a75c746fb864f85711e802546ab5cc3f8f117904ba1a85d6e4b729" + + "85122c5041891e16d55b93d6fc1b7fcfdc80ed3d72d55d64b8895bbf2f8e" + + "d188684e7e89afdc1e6a7ab9bd1d3da95d68698df2cdcbb2e1a4ae70e2fd" + + "dd4760f9e5cf4255eeb1e9e8009ab507395bacb8b2177e7c5757ad02baa9" + + "a96db967d20a150d2dd7f3081d90675fe0c82f94aa3cfdf6ac5585583901" + + "7a8e122170cc817f327a3c8ef44acd6e4fa81b73bcd0bcb5792eed470481" + + "152e87f7a20c3f7c69d5a8199bf9bb7c7269b450dc37a9b22102acaa8438" + + "134d6d733d231cee9522f7d02fbb37b5818ad3ca72df4752230ee11392ef" + + "8f8219be55202bc3d476f5a9078b32fb63d42bed4cda5ef90cc62467bf5e" + + "418ecd9d5d0cf1a33eb9a930e652ce96057fef40b65588aac67621d651a0" + + "9003dbc3925912e385296cd3b2b386a44113308ddf2af52ca390487eb20c" + + "716b76d78ad45129e7c285d918de7107ea8c3b0cfd9e73933b87c0b2b505" + + "cb4c95794f2ee6d6d43e2e76026923a0bbfbc3bb22df9ad729452283ce62" + + "dc9b26684fd45e07650581afd73713a708869a069c58b599ab478974f206" + + "dbd3e4e563e346ff1881723c5fd440bdf9f70f761c6f746113397d7c04b6" + + "b341d7e44de7de0aae79badaaef5ed372ef629dffd52926110683ab2d4da" + + "a4be83eb86c8700703a660edd5a5029f66f1581da96fe1feefc970ab4086" + + "a83ae02e959821967bd27b3b629652f5bc3db2b7f1af674f9f3fb3a788f7" + + "88e6dc1722382971831a7ed72502f85b25888c1534d81c0a4f7351ecc40f" + + "4e0412e05718403fae5746d313a78c80ac297f1391ad389070410e1330a1" + + "b07d683d1c795bda74bde947f2cf0dc9638b5d0851cda27df030403816dd" + + "3b70f042888c9c192656cc4b9fea10b81b5347900d9199c8f0f47d42f2ee" + + "482b68acfa5ff47d9950c950a926a497d94c6a796e0b715416520bd6c59f" + + "30217718d5f1d7bf7c24039f6467214ac8783cf011b25c37c67dfddde426" + + "40afe97f94879f4586954737b86701b32d560f08caec3fc45184bc719c7c" + + "5bf699074fde814acae32c189158c737665a8f94637068322f0c23ff8860" + + "f1b1c1bd766440afee290aa6f7150c7adefa6d72a738cd2268da7c94788e" + + "bb39002e9a328a51f3a92dc5c7cd9e4faed5702d3592ad16217c4978f84e" + + "af0fd2c9e4c6f4dcdd9112c781eb41a9aacb0f7935bb5c92d41e67cfff6b" + + "991ccefbd667ffeded1de325da50c33e28e2eef2f636c9726dc5bfe753ee" + + "c7bb6e1f080c89451f81bc8c29dc9067ce83deed02769714fa9bb477aca5" + + "c09089934674a0cc8e4b2c3136b2e4af8040cc601b90a4dec898dc922ca4" + + "976ab5ae4ac5af93fa5b1854a76ac3bcc2090bdeaa49ec4f319cf7c7b674" + + "6d8e617abb3361b28b27983dd1b139ec4f5af7e116439d7ecb16534817bf" + + "264dbd8f59e80b443be12c17fa013c7f4d029504c9bb62b296c2326f4f49" + + "cc3201b70ac3f62abb683c630179594a6d4cf30fd55b163bf8d01986bb6b" + + "cb7050fd527f095c45661920268e56f760fee80a29c9d37b7fc23f608710" + + "1e723038e64ee1b91c4849d69bd95fc9bc24fc4a234f4855f2a203e3f699" + + "c32698585c83781677739f2c48697c93b3388dcc64aa61f01118495ded33" + + "21ef9a1c949481f96005f8d5b277a7d6a0d906ec304cf4292df172e72d20" + + "29ecdeb65f06267a605f376804bf7bc5b82d5c8facfe7e41dc10806d27e0" + + "bcc5a341d80b3c1532407f75088716d732632cd88b0037f0d829bf385fec" + + "b52a202956489f61f16b0f4781bf59068b33d7330571d0b4a6ed91830258" + + "e1220b308784fa155be9bc821f5c0009a33802fa66dd66d1dde997dddd97" + + "873ddf65927dc1be979af2b5f110eee627dc1e210326ac20544a757ac168" + + "1823f3dd04b1ddc4bf96677a0a87633994e7af2ec99b7d5dfe44c6192be6" + + "a6e69d17b074256da3947808fbf68c7506a7e2c99e6b64d1ffadbd6285d8" + + "e7e032e24d42dde0594bf03fd550be05e5d66c91a660cd1ab7cb1f43fa9d" + + "69885203a7aee35a28f117427d7ac02b742f53d13b818f8631081b1730d1" + + "5b4e1e283cc8e5c4fc3b4652fce05fd8db821f99fcf93e6842816a549791" + + "7f6c49cc53d733788b2fe3c687de58bfe6153c70d99380df1fd566a7c758" + + "8052c62e73340d6a9eccd2ed26b763d518f3a0c4d6362212fbecebb4ffb7" + + "dc94d29944fcc4ab37725b105aa7571f364146782356d8ef056a0be93a55" + + "0c890df8fecc178776fe40703ad1bd2443d92c420be4306d99686592c030" + + "fd3e2230c0b48d8db79002e8a832ef27edb53a45532955f1171203d38414" + + "b4692e901e9f40f918528fc494430f86cf967452f456b01846ac6a383fc0" + + "de2243c7d804e8643aabcb78e2653b145f400a999670217c8da43bbb9c11" + + "e074176424be0c116c304a420120138e901eca4b12ce68fec460b23bc0c7" + + "765a74fc66cbda0e503e7b1baf5883744e468c97c5f1c4b0acc4b87de9f1" + + "4b537405dfb28195439d1ff848d9cd28a8d375038ebb540a9075b7b5074b" + + "ebc18418a370f1d3ac5d68f5d239513002ad11bfc2b7ff53e2e41ccffc4b" + + "0503acc4967c93ae8590a43439b5e7987d10cb8d1957bd9ef717ee3d12df" + + "5d6736c1d8bd8da102337a94b7d14f830f6c403cbaf7925a8a2a7af1311c" + + "57224967a38f6ca374013a9819c55fd2e2a5fac4f2490be5b059f4cd9c60" + + "2d62f80789eb8d9ab893c7f44a4945e41886af218179dfa754bbb59aab68" + + "13b71d2202eb8fc8a425625d21176a28a620e21bb0dad820c0b7051ce8d1" + + "3a33f3af0958bb6cd89f9d6414ab00ddd1d2f9fdece9183d0c05fcdfd117" + + "10d250e4b2029e6992a88293d0457e73e5b1b6a1aae182c69b9cb664992f" + + "073595ef68117026ad7ea579a4043cda318931eee7b2946a34cdc7c9755f" + + "80cc79a2bfe3ed9c79dc52faa5126b824868c965eeb37e9e4e6a49600f3a" + + "cce93c0853b546edb310dcd16a5755f15b1098b2f59dbd2d90e2ea8360ba" + + "f12108236e854465456598ae2f7bc380f008f2e3cd7c98c87643cafd7c36" + + "d40e2597236428d46aa5b260f84b4212d5e26804086adcf00363ce4becb4" + + "9b57eb2847b2f18ec82c99714ad4ddfe4ff3bcac1d0fcaa32660a1dccc68" + + "5bed83254c8e2ea0ae3632a70cfbcbeadef922d78a006d43ac7ab1f8a609" + + "c6e0ebc3ca6bb8430f1a562f41010db74b9febf931ca794fa08d1bc17780" + + "532ae76f25c4ee679d788835dfa4e70ca154c9e2865c3750ffe7b837eed1" + + "972be058fdf2bdb3eb301867bb132306c7aa237f6771d60bbc56cf31cb30" + + "32a87204d454542de747418470025ab84935d3eaaca01dbbdae9ef6b5d3a" + + "ca62ce9f871a3e1272b2b671582c096a349c00f32d742ddb17993994d8ae" + + "fc178cbcf9abc03114ff2bf7db8f757c63d6898faccd822f5c2e9a7570fb" + + "9cfff148570888be24ae42644c1a5bebb6f6287147a4bcc01c7675be9e4a" + + "897519dd3132a7cc2e778f8c90d23dc8073f6fa108d7ef82d561794bd9d5" + + "f1faa306334f338ac3ba99c853f79c24f7048fa906fde87d1ed28a7b11c0" + + "66a3bb98f8d21055aaafdf7e069b77b60b3d5cbe7c5e4379c7651af955cd" + + "82a19a09caf36becb6cd3fe9e12f40379941542709991066df21b7b12dfb" + + "2416d83fcdc33bb583e3b42f24f53edf8dc7c579ad3be831c99f72bf9fb7" + + "a35b6562e824e039e6bf1adc8f5ca53846de7bae11c4317e696d887df33c" + + "525f0a9c01fc29f2c26c90b85fe82ed8bd50954cd4e9ac7c85c7f3efec75" + + "da1da4ed173cb695cee295190527edb3cb06c5dbdabe0228cc60b6455153" + + "76244f27aa56da2db10f2659090137ffb82c57233c833e0bbf22d6f647fb" + + "97b3652d2888b3ab08010b8e8a6967d560b747757806736dc98b78226634" + + "f1eecaa4a2e23ba36591acb5737d735c5bc7a2e36f1a46946927e061fdf7" + + "7a3b68ef582c26b01f5aa9a438ecc26c6941221d1590c838072f9e471fe7" + + "fd59dacb0d092d40d76ea2f7c6e954a132a015bd4cb31147f3ebe4518322" + + "916438a62836ac85a4cf4492190a85bcc8edb37e38b99ea552d749c30f74" + + "ca20c298165e8ed02d4671e0b41cac3a32a345b9349ad22c2a4bb2c16a4c" + + "e0613ca0f0518759f7d2b33cfad2fae764f410d4d9ff8a76ae02a8107e7e" + + "01d9cd0552676b85ba002f19c01ad5f416d1d08bb84fec7c3555b098dbce" + + "48e1a5d847895e54db9c5b80cc22d5b87cd41a1a94be102bdd45a3cda5d1" + + "181e10446d213d6b3fdc350d486d2011d705c5f16ccf7519065c47bad7d6" + + "89c71e5fdf9d04bfb91eb1f07fa0f001009c1d4b1f6a116a570823a8580b", + }, + { + key: "392468efccff36dade31fc1c62eb38bb61394fe448def9d9d9beec2413ddb418", + tag: "e1122e7c8e6965b90addbd46d8a548d6", + in: "6a13d37f0ec933194c227351f4a19b507d93465b1f3e88dcb5f1ed1262fa" + + "58ea99ff31e6fc85c39c04129fa69195b71b2060122fe618dd9430a63f97" + + "54b52a80b3cd099f248f91a468bae211a27bdb47ba005d29881ea5143a82" + + "967c4c30c9a4f0dba1a4975e6407fe296d40023a00efa06be763f2d73d46" + + "a2901ae28b3d8ce18009a462e223b71476d7b954c138e177d15a390847de" + + "96a7f7fd0598748e86b0f08e64d915e67c7e3cf936f3dcd60edebd36e2a1" + + "d65b6ac29530c48ab3bd52d45b4f938a19b9b31e2911105a8561600d5377" + + "905a67112ec28025aa680350ff85b808c5b4c98b7b9567d03f5ed3911ec9" + + "365a8de4b15ca62adaa69e5ba710eb1756a346016c67a297d8624f9f1ab5" + + "b3fbce98b141049f0ce26c85d2f8a9cc6ca8ab6c6e148be968931430dcc6" + + "2bf58ea9698ef52a5d271cf48e6748ac9e04bc7ae7da205a1a7535478322" + + "d820eca146cedf4b2f9aa9fcfd77ab56a7276977401dcc1f96baa1b607e0" + + "256bd04ec324ec67a4313e2d5a53d3a3fb5332927929b20c63bde805f637" + + "eb1050fee2a152a0405634f55c48a59fe370d54b2ab1671dae2c7fd92243" + + "10627808e553127c74f724362b4a6ee49b697daae7df3ddc5d2ed9d6befd" + + "77fb9f68fe3041f6ef13f46f34ab682ab8563e8996344f82b2ef006a8d54" + + "3dd9c1db4979d7da97bda45e722065f8a238f0873217b783a9a629a12b3a" + + "4de437445039997bd243efbf5e3b6059b9459d395290efb9081c632fb694" + + "81000dc74c395cb507422df181aba20f776ce3fd8765ac485021992c98b1" + + "67c68805662cb4356a0ee7ba6bdae51ac10cd06bb5b2f3a72841c714c8ed" + + "bc56998fe2fefb9bf69e172fdf54b2ab138ae59372c52a67e93882a3000f" + + "d966992aa2250c6ff93e9cac89645d70625d79332ade5dab7eb1adbe7dce" + + "5a013fb65ad32fe22ed16fb9bb35eca1f37a0433c320e8752f8fc4b7618c" + + "5e4df2efece832e259ad98b895c474e47d0e3fc488bea8f717a17de0dcf7" + + "597fb8fe12e62246296f9a887dcc3a700820c190a55a4931a7d44bd3bb2e" + + "ab6c8a8126f1be93790cebabc1d69e01796e6cc80e7c16bbc82fb333fb21" + + "c774ab7db843242838e82d8e1cb8ccab385e67a4271fe7031d74b6e8edcc" + + "8ed585d1c05a365c7665899c1dbc561151d3b44bceace77c4f53c0e0f6f7" + + "74d42f9ad3e56f1c2a8d53879d695f895690afb4698472a3d52d67159313" + + "133c87823fe0500eb68fe286f8b9a2f59f12785d026dc97bdbf793c7d1eb" + + "155f1f136aae66c256583e987f718afbe733e0a5ce30d021493fb84e2242" + + "5b18754d126235ef80335004fa84f88361a584753df409360cd8bd45bace" + + "8f48156bec66577bf2c685089f5ac7e7ec76c0df068fbaa47661f8517f92" + + "e14723b3b278f151816537a7212c96bd340a00c15c9c9bc9a2a5d163655d" + + "84b38073e2be9217cad97d362d89d4baf3ce0a8d8562f19a8c97a9aaf5e7" + + "77d60456360ffb77b30f177d2809052020d141697ecf9cb65f42b9190caf" + + "6540b2c82f6e5a8482934a6a1a5711a8c24546cd8ba432068404eae5a827" + + "2e09efc3c6037af4feaac0a46329229b010ecac6b9f077a9b076bb6d9ce1" + + "38401eb38d124baa11507a994185295020bf9b754fcf78430db9253f5929" + + "87c46c0f8589c4e463b15a3840b1cea795e24cf6b20f29a630136e0589b3" + + "8dd7fbe5ea21da72c88bd8e56473586822aa3765660a45a988df9b8eb8e8" + + "141939d3e4cc637c5d788064d40a9f7c734e43fdf8d7189a5d76700d9743" + + "fe0122944663afdb88c5201318ca782f6848b742ddebe7463fd4a32280ac" + + "1cf8311e9137d319de05ce9cd85abab24c5364041c14d3b4ce650400498e" + + "122166eccc12784b7ac3b262ac0b198ffc26eeed9a5da5374f7a2a53c87a" + + "78c217ea1fbf8d38f62511657b73109f31691aef14d82ce6e1010eae9e6f" + + "a419e5c1c16c0cc70651eb3374c03549a1bc7d3ed42d60f886102c798dbc" + + "ba56f0a2b3b9b412530c35f5f7ed06311ee14571f9c26ed9c81ef38ff000" + + "2f5ef3aab7351e32049a6ef8f48a43da1d84402d229df513dfaf1b2e4043" + + "6ce68c70ebeddd7477c9164f0dce45a6fc5de050f52ec269659d5854bcae" + + "f7762ed7400713c27a4d523eaf8c136c4a1ca00b9e9e55902daf6cdf8528" + + "c22ca1f2fa7ce87902d75a6850e1a5a4592497be1bb401878f18b189b0e2" + + "c59d10705bfabde3cd2da01eb452006b294108d5d42e88e9e15424d8cd0b" + + "8ab43a6c546b3dbf52e47b59cde6a3e417b0395220b6d63736d429da3458" + + "9a2524f1629320206fa7f1d8a041e17222c4a5814561937e1030e6375c77" + + "9dc988bb928bbdbe2c2eb20111639725d82b5d7192cd3e4acc27581f0ba7" + + "286cff41f97aa5a52ea0083de5057fd2ba985aa738e4d03fcf11ebab1d97" + + "e2ac77d1c2beb8799150a421a07b3777d0b850f24194b8309135b13da6c7" + + "e38653a711e407a1811290fbb7bc15d8b12efc6916e97ead41e042a44721" + + "e9cde3388073d921595bcddcac758dc675173f38242e65e4a284aaa7e8fa" + + "6adddaf00bc46428ab2d8601205b8895bcedfc80ca0aa4619ed6bb082ddf" + + "33ec04fa5d417f33fcdd238c6b11320c5a08f800e0f350b75d81e3bcbd15" + + "58a1eab87a3c8c2ffd7ba1d7e754e607cf98ba22a3fc766c45bd6f2569b4" + + "84639e6611714119d188a24a5e963089a16ed34e20b9f154cad8ac6031dd" + + "7a3a885afc2ae5e003ae8d4e4aabdb3e51dfc423b8cf4ed9ae2010072cbb" + + "b1108c7da1ff075e54ed827a0963ac5523ecdf3fc5eee7b4d1a6773764ec" + + "5c30f41690523fd70d895edb7ca6a1806d54240c4c7b43410da73503a323" + + "90d9070ed30da3a2fb5eccd40d083be7cf8bf40b4279f819cf795b6f075b" + + "5a67a10a06a6076d0d83c72efea05f244901c4b5fd9eb380432519311baf" + + "8c81f6325df4d37ff4d30d318f904ebb837ec76b341dd00a8f247cf0bbe9" + + "6f3784dc8f5feb344958fdf1a9ececb105f8770826db1f17a5281e997951" + + "d3c60cc28fc3e66ffeb5dbac315f98f6d240208043f28dee963d843e68ab" + + "57d847f76ae2f96ce6e37f377ef5dfef2176ecd7440ce4dadcec2231b606" + + "e4a80420fb3ed135640e1f05d6bd58b8dce062dd7d36b885d424f6318e5e" + + "a0753efbb33bbc7360d2b5dfab3ae0d5e000b8d31f2ba0f5fd8b34f96b55" + + "28fff35e769461d0f03cf3bfdf0b801dcbbf2838180cb9b108e06c353e3f" + + "0b9ef61678cfed1ea37ae76bccb5ef5957ac2c8e8f4794c8145a15f1cc88" + + "bfb0881080326c481b373c3bc9b07a9b60a0c8bd5fa4f6f90145590a5227" + + "6fcc0ccc2375d0ccb571d414d1b0c38b4e02c39db4d701c5e25e90785ef4" + + "d26f35edd8c4b96455bdca7245cfefd9cfbd2f319615e5fdf07bb9564fa0" + + "44bb35a58391d02e3927780b4076bc0893dfcb4b63a32cd7a541a4a8c253" + + "0349c6e96e378dbeb66dedf87d813d0b744452c1c4088507dca722193827" + + "9e2dfa24e4a409de494acf654f44262db9206a7717fa434ac4fdc6a6eb5b" + + "1fd5a193b6043bc4327c8c09fd6822eaa9df37bbcac1077754a295621601" + + "267b68733b62dadc2563f1700af180141f29899e2689dbbe9745ba8477f4" + + "352921900b403a01c9dd042a8c1b0e0489959fb0b0a8431c97b41e202204" + + "212ebfa00c593399dbd14d7aec07b8292d2e40b48f05fcd54a15da4a24d7" + + "2759e409f4c7b5b98fce4abac6c30e4872d92efa1f96479ec30f21699825" + + "50fa60584f5a09051a00f8e7dbb3853e66ca3f05fbfe43bef9b120a25a01" + + "eb436ba8ecda715201eda72e517d628f883386c1503aa8b8e75610f7155e" + + "9f916335ab6d6f0f9589b6220cd2b81c2c937dc065d3d14a7df8cc916cd0" + + "0ce1bb53fd9c8974298d3bd316f3658aa8cc6904f073a1472149e4b08c64" + + "5e11abe0428ccb6174df2103edd735965d6454b543d3f01410f77053f65e" + + "c1d1aee56fdd3af23bcd4e1a7fcc4e600c4831007c33fe5f0c8300f686eb" + + "9b4d1e4f08fe4ddc8a90be14dc3a5a88ff96716509341d5db24c0d016863" + + "998b1859c5021df815a6f1ca9845f1a8e99dbad132b406227c5897a1bdf3" + + "e698962f799133ff4429decbef6ce036296facf38e4812fec102b76c6d30" + + "beba1b70722254fafbc471096153478c971db7d96263660209265cb10f13" + + "b34b5fd55c4abe818a5f9715d8a85094e2946b7a001b47f629e26c636d86" + + "4968ad2ab616dfe28840bd60b4b9855c8dbe1cb873fcbc4577b5fefeb8bb" + + "4832039867dc35db9c036c83bc204396e3474ddfe806c77c65c936f488b6" + + "7c1028739562d7bb055d21441af29ae2921290e548dccf8a56021385422b" + + "15da6b232b24151309a75a00296d11aa1952a1513110b0faa93d1d8cd9ae" + + "fa9f1c59377ec9165b2c9e07cbde40db7b81bca6d58fc28bae8f473cd0e9" + + "a2420e0b943a83d284108626c24ac570b1d6c1ab971e71f43fbd6c00e171" + + "238141a6dc987a60385c3a04dd147a2f8e80dfe727b104c0fdd80b326f59" + + "0b9f86fd7b2fd1122a390979889eabd803ab57159c8509a1443eb6789382" + + "090a770ae4eba03306f96e50e19a7d44c584ccc230d104548946efca4520" + + "d61de5f473e2f4eada6c8ce9c7ee975eb4f63c0483cb775ed7d3cf690a61" + + "7d6656d683a8512707d81ca5ba176a42bcffcfa692129f292607d2a47536" + + "ccaeb464c9272d6f3816074b712af602470088b253deba18771e5f67734b" + + "587707cdd06f35264b2262fd253c25b5d38ee7db287610e5398062b7a34e" + + "6e4cf7447d00873b930ad148fd96f0ab18771bc468b874bb109924101c84" + + "c4e239ecc7687d875e4d94a1a973620ca61e35a872c2e2e61a502169f1bb" + + "4e5ff5fa2bff657be6195b3e2c7151a52fc0096d98e7f08f5a98f570aee1" + + "7b4275f1356e87e080ce0e1b9bbabe7dea48b5903bc390ce23472ad64a89" + + "41c3247bfd23ea90b2dee09085571bad85568040105e098f993bb37e43c3" + + "e6d511171c77cfc450570dfb9fc6a3930ef43c03f8213f6203d545d791c7" + + "d3fa42d5dde1655038d35c5dfacc12e9dee24fe833977549eda68ae8b508" + + "be277e743921b584f9dfa0eefbd8bf3c23f51efdef7f7487001d29e8097b" + + "ba63289cfca743023d1668555a46fe6d5b7421377414df1e9ef135480622" + + "22e2e9a7baa618d88f407517f6317b6a0ba3384ace16d68631d59ea169d5" + + "092d20afc1a481b82be5e734bb092953a0a94702bae1a0f48d2a22b9a05f" + + "f64493b7b2e984f27582b1eb937fddf8512c49830435d146dcc291a4118d" + + "5dc638b99cdcbcc5860de7a92c5b13cbd1e01e051f01af40afe124346320" + + "d3626bf9d8f7850744e032a993c276fd388718237740c6caf260fca60b8d" + + "d846102e3262b6e05ceca00c6affe938fac1847350865fc858d3ddd1d130" + + "71d1221ce7c5d575587fcba580e544b74d877ed5ca92763ef0ca0d7bfa08" + + "d57a0216b2a01a2b9ec74b8430051e0074862b7be25b6766ab520f2eb75d" + + "eeb979c28f03795f6f1e4b8410beab19a20febc91985b8a7c298534a6598" + + "f2c5b0dc5de9f5e55a97791507bc6373db26", + }, +} diff --git a/libgo/go/golang_org/x/net/dns/dnsmessage/example_test.go b/libgo/go/golang_org/x/net/dns/dnsmessage/example_test.go new file mode 100644 index 0000000..d14fb3a --- /dev/null +++ b/libgo/go/golang_org/x/net/dns/dnsmessage/example_test.go @@ -0,0 +1,134 @@ +// Copyright 2017 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 gccgo_examples + +package dnsmessage_test + +import ( + "fmt" + "net" + "strings" + + "golang_org/x/net/dns/dnsmessage" +) + +func mustNewName(name string) dnsmessage.Name { + n, err := dnsmessage.NewName(name) + if err != nil { + panic(err) + } + return n +} + +func ExampleParser() { + msg := dnsmessage.Message{ + Header: dnsmessage.Header{Response: true, Authoritative: true}, + Questions: []dnsmessage.Question{ + { + Name: mustNewName("foo.bar.example.com."), + Type: dnsmessage.TypeA, + Class: dnsmessage.ClassINET, + }, + { + Name: mustNewName("bar.example.com."), + Type: dnsmessage.TypeA, + Class: dnsmessage.ClassINET, + }, + }, + Answers: []dnsmessage.Resource{ + { + Header: dnsmessage.ResourceHeader{ + Name: mustNewName("foo.bar.example.com."), + Type: dnsmessage.TypeA, + Class: dnsmessage.ClassINET, + }, + Body: &dnsmessage.AResource{A: [4]byte{127, 0, 0, 1}}, + }, + { + Header: dnsmessage.ResourceHeader{ + Name: mustNewName("bar.example.com."), + Type: dnsmessage.TypeA, + Class: dnsmessage.ClassINET, + }, + Body: &dnsmessage.AResource{A: [4]byte{127, 0, 0, 2}}, + }, + }, + } + + buf, err := msg.Pack() + if err != nil { + panic(err) + } + + wantName := "bar.example.com." + + var p dnsmessage.Parser + if _, err := p.Start(buf); err != nil { + panic(err) + } + + for { + q, err := p.Question() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + panic(err) + } + + if q.Name.String() != wantName { + continue + } + + fmt.Println("Found question for name", wantName) + if err := p.SkipAllQuestions(); err != nil { + panic(err) + } + break + } + + var gotIPs []net.IP + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + panic(err) + } + + if (h.Type != dnsmessage.TypeA && h.Type != dnsmessage.TypeAAAA) || h.Class != dnsmessage.ClassINET { + continue + } + + if !strings.EqualFold(h.Name.String(), wantName) { + if err := p.SkipAnswer(); err != nil { + panic(err) + } + continue + } + + switch h.Type { + case dnsmessage.TypeA: + r, err := p.AResource() + if err != nil { + panic(err) + } + gotIPs = append(gotIPs, r.A[:]) + case dnsmessage.TypeAAAA: + r, err := p.AAAAResource() + if err != nil { + panic(err) + } + gotIPs = append(gotIPs, r.AAAA[:]) + } + } + + fmt.Printf("Found A/AAAA records for name %s: %v\n", wantName, gotIPs) + + // Output: + // Found question for name bar.example.com. + // Found A/AAAA records for name bar.example.com.: [127.0.0.2] +} diff --git a/libgo/go/golang_org/x/net/dns/dnsmessage/message.go b/libgo/go/golang_org/x/net/dns/dnsmessage/message.go new file mode 100644 index 0000000..d8d3b03 --- /dev/null +++ b/libgo/go/golang_org/x/net/dns/dnsmessage/message.go @@ -0,0 +1,2103 @@ +// 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 dnsmessage provides a mostly RFC 1035 compliant implementation of +// DNS message packing and unpacking. +// +// This implementation is designed to minimize heap allocations and avoid +// unnecessary packing and unpacking as much as possible. +package dnsmessage + +import ( + "errors" +) + +// Message formats + +// A Type is a type of DNS request and response. +type Type uint16 + +// A Class is a type of network. +type Class uint16 + +// An OpCode is a DNS operation code. +type OpCode uint16 + +// An RCode is a DNS response status code. +type RCode uint16 + +// Wire constants. +const ( + // ResourceHeader.Type and Question.Type + TypeA Type = 1 + TypeNS Type = 2 + TypeCNAME Type = 5 + TypeSOA Type = 6 + TypePTR Type = 12 + TypeMX Type = 15 + TypeTXT Type = 16 + TypeAAAA Type = 28 + TypeSRV Type = 33 + + // Question.Type + TypeWKS Type = 11 + TypeHINFO Type = 13 + TypeMINFO Type = 14 + TypeAXFR Type = 252 + TypeALL Type = 255 + + // ResourceHeader.Class and Question.Class + ClassINET Class = 1 + ClassCSNET Class = 2 + ClassCHAOS Class = 3 + ClassHESIOD Class = 4 + + // Question.Class + ClassANY Class = 255 + + // Message.Rcode + RCodeSuccess RCode = 0 + RCodeFormatError RCode = 1 + RCodeServerFailure RCode = 2 + RCodeNameError RCode = 3 + RCodeNotImplemented RCode = 4 + RCodeRefused RCode = 5 +) + +var ( + // ErrNotStarted indicates that the prerequisite information isn't + // available yet because the previous records haven't been appropriately + // parsed, skipped or finished. + ErrNotStarted = errors.New("parsing/packing of this type isn't available yet") + + // ErrSectionDone indicated that all records in the section have been + // parsed or finished. + ErrSectionDone = errors.New("parsing/packing of this section has completed") + + errBaseLen = errors.New("insufficient data for base length type") + errCalcLen = errors.New("insufficient data for calculated length type") + errReserved = errors.New("segment prefix is reserved") + errTooManyPtr = errors.New("too many pointers (>10)") + errInvalidPtr = errors.New("invalid pointer") + errNilResouceBody = errors.New("nil resource body") + errResourceLen = errors.New("insufficient data for resource body length") + errSegTooLong = errors.New("segment length too long") + errZeroSegLen = errors.New("zero length segment") + errResTooLong = errors.New("resource length too long") + errTooManyQuestions = errors.New("too many Questions to pack (>65535)") + errTooManyAnswers = errors.New("too many Answers to pack (>65535)") + errTooManyAuthorities = errors.New("too many Authorities to pack (>65535)") + errTooManyAdditionals = errors.New("too many Additionals to pack (>65535)") + errNonCanonicalName = errors.New("name is not in canonical format (it must end with a .)") + errStringTooLong = errors.New("character string exceeds maximum length (255)") + errCompressedSRV = errors.New("compressed name in SRV resource data") +) + +// Internal constants. +const ( + // packStartingCap is the default initial buffer size allocated during + // packing. + // + // The starting capacity doesn't matter too much, but most DNS responses + // Will be <= 512 bytes as it is the limit for DNS over UDP. + packStartingCap = 512 + + // uint16Len is the length (in bytes) of a uint16. + uint16Len = 2 + + // uint32Len is the length (in bytes) of a uint32. + uint32Len = 4 + + // headerLen is the length (in bytes) of a DNS header. + // + // A header is comprised of 6 uint16s and no padding. + headerLen = 6 * uint16Len +) + +type nestedError struct { + // s is the current level's error message. + s string + + // err is the nested error. + err error +} + +// nestedError implements error.Error. +func (e *nestedError) Error() string { + return e.s + ": " + e.err.Error() +} + +// Header is a representation of a DNS message header. +type Header struct { + ID uint16 + Response bool + OpCode OpCode + Authoritative bool + Truncated bool + RecursionDesired bool + RecursionAvailable bool + RCode RCode +} + +func (m *Header) pack() (id uint16, bits uint16) { + id = m.ID + bits = uint16(m.OpCode)<<11 | uint16(m.RCode) + if m.RecursionAvailable { + bits |= headerBitRA + } + if m.RecursionDesired { + bits |= headerBitRD + } + if m.Truncated { + bits |= headerBitTC + } + if m.Authoritative { + bits |= headerBitAA + } + if m.Response { + bits |= headerBitQR + } + return +} + +// Message is a representation of a DNS message. +type Message struct { + Header + Questions []Question + Answers []Resource + Authorities []Resource + Additionals []Resource +} + +type section uint8 + +const ( + sectionNotStarted section = iota + sectionHeader + sectionQuestions + sectionAnswers + sectionAuthorities + sectionAdditionals + sectionDone + + headerBitQR = 1 << 15 // query/response (response=1) + headerBitAA = 1 << 10 // authoritative + headerBitTC = 1 << 9 // truncated + headerBitRD = 1 << 8 // recursion desired + headerBitRA = 1 << 7 // recursion available +) + +var sectionNames = map[section]string{ + sectionHeader: "header", + sectionQuestions: "Question", + sectionAnswers: "Answer", + sectionAuthorities: "Authority", + sectionAdditionals: "Additional", +} + +// header is the wire format for a DNS message header. +type header struct { + id uint16 + bits uint16 + questions uint16 + answers uint16 + authorities uint16 + additionals uint16 +} + +func (h *header) count(sec section) uint16 { + switch sec { + case sectionQuestions: + return h.questions + case sectionAnswers: + return h.answers + case sectionAuthorities: + return h.authorities + case sectionAdditionals: + return h.additionals + } + return 0 +} + +// pack appends the wire format of the header to msg. +func (h *header) pack(msg []byte) []byte { + msg = packUint16(msg, h.id) + msg = packUint16(msg, h.bits) + msg = packUint16(msg, h.questions) + msg = packUint16(msg, h.answers) + msg = packUint16(msg, h.authorities) + return packUint16(msg, h.additionals) +} + +func (h *header) unpack(msg []byte, off int) (int, error) { + newOff := off + var err error + if h.id, newOff, err = unpackUint16(msg, newOff); err != nil { + return off, &nestedError{"id", err} + } + if h.bits, newOff, err = unpackUint16(msg, newOff); err != nil { + return off, &nestedError{"bits", err} + } + if h.questions, newOff, err = unpackUint16(msg, newOff); err != nil { + return off, &nestedError{"questions", err} + } + if h.answers, newOff, err = unpackUint16(msg, newOff); err != nil { + return off, &nestedError{"answers", err} + } + if h.authorities, newOff, err = unpackUint16(msg, newOff); err != nil { + return off, &nestedError{"authorities", err} + } + if h.additionals, newOff, err = unpackUint16(msg, newOff); err != nil { + return off, &nestedError{"additionals", err} + } + return newOff, nil +} + +func (h *header) header() Header { + return Header{ + ID: h.id, + Response: (h.bits & headerBitQR) != 0, + OpCode: OpCode(h.bits>>11) & 0xF, + Authoritative: (h.bits & headerBitAA) != 0, + Truncated: (h.bits & headerBitTC) != 0, + RecursionDesired: (h.bits & headerBitRD) != 0, + RecursionAvailable: (h.bits & headerBitRA) != 0, + RCode: RCode(h.bits & 0xF), + } +} + +// A Resource is a DNS resource record. +type Resource struct { + Header ResourceHeader + Body ResourceBody +} + +// A ResourceBody is a DNS resource record minus the header. +type ResourceBody interface { + // pack packs a Resource except for its header. + pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) + + // realType returns the actual type of the Resource. This is used to + // fill in the header Type field. + realType() Type +} + +// pack appends the wire format of the Resource to msg. +func (r *Resource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { + if r.Body == nil { + return msg, errNilResouceBody + } + oldMsg := msg + r.Header.Type = r.Body.realType() + msg, length, err := r.Header.pack(msg, compression, compressionOff) + if err != nil { + return msg, &nestedError{"ResourceHeader", err} + } + preLen := len(msg) + msg, err = r.Body.pack(msg, compression, compressionOff) + if err != nil { + return msg, &nestedError{"content", err} + } + if err := r.Header.fixLen(msg, length, preLen); err != nil { + return oldMsg, err + } + return msg, nil +} + +// A Parser allows incrementally parsing a DNS message. +// +// When parsing is started, the Header is parsed. Next, each Question can be +// either parsed or skipped. Alternatively, all Questions can be skipped at +// once. When all Questions have been parsed, attempting to parse Questions +// will return (nil, nil) and attempting to skip Questions will return +// (true, nil). After all Questions have been either parsed or skipped, all +// Answers, Authorities and Additionals can be either parsed or skipped in the +// same way, and each type of Resource must be fully parsed or skipped before +// proceeding to the next type of Resource. +// +// Note that there is no requirement to fully skip or parse the message. +type Parser struct { + msg []byte + header header + + section section + off int + index int + resHeaderValid bool + resHeader ResourceHeader +} + +// Start parses the header and enables the parsing of Questions. +func (p *Parser) Start(msg []byte) (Header, error) { + if p.msg != nil { + *p = Parser{} + } + p.msg = msg + var err error + if p.off, err = p.header.unpack(msg, 0); err != nil { + return Header{}, &nestedError{"unpacking header", err} + } + p.section = sectionQuestions + return p.header.header(), nil +} + +func (p *Parser) checkAdvance(sec section) error { + if p.section < sec { + return ErrNotStarted + } + if p.section > sec { + return ErrSectionDone + } + p.resHeaderValid = false + if p.index == int(p.header.count(sec)) { + p.index = 0 + p.section++ + return ErrSectionDone + } + return nil +} + +func (p *Parser) resource(sec section) (Resource, error) { + var r Resource + var err error + r.Header, err = p.resourceHeader(sec) + if err != nil { + return r, err + } + p.resHeaderValid = false + r.Body, p.off, err = unpackResourceBody(p.msg, p.off, r.Header) + if err != nil { + return Resource{}, &nestedError{"unpacking " + sectionNames[sec], err} + } + p.index++ + return r, nil +} + +func (p *Parser) resourceHeader(sec section) (ResourceHeader, error) { + if p.resHeaderValid { + return p.resHeader, nil + } + if err := p.checkAdvance(sec); err != nil { + return ResourceHeader{}, err + } + var hdr ResourceHeader + off, err := hdr.unpack(p.msg, p.off) + if err != nil { + return ResourceHeader{}, err + } + p.resHeaderValid = true + p.resHeader = hdr + p.off = off + return hdr, nil +} + +func (p *Parser) skipResource(sec section) error { + if p.resHeaderValid { + newOff := p.off + int(p.resHeader.Length) + if newOff > len(p.msg) { + return errResourceLen + } + p.off = newOff + p.resHeaderValid = false + p.index++ + return nil + } + if err := p.checkAdvance(sec); err != nil { + return err + } + var err error + p.off, err = skipResource(p.msg, p.off) + if err != nil { + return &nestedError{"skipping: " + sectionNames[sec], err} + } + p.index++ + return nil +} + +// Question parses a single Question. +func (p *Parser) Question() (Question, error) { + if err := p.checkAdvance(sectionQuestions); err != nil { + return Question{}, err + } + var name Name + off, err := name.unpack(p.msg, p.off) + if err != nil { + return Question{}, &nestedError{"unpacking Question.Name", err} + } + typ, off, err := unpackType(p.msg, off) + if err != nil { + return Question{}, &nestedError{"unpacking Question.Type", err} + } + class, off, err := unpackClass(p.msg, off) + if err != nil { + return Question{}, &nestedError{"unpacking Question.Class", err} + } + p.off = off + p.index++ + return Question{name, typ, class}, nil +} + +// AllQuestions parses all Questions. +func (p *Parser) AllQuestions() ([]Question, error) { + // Multiple questions are valid according to the spec, + // but servers don't actually support them. There will + // be at most one question here. + // + // Do not pre-allocate based on info in p.header, since + // the data is untrusted. + qs := []Question{} + for { + q, err := p.Question() + if err == ErrSectionDone { + return qs, nil + } + if err != nil { + return nil, err + } + qs = append(qs, q) + } +} + +// SkipQuestion skips a single Question. +func (p *Parser) SkipQuestion() error { + if err := p.checkAdvance(sectionQuestions); err != nil { + return err + } + off, err := skipName(p.msg, p.off) + if err != nil { + return &nestedError{"skipping Question Name", err} + } + if off, err = skipType(p.msg, off); err != nil { + return &nestedError{"skipping Question Type", err} + } + if off, err = skipClass(p.msg, off); err != nil { + return &nestedError{"skipping Question Class", err} + } + p.off = off + p.index++ + return nil +} + +// SkipAllQuestions skips all Questions. +func (p *Parser) SkipAllQuestions() error { + for { + if err := p.SkipQuestion(); err == ErrSectionDone { + return nil + } else if err != nil { + return err + } + } +} + +// AnswerHeader parses a single Answer ResourceHeader. +func (p *Parser) AnswerHeader() (ResourceHeader, error) { + return p.resourceHeader(sectionAnswers) +} + +// Answer parses a single Answer Resource. +func (p *Parser) Answer() (Resource, error) { + return p.resource(sectionAnswers) +} + +// AllAnswers parses all Answer Resources. +func (p *Parser) AllAnswers() ([]Resource, error) { + // The most common query is for A/AAAA, which usually returns + // a handful of IPs. + // + // Pre-allocate up to a certain limit, since p.header is + // untrusted data. + n := int(p.header.answers) + if n > 20 { + n = 20 + } + as := make([]Resource, 0, n) + for { + a, err := p.Answer() + if err == ErrSectionDone { + return as, nil + } + if err != nil { + return nil, err + } + as = append(as, a) + } +} + +// SkipAnswer skips a single Answer Resource. +func (p *Parser) SkipAnswer() error { + return p.skipResource(sectionAnswers) +} + +// SkipAllAnswers skips all Answer Resources. +func (p *Parser) SkipAllAnswers() error { + for { + if err := p.SkipAnswer(); err == ErrSectionDone { + return nil + } else if err != nil { + return err + } + } +} + +// AuthorityHeader parses a single Authority ResourceHeader. +func (p *Parser) AuthorityHeader() (ResourceHeader, error) { + return p.resourceHeader(sectionAuthorities) +} + +// Authority parses a single Authority Resource. +func (p *Parser) Authority() (Resource, error) { + return p.resource(sectionAuthorities) +} + +// AllAuthorities parses all Authority Resources. +func (p *Parser) AllAuthorities() ([]Resource, error) { + // Authorities contains SOA in case of NXDOMAIN and friends, + // otherwise it is empty. + // + // Pre-allocate up to a certain limit, since p.header is + // untrusted data. + n := int(p.header.authorities) + if n > 10 { + n = 10 + } + as := make([]Resource, 0, n) + for { + a, err := p.Authority() + if err == ErrSectionDone { + return as, nil + } + if err != nil { + return nil, err + } + as = append(as, a) + } +} + +// SkipAuthority skips a single Authority Resource. +func (p *Parser) SkipAuthority() error { + return p.skipResource(sectionAuthorities) +} + +// SkipAllAuthorities skips all Authority Resources. +func (p *Parser) SkipAllAuthorities() error { + for { + if err := p.SkipAuthority(); err == ErrSectionDone { + return nil + } else if err != nil { + return err + } + } +} + +// AdditionalHeader parses a single Additional ResourceHeader. +func (p *Parser) AdditionalHeader() (ResourceHeader, error) { + return p.resourceHeader(sectionAdditionals) +} + +// Additional parses a single Additional Resource. +func (p *Parser) Additional() (Resource, error) { + return p.resource(sectionAdditionals) +} + +// AllAdditionals parses all Additional Resources. +func (p *Parser) AllAdditionals() ([]Resource, error) { + // Additionals usually contain OPT, and sometimes A/AAAA + // glue records. + // + // Pre-allocate up to a certain limit, since p.header is + // untrusted data. + n := int(p.header.additionals) + if n > 10 { + n = 10 + } + as := make([]Resource, 0, n) + for { + a, err := p.Additional() + if err == ErrSectionDone { + return as, nil + } + if err != nil { + return nil, err + } + as = append(as, a) + } +} + +// SkipAdditional skips a single Additional Resource. +func (p *Parser) SkipAdditional() error { + return p.skipResource(sectionAdditionals) +} + +// SkipAllAdditionals skips all Additional Resources. +func (p *Parser) SkipAllAdditionals() error { + for { + if err := p.SkipAdditional(); err == ErrSectionDone { + return nil + } else if err != nil { + return err + } + } +} + +// CNAMEResource parses a single CNAMEResource. +// +// One of the XXXHeader methods must have been called before calling this +// method. +func (p *Parser) CNAMEResource() (CNAMEResource, error) { + if !p.resHeaderValid || p.resHeader.Type != TypeCNAME { + return CNAMEResource{}, ErrNotStarted + } + r, err := unpackCNAMEResource(p.msg, p.off) + if err != nil { + return CNAMEResource{}, err + } + p.off += int(p.resHeader.Length) + p.resHeaderValid = false + p.index++ + return r, nil +} + +// MXResource parses a single MXResource. +// +// One of the XXXHeader methods must have been called before calling this +// method. +func (p *Parser) MXResource() (MXResource, error) { + if !p.resHeaderValid || p.resHeader.Type != TypeMX { + return MXResource{}, ErrNotStarted + } + r, err := unpackMXResource(p.msg, p.off) + if err != nil { + return MXResource{}, err + } + p.off += int(p.resHeader.Length) + p.resHeaderValid = false + p.index++ + return r, nil +} + +// NSResource parses a single NSResource. +// +// One of the XXXHeader methods must have been called before calling this +// method. +func (p *Parser) NSResource() (NSResource, error) { + if !p.resHeaderValid || p.resHeader.Type != TypeNS { + return NSResource{}, ErrNotStarted + } + r, err := unpackNSResource(p.msg, p.off) + if err != nil { + return NSResource{}, err + } + p.off += int(p.resHeader.Length) + p.resHeaderValid = false + p.index++ + return r, nil +} + +// PTRResource parses a single PTRResource. +// +// One of the XXXHeader methods must have been called before calling this +// method. +func (p *Parser) PTRResource() (PTRResource, error) { + if !p.resHeaderValid || p.resHeader.Type != TypePTR { + return PTRResource{}, ErrNotStarted + } + r, err := unpackPTRResource(p.msg, p.off) + if err != nil { + return PTRResource{}, err + } + p.off += int(p.resHeader.Length) + p.resHeaderValid = false + p.index++ + return r, nil +} + +// SOAResource parses a single SOAResource. +// +// One of the XXXHeader methods must have been called before calling this +// method. +func (p *Parser) SOAResource() (SOAResource, error) { + if !p.resHeaderValid || p.resHeader.Type != TypeSOA { + return SOAResource{}, ErrNotStarted + } + r, err := unpackSOAResource(p.msg, p.off) + if err != nil { + return SOAResource{}, err + } + p.off += int(p.resHeader.Length) + p.resHeaderValid = false + p.index++ + return r, nil +} + +// TXTResource parses a single TXTResource. +// +// One of the XXXHeader methods must have been called before calling this +// method. +func (p *Parser) TXTResource() (TXTResource, error) { + if !p.resHeaderValid || p.resHeader.Type != TypeTXT { + return TXTResource{}, ErrNotStarted + } + r, err := unpackTXTResource(p.msg, p.off, p.resHeader.Length) + if err != nil { + return TXTResource{}, err + } + p.off += int(p.resHeader.Length) + p.resHeaderValid = false + p.index++ + return r, nil +} + +// SRVResource parses a single SRVResource. +// +// One of the XXXHeader methods must have been called before calling this +// method. +func (p *Parser) SRVResource() (SRVResource, error) { + if !p.resHeaderValid || p.resHeader.Type != TypeSRV { + return SRVResource{}, ErrNotStarted + } + r, err := unpackSRVResource(p.msg, p.off) + if err != nil { + return SRVResource{}, err + } + p.off += int(p.resHeader.Length) + p.resHeaderValid = false + p.index++ + return r, nil +} + +// AResource parses a single AResource. +// +// One of the XXXHeader methods must have been called before calling this +// method. +func (p *Parser) AResource() (AResource, error) { + if !p.resHeaderValid || p.resHeader.Type != TypeA { + return AResource{}, ErrNotStarted + } + r, err := unpackAResource(p.msg, p.off) + if err != nil { + return AResource{}, err + } + p.off += int(p.resHeader.Length) + p.resHeaderValid = false + p.index++ + return r, nil +} + +// AAAAResource parses a single AAAAResource. +// +// One of the XXXHeader methods must have been called before calling this +// method. +func (p *Parser) AAAAResource() (AAAAResource, error) { + if !p.resHeaderValid || p.resHeader.Type != TypeAAAA { + return AAAAResource{}, ErrNotStarted + } + r, err := unpackAAAAResource(p.msg, p.off) + if err != nil { + return AAAAResource{}, err + } + p.off += int(p.resHeader.Length) + p.resHeaderValid = false + p.index++ + return r, nil +} + +// Unpack parses a full Message. +func (m *Message) Unpack(msg []byte) error { + var p Parser + var err error + if m.Header, err = p.Start(msg); err != nil { + return err + } + if m.Questions, err = p.AllQuestions(); err != nil { + return err + } + if m.Answers, err = p.AllAnswers(); err != nil { + return err + } + if m.Authorities, err = p.AllAuthorities(); err != nil { + return err + } + if m.Additionals, err = p.AllAdditionals(); err != nil { + return err + } + return nil +} + +// Pack packs a full Message. +func (m *Message) Pack() ([]byte, error) { + return m.AppendPack(make([]byte, 0, packStartingCap)) +} + +// AppendPack is like Pack but appends the full Message to b and returns the +// extended buffer. +func (m *Message) AppendPack(b []byte) ([]byte, error) { + // Validate the lengths. It is very unlikely that anyone will try to + // pack more than 65535 of any particular type, but it is possible and + // we should fail gracefully. + if len(m.Questions) > int(^uint16(0)) { + return nil, errTooManyQuestions + } + if len(m.Answers) > int(^uint16(0)) { + return nil, errTooManyAnswers + } + if len(m.Authorities) > int(^uint16(0)) { + return nil, errTooManyAuthorities + } + if len(m.Additionals) > int(^uint16(0)) { + return nil, errTooManyAdditionals + } + + var h header + h.id, h.bits = m.Header.pack() + + h.questions = uint16(len(m.Questions)) + h.answers = uint16(len(m.Answers)) + h.authorities = uint16(len(m.Authorities)) + h.additionals = uint16(len(m.Additionals)) + + compressionOff := len(b) + msg := h.pack(b) + + // RFC 1035 allows (but does not require) compression for packing. RFC + // 1035 requires unpacking implementations to support compression, so + // unconditionally enabling it is fine. + // + // DNS lookups are typically done over UDP, and RFC 1035 states that UDP + // DNS messages can be a maximum of 512 bytes long. Without compression, + // many DNS response messages are over this limit, so enabling + // compression will help ensure compliance. + compression := map[string]int{} + + for i := range m.Questions { + var err error + if msg, err = m.Questions[i].pack(msg, compression, compressionOff); err != nil { + return nil, &nestedError{"packing Question", err} + } + } + for i := range m.Answers { + var err error + if msg, err = m.Answers[i].pack(msg, compression, compressionOff); err != nil { + return nil, &nestedError{"packing Answer", err} + } + } + for i := range m.Authorities { + var err error + if msg, err = m.Authorities[i].pack(msg, compression, compressionOff); err != nil { + return nil, &nestedError{"packing Authority", err} + } + } + for i := range m.Additionals { + var err error + if msg, err = m.Additionals[i].pack(msg, compression, compressionOff); err != nil { + return nil, &nestedError{"packing Additional", err} + } + } + + return msg, nil +} + +// A Builder allows incrementally packing a DNS message. +// +// Example usage: +// buf := make([]byte, 2, 514) +// b := NewBuilder(buf, Header{...}) +// b.EnableCompression() +// // Optionally start a section and add things to that section. +// // Repeat adding sections as necessary. +// buf, err := b.Finish() +// // If err is nil, buf[2:] will contain the built bytes. +type Builder struct { + // msg is the storage for the message being built. + msg []byte + + // section keeps track of the current section being built. + section section + + // header keeps track of what should go in the header when Finish is + // called. + header header + + // start is the starting index of the bytes allocated in msg for header. + start int + + // compression is a mapping from name suffixes to their starting index + // in msg. + compression map[string]int +} + +// NewBuilder creates a new builder with compression disabled. +// +// Note: Most users will want to immediately enable compression with the +// EnableCompression method. See that method's comment for why you may or may +// not want to enable compression. +// +// The DNS message is appended to the provided initial buffer buf (which may be +// nil) as it is built. The final message is returned by the (*Builder).Finish +// method, which may return the same underlying array if there was sufficient +// capacity in the slice. +func NewBuilder(buf []byte, h Header) Builder { + if buf == nil { + buf = make([]byte, 0, packStartingCap) + } + b := Builder{msg: buf, start: len(buf)} + b.header.id, b.header.bits = h.pack() + var hb [headerLen]byte + b.msg = append(b.msg, hb[:]...) + b.section = sectionHeader + return b +} + +// EnableCompression enables compression in the Builder. +// +// Leaving compression disabled avoids compression related allocations, but can +// result in larger message sizes. Be careful with this mode as it can cause +// messages to exceed the UDP size limit. +// +// According to RFC 1035, section 4.1.4, the use of compression is optional, but +// all implementations must accept both compressed and uncompressed DNS +// messages. +// +// Compression should be enabled before any sections are added for best results. +func (b *Builder) EnableCompression() { + b.compression = map[string]int{} +} + +func (b *Builder) startCheck(s section) error { + if b.section <= sectionNotStarted { + return ErrNotStarted + } + if b.section > s { + return ErrSectionDone + } + return nil +} + +// StartQuestions prepares the builder for packing Questions. +func (b *Builder) StartQuestions() error { + if err := b.startCheck(sectionQuestions); err != nil { + return err + } + b.section = sectionQuestions + return nil +} + +// StartAnswers prepares the builder for packing Answers. +func (b *Builder) StartAnswers() error { + if err := b.startCheck(sectionAnswers); err != nil { + return err + } + b.section = sectionAnswers + return nil +} + +// StartAuthorities prepares the builder for packing Authorities. +func (b *Builder) StartAuthorities() error { + if err := b.startCheck(sectionAuthorities); err != nil { + return err + } + b.section = sectionAuthorities + return nil +} + +// StartAdditionals prepares the builder for packing Additionals. +func (b *Builder) StartAdditionals() error { + if err := b.startCheck(sectionAdditionals); err != nil { + return err + } + b.section = sectionAdditionals + return nil +} + +func (b *Builder) incrementSectionCount() error { + var count *uint16 + var err error + switch b.section { + case sectionQuestions: + count = &b.header.questions + err = errTooManyQuestions + case sectionAnswers: + count = &b.header.answers + err = errTooManyAnswers + case sectionAuthorities: + count = &b.header.authorities + err = errTooManyAuthorities + case sectionAdditionals: + count = &b.header.additionals + err = errTooManyAdditionals + } + if *count == ^uint16(0) { + return err + } + *count++ + return nil +} + +// Question adds a single Question. +func (b *Builder) Question(q Question) error { + if b.section < sectionQuestions { + return ErrNotStarted + } + if b.section > sectionQuestions { + return ErrSectionDone + } + msg, err := q.pack(b.msg, b.compression, b.start) + if err != nil { + return err + } + if err := b.incrementSectionCount(); err != nil { + return err + } + b.msg = msg + return nil +} + +func (b *Builder) checkResourceSection() error { + if b.section < sectionAnswers { + return ErrNotStarted + } + if b.section > sectionAdditionals { + return ErrSectionDone + } + return nil +} + +// CNAMEResource adds a single CNAMEResource. +func (b *Builder) CNAMEResource(h ResourceHeader, r CNAMEResource) error { + if err := b.checkResourceSection(); err != nil { + return err + } + h.Type = r.realType() + msg, length, err := h.pack(b.msg, b.compression, b.start) + if err != nil { + return &nestedError{"ResourceHeader", err} + } + preLen := len(msg) + if msg, err = r.pack(msg, b.compression, b.start); err != nil { + return &nestedError{"CNAMEResource body", err} + } + if err := h.fixLen(msg, length, preLen); err != nil { + return err + } + if err := b.incrementSectionCount(); err != nil { + return err + } + b.msg = msg + return nil +} + +// MXResource adds a single MXResource. +func (b *Builder) MXResource(h ResourceHeader, r MXResource) error { + if err := b.checkResourceSection(); err != nil { + return err + } + h.Type = r.realType() + msg, length, err := h.pack(b.msg, b.compression, b.start) + if err != nil { + return &nestedError{"ResourceHeader", err} + } + preLen := len(msg) + if msg, err = r.pack(msg, b.compression, b.start); err != nil { + return &nestedError{"MXResource body", err} + } + if err := h.fixLen(msg, length, preLen); err != nil { + return err + } + if err := b.incrementSectionCount(); err != nil { + return err + } + b.msg = msg + return nil +} + +// NSResource adds a single NSResource. +func (b *Builder) NSResource(h ResourceHeader, r NSResource) error { + if err := b.checkResourceSection(); err != nil { + return err + } + h.Type = r.realType() + msg, length, err := h.pack(b.msg, b.compression, b.start) + if err != nil { + return &nestedError{"ResourceHeader", err} + } + preLen := len(msg) + if msg, err = r.pack(msg, b.compression, b.start); err != nil { + return &nestedError{"NSResource body", err} + } + if err := h.fixLen(msg, length, preLen); err != nil { + return err + } + if err := b.incrementSectionCount(); err != nil { + return err + } + b.msg = msg + return nil +} + +// PTRResource adds a single PTRResource. +func (b *Builder) PTRResource(h ResourceHeader, r PTRResource) error { + if err := b.checkResourceSection(); err != nil { + return err + } + h.Type = r.realType() + msg, length, err := h.pack(b.msg, b.compression, b.start) + if err != nil { + return &nestedError{"ResourceHeader", err} + } + preLen := len(msg) + if msg, err = r.pack(msg, b.compression, b.start); err != nil { + return &nestedError{"PTRResource body", err} + } + if err := h.fixLen(msg, length, preLen); err != nil { + return err + } + if err := b.incrementSectionCount(); err != nil { + return err + } + b.msg = msg + return nil +} + +// SOAResource adds a single SOAResource. +func (b *Builder) SOAResource(h ResourceHeader, r SOAResource) error { + if err := b.checkResourceSection(); err != nil { + return err + } + h.Type = r.realType() + msg, length, err := h.pack(b.msg, b.compression, b.start) + if err != nil { + return &nestedError{"ResourceHeader", err} + } + preLen := len(msg) + if msg, err = r.pack(msg, b.compression, b.start); err != nil { + return &nestedError{"SOAResource body", err} + } + if err := h.fixLen(msg, length, preLen); err != nil { + return err + } + if err := b.incrementSectionCount(); err != nil { + return err + } + b.msg = msg + return nil +} + +// TXTResource adds a single TXTResource. +func (b *Builder) TXTResource(h ResourceHeader, r TXTResource) error { + if err := b.checkResourceSection(); err != nil { + return err + } + h.Type = r.realType() + msg, length, err := h.pack(b.msg, b.compression, b.start) + if err != nil { + return &nestedError{"ResourceHeader", err} + } + preLen := len(msg) + if msg, err = r.pack(msg, b.compression, b.start); err != nil { + return &nestedError{"TXTResource body", err} + } + if err := h.fixLen(msg, length, preLen); err != nil { + return err + } + if err := b.incrementSectionCount(); err != nil { + return err + } + b.msg = msg + return nil +} + +// SRVResource adds a single SRVResource. +func (b *Builder) SRVResource(h ResourceHeader, r SRVResource) error { + if err := b.checkResourceSection(); err != nil { + return err + } + h.Type = r.realType() + msg, length, err := h.pack(b.msg, b.compression, b.start) + if err != nil { + return &nestedError{"ResourceHeader", err} + } + preLen := len(msg) + if msg, err = r.pack(msg, b.compression, b.start); err != nil { + return &nestedError{"SRVResource body", err} + } + if err := h.fixLen(msg, length, preLen); err != nil { + return err + } + if err := b.incrementSectionCount(); err != nil { + return err + } + b.msg = msg + return nil +} + +// AResource adds a single AResource. +func (b *Builder) AResource(h ResourceHeader, r AResource) error { + if err := b.checkResourceSection(); err != nil { + return err + } + h.Type = r.realType() + msg, length, err := h.pack(b.msg, b.compression, b.start) + if err != nil { + return &nestedError{"ResourceHeader", err} + } + preLen := len(msg) + if msg, err = r.pack(msg, b.compression, b.start); err != nil { + return &nestedError{"AResource body", err} + } + if err := h.fixLen(msg, length, preLen); err != nil { + return err + } + if err := b.incrementSectionCount(); err != nil { + return err + } + b.msg = msg + return nil +} + +// AAAAResource adds a single AAAAResource. +func (b *Builder) AAAAResource(h ResourceHeader, r AAAAResource) error { + if err := b.checkResourceSection(); err != nil { + return err + } + h.Type = r.realType() + msg, length, err := h.pack(b.msg, b.compression, b.start) + if err != nil { + return &nestedError{"ResourceHeader", err} + } + preLen := len(msg) + if msg, err = r.pack(msg, b.compression, b.start); err != nil { + return &nestedError{"AAAAResource body", err} + } + if err := h.fixLen(msg, length, preLen); err != nil { + return err + } + if err := b.incrementSectionCount(); err != nil { + return err + } + b.msg = msg + return nil +} + +// Finish ends message building and generates a binary message. +func (b *Builder) Finish() ([]byte, error) { + if b.section < sectionHeader { + return nil, ErrNotStarted + } + b.section = sectionDone + // Space for the header was allocated in NewBuilder. + b.header.pack(b.msg[b.start:b.start]) + return b.msg, nil +} + +// A ResourceHeader is the header of a DNS resource record. There are +// many types of DNS resource records, but they all share the same header. +type ResourceHeader struct { + // Name is the domain name for which this resource record pertains. + Name Name + + // Type is the type of DNS resource record. + // + // This field will be set automatically during packing. + Type Type + + // Class is the class of network to which this DNS resource record + // pertains. + Class Class + + // TTL is the length of time (measured in seconds) which this resource + // record is valid for (time to live). All Resources in a set should + // have the same TTL (RFC 2181 Section 5.2). + TTL uint32 + + // Length is the length of data in the resource record after the header. + // + // This field will be set automatically during packing. + Length uint16 +} + +// pack appends the wire format of the ResourceHeader to oldMsg. +// +// The bytes where length was packed are returned as a slice so they can be +// updated after the rest of the Resource has been packed. +func (h *ResourceHeader) pack(oldMsg []byte, compression map[string]int, compressionOff int) (msg []byte, length []byte, err error) { + msg = oldMsg + if msg, err = h.Name.pack(msg, compression, compressionOff); err != nil { + return oldMsg, nil, &nestedError{"Name", err} + } + msg = packType(msg, h.Type) + msg = packClass(msg, h.Class) + msg = packUint32(msg, h.TTL) + lenBegin := len(msg) + msg = packUint16(msg, h.Length) + return msg, msg[lenBegin : lenBegin+uint16Len], nil +} + +func (h *ResourceHeader) unpack(msg []byte, off int) (int, error) { + newOff := off + var err error + if newOff, err = h.Name.unpack(msg, newOff); err != nil { + return off, &nestedError{"Name", err} + } + if h.Type, newOff, err = unpackType(msg, newOff); err != nil { + return off, &nestedError{"Type", err} + } + if h.Class, newOff, err = unpackClass(msg, newOff); err != nil { + return off, &nestedError{"Class", err} + } + if h.TTL, newOff, err = unpackUint32(msg, newOff); err != nil { + return off, &nestedError{"TTL", err} + } + if h.Length, newOff, err = unpackUint16(msg, newOff); err != nil { + return off, &nestedError{"Length", err} + } + return newOff, nil +} + +func (h *ResourceHeader) fixLen(msg []byte, length []byte, preLen int) error { + conLen := len(msg) - preLen + if conLen > int(^uint16(0)) { + return errResTooLong + } + + // Fill in the length now that we know how long the content is. + packUint16(length[:0], uint16(conLen)) + h.Length = uint16(conLen) + + return nil +} + +func skipResource(msg []byte, off int) (int, error) { + newOff, err := skipName(msg, off) + if err != nil { + return off, &nestedError{"Name", err} + } + if newOff, err = skipType(msg, newOff); err != nil { + return off, &nestedError{"Type", err} + } + if newOff, err = skipClass(msg, newOff); err != nil { + return off, &nestedError{"Class", err} + } + if newOff, err = skipUint32(msg, newOff); err != nil { + return off, &nestedError{"TTL", err} + } + length, newOff, err := unpackUint16(msg, newOff) + if err != nil { + return off, &nestedError{"Length", err} + } + if newOff += int(length); newOff > len(msg) { + return off, errResourceLen + } + return newOff, nil +} + +// packUint16 appends the wire format of field to msg. +func packUint16(msg []byte, field uint16) []byte { + return append(msg, byte(field>>8), byte(field)) +} + +func unpackUint16(msg []byte, off int) (uint16, int, error) { + if off+uint16Len > len(msg) { + return 0, off, errBaseLen + } + return uint16(msg[off])<<8 | uint16(msg[off+1]), off + uint16Len, nil +} + +func skipUint16(msg []byte, off int) (int, error) { + if off+uint16Len > len(msg) { + return off, errBaseLen + } + return off + uint16Len, nil +} + +// packType appends the wire format of field to msg. +func packType(msg []byte, field Type) []byte { + return packUint16(msg, uint16(field)) +} + +func unpackType(msg []byte, off int) (Type, int, error) { + t, o, err := unpackUint16(msg, off) + return Type(t), o, err +} + +func skipType(msg []byte, off int) (int, error) { + return skipUint16(msg, off) +} + +// packClass appends the wire format of field to msg. +func packClass(msg []byte, field Class) []byte { + return packUint16(msg, uint16(field)) +} + +func unpackClass(msg []byte, off int) (Class, int, error) { + c, o, err := unpackUint16(msg, off) + return Class(c), o, err +} + +func skipClass(msg []byte, off int) (int, error) { + return skipUint16(msg, off) +} + +// packUint32 appends the wire format of field to msg. +func packUint32(msg []byte, field uint32) []byte { + return append( + msg, + byte(field>>24), + byte(field>>16), + byte(field>>8), + byte(field), + ) +} + +func unpackUint32(msg []byte, off int) (uint32, int, error) { + if off+uint32Len > len(msg) { + return 0, off, errBaseLen + } + v := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]) + return v, off + uint32Len, nil +} + +func skipUint32(msg []byte, off int) (int, error) { + if off+uint32Len > len(msg) { + return off, errBaseLen + } + return off + uint32Len, nil +} + +// packText appends the wire format of field to msg. +func packText(msg []byte, field string) ([]byte, error) { + l := len(field) + if l > 255 { + return nil, errStringTooLong + } + msg = append(msg, byte(l)) + msg = append(msg, field...) + + return msg, nil +} + +func unpackText(msg []byte, off int) (string, int, error) { + if off >= len(msg) { + return "", off, errBaseLen + } + beginOff := off + 1 + endOff := beginOff + int(msg[off]) + if endOff > len(msg) { + return "", off, errCalcLen + } + return string(msg[beginOff:endOff]), endOff, nil +} + +func skipText(msg []byte, off int) (int, error) { + if off >= len(msg) { + return off, errBaseLen + } + endOff := off + 1 + int(msg[off]) + if endOff > len(msg) { + return off, errCalcLen + } + return endOff, nil +} + +// packBytes appends the wire format of field to msg. +func packBytes(msg []byte, field []byte) []byte { + return append(msg, field...) +} + +func unpackBytes(msg []byte, off int, field []byte) (int, error) { + newOff := off + len(field) + if newOff > len(msg) { + return off, errBaseLen + } + copy(field, msg[off:newOff]) + return newOff, nil +} + +func skipBytes(msg []byte, off int, field []byte) (int, error) { + newOff := off + len(field) + if newOff > len(msg) { + return off, errBaseLen + } + return newOff, nil +} + +const nameLen = 255 + +// A Name is a non-encoded domain name. It is used instead of strings to avoid +// allocations. +type Name struct { + Data [nameLen]byte + Length uint8 +} + +// NewName creates a new Name from a string. +func NewName(name string) (Name, error) { + if len([]byte(name)) > nameLen { + return Name{}, errCalcLen + } + n := Name{Length: uint8(len(name))} + copy(n.Data[:], []byte(name)) + return n, nil +} + +func (n Name) String() string { + return string(n.Data[:n.Length]) +} + +// pack appends the wire format of the Name to msg. +// +// Domain names are a sequence of counted strings split at the dots. They end +// with a zero-length string. Compression can be used to reuse domain suffixes. +// +// The compression map will be updated with new domain suffixes. If compression +// is nil, compression will not be used. +func (n *Name) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { + oldMsg := msg + + // Add a trailing dot to canonicalize name. + if n.Length == 0 || n.Data[n.Length-1] != '.' { + return oldMsg, errNonCanonicalName + } + + // Allow root domain. + if n.Data[0] == '.' && n.Length == 1 { + return append(msg, 0), nil + } + + // Emit sequence of counted strings, chopping at dots. + for i, begin := 0, 0; i < int(n.Length); i++ { + // Check for the end of the segment. + if n.Data[i] == '.' { + // The two most significant bits have special meaning. + // It isn't allowed for segments to be long enough to + // need them. + if i-begin >= 1<<6 { + return oldMsg, errSegTooLong + } + + // Segments must have a non-zero length. + if i-begin == 0 { + return oldMsg, errZeroSegLen + } + + msg = append(msg, byte(i-begin)) + + for j := begin; j < i; j++ { + msg = append(msg, n.Data[j]) + } + + begin = i + 1 + continue + } + + // We can only compress domain suffixes starting with a new + // segment. A pointer is two bytes with the two most significant + // bits set to 1 to indicate that it is a pointer. + if (i == 0 || n.Data[i-1] == '.') && compression != nil { + if ptr, ok := compression[string(n.Data[i:])]; ok { + // Hit. Emit a pointer instead of the rest of + // the domain. + return append(msg, byte(ptr>>8|0xC0), byte(ptr)), nil + } + + // Miss. Add the suffix to the compression table if the + // offset can be stored in the available 14 bytes. + if len(msg) <= int(^uint16(0)>>2) { + compression[string(n.Data[i:])] = len(msg) - compressionOff + } + } + } + return append(msg, 0), nil +} + +// unpack unpacks a domain name. +func (n *Name) unpack(msg []byte, off int) (int, error) { + return n.unpackCompressed(msg, off, true /* allowCompression */) +} + +func (n *Name) unpackCompressed(msg []byte, off int, allowCompression bool) (int, error) { + // currOff is the current working offset. + currOff := off + + // newOff is the offset where the next record will start. Pointers lead + // to data that belongs to other names and thus doesn't count towards to + // the usage of this name. + newOff := off + + // ptr is the number of pointers followed. + var ptr int + + // Name is a slice representation of the name data. + name := n.Data[:0] + +Loop: + for { + if currOff >= len(msg) { + return off, errBaseLen + } + c := int(msg[currOff]) + currOff++ + switch c & 0xC0 { + case 0x00: // String segment + if c == 0x00 { + // A zero length signals the end of the name. + break Loop + } + endOff := currOff + c + if endOff > len(msg) { + return off, errCalcLen + } + name = append(name, msg[currOff:endOff]...) + name = append(name, '.') + currOff = endOff + case 0xC0: // Pointer + if !allowCompression { + return off, errCompressedSRV + } + if currOff >= len(msg) { + return off, errInvalidPtr + } + c1 := msg[currOff] + currOff++ + if ptr == 0 { + newOff = currOff + } + // Don't follow too many pointers, maybe there's a loop. + if ptr++; ptr > 10 { + return off, errTooManyPtr + } + currOff = (c^0xC0)<<8 | int(c1) + default: + // Prefixes 0x80 and 0x40 are reserved. + return off, errReserved + } + } + if len(name) == 0 { + name = append(name, '.') + } + if len(name) > len(n.Data) { + return off, errCalcLen + } + n.Length = uint8(len(name)) + if ptr == 0 { + newOff = currOff + } + return newOff, nil +} + +func skipName(msg []byte, off int) (int, error) { + // newOff is the offset where the next record will start. Pointers lead + // to data that belongs to other names and thus doesn't count towards to + // the usage of this name. + newOff := off + +Loop: + for { + if newOff >= len(msg) { + return off, errBaseLen + } + c := int(msg[newOff]) + newOff++ + switch c & 0xC0 { + case 0x00: + if c == 0x00 { + // A zero length signals the end of the name. + break Loop + } + // literal string + newOff += c + if newOff > len(msg) { + return off, errCalcLen + } + case 0xC0: + // Pointer to somewhere else in msg. + + // Pointers are two bytes. + newOff++ + + // Don't follow the pointer as the data here has ended. + break Loop + default: + // Prefixes 0x80 and 0x40 are reserved. + return off, errReserved + } + } + + return newOff, nil +} + +// A Question is a DNS query. +type Question struct { + Name Name + Type Type + Class Class +} + +// pack appends the wire format of the Question to msg. +func (q *Question) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { + msg, err := q.Name.pack(msg, compression, compressionOff) + if err != nil { + return msg, &nestedError{"Name", err} + } + msg = packType(msg, q.Type) + return packClass(msg, q.Class), nil +} + +func unpackResourceBody(msg []byte, off int, hdr ResourceHeader) (ResourceBody, int, error) { + var ( + r ResourceBody + err error + name string + ) + switch hdr.Type { + case TypeA: + var rb AResource + rb, err = unpackAResource(msg, off) + r = &rb + name = "A" + case TypeNS: + var rb NSResource + rb, err = unpackNSResource(msg, off) + r = &rb + name = "NS" + case TypeCNAME: + var rb CNAMEResource + rb, err = unpackCNAMEResource(msg, off) + r = &rb + name = "CNAME" + case TypeSOA: + var rb SOAResource + rb, err = unpackSOAResource(msg, off) + r = &rb + name = "SOA" + case TypePTR: + var rb PTRResource + rb, err = unpackPTRResource(msg, off) + r = &rb + name = "PTR" + case TypeMX: + var rb MXResource + rb, err = unpackMXResource(msg, off) + r = &rb + name = "MX" + case TypeTXT: + var rb TXTResource + rb, err = unpackTXTResource(msg, off, hdr.Length) + r = &rb + name = "TXT" + case TypeAAAA: + var rb AAAAResource + rb, err = unpackAAAAResource(msg, off) + r = &rb + name = "AAAA" + case TypeSRV: + var rb SRVResource + rb, err = unpackSRVResource(msg, off) + r = &rb + name = "SRV" + } + if err != nil { + return nil, off, &nestedError{name + " record", err} + } + if r == nil { + return nil, off, errors.New("invalid resource type: " + string(hdr.Type+'0')) + } + return r, off + int(hdr.Length), nil +} + +// A CNAMEResource is a CNAME Resource record. +type CNAMEResource struct { + CNAME Name +} + +func (r *CNAMEResource) realType() Type { + return TypeCNAME +} + +// pack appends the wire format of the CNAMEResource to msg. +func (r *CNAMEResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { + return r.CNAME.pack(msg, compression, compressionOff) +} + +func unpackCNAMEResource(msg []byte, off int) (CNAMEResource, error) { + var cname Name + if _, err := cname.unpack(msg, off); err != nil { + return CNAMEResource{}, err + } + return CNAMEResource{cname}, nil +} + +// An MXResource is an MX Resource record. +type MXResource struct { + Pref uint16 + MX Name +} + +func (r *MXResource) realType() Type { + return TypeMX +} + +// pack appends the wire format of the MXResource to msg. +func (r *MXResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { + oldMsg := msg + msg = packUint16(msg, r.Pref) + msg, err := r.MX.pack(msg, compression, compressionOff) + if err != nil { + return oldMsg, &nestedError{"MXResource.MX", err} + } + return msg, nil +} + +func unpackMXResource(msg []byte, off int) (MXResource, error) { + pref, off, err := unpackUint16(msg, off) + if err != nil { + return MXResource{}, &nestedError{"Pref", err} + } + var mx Name + if _, err := mx.unpack(msg, off); err != nil { + return MXResource{}, &nestedError{"MX", err} + } + return MXResource{pref, mx}, nil +} + +// An NSResource is an NS Resource record. +type NSResource struct { + NS Name +} + +func (r *NSResource) realType() Type { + return TypeNS +} + +// pack appends the wire format of the NSResource to msg. +func (r *NSResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { + return r.NS.pack(msg, compression, compressionOff) +} + +func unpackNSResource(msg []byte, off int) (NSResource, error) { + var ns Name + if _, err := ns.unpack(msg, off); err != nil { + return NSResource{}, err + } + return NSResource{ns}, nil +} + +// A PTRResource is a PTR Resource record. +type PTRResource struct { + PTR Name +} + +func (r *PTRResource) realType() Type { + return TypePTR +} + +// pack appends the wire format of the PTRResource to msg. +func (r *PTRResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { + return r.PTR.pack(msg, compression, compressionOff) +} + +func unpackPTRResource(msg []byte, off int) (PTRResource, error) { + var ptr Name + if _, err := ptr.unpack(msg, off); err != nil { + return PTRResource{}, err + } + return PTRResource{ptr}, nil +} + +// An SOAResource is an SOA Resource record. +type SOAResource struct { + NS Name + MBox Name + Serial uint32 + Refresh uint32 + Retry uint32 + Expire uint32 + + // MinTTL the is the default TTL of Resources records which did not + // contain a TTL value and the TTL of negative responses. (RFC 2308 + // Section 4) + MinTTL uint32 +} + +func (r *SOAResource) realType() Type { + return TypeSOA +} + +// pack appends the wire format of the SOAResource to msg. +func (r *SOAResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { + oldMsg := msg + msg, err := r.NS.pack(msg, compression, compressionOff) + if err != nil { + return oldMsg, &nestedError{"SOAResource.NS", err} + } + msg, err = r.MBox.pack(msg, compression, compressionOff) + if err != nil { + return oldMsg, &nestedError{"SOAResource.MBox", err} + } + msg = packUint32(msg, r.Serial) + msg = packUint32(msg, r.Refresh) + msg = packUint32(msg, r.Retry) + msg = packUint32(msg, r.Expire) + return packUint32(msg, r.MinTTL), nil +} + +func unpackSOAResource(msg []byte, off int) (SOAResource, error) { + var ns Name + off, err := ns.unpack(msg, off) + if err != nil { + return SOAResource{}, &nestedError{"NS", err} + } + var mbox Name + if off, err = mbox.unpack(msg, off); err != nil { + return SOAResource{}, &nestedError{"MBox", err} + } + serial, off, err := unpackUint32(msg, off) + if err != nil { + return SOAResource{}, &nestedError{"Serial", err} + } + refresh, off, err := unpackUint32(msg, off) + if err != nil { + return SOAResource{}, &nestedError{"Refresh", err} + } + retry, off, err := unpackUint32(msg, off) + if err != nil { + return SOAResource{}, &nestedError{"Retry", err} + } + expire, off, err := unpackUint32(msg, off) + if err != nil { + return SOAResource{}, &nestedError{"Expire", err} + } + minTTL, _, err := unpackUint32(msg, off) + if err != nil { + return SOAResource{}, &nestedError{"MinTTL", err} + } + return SOAResource{ns, mbox, serial, refresh, retry, expire, minTTL}, nil +} + +// A TXTResource is a TXT Resource record. +type TXTResource struct { + TXT []string +} + +func (r *TXTResource) realType() Type { + return TypeTXT +} + +// pack appends the wire format of the TXTResource to msg. +func (r *TXTResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { + oldMsg := msg + for _, s := range r.TXT { + var err error + msg, err = packText(msg, s) + if err != nil { + return oldMsg, err + } + } + return msg, nil +} + +func unpackTXTResource(msg []byte, off int, length uint16) (TXTResource, error) { + txts := make([]string, 0, 1) + for n := uint16(0); n < length; { + var t string + var err error + if t, off, err = unpackText(msg, off); err != nil { + return TXTResource{}, &nestedError{"text", err} + } + // Check if we got too many bytes. + if length-n < uint16(len(t))+1 { + return TXTResource{}, errCalcLen + } + n += uint16(len(t)) + 1 + txts = append(txts, t) + } + return TXTResource{txts}, nil +} + +// An SRVResource is an SRV Resource record. +type SRVResource struct { + Priority uint16 + Weight uint16 + Port uint16 + Target Name // Not compressed as per RFC 2782. +} + +func (r *SRVResource) realType() Type { + return TypeSRV +} + +// pack appends the wire format of the SRVResource to msg. +func (r *SRVResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { + oldMsg := msg + msg = packUint16(msg, r.Priority) + msg = packUint16(msg, r.Weight) + msg = packUint16(msg, r.Port) + msg, err := r.Target.pack(msg, nil, compressionOff) + if err != nil { + return oldMsg, &nestedError{"SRVResource.Target", err} + } + return msg, nil +} + +func unpackSRVResource(msg []byte, off int) (SRVResource, error) { + priority, off, err := unpackUint16(msg, off) + if err != nil { + return SRVResource{}, &nestedError{"Priority", err} + } + weight, off, err := unpackUint16(msg, off) + if err != nil { + return SRVResource{}, &nestedError{"Weight", err} + } + port, off, err := unpackUint16(msg, off) + if err != nil { + return SRVResource{}, &nestedError{"Port", err} + } + var target Name + if _, err := target.unpackCompressed(msg, off, false /* allowCompression */); err != nil { + return SRVResource{}, &nestedError{"Target", err} + } + return SRVResource{priority, weight, port, target}, nil +} + +// An AResource is an A Resource record. +type AResource struct { + A [4]byte +} + +func (r *AResource) realType() Type { + return TypeA +} + +// pack appends the wire format of the AResource to msg. +func (r *AResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { + return packBytes(msg, r.A[:]), nil +} + +func unpackAResource(msg []byte, off int) (AResource, error) { + var a [4]byte + if _, err := unpackBytes(msg, off, a[:]); err != nil { + return AResource{}, err + } + return AResource{a}, nil +} + +// An AAAAResource is an AAAA Resource record. +type AAAAResource struct { + AAAA [16]byte +} + +func (r *AAAAResource) realType() Type { + return TypeAAAA +} + +// pack appends the wire format of the AAAAResource to msg. +func (r *AAAAResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { + return packBytes(msg, r.AAAA[:]), nil +} + +func unpackAAAAResource(msg []byte, off int) (AAAAResource, error) { + var aaaa [16]byte + if _, err := unpackBytes(msg, off, aaaa[:]); err != nil { + return AAAAResource{}, err + } + return AAAAResource{aaaa}, nil +} diff --git a/libgo/go/golang_org/x/net/dns/dnsmessage/message_test.go b/libgo/go/golang_org/x/net/dns/dnsmessage/message_test.go new file mode 100644 index 0000000..052897f --- /dev/null +++ b/libgo/go/golang_org/x/net/dns/dnsmessage/message_test.go @@ -0,0 +1,1137 @@ +// 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 dnsmessage + +import ( + "bytes" + "fmt" + "reflect" + "strings" + "testing" +) + +func mustNewName(name string) Name { + n, err := NewName(name) + if err != nil { + panic(err) + } + return n +} + +func (m *Message) String() string { + s := fmt.Sprintf("Message: %#v\n", &m.Header) + if len(m.Questions) > 0 { + s += "-- Questions\n" + for _, q := range m.Questions { + s += fmt.Sprintf("%#v\n", q) + } + } + if len(m.Answers) > 0 { + s += "-- Answers\n" + for _, a := range m.Answers { + s += fmt.Sprintf("%#v\n", a) + } + } + if len(m.Authorities) > 0 { + s += "-- Authorities\n" + for _, ns := range m.Authorities { + s += fmt.Sprintf("%#v\n", ns) + } + } + if len(m.Additionals) > 0 { + s += "-- Additionals\n" + for _, e := range m.Additionals { + s += fmt.Sprintf("%#v\n", e) + } + } + return s +} + +func TestNameString(t *testing.T) { + want := "foo" + name := mustNewName(want) + if got := fmt.Sprint(name); got != want { + t.Errorf("got fmt.Sprint(%#v) = %s, want = %s", name, got, want) + } +} + +func TestQuestionPackUnpack(t *testing.T) { + want := Question{ + Name: mustNewName("."), + Type: TypeA, + Class: ClassINET, + } + buf, err := want.pack(make([]byte, 1, 50), map[string]int{}, 1) + if err != nil { + t.Fatal("Packing failed:", err) + } + var p Parser + p.msg = buf + p.header.questions = 1 + p.section = sectionQuestions + p.off = 1 + got, err := p.Question() + if err != nil { + t.Fatalf("Unpacking failed: %v\n%s", err, string(buf[1:])) + } + if p.off != len(buf) { + t.Errorf("Unpacked different amount than packed: got n = %d, want = %d", p.off, len(buf)) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("Got = %+v, want = %+v", got, want) + } +} + +func TestName(t *testing.T) { + tests := []string{ + "", + ".", + "google..com", + "google.com", + "google..com.", + "google.com.", + ".google.com.", + "www..google.com.", + "www.google.com.", + } + + for _, test := range tests { + n, err := NewName(test) + if err != nil { + t.Errorf("Creating name for %q: %v", test, err) + continue + } + if ns := n.String(); ns != test { + t.Errorf("Got %#v.String() = %q, want = %q", n, ns, test) + continue + } + } +} + +func TestNamePackUnpack(t *testing.T) { + tests := []struct { + in string + want string + err error + }{ + {"", "", errNonCanonicalName}, + {".", ".", nil}, + {"google..com", "", errNonCanonicalName}, + {"google.com", "", errNonCanonicalName}, + {"google..com.", "", errZeroSegLen}, + {"google.com.", "google.com.", nil}, + {".google.com.", "", errZeroSegLen}, + {"www..google.com.", "", errZeroSegLen}, + {"www.google.com.", "www.google.com.", nil}, + } + + for _, test := range tests { + in := mustNewName(test.in) + want := mustNewName(test.want) + buf, err := in.pack(make([]byte, 0, 30), map[string]int{}, 0) + if err != test.err { + t.Errorf("Packing of %q: got err = %v, want err = %v", test.in, err, test.err) + continue + } + if test.err != nil { + continue + } + var got Name + n, err := got.unpack(buf, 0) + if err != nil { + t.Errorf("Unpacking for %q failed: %v", test.in, err) + continue + } + if n != len(buf) { + t.Errorf( + "Unpacked different amount than packed for %q: got n = %d, want = %d", + test.in, + n, + len(buf), + ) + } + if got != want { + t.Errorf("Unpacking packing of %q: got = %#v, want = %#v", test.in, got, want) + } + } +} + +func TestIncompressibleName(t *testing.T) { + name := mustNewName("example.com.") + compression := map[string]int{} + buf, err := name.pack(make([]byte, 0, 100), compression, 0) + if err != nil { + t.Fatal("First packing failed:", err) + } + buf, err = name.pack(buf, compression, 0) + if err != nil { + t.Fatal("Second packing failed:", err) + } + var n1 Name + off, err := n1.unpackCompressed(buf, 0, false /* allowCompression */) + if err != nil { + t.Fatal("Unpacking incompressible name without pointers failed:", err) + } + var n2 Name + if _, err := n2.unpackCompressed(buf, off, false /* allowCompression */); err != errCompressedSRV { + t.Errorf("Unpacking compressed incompressible name with pointers: got err = %v, want = %v", err, errCompressedSRV) + } +} + +func checkErrorPrefix(err error, prefix string) bool { + e, ok := err.(*nestedError) + return ok && e.s == prefix +} + +func TestHeaderUnpackError(t *testing.T) { + wants := []string{ + "id", + "bits", + "questions", + "answers", + "authorities", + "additionals", + } + var buf []byte + var h header + for _, want := range wants { + n, err := h.unpack(buf, 0) + if n != 0 || !checkErrorPrefix(err, want) { + t.Errorf("got h.unpack([%d]byte, 0) = %d, %v, want = 0, %s", len(buf), n, err, want) + } + buf = append(buf, 0, 0) + } +} + +func TestParserStart(t *testing.T) { + const want = "unpacking header" + var p Parser + for i := 0; i <= 1; i++ { + _, err := p.Start([]byte{}) + if !checkErrorPrefix(err, want) { + t.Errorf("got p.Start(nil) = _, %v, want = _, %s", err, want) + } + } +} + +func TestResourceNotStarted(t *testing.T) { + tests := []struct { + name string + fn func(*Parser) error + }{ + {"CNAMEResource", func(p *Parser) error { _, err := p.CNAMEResource(); return err }}, + {"MXResource", func(p *Parser) error { _, err := p.MXResource(); return err }}, + {"NSResource", func(p *Parser) error { _, err := p.NSResource(); return err }}, + {"PTRResource", func(p *Parser) error { _, err := p.PTRResource(); return err }}, + {"SOAResource", func(p *Parser) error { _, err := p.SOAResource(); return err }}, + {"TXTResource", func(p *Parser) error { _, err := p.TXTResource(); return err }}, + {"SRVResource", func(p *Parser) error { _, err := p.SRVResource(); return err }}, + {"AResource", func(p *Parser) error { _, err := p.AResource(); return err }}, + {"AAAAResource", func(p *Parser) error { _, err := p.AAAAResource(); return err }}, + } + + for _, test := range tests { + if err := test.fn(&Parser{}); err != ErrNotStarted { + t.Errorf("got _, %v = p.%s(), want = _, %v", err, test.name, ErrNotStarted) + } + } +} + +func TestDNSPackUnpack(t *testing.T) { + wants := []Message{ + { + Questions: []Question{ + { + Name: mustNewName("."), + Type: TypeAAAA, + Class: ClassINET, + }, + }, + Answers: []Resource{}, + Authorities: []Resource{}, + Additionals: []Resource{}, + }, + largeTestMsg(), + } + for i, want := range wants { + b, err := want.Pack() + if err != nil { + t.Fatalf("%d: packing failed: %v", i, err) + } + var got Message + err = got.Unpack(b) + if err != nil { + t.Fatalf("%d: unpacking failed: %v", i, err) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("%d: got = %+v, want = %+v", i, &got, &want) + } + } +} + +func TestDNSAppendPackUnpack(t *testing.T) { + wants := []Message{ + { + Questions: []Question{ + { + Name: mustNewName("."), + Type: TypeAAAA, + Class: ClassINET, + }, + }, + Answers: []Resource{}, + Authorities: []Resource{}, + Additionals: []Resource{}, + }, + largeTestMsg(), + } + for i, want := range wants { + b := make([]byte, 2, 514) + b, err := want.AppendPack(b) + if err != nil { + t.Fatalf("%d: packing failed: %v", i, err) + } + b = b[2:] + var got Message + err = got.Unpack(b) + if err != nil { + t.Fatalf("%d: unpacking failed: %v", i, err) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("%d: got = %+v, want = %+v", i, &got, &want) + } + } +} + +func TestSkipAll(t *testing.T) { + msg := largeTestMsg() + buf, err := msg.Pack() + if err != nil { + t.Fatal("Packing large test message:", err) + } + var p Parser + if _, err := p.Start(buf); err != nil { + t.Fatal(err) + } + + tests := []struct { + name string + f func() error + }{ + {"SkipAllQuestions", p.SkipAllQuestions}, + {"SkipAllAnswers", p.SkipAllAnswers}, + {"SkipAllAuthorities", p.SkipAllAuthorities}, + {"SkipAllAdditionals", p.SkipAllAdditionals}, + } + for _, test := range tests { + for i := 1; i <= 3; i++ { + if err := test.f(); err != nil { + t.Errorf("Call #%d to %s(): %v", i, test.name, err) + } + } + } +} + +func TestSkipEach(t *testing.T) { + msg := smallTestMsg() + + buf, err := msg.Pack() + if err != nil { + t.Fatal("Packing test message:", err) + } + var p Parser + if _, err := p.Start(buf); err != nil { + t.Fatal(err) + } + + tests := []struct { + name string + f func() error + }{ + {"SkipQuestion", p.SkipQuestion}, + {"SkipAnswer", p.SkipAnswer}, + {"SkipAuthority", p.SkipAuthority}, + {"SkipAdditional", p.SkipAdditional}, + } + for _, test := range tests { + if err := test.f(); err != nil { + t.Errorf("First call: got %s() = %v, want = %v", test.name, err, nil) + } + if err := test.f(); err != ErrSectionDone { + t.Errorf("Second call: got %s() = %v, want = %v", test.name, err, ErrSectionDone) + } + } +} + +func TestSkipAfterRead(t *testing.T) { + msg := smallTestMsg() + + buf, err := msg.Pack() + if err != nil { + t.Fatal("Packing test message:", err) + } + var p Parser + if _, err := p.Start(buf); err != nil { + t.Fatal(err) + } + + tests := []struct { + name string + skip func() error + read func() error + }{ + {"Question", p.SkipQuestion, func() error { _, err := p.Question(); return err }}, + {"Answer", p.SkipAnswer, func() error { _, err := p.Answer(); return err }}, + {"Authority", p.SkipAuthority, func() error { _, err := p.Authority(); return err }}, + {"Additional", p.SkipAdditional, func() error { _, err := p.Additional(); return err }}, + } + for _, test := range tests { + if err := test.read(); err != nil { + t.Errorf("Got %s() = _, %v, want = _, %v", test.name, err, nil) + } + if err := test.skip(); err != ErrSectionDone { + t.Errorf("Got Skip%s() = %v, want = %v", test.name, err, ErrSectionDone) + } + } +} + +func TestSkipNotStarted(t *testing.T) { + var p Parser + + tests := []struct { + name string + f func() error + }{ + {"SkipAllQuestions", p.SkipAllQuestions}, + {"SkipAllAnswers", p.SkipAllAnswers}, + {"SkipAllAuthorities", p.SkipAllAuthorities}, + {"SkipAllAdditionals", p.SkipAllAdditionals}, + } + for _, test := range tests { + if err := test.f(); err != ErrNotStarted { + t.Errorf("Got %s() = %v, want = %v", test.name, err, ErrNotStarted) + } + } +} + +func TestTooManyRecords(t *testing.T) { + const recs = int(^uint16(0)) + 1 + tests := []struct { + name string + msg Message + want error + }{ + { + "Questions", + Message{ + Questions: make([]Question, recs), + }, + errTooManyQuestions, + }, + { + "Answers", + Message{ + Answers: make([]Resource, recs), + }, + errTooManyAnswers, + }, + { + "Authorities", + Message{ + Authorities: make([]Resource, recs), + }, + errTooManyAuthorities, + }, + { + "Additionals", + Message{ + Additionals: make([]Resource, recs), + }, + errTooManyAdditionals, + }, + } + + for _, test := range tests { + if _, got := test.msg.Pack(); got != test.want { + t.Errorf("Packing %d %s: got = %v, want = %v", recs, test.name, got, test.want) + } + } +} + +func TestVeryLongTxt(t *testing.T) { + want := Resource{ + ResourceHeader{ + Name: mustNewName("foo.bar.example.com."), + Type: TypeTXT, + Class: ClassINET, + }, + &TXTResource{[]string{ + "", + "", + "foo bar", + "", + "www.example.com", + "www.example.com.", + strings.Repeat(".", 255), + }}, + } + buf, err := want.pack(make([]byte, 0, 8000), map[string]int{}, 0) + if err != nil { + t.Fatal("Packing failed:", err) + } + var got Resource + off, err := got.Header.unpack(buf, 0) + if err != nil { + t.Fatal("Unpacking ResourceHeader failed:", err) + } + body, n, err := unpackResourceBody(buf, off, got.Header) + if err != nil { + t.Fatal("Unpacking failed:", err) + } + got.Body = body + if n != len(buf) { + t.Errorf("Unpacked different amount than packed: got n = %d, want = %d", n, len(buf)) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("Got = %#v, want = %#v", got, want) + } +} + +func TestTooLongTxt(t *testing.T) { + rb := TXTResource{[]string{strings.Repeat(".", 256)}} + if _, err := rb.pack(make([]byte, 0, 8000), map[string]int{}, 0); err != errStringTooLong { + t.Errorf("Packing TXTRecord with 256 character string: got err = %v, want = %v", err, errStringTooLong) + } +} + +func TestStartAppends(t *testing.T) { + buf := make([]byte, 2, 514) + wantBuf := []byte{4, 44} + copy(buf, wantBuf) + + b := NewBuilder(buf, Header{}) + b.EnableCompression() + + buf, err := b.Finish() + if err != nil { + t.Fatal("Building failed:", err) + } + if got, want := len(buf), headerLen+2; got != want { + t.Errorf("Got len(buf} = %d, want = %d", got, want) + } + if string(buf[:2]) != string(wantBuf) { + t.Errorf("Original data not preserved, got = %v, want = %v", buf[:2], wantBuf) + } +} + +func TestStartError(t *testing.T) { + tests := []struct { + name string + fn func(*Builder) error + }{ + {"Questions", func(b *Builder) error { return b.StartQuestions() }}, + {"Answers", func(b *Builder) error { return b.StartAnswers() }}, + {"Authorities", func(b *Builder) error { return b.StartAuthorities() }}, + {"Additionals", func(b *Builder) error { return b.StartAdditionals() }}, + } + + envs := []struct { + name string + fn func() *Builder + want error + }{ + {"sectionNotStarted", func() *Builder { return &Builder{section: sectionNotStarted} }, ErrNotStarted}, + {"sectionDone", func() *Builder { return &Builder{section: sectionDone} }, ErrSectionDone}, + } + + for _, env := range envs { + for _, test := range tests { + if got := test.fn(env.fn()); got != env.want { + t.Errorf("got Builder{%s}.Start%s = %v, want = %v", env.name, test.name, got, env.want) + } + } + } +} + +func TestBuilderResourceError(t *testing.T) { + tests := []struct { + name string + fn func(*Builder) error + }{ + {"CNAMEResource", func(b *Builder) error { return b.CNAMEResource(ResourceHeader{}, CNAMEResource{}) }}, + {"MXResource", func(b *Builder) error { return b.MXResource(ResourceHeader{}, MXResource{}) }}, + {"NSResource", func(b *Builder) error { return b.NSResource(ResourceHeader{}, NSResource{}) }}, + {"PTRResource", func(b *Builder) error { return b.PTRResource(ResourceHeader{}, PTRResource{}) }}, + {"SOAResource", func(b *Builder) error { return b.SOAResource(ResourceHeader{}, SOAResource{}) }}, + {"TXTResource", func(b *Builder) error { return b.TXTResource(ResourceHeader{}, TXTResource{}) }}, + {"SRVResource", func(b *Builder) error { return b.SRVResource(ResourceHeader{}, SRVResource{}) }}, + {"AResource", func(b *Builder) error { return b.AResource(ResourceHeader{}, AResource{}) }}, + {"AAAAResource", func(b *Builder) error { return b.AAAAResource(ResourceHeader{}, AAAAResource{}) }}, + } + + envs := []struct { + name string + fn func() *Builder + want error + }{ + {"sectionNotStarted", func() *Builder { return &Builder{section: sectionNotStarted} }, ErrNotStarted}, + {"sectionHeader", func() *Builder { return &Builder{section: sectionHeader} }, ErrNotStarted}, + {"sectionQuestions", func() *Builder { return &Builder{section: sectionQuestions} }, ErrNotStarted}, + {"sectionDone", func() *Builder { return &Builder{section: sectionDone} }, ErrSectionDone}, + } + + for _, env := range envs { + for _, test := range tests { + if got := test.fn(env.fn()); got != env.want { + t.Errorf("got Builder{%s}.%s = %v, want = %v", env.name, test.name, got, env.want) + } + } + } +} + +func TestFinishError(t *testing.T) { + var b Builder + want := ErrNotStarted + if _, got := b.Finish(); got != want { + t.Errorf("got Builder{}.Finish() = %v, want = %v", got, want) + } +} + +func TestBuilder(t *testing.T) { + msg := largeTestMsg() + want, err := msg.Pack() + if err != nil { + t.Fatal("Packing without builder:", err) + } + + b := NewBuilder(nil, msg.Header) + b.EnableCompression() + + if err := b.StartQuestions(); err != nil { + t.Fatal("b.StartQuestions():", err) + } + for _, q := range msg.Questions { + if err := b.Question(q); err != nil { + t.Fatalf("b.Question(%#v): %v", q, err) + } + } + + if err := b.StartAnswers(); err != nil { + t.Fatal("b.StartAnswers():", err) + } + for _, a := range msg.Answers { + switch a.Header.Type { + case TypeA: + if err := b.AResource(a.Header, *a.Body.(*AResource)); err != nil { + t.Fatalf("b.AResource(%#v): %v", a, err) + } + case TypeNS: + if err := b.NSResource(a.Header, *a.Body.(*NSResource)); err != nil { + t.Fatalf("b.NSResource(%#v): %v", a, err) + } + case TypeCNAME: + if err := b.CNAMEResource(a.Header, *a.Body.(*CNAMEResource)); err != nil { + t.Fatalf("b.CNAMEResource(%#v): %v", a, err) + } + case TypeSOA: + if err := b.SOAResource(a.Header, *a.Body.(*SOAResource)); err != nil { + t.Fatalf("b.SOAResource(%#v): %v", a, err) + } + case TypePTR: + if err := b.PTRResource(a.Header, *a.Body.(*PTRResource)); err != nil { + t.Fatalf("b.PTRResource(%#v): %v", a, err) + } + case TypeMX: + if err := b.MXResource(a.Header, *a.Body.(*MXResource)); err != nil { + t.Fatalf("b.MXResource(%#v): %v", a, err) + } + case TypeTXT: + if err := b.TXTResource(a.Header, *a.Body.(*TXTResource)); err != nil { + t.Fatalf("b.TXTResource(%#v): %v", a, err) + } + case TypeAAAA: + if err := b.AAAAResource(a.Header, *a.Body.(*AAAAResource)); err != nil { + t.Fatalf("b.AAAAResource(%#v): %v", a, err) + } + case TypeSRV: + if err := b.SRVResource(a.Header, *a.Body.(*SRVResource)); err != nil { + t.Fatalf("b.SRVResource(%#v): %v", a, err) + } + } + } + + if err := b.StartAuthorities(); err != nil { + t.Fatal("b.StartAuthorities():", err) + } + for _, a := range msg.Authorities { + if err := b.NSResource(a.Header, *a.Body.(*NSResource)); err != nil { + t.Fatalf("b.NSResource(%#v): %v", a, err) + } + } + + if err := b.StartAdditionals(); err != nil { + t.Fatal("b.StartAdditionals():", err) + } + for _, a := range msg.Additionals { + if err := b.TXTResource(a.Header, *a.Body.(*TXTResource)); err != nil { + t.Fatalf("b.TXTResource(%#v): %v", a, err) + } + } + + got, err := b.Finish() + if err != nil { + t.Fatal("b.Finish():", err) + } + if !bytes.Equal(got, want) { + t.Fatalf("Got from Builder: %#v\nwant = %#v", got, want) + } +} + +func TestResourcePack(t *testing.T) { + for _, tt := range []struct { + m Message + err error + }{ + { + Message{ + Questions: []Question{ + { + Name: mustNewName("."), + Type: TypeAAAA, + Class: ClassINET, + }, + }, + Answers: []Resource{{ResourceHeader{}, nil}}, + }, + &nestedError{"packing Answer", errNilResouceBody}, + }, + { + Message{ + Questions: []Question{ + { + Name: mustNewName("."), + Type: TypeAAAA, + Class: ClassINET, + }, + }, + Authorities: []Resource{{ResourceHeader{}, (*NSResource)(nil)}}, + }, + &nestedError{"packing Authority", + &nestedError{"ResourceHeader", + &nestedError{"Name", errNonCanonicalName}, + }, + }, + }, + { + Message{ + Questions: []Question{ + { + Name: mustNewName("."), + Type: TypeA, + Class: ClassINET, + }, + }, + Additionals: []Resource{{ResourceHeader{}, nil}}, + }, + &nestedError{"packing Additional", errNilResouceBody}, + }, + } { + _, err := tt.m.Pack() + if !reflect.DeepEqual(err, tt.err) { + t.Errorf("got %v for %v; want %v", err, tt.m, tt.err) + } + } +} + +func benchmarkParsingSetup() ([]byte, error) { + name := mustNewName("foo.bar.example.com.") + msg := Message{ + Header: Header{Response: true, Authoritative: true}, + Questions: []Question{ + { + Name: name, + Type: TypeA, + Class: ClassINET, + }, + }, + Answers: []Resource{ + { + ResourceHeader{ + Name: name, + Class: ClassINET, + }, + &AResource{[4]byte{}}, + }, + { + ResourceHeader{ + Name: name, + Class: ClassINET, + }, + &AAAAResource{[16]byte{}}, + }, + { + ResourceHeader{ + Name: name, + Class: ClassINET, + }, + &CNAMEResource{name}, + }, + { + ResourceHeader{ + Name: name, + Class: ClassINET, + }, + &NSResource{name}, + }, + }, + } + + buf, err := msg.Pack() + if err != nil { + return nil, fmt.Errorf("msg.Pack(): %v", err) + } + return buf, nil +} + +func benchmarkParsing(tb testing.TB, buf []byte) { + var p Parser + if _, err := p.Start(buf); err != nil { + tb.Fatal("p.Start(buf):", err) + } + + for { + _, err := p.Question() + if err == ErrSectionDone { + break + } + if err != nil { + tb.Fatal("p.Question():", err) + } + } + + for { + h, err := p.AnswerHeader() + if err == ErrSectionDone { + break + } + if err != nil { + panic(err) + } + + switch h.Type { + case TypeA: + if _, err := p.AResource(); err != nil { + tb.Fatal("p.AResource():", err) + } + case TypeAAAA: + if _, err := p.AAAAResource(); err != nil { + tb.Fatal("p.AAAAResource():", err) + } + case TypeCNAME: + if _, err := p.CNAMEResource(); err != nil { + tb.Fatal("p.CNAMEResource():", err) + } + case TypeNS: + if _, err := p.NSResource(); err != nil { + tb.Fatal("p.NSResource():", err) + } + default: + tb.Fatalf("unknown type: %T", h) + } + } +} + +func BenchmarkParsing(b *testing.B) { + buf, err := benchmarkParsingSetup() + if err != nil { + b.Fatal(err) + } + + b.ReportAllocs() + for i := 0; i < b.N; i++ { + benchmarkParsing(b, buf) + } +} + +func TestParsingAllocs(t *testing.T) { + buf, err := benchmarkParsingSetup() + if err != nil { + t.Fatal(err) + } + + if allocs := testing.AllocsPerRun(100, func() { benchmarkParsing(t, buf) }); allocs > 0.5 { + t.Errorf("Allocations during parsing: got = %f, want ~0", allocs) + } +} + +func benchmarkBuildingSetup() (Name, []byte) { + name := mustNewName("foo.bar.example.com.") + buf := make([]byte, 0, packStartingCap) + return name, buf +} + +func benchmarkBuilding(tb testing.TB, name Name, buf []byte) { + bld := NewBuilder(buf, Header{Response: true, Authoritative: true}) + + if err := bld.StartQuestions(); err != nil { + tb.Fatal("bld.StartQuestions():", err) + } + q := Question{ + Name: name, + Type: TypeA, + Class: ClassINET, + } + if err := bld.Question(q); err != nil { + tb.Fatalf("bld.Question(%+v): %v", q, err) + } + + hdr := ResourceHeader{ + Name: name, + Class: ClassINET, + } + if err := bld.StartAnswers(); err != nil { + tb.Fatal("bld.StartQuestions():", err) + } + + ar := AResource{[4]byte{}} + if err := bld.AResource(hdr, ar); err != nil { + tb.Fatalf("bld.AResource(%+v, %+v): %v", hdr, ar, err) + } + + aaar := AAAAResource{[16]byte{}} + if err := bld.AAAAResource(hdr, aaar); err != nil { + tb.Fatalf("bld.AAAAResource(%+v, %+v): %v", hdr, aaar, err) + } + + cnr := CNAMEResource{name} + if err := bld.CNAMEResource(hdr, cnr); err != nil { + tb.Fatalf("bld.CNAMEResource(%+v, %+v): %v", hdr, cnr, err) + } + + nsr := NSResource{name} + if err := bld.NSResource(hdr, nsr); err != nil { + tb.Fatalf("bld.NSResource(%+v, %+v): %v", hdr, nsr, err) + } + + if _, err := bld.Finish(); err != nil { + tb.Fatal("bld.Finish():", err) + } +} + +func BenchmarkBuilding(b *testing.B) { + name, buf := benchmarkBuildingSetup() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + benchmarkBuilding(b, name, buf) + } +} + +func TestBuildingAllocs(t *testing.T) { + name, buf := benchmarkBuildingSetup() + if allocs := testing.AllocsPerRun(100, func() { benchmarkBuilding(t, name, buf) }); allocs > 0.5 { + t.Errorf("Allocations during building: got = %f, want ~0", allocs) + } +} + +func smallTestMsg() Message { + name := mustNewName("example.com.") + return Message{ + Header: Header{Response: true, Authoritative: true}, + Questions: []Question{ + { + Name: name, + Type: TypeA, + Class: ClassINET, + }, + }, + Answers: []Resource{ + { + ResourceHeader{ + Name: name, + Type: TypeA, + Class: ClassINET, + }, + &AResource{[4]byte{127, 0, 0, 1}}, + }, + }, + Authorities: []Resource{ + { + ResourceHeader{ + Name: name, + Type: TypeA, + Class: ClassINET, + }, + &AResource{[4]byte{127, 0, 0, 1}}, + }, + }, + Additionals: []Resource{ + { + ResourceHeader{ + Name: name, + Type: TypeA, + Class: ClassINET, + }, + &AResource{[4]byte{127, 0, 0, 1}}, + }, + }, + } +} + +func BenchmarkPack(b *testing.B) { + msg := largeTestMsg() + + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + if _, err := msg.Pack(); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkAppendPack(b *testing.B) { + msg := largeTestMsg() + buf := make([]byte, 0, packStartingCap) + + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + if _, err := msg.AppendPack(buf[:0]); err != nil { + b.Fatal(err) + } + } +} + +func largeTestMsg() Message { + name := mustNewName("foo.bar.example.com.") + return Message{ + Header: Header{Response: true, Authoritative: true}, + Questions: []Question{ + { + Name: name, + Type: TypeA, + Class: ClassINET, + }, + }, + Answers: []Resource{ + { + ResourceHeader{ + Name: name, + Type: TypeA, + Class: ClassINET, + }, + &AResource{[4]byte{127, 0, 0, 1}}, + }, + { + ResourceHeader{ + Name: name, + Type: TypeA, + Class: ClassINET, + }, + &AResource{[4]byte{127, 0, 0, 2}}, + }, + { + ResourceHeader{ + Name: name, + Type: TypeAAAA, + Class: ClassINET, + }, + &AAAAResource{[16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}, + }, + { + ResourceHeader{ + Name: name, + Type: TypeCNAME, + Class: ClassINET, + }, + &CNAMEResource{mustNewName("alias.example.com.")}, + }, + { + ResourceHeader{ + Name: name, + Type: TypeSOA, + Class: ClassINET, + }, + &SOAResource{ + NS: mustNewName("ns1.example.com."), + MBox: mustNewName("mb.example.com."), + Serial: 1, + Refresh: 2, + Retry: 3, + Expire: 4, + MinTTL: 5, + }, + }, + { + ResourceHeader{ + Name: name, + Type: TypePTR, + Class: ClassINET, + }, + &PTRResource{mustNewName("ptr.example.com.")}, + }, + { + ResourceHeader{ + Name: name, + Type: TypeMX, + Class: ClassINET, + }, + &MXResource{ + 7, + mustNewName("mx.example.com."), + }, + }, + { + ResourceHeader{ + Name: name, + Type: TypeSRV, + Class: ClassINET, + }, + &SRVResource{ + 8, + 9, + 11, + mustNewName("srv.example.com."), + }, + }, + }, + Authorities: []Resource{ + { + ResourceHeader{ + Name: name, + Type: TypeNS, + Class: ClassINET, + }, + &NSResource{mustNewName("ns1.example.com.")}, + }, + { + ResourceHeader{ + Name: name, + Type: TypeNS, + Class: ClassINET, + }, + &NSResource{mustNewName("ns2.example.com.")}, + }, + }, + Additionals: []Resource{ + { + ResourceHeader{ + Name: name, + Type: TypeTXT, + Class: ClassINET, + }, + &TXTResource{[]string{"So Long, and Thanks for All the Fish"}}, + }, + { + ResourceHeader{ + Name: name, + Type: TypeTXT, + Class: ClassINET, + }, + &TXTResource{[]string{"Hamster Huey and the Gooey Kablooie"}}, + }, + }, + } +} diff --git a/libgo/go/golang_org/x/net/http/httpguts/guts.go b/libgo/go/golang_org/x/net/http/httpguts/guts.go new file mode 100644 index 0000000..e6cd0ce --- /dev/null +++ b/libgo/go/golang_org/x/net/http/httpguts/guts.go @@ -0,0 +1,50 @@ +// Copyright 2018 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 httpguts provides functions implementing various details +// of the HTTP specification. +// +// This package is shared by the standard library (which vendors it) +// and x/net/http2. It comes with no API stability promise. +package httpguts + +import ( + "net/textproto" + "strings" +) + +// ValidTrailerHeader reports whether name is a valid header field name to appear +// in trailers. +// See RFC 7230, Section 4.1.2 +func ValidTrailerHeader(name string) bool { + name = textproto.CanonicalMIMEHeaderKey(name) + if strings.HasPrefix(name, "If-") || badTrailer[name] { + return false + } + return true +} + +var badTrailer = map[string]bool{ + "Authorization": true, + "Cache-Control": true, + "Connection": true, + "Content-Encoding": true, + "Content-Length": true, + "Content-Range": true, + "Content-Type": true, + "Expect": true, + "Host": true, + "Keep-Alive": true, + "Max-Forwards": true, + "Pragma": true, + "Proxy-Authenticate": true, + "Proxy-Authorization": true, + "Proxy-Connection": true, + "Range": true, + "Realm": true, + "Te": true, + "Trailer": true, + "Transfer-Encoding": true, + "Www-Authenticate": true, +} diff --git a/libgo/go/golang_org/x/net/http/httpguts/httplex.go b/libgo/go/golang_org/x/net/http/httpguts/httplex.go new file mode 100644 index 0000000..9337435 --- /dev/null +++ b/libgo/go/golang_org/x/net/http/httpguts/httplex.go @@ -0,0 +1,346 @@ +// Copyright 2016 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 httpguts + +import ( + "net" + "strings" + "unicode/utf8" + + "golang_org/x/net/idna" +) + +var isTokenTable = [127]bool{ + '!': true, + '#': true, + '$': true, + '%': true, + '&': true, + '\'': true, + '*': true, + '+': true, + '-': true, + '.': true, + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + '5': true, + '6': true, + '7': true, + '8': true, + '9': true, + 'A': true, + 'B': true, + 'C': true, + 'D': true, + 'E': true, + 'F': true, + 'G': true, + 'H': true, + 'I': true, + 'J': true, + 'K': true, + 'L': true, + 'M': true, + 'N': true, + 'O': true, + 'P': true, + 'Q': true, + 'R': true, + 'S': true, + 'T': true, + 'U': true, + 'W': true, + 'V': true, + 'X': true, + 'Y': true, + 'Z': true, + '^': true, + '_': true, + '`': true, + 'a': true, + 'b': true, + 'c': true, + 'd': true, + 'e': true, + 'f': true, + 'g': true, + 'h': true, + 'i': true, + 'j': true, + 'k': true, + 'l': true, + 'm': true, + 'n': true, + 'o': true, + 'p': true, + 'q': true, + 'r': true, + 's': true, + 't': true, + 'u': true, + 'v': true, + 'w': true, + 'x': true, + 'y': true, + 'z': true, + '|': true, + '~': true, +} + +func IsTokenRune(r rune) bool { + i := int(r) + return i < len(isTokenTable) && isTokenTable[i] +} + +func isNotToken(r rune) bool { + return !IsTokenRune(r) +} + +// HeaderValuesContainsToken reports whether any string in values +// contains the provided token, ASCII case-insensitively. +func HeaderValuesContainsToken(values []string, token string) bool { + for _, v := range values { + if headerValueContainsToken(v, token) { + return true + } + } + return false +} + +// isOWS reports whether b is an optional whitespace byte, as defined +// by RFC 7230 section 3.2.3. +func isOWS(b byte) bool { return b == ' ' || b == '\t' } + +// trimOWS returns x with all optional whitespace removes from the +// beginning and end. +func trimOWS(x string) string { + // TODO: consider using strings.Trim(x, " \t") instead, + // if and when it's fast enough. See issue 10292. + // But this ASCII-only code will probably always beat UTF-8 + // aware code. + for len(x) > 0 && isOWS(x[0]) { + x = x[1:] + } + for len(x) > 0 && isOWS(x[len(x)-1]) { + x = x[:len(x)-1] + } + return x +} + +// headerValueContainsToken reports whether v (assumed to be a +// 0#element, in the ABNF extension described in RFC 7230 section 7) +// contains token amongst its comma-separated tokens, ASCII +// case-insensitively. +func headerValueContainsToken(v string, token string) bool { + v = trimOWS(v) + if comma := strings.IndexByte(v, ','); comma != -1 { + return tokenEqual(trimOWS(v[:comma]), token) || headerValueContainsToken(v[comma+1:], token) + } + return tokenEqual(v, token) +} + +// lowerASCII returns the ASCII lowercase version of b. +func lowerASCII(b byte) byte { + if 'A' <= b && b <= 'Z' { + return b + ('a' - 'A') + } + return b +} + +// tokenEqual reports whether t1 and t2 are equal, ASCII case-insensitively. +func tokenEqual(t1, t2 string) bool { + if len(t1) != len(t2) { + return false + } + for i, b := range t1 { + if b >= utf8.RuneSelf { + // No UTF-8 or non-ASCII allowed in tokens. + return false + } + if lowerASCII(byte(b)) != lowerASCII(t2[i]) { + return false + } + } + return true +} + +// isLWS reports whether b is linear white space, according +// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 +// LWS = [CRLF] 1*( SP | HT ) +func isLWS(b byte) bool { return b == ' ' || b == '\t' } + +// isCTL reports whether b is a control byte, according +// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 +// CTL = <any US-ASCII control character +// (octets 0 - 31) and DEL (127)> +func isCTL(b byte) bool { + const del = 0x7f // a CTL + return b < ' ' || b == del +} + +// ValidHeaderFieldName reports whether v is a valid HTTP/1.x header name. +// HTTP/2 imposes the additional restriction that uppercase ASCII +// letters are not allowed. +// +// RFC 7230 says: +// header-field = field-name ":" OWS field-value OWS +// field-name = token +// token = 1*tchar +// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / +// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA +func ValidHeaderFieldName(v string) bool { + if len(v) == 0 { + return false + } + for _, r := range v { + if !IsTokenRune(r) { + return false + } + } + return true +} + +// ValidHostHeader reports whether h is a valid host header. +func ValidHostHeader(h string) bool { + // The latest spec is actually this: + // + // http://tools.ietf.org/html/rfc7230#section-5.4 + // Host = uri-host [ ":" port ] + // + // Where uri-host is: + // http://tools.ietf.org/html/rfc3986#section-3.2.2 + // + // But we're going to be much more lenient for now and just + // search for any byte that's not a valid byte in any of those + // expressions. + for i := 0; i < len(h); i++ { + if !validHostByte[h[i]] { + return false + } + } + return true +} + +// See the validHostHeader comment. +var validHostByte = [256]bool{ + '0': true, '1': true, '2': true, '3': true, '4': true, '5': true, '6': true, '7': true, + '8': true, '9': true, + + 'a': true, 'b': true, 'c': true, 'd': true, 'e': true, 'f': true, 'g': true, 'h': true, + 'i': true, 'j': true, 'k': true, 'l': true, 'm': true, 'n': true, 'o': true, 'p': true, + 'q': true, 'r': true, 's': true, 't': true, 'u': true, 'v': true, 'w': true, 'x': true, + 'y': true, 'z': true, + + 'A': true, 'B': true, 'C': true, 'D': true, 'E': true, 'F': true, 'G': true, 'H': true, + 'I': true, 'J': true, 'K': true, 'L': true, 'M': true, 'N': true, 'O': true, 'P': true, + 'Q': true, 'R': true, 'S': true, 'T': true, 'U': true, 'V': true, 'W': true, 'X': true, + 'Y': true, 'Z': true, + + '!': true, // sub-delims + '$': true, // sub-delims + '%': true, // pct-encoded (and used in IPv6 zones) + '&': true, // sub-delims + '(': true, // sub-delims + ')': true, // sub-delims + '*': true, // sub-delims + '+': true, // sub-delims + ',': true, // sub-delims + '-': true, // unreserved + '.': true, // unreserved + ':': true, // IPv6address + Host expression's optional port + ';': true, // sub-delims + '=': true, // sub-delims + '[': true, + '\'': true, // sub-delims + ']': true, + '_': true, // unreserved + '~': true, // unreserved +} + +// ValidHeaderFieldValue reports whether v is a valid "field-value" according to +// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 : +// +// message-header = field-name ":" [ field-value ] +// field-value = *( field-content | LWS ) +// field-content = <the OCTETs making up the field-value +// and consisting of either *TEXT or combinations +// of token, separators, and quoted-string> +// +// http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 : +// +// TEXT = <any OCTET except CTLs, +// but including LWS> +// LWS = [CRLF] 1*( SP | HT ) +// CTL = <any US-ASCII control character +// (octets 0 - 31) and DEL (127)> +// +// RFC 7230 says: +// field-value = *( field-content / obs-fold ) +// obj-fold = N/A to http2, and deprecated +// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] +// field-vchar = VCHAR / obs-text +// obs-text = %x80-FF +// VCHAR = "any visible [USASCII] character" +// +// http2 further says: "Similarly, HTTP/2 allows header field values +// that are not valid. While most of the values that can be encoded +// will not alter header field parsing, carriage return (CR, ASCII +// 0xd), line feed (LF, ASCII 0xa), and the zero character (NUL, ASCII +// 0x0) might be exploited by an attacker if they are translated +// verbatim. Any request or response that contains a character not +// permitted in a header field value MUST be treated as malformed +// (Section 8.1.2.6). Valid characters are defined by the +// field-content ABNF rule in Section 3.2 of [RFC7230]." +// +// This function does not (yet?) properly handle the rejection of +// strings that begin or end with SP or HTAB. +func ValidHeaderFieldValue(v string) bool { + for i := 0; i < len(v); i++ { + b := v[i] + if isCTL(b) && !isLWS(b) { + return false + } + } + return true +} + +func isASCII(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] >= utf8.RuneSelf { + return false + } + } + return true +} + +// PunycodeHostPort returns the IDNA Punycode version +// of the provided "host" or "host:port" string. +func PunycodeHostPort(v string) (string, error) { + if isASCII(v) { + return v, nil + } + + host, port, err := net.SplitHostPort(v) + if err != nil { + // The input 'v' argument was just a "host" argument, + // without a port. This error should not be returned + // to the caller. + host = v + port = "" + } + host, err = idna.ToASCII(host) + if err != nil { + // Non-UTF-8? Not representable in Punycode, in any + // case. + return "", err + } + if port == "" { + return host, nil + } + return net.JoinHostPort(host, port), nil +} diff --git a/libgo/go/golang_org/x/net/http/httpguts/httplex_test.go b/libgo/go/golang_org/x/net/http/httpguts/httplex_test.go new file mode 100644 index 0000000..a2c57f3 --- /dev/null +++ b/libgo/go/golang_org/x/net/http/httpguts/httplex_test.go @@ -0,0 +1,119 @@ +// 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 httpguts + +import ( + "testing" +) + +func isChar(c rune) bool { return c <= 127 } + +func isCtl(c rune) bool { return c <= 31 || c == 127 } + +func isSeparator(c rune) bool { + switch c { + case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t': + return true + } + return false +} + +func TestIsToken(t *testing.T) { + for i := 0; i <= 130; i++ { + r := rune(i) + expected := isChar(r) && !isCtl(r) && !isSeparator(r) + if IsTokenRune(r) != expected { + t.Errorf("isToken(0x%x) = %v", r, !expected) + } + } +} + +func TestHeaderValuesContainsToken(t *testing.T) { + tests := []struct { + vals []string + token string + want bool + }{ + { + vals: []string{"foo"}, + token: "foo", + want: true, + }, + { + vals: []string{"bar", "foo"}, + token: "foo", + want: true, + }, + { + vals: []string{"foo"}, + token: "FOO", + want: true, + }, + { + vals: []string{"foo"}, + token: "bar", + want: false, + }, + { + vals: []string{" foo "}, + token: "FOO", + want: true, + }, + { + vals: []string{"foo,bar"}, + token: "FOO", + want: true, + }, + { + vals: []string{"bar,foo,bar"}, + token: "FOO", + want: true, + }, + { + vals: []string{"bar , foo"}, + token: "FOO", + want: true, + }, + { + vals: []string{"foo ,bar "}, + token: "FOO", + want: true, + }, + { + vals: []string{"bar, foo ,bar"}, + token: "FOO", + want: true, + }, + { + vals: []string{"bar , foo"}, + token: "FOO", + want: true, + }, + } + for _, tt := range tests { + got := HeaderValuesContainsToken(tt.vals, tt.token) + if got != tt.want { + t.Errorf("headerValuesContainsToken(%q, %q) = %v; want %v", tt.vals, tt.token, got, tt.want) + } + } +} + +func TestPunycodeHostPort(t *testing.T) { + tests := []struct { + in, want string + }{ + {"www.google.com", "www.google.com"}, + {"гофер.рф", "xn--c1ae0ajs.xn--p1ai"}, + {"bücher.de", "xn--bcher-kva.de"}, + {"bücher.de:8080", "xn--bcher-kva.de:8080"}, + {"[1::6]:8080", "[1::6]:8080"}, + } + for _, tt := range tests { + got, err := PunycodeHostPort(tt.in) + if tt.want != got || err != nil { + t.Errorf("PunycodeHostPort(%q) = %q, %v, want %q, nil", tt.in, got, err, tt.want) + } + } +} diff --git a/libgo/go/golang_org/x/net/http/httpproxy/export_test.go b/libgo/go/golang_org/x/net/http/httpproxy/export_test.go new file mode 100644 index 0000000..5d30018 --- /dev/null +++ b/libgo/go/golang_org/x/net/http/httpproxy/export_test.go @@ -0,0 +1,13 @@ +// Copyright 2017 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 httpproxy + +func ExportUseProxy(cfg *Config, host string) bool { + cfg1 := &config{ + Config: *cfg, + } + cfg1.init() + return cfg1.useProxy(host) +} diff --git a/libgo/go/golang_org/x/net/http/httpproxy/proxy.go b/libgo/go/golang_org/x/net/http/httpproxy/proxy.go new file mode 100644 index 0000000..0409f43 --- /dev/null +++ b/libgo/go/golang_org/x/net/http/httpproxy/proxy.go @@ -0,0 +1,370 @@ +// Copyright 2017 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 httpproxy provides support for HTTP proxy determination +// based on environment variables, as provided by net/http's +// ProxyFromEnvironment function. +// +// The API is not subject to the Go 1 compatibility promise and may change at +// any time. +package httpproxy + +import ( + "errors" + "fmt" + "net" + "net/url" + "os" + "strings" + "unicode/utf8" + + "golang_org/x/net/idna" +) + +// Config holds configuration for HTTP proxy settings. See +// FromEnvironment for details. +type Config struct { + // HTTPProxy represents the value of the HTTP_PROXY or + // http_proxy environment variable. It will be used as the proxy + // URL for HTTP requests and HTTPS requests unless overridden by + // HTTPSProxy or NoProxy. + HTTPProxy string + + // HTTPSProxy represents the HTTPS_PROXY or https_proxy + // environment variable. It will be used as the proxy URL for + // HTTPS requests unless overridden by NoProxy. + HTTPSProxy string + + // NoProxy represents the NO_PROXY or no_proxy environment + // variable. It specifies a string that contains comma-separated values + // specifying hosts that should be excluded from proxying. Each value is + // represented by an IP address prefix (1.2.3.4), an IP address prefix in + // CIDR notation (1.2.3.4/8), a domain name, or a special DNS label (*). + // An IP address prefix and domain name can also include a literal port + // number (1.2.3.4:80). + // A domain name matches that name and all subdomains. A domain name with + // a leading "." matches subdomains only. For example "foo.com" matches + // "foo.com" and "bar.foo.com"; ".y.com" matches "x.y.com" but not "y.com". + // A single asterisk (*) indicates that no proxying should be done. + // A best effort is made to parse the string and errors are + // ignored. + NoProxy string + + // CGI holds whether the current process is running + // as a CGI handler (FromEnvironment infers this from the + // presence of a REQUEST_METHOD environment variable). + // When this is set, ProxyForURL will return an error + // when HTTPProxy applies, because a client could be + // setting HTTP_PROXY maliciously. See https://golang.org/s/cgihttpproxy. + CGI bool +} + +// config holds the parsed configuration for HTTP proxy settings. +type config struct { + // Config represents the original configuration as defined above. + Config + + // httpsProxy is the parsed URL of the HTTPSProxy if defined. + httpsProxy *url.URL + + // httpProxy is the parsed URL of the HTTPProxy if defined. + httpProxy *url.URL + + // ipMatchers represent all values in the NoProxy that are IP address + // prefixes or an IP address in CIDR notation. + ipMatchers []matcher + + // domainMatchers represent all values in the NoProxy that are a domain + // name or hostname & domain name + domainMatchers []matcher +} + +// FromEnvironment returns a Config instance populated from the +// environment variables HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the +// lowercase versions thereof). HTTPS_PROXY takes precedence over +// HTTP_PROXY for https requests. +// +// The environment values may be either a complete URL or a +// "host[:port]", in which case the "http" scheme is assumed. An error +// is returned if the value is a different form. +func FromEnvironment() *Config { + return &Config{ + HTTPProxy: getEnvAny("HTTP_PROXY", "http_proxy"), + HTTPSProxy: getEnvAny("HTTPS_PROXY", "https_proxy"), + NoProxy: getEnvAny("NO_PROXY", "no_proxy"), + CGI: os.Getenv("REQUEST_METHOD") != "", + } +} + +func getEnvAny(names ...string) string { + for _, n := range names { + if val := os.Getenv(n); val != "" { + return val + } + } + return "" +} + +// ProxyFunc returns a function that determines the proxy URL to use for +// a given request URL. Changing the contents of cfg will not affect +// proxy functions created earlier. +// +// A nil URL and nil error are returned if no proxy is defined in the +// environment, or a proxy should not be used for the given request, as +// defined by NO_PROXY. +// +// As a special case, if req.URL.Host is "localhost" (with or without a +// port number), then a nil URL and nil error will be returned. +func (cfg *Config) ProxyFunc() func(reqURL *url.URL) (*url.URL, error) { + // Preprocess the Config settings for more efficient evaluation. + cfg1 := &config{ + Config: *cfg, + } + cfg1.init() + return cfg1.proxyForURL +} + +func (cfg *config) proxyForURL(reqURL *url.URL) (*url.URL, error) { + var proxy *url.URL + if reqURL.Scheme == "https" { + proxy = cfg.httpsProxy + } + if proxy == nil { + proxy = cfg.httpProxy + if proxy != nil && cfg.CGI { + return nil, errors.New("refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy") + } + } + if proxy == nil { + return nil, nil + } + if !cfg.useProxy(canonicalAddr(reqURL)) { + return nil, nil + } + + return proxy, nil +} + +func parseProxy(proxy string) (*url.URL, error) { + if proxy == "" { + return nil, nil + } + + proxyURL, err := url.Parse(proxy) + if err != nil || + (proxyURL.Scheme != "http" && + proxyURL.Scheme != "https" && + proxyURL.Scheme != "socks5") { + // proxy was bogus. Try prepending "http://" to it and + // see if that parses correctly. If not, we fall + // through and complain about the original one. + if proxyURL, err := url.Parse("http://" + proxy); err == nil { + return proxyURL, nil + } + } + if err != nil { + return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err) + } + return proxyURL, nil +} + +// useProxy reports whether requests to addr should use a proxy, +// according to the NO_PROXY or no_proxy environment variable. +// addr is always a canonicalAddr with a host and port. +func (cfg *config) useProxy(addr string) bool { + if len(addr) == 0 { + return true + } + host, port, err := net.SplitHostPort(addr) + if err != nil { + return false + } + if host == "localhost" { + return false + } + ip := net.ParseIP(host) + if ip != nil { + if ip.IsLoopback() { + return false + } + } + + addr = strings.ToLower(strings.TrimSpace(host)) + + if ip != nil { + for _, m := range cfg.ipMatchers { + if m.match(addr, port, ip) { + return false + } + } + } + for _, m := range cfg.domainMatchers { + if m.match(addr, port, ip) { + return false + } + } + return true +} + +func (c *config) init() { + if parsed, err := parseProxy(c.HTTPProxy); err == nil { + c.httpProxy = parsed + } + if parsed, err := parseProxy(c.HTTPSProxy); err == nil { + c.httpsProxy = parsed + } + + for _, p := range strings.Split(c.NoProxy, ",") { + p = strings.ToLower(strings.TrimSpace(p)) + if len(p) == 0 { + continue + } + + if p == "*" { + c.ipMatchers = []matcher{allMatch{}} + c.domainMatchers = []matcher{allMatch{}} + return + } + + // IPv4/CIDR, IPv6/CIDR + if _, pnet, err := net.ParseCIDR(p); err == nil { + c.ipMatchers = append(c.ipMatchers, cidrMatch{cidr: pnet}) + continue + } + + // IPv4:port, [IPv6]:port + phost, pport, err := net.SplitHostPort(p) + if err == nil { + if len(phost) == 0 { + // There is no host part, likely the entry is malformed; ignore. + continue + } + if phost[0] == '[' && phost[len(phost)-1] == ']' { + phost = phost[1 : len(phost)-1] + } + } else { + phost = p + } + // IPv4, IPv6 + if pip := net.ParseIP(phost); pip != nil { + c.ipMatchers = append(c.ipMatchers, ipMatch{ip: pip, port: pport}) + continue + } + + if len(phost) == 0 { + // There is no host part, likely the entry is malformed; ignore. + continue + } + + // domain.com or domain.com:80 + // foo.com matches bar.foo.com + // .domain.com or .domain.com:port + // *.domain.com or *.domain.com:port + if strings.HasPrefix(phost, "*.") { + phost = phost[1:] + } + matchHost := false + if phost[0] != '.' { + matchHost = true + phost = "." + phost + } + c.domainMatchers = append(c.domainMatchers, domainMatch{host: phost, port: pport, matchHost: matchHost}) + } +} + +var portMap = map[string]string{ + "http": "80", + "https": "443", + "socks5": "1080", +} + +// canonicalAddr returns url.Host but always with a ":port" suffix +func canonicalAddr(url *url.URL) string { + addr := url.Hostname() + if v, err := idnaASCII(addr); err == nil { + addr = v + } + port := url.Port() + if port == "" { + port = portMap[url.Scheme] + } + return net.JoinHostPort(addr, port) +} + +// Given a string of the form "host", "host:port", or "[ipv6::address]:port", +// return true if the string includes a port. +func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } + +func idnaASCII(v string) (string, error) { + // TODO: Consider removing this check after verifying performance is okay. + // Right now punycode verification, length checks, context checks, and the + // permissible character tests are all omitted. It also prevents the ToASCII + // call from salvaging an invalid IDN, when possible. As a result it may be + // possible to have two IDNs that appear identical to the user where the + // ASCII-only version causes an error downstream whereas the non-ASCII + // version does not. + // Note that for correct ASCII IDNs ToASCII will only do considerably more + // work, but it will not cause an allocation. + if isASCII(v) { + return v, nil + } + return idna.Lookup.ToASCII(v) +} + +func isASCII(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] >= utf8.RuneSelf { + return false + } + } + return true +} + +// matcher represents the matching rule for a given value in the NO_PROXY list +type matcher interface { + // match returns true if the host and optional port or ip and optional port + // are allowed + match(host, port string, ip net.IP) bool +} + +// allMatch matches on all possible inputs +type allMatch struct{} + +func (a allMatch) match(host, port string, ip net.IP) bool { + return true +} + +type cidrMatch struct { + cidr *net.IPNet +} + +func (m cidrMatch) match(host, port string, ip net.IP) bool { + return m.cidr.Contains(ip) +} + +type ipMatch struct { + ip net.IP + port string +} + +func (m ipMatch) match(host, port string, ip net.IP) bool { + if m.ip.Equal(ip) { + return m.port == "" || m.port == port + } + return false +} + +type domainMatch struct { + host string + port string + + matchHost bool +} + +func (m domainMatch) match(host, port string, ip net.IP) bool { + if strings.HasSuffix(host, m.host) || (m.matchHost && host == m.host[1:]) { + return m.port == "" || m.port == port + } + return false +} diff --git a/libgo/go/golang_org/x/net/http/httpproxy/proxy_test.go b/libgo/go/golang_org/x/net/http/httpproxy/proxy_test.go new file mode 100644 index 0000000..8791f64b --- /dev/null +++ b/libgo/go/golang_org/x/net/http/httpproxy/proxy_test.go @@ -0,0 +1,351 @@ +// Copyright 2017 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 httpproxy_test + +import ( + "bytes" + "errors" + "fmt" + "net/url" + "os" + "strings" + "testing" + + "golang_org/x/net/http/httpproxy" +) + +// setHelper calls t.Helper() for Go 1.9+ (see go19_test.go) and does nothing otherwise. +var setHelper = func(t *testing.T) {} + +type proxyForURLTest struct { + cfg httpproxy.Config + req string // URL to fetch; blank means "http://example.com" + want string + wanterr error +} + +func (t proxyForURLTest) String() string { + var buf bytes.Buffer + space := func() { + if buf.Len() > 0 { + buf.WriteByte(' ') + } + } + if t.cfg.HTTPProxy != "" { + fmt.Fprintf(&buf, "http_proxy=%q", t.cfg.HTTPProxy) + } + if t.cfg.HTTPSProxy != "" { + space() + fmt.Fprintf(&buf, "https_proxy=%q", t.cfg.HTTPSProxy) + } + if t.cfg.NoProxy != "" { + space() + fmt.Fprintf(&buf, "no_proxy=%q", t.cfg.NoProxy) + } + req := "http://example.com" + if t.req != "" { + req = t.req + } + space() + fmt.Fprintf(&buf, "req=%q", req) + return strings.TrimSpace(buf.String()) +} + +var proxyForURLTests = []proxyForURLTest{{ + cfg: httpproxy.Config{ + HTTPProxy: "127.0.0.1:8080", + }, + want: "http://127.0.0.1:8080", +}, { + cfg: httpproxy.Config{ + HTTPProxy: "cache.corp.example.com:1234", + }, + want: "http://cache.corp.example.com:1234", +}, { + cfg: httpproxy.Config{ + HTTPProxy: "cache.corp.example.com", + }, + want: "http://cache.corp.example.com", +}, { + cfg: httpproxy.Config{ + HTTPProxy: "https://cache.corp.example.com", + }, + want: "https://cache.corp.example.com", +}, { + cfg: httpproxy.Config{ + HTTPProxy: "http://127.0.0.1:8080", + }, + want: "http://127.0.0.1:8080", +}, { + cfg: httpproxy.Config{ + HTTPProxy: "https://127.0.0.1:8080", + }, + want: "https://127.0.0.1:8080", +}, { + cfg: httpproxy.Config{ + HTTPProxy: "socks5://127.0.0.1", + }, + want: "socks5://127.0.0.1", +}, { + // Don't use secure for http + cfg: httpproxy.Config{ + HTTPProxy: "http.proxy.tld", + HTTPSProxy: "secure.proxy.tld", + }, + req: "http://insecure.tld/", + want: "http://http.proxy.tld", +}, { + // Use secure for https. + cfg: httpproxy.Config{ + HTTPProxy: "http.proxy.tld", + HTTPSProxy: "secure.proxy.tld", + }, + req: "https://secure.tld/", + want: "http://secure.proxy.tld", +}, { + cfg: httpproxy.Config{ + HTTPProxy: "http.proxy.tld", + HTTPSProxy: "https://secure.proxy.tld", + }, + req: "https://secure.tld/", + want: "https://secure.proxy.tld", +}, { + // Issue 16405: don't use HTTP_PROXY in a CGI environment, + // where HTTP_PROXY can be attacker-controlled. + cfg: httpproxy.Config{ + HTTPProxy: "http://10.1.2.3:8080", + CGI: true, + }, + want: "<nil>", + wanterr: errors.New("refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy"), +}, { + // HTTPS proxy is still used even in CGI environment. + // (perhaps dubious but it's the historical behaviour). + cfg: httpproxy.Config{ + HTTPSProxy: "https://secure.proxy.tld", + CGI: true, + }, + req: "https://secure.tld/", + want: "https://secure.proxy.tld", +}, { + want: "<nil>", +}, { + cfg: httpproxy.Config{ + NoProxy: "example.com", + HTTPProxy: "proxy", + }, + req: "http://example.com/", + want: "<nil>", +}, { + cfg: httpproxy.Config{ + NoProxy: ".example.com", + HTTPProxy: "proxy", + }, + req: "http://example.com/", + want: "http://proxy", +}, { + cfg: httpproxy.Config{ + NoProxy: "ample.com", + HTTPProxy: "proxy", + }, + req: "http://example.com/", + want: "http://proxy", +}, { + cfg: httpproxy.Config{ + NoProxy: "example.com", + HTTPProxy: "proxy", + }, + req: "http://foo.example.com/", + want: "<nil>", +}, { + cfg: httpproxy.Config{ + NoProxy: ".foo.com", + HTTPProxy: "proxy", + }, + req: "http://example.com/", + want: "http://proxy", +}} + +func testProxyForURL(t *testing.T, tt proxyForURLTest) { + setHelper(t) + reqURLStr := tt.req + if reqURLStr == "" { + reqURLStr = "http://example.com" + } + reqURL, err := url.Parse(reqURLStr) + if err != nil { + t.Errorf("invalid URL %q", reqURLStr) + return + } + cfg := tt.cfg + proxyForURL := cfg.ProxyFunc() + url, err := proxyForURL(reqURL) + if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e { + t.Errorf("%v: got error = %q, want %q", tt, g, e) + return + } + if got := fmt.Sprintf("%s", url); got != tt.want { + t.Errorf("%v: got URL = %q, want %q", tt, url, tt.want) + } + + // Check that changing the Config doesn't change the results + // of the functuon. + cfg = httpproxy.Config{} + url, err = proxyForURL(reqURL) + if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e { + t.Errorf("(after mutating config) %v: got error = %q, want %q", tt, g, e) + return + } + if got := fmt.Sprintf("%s", url); got != tt.want { + t.Errorf("(after mutating config) %v: got URL = %q, want %q", tt, url, tt.want) + } +} + +func TestProxyForURL(t *testing.T) { + for _, tt := range proxyForURLTests { + testProxyForURL(t, tt) + } +} + +func TestFromEnvironment(t *testing.T) { + os.Setenv("HTTP_PROXY", "httpproxy") + os.Setenv("HTTPS_PROXY", "httpsproxy") + os.Setenv("NO_PROXY", "noproxy") + os.Setenv("REQUEST_METHOD", "") + got := httpproxy.FromEnvironment() + want := httpproxy.Config{ + HTTPProxy: "httpproxy", + HTTPSProxy: "httpsproxy", + NoProxy: "noproxy", + } + if *got != want { + t.Errorf("unexpected proxy config, got %#v want %#v", got, want) + } +} + +func TestFromEnvironmentWithRequestMethod(t *testing.T) { + os.Setenv("HTTP_PROXY", "httpproxy") + os.Setenv("HTTPS_PROXY", "httpsproxy") + os.Setenv("NO_PROXY", "noproxy") + os.Setenv("REQUEST_METHOD", "PUT") + got := httpproxy.FromEnvironment() + want := httpproxy.Config{ + HTTPProxy: "httpproxy", + HTTPSProxy: "httpsproxy", + NoProxy: "noproxy", + CGI: true, + } + if *got != want { + t.Errorf("unexpected proxy config, got %#v want %#v", got, want) + } +} + +func TestFromEnvironmentLowerCase(t *testing.T) { + os.Setenv("http_proxy", "httpproxy") + os.Setenv("https_proxy", "httpsproxy") + os.Setenv("no_proxy", "noproxy") + os.Setenv("REQUEST_METHOD", "") + got := httpproxy.FromEnvironment() + want := httpproxy.Config{ + HTTPProxy: "httpproxy", + HTTPSProxy: "httpsproxy", + NoProxy: "noproxy", + } + if *got != want { + t.Errorf("unexpected proxy config, got %#v want %#v", got, want) + } +} + +var UseProxyTests = []struct { + host string + match bool +}{ + // Never proxy localhost: + {"localhost", false}, + {"127.0.0.1", false}, + {"127.0.0.2", false}, + {"[::1]", false}, + {"[::2]", true}, // not a loopback address + + {"192.168.1.1", false}, // matches exact IPv4 + {"192.168.1.2", true}, // ports do not match + {"192.168.1.3", false}, // matches exact IPv4:port + {"192.168.1.4", true}, // no match + {"10.0.0.2", false}, // matches IPv4/CIDR + {"[2001:db8::52:0:1]", false}, // matches exact IPv6 + {"[2001:db8::52:0:2]", true}, // no match + {"[2001:db8::52:0:3]", false}, // matches exact [IPv6]:port + {"[2002:db8:a::123]", false}, // matches IPv6/CIDR + {"[fe80::424b:c8be:1643:a1b6]", true}, // no match + + {"barbaz.net", true}, // does not match as .barbaz.net + {"www.barbaz.net", false}, // does match as .barbaz.net + {"foobar.com", false}, // does match as foobar.com + {"www.foobar.com", false}, // match because NO_PROXY includes "foobar.com" + {"foofoobar.com", true}, // not match as a part of foobar.com + {"baz.com", true}, // not match as a part of barbaz.com + {"localhost.net", true}, // not match as suffix of address + {"local.localhost", true}, // not match as prefix as address + {"barbarbaz.net", true}, // not match, wrong domain + {"wildcard.io", true}, // does not match as *.wildcard.io + {"nested.wildcard.io", false}, // match as *.wildcard.io + {"awildcard.io", true}, // not a match because of '*' +} + +var noProxy = "foobar.com, .barbaz.net, *.wildcard.io, 192.168.1.1, 192.168.1.2:81, 192.168.1.3:80, 10.0.0.0/30, 2001:db8::52:0:1, [2001:db8::52:0:2]:443, [2001:db8::52:0:3]:80, 2002:db8:a::45/64" + +func TestUseProxy(t *testing.T) { + cfg := &httpproxy.Config{ + NoProxy: noProxy, + } + for _, test := range UseProxyTests { + if httpproxy.ExportUseProxy(cfg, test.host+":80") != test.match { + t.Errorf("useProxy(%v) = %v, want %v", test.host, !test.match, test.match) + } + } +} + +func TestInvalidNoProxy(t *testing.T) { + cfg := &httpproxy.Config{ + NoProxy: ":1", + } + ok := httpproxy.ExportUseProxy(cfg, "example.com:80") // should not panic + if !ok { + t.Errorf("useProxy unexpected return; got false; want true") + } +} + +func TestAllNoProxy(t *testing.T) { + cfg := &httpproxy.Config{ + NoProxy: "*", + } + for _, test := range UseProxyTests { + if httpproxy.ExportUseProxy(cfg, test.host+":80") != false { + t.Errorf("useProxy(%v) = true, want false", test.host) + } + } +} + +func BenchmarkProxyForURL(b *testing.B) { + cfg := &httpproxy.Config{ + HTTPProxy: "http://proxy.example.org", + HTTPSProxy: "https://proxy.example.org", + NoProxy: noProxy, + } + for _, test := range UseProxyTests { + u, err := url.Parse("https://" + test.host + ":80") + if err != nil { + b.Fatalf("parsed failed: %s", test.host) + } + proxyFunc := cfg.ProxyFunc() + b.Run(test.host, func(b *testing.B) { + for n := 0; n < b.N; n++ { + if au, e := proxyFunc(u); e != nil && test.match == (au != nil) { + b.Errorf("useProxy(%v) = %v, want %v", test.host, !test.match, test.match) + } + } + }) + } +} diff --git a/libgo/go/golang_org/x/net/http2/hpack/encode.go b/libgo/go/golang_org/x/net/http2/hpack/encode.go index 54726c2..1565cf2 100644 --- a/libgo/go/golang_org/x/net/http2/hpack/encode.go +++ b/libgo/go/golang_org/x/net/http2/hpack/encode.go @@ -206,7 +206,7 @@ func appendVarInt(dst []byte, n byte, i uint64) []byte { } // appendHpackString appends s, as encoded in "String Literal" -// representation, to dst and returns the the extended buffer. +// representation, to dst and returns the extended buffer. // // s will be encoded in Huffman codes only when it produces strictly // shorter byte string. diff --git a/libgo/go/golang_org/x/net/http2/hpack/hpack.go b/libgo/go/golang_org/x/net/http2/hpack/hpack.go index 176644a..166788c 100644 --- a/libgo/go/golang_org/x/net/http2/hpack/hpack.go +++ b/libgo/go/golang_org/x/net/http2/hpack/hpack.go @@ -389,6 +389,12 @@ func (d *Decoder) callEmit(hf HeaderField) error { // (same invariants and behavior as parseHeaderFieldRepr) func (d *Decoder) parseDynamicTableSizeUpdate() error { + // RFC 7541, sec 4.2: This dynamic table size update MUST occur at the + // beginning of the first header block following the change to the dynamic table size. + if d.dynTab.size > 0 { + return DecodingError{errors.New("dynamic table size update MUST occur at the beginning of a header block")} + } + buf := d.buf size, buf, err := readVarInt(5, buf) if err != nil { diff --git a/libgo/go/golang_org/x/net/http2/hpack/hpack_test.go b/libgo/go/golang_org/x/net/http2/hpack/hpack_test.go index bc7f476..3f22274 100644 --- a/libgo/go/golang_org/x/net/http2/hpack/hpack_test.go +++ b/libgo/go/golang_org/x/net/http2/hpack/hpack_test.go @@ -462,6 +462,27 @@ func TestHuffmanDecode(t *testing.T) { } } +func BenchmarkHuffmanDecode(b *testing.B) { + b.StopTimer() + enc, err := hex.DecodeString(strings.Replace("94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07", + " ", "", -1)) + if err != nil { + b.Fatal(err) + } + b.ReportAllocs() + b.StartTimer() + var buf bytes.Buffer + for i := 0; i < b.N; i++ { + buf.Reset() + if _, err := HuffmanDecode(&buf, enc); err != nil { + b.Fatalf("decode error: %v", err) + } + if string(buf.Bytes()) != "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" { + b.Fatalf("bogus output %q", buf.Bytes()) + } + } +} + func TestAppendHuffmanString(t *testing.T) { tests := []struct { in, want string @@ -720,3 +741,22 @@ func TestSaveBufLimit(t *testing.T) { t.Fatalf("Write error = %v; want ErrStringLength", err) } } + +func TestDynamicSizeUpdate(t *testing.T) { + var buf bytes.Buffer + enc := NewEncoder(&buf) + enc.SetMaxDynamicTableSize(255) + enc.WriteField(HeaderField{Name: "foo", Value: "bar"}) + + d := NewDecoder(4096, nil) + _, err := d.DecodeFull(buf.Bytes()) + if err != nil { + t.Fatalf("unexpected error: got = %v", err) + } + + // must fail since the dynamic table update must be at the beginning + _, err = d.DecodeFull(buf.Bytes()) + if err == nil { + t.Fatalf("dynamic table size update not at the beginning of a header block") + } +} diff --git a/libgo/go/golang_org/x/net/http2/hpack/huffman.go b/libgo/go/golang_org/x/net/http2/hpack/huffman.go index 8850e39..b412a96 100644 --- a/libgo/go/golang_org/x/net/http2/hpack/huffman.go +++ b/libgo/go/golang_org/x/net/http2/hpack/huffman.go @@ -47,6 +47,7 @@ var ErrInvalidHuffman = errors.New("hpack: invalid Huffman-encoded data") // If maxLen is greater than 0, attempts to write more to buf than // maxLen bytes will return ErrStringLength. func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error { + rootHuffmanNode := getRootHuffmanNode() n := rootHuffmanNode // cur is the bit buffer that has not been fed into n. // cbits is the number of low order bits in cur that are valid. @@ -106,7 +107,7 @@ func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error { type node struct { // children is non-nil for internal nodes - children []*node + children *[256]*node // The following are only valid if children is nil: codeLen uint8 // number of bits that led to the output of sym @@ -114,22 +115,31 @@ type node struct { } func newInternalNode() *node { - return &node{children: make([]*node, 256)} + return &node{children: new([256]*node)} } -var rootHuffmanNode = newInternalNode() +var ( + buildRootOnce sync.Once + lazyRootHuffmanNode *node +) + +func getRootHuffmanNode() *node { + buildRootOnce.Do(buildRootHuffmanNode) + return lazyRootHuffmanNode +} -func init() { +func buildRootHuffmanNode() { if len(huffmanCodes) != 256 { panic("unexpected size") } + lazyRootHuffmanNode = newInternalNode() for i, code := range huffmanCodes { addDecoderNode(byte(i), code, huffmanCodeLen[i]) } } func addDecoderNode(sym byte, code uint32, codeLen uint8) { - cur := rootHuffmanNode + cur := lazyRootHuffmanNode for codeLen > 8 { codeLen -= 8 i := uint8(code >> codeLen) diff --git a/libgo/go/golang_org/x/net/http2/hpack/tables.go b/libgo/go/golang_org/x/net/http2/hpack/tables.go index 8bd975d..a66cfbe 100644 --- a/libgo/go/golang_org/x/net/http2/hpack/tables.go +++ b/libgo/go/golang_org/x/net/http2/hpack/tables.go @@ -128,67 +128,67 @@ func (t *headerFieldTable) idToIndex(id uint64) uint64 { // http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B var staticTable = newStaticTable() var staticTableEntries = [...]HeaderField{ - HeaderField{Name: ":authority"}, - HeaderField{Name: ":method", Value: "GET"}, - HeaderField{Name: ":method", Value: "POST"}, - HeaderField{Name: ":path", Value: "/"}, - HeaderField{Name: ":path", Value: "/index.html"}, - HeaderField{Name: ":scheme", Value: "http"}, - HeaderField{Name: ":scheme", Value: "https"}, - HeaderField{Name: ":status", Value: "200"}, - HeaderField{Name: ":status", Value: "204"}, - HeaderField{Name: ":status", Value: "206"}, - HeaderField{Name: ":status", Value: "304"}, - HeaderField{Name: ":status", Value: "400"}, - HeaderField{Name: ":status", Value: "404"}, - HeaderField{Name: ":status", Value: "500"}, - HeaderField{Name: "accept-charset"}, - HeaderField{Name: "accept-encoding", Value: "gzip, deflate"}, - HeaderField{Name: "accept-language"}, - HeaderField{Name: "accept-ranges"}, - HeaderField{Name: "accept"}, - HeaderField{Name: "access-control-allow-origin"}, - HeaderField{Name: "age"}, - HeaderField{Name: "allow"}, - HeaderField{Name: "authorization"}, - HeaderField{Name: "cache-control"}, - HeaderField{Name: "content-disposition"}, - HeaderField{Name: "content-encoding"}, - HeaderField{Name: "content-language"}, - HeaderField{Name: "content-length"}, - HeaderField{Name: "content-location"}, - HeaderField{Name: "content-range"}, - HeaderField{Name: "content-type"}, - HeaderField{Name: "cookie"}, - HeaderField{Name: "date"}, - HeaderField{Name: "etag"}, - HeaderField{Name: "expect"}, - HeaderField{Name: "expires"}, - HeaderField{Name: "from"}, - HeaderField{Name: "host"}, - HeaderField{Name: "if-match"}, - HeaderField{Name: "if-modified-since"}, - HeaderField{Name: "if-none-match"}, - HeaderField{Name: "if-range"}, - HeaderField{Name: "if-unmodified-since"}, - HeaderField{Name: "last-modified"}, - HeaderField{Name: "link"}, - HeaderField{Name: "location"}, - HeaderField{Name: "max-forwards"}, - HeaderField{Name: "proxy-authenticate"}, - HeaderField{Name: "proxy-authorization"}, - HeaderField{Name: "range"}, - HeaderField{Name: "referer"}, - HeaderField{Name: "refresh"}, - HeaderField{Name: "retry-after"}, - HeaderField{Name: "server"}, - HeaderField{Name: "set-cookie"}, - HeaderField{Name: "strict-transport-security"}, - HeaderField{Name: "transfer-encoding"}, - HeaderField{Name: "user-agent"}, - HeaderField{Name: "vary"}, - HeaderField{Name: "via"}, - HeaderField{Name: "www-authenticate"}, + {Name: ":authority"}, + {Name: ":method", Value: "GET"}, + {Name: ":method", Value: "POST"}, + {Name: ":path", Value: "/"}, + {Name: ":path", Value: "/index.html"}, + {Name: ":scheme", Value: "http"}, + {Name: ":scheme", Value: "https"}, + {Name: ":status", Value: "200"}, + {Name: ":status", Value: "204"}, + {Name: ":status", Value: "206"}, + {Name: ":status", Value: "304"}, + {Name: ":status", Value: "400"}, + {Name: ":status", Value: "404"}, + {Name: ":status", Value: "500"}, + {Name: "accept-charset"}, + {Name: "accept-encoding", Value: "gzip, deflate"}, + {Name: "accept-language"}, + {Name: "accept-ranges"}, + {Name: "accept"}, + {Name: "access-control-allow-origin"}, + {Name: "age"}, + {Name: "allow"}, + {Name: "authorization"}, + {Name: "cache-control"}, + {Name: "content-disposition"}, + {Name: "content-encoding"}, + {Name: "content-language"}, + {Name: "content-length"}, + {Name: "content-location"}, + {Name: "content-range"}, + {Name: "content-type"}, + {Name: "cookie"}, + {Name: "date"}, + {Name: "etag"}, + {Name: "expect"}, + {Name: "expires"}, + {Name: "from"}, + {Name: "host"}, + {Name: "if-match"}, + {Name: "if-modified-since"}, + {Name: "if-none-match"}, + {Name: "if-range"}, + {Name: "if-unmodified-since"}, + {Name: "last-modified"}, + {Name: "link"}, + {Name: "location"}, + {Name: "max-forwards"}, + {Name: "proxy-authenticate"}, + {Name: "proxy-authorization"}, + {Name: "range"}, + {Name: "referer"}, + {Name: "refresh"}, + {Name: "retry-after"}, + {Name: "server"}, + {Name: "set-cookie"}, + {Name: "strict-transport-security"}, + {Name: "transfer-encoding"}, + {Name: "user-agent"}, + {Name: "vary"}, + {Name: "via"}, + {Name: "www-authenticate"}, } func newStaticTable() *headerFieldTable { diff --git a/libgo/go/golang_org/x/net/internal/nettest/helper_stub.go b/libgo/go/golang_org/x/net/internal/nettest/helper_stub.go index ea61b6f..d729156 100644 --- a/libgo/go/golang_org/x/net/internal/nettest/helper_stub.go +++ b/libgo/go/golang_org/x/net/internal/nettest/helper_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build nacl plan9 +// +build js,wasm nacl plan9 package nettest diff --git a/libgo/go/golang_org/x/net/internal/nettest/stack.go b/libgo/go/golang_org/x/net/internal/nettest/stack.go index 06f4e09..46d2fcc 100644 --- a/libgo/go/golang_org/x/net/internal/nettest/stack.go +++ b/libgo/go/golang_org/x/net/internal/nettest/stack.go @@ -64,7 +64,7 @@ func TestableNetwork(network string) bool { switch network { case "unix", "unixgram": switch runtime.GOOS { - case "android", "nacl", "plan9", "windows": + case "android", "js", "nacl", "plan9", "windows": return false } if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { @@ -72,7 +72,7 @@ func TestableNetwork(network string) bool { } case "unixpacket": switch runtime.GOOS { - case "android", "darwin", "freebsd", "nacl", "plan9", "windows": + case "android", "darwin", "freebsd", "js", "nacl", "plan9", "windows": return false case "netbsd": // It passes on amd64 at least. 386 fails (Issue 22927). arm is unknown. diff --git a/libgo/go/hash/crc32/crc32.go b/libgo/go/hash/crc32/crc32.go index 1912caa..908b84a 100644 --- a/libgo/go/hash/crc32/crc32.go +++ b/libgo/go/hash/crc32/crc32.go @@ -3,12 +3,12 @@ // license that can be found in the LICENSE file. // Package crc32 implements the 32-bit cyclic redundancy check, or CRC-32, -// checksum. See http://en.wikipedia.org/wiki/Cyclic_redundancy_check for +// checksum. See https://en.wikipedia.org/wiki/Cyclic_redundancy_check for // information. // // Polynomials are represented in LSB-first form also known as reversed representation. // -// See http://en.wikipedia.org/wiki/Mathematics_of_cyclic_redundancy_checks#Reversed_representations_and_reciprocal_polynomials +// See https://en.wikipedia.org/wiki/Mathematics_of_cyclic_redundancy_checks#Reversed_representations_and_reciprocal_polynomials // for information. package crc32 @@ -29,12 +29,12 @@ const ( // Castagnoli's polynomial, used in iSCSI. // Has better error detection characteristics than IEEE. - // http://dx.doi.org/10.1109/26.231911 + // https://dx.doi.org/10.1109/26.231911 Castagnoli = 0x82f63b78 // Koopman's polynomial. // Also has better error detection characteristics than IEEE. - // http://dx.doi.org/10.1109/DSN.2002.1028931 + // https://dx.doi.org/10.1109/DSN.2002.1028931 Koopman = 0xeb31d82e ) diff --git a/libgo/go/hash/crc64/crc64.go b/libgo/go/hash/crc64/crc64.go index 3b24c24..a799a01 100644 --- a/libgo/go/hash/crc64/crc64.go +++ b/libgo/go/hash/crc64/crc64.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package crc64 implements the 64-bit cyclic redundancy check, or CRC-64, -// checksum. See http://en.wikipedia.org/wiki/Cyclic_redundancy_check for +// checksum. See https://en.wikipedia.org/wiki/Cyclic_redundancy_check for // information. package crc64 diff --git a/libgo/go/html/entity.go b/libgo/go/html/entity.go index dfeaf6c..f0f9a6a 100644 --- a/libgo/go/html/entity.go +++ b/libgo/go/html/entity.go @@ -4,6 +4,8 @@ package html +import "sync" + // All entities that do not end with ';' are 6 or fewer bytes long. const longestEntityWithoutSemicolon = 6 @@ -13,2241 +15,2251 @@ const longestEntityWithoutSemicolon = 6 // // Note that the HTML5 list is larger than the HTML4 list at // http://www.w3.org/TR/html4/sgml/entities.html -var entity = map[string]rune{ - "AElig;": '\U000000C6', - "AMP;": '\U00000026', - "Aacute;": '\U000000C1', - "Abreve;": '\U00000102', - "Acirc;": '\U000000C2', - "Acy;": '\U00000410', - "Afr;": '\U0001D504', - "Agrave;": '\U000000C0', - "Alpha;": '\U00000391', - "Amacr;": '\U00000100', - "And;": '\U00002A53', - "Aogon;": '\U00000104', - "Aopf;": '\U0001D538', - "ApplyFunction;": '\U00002061', - "Aring;": '\U000000C5', - "Ascr;": '\U0001D49C', - "Assign;": '\U00002254', - "Atilde;": '\U000000C3', - "Auml;": '\U000000C4', - "Backslash;": '\U00002216', - "Barv;": '\U00002AE7', - "Barwed;": '\U00002306', - "Bcy;": '\U00000411', - "Because;": '\U00002235', - "Bernoullis;": '\U0000212C', - "Beta;": '\U00000392', - "Bfr;": '\U0001D505', - "Bopf;": '\U0001D539', - "Breve;": '\U000002D8', - "Bscr;": '\U0000212C', - "Bumpeq;": '\U0000224E', - "CHcy;": '\U00000427', - "COPY;": '\U000000A9', - "Cacute;": '\U00000106', - "Cap;": '\U000022D2', - "CapitalDifferentialD;": '\U00002145', - "Cayleys;": '\U0000212D', - "Ccaron;": '\U0000010C', - "Ccedil;": '\U000000C7', - "Ccirc;": '\U00000108', - "Cconint;": '\U00002230', - "Cdot;": '\U0000010A', - "Cedilla;": '\U000000B8', - "CenterDot;": '\U000000B7', - "Cfr;": '\U0000212D', - "Chi;": '\U000003A7', - "CircleDot;": '\U00002299', - "CircleMinus;": '\U00002296', - "CirclePlus;": '\U00002295', - "CircleTimes;": '\U00002297', - "ClockwiseContourIntegral;": '\U00002232', - "CloseCurlyDoubleQuote;": '\U0000201D', - "CloseCurlyQuote;": '\U00002019', - "Colon;": '\U00002237', - "Colone;": '\U00002A74', - "Congruent;": '\U00002261', - "Conint;": '\U0000222F', - "ContourIntegral;": '\U0000222E', - "Copf;": '\U00002102', - "Coproduct;": '\U00002210', - "CounterClockwiseContourIntegral;": '\U00002233', - "Cross;": '\U00002A2F', - "Cscr;": '\U0001D49E', - "Cup;": '\U000022D3', - "CupCap;": '\U0000224D', - "DD;": '\U00002145', - "DDotrahd;": '\U00002911', - "DJcy;": '\U00000402', - "DScy;": '\U00000405', - "DZcy;": '\U0000040F', - "Dagger;": '\U00002021', - "Darr;": '\U000021A1', - "Dashv;": '\U00002AE4', - "Dcaron;": '\U0000010E', - "Dcy;": '\U00000414', - "Del;": '\U00002207', - "Delta;": '\U00000394', - "Dfr;": '\U0001D507', - "DiacriticalAcute;": '\U000000B4', - "DiacriticalDot;": '\U000002D9', - "DiacriticalDoubleAcute;": '\U000002DD', - "DiacriticalGrave;": '\U00000060', - "DiacriticalTilde;": '\U000002DC', - "Diamond;": '\U000022C4', - "DifferentialD;": '\U00002146', - "Dopf;": '\U0001D53B', - "Dot;": '\U000000A8', - "DotDot;": '\U000020DC', - "DotEqual;": '\U00002250', - "DoubleContourIntegral;": '\U0000222F', - "DoubleDot;": '\U000000A8', - "DoubleDownArrow;": '\U000021D3', - "DoubleLeftArrow;": '\U000021D0', - "DoubleLeftRightArrow;": '\U000021D4', - "DoubleLeftTee;": '\U00002AE4', - "DoubleLongLeftArrow;": '\U000027F8', - "DoubleLongLeftRightArrow;": '\U000027FA', - "DoubleLongRightArrow;": '\U000027F9', - "DoubleRightArrow;": '\U000021D2', - "DoubleRightTee;": '\U000022A8', - "DoubleUpArrow;": '\U000021D1', - "DoubleUpDownArrow;": '\U000021D5', - "DoubleVerticalBar;": '\U00002225', - "DownArrow;": '\U00002193', - "DownArrowBar;": '\U00002913', - "DownArrowUpArrow;": '\U000021F5', - "DownBreve;": '\U00000311', - "DownLeftRightVector;": '\U00002950', - "DownLeftTeeVector;": '\U0000295E', - "DownLeftVector;": '\U000021BD', - "DownLeftVectorBar;": '\U00002956', - "DownRightTeeVector;": '\U0000295F', - "DownRightVector;": '\U000021C1', - "DownRightVectorBar;": '\U00002957', - "DownTee;": '\U000022A4', - "DownTeeArrow;": '\U000021A7', - "Downarrow;": '\U000021D3', - "Dscr;": '\U0001D49F', - "Dstrok;": '\U00000110', - "ENG;": '\U0000014A', - "ETH;": '\U000000D0', - "Eacute;": '\U000000C9', - "Ecaron;": '\U0000011A', - "Ecirc;": '\U000000CA', - "Ecy;": '\U0000042D', - "Edot;": '\U00000116', - "Efr;": '\U0001D508', - "Egrave;": '\U000000C8', - "Element;": '\U00002208', - "Emacr;": '\U00000112', - "EmptySmallSquare;": '\U000025FB', - "EmptyVerySmallSquare;": '\U000025AB', - "Eogon;": '\U00000118', - "Eopf;": '\U0001D53C', - "Epsilon;": '\U00000395', - "Equal;": '\U00002A75', - "EqualTilde;": '\U00002242', - "Equilibrium;": '\U000021CC', - "Escr;": '\U00002130', - "Esim;": '\U00002A73', - "Eta;": '\U00000397', - "Euml;": '\U000000CB', - "Exists;": '\U00002203', - "ExponentialE;": '\U00002147', - "Fcy;": '\U00000424', - "Ffr;": '\U0001D509', - "FilledSmallSquare;": '\U000025FC', - "FilledVerySmallSquare;": '\U000025AA', - "Fopf;": '\U0001D53D', - "ForAll;": '\U00002200', - "Fouriertrf;": '\U00002131', - "Fscr;": '\U00002131', - "GJcy;": '\U00000403', - "GT;": '\U0000003E', - "Gamma;": '\U00000393', - "Gammad;": '\U000003DC', - "Gbreve;": '\U0000011E', - "Gcedil;": '\U00000122', - "Gcirc;": '\U0000011C', - "Gcy;": '\U00000413', - "Gdot;": '\U00000120', - "Gfr;": '\U0001D50A', - "Gg;": '\U000022D9', - "Gopf;": '\U0001D53E', - "GreaterEqual;": '\U00002265', - "GreaterEqualLess;": '\U000022DB', - "GreaterFullEqual;": '\U00002267', - "GreaterGreater;": '\U00002AA2', - "GreaterLess;": '\U00002277', - "GreaterSlantEqual;": '\U00002A7E', - "GreaterTilde;": '\U00002273', - "Gscr;": '\U0001D4A2', - "Gt;": '\U0000226B', - "HARDcy;": '\U0000042A', - "Hacek;": '\U000002C7', - "Hat;": '\U0000005E', - "Hcirc;": '\U00000124', - "Hfr;": '\U0000210C', - "HilbertSpace;": '\U0000210B', - "Hopf;": '\U0000210D', - "HorizontalLine;": '\U00002500', - "Hscr;": '\U0000210B', - "Hstrok;": '\U00000126', - "HumpDownHump;": '\U0000224E', - "HumpEqual;": '\U0000224F', - "IEcy;": '\U00000415', - "IJlig;": '\U00000132', - "IOcy;": '\U00000401', - "Iacute;": '\U000000CD', - "Icirc;": '\U000000CE', - "Icy;": '\U00000418', - "Idot;": '\U00000130', - "Ifr;": '\U00002111', - "Igrave;": '\U000000CC', - "Im;": '\U00002111', - "Imacr;": '\U0000012A', - "ImaginaryI;": '\U00002148', - "Implies;": '\U000021D2', - "Int;": '\U0000222C', - "Integral;": '\U0000222B', - "Intersection;": '\U000022C2', - "InvisibleComma;": '\U00002063', - "InvisibleTimes;": '\U00002062', - "Iogon;": '\U0000012E', - "Iopf;": '\U0001D540', - "Iota;": '\U00000399', - "Iscr;": '\U00002110', - "Itilde;": '\U00000128', - "Iukcy;": '\U00000406', - "Iuml;": '\U000000CF', - "Jcirc;": '\U00000134', - "Jcy;": '\U00000419', - "Jfr;": '\U0001D50D', - "Jopf;": '\U0001D541', - "Jscr;": '\U0001D4A5', - "Jsercy;": '\U00000408', - "Jukcy;": '\U00000404', - "KHcy;": '\U00000425', - "KJcy;": '\U0000040C', - "Kappa;": '\U0000039A', - "Kcedil;": '\U00000136', - "Kcy;": '\U0000041A', - "Kfr;": '\U0001D50E', - "Kopf;": '\U0001D542', - "Kscr;": '\U0001D4A6', - "LJcy;": '\U00000409', - "LT;": '\U0000003C', - "Lacute;": '\U00000139', - "Lambda;": '\U0000039B', - "Lang;": '\U000027EA', - "Laplacetrf;": '\U00002112', - "Larr;": '\U0000219E', - "Lcaron;": '\U0000013D', - "Lcedil;": '\U0000013B', - "Lcy;": '\U0000041B', - "LeftAngleBracket;": '\U000027E8', - "LeftArrow;": '\U00002190', - "LeftArrowBar;": '\U000021E4', - "LeftArrowRightArrow;": '\U000021C6', - "LeftCeiling;": '\U00002308', - "LeftDoubleBracket;": '\U000027E6', - "LeftDownTeeVector;": '\U00002961', - "LeftDownVector;": '\U000021C3', - "LeftDownVectorBar;": '\U00002959', - "LeftFloor;": '\U0000230A', - "LeftRightArrow;": '\U00002194', - "LeftRightVector;": '\U0000294E', - "LeftTee;": '\U000022A3', - "LeftTeeArrow;": '\U000021A4', - "LeftTeeVector;": '\U0000295A', - "LeftTriangle;": '\U000022B2', - "LeftTriangleBar;": '\U000029CF', - "LeftTriangleEqual;": '\U000022B4', - "LeftUpDownVector;": '\U00002951', - "LeftUpTeeVector;": '\U00002960', - "LeftUpVector;": '\U000021BF', - "LeftUpVectorBar;": '\U00002958', - "LeftVector;": '\U000021BC', - "LeftVectorBar;": '\U00002952', - "Leftarrow;": '\U000021D0', - "Leftrightarrow;": '\U000021D4', - "LessEqualGreater;": '\U000022DA', - "LessFullEqual;": '\U00002266', - "LessGreater;": '\U00002276', - "LessLess;": '\U00002AA1', - "LessSlantEqual;": '\U00002A7D', - "LessTilde;": '\U00002272', - "Lfr;": '\U0001D50F', - "Ll;": '\U000022D8', - "Lleftarrow;": '\U000021DA', - "Lmidot;": '\U0000013F', - "LongLeftArrow;": '\U000027F5', - "LongLeftRightArrow;": '\U000027F7', - "LongRightArrow;": '\U000027F6', - "Longleftarrow;": '\U000027F8', - "Longleftrightarrow;": '\U000027FA', - "Longrightarrow;": '\U000027F9', - "Lopf;": '\U0001D543', - "LowerLeftArrow;": '\U00002199', - "LowerRightArrow;": '\U00002198', - "Lscr;": '\U00002112', - "Lsh;": '\U000021B0', - "Lstrok;": '\U00000141', - "Lt;": '\U0000226A', - "Map;": '\U00002905', - "Mcy;": '\U0000041C', - "MediumSpace;": '\U0000205F', - "Mellintrf;": '\U00002133', - "Mfr;": '\U0001D510', - "MinusPlus;": '\U00002213', - "Mopf;": '\U0001D544', - "Mscr;": '\U00002133', - "Mu;": '\U0000039C', - "NJcy;": '\U0000040A', - "Nacute;": '\U00000143', - "Ncaron;": '\U00000147', - "Ncedil;": '\U00000145', - "Ncy;": '\U0000041D', - "NegativeMediumSpace;": '\U0000200B', - "NegativeThickSpace;": '\U0000200B', - "NegativeThinSpace;": '\U0000200B', - "NegativeVeryThinSpace;": '\U0000200B', - "NestedGreaterGreater;": '\U0000226B', - "NestedLessLess;": '\U0000226A', - "NewLine;": '\U0000000A', - "Nfr;": '\U0001D511', - "NoBreak;": '\U00002060', - "NonBreakingSpace;": '\U000000A0', - "Nopf;": '\U00002115', - "Not;": '\U00002AEC', - "NotCongruent;": '\U00002262', - "NotCupCap;": '\U0000226D', - "NotDoubleVerticalBar;": '\U00002226', - "NotElement;": '\U00002209', - "NotEqual;": '\U00002260', - "NotExists;": '\U00002204', - "NotGreater;": '\U0000226F', - "NotGreaterEqual;": '\U00002271', - "NotGreaterLess;": '\U00002279', - "NotGreaterTilde;": '\U00002275', - "NotLeftTriangle;": '\U000022EA', - "NotLeftTriangleEqual;": '\U000022EC', - "NotLess;": '\U0000226E', - "NotLessEqual;": '\U00002270', - "NotLessGreater;": '\U00002278', - "NotLessTilde;": '\U00002274', - "NotPrecedes;": '\U00002280', - "NotPrecedesSlantEqual;": '\U000022E0', - "NotReverseElement;": '\U0000220C', - "NotRightTriangle;": '\U000022EB', - "NotRightTriangleEqual;": '\U000022ED', - "NotSquareSubsetEqual;": '\U000022E2', - "NotSquareSupersetEqual;": '\U000022E3', - "NotSubsetEqual;": '\U00002288', - "NotSucceeds;": '\U00002281', - "NotSucceedsSlantEqual;": '\U000022E1', - "NotSupersetEqual;": '\U00002289', - "NotTilde;": '\U00002241', - "NotTildeEqual;": '\U00002244', - "NotTildeFullEqual;": '\U00002247', - "NotTildeTilde;": '\U00002249', - "NotVerticalBar;": '\U00002224', - "Nscr;": '\U0001D4A9', - "Ntilde;": '\U000000D1', - "Nu;": '\U0000039D', - "OElig;": '\U00000152', - "Oacute;": '\U000000D3', - "Ocirc;": '\U000000D4', - "Ocy;": '\U0000041E', - "Odblac;": '\U00000150', - "Ofr;": '\U0001D512', - "Ograve;": '\U000000D2', - "Omacr;": '\U0000014C', - "Omega;": '\U000003A9', - "Omicron;": '\U0000039F', - "Oopf;": '\U0001D546', - "OpenCurlyDoubleQuote;": '\U0000201C', - "OpenCurlyQuote;": '\U00002018', - "Or;": '\U00002A54', - "Oscr;": '\U0001D4AA', - "Oslash;": '\U000000D8', - "Otilde;": '\U000000D5', - "Otimes;": '\U00002A37', - "Ouml;": '\U000000D6', - "OverBar;": '\U0000203E', - "OverBrace;": '\U000023DE', - "OverBracket;": '\U000023B4', - "OverParenthesis;": '\U000023DC', - "PartialD;": '\U00002202', - "Pcy;": '\U0000041F', - "Pfr;": '\U0001D513', - "Phi;": '\U000003A6', - "Pi;": '\U000003A0', - "PlusMinus;": '\U000000B1', - "Poincareplane;": '\U0000210C', - "Popf;": '\U00002119', - "Pr;": '\U00002ABB', - "Precedes;": '\U0000227A', - "PrecedesEqual;": '\U00002AAF', - "PrecedesSlantEqual;": '\U0000227C', - "PrecedesTilde;": '\U0000227E', - "Prime;": '\U00002033', - "Product;": '\U0000220F', - "Proportion;": '\U00002237', - "Proportional;": '\U0000221D', - "Pscr;": '\U0001D4AB', - "Psi;": '\U000003A8', - "QUOT;": '\U00000022', - "Qfr;": '\U0001D514', - "Qopf;": '\U0000211A', - "Qscr;": '\U0001D4AC', - "RBarr;": '\U00002910', - "REG;": '\U000000AE', - "Racute;": '\U00000154', - "Rang;": '\U000027EB', - "Rarr;": '\U000021A0', - "Rarrtl;": '\U00002916', - "Rcaron;": '\U00000158', - "Rcedil;": '\U00000156', - "Rcy;": '\U00000420', - "Re;": '\U0000211C', - "ReverseElement;": '\U0000220B', - "ReverseEquilibrium;": '\U000021CB', - "ReverseUpEquilibrium;": '\U0000296F', - "Rfr;": '\U0000211C', - "Rho;": '\U000003A1', - "RightAngleBracket;": '\U000027E9', - "RightArrow;": '\U00002192', - "RightArrowBar;": '\U000021E5', - "RightArrowLeftArrow;": '\U000021C4', - "RightCeiling;": '\U00002309', - "RightDoubleBracket;": '\U000027E7', - "RightDownTeeVector;": '\U0000295D', - "RightDownVector;": '\U000021C2', - "RightDownVectorBar;": '\U00002955', - "RightFloor;": '\U0000230B', - "RightTee;": '\U000022A2', - "RightTeeArrow;": '\U000021A6', - "RightTeeVector;": '\U0000295B', - "RightTriangle;": '\U000022B3', - "RightTriangleBar;": '\U000029D0', - "RightTriangleEqual;": '\U000022B5', - "RightUpDownVector;": '\U0000294F', - "RightUpTeeVector;": '\U0000295C', - "RightUpVector;": '\U000021BE', - "RightUpVectorBar;": '\U00002954', - "RightVector;": '\U000021C0', - "RightVectorBar;": '\U00002953', - "Rightarrow;": '\U000021D2', - "Ropf;": '\U0000211D', - "RoundImplies;": '\U00002970', - "Rrightarrow;": '\U000021DB', - "Rscr;": '\U0000211B', - "Rsh;": '\U000021B1', - "RuleDelayed;": '\U000029F4', - "SHCHcy;": '\U00000429', - "SHcy;": '\U00000428', - "SOFTcy;": '\U0000042C', - "Sacute;": '\U0000015A', - "Sc;": '\U00002ABC', - "Scaron;": '\U00000160', - "Scedil;": '\U0000015E', - "Scirc;": '\U0000015C', - "Scy;": '\U00000421', - "Sfr;": '\U0001D516', - "ShortDownArrow;": '\U00002193', - "ShortLeftArrow;": '\U00002190', - "ShortRightArrow;": '\U00002192', - "ShortUpArrow;": '\U00002191', - "Sigma;": '\U000003A3', - "SmallCircle;": '\U00002218', - "Sopf;": '\U0001D54A', - "Sqrt;": '\U0000221A', - "Square;": '\U000025A1', - "SquareIntersection;": '\U00002293', - "SquareSubset;": '\U0000228F', - "SquareSubsetEqual;": '\U00002291', - "SquareSuperset;": '\U00002290', - "SquareSupersetEqual;": '\U00002292', - "SquareUnion;": '\U00002294', - "Sscr;": '\U0001D4AE', - "Star;": '\U000022C6', - "Sub;": '\U000022D0', - "Subset;": '\U000022D0', - "SubsetEqual;": '\U00002286', - "Succeeds;": '\U0000227B', - "SucceedsEqual;": '\U00002AB0', - "SucceedsSlantEqual;": '\U0000227D', - "SucceedsTilde;": '\U0000227F', - "SuchThat;": '\U0000220B', - "Sum;": '\U00002211', - "Sup;": '\U000022D1', - "Superset;": '\U00002283', - "SupersetEqual;": '\U00002287', - "Supset;": '\U000022D1', - "THORN;": '\U000000DE', - "TRADE;": '\U00002122', - "TSHcy;": '\U0000040B', - "TScy;": '\U00000426', - "Tab;": '\U00000009', - "Tau;": '\U000003A4', - "Tcaron;": '\U00000164', - "Tcedil;": '\U00000162', - "Tcy;": '\U00000422', - "Tfr;": '\U0001D517', - "Therefore;": '\U00002234', - "Theta;": '\U00000398', - "ThinSpace;": '\U00002009', - "Tilde;": '\U0000223C', - "TildeEqual;": '\U00002243', - "TildeFullEqual;": '\U00002245', - "TildeTilde;": '\U00002248', - "Topf;": '\U0001D54B', - "TripleDot;": '\U000020DB', - "Tscr;": '\U0001D4AF', - "Tstrok;": '\U00000166', - "Uacute;": '\U000000DA', - "Uarr;": '\U0000219F', - "Uarrocir;": '\U00002949', - "Ubrcy;": '\U0000040E', - "Ubreve;": '\U0000016C', - "Ucirc;": '\U000000DB', - "Ucy;": '\U00000423', - "Udblac;": '\U00000170', - "Ufr;": '\U0001D518', - "Ugrave;": '\U000000D9', - "Umacr;": '\U0000016A', - "UnderBar;": '\U0000005F', - "UnderBrace;": '\U000023DF', - "UnderBracket;": '\U000023B5', - "UnderParenthesis;": '\U000023DD', - "Union;": '\U000022C3', - "UnionPlus;": '\U0000228E', - "Uogon;": '\U00000172', - "Uopf;": '\U0001D54C', - "UpArrow;": '\U00002191', - "UpArrowBar;": '\U00002912', - "UpArrowDownArrow;": '\U000021C5', - "UpDownArrow;": '\U00002195', - "UpEquilibrium;": '\U0000296E', - "UpTee;": '\U000022A5', - "UpTeeArrow;": '\U000021A5', - "Uparrow;": '\U000021D1', - "Updownarrow;": '\U000021D5', - "UpperLeftArrow;": '\U00002196', - "UpperRightArrow;": '\U00002197', - "Upsi;": '\U000003D2', - "Upsilon;": '\U000003A5', - "Uring;": '\U0000016E', - "Uscr;": '\U0001D4B0', - "Utilde;": '\U00000168', - "Uuml;": '\U000000DC', - "VDash;": '\U000022AB', - "Vbar;": '\U00002AEB', - "Vcy;": '\U00000412', - "Vdash;": '\U000022A9', - "Vdashl;": '\U00002AE6', - "Vee;": '\U000022C1', - "Verbar;": '\U00002016', - "Vert;": '\U00002016', - "VerticalBar;": '\U00002223', - "VerticalLine;": '\U0000007C', - "VerticalSeparator;": '\U00002758', - "VerticalTilde;": '\U00002240', - "VeryThinSpace;": '\U0000200A', - "Vfr;": '\U0001D519', - "Vopf;": '\U0001D54D', - "Vscr;": '\U0001D4B1', - "Vvdash;": '\U000022AA', - "Wcirc;": '\U00000174', - "Wedge;": '\U000022C0', - "Wfr;": '\U0001D51A', - "Wopf;": '\U0001D54E', - "Wscr;": '\U0001D4B2', - "Xfr;": '\U0001D51B', - "Xi;": '\U0000039E', - "Xopf;": '\U0001D54F', - "Xscr;": '\U0001D4B3', - "YAcy;": '\U0000042F', - "YIcy;": '\U00000407', - "YUcy;": '\U0000042E', - "Yacute;": '\U000000DD', - "Ycirc;": '\U00000176', - "Ycy;": '\U0000042B', - "Yfr;": '\U0001D51C', - "Yopf;": '\U0001D550', - "Yscr;": '\U0001D4B4', - "Yuml;": '\U00000178', - "ZHcy;": '\U00000416', - "Zacute;": '\U00000179', - "Zcaron;": '\U0000017D', - "Zcy;": '\U00000417', - "Zdot;": '\U0000017B', - "ZeroWidthSpace;": '\U0000200B', - "Zeta;": '\U00000396', - "Zfr;": '\U00002128', - "Zopf;": '\U00002124', - "Zscr;": '\U0001D4B5', - "aacute;": '\U000000E1', - "abreve;": '\U00000103', - "ac;": '\U0000223E', - "acd;": '\U0000223F', - "acirc;": '\U000000E2', - "acute;": '\U000000B4', - "acy;": '\U00000430', - "aelig;": '\U000000E6', - "af;": '\U00002061', - "afr;": '\U0001D51E', - "agrave;": '\U000000E0', - "alefsym;": '\U00002135', - "aleph;": '\U00002135', - "alpha;": '\U000003B1', - "amacr;": '\U00000101', - "amalg;": '\U00002A3F', - "amp;": '\U00000026', - "and;": '\U00002227', - "andand;": '\U00002A55', - "andd;": '\U00002A5C', - "andslope;": '\U00002A58', - "andv;": '\U00002A5A', - "ang;": '\U00002220', - "ange;": '\U000029A4', - "angle;": '\U00002220', - "angmsd;": '\U00002221', - "angmsdaa;": '\U000029A8', - "angmsdab;": '\U000029A9', - "angmsdac;": '\U000029AA', - "angmsdad;": '\U000029AB', - "angmsdae;": '\U000029AC', - "angmsdaf;": '\U000029AD', - "angmsdag;": '\U000029AE', - "angmsdah;": '\U000029AF', - "angrt;": '\U0000221F', - "angrtvb;": '\U000022BE', - "angrtvbd;": '\U0000299D', - "angsph;": '\U00002222', - "angst;": '\U000000C5', - "angzarr;": '\U0000237C', - "aogon;": '\U00000105', - "aopf;": '\U0001D552', - "ap;": '\U00002248', - "apE;": '\U00002A70', - "apacir;": '\U00002A6F', - "ape;": '\U0000224A', - "apid;": '\U0000224B', - "apos;": '\U00000027', - "approx;": '\U00002248', - "approxeq;": '\U0000224A', - "aring;": '\U000000E5', - "ascr;": '\U0001D4B6', - "ast;": '\U0000002A', - "asymp;": '\U00002248', - "asympeq;": '\U0000224D', - "atilde;": '\U000000E3', - "auml;": '\U000000E4', - "awconint;": '\U00002233', - "awint;": '\U00002A11', - "bNot;": '\U00002AED', - "backcong;": '\U0000224C', - "backepsilon;": '\U000003F6', - "backprime;": '\U00002035', - "backsim;": '\U0000223D', - "backsimeq;": '\U000022CD', - "barvee;": '\U000022BD', - "barwed;": '\U00002305', - "barwedge;": '\U00002305', - "bbrk;": '\U000023B5', - "bbrktbrk;": '\U000023B6', - "bcong;": '\U0000224C', - "bcy;": '\U00000431', - "bdquo;": '\U0000201E', - "becaus;": '\U00002235', - "because;": '\U00002235', - "bemptyv;": '\U000029B0', - "bepsi;": '\U000003F6', - "bernou;": '\U0000212C', - "beta;": '\U000003B2', - "beth;": '\U00002136', - "between;": '\U0000226C', - "bfr;": '\U0001D51F', - "bigcap;": '\U000022C2', - "bigcirc;": '\U000025EF', - "bigcup;": '\U000022C3', - "bigodot;": '\U00002A00', - "bigoplus;": '\U00002A01', - "bigotimes;": '\U00002A02', - "bigsqcup;": '\U00002A06', - "bigstar;": '\U00002605', - "bigtriangledown;": '\U000025BD', - "bigtriangleup;": '\U000025B3', - "biguplus;": '\U00002A04', - "bigvee;": '\U000022C1', - "bigwedge;": '\U000022C0', - "bkarow;": '\U0000290D', - "blacklozenge;": '\U000029EB', - "blacksquare;": '\U000025AA', - "blacktriangle;": '\U000025B4', - "blacktriangledown;": '\U000025BE', - "blacktriangleleft;": '\U000025C2', - "blacktriangleright;": '\U000025B8', - "blank;": '\U00002423', - "blk12;": '\U00002592', - "blk14;": '\U00002591', - "blk34;": '\U00002593', - "block;": '\U00002588', - "bnot;": '\U00002310', - "bopf;": '\U0001D553', - "bot;": '\U000022A5', - "bottom;": '\U000022A5', - "bowtie;": '\U000022C8', - "boxDL;": '\U00002557', - "boxDR;": '\U00002554', - "boxDl;": '\U00002556', - "boxDr;": '\U00002553', - "boxH;": '\U00002550', - "boxHD;": '\U00002566', - "boxHU;": '\U00002569', - "boxHd;": '\U00002564', - "boxHu;": '\U00002567', - "boxUL;": '\U0000255D', - "boxUR;": '\U0000255A', - "boxUl;": '\U0000255C', - "boxUr;": '\U00002559', - "boxV;": '\U00002551', - "boxVH;": '\U0000256C', - "boxVL;": '\U00002563', - "boxVR;": '\U00002560', - "boxVh;": '\U0000256B', - "boxVl;": '\U00002562', - "boxVr;": '\U0000255F', - "boxbox;": '\U000029C9', - "boxdL;": '\U00002555', - "boxdR;": '\U00002552', - "boxdl;": '\U00002510', - "boxdr;": '\U0000250C', - "boxh;": '\U00002500', - "boxhD;": '\U00002565', - "boxhU;": '\U00002568', - "boxhd;": '\U0000252C', - "boxhu;": '\U00002534', - "boxminus;": '\U0000229F', - "boxplus;": '\U0000229E', - "boxtimes;": '\U000022A0', - "boxuL;": '\U0000255B', - "boxuR;": '\U00002558', - "boxul;": '\U00002518', - "boxur;": '\U00002514', - "boxv;": '\U00002502', - "boxvH;": '\U0000256A', - "boxvL;": '\U00002561', - "boxvR;": '\U0000255E', - "boxvh;": '\U0000253C', - "boxvl;": '\U00002524', - "boxvr;": '\U0000251C', - "bprime;": '\U00002035', - "breve;": '\U000002D8', - "brvbar;": '\U000000A6', - "bscr;": '\U0001D4B7', - "bsemi;": '\U0000204F', - "bsim;": '\U0000223D', - "bsime;": '\U000022CD', - "bsol;": '\U0000005C', - "bsolb;": '\U000029C5', - "bsolhsub;": '\U000027C8', - "bull;": '\U00002022', - "bullet;": '\U00002022', - "bump;": '\U0000224E', - "bumpE;": '\U00002AAE', - "bumpe;": '\U0000224F', - "bumpeq;": '\U0000224F', - "cacute;": '\U00000107', - "cap;": '\U00002229', - "capand;": '\U00002A44', - "capbrcup;": '\U00002A49', - "capcap;": '\U00002A4B', - "capcup;": '\U00002A47', - "capdot;": '\U00002A40', - "caret;": '\U00002041', - "caron;": '\U000002C7', - "ccaps;": '\U00002A4D', - "ccaron;": '\U0000010D', - "ccedil;": '\U000000E7', - "ccirc;": '\U00000109', - "ccups;": '\U00002A4C', - "ccupssm;": '\U00002A50', - "cdot;": '\U0000010B', - "cedil;": '\U000000B8', - "cemptyv;": '\U000029B2', - "cent;": '\U000000A2', - "centerdot;": '\U000000B7', - "cfr;": '\U0001D520', - "chcy;": '\U00000447', - "check;": '\U00002713', - "checkmark;": '\U00002713', - "chi;": '\U000003C7', - "cir;": '\U000025CB', - "cirE;": '\U000029C3', - "circ;": '\U000002C6', - "circeq;": '\U00002257', - "circlearrowleft;": '\U000021BA', - "circlearrowright;": '\U000021BB', - "circledR;": '\U000000AE', - "circledS;": '\U000024C8', - "circledast;": '\U0000229B', - "circledcirc;": '\U0000229A', - "circleddash;": '\U0000229D', - "cire;": '\U00002257', - "cirfnint;": '\U00002A10', - "cirmid;": '\U00002AEF', - "cirscir;": '\U000029C2', - "clubs;": '\U00002663', - "clubsuit;": '\U00002663', - "colon;": '\U0000003A', - "colone;": '\U00002254', - "coloneq;": '\U00002254', - "comma;": '\U0000002C', - "commat;": '\U00000040', - "comp;": '\U00002201', - "compfn;": '\U00002218', - "complement;": '\U00002201', - "complexes;": '\U00002102', - "cong;": '\U00002245', - "congdot;": '\U00002A6D', - "conint;": '\U0000222E', - "copf;": '\U0001D554', - "coprod;": '\U00002210', - "copy;": '\U000000A9', - "copysr;": '\U00002117', - "crarr;": '\U000021B5', - "cross;": '\U00002717', - "cscr;": '\U0001D4B8', - "csub;": '\U00002ACF', - "csube;": '\U00002AD1', - "csup;": '\U00002AD0', - "csupe;": '\U00002AD2', - "ctdot;": '\U000022EF', - "cudarrl;": '\U00002938', - "cudarrr;": '\U00002935', - "cuepr;": '\U000022DE', - "cuesc;": '\U000022DF', - "cularr;": '\U000021B6', - "cularrp;": '\U0000293D', - "cup;": '\U0000222A', - "cupbrcap;": '\U00002A48', - "cupcap;": '\U00002A46', - "cupcup;": '\U00002A4A', - "cupdot;": '\U0000228D', - "cupor;": '\U00002A45', - "curarr;": '\U000021B7', - "curarrm;": '\U0000293C', - "curlyeqprec;": '\U000022DE', - "curlyeqsucc;": '\U000022DF', - "curlyvee;": '\U000022CE', - "curlywedge;": '\U000022CF', - "curren;": '\U000000A4', - "curvearrowleft;": '\U000021B6', - "curvearrowright;": '\U000021B7', - "cuvee;": '\U000022CE', - "cuwed;": '\U000022CF', - "cwconint;": '\U00002232', - "cwint;": '\U00002231', - "cylcty;": '\U0000232D', - "dArr;": '\U000021D3', - "dHar;": '\U00002965', - "dagger;": '\U00002020', - "daleth;": '\U00002138', - "darr;": '\U00002193', - "dash;": '\U00002010', - "dashv;": '\U000022A3', - "dbkarow;": '\U0000290F', - "dblac;": '\U000002DD', - "dcaron;": '\U0000010F', - "dcy;": '\U00000434', - "dd;": '\U00002146', - "ddagger;": '\U00002021', - "ddarr;": '\U000021CA', - "ddotseq;": '\U00002A77', - "deg;": '\U000000B0', - "delta;": '\U000003B4', - "demptyv;": '\U000029B1', - "dfisht;": '\U0000297F', - "dfr;": '\U0001D521', - "dharl;": '\U000021C3', - "dharr;": '\U000021C2', - "diam;": '\U000022C4', - "diamond;": '\U000022C4', - "diamondsuit;": '\U00002666', - "diams;": '\U00002666', - "die;": '\U000000A8', - "digamma;": '\U000003DD', - "disin;": '\U000022F2', - "div;": '\U000000F7', - "divide;": '\U000000F7', - "divideontimes;": '\U000022C7', - "divonx;": '\U000022C7', - "djcy;": '\U00000452', - "dlcorn;": '\U0000231E', - "dlcrop;": '\U0000230D', - "dollar;": '\U00000024', - "dopf;": '\U0001D555', - "dot;": '\U000002D9', - "doteq;": '\U00002250', - "doteqdot;": '\U00002251', - "dotminus;": '\U00002238', - "dotplus;": '\U00002214', - "dotsquare;": '\U000022A1', - "doublebarwedge;": '\U00002306', - "downarrow;": '\U00002193', - "downdownarrows;": '\U000021CA', - "downharpoonleft;": '\U000021C3', - "downharpoonright;": '\U000021C2', - "drbkarow;": '\U00002910', - "drcorn;": '\U0000231F', - "drcrop;": '\U0000230C', - "dscr;": '\U0001D4B9', - "dscy;": '\U00000455', - "dsol;": '\U000029F6', - "dstrok;": '\U00000111', - "dtdot;": '\U000022F1', - "dtri;": '\U000025BF', - "dtrif;": '\U000025BE', - "duarr;": '\U000021F5', - "duhar;": '\U0000296F', - "dwangle;": '\U000029A6', - "dzcy;": '\U0000045F', - "dzigrarr;": '\U000027FF', - "eDDot;": '\U00002A77', - "eDot;": '\U00002251', - "eacute;": '\U000000E9', - "easter;": '\U00002A6E', - "ecaron;": '\U0000011B', - "ecir;": '\U00002256', - "ecirc;": '\U000000EA', - "ecolon;": '\U00002255', - "ecy;": '\U0000044D', - "edot;": '\U00000117', - "ee;": '\U00002147', - "efDot;": '\U00002252', - "efr;": '\U0001D522', - "eg;": '\U00002A9A', - "egrave;": '\U000000E8', - "egs;": '\U00002A96', - "egsdot;": '\U00002A98', - "el;": '\U00002A99', - "elinters;": '\U000023E7', - "ell;": '\U00002113', - "els;": '\U00002A95', - "elsdot;": '\U00002A97', - "emacr;": '\U00000113', - "empty;": '\U00002205', - "emptyset;": '\U00002205', - "emptyv;": '\U00002205', - "emsp;": '\U00002003', - "emsp13;": '\U00002004', - "emsp14;": '\U00002005', - "eng;": '\U0000014B', - "ensp;": '\U00002002', - "eogon;": '\U00000119', - "eopf;": '\U0001D556', - "epar;": '\U000022D5', - "eparsl;": '\U000029E3', - "eplus;": '\U00002A71', - "epsi;": '\U000003B5', - "epsilon;": '\U000003B5', - "epsiv;": '\U000003F5', - "eqcirc;": '\U00002256', - "eqcolon;": '\U00002255', - "eqsim;": '\U00002242', - "eqslantgtr;": '\U00002A96', - "eqslantless;": '\U00002A95', - "equals;": '\U0000003D', - "equest;": '\U0000225F', - "equiv;": '\U00002261', - "equivDD;": '\U00002A78', - "eqvparsl;": '\U000029E5', - "erDot;": '\U00002253', - "erarr;": '\U00002971', - "escr;": '\U0000212F', - "esdot;": '\U00002250', - "esim;": '\U00002242', - "eta;": '\U000003B7', - "eth;": '\U000000F0', - "euml;": '\U000000EB', - "euro;": '\U000020AC', - "excl;": '\U00000021', - "exist;": '\U00002203', - "expectation;": '\U00002130', - "exponentiale;": '\U00002147', - "fallingdotseq;": '\U00002252', - "fcy;": '\U00000444', - "female;": '\U00002640', - "ffilig;": '\U0000FB03', - "fflig;": '\U0000FB00', - "ffllig;": '\U0000FB04', - "ffr;": '\U0001D523', - "filig;": '\U0000FB01', - "flat;": '\U0000266D', - "fllig;": '\U0000FB02', - "fltns;": '\U000025B1', - "fnof;": '\U00000192', - "fopf;": '\U0001D557', - "forall;": '\U00002200', - "fork;": '\U000022D4', - "forkv;": '\U00002AD9', - "fpartint;": '\U00002A0D', - "frac12;": '\U000000BD', - "frac13;": '\U00002153', - "frac14;": '\U000000BC', - "frac15;": '\U00002155', - "frac16;": '\U00002159', - "frac18;": '\U0000215B', - "frac23;": '\U00002154', - "frac25;": '\U00002156', - "frac34;": '\U000000BE', - "frac35;": '\U00002157', - "frac38;": '\U0000215C', - "frac45;": '\U00002158', - "frac56;": '\U0000215A', - "frac58;": '\U0000215D', - "frac78;": '\U0000215E', - "frasl;": '\U00002044', - "frown;": '\U00002322', - "fscr;": '\U0001D4BB', - "gE;": '\U00002267', - "gEl;": '\U00002A8C', - "gacute;": '\U000001F5', - "gamma;": '\U000003B3', - "gammad;": '\U000003DD', - "gap;": '\U00002A86', - "gbreve;": '\U0000011F', - "gcirc;": '\U0000011D', - "gcy;": '\U00000433', - "gdot;": '\U00000121', - "ge;": '\U00002265', - "gel;": '\U000022DB', - "geq;": '\U00002265', - "geqq;": '\U00002267', - "geqslant;": '\U00002A7E', - "ges;": '\U00002A7E', - "gescc;": '\U00002AA9', - "gesdot;": '\U00002A80', - "gesdoto;": '\U00002A82', - "gesdotol;": '\U00002A84', - "gesles;": '\U00002A94', - "gfr;": '\U0001D524', - "gg;": '\U0000226B', - "ggg;": '\U000022D9', - "gimel;": '\U00002137', - "gjcy;": '\U00000453', - "gl;": '\U00002277', - "glE;": '\U00002A92', - "gla;": '\U00002AA5', - "glj;": '\U00002AA4', - "gnE;": '\U00002269', - "gnap;": '\U00002A8A', - "gnapprox;": '\U00002A8A', - "gne;": '\U00002A88', - "gneq;": '\U00002A88', - "gneqq;": '\U00002269', - "gnsim;": '\U000022E7', - "gopf;": '\U0001D558', - "grave;": '\U00000060', - "gscr;": '\U0000210A', - "gsim;": '\U00002273', - "gsime;": '\U00002A8E', - "gsiml;": '\U00002A90', - "gt;": '\U0000003E', - "gtcc;": '\U00002AA7', - "gtcir;": '\U00002A7A', - "gtdot;": '\U000022D7', - "gtlPar;": '\U00002995', - "gtquest;": '\U00002A7C', - "gtrapprox;": '\U00002A86', - "gtrarr;": '\U00002978', - "gtrdot;": '\U000022D7', - "gtreqless;": '\U000022DB', - "gtreqqless;": '\U00002A8C', - "gtrless;": '\U00002277', - "gtrsim;": '\U00002273', - "hArr;": '\U000021D4', - "hairsp;": '\U0000200A', - "half;": '\U000000BD', - "hamilt;": '\U0000210B', - "hardcy;": '\U0000044A', - "harr;": '\U00002194', - "harrcir;": '\U00002948', - "harrw;": '\U000021AD', - "hbar;": '\U0000210F', - "hcirc;": '\U00000125', - "hearts;": '\U00002665', - "heartsuit;": '\U00002665', - "hellip;": '\U00002026', - "hercon;": '\U000022B9', - "hfr;": '\U0001D525', - "hksearow;": '\U00002925', - "hkswarow;": '\U00002926', - "hoarr;": '\U000021FF', - "homtht;": '\U0000223B', - "hookleftarrow;": '\U000021A9', - "hookrightarrow;": '\U000021AA', - "hopf;": '\U0001D559', - "horbar;": '\U00002015', - "hscr;": '\U0001D4BD', - "hslash;": '\U0000210F', - "hstrok;": '\U00000127', - "hybull;": '\U00002043', - "hyphen;": '\U00002010', - "iacute;": '\U000000ED', - "ic;": '\U00002063', - "icirc;": '\U000000EE', - "icy;": '\U00000438', - "iecy;": '\U00000435', - "iexcl;": '\U000000A1', - "iff;": '\U000021D4', - "ifr;": '\U0001D526', - "igrave;": '\U000000EC', - "ii;": '\U00002148', - "iiiint;": '\U00002A0C', - "iiint;": '\U0000222D', - "iinfin;": '\U000029DC', - "iiota;": '\U00002129', - "ijlig;": '\U00000133', - "imacr;": '\U0000012B', - "image;": '\U00002111', - "imagline;": '\U00002110', - "imagpart;": '\U00002111', - "imath;": '\U00000131', - "imof;": '\U000022B7', - "imped;": '\U000001B5', - "in;": '\U00002208', - "incare;": '\U00002105', - "infin;": '\U0000221E', - "infintie;": '\U000029DD', - "inodot;": '\U00000131', - "int;": '\U0000222B', - "intcal;": '\U000022BA', - "integers;": '\U00002124', - "intercal;": '\U000022BA', - "intlarhk;": '\U00002A17', - "intprod;": '\U00002A3C', - "iocy;": '\U00000451', - "iogon;": '\U0000012F', - "iopf;": '\U0001D55A', - "iota;": '\U000003B9', - "iprod;": '\U00002A3C', - "iquest;": '\U000000BF', - "iscr;": '\U0001D4BE', - "isin;": '\U00002208', - "isinE;": '\U000022F9', - "isindot;": '\U000022F5', - "isins;": '\U000022F4', - "isinsv;": '\U000022F3', - "isinv;": '\U00002208', - "it;": '\U00002062', - "itilde;": '\U00000129', - "iukcy;": '\U00000456', - "iuml;": '\U000000EF', - "jcirc;": '\U00000135', - "jcy;": '\U00000439', - "jfr;": '\U0001D527', - "jmath;": '\U00000237', - "jopf;": '\U0001D55B', - "jscr;": '\U0001D4BF', - "jsercy;": '\U00000458', - "jukcy;": '\U00000454', - "kappa;": '\U000003BA', - "kappav;": '\U000003F0', - "kcedil;": '\U00000137', - "kcy;": '\U0000043A', - "kfr;": '\U0001D528', - "kgreen;": '\U00000138', - "khcy;": '\U00000445', - "kjcy;": '\U0000045C', - "kopf;": '\U0001D55C', - "kscr;": '\U0001D4C0', - "lAarr;": '\U000021DA', - "lArr;": '\U000021D0', - "lAtail;": '\U0000291B', - "lBarr;": '\U0000290E', - "lE;": '\U00002266', - "lEg;": '\U00002A8B', - "lHar;": '\U00002962', - "lacute;": '\U0000013A', - "laemptyv;": '\U000029B4', - "lagran;": '\U00002112', - "lambda;": '\U000003BB', - "lang;": '\U000027E8', - "langd;": '\U00002991', - "langle;": '\U000027E8', - "lap;": '\U00002A85', - "laquo;": '\U000000AB', - "larr;": '\U00002190', - "larrb;": '\U000021E4', - "larrbfs;": '\U0000291F', - "larrfs;": '\U0000291D', - "larrhk;": '\U000021A9', - "larrlp;": '\U000021AB', - "larrpl;": '\U00002939', - "larrsim;": '\U00002973', - "larrtl;": '\U000021A2', - "lat;": '\U00002AAB', - "latail;": '\U00002919', - "late;": '\U00002AAD', - "lbarr;": '\U0000290C', - "lbbrk;": '\U00002772', - "lbrace;": '\U0000007B', - "lbrack;": '\U0000005B', - "lbrke;": '\U0000298B', - "lbrksld;": '\U0000298F', - "lbrkslu;": '\U0000298D', - "lcaron;": '\U0000013E', - "lcedil;": '\U0000013C', - "lceil;": '\U00002308', - "lcub;": '\U0000007B', - "lcy;": '\U0000043B', - "ldca;": '\U00002936', - "ldquo;": '\U0000201C', - "ldquor;": '\U0000201E', - "ldrdhar;": '\U00002967', - "ldrushar;": '\U0000294B', - "ldsh;": '\U000021B2', - "le;": '\U00002264', - "leftarrow;": '\U00002190', - "leftarrowtail;": '\U000021A2', - "leftharpoondown;": '\U000021BD', - "leftharpoonup;": '\U000021BC', - "leftleftarrows;": '\U000021C7', - "leftrightarrow;": '\U00002194', - "leftrightarrows;": '\U000021C6', - "leftrightharpoons;": '\U000021CB', - "leftrightsquigarrow;": '\U000021AD', - "leftthreetimes;": '\U000022CB', - "leg;": '\U000022DA', - "leq;": '\U00002264', - "leqq;": '\U00002266', - "leqslant;": '\U00002A7D', - "les;": '\U00002A7D', - "lescc;": '\U00002AA8', - "lesdot;": '\U00002A7F', - "lesdoto;": '\U00002A81', - "lesdotor;": '\U00002A83', - "lesges;": '\U00002A93', - "lessapprox;": '\U00002A85', - "lessdot;": '\U000022D6', - "lesseqgtr;": '\U000022DA', - "lesseqqgtr;": '\U00002A8B', - "lessgtr;": '\U00002276', - "lesssim;": '\U00002272', - "lfisht;": '\U0000297C', - "lfloor;": '\U0000230A', - "lfr;": '\U0001D529', - "lg;": '\U00002276', - "lgE;": '\U00002A91', - "lhard;": '\U000021BD', - "lharu;": '\U000021BC', - "lharul;": '\U0000296A', - "lhblk;": '\U00002584', - "ljcy;": '\U00000459', - "ll;": '\U0000226A', - "llarr;": '\U000021C7', - "llcorner;": '\U0000231E', - "llhard;": '\U0000296B', - "lltri;": '\U000025FA', - "lmidot;": '\U00000140', - "lmoust;": '\U000023B0', - "lmoustache;": '\U000023B0', - "lnE;": '\U00002268', - "lnap;": '\U00002A89', - "lnapprox;": '\U00002A89', - "lne;": '\U00002A87', - "lneq;": '\U00002A87', - "lneqq;": '\U00002268', - "lnsim;": '\U000022E6', - "loang;": '\U000027EC', - "loarr;": '\U000021FD', - "lobrk;": '\U000027E6', - "longleftarrow;": '\U000027F5', - "longleftrightarrow;": '\U000027F7', - "longmapsto;": '\U000027FC', - "longrightarrow;": '\U000027F6', - "looparrowleft;": '\U000021AB', - "looparrowright;": '\U000021AC', - "lopar;": '\U00002985', - "lopf;": '\U0001D55D', - "loplus;": '\U00002A2D', - "lotimes;": '\U00002A34', - "lowast;": '\U00002217', - "lowbar;": '\U0000005F', - "loz;": '\U000025CA', - "lozenge;": '\U000025CA', - "lozf;": '\U000029EB', - "lpar;": '\U00000028', - "lparlt;": '\U00002993', - "lrarr;": '\U000021C6', - "lrcorner;": '\U0000231F', - "lrhar;": '\U000021CB', - "lrhard;": '\U0000296D', - "lrm;": '\U0000200E', - "lrtri;": '\U000022BF', - "lsaquo;": '\U00002039', - "lscr;": '\U0001D4C1', - "lsh;": '\U000021B0', - "lsim;": '\U00002272', - "lsime;": '\U00002A8D', - "lsimg;": '\U00002A8F', - "lsqb;": '\U0000005B', - "lsquo;": '\U00002018', - "lsquor;": '\U0000201A', - "lstrok;": '\U00000142', - "lt;": '\U0000003C', - "ltcc;": '\U00002AA6', - "ltcir;": '\U00002A79', - "ltdot;": '\U000022D6', - "lthree;": '\U000022CB', - "ltimes;": '\U000022C9', - "ltlarr;": '\U00002976', - "ltquest;": '\U00002A7B', - "ltrPar;": '\U00002996', - "ltri;": '\U000025C3', - "ltrie;": '\U000022B4', - "ltrif;": '\U000025C2', - "lurdshar;": '\U0000294A', - "luruhar;": '\U00002966', - "mDDot;": '\U0000223A', - "macr;": '\U000000AF', - "male;": '\U00002642', - "malt;": '\U00002720', - "maltese;": '\U00002720', - "map;": '\U000021A6', - "mapsto;": '\U000021A6', - "mapstodown;": '\U000021A7', - "mapstoleft;": '\U000021A4', - "mapstoup;": '\U000021A5', - "marker;": '\U000025AE', - "mcomma;": '\U00002A29', - "mcy;": '\U0000043C', - "mdash;": '\U00002014', - "measuredangle;": '\U00002221', - "mfr;": '\U0001D52A', - "mho;": '\U00002127', - "micro;": '\U000000B5', - "mid;": '\U00002223', - "midast;": '\U0000002A', - "midcir;": '\U00002AF0', - "middot;": '\U000000B7', - "minus;": '\U00002212', - "minusb;": '\U0000229F', - "minusd;": '\U00002238', - "minusdu;": '\U00002A2A', - "mlcp;": '\U00002ADB', - "mldr;": '\U00002026', - "mnplus;": '\U00002213', - "models;": '\U000022A7', - "mopf;": '\U0001D55E', - "mp;": '\U00002213', - "mscr;": '\U0001D4C2', - "mstpos;": '\U0000223E', - "mu;": '\U000003BC', - "multimap;": '\U000022B8', - "mumap;": '\U000022B8', - "nLeftarrow;": '\U000021CD', - "nLeftrightarrow;": '\U000021CE', - "nRightarrow;": '\U000021CF', - "nVDash;": '\U000022AF', - "nVdash;": '\U000022AE', - "nabla;": '\U00002207', - "nacute;": '\U00000144', - "nap;": '\U00002249', - "napos;": '\U00000149', - "napprox;": '\U00002249', - "natur;": '\U0000266E', - "natural;": '\U0000266E', - "naturals;": '\U00002115', - "nbsp;": '\U000000A0', - "ncap;": '\U00002A43', - "ncaron;": '\U00000148', - "ncedil;": '\U00000146', - "ncong;": '\U00002247', - "ncup;": '\U00002A42', - "ncy;": '\U0000043D', - "ndash;": '\U00002013', - "ne;": '\U00002260', - "neArr;": '\U000021D7', - "nearhk;": '\U00002924', - "nearr;": '\U00002197', - "nearrow;": '\U00002197', - "nequiv;": '\U00002262', - "nesear;": '\U00002928', - "nexist;": '\U00002204', - "nexists;": '\U00002204', - "nfr;": '\U0001D52B', - "nge;": '\U00002271', - "ngeq;": '\U00002271', - "ngsim;": '\U00002275', - "ngt;": '\U0000226F', - "ngtr;": '\U0000226F', - "nhArr;": '\U000021CE', - "nharr;": '\U000021AE', - "nhpar;": '\U00002AF2', - "ni;": '\U0000220B', - "nis;": '\U000022FC', - "nisd;": '\U000022FA', - "niv;": '\U0000220B', - "njcy;": '\U0000045A', - "nlArr;": '\U000021CD', - "nlarr;": '\U0000219A', - "nldr;": '\U00002025', - "nle;": '\U00002270', - "nleftarrow;": '\U0000219A', - "nleftrightarrow;": '\U000021AE', - "nleq;": '\U00002270', - "nless;": '\U0000226E', - "nlsim;": '\U00002274', - "nlt;": '\U0000226E', - "nltri;": '\U000022EA', - "nltrie;": '\U000022EC', - "nmid;": '\U00002224', - "nopf;": '\U0001D55F', - "not;": '\U000000AC', - "notin;": '\U00002209', - "notinva;": '\U00002209', - "notinvb;": '\U000022F7', - "notinvc;": '\U000022F6', - "notni;": '\U0000220C', - "notniva;": '\U0000220C', - "notnivb;": '\U000022FE', - "notnivc;": '\U000022FD', - "npar;": '\U00002226', - "nparallel;": '\U00002226', - "npolint;": '\U00002A14', - "npr;": '\U00002280', - "nprcue;": '\U000022E0', - "nprec;": '\U00002280', - "nrArr;": '\U000021CF', - "nrarr;": '\U0000219B', - "nrightarrow;": '\U0000219B', - "nrtri;": '\U000022EB', - "nrtrie;": '\U000022ED', - "nsc;": '\U00002281', - "nsccue;": '\U000022E1', - "nscr;": '\U0001D4C3', - "nshortmid;": '\U00002224', - "nshortparallel;": '\U00002226', - "nsim;": '\U00002241', - "nsime;": '\U00002244', - "nsimeq;": '\U00002244', - "nsmid;": '\U00002224', - "nspar;": '\U00002226', - "nsqsube;": '\U000022E2', - "nsqsupe;": '\U000022E3', - "nsub;": '\U00002284', - "nsube;": '\U00002288', - "nsubseteq;": '\U00002288', - "nsucc;": '\U00002281', - "nsup;": '\U00002285', - "nsupe;": '\U00002289', - "nsupseteq;": '\U00002289', - "ntgl;": '\U00002279', - "ntilde;": '\U000000F1', - "ntlg;": '\U00002278', - "ntriangleleft;": '\U000022EA', - "ntrianglelefteq;": '\U000022EC', - "ntriangleright;": '\U000022EB', - "ntrianglerighteq;": '\U000022ED', - "nu;": '\U000003BD', - "num;": '\U00000023', - "numero;": '\U00002116', - "numsp;": '\U00002007', - "nvDash;": '\U000022AD', - "nvHarr;": '\U00002904', - "nvdash;": '\U000022AC', - "nvinfin;": '\U000029DE', - "nvlArr;": '\U00002902', - "nvrArr;": '\U00002903', - "nwArr;": '\U000021D6', - "nwarhk;": '\U00002923', - "nwarr;": '\U00002196', - "nwarrow;": '\U00002196', - "nwnear;": '\U00002927', - "oS;": '\U000024C8', - "oacute;": '\U000000F3', - "oast;": '\U0000229B', - "ocir;": '\U0000229A', - "ocirc;": '\U000000F4', - "ocy;": '\U0000043E', - "odash;": '\U0000229D', - "odblac;": '\U00000151', - "odiv;": '\U00002A38', - "odot;": '\U00002299', - "odsold;": '\U000029BC', - "oelig;": '\U00000153', - "ofcir;": '\U000029BF', - "ofr;": '\U0001D52C', - "ogon;": '\U000002DB', - "ograve;": '\U000000F2', - "ogt;": '\U000029C1', - "ohbar;": '\U000029B5', - "ohm;": '\U000003A9', - "oint;": '\U0000222E', - "olarr;": '\U000021BA', - "olcir;": '\U000029BE', - "olcross;": '\U000029BB', - "oline;": '\U0000203E', - "olt;": '\U000029C0', - "omacr;": '\U0000014D', - "omega;": '\U000003C9', - "omicron;": '\U000003BF', - "omid;": '\U000029B6', - "ominus;": '\U00002296', - "oopf;": '\U0001D560', - "opar;": '\U000029B7', - "operp;": '\U000029B9', - "oplus;": '\U00002295', - "or;": '\U00002228', - "orarr;": '\U000021BB', - "ord;": '\U00002A5D', - "order;": '\U00002134', - "orderof;": '\U00002134', - "ordf;": '\U000000AA', - "ordm;": '\U000000BA', - "origof;": '\U000022B6', - "oror;": '\U00002A56', - "orslope;": '\U00002A57', - "orv;": '\U00002A5B', - "oscr;": '\U00002134', - "oslash;": '\U000000F8', - "osol;": '\U00002298', - "otilde;": '\U000000F5', - "otimes;": '\U00002297', - "otimesas;": '\U00002A36', - "ouml;": '\U000000F6', - "ovbar;": '\U0000233D', - "par;": '\U00002225', - "para;": '\U000000B6', - "parallel;": '\U00002225', - "parsim;": '\U00002AF3', - "parsl;": '\U00002AFD', - "part;": '\U00002202', - "pcy;": '\U0000043F', - "percnt;": '\U00000025', - "period;": '\U0000002E', - "permil;": '\U00002030', - "perp;": '\U000022A5', - "pertenk;": '\U00002031', - "pfr;": '\U0001D52D', - "phi;": '\U000003C6', - "phiv;": '\U000003D5', - "phmmat;": '\U00002133', - "phone;": '\U0000260E', - "pi;": '\U000003C0', - "pitchfork;": '\U000022D4', - "piv;": '\U000003D6', - "planck;": '\U0000210F', - "planckh;": '\U0000210E', - "plankv;": '\U0000210F', - "plus;": '\U0000002B', - "plusacir;": '\U00002A23', - "plusb;": '\U0000229E', - "pluscir;": '\U00002A22', - "plusdo;": '\U00002214', - "plusdu;": '\U00002A25', - "pluse;": '\U00002A72', - "plusmn;": '\U000000B1', - "plussim;": '\U00002A26', - "plustwo;": '\U00002A27', - "pm;": '\U000000B1', - "pointint;": '\U00002A15', - "popf;": '\U0001D561', - "pound;": '\U000000A3', - "pr;": '\U0000227A', - "prE;": '\U00002AB3', - "prap;": '\U00002AB7', - "prcue;": '\U0000227C', - "pre;": '\U00002AAF', - "prec;": '\U0000227A', - "precapprox;": '\U00002AB7', - "preccurlyeq;": '\U0000227C', - "preceq;": '\U00002AAF', - "precnapprox;": '\U00002AB9', - "precneqq;": '\U00002AB5', - "precnsim;": '\U000022E8', - "precsim;": '\U0000227E', - "prime;": '\U00002032', - "primes;": '\U00002119', - "prnE;": '\U00002AB5', - "prnap;": '\U00002AB9', - "prnsim;": '\U000022E8', - "prod;": '\U0000220F', - "profalar;": '\U0000232E', - "profline;": '\U00002312', - "profsurf;": '\U00002313', - "prop;": '\U0000221D', - "propto;": '\U0000221D', - "prsim;": '\U0000227E', - "prurel;": '\U000022B0', - "pscr;": '\U0001D4C5', - "psi;": '\U000003C8', - "puncsp;": '\U00002008', - "qfr;": '\U0001D52E', - "qint;": '\U00002A0C', - "qopf;": '\U0001D562', - "qprime;": '\U00002057', - "qscr;": '\U0001D4C6', - "quaternions;": '\U0000210D', - "quatint;": '\U00002A16', - "quest;": '\U0000003F', - "questeq;": '\U0000225F', - "quot;": '\U00000022', - "rAarr;": '\U000021DB', - "rArr;": '\U000021D2', - "rAtail;": '\U0000291C', - "rBarr;": '\U0000290F', - "rHar;": '\U00002964', - "racute;": '\U00000155', - "radic;": '\U0000221A', - "raemptyv;": '\U000029B3', - "rang;": '\U000027E9', - "rangd;": '\U00002992', - "range;": '\U000029A5', - "rangle;": '\U000027E9', - "raquo;": '\U000000BB', - "rarr;": '\U00002192', - "rarrap;": '\U00002975', - "rarrb;": '\U000021E5', - "rarrbfs;": '\U00002920', - "rarrc;": '\U00002933', - "rarrfs;": '\U0000291E', - "rarrhk;": '\U000021AA', - "rarrlp;": '\U000021AC', - "rarrpl;": '\U00002945', - "rarrsim;": '\U00002974', - "rarrtl;": '\U000021A3', - "rarrw;": '\U0000219D', - "ratail;": '\U0000291A', - "ratio;": '\U00002236', - "rationals;": '\U0000211A', - "rbarr;": '\U0000290D', - "rbbrk;": '\U00002773', - "rbrace;": '\U0000007D', - "rbrack;": '\U0000005D', - "rbrke;": '\U0000298C', - "rbrksld;": '\U0000298E', - "rbrkslu;": '\U00002990', - "rcaron;": '\U00000159', - "rcedil;": '\U00000157', - "rceil;": '\U00002309', - "rcub;": '\U0000007D', - "rcy;": '\U00000440', - "rdca;": '\U00002937', - "rdldhar;": '\U00002969', - "rdquo;": '\U0000201D', - "rdquor;": '\U0000201D', - "rdsh;": '\U000021B3', - "real;": '\U0000211C', - "realine;": '\U0000211B', - "realpart;": '\U0000211C', - "reals;": '\U0000211D', - "rect;": '\U000025AD', - "reg;": '\U000000AE', - "rfisht;": '\U0000297D', - "rfloor;": '\U0000230B', - "rfr;": '\U0001D52F', - "rhard;": '\U000021C1', - "rharu;": '\U000021C0', - "rharul;": '\U0000296C', - "rho;": '\U000003C1', - "rhov;": '\U000003F1', - "rightarrow;": '\U00002192', - "rightarrowtail;": '\U000021A3', - "rightharpoondown;": '\U000021C1', - "rightharpoonup;": '\U000021C0', - "rightleftarrows;": '\U000021C4', - "rightleftharpoons;": '\U000021CC', - "rightrightarrows;": '\U000021C9', - "rightsquigarrow;": '\U0000219D', - "rightthreetimes;": '\U000022CC', - "ring;": '\U000002DA', - "risingdotseq;": '\U00002253', - "rlarr;": '\U000021C4', - "rlhar;": '\U000021CC', - "rlm;": '\U0000200F', - "rmoust;": '\U000023B1', - "rmoustache;": '\U000023B1', - "rnmid;": '\U00002AEE', - "roang;": '\U000027ED', - "roarr;": '\U000021FE', - "robrk;": '\U000027E7', - "ropar;": '\U00002986', - "ropf;": '\U0001D563', - "roplus;": '\U00002A2E', - "rotimes;": '\U00002A35', - "rpar;": '\U00000029', - "rpargt;": '\U00002994', - "rppolint;": '\U00002A12', - "rrarr;": '\U000021C9', - "rsaquo;": '\U0000203A', - "rscr;": '\U0001D4C7', - "rsh;": '\U000021B1', - "rsqb;": '\U0000005D', - "rsquo;": '\U00002019', - "rsquor;": '\U00002019', - "rthree;": '\U000022CC', - "rtimes;": '\U000022CA', - "rtri;": '\U000025B9', - "rtrie;": '\U000022B5', - "rtrif;": '\U000025B8', - "rtriltri;": '\U000029CE', - "ruluhar;": '\U00002968', - "rx;": '\U0000211E', - "sacute;": '\U0000015B', - "sbquo;": '\U0000201A', - "sc;": '\U0000227B', - "scE;": '\U00002AB4', - "scap;": '\U00002AB8', - "scaron;": '\U00000161', - "sccue;": '\U0000227D', - "sce;": '\U00002AB0', - "scedil;": '\U0000015F', - "scirc;": '\U0000015D', - "scnE;": '\U00002AB6', - "scnap;": '\U00002ABA', - "scnsim;": '\U000022E9', - "scpolint;": '\U00002A13', - "scsim;": '\U0000227F', - "scy;": '\U00000441', - "sdot;": '\U000022C5', - "sdotb;": '\U000022A1', - "sdote;": '\U00002A66', - "seArr;": '\U000021D8', - "searhk;": '\U00002925', - "searr;": '\U00002198', - "searrow;": '\U00002198', - "sect;": '\U000000A7', - "semi;": '\U0000003B', - "seswar;": '\U00002929', - "setminus;": '\U00002216', - "setmn;": '\U00002216', - "sext;": '\U00002736', - "sfr;": '\U0001D530', - "sfrown;": '\U00002322', - "sharp;": '\U0000266F', - "shchcy;": '\U00000449', - "shcy;": '\U00000448', - "shortmid;": '\U00002223', - "shortparallel;": '\U00002225', - "shy;": '\U000000AD', - "sigma;": '\U000003C3', - "sigmaf;": '\U000003C2', - "sigmav;": '\U000003C2', - "sim;": '\U0000223C', - "simdot;": '\U00002A6A', - "sime;": '\U00002243', - "simeq;": '\U00002243', - "simg;": '\U00002A9E', - "simgE;": '\U00002AA0', - "siml;": '\U00002A9D', - "simlE;": '\U00002A9F', - "simne;": '\U00002246', - "simplus;": '\U00002A24', - "simrarr;": '\U00002972', - "slarr;": '\U00002190', - "smallsetminus;": '\U00002216', - "smashp;": '\U00002A33', - "smeparsl;": '\U000029E4', - "smid;": '\U00002223', - "smile;": '\U00002323', - "smt;": '\U00002AAA', - "smte;": '\U00002AAC', - "softcy;": '\U0000044C', - "sol;": '\U0000002F', - "solb;": '\U000029C4', - "solbar;": '\U0000233F', - "sopf;": '\U0001D564', - "spades;": '\U00002660', - "spadesuit;": '\U00002660', - "spar;": '\U00002225', - "sqcap;": '\U00002293', - "sqcup;": '\U00002294', - "sqsub;": '\U0000228F', - "sqsube;": '\U00002291', - "sqsubset;": '\U0000228F', - "sqsubseteq;": '\U00002291', - "sqsup;": '\U00002290', - "sqsupe;": '\U00002292', - "sqsupset;": '\U00002290', - "sqsupseteq;": '\U00002292', - "squ;": '\U000025A1', - "square;": '\U000025A1', - "squarf;": '\U000025AA', - "squf;": '\U000025AA', - "srarr;": '\U00002192', - "sscr;": '\U0001D4C8', - "ssetmn;": '\U00002216', - "ssmile;": '\U00002323', - "sstarf;": '\U000022C6', - "star;": '\U00002606', - "starf;": '\U00002605', - "straightepsilon;": '\U000003F5', - "straightphi;": '\U000003D5', - "strns;": '\U000000AF', - "sub;": '\U00002282', - "subE;": '\U00002AC5', - "subdot;": '\U00002ABD', - "sube;": '\U00002286', - "subedot;": '\U00002AC3', - "submult;": '\U00002AC1', - "subnE;": '\U00002ACB', - "subne;": '\U0000228A', - "subplus;": '\U00002ABF', - "subrarr;": '\U00002979', - "subset;": '\U00002282', - "subseteq;": '\U00002286', - "subseteqq;": '\U00002AC5', - "subsetneq;": '\U0000228A', - "subsetneqq;": '\U00002ACB', - "subsim;": '\U00002AC7', - "subsub;": '\U00002AD5', - "subsup;": '\U00002AD3', - "succ;": '\U0000227B', - "succapprox;": '\U00002AB8', - "succcurlyeq;": '\U0000227D', - "succeq;": '\U00002AB0', - "succnapprox;": '\U00002ABA', - "succneqq;": '\U00002AB6', - "succnsim;": '\U000022E9', - "succsim;": '\U0000227F', - "sum;": '\U00002211', - "sung;": '\U0000266A', - "sup;": '\U00002283', - "sup1;": '\U000000B9', - "sup2;": '\U000000B2', - "sup3;": '\U000000B3', - "supE;": '\U00002AC6', - "supdot;": '\U00002ABE', - "supdsub;": '\U00002AD8', - "supe;": '\U00002287', - "supedot;": '\U00002AC4', - "suphsol;": '\U000027C9', - "suphsub;": '\U00002AD7', - "suplarr;": '\U0000297B', - "supmult;": '\U00002AC2', - "supnE;": '\U00002ACC', - "supne;": '\U0000228B', - "supplus;": '\U00002AC0', - "supset;": '\U00002283', - "supseteq;": '\U00002287', - "supseteqq;": '\U00002AC6', - "supsetneq;": '\U0000228B', - "supsetneqq;": '\U00002ACC', - "supsim;": '\U00002AC8', - "supsub;": '\U00002AD4', - "supsup;": '\U00002AD6', - "swArr;": '\U000021D9', - "swarhk;": '\U00002926', - "swarr;": '\U00002199', - "swarrow;": '\U00002199', - "swnwar;": '\U0000292A', - "szlig;": '\U000000DF', - "target;": '\U00002316', - "tau;": '\U000003C4', - "tbrk;": '\U000023B4', - "tcaron;": '\U00000165', - "tcedil;": '\U00000163', - "tcy;": '\U00000442', - "tdot;": '\U000020DB', - "telrec;": '\U00002315', - "tfr;": '\U0001D531', - "there4;": '\U00002234', - "therefore;": '\U00002234', - "theta;": '\U000003B8', - "thetasym;": '\U000003D1', - "thetav;": '\U000003D1', - "thickapprox;": '\U00002248', - "thicksim;": '\U0000223C', - "thinsp;": '\U00002009', - "thkap;": '\U00002248', - "thksim;": '\U0000223C', - "thorn;": '\U000000FE', - "tilde;": '\U000002DC', - "times;": '\U000000D7', - "timesb;": '\U000022A0', - "timesbar;": '\U00002A31', - "timesd;": '\U00002A30', - "tint;": '\U0000222D', - "toea;": '\U00002928', - "top;": '\U000022A4', - "topbot;": '\U00002336', - "topcir;": '\U00002AF1', - "topf;": '\U0001D565', - "topfork;": '\U00002ADA', - "tosa;": '\U00002929', - "tprime;": '\U00002034', - "trade;": '\U00002122', - "triangle;": '\U000025B5', - "triangledown;": '\U000025BF', - "triangleleft;": '\U000025C3', - "trianglelefteq;": '\U000022B4', - "triangleq;": '\U0000225C', - "triangleright;": '\U000025B9', - "trianglerighteq;": '\U000022B5', - "tridot;": '\U000025EC', - "trie;": '\U0000225C', - "triminus;": '\U00002A3A', - "triplus;": '\U00002A39', - "trisb;": '\U000029CD', - "tritime;": '\U00002A3B', - "trpezium;": '\U000023E2', - "tscr;": '\U0001D4C9', - "tscy;": '\U00000446', - "tshcy;": '\U0000045B', - "tstrok;": '\U00000167', - "twixt;": '\U0000226C', - "twoheadleftarrow;": '\U0000219E', - "twoheadrightarrow;": '\U000021A0', - "uArr;": '\U000021D1', - "uHar;": '\U00002963', - "uacute;": '\U000000FA', - "uarr;": '\U00002191', - "ubrcy;": '\U0000045E', - "ubreve;": '\U0000016D', - "ucirc;": '\U000000FB', - "ucy;": '\U00000443', - "udarr;": '\U000021C5', - "udblac;": '\U00000171', - "udhar;": '\U0000296E', - "ufisht;": '\U0000297E', - "ufr;": '\U0001D532', - "ugrave;": '\U000000F9', - "uharl;": '\U000021BF', - "uharr;": '\U000021BE', - "uhblk;": '\U00002580', - "ulcorn;": '\U0000231C', - "ulcorner;": '\U0000231C', - "ulcrop;": '\U0000230F', - "ultri;": '\U000025F8', - "umacr;": '\U0000016B', - "uml;": '\U000000A8', - "uogon;": '\U00000173', - "uopf;": '\U0001D566', - "uparrow;": '\U00002191', - "updownarrow;": '\U00002195', - "upharpoonleft;": '\U000021BF', - "upharpoonright;": '\U000021BE', - "uplus;": '\U0000228E', - "upsi;": '\U000003C5', - "upsih;": '\U000003D2', - "upsilon;": '\U000003C5', - "upuparrows;": '\U000021C8', - "urcorn;": '\U0000231D', - "urcorner;": '\U0000231D', - "urcrop;": '\U0000230E', - "uring;": '\U0000016F', - "urtri;": '\U000025F9', - "uscr;": '\U0001D4CA', - "utdot;": '\U000022F0', - "utilde;": '\U00000169', - "utri;": '\U000025B5', - "utrif;": '\U000025B4', - "uuarr;": '\U000021C8', - "uuml;": '\U000000FC', - "uwangle;": '\U000029A7', - "vArr;": '\U000021D5', - "vBar;": '\U00002AE8', - "vBarv;": '\U00002AE9', - "vDash;": '\U000022A8', - "vangrt;": '\U0000299C', - "varepsilon;": '\U000003F5', - "varkappa;": '\U000003F0', - "varnothing;": '\U00002205', - "varphi;": '\U000003D5', - "varpi;": '\U000003D6', - "varpropto;": '\U0000221D', - "varr;": '\U00002195', - "varrho;": '\U000003F1', - "varsigma;": '\U000003C2', - "vartheta;": '\U000003D1', - "vartriangleleft;": '\U000022B2', - "vartriangleright;": '\U000022B3', - "vcy;": '\U00000432', - "vdash;": '\U000022A2', - "vee;": '\U00002228', - "veebar;": '\U000022BB', - "veeeq;": '\U0000225A', - "vellip;": '\U000022EE', - "verbar;": '\U0000007C', - "vert;": '\U0000007C', - "vfr;": '\U0001D533', - "vltri;": '\U000022B2', - "vopf;": '\U0001D567', - "vprop;": '\U0000221D', - "vrtri;": '\U000022B3', - "vscr;": '\U0001D4CB', - "vzigzag;": '\U0000299A', - "wcirc;": '\U00000175', - "wedbar;": '\U00002A5F', - "wedge;": '\U00002227', - "wedgeq;": '\U00002259', - "weierp;": '\U00002118', - "wfr;": '\U0001D534', - "wopf;": '\U0001D568', - "wp;": '\U00002118', - "wr;": '\U00002240', - "wreath;": '\U00002240', - "wscr;": '\U0001D4CC', - "xcap;": '\U000022C2', - "xcirc;": '\U000025EF', - "xcup;": '\U000022C3', - "xdtri;": '\U000025BD', - "xfr;": '\U0001D535', - "xhArr;": '\U000027FA', - "xharr;": '\U000027F7', - "xi;": '\U000003BE', - "xlArr;": '\U000027F8', - "xlarr;": '\U000027F5', - "xmap;": '\U000027FC', - "xnis;": '\U000022FB', - "xodot;": '\U00002A00', - "xopf;": '\U0001D569', - "xoplus;": '\U00002A01', - "xotime;": '\U00002A02', - "xrArr;": '\U000027F9', - "xrarr;": '\U000027F6', - "xscr;": '\U0001D4CD', - "xsqcup;": '\U00002A06', - "xuplus;": '\U00002A04', - "xutri;": '\U000025B3', - "xvee;": '\U000022C1', - "xwedge;": '\U000022C0', - "yacute;": '\U000000FD', - "yacy;": '\U0000044F', - "ycirc;": '\U00000177', - "ycy;": '\U0000044B', - "yen;": '\U000000A5', - "yfr;": '\U0001D536', - "yicy;": '\U00000457', - "yopf;": '\U0001D56A', - "yscr;": '\U0001D4CE', - "yucy;": '\U0000044E', - "yuml;": '\U000000FF', - "zacute;": '\U0000017A', - "zcaron;": '\U0000017E', - "zcy;": '\U00000437', - "zdot;": '\U0000017C', - "zeetrf;": '\U00002128', - "zeta;": '\U000003B6', - "zfr;": '\U0001D537', - "zhcy;": '\U00000436', - "zigrarr;": '\U000021DD', - "zopf;": '\U0001D56B', - "zscr;": '\U0001D4CF', - "zwj;": '\U0000200D', - "zwnj;": '\U0000200C', - "AElig": '\U000000C6', - "AMP": '\U00000026', - "Aacute": '\U000000C1', - "Acirc": '\U000000C2', - "Agrave": '\U000000C0', - "Aring": '\U000000C5', - "Atilde": '\U000000C3', - "Auml": '\U000000C4', - "COPY": '\U000000A9', - "Ccedil": '\U000000C7', - "ETH": '\U000000D0', - "Eacute": '\U000000C9', - "Ecirc": '\U000000CA', - "Egrave": '\U000000C8', - "Euml": '\U000000CB', - "GT": '\U0000003E', - "Iacute": '\U000000CD', - "Icirc": '\U000000CE', - "Igrave": '\U000000CC', - "Iuml": '\U000000CF', - "LT": '\U0000003C', - "Ntilde": '\U000000D1', - "Oacute": '\U000000D3', - "Ocirc": '\U000000D4', - "Ograve": '\U000000D2', - "Oslash": '\U000000D8', - "Otilde": '\U000000D5', - "Ouml": '\U000000D6', - "QUOT": '\U00000022', - "REG": '\U000000AE', - "THORN": '\U000000DE', - "Uacute": '\U000000DA', - "Ucirc": '\U000000DB', - "Ugrave": '\U000000D9', - "Uuml": '\U000000DC', - "Yacute": '\U000000DD', - "aacute": '\U000000E1', - "acirc": '\U000000E2', - "acute": '\U000000B4', - "aelig": '\U000000E6', - "agrave": '\U000000E0', - "amp": '\U00000026', - "aring": '\U000000E5', - "atilde": '\U000000E3', - "auml": '\U000000E4', - "brvbar": '\U000000A6', - "ccedil": '\U000000E7', - "cedil": '\U000000B8', - "cent": '\U000000A2', - "copy": '\U000000A9', - "curren": '\U000000A4', - "deg": '\U000000B0', - "divide": '\U000000F7', - "eacute": '\U000000E9', - "ecirc": '\U000000EA', - "egrave": '\U000000E8', - "eth": '\U000000F0', - "euml": '\U000000EB', - "frac12": '\U000000BD', - "frac14": '\U000000BC', - "frac34": '\U000000BE', - "gt": '\U0000003E', - "iacute": '\U000000ED', - "icirc": '\U000000EE', - "iexcl": '\U000000A1', - "igrave": '\U000000EC', - "iquest": '\U000000BF', - "iuml": '\U000000EF', - "laquo": '\U000000AB', - "lt": '\U0000003C', - "macr": '\U000000AF', - "micro": '\U000000B5', - "middot": '\U000000B7', - "nbsp": '\U000000A0', - "not": '\U000000AC', - "ntilde": '\U000000F1', - "oacute": '\U000000F3', - "ocirc": '\U000000F4', - "ograve": '\U000000F2', - "ordf": '\U000000AA', - "ordm": '\U000000BA', - "oslash": '\U000000F8', - "otilde": '\U000000F5', - "ouml": '\U000000F6', - "para": '\U000000B6', - "plusmn": '\U000000B1', - "pound": '\U000000A3', - "quot": '\U00000022', - "raquo": '\U000000BB', - "reg": '\U000000AE', - "sect": '\U000000A7', - "shy": '\U000000AD', - "sup1": '\U000000B9', - "sup2": '\U000000B2', - "sup3": '\U000000B3', - "szlig": '\U000000DF', - "thorn": '\U000000FE', - "times": '\U000000D7', - "uacute": '\U000000FA', - "ucirc": '\U000000FB', - "ugrave": '\U000000F9', - "uml": '\U000000A8', - "uuml": '\U000000FC', - "yacute": '\U000000FD', - "yen": '\U000000A5', - "yuml": '\U000000FF', -} +var entity map[string]rune // HTML entities that are two unicode codepoints. -var entity2 = map[string][2]rune{ - // TODO(nigeltao): Handle replacements that are wider than their names. - // "nLt;": {'\u226A', '\u20D2'}, - // "nGt;": {'\u226B', '\u20D2'}, - "NotEqualTilde;": {'\u2242', '\u0338'}, - "NotGreaterFullEqual;": {'\u2267', '\u0338'}, - "NotGreaterGreater;": {'\u226B', '\u0338'}, - "NotGreaterSlantEqual;": {'\u2A7E', '\u0338'}, - "NotHumpDownHump;": {'\u224E', '\u0338'}, - "NotHumpEqual;": {'\u224F', '\u0338'}, - "NotLeftTriangleBar;": {'\u29CF', '\u0338'}, - "NotLessLess;": {'\u226A', '\u0338'}, - "NotLessSlantEqual;": {'\u2A7D', '\u0338'}, - "NotNestedGreaterGreater;": {'\u2AA2', '\u0338'}, - "NotNestedLessLess;": {'\u2AA1', '\u0338'}, - "NotPrecedesEqual;": {'\u2AAF', '\u0338'}, - "NotRightTriangleBar;": {'\u29D0', '\u0338'}, - "NotSquareSubset;": {'\u228F', '\u0338'}, - "NotSquareSuperset;": {'\u2290', '\u0338'}, - "NotSubset;": {'\u2282', '\u20D2'}, - "NotSucceedsEqual;": {'\u2AB0', '\u0338'}, - "NotSucceedsTilde;": {'\u227F', '\u0338'}, - "NotSuperset;": {'\u2283', '\u20D2'}, - "ThickSpace;": {'\u205F', '\u200A'}, - "acE;": {'\u223E', '\u0333'}, - "bne;": {'\u003D', '\u20E5'}, - "bnequiv;": {'\u2261', '\u20E5'}, - "caps;": {'\u2229', '\uFE00'}, - "cups;": {'\u222A', '\uFE00'}, - "fjlig;": {'\u0066', '\u006A'}, - "gesl;": {'\u22DB', '\uFE00'}, - "gvertneqq;": {'\u2269', '\uFE00'}, - "gvnE;": {'\u2269', '\uFE00'}, - "lates;": {'\u2AAD', '\uFE00'}, - "lesg;": {'\u22DA', '\uFE00'}, - "lvertneqq;": {'\u2268', '\uFE00'}, - "lvnE;": {'\u2268', '\uFE00'}, - "nGg;": {'\u22D9', '\u0338'}, - "nGtv;": {'\u226B', '\u0338'}, - "nLl;": {'\u22D8', '\u0338'}, - "nLtv;": {'\u226A', '\u0338'}, - "nang;": {'\u2220', '\u20D2'}, - "napE;": {'\u2A70', '\u0338'}, - "napid;": {'\u224B', '\u0338'}, - "nbump;": {'\u224E', '\u0338'}, - "nbumpe;": {'\u224F', '\u0338'}, - "ncongdot;": {'\u2A6D', '\u0338'}, - "nedot;": {'\u2250', '\u0338'}, - "nesim;": {'\u2242', '\u0338'}, - "ngE;": {'\u2267', '\u0338'}, - "ngeqq;": {'\u2267', '\u0338'}, - "ngeqslant;": {'\u2A7E', '\u0338'}, - "nges;": {'\u2A7E', '\u0338'}, - "nlE;": {'\u2266', '\u0338'}, - "nleqq;": {'\u2266', '\u0338'}, - "nleqslant;": {'\u2A7D', '\u0338'}, - "nles;": {'\u2A7D', '\u0338'}, - "notinE;": {'\u22F9', '\u0338'}, - "notindot;": {'\u22F5', '\u0338'}, - "nparsl;": {'\u2AFD', '\u20E5'}, - "npart;": {'\u2202', '\u0338'}, - "npre;": {'\u2AAF', '\u0338'}, - "npreceq;": {'\u2AAF', '\u0338'}, - "nrarrc;": {'\u2933', '\u0338'}, - "nrarrw;": {'\u219D', '\u0338'}, - "nsce;": {'\u2AB0', '\u0338'}, - "nsubE;": {'\u2AC5', '\u0338'}, - "nsubset;": {'\u2282', '\u20D2'}, - "nsubseteqq;": {'\u2AC5', '\u0338'}, - "nsucceq;": {'\u2AB0', '\u0338'}, - "nsupE;": {'\u2AC6', '\u0338'}, - "nsupset;": {'\u2283', '\u20D2'}, - "nsupseteqq;": {'\u2AC6', '\u0338'}, - "nvap;": {'\u224D', '\u20D2'}, - "nvge;": {'\u2265', '\u20D2'}, - "nvgt;": {'\u003E', '\u20D2'}, - "nvle;": {'\u2264', '\u20D2'}, - "nvlt;": {'\u003C', '\u20D2'}, - "nvltrie;": {'\u22B4', '\u20D2'}, - "nvrtrie;": {'\u22B5', '\u20D2'}, - "nvsim;": {'\u223C', '\u20D2'}, - "race;": {'\u223D', '\u0331'}, - "smtes;": {'\u2AAC', '\uFE00'}, - "sqcaps;": {'\u2293', '\uFE00'}, - "sqcups;": {'\u2294', '\uFE00'}, - "varsubsetneq;": {'\u228A', '\uFE00'}, - "varsubsetneqq;": {'\u2ACB', '\uFE00'}, - "varsupsetneq;": {'\u228B', '\uFE00'}, - "varsupsetneqq;": {'\u2ACC', '\uFE00'}, - "vnsub;": {'\u2282', '\u20D2'}, - "vnsup;": {'\u2283', '\u20D2'}, - "vsubnE;": {'\u2ACB', '\uFE00'}, - "vsubne;": {'\u228A', '\uFE00'}, - "vsupnE;": {'\u2ACC', '\uFE00'}, - "vsupne;": {'\u228B', '\uFE00'}, +var entity2 map[string][2]rune + +// populateMapsOnce guards calling populateMaps. +var populateMapsOnce sync.Once + +// populateMaps populates entity and entity2. +func populateMaps() { + entity = map[string]rune{ + "AElig;": '\U000000C6', + "AMP;": '\U00000026', + "Aacute;": '\U000000C1', + "Abreve;": '\U00000102', + "Acirc;": '\U000000C2', + "Acy;": '\U00000410', + "Afr;": '\U0001D504', + "Agrave;": '\U000000C0', + "Alpha;": '\U00000391', + "Amacr;": '\U00000100', + "And;": '\U00002A53', + "Aogon;": '\U00000104', + "Aopf;": '\U0001D538', + "ApplyFunction;": '\U00002061', + "Aring;": '\U000000C5', + "Ascr;": '\U0001D49C', + "Assign;": '\U00002254', + "Atilde;": '\U000000C3', + "Auml;": '\U000000C4', + "Backslash;": '\U00002216', + "Barv;": '\U00002AE7', + "Barwed;": '\U00002306', + "Bcy;": '\U00000411', + "Because;": '\U00002235', + "Bernoullis;": '\U0000212C', + "Beta;": '\U00000392', + "Bfr;": '\U0001D505', + "Bopf;": '\U0001D539', + "Breve;": '\U000002D8', + "Bscr;": '\U0000212C', + "Bumpeq;": '\U0000224E', + "CHcy;": '\U00000427', + "COPY;": '\U000000A9', + "Cacute;": '\U00000106', + "Cap;": '\U000022D2', + "CapitalDifferentialD;": '\U00002145', + "Cayleys;": '\U0000212D', + "Ccaron;": '\U0000010C', + "Ccedil;": '\U000000C7', + "Ccirc;": '\U00000108', + "Cconint;": '\U00002230', + "Cdot;": '\U0000010A', + "Cedilla;": '\U000000B8', + "CenterDot;": '\U000000B7', + "Cfr;": '\U0000212D', + "Chi;": '\U000003A7', + "CircleDot;": '\U00002299', + "CircleMinus;": '\U00002296', + "CirclePlus;": '\U00002295', + "CircleTimes;": '\U00002297', + "ClockwiseContourIntegral;": '\U00002232', + "CloseCurlyDoubleQuote;": '\U0000201D', + "CloseCurlyQuote;": '\U00002019', + "Colon;": '\U00002237', + "Colone;": '\U00002A74', + "Congruent;": '\U00002261', + "Conint;": '\U0000222F', + "ContourIntegral;": '\U0000222E', + "Copf;": '\U00002102', + "Coproduct;": '\U00002210', + "CounterClockwiseContourIntegral;": '\U00002233', + "Cross;": '\U00002A2F', + "Cscr;": '\U0001D49E', + "Cup;": '\U000022D3', + "CupCap;": '\U0000224D', + "DD;": '\U00002145', + "DDotrahd;": '\U00002911', + "DJcy;": '\U00000402', + "DScy;": '\U00000405', + "DZcy;": '\U0000040F', + "Dagger;": '\U00002021', + "Darr;": '\U000021A1', + "Dashv;": '\U00002AE4', + "Dcaron;": '\U0000010E', + "Dcy;": '\U00000414', + "Del;": '\U00002207', + "Delta;": '\U00000394', + "Dfr;": '\U0001D507', + "DiacriticalAcute;": '\U000000B4', + "DiacriticalDot;": '\U000002D9', + "DiacriticalDoubleAcute;": '\U000002DD', + "DiacriticalGrave;": '\U00000060', + "DiacriticalTilde;": '\U000002DC', + "Diamond;": '\U000022C4', + "DifferentialD;": '\U00002146', + "Dopf;": '\U0001D53B', + "Dot;": '\U000000A8', + "DotDot;": '\U000020DC', + "DotEqual;": '\U00002250', + "DoubleContourIntegral;": '\U0000222F', + "DoubleDot;": '\U000000A8', + "DoubleDownArrow;": '\U000021D3', + "DoubleLeftArrow;": '\U000021D0', + "DoubleLeftRightArrow;": '\U000021D4', + "DoubleLeftTee;": '\U00002AE4', + "DoubleLongLeftArrow;": '\U000027F8', + "DoubleLongLeftRightArrow;": '\U000027FA', + "DoubleLongRightArrow;": '\U000027F9', + "DoubleRightArrow;": '\U000021D2', + "DoubleRightTee;": '\U000022A8', + "DoubleUpArrow;": '\U000021D1', + "DoubleUpDownArrow;": '\U000021D5', + "DoubleVerticalBar;": '\U00002225', + "DownArrow;": '\U00002193', + "DownArrowBar;": '\U00002913', + "DownArrowUpArrow;": '\U000021F5', + "DownBreve;": '\U00000311', + "DownLeftRightVector;": '\U00002950', + "DownLeftTeeVector;": '\U0000295E', + "DownLeftVector;": '\U000021BD', + "DownLeftVectorBar;": '\U00002956', + "DownRightTeeVector;": '\U0000295F', + "DownRightVector;": '\U000021C1', + "DownRightVectorBar;": '\U00002957', + "DownTee;": '\U000022A4', + "DownTeeArrow;": '\U000021A7', + "Downarrow;": '\U000021D3', + "Dscr;": '\U0001D49F', + "Dstrok;": '\U00000110', + "ENG;": '\U0000014A', + "ETH;": '\U000000D0', + "Eacute;": '\U000000C9', + "Ecaron;": '\U0000011A', + "Ecirc;": '\U000000CA', + "Ecy;": '\U0000042D', + "Edot;": '\U00000116', + "Efr;": '\U0001D508', + "Egrave;": '\U000000C8', + "Element;": '\U00002208', + "Emacr;": '\U00000112', + "EmptySmallSquare;": '\U000025FB', + "EmptyVerySmallSquare;": '\U000025AB', + "Eogon;": '\U00000118', + "Eopf;": '\U0001D53C', + "Epsilon;": '\U00000395', + "Equal;": '\U00002A75', + "EqualTilde;": '\U00002242', + "Equilibrium;": '\U000021CC', + "Escr;": '\U00002130', + "Esim;": '\U00002A73', + "Eta;": '\U00000397', + "Euml;": '\U000000CB', + "Exists;": '\U00002203', + "ExponentialE;": '\U00002147', + "Fcy;": '\U00000424', + "Ffr;": '\U0001D509', + "FilledSmallSquare;": '\U000025FC', + "FilledVerySmallSquare;": '\U000025AA', + "Fopf;": '\U0001D53D', + "ForAll;": '\U00002200', + "Fouriertrf;": '\U00002131', + "Fscr;": '\U00002131', + "GJcy;": '\U00000403', + "GT;": '\U0000003E', + "Gamma;": '\U00000393', + "Gammad;": '\U000003DC', + "Gbreve;": '\U0000011E', + "Gcedil;": '\U00000122', + "Gcirc;": '\U0000011C', + "Gcy;": '\U00000413', + "Gdot;": '\U00000120', + "Gfr;": '\U0001D50A', + "Gg;": '\U000022D9', + "Gopf;": '\U0001D53E', + "GreaterEqual;": '\U00002265', + "GreaterEqualLess;": '\U000022DB', + "GreaterFullEqual;": '\U00002267', + "GreaterGreater;": '\U00002AA2', + "GreaterLess;": '\U00002277', + "GreaterSlantEqual;": '\U00002A7E', + "GreaterTilde;": '\U00002273', + "Gscr;": '\U0001D4A2', + "Gt;": '\U0000226B', + "HARDcy;": '\U0000042A', + "Hacek;": '\U000002C7', + "Hat;": '\U0000005E', + "Hcirc;": '\U00000124', + "Hfr;": '\U0000210C', + "HilbertSpace;": '\U0000210B', + "Hopf;": '\U0000210D', + "HorizontalLine;": '\U00002500', + "Hscr;": '\U0000210B', + "Hstrok;": '\U00000126', + "HumpDownHump;": '\U0000224E', + "HumpEqual;": '\U0000224F', + "IEcy;": '\U00000415', + "IJlig;": '\U00000132', + "IOcy;": '\U00000401', + "Iacute;": '\U000000CD', + "Icirc;": '\U000000CE', + "Icy;": '\U00000418', + "Idot;": '\U00000130', + "Ifr;": '\U00002111', + "Igrave;": '\U000000CC', + "Im;": '\U00002111', + "Imacr;": '\U0000012A', + "ImaginaryI;": '\U00002148', + "Implies;": '\U000021D2', + "Int;": '\U0000222C', + "Integral;": '\U0000222B', + "Intersection;": '\U000022C2', + "InvisibleComma;": '\U00002063', + "InvisibleTimes;": '\U00002062', + "Iogon;": '\U0000012E', + "Iopf;": '\U0001D540', + "Iota;": '\U00000399', + "Iscr;": '\U00002110', + "Itilde;": '\U00000128', + "Iukcy;": '\U00000406', + "Iuml;": '\U000000CF', + "Jcirc;": '\U00000134', + "Jcy;": '\U00000419', + "Jfr;": '\U0001D50D', + "Jopf;": '\U0001D541', + "Jscr;": '\U0001D4A5', + "Jsercy;": '\U00000408', + "Jukcy;": '\U00000404', + "KHcy;": '\U00000425', + "KJcy;": '\U0000040C', + "Kappa;": '\U0000039A', + "Kcedil;": '\U00000136', + "Kcy;": '\U0000041A', + "Kfr;": '\U0001D50E', + "Kopf;": '\U0001D542', + "Kscr;": '\U0001D4A6', + "LJcy;": '\U00000409', + "LT;": '\U0000003C', + "Lacute;": '\U00000139', + "Lambda;": '\U0000039B', + "Lang;": '\U000027EA', + "Laplacetrf;": '\U00002112', + "Larr;": '\U0000219E', + "Lcaron;": '\U0000013D', + "Lcedil;": '\U0000013B', + "Lcy;": '\U0000041B', + "LeftAngleBracket;": '\U000027E8', + "LeftArrow;": '\U00002190', + "LeftArrowBar;": '\U000021E4', + "LeftArrowRightArrow;": '\U000021C6', + "LeftCeiling;": '\U00002308', + "LeftDoubleBracket;": '\U000027E6', + "LeftDownTeeVector;": '\U00002961', + "LeftDownVector;": '\U000021C3', + "LeftDownVectorBar;": '\U00002959', + "LeftFloor;": '\U0000230A', + "LeftRightArrow;": '\U00002194', + "LeftRightVector;": '\U0000294E', + "LeftTee;": '\U000022A3', + "LeftTeeArrow;": '\U000021A4', + "LeftTeeVector;": '\U0000295A', + "LeftTriangle;": '\U000022B2', + "LeftTriangleBar;": '\U000029CF', + "LeftTriangleEqual;": '\U000022B4', + "LeftUpDownVector;": '\U00002951', + "LeftUpTeeVector;": '\U00002960', + "LeftUpVector;": '\U000021BF', + "LeftUpVectorBar;": '\U00002958', + "LeftVector;": '\U000021BC', + "LeftVectorBar;": '\U00002952', + "Leftarrow;": '\U000021D0', + "Leftrightarrow;": '\U000021D4', + "LessEqualGreater;": '\U000022DA', + "LessFullEqual;": '\U00002266', + "LessGreater;": '\U00002276', + "LessLess;": '\U00002AA1', + "LessSlantEqual;": '\U00002A7D', + "LessTilde;": '\U00002272', + "Lfr;": '\U0001D50F', + "Ll;": '\U000022D8', + "Lleftarrow;": '\U000021DA', + "Lmidot;": '\U0000013F', + "LongLeftArrow;": '\U000027F5', + "LongLeftRightArrow;": '\U000027F7', + "LongRightArrow;": '\U000027F6', + "Longleftarrow;": '\U000027F8', + "Longleftrightarrow;": '\U000027FA', + "Longrightarrow;": '\U000027F9', + "Lopf;": '\U0001D543', + "LowerLeftArrow;": '\U00002199', + "LowerRightArrow;": '\U00002198', + "Lscr;": '\U00002112', + "Lsh;": '\U000021B0', + "Lstrok;": '\U00000141', + "Lt;": '\U0000226A', + "Map;": '\U00002905', + "Mcy;": '\U0000041C', + "MediumSpace;": '\U0000205F', + "Mellintrf;": '\U00002133', + "Mfr;": '\U0001D510', + "MinusPlus;": '\U00002213', + "Mopf;": '\U0001D544', + "Mscr;": '\U00002133', + "Mu;": '\U0000039C', + "NJcy;": '\U0000040A', + "Nacute;": '\U00000143', + "Ncaron;": '\U00000147', + "Ncedil;": '\U00000145', + "Ncy;": '\U0000041D', + "NegativeMediumSpace;": '\U0000200B', + "NegativeThickSpace;": '\U0000200B', + "NegativeThinSpace;": '\U0000200B', + "NegativeVeryThinSpace;": '\U0000200B', + "NestedGreaterGreater;": '\U0000226B', + "NestedLessLess;": '\U0000226A', + "NewLine;": '\U0000000A', + "Nfr;": '\U0001D511', + "NoBreak;": '\U00002060', + "NonBreakingSpace;": '\U000000A0', + "Nopf;": '\U00002115', + "Not;": '\U00002AEC', + "NotCongruent;": '\U00002262', + "NotCupCap;": '\U0000226D', + "NotDoubleVerticalBar;": '\U00002226', + "NotElement;": '\U00002209', + "NotEqual;": '\U00002260', + "NotExists;": '\U00002204', + "NotGreater;": '\U0000226F', + "NotGreaterEqual;": '\U00002271', + "NotGreaterLess;": '\U00002279', + "NotGreaterTilde;": '\U00002275', + "NotLeftTriangle;": '\U000022EA', + "NotLeftTriangleEqual;": '\U000022EC', + "NotLess;": '\U0000226E', + "NotLessEqual;": '\U00002270', + "NotLessGreater;": '\U00002278', + "NotLessTilde;": '\U00002274', + "NotPrecedes;": '\U00002280', + "NotPrecedesSlantEqual;": '\U000022E0', + "NotReverseElement;": '\U0000220C', + "NotRightTriangle;": '\U000022EB', + "NotRightTriangleEqual;": '\U000022ED', + "NotSquareSubsetEqual;": '\U000022E2', + "NotSquareSupersetEqual;": '\U000022E3', + "NotSubsetEqual;": '\U00002288', + "NotSucceeds;": '\U00002281', + "NotSucceedsSlantEqual;": '\U000022E1', + "NotSupersetEqual;": '\U00002289', + "NotTilde;": '\U00002241', + "NotTildeEqual;": '\U00002244', + "NotTildeFullEqual;": '\U00002247', + "NotTildeTilde;": '\U00002249', + "NotVerticalBar;": '\U00002224', + "Nscr;": '\U0001D4A9', + "Ntilde;": '\U000000D1', + "Nu;": '\U0000039D', + "OElig;": '\U00000152', + "Oacute;": '\U000000D3', + "Ocirc;": '\U000000D4', + "Ocy;": '\U0000041E', + "Odblac;": '\U00000150', + "Ofr;": '\U0001D512', + "Ograve;": '\U000000D2', + "Omacr;": '\U0000014C', + "Omega;": '\U000003A9', + "Omicron;": '\U0000039F', + "Oopf;": '\U0001D546', + "OpenCurlyDoubleQuote;": '\U0000201C', + "OpenCurlyQuote;": '\U00002018', + "Or;": '\U00002A54', + "Oscr;": '\U0001D4AA', + "Oslash;": '\U000000D8', + "Otilde;": '\U000000D5', + "Otimes;": '\U00002A37', + "Ouml;": '\U000000D6', + "OverBar;": '\U0000203E', + "OverBrace;": '\U000023DE', + "OverBracket;": '\U000023B4', + "OverParenthesis;": '\U000023DC', + "PartialD;": '\U00002202', + "Pcy;": '\U0000041F', + "Pfr;": '\U0001D513', + "Phi;": '\U000003A6', + "Pi;": '\U000003A0', + "PlusMinus;": '\U000000B1', + "Poincareplane;": '\U0000210C', + "Popf;": '\U00002119', + "Pr;": '\U00002ABB', + "Precedes;": '\U0000227A', + "PrecedesEqual;": '\U00002AAF', + "PrecedesSlantEqual;": '\U0000227C', + "PrecedesTilde;": '\U0000227E', + "Prime;": '\U00002033', + "Product;": '\U0000220F', + "Proportion;": '\U00002237', + "Proportional;": '\U0000221D', + "Pscr;": '\U0001D4AB', + "Psi;": '\U000003A8', + "QUOT;": '\U00000022', + "Qfr;": '\U0001D514', + "Qopf;": '\U0000211A', + "Qscr;": '\U0001D4AC', + "RBarr;": '\U00002910', + "REG;": '\U000000AE', + "Racute;": '\U00000154', + "Rang;": '\U000027EB', + "Rarr;": '\U000021A0', + "Rarrtl;": '\U00002916', + "Rcaron;": '\U00000158', + "Rcedil;": '\U00000156', + "Rcy;": '\U00000420', + "Re;": '\U0000211C', + "ReverseElement;": '\U0000220B', + "ReverseEquilibrium;": '\U000021CB', + "ReverseUpEquilibrium;": '\U0000296F', + "Rfr;": '\U0000211C', + "Rho;": '\U000003A1', + "RightAngleBracket;": '\U000027E9', + "RightArrow;": '\U00002192', + "RightArrowBar;": '\U000021E5', + "RightArrowLeftArrow;": '\U000021C4', + "RightCeiling;": '\U00002309', + "RightDoubleBracket;": '\U000027E7', + "RightDownTeeVector;": '\U0000295D', + "RightDownVector;": '\U000021C2', + "RightDownVectorBar;": '\U00002955', + "RightFloor;": '\U0000230B', + "RightTee;": '\U000022A2', + "RightTeeArrow;": '\U000021A6', + "RightTeeVector;": '\U0000295B', + "RightTriangle;": '\U000022B3', + "RightTriangleBar;": '\U000029D0', + "RightTriangleEqual;": '\U000022B5', + "RightUpDownVector;": '\U0000294F', + "RightUpTeeVector;": '\U0000295C', + "RightUpVector;": '\U000021BE', + "RightUpVectorBar;": '\U00002954', + "RightVector;": '\U000021C0', + "RightVectorBar;": '\U00002953', + "Rightarrow;": '\U000021D2', + "Ropf;": '\U0000211D', + "RoundImplies;": '\U00002970', + "Rrightarrow;": '\U000021DB', + "Rscr;": '\U0000211B', + "Rsh;": '\U000021B1', + "RuleDelayed;": '\U000029F4', + "SHCHcy;": '\U00000429', + "SHcy;": '\U00000428', + "SOFTcy;": '\U0000042C', + "Sacute;": '\U0000015A', + "Sc;": '\U00002ABC', + "Scaron;": '\U00000160', + "Scedil;": '\U0000015E', + "Scirc;": '\U0000015C', + "Scy;": '\U00000421', + "Sfr;": '\U0001D516', + "ShortDownArrow;": '\U00002193', + "ShortLeftArrow;": '\U00002190', + "ShortRightArrow;": '\U00002192', + "ShortUpArrow;": '\U00002191', + "Sigma;": '\U000003A3', + "SmallCircle;": '\U00002218', + "Sopf;": '\U0001D54A', + "Sqrt;": '\U0000221A', + "Square;": '\U000025A1', + "SquareIntersection;": '\U00002293', + "SquareSubset;": '\U0000228F', + "SquareSubsetEqual;": '\U00002291', + "SquareSuperset;": '\U00002290', + "SquareSupersetEqual;": '\U00002292', + "SquareUnion;": '\U00002294', + "Sscr;": '\U0001D4AE', + "Star;": '\U000022C6', + "Sub;": '\U000022D0', + "Subset;": '\U000022D0', + "SubsetEqual;": '\U00002286', + "Succeeds;": '\U0000227B', + "SucceedsEqual;": '\U00002AB0', + "SucceedsSlantEqual;": '\U0000227D', + "SucceedsTilde;": '\U0000227F', + "SuchThat;": '\U0000220B', + "Sum;": '\U00002211', + "Sup;": '\U000022D1', + "Superset;": '\U00002283', + "SupersetEqual;": '\U00002287', + "Supset;": '\U000022D1', + "THORN;": '\U000000DE', + "TRADE;": '\U00002122', + "TSHcy;": '\U0000040B', + "TScy;": '\U00000426', + "Tab;": '\U00000009', + "Tau;": '\U000003A4', + "Tcaron;": '\U00000164', + "Tcedil;": '\U00000162', + "Tcy;": '\U00000422', + "Tfr;": '\U0001D517', + "Therefore;": '\U00002234', + "Theta;": '\U00000398', + "ThinSpace;": '\U00002009', + "Tilde;": '\U0000223C', + "TildeEqual;": '\U00002243', + "TildeFullEqual;": '\U00002245', + "TildeTilde;": '\U00002248', + "Topf;": '\U0001D54B', + "TripleDot;": '\U000020DB', + "Tscr;": '\U0001D4AF', + "Tstrok;": '\U00000166', + "Uacute;": '\U000000DA', + "Uarr;": '\U0000219F', + "Uarrocir;": '\U00002949', + "Ubrcy;": '\U0000040E', + "Ubreve;": '\U0000016C', + "Ucirc;": '\U000000DB', + "Ucy;": '\U00000423', + "Udblac;": '\U00000170', + "Ufr;": '\U0001D518', + "Ugrave;": '\U000000D9', + "Umacr;": '\U0000016A', + "UnderBar;": '\U0000005F', + "UnderBrace;": '\U000023DF', + "UnderBracket;": '\U000023B5', + "UnderParenthesis;": '\U000023DD', + "Union;": '\U000022C3', + "UnionPlus;": '\U0000228E', + "Uogon;": '\U00000172', + "Uopf;": '\U0001D54C', + "UpArrow;": '\U00002191', + "UpArrowBar;": '\U00002912', + "UpArrowDownArrow;": '\U000021C5', + "UpDownArrow;": '\U00002195', + "UpEquilibrium;": '\U0000296E', + "UpTee;": '\U000022A5', + "UpTeeArrow;": '\U000021A5', + "Uparrow;": '\U000021D1', + "Updownarrow;": '\U000021D5', + "UpperLeftArrow;": '\U00002196', + "UpperRightArrow;": '\U00002197', + "Upsi;": '\U000003D2', + "Upsilon;": '\U000003A5', + "Uring;": '\U0000016E', + "Uscr;": '\U0001D4B0', + "Utilde;": '\U00000168', + "Uuml;": '\U000000DC', + "VDash;": '\U000022AB', + "Vbar;": '\U00002AEB', + "Vcy;": '\U00000412', + "Vdash;": '\U000022A9', + "Vdashl;": '\U00002AE6', + "Vee;": '\U000022C1', + "Verbar;": '\U00002016', + "Vert;": '\U00002016', + "VerticalBar;": '\U00002223', + "VerticalLine;": '\U0000007C', + "VerticalSeparator;": '\U00002758', + "VerticalTilde;": '\U00002240', + "VeryThinSpace;": '\U0000200A', + "Vfr;": '\U0001D519', + "Vopf;": '\U0001D54D', + "Vscr;": '\U0001D4B1', + "Vvdash;": '\U000022AA', + "Wcirc;": '\U00000174', + "Wedge;": '\U000022C0', + "Wfr;": '\U0001D51A', + "Wopf;": '\U0001D54E', + "Wscr;": '\U0001D4B2', + "Xfr;": '\U0001D51B', + "Xi;": '\U0000039E', + "Xopf;": '\U0001D54F', + "Xscr;": '\U0001D4B3', + "YAcy;": '\U0000042F', + "YIcy;": '\U00000407', + "YUcy;": '\U0000042E', + "Yacute;": '\U000000DD', + "Ycirc;": '\U00000176', + "Ycy;": '\U0000042B', + "Yfr;": '\U0001D51C', + "Yopf;": '\U0001D550', + "Yscr;": '\U0001D4B4', + "Yuml;": '\U00000178', + "ZHcy;": '\U00000416', + "Zacute;": '\U00000179', + "Zcaron;": '\U0000017D', + "Zcy;": '\U00000417', + "Zdot;": '\U0000017B', + "ZeroWidthSpace;": '\U0000200B', + "Zeta;": '\U00000396', + "Zfr;": '\U00002128', + "Zopf;": '\U00002124', + "Zscr;": '\U0001D4B5', + "aacute;": '\U000000E1', + "abreve;": '\U00000103', + "ac;": '\U0000223E', + "acd;": '\U0000223F', + "acirc;": '\U000000E2', + "acute;": '\U000000B4', + "acy;": '\U00000430', + "aelig;": '\U000000E6', + "af;": '\U00002061', + "afr;": '\U0001D51E', + "agrave;": '\U000000E0', + "alefsym;": '\U00002135', + "aleph;": '\U00002135', + "alpha;": '\U000003B1', + "amacr;": '\U00000101', + "amalg;": '\U00002A3F', + "amp;": '\U00000026', + "and;": '\U00002227', + "andand;": '\U00002A55', + "andd;": '\U00002A5C', + "andslope;": '\U00002A58', + "andv;": '\U00002A5A', + "ang;": '\U00002220', + "ange;": '\U000029A4', + "angle;": '\U00002220', + "angmsd;": '\U00002221', + "angmsdaa;": '\U000029A8', + "angmsdab;": '\U000029A9', + "angmsdac;": '\U000029AA', + "angmsdad;": '\U000029AB', + "angmsdae;": '\U000029AC', + "angmsdaf;": '\U000029AD', + "angmsdag;": '\U000029AE', + "angmsdah;": '\U000029AF', + "angrt;": '\U0000221F', + "angrtvb;": '\U000022BE', + "angrtvbd;": '\U0000299D', + "angsph;": '\U00002222', + "angst;": '\U000000C5', + "angzarr;": '\U0000237C', + "aogon;": '\U00000105', + "aopf;": '\U0001D552', + "ap;": '\U00002248', + "apE;": '\U00002A70', + "apacir;": '\U00002A6F', + "ape;": '\U0000224A', + "apid;": '\U0000224B', + "apos;": '\U00000027', + "approx;": '\U00002248', + "approxeq;": '\U0000224A', + "aring;": '\U000000E5', + "ascr;": '\U0001D4B6', + "ast;": '\U0000002A', + "asymp;": '\U00002248', + "asympeq;": '\U0000224D', + "atilde;": '\U000000E3', + "auml;": '\U000000E4', + "awconint;": '\U00002233', + "awint;": '\U00002A11', + "bNot;": '\U00002AED', + "backcong;": '\U0000224C', + "backepsilon;": '\U000003F6', + "backprime;": '\U00002035', + "backsim;": '\U0000223D', + "backsimeq;": '\U000022CD', + "barvee;": '\U000022BD', + "barwed;": '\U00002305', + "barwedge;": '\U00002305', + "bbrk;": '\U000023B5', + "bbrktbrk;": '\U000023B6', + "bcong;": '\U0000224C', + "bcy;": '\U00000431', + "bdquo;": '\U0000201E', + "becaus;": '\U00002235', + "because;": '\U00002235', + "bemptyv;": '\U000029B0', + "bepsi;": '\U000003F6', + "bernou;": '\U0000212C', + "beta;": '\U000003B2', + "beth;": '\U00002136', + "between;": '\U0000226C', + "bfr;": '\U0001D51F', + "bigcap;": '\U000022C2', + "bigcirc;": '\U000025EF', + "bigcup;": '\U000022C3', + "bigodot;": '\U00002A00', + "bigoplus;": '\U00002A01', + "bigotimes;": '\U00002A02', + "bigsqcup;": '\U00002A06', + "bigstar;": '\U00002605', + "bigtriangledown;": '\U000025BD', + "bigtriangleup;": '\U000025B3', + "biguplus;": '\U00002A04', + "bigvee;": '\U000022C1', + "bigwedge;": '\U000022C0', + "bkarow;": '\U0000290D', + "blacklozenge;": '\U000029EB', + "blacksquare;": '\U000025AA', + "blacktriangle;": '\U000025B4', + "blacktriangledown;": '\U000025BE', + "blacktriangleleft;": '\U000025C2', + "blacktriangleright;": '\U000025B8', + "blank;": '\U00002423', + "blk12;": '\U00002592', + "blk14;": '\U00002591', + "blk34;": '\U00002593', + "block;": '\U00002588', + "bnot;": '\U00002310', + "bopf;": '\U0001D553', + "bot;": '\U000022A5', + "bottom;": '\U000022A5', + "bowtie;": '\U000022C8', + "boxDL;": '\U00002557', + "boxDR;": '\U00002554', + "boxDl;": '\U00002556', + "boxDr;": '\U00002553', + "boxH;": '\U00002550', + "boxHD;": '\U00002566', + "boxHU;": '\U00002569', + "boxHd;": '\U00002564', + "boxHu;": '\U00002567', + "boxUL;": '\U0000255D', + "boxUR;": '\U0000255A', + "boxUl;": '\U0000255C', + "boxUr;": '\U00002559', + "boxV;": '\U00002551', + "boxVH;": '\U0000256C', + "boxVL;": '\U00002563', + "boxVR;": '\U00002560', + "boxVh;": '\U0000256B', + "boxVl;": '\U00002562', + "boxVr;": '\U0000255F', + "boxbox;": '\U000029C9', + "boxdL;": '\U00002555', + "boxdR;": '\U00002552', + "boxdl;": '\U00002510', + "boxdr;": '\U0000250C', + "boxh;": '\U00002500', + "boxhD;": '\U00002565', + "boxhU;": '\U00002568', + "boxhd;": '\U0000252C', + "boxhu;": '\U00002534', + "boxminus;": '\U0000229F', + "boxplus;": '\U0000229E', + "boxtimes;": '\U000022A0', + "boxuL;": '\U0000255B', + "boxuR;": '\U00002558', + "boxul;": '\U00002518', + "boxur;": '\U00002514', + "boxv;": '\U00002502', + "boxvH;": '\U0000256A', + "boxvL;": '\U00002561', + "boxvR;": '\U0000255E', + "boxvh;": '\U0000253C', + "boxvl;": '\U00002524', + "boxvr;": '\U0000251C', + "bprime;": '\U00002035', + "breve;": '\U000002D8', + "brvbar;": '\U000000A6', + "bscr;": '\U0001D4B7', + "bsemi;": '\U0000204F', + "bsim;": '\U0000223D', + "bsime;": '\U000022CD', + "bsol;": '\U0000005C', + "bsolb;": '\U000029C5', + "bsolhsub;": '\U000027C8', + "bull;": '\U00002022', + "bullet;": '\U00002022', + "bump;": '\U0000224E', + "bumpE;": '\U00002AAE', + "bumpe;": '\U0000224F', + "bumpeq;": '\U0000224F', + "cacute;": '\U00000107', + "cap;": '\U00002229', + "capand;": '\U00002A44', + "capbrcup;": '\U00002A49', + "capcap;": '\U00002A4B', + "capcup;": '\U00002A47', + "capdot;": '\U00002A40', + "caret;": '\U00002041', + "caron;": '\U000002C7', + "ccaps;": '\U00002A4D', + "ccaron;": '\U0000010D', + "ccedil;": '\U000000E7', + "ccirc;": '\U00000109', + "ccups;": '\U00002A4C', + "ccupssm;": '\U00002A50', + "cdot;": '\U0000010B', + "cedil;": '\U000000B8', + "cemptyv;": '\U000029B2', + "cent;": '\U000000A2', + "centerdot;": '\U000000B7', + "cfr;": '\U0001D520', + "chcy;": '\U00000447', + "check;": '\U00002713', + "checkmark;": '\U00002713', + "chi;": '\U000003C7', + "cir;": '\U000025CB', + "cirE;": '\U000029C3', + "circ;": '\U000002C6', + "circeq;": '\U00002257', + "circlearrowleft;": '\U000021BA', + "circlearrowright;": '\U000021BB', + "circledR;": '\U000000AE', + "circledS;": '\U000024C8', + "circledast;": '\U0000229B', + "circledcirc;": '\U0000229A', + "circleddash;": '\U0000229D', + "cire;": '\U00002257', + "cirfnint;": '\U00002A10', + "cirmid;": '\U00002AEF', + "cirscir;": '\U000029C2', + "clubs;": '\U00002663', + "clubsuit;": '\U00002663', + "colon;": '\U0000003A', + "colone;": '\U00002254', + "coloneq;": '\U00002254', + "comma;": '\U0000002C', + "commat;": '\U00000040', + "comp;": '\U00002201', + "compfn;": '\U00002218', + "complement;": '\U00002201', + "complexes;": '\U00002102', + "cong;": '\U00002245', + "congdot;": '\U00002A6D', + "conint;": '\U0000222E', + "copf;": '\U0001D554', + "coprod;": '\U00002210', + "copy;": '\U000000A9', + "copysr;": '\U00002117', + "crarr;": '\U000021B5', + "cross;": '\U00002717', + "cscr;": '\U0001D4B8', + "csub;": '\U00002ACF', + "csube;": '\U00002AD1', + "csup;": '\U00002AD0', + "csupe;": '\U00002AD2', + "ctdot;": '\U000022EF', + "cudarrl;": '\U00002938', + "cudarrr;": '\U00002935', + "cuepr;": '\U000022DE', + "cuesc;": '\U000022DF', + "cularr;": '\U000021B6', + "cularrp;": '\U0000293D', + "cup;": '\U0000222A', + "cupbrcap;": '\U00002A48', + "cupcap;": '\U00002A46', + "cupcup;": '\U00002A4A', + "cupdot;": '\U0000228D', + "cupor;": '\U00002A45', + "curarr;": '\U000021B7', + "curarrm;": '\U0000293C', + "curlyeqprec;": '\U000022DE', + "curlyeqsucc;": '\U000022DF', + "curlyvee;": '\U000022CE', + "curlywedge;": '\U000022CF', + "curren;": '\U000000A4', + "curvearrowleft;": '\U000021B6', + "curvearrowright;": '\U000021B7', + "cuvee;": '\U000022CE', + "cuwed;": '\U000022CF', + "cwconint;": '\U00002232', + "cwint;": '\U00002231', + "cylcty;": '\U0000232D', + "dArr;": '\U000021D3', + "dHar;": '\U00002965', + "dagger;": '\U00002020', + "daleth;": '\U00002138', + "darr;": '\U00002193', + "dash;": '\U00002010', + "dashv;": '\U000022A3', + "dbkarow;": '\U0000290F', + "dblac;": '\U000002DD', + "dcaron;": '\U0000010F', + "dcy;": '\U00000434', + "dd;": '\U00002146', + "ddagger;": '\U00002021', + "ddarr;": '\U000021CA', + "ddotseq;": '\U00002A77', + "deg;": '\U000000B0', + "delta;": '\U000003B4', + "demptyv;": '\U000029B1', + "dfisht;": '\U0000297F', + "dfr;": '\U0001D521', + "dharl;": '\U000021C3', + "dharr;": '\U000021C2', + "diam;": '\U000022C4', + "diamond;": '\U000022C4', + "diamondsuit;": '\U00002666', + "diams;": '\U00002666', + "die;": '\U000000A8', + "digamma;": '\U000003DD', + "disin;": '\U000022F2', + "div;": '\U000000F7', + "divide;": '\U000000F7', + "divideontimes;": '\U000022C7', + "divonx;": '\U000022C7', + "djcy;": '\U00000452', + "dlcorn;": '\U0000231E', + "dlcrop;": '\U0000230D', + "dollar;": '\U00000024', + "dopf;": '\U0001D555', + "dot;": '\U000002D9', + "doteq;": '\U00002250', + "doteqdot;": '\U00002251', + "dotminus;": '\U00002238', + "dotplus;": '\U00002214', + "dotsquare;": '\U000022A1', + "doublebarwedge;": '\U00002306', + "downarrow;": '\U00002193', + "downdownarrows;": '\U000021CA', + "downharpoonleft;": '\U000021C3', + "downharpoonright;": '\U000021C2', + "drbkarow;": '\U00002910', + "drcorn;": '\U0000231F', + "drcrop;": '\U0000230C', + "dscr;": '\U0001D4B9', + "dscy;": '\U00000455', + "dsol;": '\U000029F6', + "dstrok;": '\U00000111', + "dtdot;": '\U000022F1', + "dtri;": '\U000025BF', + "dtrif;": '\U000025BE', + "duarr;": '\U000021F5', + "duhar;": '\U0000296F', + "dwangle;": '\U000029A6', + "dzcy;": '\U0000045F', + "dzigrarr;": '\U000027FF', + "eDDot;": '\U00002A77', + "eDot;": '\U00002251', + "eacute;": '\U000000E9', + "easter;": '\U00002A6E', + "ecaron;": '\U0000011B', + "ecir;": '\U00002256', + "ecirc;": '\U000000EA', + "ecolon;": '\U00002255', + "ecy;": '\U0000044D', + "edot;": '\U00000117', + "ee;": '\U00002147', + "efDot;": '\U00002252', + "efr;": '\U0001D522', + "eg;": '\U00002A9A', + "egrave;": '\U000000E8', + "egs;": '\U00002A96', + "egsdot;": '\U00002A98', + "el;": '\U00002A99', + "elinters;": '\U000023E7', + "ell;": '\U00002113', + "els;": '\U00002A95', + "elsdot;": '\U00002A97', + "emacr;": '\U00000113', + "empty;": '\U00002205', + "emptyset;": '\U00002205', + "emptyv;": '\U00002205', + "emsp;": '\U00002003', + "emsp13;": '\U00002004', + "emsp14;": '\U00002005', + "eng;": '\U0000014B', + "ensp;": '\U00002002', + "eogon;": '\U00000119', + "eopf;": '\U0001D556', + "epar;": '\U000022D5', + "eparsl;": '\U000029E3', + "eplus;": '\U00002A71', + "epsi;": '\U000003B5', + "epsilon;": '\U000003B5', + "epsiv;": '\U000003F5', + "eqcirc;": '\U00002256', + "eqcolon;": '\U00002255', + "eqsim;": '\U00002242', + "eqslantgtr;": '\U00002A96', + "eqslantless;": '\U00002A95', + "equals;": '\U0000003D', + "equest;": '\U0000225F', + "equiv;": '\U00002261', + "equivDD;": '\U00002A78', + "eqvparsl;": '\U000029E5', + "erDot;": '\U00002253', + "erarr;": '\U00002971', + "escr;": '\U0000212F', + "esdot;": '\U00002250', + "esim;": '\U00002242', + "eta;": '\U000003B7', + "eth;": '\U000000F0', + "euml;": '\U000000EB', + "euro;": '\U000020AC', + "excl;": '\U00000021', + "exist;": '\U00002203', + "expectation;": '\U00002130', + "exponentiale;": '\U00002147', + "fallingdotseq;": '\U00002252', + "fcy;": '\U00000444', + "female;": '\U00002640', + "ffilig;": '\U0000FB03', + "fflig;": '\U0000FB00', + "ffllig;": '\U0000FB04', + "ffr;": '\U0001D523', + "filig;": '\U0000FB01', + "flat;": '\U0000266D', + "fllig;": '\U0000FB02', + "fltns;": '\U000025B1', + "fnof;": '\U00000192', + "fopf;": '\U0001D557', + "forall;": '\U00002200', + "fork;": '\U000022D4', + "forkv;": '\U00002AD9', + "fpartint;": '\U00002A0D', + "frac12;": '\U000000BD', + "frac13;": '\U00002153', + "frac14;": '\U000000BC', + "frac15;": '\U00002155', + "frac16;": '\U00002159', + "frac18;": '\U0000215B', + "frac23;": '\U00002154', + "frac25;": '\U00002156', + "frac34;": '\U000000BE', + "frac35;": '\U00002157', + "frac38;": '\U0000215C', + "frac45;": '\U00002158', + "frac56;": '\U0000215A', + "frac58;": '\U0000215D', + "frac78;": '\U0000215E', + "frasl;": '\U00002044', + "frown;": '\U00002322', + "fscr;": '\U0001D4BB', + "gE;": '\U00002267', + "gEl;": '\U00002A8C', + "gacute;": '\U000001F5', + "gamma;": '\U000003B3', + "gammad;": '\U000003DD', + "gap;": '\U00002A86', + "gbreve;": '\U0000011F', + "gcirc;": '\U0000011D', + "gcy;": '\U00000433', + "gdot;": '\U00000121', + "ge;": '\U00002265', + "gel;": '\U000022DB', + "geq;": '\U00002265', + "geqq;": '\U00002267', + "geqslant;": '\U00002A7E', + "ges;": '\U00002A7E', + "gescc;": '\U00002AA9', + "gesdot;": '\U00002A80', + "gesdoto;": '\U00002A82', + "gesdotol;": '\U00002A84', + "gesles;": '\U00002A94', + "gfr;": '\U0001D524', + "gg;": '\U0000226B', + "ggg;": '\U000022D9', + "gimel;": '\U00002137', + "gjcy;": '\U00000453', + "gl;": '\U00002277', + "glE;": '\U00002A92', + "gla;": '\U00002AA5', + "glj;": '\U00002AA4', + "gnE;": '\U00002269', + "gnap;": '\U00002A8A', + "gnapprox;": '\U00002A8A', + "gne;": '\U00002A88', + "gneq;": '\U00002A88', + "gneqq;": '\U00002269', + "gnsim;": '\U000022E7', + "gopf;": '\U0001D558', + "grave;": '\U00000060', + "gscr;": '\U0000210A', + "gsim;": '\U00002273', + "gsime;": '\U00002A8E', + "gsiml;": '\U00002A90', + "gt;": '\U0000003E', + "gtcc;": '\U00002AA7', + "gtcir;": '\U00002A7A', + "gtdot;": '\U000022D7', + "gtlPar;": '\U00002995', + "gtquest;": '\U00002A7C', + "gtrapprox;": '\U00002A86', + "gtrarr;": '\U00002978', + "gtrdot;": '\U000022D7', + "gtreqless;": '\U000022DB', + "gtreqqless;": '\U00002A8C', + "gtrless;": '\U00002277', + "gtrsim;": '\U00002273', + "hArr;": '\U000021D4', + "hairsp;": '\U0000200A', + "half;": '\U000000BD', + "hamilt;": '\U0000210B', + "hardcy;": '\U0000044A', + "harr;": '\U00002194', + "harrcir;": '\U00002948', + "harrw;": '\U000021AD', + "hbar;": '\U0000210F', + "hcirc;": '\U00000125', + "hearts;": '\U00002665', + "heartsuit;": '\U00002665', + "hellip;": '\U00002026', + "hercon;": '\U000022B9', + "hfr;": '\U0001D525', + "hksearow;": '\U00002925', + "hkswarow;": '\U00002926', + "hoarr;": '\U000021FF', + "homtht;": '\U0000223B', + "hookleftarrow;": '\U000021A9', + "hookrightarrow;": '\U000021AA', + "hopf;": '\U0001D559', + "horbar;": '\U00002015', + "hscr;": '\U0001D4BD', + "hslash;": '\U0000210F', + "hstrok;": '\U00000127', + "hybull;": '\U00002043', + "hyphen;": '\U00002010', + "iacute;": '\U000000ED', + "ic;": '\U00002063', + "icirc;": '\U000000EE', + "icy;": '\U00000438', + "iecy;": '\U00000435', + "iexcl;": '\U000000A1', + "iff;": '\U000021D4', + "ifr;": '\U0001D526', + "igrave;": '\U000000EC', + "ii;": '\U00002148', + "iiiint;": '\U00002A0C', + "iiint;": '\U0000222D', + "iinfin;": '\U000029DC', + "iiota;": '\U00002129', + "ijlig;": '\U00000133', + "imacr;": '\U0000012B', + "image;": '\U00002111', + "imagline;": '\U00002110', + "imagpart;": '\U00002111', + "imath;": '\U00000131', + "imof;": '\U000022B7', + "imped;": '\U000001B5', + "in;": '\U00002208', + "incare;": '\U00002105', + "infin;": '\U0000221E', + "infintie;": '\U000029DD', + "inodot;": '\U00000131', + "int;": '\U0000222B', + "intcal;": '\U000022BA', + "integers;": '\U00002124', + "intercal;": '\U000022BA', + "intlarhk;": '\U00002A17', + "intprod;": '\U00002A3C', + "iocy;": '\U00000451', + "iogon;": '\U0000012F', + "iopf;": '\U0001D55A', + "iota;": '\U000003B9', + "iprod;": '\U00002A3C', + "iquest;": '\U000000BF', + "iscr;": '\U0001D4BE', + "isin;": '\U00002208', + "isinE;": '\U000022F9', + "isindot;": '\U000022F5', + "isins;": '\U000022F4', + "isinsv;": '\U000022F3', + "isinv;": '\U00002208', + "it;": '\U00002062', + "itilde;": '\U00000129', + "iukcy;": '\U00000456', + "iuml;": '\U000000EF', + "jcirc;": '\U00000135', + "jcy;": '\U00000439', + "jfr;": '\U0001D527', + "jmath;": '\U00000237', + "jopf;": '\U0001D55B', + "jscr;": '\U0001D4BF', + "jsercy;": '\U00000458', + "jukcy;": '\U00000454', + "kappa;": '\U000003BA', + "kappav;": '\U000003F0', + "kcedil;": '\U00000137', + "kcy;": '\U0000043A', + "kfr;": '\U0001D528', + "kgreen;": '\U00000138', + "khcy;": '\U00000445', + "kjcy;": '\U0000045C', + "kopf;": '\U0001D55C', + "kscr;": '\U0001D4C0', + "lAarr;": '\U000021DA', + "lArr;": '\U000021D0', + "lAtail;": '\U0000291B', + "lBarr;": '\U0000290E', + "lE;": '\U00002266', + "lEg;": '\U00002A8B', + "lHar;": '\U00002962', + "lacute;": '\U0000013A', + "laemptyv;": '\U000029B4', + "lagran;": '\U00002112', + "lambda;": '\U000003BB', + "lang;": '\U000027E8', + "langd;": '\U00002991', + "langle;": '\U000027E8', + "lap;": '\U00002A85', + "laquo;": '\U000000AB', + "larr;": '\U00002190', + "larrb;": '\U000021E4', + "larrbfs;": '\U0000291F', + "larrfs;": '\U0000291D', + "larrhk;": '\U000021A9', + "larrlp;": '\U000021AB', + "larrpl;": '\U00002939', + "larrsim;": '\U00002973', + "larrtl;": '\U000021A2', + "lat;": '\U00002AAB', + "latail;": '\U00002919', + "late;": '\U00002AAD', + "lbarr;": '\U0000290C', + "lbbrk;": '\U00002772', + "lbrace;": '\U0000007B', + "lbrack;": '\U0000005B', + "lbrke;": '\U0000298B', + "lbrksld;": '\U0000298F', + "lbrkslu;": '\U0000298D', + "lcaron;": '\U0000013E', + "lcedil;": '\U0000013C', + "lceil;": '\U00002308', + "lcub;": '\U0000007B', + "lcy;": '\U0000043B', + "ldca;": '\U00002936', + "ldquo;": '\U0000201C', + "ldquor;": '\U0000201E', + "ldrdhar;": '\U00002967', + "ldrushar;": '\U0000294B', + "ldsh;": '\U000021B2', + "le;": '\U00002264', + "leftarrow;": '\U00002190', + "leftarrowtail;": '\U000021A2', + "leftharpoondown;": '\U000021BD', + "leftharpoonup;": '\U000021BC', + "leftleftarrows;": '\U000021C7', + "leftrightarrow;": '\U00002194', + "leftrightarrows;": '\U000021C6', + "leftrightharpoons;": '\U000021CB', + "leftrightsquigarrow;": '\U000021AD', + "leftthreetimes;": '\U000022CB', + "leg;": '\U000022DA', + "leq;": '\U00002264', + "leqq;": '\U00002266', + "leqslant;": '\U00002A7D', + "les;": '\U00002A7D', + "lescc;": '\U00002AA8', + "lesdot;": '\U00002A7F', + "lesdoto;": '\U00002A81', + "lesdotor;": '\U00002A83', + "lesges;": '\U00002A93', + "lessapprox;": '\U00002A85', + "lessdot;": '\U000022D6', + "lesseqgtr;": '\U000022DA', + "lesseqqgtr;": '\U00002A8B', + "lessgtr;": '\U00002276', + "lesssim;": '\U00002272', + "lfisht;": '\U0000297C', + "lfloor;": '\U0000230A', + "lfr;": '\U0001D529', + "lg;": '\U00002276', + "lgE;": '\U00002A91', + "lhard;": '\U000021BD', + "lharu;": '\U000021BC', + "lharul;": '\U0000296A', + "lhblk;": '\U00002584', + "ljcy;": '\U00000459', + "ll;": '\U0000226A', + "llarr;": '\U000021C7', + "llcorner;": '\U0000231E', + "llhard;": '\U0000296B', + "lltri;": '\U000025FA', + "lmidot;": '\U00000140', + "lmoust;": '\U000023B0', + "lmoustache;": '\U000023B0', + "lnE;": '\U00002268', + "lnap;": '\U00002A89', + "lnapprox;": '\U00002A89', + "lne;": '\U00002A87', + "lneq;": '\U00002A87', + "lneqq;": '\U00002268', + "lnsim;": '\U000022E6', + "loang;": '\U000027EC', + "loarr;": '\U000021FD', + "lobrk;": '\U000027E6', + "longleftarrow;": '\U000027F5', + "longleftrightarrow;": '\U000027F7', + "longmapsto;": '\U000027FC', + "longrightarrow;": '\U000027F6', + "looparrowleft;": '\U000021AB', + "looparrowright;": '\U000021AC', + "lopar;": '\U00002985', + "lopf;": '\U0001D55D', + "loplus;": '\U00002A2D', + "lotimes;": '\U00002A34', + "lowast;": '\U00002217', + "lowbar;": '\U0000005F', + "loz;": '\U000025CA', + "lozenge;": '\U000025CA', + "lozf;": '\U000029EB', + "lpar;": '\U00000028', + "lparlt;": '\U00002993', + "lrarr;": '\U000021C6', + "lrcorner;": '\U0000231F', + "lrhar;": '\U000021CB', + "lrhard;": '\U0000296D', + "lrm;": '\U0000200E', + "lrtri;": '\U000022BF', + "lsaquo;": '\U00002039', + "lscr;": '\U0001D4C1', + "lsh;": '\U000021B0', + "lsim;": '\U00002272', + "lsime;": '\U00002A8D', + "lsimg;": '\U00002A8F', + "lsqb;": '\U0000005B', + "lsquo;": '\U00002018', + "lsquor;": '\U0000201A', + "lstrok;": '\U00000142', + "lt;": '\U0000003C', + "ltcc;": '\U00002AA6', + "ltcir;": '\U00002A79', + "ltdot;": '\U000022D6', + "lthree;": '\U000022CB', + "ltimes;": '\U000022C9', + "ltlarr;": '\U00002976', + "ltquest;": '\U00002A7B', + "ltrPar;": '\U00002996', + "ltri;": '\U000025C3', + "ltrie;": '\U000022B4', + "ltrif;": '\U000025C2', + "lurdshar;": '\U0000294A', + "luruhar;": '\U00002966', + "mDDot;": '\U0000223A', + "macr;": '\U000000AF', + "male;": '\U00002642', + "malt;": '\U00002720', + "maltese;": '\U00002720', + "map;": '\U000021A6', + "mapsto;": '\U000021A6', + "mapstodown;": '\U000021A7', + "mapstoleft;": '\U000021A4', + "mapstoup;": '\U000021A5', + "marker;": '\U000025AE', + "mcomma;": '\U00002A29', + "mcy;": '\U0000043C', + "mdash;": '\U00002014', + "measuredangle;": '\U00002221', + "mfr;": '\U0001D52A', + "mho;": '\U00002127', + "micro;": '\U000000B5', + "mid;": '\U00002223', + "midast;": '\U0000002A', + "midcir;": '\U00002AF0', + "middot;": '\U000000B7', + "minus;": '\U00002212', + "minusb;": '\U0000229F', + "minusd;": '\U00002238', + "minusdu;": '\U00002A2A', + "mlcp;": '\U00002ADB', + "mldr;": '\U00002026', + "mnplus;": '\U00002213', + "models;": '\U000022A7', + "mopf;": '\U0001D55E', + "mp;": '\U00002213', + "mscr;": '\U0001D4C2', + "mstpos;": '\U0000223E', + "mu;": '\U000003BC', + "multimap;": '\U000022B8', + "mumap;": '\U000022B8', + "nLeftarrow;": '\U000021CD', + "nLeftrightarrow;": '\U000021CE', + "nRightarrow;": '\U000021CF', + "nVDash;": '\U000022AF', + "nVdash;": '\U000022AE', + "nabla;": '\U00002207', + "nacute;": '\U00000144', + "nap;": '\U00002249', + "napos;": '\U00000149', + "napprox;": '\U00002249', + "natur;": '\U0000266E', + "natural;": '\U0000266E', + "naturals;": '\U00002115', + "nbsp;": '\U000000A0', + "ncap;": '\U00002A43', + "ncaron;": '\U00000148', + "ncedil;": '\U00000146', + "ncong;": '\U00002247', + "ncup;": '\U00002A42', + "ncy;": '\U0000043D', + "ndash;": '\U00002013', + "ne;": '\U00002260', + "neArr;": '\U000021D7', + "nearhk;": '\U00002924', + "nearr;": '\U00002197', + "nearrow;": '\U00002197', + "nequiv;": '\U00002262', + "nesear;": '\U00002928', + "nexist;": '\U00002204', + "nexists;": '\U00002204', + "nfr;": '\U0001D52B', + "nge;": '\U00002271', + "ngeq;": '\U00002271', + "ngsim;": '\U00002275', + "ngt;": '\U0000226F', + "ngtr;": '\U0000226F', + "nhArr;": '\U000021CE', + "nharr;": '\U000021AE', + "nhpar;": '\U00002AF2', + "ni;": '\U0000220B', + "nis;": '\U000022FC', + "nisd;": '\U000022FA', + "niv;": '\U0000220B', + "njcy;": '\U0000045A', + "nlArr;": '\U000021CD', + "nlarr;": '\U0000219A', + "nldr;": '\U00002025', + "nle;": '\U00002270', + "nleftarrow;": '\U0000219A', + "nleftrightarrow;": '\U000021AE', + "nleq;": '\U00002270', + "nless;": '\U0000226E', + "nlsim;": '\U00002274', + "nlt;": '\U0000226E', + "nltri;": '\U000022EA', + "nltrie;": '\U000022EC', + "nmid;": '\U00002224', + "nopf;": '\U0001D55F', + "not;": '\U000000AC', + "notin;": '\U00002209', + "notinva;": '\U00002209', + "notinvb;": '\U000022F7', + "notinvc;": '\U000022F6', + "notni;": '\U0000220C', + "notniva;": '\U0000220C', + "notnivb;": '\U000022FE', + "notnivc;": '\U000022FD', + "npar;": '\U00002226', + "nparallel;": '\U00002226', + "npolint;": '\U00002A14', + "npr;": '\U00002280', + "nprcue;": '\U000022E0', + "nprec;": '\U00002280', + "nrArr;": '\U000021CF', + "nrarr;": '\U0000219B', + "nrightarrow;": '\U0000219B', + "nrtri;": '\U000022EB', + "nrtrie;": '\U000022ED', + "nsc;": '\U00002281', + "nsccue;": '\U000022E1', + "nscr;": '\U0001D4C3', + "nshortmid;": '\U00002224', + "nshortparallel;": '\U00002226', + "nsim;": '\U00002241', + "nsime;": '\U00002244', + "nsimeq;": '\U00002244', + "nsmid;": '\U00002224', + "nspar;": '\U00002226', + "nsqsube;": '\U000022E2', + "nsqsupe;": '\U000022E3', + "nsub;": '\U00002284', + "nsube;": '\U00002288', + "nsubseteq;": '\U00002288', + "nsucc;": '\U00002281', + "nsup;": '\U00002285', + "nsupe;": '\U00002289', + "nsupseteq;": '\U00002289', + "ntgl;": '\U00002279', + "ntilde;": '\U000000F1', + "ntlg;": '\U00002278', + "ntriangleleft;": '\U000022EA', + "ntrianglelefteq;": '\U000022EC', + "ntriangleright;": '\U000022EB', + "ntrianglerighteq;": '\U000022ED', + "nu;": '\U000003BD', + "num;": '\U00000023', + "numero;": '\U00002116', + "numsp;": '\U00002007', + "nvDash;": '\U000022AD', + "nvHarr;": '\U00002904', + "nvdash;": '\U000022AC', + "nvinfin;": '\U000029DE', + "nvlArr;": '\U00002902', + "nvrArr;": '\U00002903', + "nwArr;": '\U000021D6', + "nwarhk;": '\U00002923', + "nwarr;": '\U00002196', + "nwarrow;": '\U00002196', + "nwnear;": '\U00002927', + "oS;": '\U000024C8', + "oacute;": '\U000000F3', + "oast;": '\U0000229B', + "ocir;": '\U0000229A', + "ocirc;": '\U000000F4', + "ocy;": '\U0000043E', + "odash;": '\U0000229D', + "odblac;": '\U00000151', + "odiv;": '\U00002A38', + "odot;": '\U00002299', + "odsold;": '\U000029BC', + "oelig;": '\U00000153', + "ofcir;": '\U000029BF', + "ofr;": '\U0001D52C', + "ogon;": '\U000002DB', + "ograve;": '\U000000F2', + "ogt;": '\U000029C1', + "ohbar;": '\U000029B5', + "ohm;": '\U000003A9', + "oint;": '\U0000222E', + "olarr;": '\U000021BA', + "olcir;": '\U000029BE', + "olcross;": '\U000029BB', + "oline;": '\U0000203E', + "olt;": '\U000029C0', + "omacr;": '\U0000014D', + "omega;": '\U000003C9', + "omicron;": '\U000003BF', + "omid;": '\U000029B6', + "ominus;": '\U00002296', + "oopf;": '\U0001D560', + "opar;": '\U000029B7', + "operp;": '\U000029B9', + "oplus;": '\U00002295', + "or;": '\U00002228', + "orarr;": '\U000021BB', + "ord;": '\U00002A5D', + "order;": '\U00002134', + "orderof;": '\U00002134', + "ordf;": '\U000000AA', + "ordm;": '\U000000BA', + "origof;": '\U000022B6', + "oror;": '\U00002A56', + "orslope;": '\U00002A57', + "orv;": '\U00002A5B', + "oscr;": '\U00002134', + "oslash;": '\U000000F8', + "osol;": '\U00002298', + "otilde;": '\U000000F5', + "otimes;": '\U00002297', + "otimesas;": '\U00002A36', + "ouml;": '\U000000F6', + "ovbar;": '\U0000233D', + "par;": '\U00002225', + "para;": '\U000000B6', + "parallel;": '\U00002225', + "parsim;": '\U00002AF3', + "parsl;": '\U00002AFD', + "part;": '\U00002202', + "pcy;": '\U0000043F', + "percnt;": '\U00000025', + "period;": '\U0000002E', + "permil;": '\U00002030', + "perp;": '\U000022A5', + "pertenk;": '\U00002031', + "pfr;": '\U0001D52D', + "phi;": '\U000003C6', + "phiv;": '\U000003D5', + "phmmat;": '\U00002133', + "phone;": '\U0000260E', + "pi;": '\U000003C0', + "pitchfork;": '\U000022D4', + "piv;": '\U000003D6', + "planck;": '\U0000210F', + "planckh;": '\U0000210E', + "plankv;": '\U0000210F', + "plus;": '\U0000002B', + "plusacir;": '\U00002A23', + "plusb;": '\U0000229E', + "pluscir;": '\U00002A22', + "plusdo;": '\U00002214', + "plusdu;": '\U00002A25', + "pluse;": '\U00002A72', + "plusmn;": '\U000000B1', + "plussim;": '\U00002A26', + "plustwo;": '\U00002A27', + "pm;": '\U000000B1', + "pointint;": '\U00002A15', + "popf;": '\U0001D561', + "pound;": '\U000000A3', + "pr;": '\U0000227A', + "prE;": '\U00002AB3', + "prap;": '\U00002AB7', + "prcue;": '\U0000227C', + "pre;": '\U00002AAF', + "prec;": '\U0000227A', + "precapprox;": '\U00002AB7', + "preccurlyeq;": '\U0000227C', + "preceq;": '\U00002AAF', + "precnapprox;": '\U00002AB9', + "precneqq;": '\U00002AB5', + "precnsim;": '\U000022E8', + "precsim;": '\U0000227E', + "prime;": '\U00002032', + "primes;": '\U00002119', + "prnE;": '\U00002AB5', + "prnap;": '\U00002AB9', + "prnsim;": '\U000022E8', + "prod;": '\U0000220F', + "profalar;": '\U0000232E', + "profline;": '\U00002312', + "profsurf;": '\U00002313', + "prop;": '\U0000221D', + "propto;": '\U0000221D', + "prsim;": '\U0000227E', + "prurel;": '\U000022B0', + "pscr;": '\U0001D4C5', + "psi;": '\U000003C8', + "puncsp;": '\U00002008', + "qfr;": '\U0001D52E', + "qint;": '\U00002A0C', + "qopf;": '\U0001D562', + "qprime;": '\U00002057', + "qscr;": '\U0001D4C6', + "quaternions;": '\U0000210D', + "quatint;": '\U00002A16', + "quest;": '\U0000003F', + "questeq;": '\U0000225F', + "quot;": '\U00000022', + "rAarr;": '\U000021DB', + "rArr;": '\U000021D2', + "rAtail;": '\U0000291C', + "rBarr;": '\U0000290F', + "rHar;": '\U00002964', + "racute;": '\U00000155', + "radic;": '\U0000221A', + "raemptyv;": '\U000029B3', + "rang;": '\U000027E9', + "rangd;": '\U00002992', + "range;": '\U000029A5', + "rangle;": '\U000027E9', + "raquo;": '\U000000BB', + "rarr;": '\U00002192', + "rarrap;": '\U00002975', + "rarrb;": '\U000021E5', + "rarrbfs;": '\U00002920', + "rarrc;": '\U00002933', + "rarrfs;": '\U0000291E', + "rarrhk;": '\U000021AA', + "rarrlp;": '\U000021AC', + "rarrpl;": '\U00002945', + "rarrsim;": '\U00002974', + "rarrtl;": '\U000021A3', + "rarrw;": '\U0000219D', + "ratail;": '\U0000291A', + "ratio;": '\U00002236', + "rationals;": '\U0000211A', + "rbarr;": '\U0000290D', + "rbbrk;": '\U00002773', + "rbrace;": '\U0000007D', + "rbrack;": '\U0000005D', + "rbrke;": '\U0000298C', + "rbrksld;": '\U0000298E', + "rbrkslu;": '\U00002990', + "rcaron;": '\U00000159', + "rcedil;": '\U00000157', + "rceil;": '\U00002309', + "rcub;": '\U0000007D', + "rcy;": '\U00000440', + "rdca;": '\U00002937', + "rdldhar;": '\U00002969', + "rdquo;": '\U0000201D', + "rdquor;": '\U0000201D', + "rdsh;": '\U000021B3', + "real;": '\U0000211C', + "realine;": '\U0000211B', + "realpart;": '\U0000211C', + "reals;": '\U0000211D', + "rect;": '\U000025AD', + "reg;": '\U000000AE', + "rfisht;": '\U0000297D', + "rfloor;": '\U0000230B', + "rfr;": '\U0001D52F', + "rhard;": '\U000021C1', + "rharu;": '\U000021C0', + "rharul;": '\U0000296C', + "rho;": '\U000003C1', + "rhov;": '\U000003F1', + "rightarrow;": '\U00002192', + "rightarrowtail;": '\U000021A3', + "rightharpoondown;": '\U000021C1', + "rightharpoonup;": '\U000021C0', + "rightleftarrows;": '\U000021C4', + "rightleftharpoons;": '\U000021CC', + "rightrightarrows;": '\U000021C9', + "rightsquigarrow;": '\U0000219D', + "rightthreetimes;": '\U000022CC', + "ring;": '\U000002DA', + "risingdotseq;": '\U00002253', + "rlarr;": '\U000021C4', + "rlhar;": '\U000021CC', + "rlm;": '\U0000200F', + "rmoust;": '\U000023B1', + "rmoustache;": '\U000023B1', + "rnmid;": '\U00002AEE', + "roang;": '\U000027ED', + "roarr;": '\U000021FE', + "robrk;": '\U000027E7', + "ropar;": '\U00002986', + "ropf;": '\U0001D563', + "roplus;": '\U00002A2E', + "rotimes;": '\U00002A35', + "rpar;": '\U00000029', + "rpargt;": '\U00002994', + "rppolint;": '\U00002A12', + "rrarr;": '\U000021C9', + "rsaquo;": '\U0000203A', + "rscr;": '\U0001D4C7', + "rsh;": '\U000021B1', + "rsqb;": '\U0000005D', + "rsquo;": '\U00002019', + "rsquor;": '\U00002019', + "rthree;": '\U000022CC', + "rtimes;": '\U000022CA', + "rtri;": '\U000025B9', + "rtrie;": '\U000022B5', + "rtrif;": '\U000025B8', + "rtriltri;": '\U000029CE', + "ruluhar;": '\U00002968', + "rx;": '\U0000211E', + "sacute;": '\U0000015B', + "sbquo;": '\U0000201A', + "sc;": '\U0000227B', + "scE;": '\U00002AB4', + "scap;": '\U00002AB8', + "scaron;": '\U00000161', + "sccue;": '\U0000227D', + "sce;": '\U00002AB0', + "scedil;": '\U0000015F', + "scirc;": '\U0000015D', + "scnE;": '\U00002AB6', + "scnap;": '\U00002ABA', + "scnsim;": '\U000022E9', + "scpolint;": '\U00002A13', + "scsim;": '\U0000227F', + "scy;": '\U00000441', + "sdot;": '\U000022C5', + "sdotb;": '\U000022A1', + "sdote;": '\U00002A66', + "seArr;": '\U000021D8', + "searhk;": '\U00002925', + "searr;": '\U00002198', + "searrow;": '\U00002198', + "sect;": '\U000000A7', + "semi;": '\U0000003B', + "seswar;": '\U00002929', + "setminus;": '\U00002216', + "setmn;": '\U00002216', + "sext;": '\U00002736', + "sfr;": '\U0001D530', + "sfrown;": '\U00002322', + "sharp;": '\U0000266F', + "shchcy;": '\U00000449', + "shcy;": '\U00000448', + "shortmid;": '\U00002223', + "shortparallel;": '\U00002225', + "shy;": '\U000000AD', + "sigma;": '\U000003C3', + "sigmaf;": '\U000003C2', + "sigmav;": '\U000003C2', + "sim;": '\U0000223C', + "simdot;": '\U00002A6A', + "sime;": '\U00002243', + "simeq;": '\U00002243', + "simg;": '\U00002A9E', + "simgE;": '\U00002AA0', + "siml;": '\U00002A9D', + "simlE;": '\U00002A9F', + "simne;": '\U00002246', + "simplus;": '\U00002A24', + "simrarr;": '\U00002972', + "slarr;": '\U00002190', + "smallsetminus;": '\U00002216', + "smashp;": '\U00002A33', + "smeparsl;": '\U000029E4', + "smid;": '\U00002223', + "smile;": '\U00002323', + "smt;": '\U00002AAA', + "smte;": '\U00002AAC', + "softcy;": '\U0000044C', + "sol;": '\U0000002F', + "solb;": '\U000029C4', + "solbar;": '\U0000233F', + "sopf;": '\U0001D564', + "spades;": '\U00002660', + "spadesuit;": '\U00002660', + "spar;": '\U00002225', + "sqcap;": '\U00002293', + "sqcup;": '\U00002294', + "sqsub;": '\U0000228F', + "sqsube;": '\U00002291', + "sqsubset;": '\U0000228F', + "sqsubseteq;": '\U00002291', + "sqsup;": '\U00002290', + "sqsupe;": '\U00002292', + "sqsupset;": '\U00002290', + "sqsupseteq;": '\U00002292', + "squ;": '\U000025A1', + "square;": '\U000025A1', + "squarf;": '\U000025AA', + "squf;": '\U000025AA', + "srarr;": '\U00002192', + "sscr;": '\U0001D4C8', + "ssetmn;": '\U00002216', + "ssmile;": '\U00002323', + "sstarf;": '\U000022C6', + "star;": '\U00002606', + "starf;": '\U00002605', + "straightepsilon;": '\U000003F5', + "straightphi;": '\U000003D5', + "strns;": '\U000000AF', + "sub;": '\U00002282', + "subE;": '\U00002AC5', + "subdot;": '\U00002ABD', + "sube;": '\U00002286', + "subedot;": '\U00002AC3', + "submult;": '\U00002AC1', + "subnE;": '\U00002ACB', + "subne;": '\U0000228A', + "subplus;": '\U00002ABF', + "subrarr;": '\U00002979', + "subset;": '\U00002282', + "subseteq;": '\U00002286', + "subseteqq;": '\U00002AC5', + "subsetneq;": '\U0000228A', + "subsetneqq;": '\U00002ACB', + "subsim;": '\U00002AC7', + "subsub;": '\U00002AD5', + "subsup;": '\U00002AD3', + "succ;": '\U0000227B', + "succapprox;": '\U00002AB8', + "succcurlyeq;": '\U0000227D', + "succeq;": '\U00002AB0', + "succnapprox;": '\U00002ABA', + "succneqq;": '\U00002AB6', + "succnsim;": '\U000022E9', + "succsim;": '\U0000227F', + "sum;": '\U00002211', + "sung;": '\U0000266A', + "sup;": '\U00002283', + "sup1;": '\U000000B9', + "sup2;": '\U000000B2', + "sup3;": '\U000000B3', + "supE;": '\U00002AC6', + "supdot;": '\U00002ABE', + "supdsub;": '\U00002AD8', + "supe;": '\U00002287', + "supedot;": '\U00002AC4', + "suphsol;": '\U000027C9', + "suphsub;": '\U00002AD7', + "suplarr;": '\U0000297B', + "supmult;": '\U00002AC2', + "supnE;": '\U00002ACC', + "supne;": '\U0000228B', + "supplus;": '\U00002AC0', + "supset;": '\U00002283', + "supseteq;": '\U00002287', + "supseteqq;": '\U00002AC6', + "supsetneq;": '\U0000228B', + "supsetneqq;": '\U00002ACC', + "supsim;": '\U00002AC8', + "supsub;": '\U00002AD4', + "supsup;": '\U00002AD6', + "swArr;": '\U000021D9', + "swarhk;": '\U00002926', + "swarr;": '\U00002199', + "swarrow;": '\U00002199', + "swnwar;": '\U0000292A', + "szlig;": '\U000000DF', + "target;": '\U00002316', + "tau;": '\U000003C4', + "tbrk;": '\U000023B4', + "tcaron;": '\U00000165', + "tcedil;": '\U00000163', + "tcy;": '\U00000442', + "tdot;": '\U000020DB', + "telrec;": '\U00002315', + "tfr;": '\U0001D531', + "there4;": '\U00002234', + "therefore;": '\U00002234', + "theta;": '\U000003B8', + "thetasym;": '\U000003D1', + "thetav;": '\U000003D1', + "thickapprox;": '\U00002248', + "thicksim;": '\U0000223C', + "thinsp;": '\U00002009', + "thkap;": '\U00002248', + "thksim;": '\U0000223C', + "thorn;": '\U000000FE', + "tilde;": '\U000002DC', + "times;": '\U000000D7', + "timesb;": '\U000022A0', + "timesbar;": '\U00002A31', + "timesd;": '\U00002A30', + "tint;": '\U0000222D', + "toea;": '\U00002928', + "top;": '\U000022A4', + "topbot;": '\U00002336', + "topcir;": '\U00002AF1', + "topf;": '\U0001D565', + "topfork;": '\U00002ADA', + "tosa;": '\U00002929', + "tprime;": '\U00002034', + "trade;": '\U00002122', + "triangle;": '\U000025B5', + "triangledown;": '\U000025BF', + "triangleleft;": '\U000025C3', + "trianglelefteq;": '\U000022B4', + "triangleq;": '\U0000225C', + "triangleright;": '\U000025B9', + "trianglerighteq;": '\U000022B5', + "tridot;": '\U000025EC', + "trie;": '\U0000225C', + "triminus;": '\U00002A3A', + "triplus;": '\U00002A39', + "trisb;": '\U000029CD', + "tritime;": '\U00002A3B', + "trpezium;": '\U000023E2', + "tscr;": '\U0001D4C9', + "tscy;": '\U00000446', + "tshcy;": '\U0000045B', + "tstrok;": '\U00000167', + "twixt;": '\U0000226C', + "twoheadleftarrow;": '\U0000219E', + "twoheadrightarrow;": '\U000021A0', + "uArr;": '\U000021D1', + "uHar;": '\U00002963', + "uacute;": '\U000000FA', + "uarr;": '\U00002191', + "ubrcy;": '\U0000045E', + "ubreve;": '\U0000016D', + "ucirc;": '\U000000FB', + "ucy;": '\U00000443', + "udarr;": '\U000021C5', + "udblac;": '\U00000171', + "udhar;": '\U0000296E', + "ufisht;": '\U0000297E', + "ufr;": '\U0001D532', + "ugrave;": '\U000000F9', + "uharl;": '\U000021BF', + "uharr;": '\U000021BE', + "uhblk;": '\U00002580', + "ulcorn;": '\U0000231C', + "ulcorner;": '\U0000231C', + "ulcrop;": '\U0000230F', + "ultri;": '\U000025F8', + "umacr;": '\U0000016B', + "uml;": '\U000000A8', + "uogon;": '\U00000173', + "uopf;": '\U0001D566', + "uparrow;": '\U00002191', + "updownarrow;": '\U00002195', + "upharpoonleft;": '\U000021BF', + "upharpoonright;": '\U000021BE', + "uplus;": '\U0000228E', + "upsi;": '\U000003C5', + "upsih;": '\U000003D2', + "upsilon;": '\U000003C5', + "upuparrows;": '\U000021C8', + "urcorn;": '\U0000231D', + "urcorner;": '\U0000231D', + "urcrop;": '\U0000230E', + "uring;": '\U0000016F', + "urtri;": '\U000025F9', + "uscr;": '\U0001D4CA', + "utdot;": '\U000022F0', + "utilde;": '\U00000169', + "utri;": '\U000025B5', + "utrif;": '\U000025B4', + "uuarr;": '\U000021C8', + "uuml;": '\U000000FC', + "uwangle;": '\U000029A7', + "vArr;": '\U000021D5', + "vBar;": '\U00002AE8', + "vBarv;": '\U00002AE9', + "vDash;": '\U000022A8', + "vangrt;": '\U0000299C', + "varepsilon;": '\U000003F5', + "varkappa;": '\U000003F0', + "varnothing;": '\U00002205', + "varphi;": '\U000003D5', + "varpi;": '\U000003D6', + "varpropto;": '\U0000221D', + "varr;": '\U00002195', + "varrho;": '\U000003F1', + "varsigma;": '\U000003C2', + "vartheta;": '\U000003D1', + "vartriangleleft;": '\U000022B2', + "vartriangleright;": '\U000022B3', + "vcy;": '\U00000432', + "vdash;": '\U000022A2', + "vee;": '\U00002228', + "veebar;": '\U000022BB', + "veeeq;": '\U0000225A', + "vellip;": '\U000022EE', + "verbar;": '\U0000007C', + "vert;": '\U0000007C', + "vfr;": '\U0001D533', + "vltri;": '\U000022B2', + "vopf;": '\U0001D567', + "vprop;": '\U0000221D', + "vrtri;": '\U000022B3', + "vscr;": '\U0001D4CB', + "vzigzag;": '\U0000299A', + "wcirc;": '\U00000175', + "wedbar;": '\U00002A5F', + "wedge;": '\U00002227', + "wedgeq;": '\U00002259', + "weierp;": '\U00002118', + "wfr;": '\U0001D534', + "wopf;": '\U0001D568', + "wp;": '\U00002118', + "wr;": '\U00002240', + "wreath;": '\U00002240', + "wscr;": '\U0001D4CC', + "xcap;": '\U000022C2', + "xcirc;": '\U000025EF', + "xcup;": '\U000022C3', + "xdtri;": '\U000025BD', + "xfr;": '\U0001D535', + "xhArr;": '\U000027FA', + "xharr;": '\U000027F7', + "xi;": '\U000003BE', + "xlArr;": '\U000027F8', + "xlarr;": '\U000027F5', + "xmap;": '\U000027FC', + "xnis;": '\U000022FB', + "xodot;": '\U00002A00', + "xopf;": '\U0001D569', + "xoplus;": '\U00002A01', + "xotime;": '\U00002A02', + "xrArr;": '\U000027F9', + "xrarr;": '\U000027F6', + "xscr;": '\U0001D4CD', + "xsqcup;": '\U00002A06', + "xuplus;": '\U00002A04', + "xutri;": '\U000025B3', + "xvee;": '\U000022C1', + "xwedge;": '\U000022C0', + "yacute;": '\U000000FD', + "yacy;": '\U0000044F', + "ycirc;": '\U00000177', + "ycy;": '\U0000044B', + "yen;": '\U000000A5', + "yfr;": '\U0001D536', + "yicy;": '\U00000457', + "yopf;": '\U0001D56A', + "yscr;": '\U0001D4CE', + "yucy;": '\U0000044E', + "yuml;": '\U000000FF', + "zacute;": '\U0000017A', + "zcaron;": '\U0000017E', + "zcy;": '\U00000437', + "zdot;": '\U0000017C', + "zeetrf;": '\U00002128', + "zeta;": '\U000003B6', + "zfr;": '\U0001D537', + "zhcy;": '\U00000436', + "zigrarr;": '\U000021DD', + "zopf;": '\U0001D56B', + "zscr;": '\U0001D4CF', + "zwj;": '\U0000200D', + "zwnj;": '\U0000200C', + "AElig": '\U000000C6', + "AMP": '\U00000026', + "Aacute": '\U000000C1', + "Acirc": '\U000000C2', + "Agrave": '\U000000C0', + "Aring": '\U000000C5', + "Atilde": '\U000000C3', + "Auml": '\U000000C4', + "COPY": '\U000000A9', + "Ccedil": '\U000000C7', + "ETH": '\U000000D0', + "Eacute": '\U000000C9', + "Ecirc": '\U000000CA', + "Egrave": '\U000000C8', + "Euml": '\U000000CB', + "GT": '\U0000003E', + "Iacute": '\U000000CD', + "Icirc": '\U000000CE', + "Igrave": '\U000000CC', + "Iuml": '\U000000CF', + "LT": '\U0000003C', + "Ntilde": '\U000000D1', + "Oacute": '\U000000D3', + "Ocirc": '\U000000D4', + "Ograve": '\U000000D2', + "Oslash": '\U000000D8', + "Otilde": '\U000000D5', + "Ouml": '\U000000D6', + "QUOT": '\U00000022', + "REG": '\U000000AE', + "THORN": '\U000000DE', + "Uacute": '\U000000DA', + "Ucirc": '\U000000DB', + "Ugrave": '\U000000D9', + "Uuml": '\U000000DC', + "Yacute": '\U000000DD', + "aacute": '\U000000E1', + "acirc": '\U000000E2', + "acute": '\U000000B4', + "aelig": '\U000000E6', + "agrave": '\U000000E0', + "amp": '\U00000026', + "aring": '\U000000E5', + "atilde": '\U000000E3', + "auml": '\U000000E4', + "brvbar": '\U000000A6', + "ccedil": '\U000000E7', + "cedil": '\U000000B8', + "cent": '\U000000A2', + "copy": '\U000000A9', + "curren": '\U000000A4', + "deg": '\U000000B0', + "divide": '\U000000F7', + "eacute": '\U000000E9', + "ecirc": '\U000000EA', + "egrave": '\U000000E8', + "eth": '\U000000F0', + "euml": '\U000000EB', + "frac12": '\U000000BD', + "frac14": '\U000000BC', + "frac34": '\U000000BE', + "gt": '\U0000003E', + "iacute": '\U000000ED', + "icirc": '\U000000EE', + "iexcl": '\U000000A1', + "igrave": '\U000000EC', + "iquest": '\U000000BF', + "iuml": '\U000000EF', + "laquo": '\U000000AB', + "lt": '\U0000003C', + "macr": '\U000000AF', + "micro": '\U000000B5', + "middot": '\U000000B7', + "nbsp": '\U000000A0', + "not": '\U000000AC', + "ntilde": '\U000000F1', + "oacute": '\U000000F3', + "ocirc": '\U000000F4', + "ograve": '\U000000F2', + "ordf": '\U000000AA', + "ordm": '\U000000BA', + "oslash": '\U000000F8', + "otilde": '\U000000F5', + "ouml": '\U000000F6', + "para": '\U000000B6', + "plusmn": '\U000000B1', + "pound": '\U000000A3', + "quot": '\U00000022', + "raquo": '\U000000BB', + "reg": '\U000000AE', + "sect": '\U000000A7', + "shy": '\U000000AD', + "sup1": '\U000000B9', + "sup2": '\U000000B2', + "sup3": '\U000000B3', + "szlig": '\U000000DF', + "thorn": '\U000000FE', + "times": '\U000000D7', + "uacute": '\U000000FA', + "ucirc": '\U000000FB', + "ugrave": '\U000000F9', + "uml": '\U000000A8', + "uuml": '\U000000FC', + "yacute": '\U000000FD', + "yen": '\U000000A5', + "yuml": '\U000000FF', + } + + entity2 = map[string][2]rune{ + // TODO(nigeltao): Handle replacements that are wider than their names. + // "nLt;": {'\u226A', '\u20D2'}, + // "nGt;": {'\u226B', '\u20D2'}, + "NotEqualTilde;": {'\u2242', '\u0338'}, + "NotGreaterFullEqual;": {'\u2267', '\u0338'}, + "NotGreaterGreater;": {'\u226B', '\u0338'}, + "NotGreaterSlantEqual;": {'\u2A7E', '\u0338'}, + "NotHumpDownHump;": {'\u224E', '\u0338'}, + "NotHumpEqual;": {'\u224F', '\u0338'}, + "NotLeftTriangleBar;": {'\u29CF', '\u0338'}, + "NotLessLess;": {'\u226A', '\u0338'}, + "NotLessSlantEqual;": {'\u2A7D', '\u0338'}, + "NotNestedGreaterGreater;": {'\u2AA2', '\u0338'}, + "NotNestedLessLess;": {'\u2AA1', '\u0338'}, + "NotPrecedesEqual;": {'\u2AAF', '\u0338'}, + "NotRightTriangleBar;": {'\u29D0', '\u0338'}, + "NotSquareSubset;": {'\u228F', '\u0338'}, + "NotSquareSuperset;": {'\u2290', '\u0338'}, + "NotSubset;": {'\u2282', '\u20D2'}, + "NotSucceedsEqual;": {'\u2AB0', '\u0338'}, + "NotSucceedsTilde;": {'\u227F', '\u0338'}, + "NotSuperset;": {'\u2283', '\u20D2'}, + "ThickSpace;": {'\u205F', '\u200A'}, + "acE;": {'\u223E', '\u0333'}, + "bne;": {'\u003D', '\u20E5'}, + "bnequiv;": {'\u2261', '\u20E5'}, + "caps;": {'\u2229', '\uFE00'}, + "cups;": {'\u222A', '\uFE00'}, + "fjlig;": {'\u0066', '\u006A'}, + "gesl;": {'\u22DB', '\uFE00'}, + "gvertneqq;": {'\u2269', '\uFE00'}, + "gvnE;": {'\u2269', '\uFE00'}, + "lates;": {'\u2AAD', '\uFE00'}, + "lesg;": {'\u22DA', '\uFE00'}, + "lvertneqq;": {'\u2268', '\uFE00'}, + "lvnE;": {'\u2268', '\uFE00'}, + "nGg;": {'\u22D9', '\u0338'}, + "nGtv;": {'\u226B', '\u0338'}, + "nLl;": {'\u22D8', '\u0338'}, + "nLtv;": {'\u226A', '\u0338'}, + "nang;": {'\u2220', '\u20D2'}, + "napE;": {'\u2A70', '\u0338'}, + "napid;": {'\u224B', '\u0338'}, + "nbump;": {'\u224E', '\u0338'}, + "nbumpe;": {'\u224F', '\u0338'}, + "ncongdot;": {'\u2A6D', '\u0338'}, + "nedot;": {'\u2250', '\u0338'}, + "nesim;": {'\u2242', '\u0338'}, + "ngE;": {'\u2267', '\u0338'}, + "ngeqq;": {'\u2267', '\u0338'}, + "ngeqslant;": {'\u2A7E', '\u0338'}, + "nges;": {'\u2A7E', '\u0338'}, + "nlE;": {'\u2266', '\u0338'}, + "nleqq;": {'\u2266', '\u0338'}, + "nleqslant;": {'\u2A7D', '\u0338'}, + "nles;": {'\u2A7D', '\u0338'}, + "notinE;": {'\u22F9', '\u0338'}, + "notindot;": {'\u22F5', '\u0338'}, + "nparsl;": {'\u2AFD', '\u20E5'}, + "npart;": {'\u2202', '\u0338'}, + "npre;": {'\u2AAF', '\u0338'}, + "npreceq;": {'\u2AAF', '\u0338'}, + "nrarrc;": {'\u2933', '\u0338'}, + "nrarrw;": {'\u219D', '\u0338'}, + "nsce;": {'\u2AB0', '\u0338'}, + "nsubE;": {'\u2AC5', '\u0338'}, + "nsubset;": {'\u2282', '\u20D2'}, + "nsubseteqq;": {'\u2AC5', '\u0338'}, + "nsucceq;": {'\u2AB0', '\u0338'}, + "nsupE;": {'\u2AC6', '\u0338'}, + "nsupset;": {'\u2283', '\u20D2'}, + "nsupseteqq;": {'\u2AC6', '\u0338'}, + "nvap;": {'\u224D', '\u20D2'}, + "nvge;": {'\u2265', '\u20D2'}, + "nvgt;": {'\u003E', '\u20D2'}, + "nvle;": {'\u2264', '\u20D2'}, + "nvlt;": {'\u003C', '\u20D2'}, + "nvltrie;": {'\u22B4', '\u20D2'}, + "nvrtrie;": {'\u22B5', '\u20D2'}, + "nvsim;": {'\u223C', '\u20D2'}, + "race;": {'\u223D', '\u0331'}, + "smtes;": {'\u2AAC', '\uFE00'}, + "sqcaps;": {'\u2293', '\uFE00'}, + "sqcups;": {'\u2294', '\uFE00'}, + "varsubsetneq;": {'\u228A', '\uFE00'}, + "varsubsetneqq;": {'\u2ACB', '\uFE00'}, + "varsupsetneq;": {'\u228B', '\uFE00'}, + "varsupsetneqq;": {'\u2ACC', '\uFE00'}, + "vnsub;": {'\u2282', '\u20D2'}, + "vnsup;": {'\u2283', '\u20D2'}, + "vsubnE;": {'\u2ACB', '\uFE00'}, + "vsubne;": {'\u228A', '\uFE00'}, + "vsupnE;": {'\u2ACC', '\uFE00'}, + "vsupne;": {'\u228B', '\uFE00'}, + } } diff --git a/libgo/go/html/entity_test.go b/libgo/go/html/entity_test.go index b53f866..6688ed2 100644 --- a/libgo/go/html/entity_test.go +++ b/libgo/go/html/entity_test.go @@ -9,7 +9,15 @@ import ( "unicode/utf8" ) +func init() { + UnescapeString("") // force load of entity maps +} + func TestEntityLength(t *testing.T) { + if len(entity) == 0 || len(entity2) == 0 { + t.Fatal("maps not loaded") + } + // We verify that the length of UTF-8 encoding of each value is <= 1 + len(key). // The +1 comes from the leading "&". This property implies that the length of // unescaped text is <= the length of escaped text. diff --git a/libgo/go/html/escape.go b/libgo/go/html/escape.go index 8dd1f4a..dae404f 100644 --- a/libgo/go/html/escape.go +++ b/libgo/go/html/escape.go @@ -185,6 +185,7 @@ func EscapeString(s string) string { // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't // always true. func UnescapeString(s string) string { + populateMapsOnce.Do(populateMaps) i := strings.IndexByte(s, '&') if i < 0 { diff --git a/libgo/go/html/template/attr.go b/libgo/go/html/template/attr.go index 92d2789..22922e6 100644 --- a/libgo/go/html/template/attr.go +++ b/libgo/go/html/template/attr.go @@ -13,9 +13,9 @@ import ( // other content, or affects the contents, idempotency, or credentials of a // network message, then the value in this map is contentTypeUnsafe. // This map is derived from HTML5, specifically -// http://www.w3.org/TR/html5/Overview.html#attributes-1 +// https://www.w3.org/TR/html5/Overview.html#attributes-1 // as well as "%URI"-typed attributes from -// http://www.w3.org/TR/html4/index/attributes.html +// https://www.w3.org/TR/html4/index/attributes.html var attrTypeMap = map[string]contentType{ "accept": contentTypePlain, "accept-charset": contentTypeUnsafe, @@ -90,7 +90,7 @@ var attrTypeMap = map[string]contentType{ "name": contentTypePlain, "novalidate": contentTypeUnsafe, // Skip handler names from - // http://www.w3.org/TR/html5/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects + // https://www.w3.org/TR/html5/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects // since we have special handling in attrType. "open": contentTypePlain, "optimum": contentTypePlain, @@ -160,7 +160,7 @@ func attrType(name string) contentType { // Heuristics to prevent "javascript:..." injection in custom // data attributes and custom attributes like g:tweetUrl. - // http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes + // https://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes // "Custom data attributes are intended to store custom data // private to the page or application, for which there are no // more appropriate attributes or elements." diff --git a/libgo/go/html/template/attr_string.go b/libgo/go/html/template/attr_string.go new file mode 100644 index 0000000..babe70c --- /dev/null +++ b/libgo/go/html/template/attr_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type attr"; DO NOT EDIT. + +package template + +import "strconv" + +const _attr_name = "attrNoneattrScriptattrScriptTypeattrStyleattrURLattrSrcset" + +var _attr_index = [...]uint8{0, 8, 18, 32, 41, 48, 58} + +func (i attr) String() string { + if i >= attr(len(_attr_index)-1) { + return "attr(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _attr_name[_attr_index[i]:_attr_index[i+1]] +} diff --git a/libgo/go/html/template/clone_test.go b/libgo/go/html/template/clone_test.go index b500715..e292321 100644 --- a/libgo/go/html/template/clone_test.go +++ b/libgo/go/html/template/clone_test.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "io/ioutil" + "strings" "sync" "testing" "text/template/parse" @@ -262,3 +263,17 @@ func TestCloneRedefinedName(t *testing.T) { } } } + +// Issue 24791. +func TestClonePipe(t *testing.T) { + a := Must(New("a").Parse(`{{define "a"}}{{range $v := .A}}{{$v}}{{end}}{{end}}`)) + data := struct{ A []string }{A: []string{"hi"}} + b := Must(a.Clone()) + var buf strings.Builder + if err := b.Execute(&buf, &data); err != nil { + t.Fatal(err) + } + if got, want := buf.String(), "hi"; got != want { + t.Errorf("got %q want %q", got, want) + } +} diff --git a/libgo/go/html/template/content.go b/libgo/go/html/template/content.go index e7cdedc..6ba87a9 100644 --- a/libgo/go/html/template/content.go +++ b/libgo/go/html/template/content.go @@ -16,7 +16,7 @@ type ( // 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/#parsing and + // See https://www.w3.org/TR/css3-syntax/#parsing and // https://web.archive.org/web/20090211114933/http://w3.org/TR/css3-syntax#style // // Use of this type presents a security risk: @@ -85,7 +85,7 @@ type ( URL string // Srcset encapsulates a known safe srcset attribute - // (see http://w3c.github.io/html/semantics-embedded-content.html#element-attrdef-img-srcset). + // (see https://w3c.github.io/html/semantics-embedded-content.html#element-attrdef-img-srcset). // // Use of this type presents a security risk: // the encapsulated content should come from a trusted source, @@ -169,8 +169,17 @@ func stringify(args ...interface{}) (string, contentType) { return string(s), contentTypeSrcset } } - for i, arg := range args { + i := 0 + for _, arg := range args { + // We skip untyped nil arguments for backward compatibility. + // Without this they would be output as <nil>, escaped. + // See issue 25875. + if arg == nil { + continue + } + args[i] = indirectToStringerOrError(arg) + i++ } - return fmt.Sprint(args...), contentTypePlain + return fmt.Sprint(args[:i]...), contentTypePlain } diff --git a/libgo/go/html/template/content_test.go b/libgo/go/html/template/content_test.go index cc092f5..72d56f5 100644 --- a/libgo/go/html/template/content_test.go +++ b/libgo/go/html/template/content_test.go @@ -447,10 +447,9 @@ func TestEscapingNilNonemptyInterfaces(t *testing.T) { testData := struct{ E error }{} // any non-empty interface here will do; error is just ready at hand tmpl.Execute(got, testData) - // Use this data instead of just hard-coding "<nil>" to avoid - // dependencies on the html escaper and the behavior of fmt w.r.t. nil. + // A non-empty interface should print like an empty interface. want := new(bytes.Buffer) - data := struct{ E string }{E: fmt.Sprint(nil)} + data := struct{ E interface{} }{} tmpl.Execute(want, data) if !bytes.Equal(want.Bytes(), got.Bytes()) { diff --git a/libgo/go/html/template/context.go b/libgo/go/html/template/context.go index 50730d3..45be3a6 100644 --- a/libgo/go/html/template/context.go +++ b/libgo/go/html/template/context.go @@ -13,7 +13,7 @@ import ( // // The zero value of type context is the start context for a template that // produces an HTML fragment as defined at -// http://www.w3.org/TR/html5/syntax.html#the-end +// https://www.w3.org/TR/html5/syntax.html#the-end // where the context element is null. type context struct { state state @@ -77,6 +77,8 @@ func (c context) mangle(templateName string) string { // is a single token in HTML's grammar but in a template spans several nodes. type state uint8 +//go:generate stringer -type state + const ( // stateText is parsed character data. An HTML parser is in // this state when its parse position is outside an HTML tag, @@ -96,7 +98,7 @@ const ( // stateHTMLCmt occurs inside an <!-- HTML comment -->. stateHTMLCmt // stateRCDATA occurs inside an RCDATA element (<textarea> or <title>) - // as described at http://www.w3.org/TR/html5/syntax.html#elements-0 + // as described at https://www.w3.org/TR/html5/syntax.html#elements-0 stateRCDATA // stateAttr occurs inside an HTML attribute whose content is text. stateAttr @@ -137,41 +139,6 @@ const ( stateError ) -var stateNames = [...]string{ - stateText: "stateText", - stateTag: "stateTag", - stateAttrName: "stateAttrName", - stateAfterName: "stateAfterName", - stateBeforeValue: "stateBeforeValue", - stateHTMLCmt: "stateHTMLCmt", - stateRCDATA: "stateRCDATA", - stateAttr: "stateAttr", - stateURL: "stateURL", - stateSrcset: "stateSrcset", - stateJS: "stateJS", - stateJSDqStr: "stateJSDqStr", - stateJSSqStr: "stateJSSqStr", - stateJSRegexp: "stateJSRegexp", - stateJSBlockCmt: "stateJSBlockCmt", - stateJSLineCmt: "stateJSLineCmt", - stateCSS: "stateCSS", - stateCSSDqStr: "stateCSSDqStr", - stateCSSSqStr: "stateCSSSqStr", - stateCSSDqURL: "stateCSSDqURL", - stateCSSSqURL: "stateCSSSqURL", - stateCSSURL: "stateCSSURL", - stateCSSBlockCmt: "stateCSSBlockCmt", - stateCSSLineCmt: "stateCSSLineCmt", - stateError: "stateError", -} - -func (s state) String() string { - if int(s) < len(stateNames) { - return stateNames[s] - } - return fmt.Sprintf("illegal state %d", int(s)) -} - // isComment is true for any state that contains content meant for template // authors & maintainers, not for end-users or machines. func isComment(s state) bool { @@ -194,6 +161,8 @@ func isInTag(s state) bool { // delim is the delimiter that will end the current HTML attribute. type delim uint8 +//go:generate stringer -type delim + const ( // delimNone occurs outside any attribute. delimNone delim = iota @@ -206,24 +175,12 @@ const ( delimSpaceOrTagEnd ) -var delimNames = [...]string{ - delimNone: "delimNone", - delimDoubleQuote: "delimDoubleQuote", - delimSingleQuote: "delimSingleQuote", - delimSpaceOrTagEnd: "delimSpaceOrTagEnd", -} - -func (d delim) String() string { - if int(d) < len(delimNames) { - return delimNames[d] - } - return fmt.Sprintf("illegal delim %d", int(d)) -} - // urlPart identifies a part in an RFC 3986 hierarchical URL to allow different // encoding strategies. type urlPart uint8 +//go:generate stringer -type urlPart + const ( // urlPartNone occurs when not in a URL, or possibly at the start: // ^ in "^http://auth/path?k=v#frag". @@ -239,24 +196,12 @@ const ( urlPartUnknown ) -var urlPartNames = [...]string{ - urlPartNone: "urlPartNone", - urlPartPreQuery: "urlPartPreQuery", - urlPartQueryOrFrag: "urlPartQueryOrFrag", - urlPartUnknown: "urlPartUnknown", -} - -func (u urlPart) String() string { - if int(u) < len(urlPartNames) { - return urlPartNames[u] - } - return fmt.Sprintf("illegal urlPart %d", int(u)) -} - // jsCtx determines whether a '/' starts a regular expression literal or a // division operator. type jsCtx uint8 +//go:generate stringer -type jsCtx + const ( // jsCtxRegexp occurs where a '/' would start a regexp literal. jsCtxRegexp jsCtx = iota @@ -266,18 +211,6 @@ const ( jsCtxUnknown ) -func (c jsCtx) String() string { - switch c { - case jsCtxRegexp: - return "jsCtxRegexp" - case jsCtxDivOp: - return "jsCtxDivOp" - case jsCtxUnknown: - return "jsCtxUnknown" - } - return fmt.Sprintf("illegal jsCtx %d", int(c)) -} - // element identifies the HTML element when inside a start tag or special body. // Certain HTML element (for example <script> and <style>) have bodies that are // treated differently from stateText so the element type is necessary to @@ -285,6 +218,8 @@ func (c jsCtx) String() string { // end delimiter for the body. type element uint8 +//go:generate stringer -type element + const ( // elementNone occurs outside a special tag or special element body. elementNone element = iota @@ -299,20 +234,7 @@ const ( elementTitle ) -var elementNames = [...]string{ - elementNone: "elementNone", - elementScript: "elementScript", - elementStyle: "elementStyle", - elementTextarea: "elementTextarea", - elementTitle: "elementTitle", -} - -func (e element) String() string { - if int(e) < len(elementNames) { - return elementNames[e] - } - return fmt.Sprintf("illegal element %d", int(e)) -} +//go:generate stringer -type attr // attr identifies the current HTML attribute when inside the attribute, // that is, starting from stateAttrName until stateTag/stateText (exclusive). @@ -332,19 +254,3 @@ const ( // attrSrcset corresponds to a srcset attribute. attrSrcset ) - -var attrNames = [...]string{ - attrNone: "attrNone", - attrScript: "attrScript", - attrScriptType: "attrScriptType", - attrStyle: "attrStyle", - attrURL: "attrURL", - attrSrcset: "attrSrcset", -} - -func (a attr) String() string { - if int(a) < len(attrNames) { - return attrNames[a] - } - return fmt.Sprintf("illegal attr %d", int(a)) -} diff --git a/libgo/go/html/template/css.go b/libgo/go/html/template/css.go index 9154d86..1587af8 100644 --- a/libgo/go/html/template/css.go +++ b/libgo/go/html/template/css.go @@ -28,7 +28,7 @@ func endsWithCSSKeyword(b []byte, kw string) bool { } // Many CSS keywords, such as "!important" can have characters encoded, // but the URI production does not allow that according to - // http://www.w3.org/TR/css3-syntax/#TOK-URI + // https://www.w3.org/TR/css3-syntax/#TOK-URI // This does not attempt to recognize encoded keywords. For example, // given "\75\72\6c" and "url" this return false. return string(bytes.ToLower(b[i:])) == kw @@ -38,7 +38,7 @@ func endsWithCSSKeyword(b []byte, kw string) bool { func isCSSNmchar(r rune) bool { // Based on the CSS3 nmchar production but ignores multi-rune escape // sequences. - // http://www.w3.org/TR/css3-syntax/#SUBTOK-nmchar + // https://www.w3.org/TR/css3-syntax/#SUBTOK-nmchar return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || '0' <= r && r <= '9' || @@ -53,7 +53,7 @@ func isCSSNmchar(r rune) bool { // decodeCSS decodes CSS3 escapes given a sequence of stringchars. // If there is no change, it returns the input, otherwise it returns a slice // backed by a new array. -// http://www.w3.org/TR/css3-syntax/#SUBTOK-stringchar defines stringchar. +// https://www.w3.org/TR/css3-syntax/#SUBTOK-stringchar defines stringchar. func decodeCSS(s []byte) []byte { i := bytes.IndexByte(s, '\\') if i == -1 { @@ -72,10 +72,10 @@ func decodeCSS(s []byte) []byte { if len(s) < 2 { break } - // http://www.w3.org/TR/css3-syntax/#SUBTOK-escape + // https://www.w3.org/TR/css3-syntax/#SUBTOK-escape // escape ::= unicode | '\' [#x20-#x7E#x80-#xD7FF#xE000-#xFFFD#x10000-#x10FFFF] if isHex(s[1]) { - // http://www.w3.org/TR/css3-syntax/#SUBTOK-unicode + // https://www.w3.org/TR/css3-syntax/#SUBTOK-unicode // unicode ::= '\' [0-9a-fA-F]{1,6} wc? j := 2 for j < len(s) && j < 7 && isHex(s[j]) { @@ -222,7 +222,7 @@ func cssValueFilter(args ...interface{}) string { b, id := decodeCSS([]byte(s)), make([]byte, 0, 64) // CSS3 error handling is specified as honoring string boundaries per - // http://www.w3.org/TR/css3-syntax/#error-handling : + // https://www.w3.org/TR/css3-syntax/#error-handling : // Malformed declarations. User agents must handle unexpected // tokens encountered while parsing a declaration by reading until // the end of the declaration, while observing the rules for diff --git a/libgo/go/html/template/delim_string.go b/libgo/go/html/template/delim_string.go new file mode 100644 index 0000000..6d80e09 --- /dev/null +++ b/libgo/go/html/template/delim_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type delim"; DO NOT EDIT. + +package template + +import "strconv" + +const _delim_name = "delimNonedelimDoubleQuotedelimSingleQuotedelimSpaceOrTagEnd" + +var _delim_index = [...]uint8{0, 9, 25, 41, 59} + +func (i delim) String() string { + if i >= delim(len(_delim_index)-1) { + return "delim(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _delim_name[_delim_index[i]:_delim_index[i+1]] +} diff --git a/libgo/go/html/template/doc.go b/libgo/go/html/template/doc.go index 35d171c..290ec81 100644 --- a/libgo/go/html/template/doc.go +++ b/libgo/go/html/template/doc.go @@ -70,6 +70,9 @@ In this case it becomes where urlescaper, attrescaper, and htmlescaper are aliases for internal escaping functions. +For these internal escaping functions, if an action pipeline evaluates to +a nil interface value, it is treated as though it were an empty string. + Errors See the documentation of ErrorCode for details. diff --git a/libgo/go/html/template/element_string.go b/libgo/go/html/template/element_string.go new file mode 100644 index 0000000..4573e08 --- /dev/null +++ b/libgo/go/html/template/element_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type element"; DO NOT EDIT. + +package template + +import "strconv" + +const _element_name = "elementNoneelementScriptelementStyleelementTextareaelementTitle" + +var _element_index = [...]uint8{0, 11, 24, 36, 51, 63} + +func (i element) String() string { + if i >= element(len(_element_index)-1) { + return "element(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _element_name[_element_index[i]:_element_index[i+1]] +} diff --git a/libgo/go/html/template/escape.go b/libgo/go/html/template/escape.go index 5963194..f12dafa 100644 --- a/libgo/go/html/template/escape.go +++ b/libgo/go/html/template/escape.go @@ -380,16 +380,6 @@ func appendCmd(cmds []*parse.CommandNode, cmd *parse.CommandNode) []*parse.Comma return append(cmds, cmd) } -// indexOfStr is the first i such that eq(s, strs[i]) or -1 if s was not found. -func indexOfStr(s string, strs []string, eq func(a, b string) bool) int { - for i, t := range strs { - if eq(s, t) { - return i - } - } - return -1 -} - // newIdentCmd produces a command containing a single identifier node. func newIdentCmd(identifier string, pos parse.Pos) *parse.CommandNode { return &parse.CommandNode{ @@ -427,7 +417,7 @@ func nudge(c context) context { // join joins the two contexts of a branch template node. The result is an // error context if either of the input contexts are error contexts, or if the -// the input contexts differ. +// input contexts differ. func join(a, b context, node parse.Node, nodeName string) context { if a.state == stateError { return a @@ -678,7 +668,7 @@ func (e *escaper) escapeText(c context, n *parse.TextNode) context { } else if isComment(c.state) && c.delim == delimNone { switch c.state { case stateJSBlockCmt: - // http://es5.github.com/#x7.4: + // https://es5.github.com/#x7.4: // "Comments behave like white space and are // discarded except that, if a MultiLineComment // contains a line terminator character, then @@ -741,7 +731,7 @@ func contextAfterText(c context, s []byte) (context, int) { i = len(s) } if c.delim == delimSpaceOrTagEnd { - // http://www.w3.org/TR/html5/syntax.html#attribute-value-(unquoted)-state + // https://www.w3.org/TR/html5/syntax.html#attribute-value-(unquoted)-state // lists the runes below as error characters. // Error out because HTML parsers may differ on whether // "<a id= onclick=f(" ends inside id's or onclick's value, diff --git a/libgo/go/html/template/escape_test.go b/libgo/go/html/template/escape_test.go index 55f808c..e6c12a8 100644 --- a/libgo/go/html/template/escape_test.go +++ b/libgo/go/html/template/escape_test.go @@ -35,12 +35,13 @@ func TestEscape(t *testing.T) { A, E []string B, M json.Marshaler N int - Z *int + U interface{} // untyped nil + Z *int // typed nil W HTML }{ F: false, T: true, - C: "<Cincinatti>", + C: "<Cincinnati>", G: "<Goodbye>", H: "<Hello>", A: []string{"<a>", "<b>"}, @@ -48,6 +49,7 @@ func TestEscape(t *testing.T) { N: 42, B: &badMarshaler{}, M: &goodMarshaler{}, + U: nil, Z: nil, W: HTML(`¡<b class="foo">Hello</b>, <textarea>O'World</textarea>!`), } @@ -61,7 +63,7 @@ func TestEscape(t *testing.T) { { "if", "{{if .T}}Hello{{end}}, {{.C}}!", - "Hello, <Cincinatti>!", + "Hello, <Cincinnati>!", }, { "else", @@ -71,17 +73,17 @@ func TestEscape(t *testing.T) { { "overescaping1", "Hello, {{.C | html}}!", - "Hello, <Cincinatti>!", + "Hello, <Cincinnati>!", }, { "overescaping2", "Hello, {{html .C}}!", - "Hello, <Cincinatti>!", + "Hello, <Cincinnati>!", }, { "overescaping3", "{{with .C}}{{$msg := .}}Hello, {{$msg}}!{{end}}", - "Hello, <Cincinatti>!", + "Hello, <Cincinnati>!", }, { "assignment", @@ -114,6 +116,16 @@ func TestEscape(t *testing.T) { "true", }, { + "untypedNilValue", + "{{.U}}", + "", + }, + { + "typedNilValue", + "{{.Z}}", + "<nil>", + }, + { "constant", `<a href="/search?q={{"'a<b'"}}">`, `<a href="/search?q=%27a%3cb%27">`, @@ -181,7 +193,7 @@ func TestEscape(t *testing.T) { { "urlBranchConflictMoot", `<a href="{{if .T}}/foo?a={{else}}/bar#{{end}}{{.C}}">`, - `<a href="/foo?a=%3cCincinatti%3e">`, + `<a href="/foo?a=%3cCincinnati%3e">`, }, { "jsStrValue", @@ -199,11 +211,16 @@ func TestEscape(t *testing.T) { `<button onclick='alert( true )'>`, }, { - "jsNilValue", + "jsNilValueTyped", "<button onclick='alert(typeof{{.Z}})'>", `<button onclick='alert(typeof null )'>`, }, { + "jsNilValueUntyped", + "<button onclick='alert(typeof{{.U}})'>", + `<button onclick='alert(typeof null )'>`, + }, + { "jsObjValue", "<button onclick='alert({{.A}})'>", `<button onclick='alert(["\u003ca\u003e","\u003cb\u003e"])'>`, @@ -237,7 +254,7 @@ func TestEscape(t *testing.T) { "jsStrNotUnderEscaped", "<button onclick='alert({{.C | urlquery}})'>", // URL escaped, then quoted for JS. - `<button onclick='alert("%3CCincinatti%3E")'>`, + `<button onclick='alert("%3CCincinnati%3E")'>`, }, { "jsRe", @@ -405,7 +422,7 @@ func TestEscape(t *testing.T) { { "HTML comment", "<b>Hello, <!-- name of world -->{{.C}}</b>", - "<b>Hello, <Cincinatti></b>", + "<b>Hello, <Cincinnati></b>", }, { "HTML comment not first < in text node.", @@ -445,7 +462,7 @@ func TestEscape(t *testing.T) { { "Split HTML comment", "<b>Hello, <!-- name of {{if .T}}city -->{{.C}}{{else}}world -->{{.W}}{{end}}</b>", - "<b>Hello, <Cincinatti></b>", + "<b>Hello, <Cincinnati></b>", }, { "JS line comment", @@ -656,6 +673,11 @@ func TestEscape(t *testing.T) { // The second URL is also filtered. `<img srcset="/not-an-image#,#ZgotmplZ">`, }, + { + "srcset buffer growth", + `<img srcset={{",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"}}>`, + `<img srcset=,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,>`, + }, } for _, test := range tests { diff --git a/libgo/go/html/template/html.go b/libgo/go/html/template/html.go index de4aa4a..2ea5a7d 100644 --- a/libgo/go/html/template/html.go +++ b/libgo/go/html/template/html.go @@ -50,12 +50,12 @@ func htmlEscaper(args ...interface{}) string { // htmlReplacementTable contains the runes that need to be escaped // inside a quoted attribute value or in a text node. var htmlReplacementTable = []string{ - // http://www.w3.org/TR/html5/syntax.html#attribute-value-(unquoted)-state + // https://www.w3.org/TR/html5/syntax.html#attribute-value-(unquoted)-state // U+0000 NULL Parse error. Append a U+FFFD REPLACEMENT // CHARACTER character to the current attribute's value. // " // and similarly - // http://www.w3.org/TR/html5/syntax.html#before-attribute-value-state + // https://www.w3.org/TR/html5/syntax.html#before-attribute-value-state 0: "\uFFFD", '"': """, '&': "&", diff --git a/libgo/go/html/template/js.go b/libgo/go/html/template/js.go index 239395f..33a18b4 100644 --- a/libgo/go/html/template/js.go +++ b/libgo/go/html/template/js.go @@ -24,7 +24,7 @@ import ( // "x = ++/foo/i" which is quite different than "x++/foo/i", but is not known to // fail on any known useful programs. It is based on the draft // JavaScript 2.0 lexical grammar and requires one token of lookbehind: -// http://www.mozilla.org/js/language/js20-2000-07/rationale/syntax.html +// https://www.mozilla.org/js/language/js20-2000-07/rationale/syntax.html func nextJSCtx(s []byte, preceding jsCtx) jsCtx { s = bytes.TrimRight(s, "\t\n\f\r \u2028\u2029") if len(s) == 0 { @@ -123,6 +123,14 @@ 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{} { + // text/template now supports passing untyped nil as a func call + // argument, so we must support it. Otherwise we'd panic below, as one + // cannot call the Type or Interface methods on an invalid + // reflect.Value. See golang.org/issue/18716. + if a == nil { + return nil + } + v := reflect.ValueOf(a) for !v.Type().Implements(jsonMarshalType) && v.Kind() == reflect.Ptr && !v.IsNil() { v = v.Elem() diff --git a/libgo/go/html/template/js_test.go b/libgo/go/html/template/js_test.go index 7484f60..12a850d 100644 --- a/libgo/go/html/template/js_test.go +++ b/libgo/go/html/template/js_test.go @@ -149,6 +149,7 @@ func TestJSValEscaper(t *testing.T) { {"]]>", `"]]\u003e"`}, {"</script", `"\u003c/script"`}, {"\U0001D11E", "\"\U0001D11E\""}, // or "\uD834\uDD1E" + {nil, " null "}, } for _, test := range tests { @@ -190,7 +191,7 @@ func TestJSStrEscaper(t *testing.T) { {"</script>", `\x3c\/script\x3e`}, {"<![CDATA[", `\x3c![CDATA[`}, {"]]>", `]]\x3e`}, - // http://dev.w3.org/html5/markup/aria/syntax.html#escaping-text-span + // https://dev.w3.org/html5/markup/aria/syntax.html#escaping-text-span // "The text in style, script, title, and textarea elements // must not have an escaping text span start that is not // followed by an escaping text span end." @@ -202,7 +203,7 @@ func TestJSStrEscaper(t *testing.T) { // injection followed by an HTML text injection. {"<!--", `\x3c!--`}, {"-->", `--\x3e`}, - // From http://code.google.com/p/doctype/wiki/ArticleUtf7 + // From https://code.google.com/p/doctype/wiki/ArticleUtf7 {"+ADw-script+AD4-alert(1)+ADw-/script+AD4-", `\x2bADw-script\x2bAD4-alert(1)\x2bADw-\/script\x2bAD4-`, }, diff --git a/libgo/go/html/template/jsctx_string.go b/libgo/go/html/template/jsctx_string.go new file mode 100644 index 0000000..dd1d87e --- /dev/null +++ b/libgo/go/html/template/jsctx_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type jsCtx"; DO NOT EDIT. + +package template + +import "strconv" + +const _jsCtx_name = "jsCtxRegexpjsCtxDivOpjsCtxUnknown" + +var _jsCtx_index = [...]uint8{0, 11, 21, 33} + +func (i jsCtx) String() string { + if i >= jsCtx(len(_jsCtx_index)-1) { + return "jsCtx(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _jsCtx_name[_jsCtx_index[i]:_jsCtx_index[i+1]] +} diff --git a/libgo/go/html/template/state_string.go b/libgo/go/html/template/state_string.go new file mode 100644 index 0000000..05104be --- /dev/null +++ b/libgo/go/html/template/state_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type state"; DO NOT EDIT. + +package template + +import "strconv" + +const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateError" + +var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 155, 170, 184, 192, 205, 218, 231, 244, 255, 271, 286, 296} + +func (i state) String() string { + if i >= state(len(_state_index)-1) { + return "state(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _state_name[_state_index[i]:_state_index[i+1]] +} diff --git a/libgo/go/html/template/transition.go b/libgo/go/html/template/transition.go index c72cf1e..06df679 100644 --- a/libgo/go/html/template/transition.go +++ b/libgo/go/html/template/transition.go @@ -254,7 +254,7 @@ func tURL(c context, s []byte) (context, int) { c.urlPart = urlPartQueryOrFrag } else if len(s) != eatWhiteSpace(s, 0) && c.urlPart == urlPartNone { // HTML5 uses "Valid URL potentially surrounded by spaces" for - // attrs: http://www.w3.org/TR/html5/index.html#attributes-1 + // attrs: https://www.w3.org/TR/html5/index.html#attributes-1 c.urlPart = urlPartPreQuery } return c, len(s) @@ -380,7 +380,7 @@ func tLineCmt(c context, s []byte) (context, int) { // are supported by the 4 major browsers. // This defines line comments as // LINECOMMENT ::= "//" [^\n\f\d]* - // since http://www.w3.org/TR/css3-syntax/#SUBTOK-nl defines + // since https://www.w3.org/TR/css3-syntax/#SUBTOK-nl defines // newlines: // nl ::= #xA | #xD #xA | #xD | #xC default: @@ -392,7 +392,7 @@ func tLineCmt(c context, s []byte) (context, int) { return c, len(s) } c.state = endState - // Per section 7.4 of EcmaScript 5 : http://es5.github.com/#x7.4 + // Per section 7.4 of EcmaScript 5 : https://es5.github.com/#x7.4 // "However, the LineTerminator at the end of the line is not // considered to be part of the single-line comment; it is // recognized separately by the lexical grammar and becomes part diff --git a/libgo/go/html/template/url.go b/libgo/go/html/template/url.go index 69a6ff4..f051630 100644 --- a/libgo/go/html/template/url.go +++ b/libgo/go/html/template/url.go @@ -37,15 +37,15 @@ func urlFilter(args ...interface{}) string { if t == contentTypeURL { return s } - if !isSafeUrl(s) { + if !isSafeURL(s) { return "#" + filterFailsafe } return s } -// isSafeUrl is true if s is a relative URL or if URL has a protocol in +// isSafeURL is true if s is a relative URL or if URL has a protocol in // (http, https, mailto). -func isSafeUrl(s string) bool { +func isSafeURL(s string) bool { if i := strings.IndexRune(s, ':'); i >= 0 && !strings.ContainsRune(s[:i], '/') { protocol := s[:i] @@ -79,16 +79,16 @@ func urlProcessor(norm bool, args ...interface{}) string { norm = true } var b bytes.Buffer - if processUrlOnto(s, norm, &b) { + if processURLOnto(s, norm, &b) { return b.String() } return s } -// processUrlOnto appends a normalized URL corresponding to its input to b +// processURLOnto appends a normalized URL corresponding to its input to b // and returns true if the appended content differs from s. -func processUrlOnto(s string, norm bool, b *bytes.Buffer) bool { - b.Grow(b.Cap() + len(s) + 16) +func processURLOnto(s string, norm bool, b *bytes.Buffer) bool { + b.Grow(len(s) + 16) written := 0 // The byte loop below assumes that all URLs use UTF-8 as the // content-encoding. This is similar to the URI to IRI encoding scheme @@ -152,7 +152,7 @@ func srcsetFilterAndEscaper(args ...interface{}) string { // Normalizing gets rid of all HTML whitespace // which separate the image URL from its metadata. var b bytes.Buffer - if processUrlOnto(s, true, &b) { + if processURLOnto(s, true, &b) { s = b.String() } // Additionally, commas separate one source from another. @@ -173,43 +173,43 @@ func srcsetFilterAndEscaper(args ...interface{}) string { } // Derived from https://play.golang.org/p/Dhmj7FORT5 -const htmlSpaceAndAsciiAlnumBytes = "\x00\x36\x00\x00\x01\x00\xff\x03\xfe\xff\xff\x07\xfe\xff\xff\x07" +const htmlSpaceAndASCIIAlnumBytes = "\x00\x36\x00\x00\x01\x00\xff\x03\xfe\xff\xff\x07\xfe\xff\xff\x07" -// isHtmlSpace is true iff c is a whitespace character per +// isHTMLSpace is true iff c is a whitespace character per // https://infra.spec.whatwg.org/#ascii-whitespace -func isHtmlSpace(c byte) bool { - return (c <= 0x20) && 0 != (htmlSpaceAndAsciiAlnumBytes[c>>3]&(1<<uint(c&0x7))) +func isHTMLSpace(c byte) bool { + return (c <= 0x20) && 0 != (htmlSpaceAndASCIIAlnumBytes[c>>3]&(1<<uint(c&0x7))) } -func isHtmlSpaceOrAsciiAlnum(c byte) bool { - return (c < 0x80) && 0 != (htmlSpaceAndAsciiAlnumBytes[c>>3]&(1<<uint(c&0x7))) +func isHTMLSpaceOrASCIIAlnum(c byte) bool { + return (c < 0x80) && 0 != (htmlSpaceAndASCIIAlnumBytes[c>>3]&(1<<uint(c&0x7))) } func filterSrcsetElement(s string, left int, right int, b *bytes.Buffer) { start := left - for start < right && isHtmlSpace(s[start]) { - start += 1 + for start < right && isHTMLSpace(s[start]) { + start++ } end := right for i := start; i < right; i++ { - if isHtmlSpace(s[i]) { + if isHTMLSpace(s[i]) { end = i break } } - if url := s[start:end]; isSafeUrl(url) { + if url := s[start:end]; isSafeURL(url) { // If image metadata is only spaces or alnums then // we don't need to URL normalize it. metadataOk := true for i := end; i < right; i++ { - if !isHtmlSpaceOrAsciiAlnum(s[i]) { + if !isHTMLSpaceOrASCIIAlnum(s[i]) { metadataOk = false break } } if metadataOk { b.WriteString(s[left:start]) - processUrlOnto(url, true, b) + processURLOnto(url, true, b) b.WriteString(s[end:right]) return } diff --git a/libgo/go/html/template/urlpart_string.go b/libgo/go/html/template/urlpart_string.go new file mode 100644 index 0000000..813eea9 --- /dev/null +++ b/libgo/go/html/template/urlpart_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type urlPart"; DO NOT EDIT. + +package template + +import "strconv" + +const _urlPart_name = "urlPartNoneurlPartPreQueryurlPartQueryOrFragurlPartUnknown" + +var _urlPart_index = [...]uint8{0, 11, 26, 44, 58} + +func (i urlPart) String() string { + if i >= urlPart(len(_urlPart_index)-1) { + return "urlPart(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _urlPart_name[_urlPart_index[i]:_urlPart_index[i+1]] +} diff --git a/libgo/go/image/color/palette/gen.go b/libgo/go/image/color/palette/gen.go index 57718e6..f8587db 100644 --- a/libgo/go/image/color/palette/gen.go +++ b/libgo/go/image/color/palette/gen.go @@ -30,7 +30,7 @@ func main() { // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file.`) fmt.Fprintln(&buf) - fmt.Fprintln(&buf, "// generated by go run gen.go -output palette.go; DO NOT EDIT") + fmt.Fprintln(&buf, "// Code generated by go run gen.go -output palette.go; DO NOT EDIT.") fmt.Fprintln(&buf) fmt.Fprintln(&buf, "package palette") fmt.Fprintln(&buf) @@ -111,7 +111,7 @@ func printWebSafe(w io.Writer) { fmt.Fprintln(w, "// WebSafe is a 216-color palette that was popularized by early versions") fmt.Fprintln(w, "// of Netscape Navigator. It is also known as the Netscape Color Cube.") fmt.Fprintln(w, "//") - fmt.Fprintln(w, "// See http://en.wikipedia.org/wiki/Web_colors#Web-safe_colors for details.") + fmt.Fprintln(w, "// See https://en.wikipedia.org/wiki/Web_colors#Web-safe_colors for details.") fmt.Fprintln(w, "var WebSafe = []color.Color{") for _, line := range lines { fmt.Fprintln(w, line) diff --git a/libgo/go/image/color/palette/palette.go b/libgo/go/image/color/palette/palette.go index b695414..2a4cdcb 100644 --- a/libgo/go/image/color/palette/palette.go +++ b/libgo/go/image/color/palette/palette.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. -// generated by go run gen.go -output palette.go; DO NOT EDIT +// Code generated by go run gen.go -output palette.go; DO NOT EDIT. package palette @@ -282,7 +282,7 @@ var Plan9 = []color.Color{ // WebSafe is a 216-color palette that was popularized by early versions // of Netscape Navigator. It is also known as the Netscape Color Cube. // -// See http://en.wikipedia.org/wiki/Web_colors#Web-safe_colors for details. +// See https://en.wikipedia.org/wiki/Web_colors#Web-safe_colors for details. var WebSafe = []color.Color{ color.RGBA{0x00, 0x00, 0x00, 0xff}, color.RGBA{0x00, 0x00, 0x33, 0xff}, diff --git a/libgo/go/image/color/ycbcr.go b/libgo/go/image/color/ycbcr.go index fd24430..8b6d508 100644 --- a/libgo/go/image/color/ycbcr.go +++ b/libgo/go/image/color/ycbcr.go @@ -10,7 +10,7 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) { // Y' = 0.2990*R + 0.5870*G + 0.1140*B // Cb = -0.1687*R - 0.3313*G + 0.5000*B + 128 // Cr = 0.5000*R - 0.4187*G - 0.0813*B + 128 - // http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'. + // https://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'. r1 := int32(r) g1 := int32(g) @@ -60,7 +60,7 @@ func YCbCrToRGB(y, cb, cr uint8) (uint8, uint8, uint8) { // R = Y' + 1.40200*(Cr-128) // G = Y' - 0.34414*(Cb-128) - 0.71414*(Cr-128) // B = Y' + 1.77200*(Cb-128) - // http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'. + // https://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'. // // Those formulae use non-integer multiplication factors. When computing, // integer math is generally faster than floating point math. We multiply @@ -163,7 +163,7 @@ func YCbCrToRGB(y, cb, cr uint8) (uint8, uint8, uint8) { // // Conversion between RGB and Y'CbCr is lossy and there are multiple, slightly // different formulae for converting between the two. This package follows -// the JFIF specification at http://www.w3.org/Graphics/JPEG/jfif3.pdf. +// the JFIF specification at https://www.w3.org/Graphics/JPEG/jfif3.pdf. type YCbCr struct { Y, Cb, Cr uint8 } diff --git a/libgo/go/image/decode_example_test.go b/libgo/go/image/decode_example_test.go index 81fa037..526c03f 100644 --- a/libgo/go/image/decode_example_test.go +++ b/libgo/go/image/decode_example_test.go @@ -21,6 +21,15 @@ import ( _ "image/jpeg" ) +func Example_decodeConfig() { + reader := base64.NewDecoder(base64.StdEncoding, strings.NewReader(data)) + config, format, err := image.DecodeConfig(reader) + if err != nil { + log.Fatal(err) + } + fmt.Println("Width:", config.Width, "Height:", config.Height, "Format:", format) +} + func Example() { // Decode the JPEG data. If reading from file, create a reader with // diff --git a/libgo/go/image/gif/reader.go b/libgo/go/image/gif/reader.go index c1c9562..e580ab0 100644 --- a/libgo/go/image/gif/reader.go +++ b/libgo/go/image/gif/reader.go @@ -4,7 +4,7 @@ // Package gif implements a GIF image decoder and encoder. // -// The GIF specification is at http://www.w3.org/Graphics/GIF/spec-gif89a.txt. +// The GIF specification is at https://www.w3.org/Graphics/GIF/spec-gif89a.txt. package gif import ( @@ -179,7 +179,7 @@ func (b *blockReader) Read(p []byte) (int, error) { // following the end of LZW data, the very next sub-block must be the block // terminator. If the very end of LZW data happened to fill one sub-block, at // most one more sub-block of length 1 may exist before the block-terminator. -// These accomodations allow us to support GIFs created by less strict encoders. +// These accommodations allow us to support GIFs created by less strict encoders. // See https://golang.org/issue/16146. func (b *blockReader) close() error { if b.err == io.EOF { @@ -224,6 +224,8 @@ func (d *decoder) decode(r io.Reader, configOnly, keepAllFrames bool) error { d.r = bufio.NewReader(r) } + d.loopCount = -1 + err := d.readHeaderAndScreenDescriptor() if err != nil { return err @@ -566,9 +568,14 @@ func Decode(r io.Reader) (image.Image, error) { // GIF represents the possibly multiple images stored in a GIF file. type GIF struct { - Image []*image.Paletted // The successive images. - Delay []int // The successive delay times, one per frame, in 100ths of a second. - LoopCount int // The loop count. + Image []*image.Paletted // The successive images. + Delay []int // The successive delay times, one per frame, in 100ths of a second. + // LoopCount controls the number of times an animation will be + // restarted during display. + // A LoopCount of 0 means to loop forever. + // A LoopCount of -1 means to show each frame only once. + // Otherwise, the animation is looped LoopCount+1 times. + LoopCount int // Disposal is the successive disposal methods, one per frame. For // backwards compatibility, a nil Disposal is valid to pass to EncodeAll, // and implies that each frame's disposal method is 0 (no disposal diff --git a/libgo/go/image/gif/reader_test.go b/libgo/go/image/gif/reader_test.go index 220e8f5..29f47b6 100644 --- a/libgo/go/image/gif/reader_test.go +++ b/libgo/go/image/gif/reader_test.go @@ -318,23 +318,62 @@ func TestTransparentPixelOutsidePaletteRange(t *testing.T) { } func TestLoopCount(t *testing.T) { - data := []byte("GIF89a000\x00000,0\x00\x00\x00\n\x00" + - "\n\x00\x80000000\x02\b\xf01u\xb9\xfdal\x05\x00;") - img, err := DecodeAll(bytes.NewReader(data)) - if err != nil { - t.Fatal("DecodeAll:", err) - } - w := new(bytes.Buffer) - err = EncodeAll(w, img) - if err != nil { - t.Fatal("EncodeAll:", err) - } - img1, err := DecodeAll(w) - if err != nil { - t.Fatal("DecodeAll:", err) + testCases := []struct { + name string + data []byte + loopCount int + }{ + { + "loopcount-missing", + []byte("GIF89a000\x00000" + + ",0\x00\x00\x00\n\x00\n\x00\x80000000" + // image 0 descriptor & color table + "\x02\b\xf01u\xb9\xfdal\x05\x00;"), // image 0 image data & trailer + -1, + }, + { + "loopcount-0", + []byte("GIF89a000\x00000" + + "!\xff\vNETSCAPE2.0\x03\x01\x00\x00\x00" + // loop count = 0 + ",0\x00\x00\x00\n\x00\n\x00\x80000000" + // image 0 descriptor & color table + "\x02\b\xf01u\xb9\xfdal\x05\x00" + // image 0 image data + ",0\x00\x00\x00\n\x00\n\x00\x80000000" + // image 1 descriptor & color table + "\x02\b\xf01u\xb9\xfdal\x05\x00;"), // image 1 image data & trailer + 0, + }, + { + "loopcount-1", + []byte("GIF89a000\x00000" + + "!\xff\vNETSCAPE2.0\x03\x01\x01\x00\x00" + // loop count = 1 + ",0\x00\x00\x00\n\x00\n\x00\x80000000" + // image 0 descriptor & color table + "\x02\b\xf01u\xb9\xfdal\x05\x00" + // image 0 image data + ",0\x00\x00\x00\n\x00\n\x00\x80000000" + // image 1 descriptor & color table + "\x02\b\xf01u\xb9\xfdal\x05\x00;"), // image 1 image data & trailer + 1, + }, } - if img.LoopCount != img1.LoopCount { - t.Errorf("loop count mismatch: %d vs %d", img.LoopCount, img1.LoopCount) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + img, err := DecodeAll(bytes.NewReader(tc.data)) + if err != nil { + t.Fatal("DecodeAll:", err) + } + w := new(bytes.Buffer) + err = EncodeAll(w, img) + if err != nil { + t.Fatal("EncodeAll:", err) + } + img1, err := DecodeAll(w) + if err != nil { + t.Fatal("DecodeAll:", err) + } + if img.LoopCount != tc.loopCount { + t.Errorf("loop count mismatch: %d vs %d", img.LoopCount, tc.loopCount) + } + if img.LoopCount != img1.LoopCount { + t.Errorf("loop count failed round-trip: %d vs %d", img.LoopCount, img1.LoopCount) + } + }) } } diff --git a/libgo/go/image/gif/writer.go b/libgo/go/image/gif/writer.go index f26af8b..5e80feb 100644 --- a/libgo/go/image/gif/writer.go +++ b/libgo/go/image/gif/writer.go @@ -178,7 +178,7 @@ func (e *encoder) writeHeader() { } // Add animation info if necessary. - if len(e.g.Image) > 1 { + if len(e.g.Image) > 1 && e.g.LoopCount >= 0 { e.buf[0] = 0x21 // Extension Introducer. e.buf[1] = 0xff // Application Label. e.buf[2] = 0x0b // Block Size. @@ -377,9 +377,6 @@ func EncodeAll(w io.Writer, g *GIF) error { if len(g.Image) != len(g.Delay) { return errors.New("gif: mismatched image and delay lengths") } - if g.LoopCount < 0 { - g.LoopCount = 0 - } e := encoder{g: *g} // The GIF.Disposal, GIF.Config and GIF.BackgroundIndex fields were added diff --git a/libgo/go/image/internal/imageutil/gen.go b/libgo/go/image/internal/imageutil/gen.go index 8b2c427..bc85c51 100644 --- a/libgo/go/image/internal/imageutil/gen.go +++ b/libgo/go/image/internal/imageutil/gen.go @@ -41,7 +41,7 @@ func main() { } } -const pre = `// generated by "go run gen.go". DO NOT EDIT. +const pre = `// Code generated by go run gen.go; DO NOT EDIT. package imageutil diff --git a/libgo/go/image/internal/imageutil/impl.go b/libgo/go/image/internal/imageutil/impl.go index cfd5047..4581dd8 100644 --- a/libgo/go/image/internal/imageutil/impl.go +++ b/libgo/go/image/internal/imageutil/impl.go @@ -1,4 +1,4 @@ -// generated by "go run gen.go". DO NOT EDIT. +// Code generated by go run gen.go; DO NOT EDIT. package imageutil diff --git a/libgo/go/image/jpeg/reader.go b/libgo/go/image/jpeg/reader.go index a915e96..4a4706f 100644 --- a/libgo/go/image/jpeg/reader.go +++ b/libgo/go/image/jpeg/reader.go @@ -4,7 +4,7 @@ // Package jpeg implements a JPEG image decoder and encoder. // -// JPEG is defined in ITU-T T.81: http://www.w3.org/Graphics/JPEG/itu-t81.pdf. +// JPEG is defined in ITU-T T.81: https://www.w3.org/Graphics/JPEG/itu-t81.pdf. package jpeg import ( @@ -62,13 +62,13 @@ const ( comMarker = 0xfe // COMment. // "APPlication specific" markers aren't part of the JPEG spec per se, // but in practice, their use is described at - // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html + // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html app0Marker = 0xe0 app14Marker = 0xee app15Marker = 0xef ) -// See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe +// See https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe const ( adobeTransformUnknown = 0 adobeTransformYCbCr = 1 @@ -89,7 +89,8 @@ var unzig = [blockSize]int{ 53, 60, 61, 54, 47, 55, 62, 63, } -// Deprecated: Reader is deprecated. +// Deprecated: Reader is not used by the image/jpeg package and should +// not be used by others. It is kept for compatibility. type Reader interface { io.ByteReader io.Reader @@ -684,7 +685,7 @@ func (d *decoder) applyBlack() (image.Image, error) { // If the 4-component JPEG image isn't explicitly marked as "Unknown (RGB // or CMYK)" as per - // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe + // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe // we assume that it is YCbCrK. This matches libjpeg's jdapimin.c. if d.adobeTransform != adobeTransformUnknown { // Convert the YCbCr part of the YCbCrK to RGB, invert the RGB to get @@ -747,7 +748,7 @@ func (d *decoder) isRGB() bool { return false } if d.adobeTransformValid && d.adobeTransform == adobeTransformUnknown { - // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe + // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe // says that 0 means Unknown (and in practice RGB) and 1 means YCbCr. return true } diff --git a/libgo/go/image/png/reader.go b/libgo/go/image/png/reader.go index 4fbcef8..53fbf3e 100644 --- a/libgo/go/image/png/reader.go +++ b/libgo/go/image/png/reader.go @@ -4,7 +4,7 @@ // Package png implements a PNG image decoder and encoder. // -// The PNG specification is at http://www.w3.org/TR/PNG/. +// The PNG specification is at https://www.w3.org/TR/PNG/. package png import ( @@ -73,7 +73,7 @@ type interlaceScan struct { } // interlacing defines Adam7 interlacing, with 7 passes of reduced images. -// See http://www.w3.org/TR/PNG/#8Interlace +// See https://www.w3.org/TR/PNG/#8Interlace var interlacing = []interlaceScan{ {8, 8, 0, 0}, {8, 8, 4, 0}, @@ -89,7 +89,7 @@ var interlacing = []interlaceScan{ // present), IDAT and IEND chunks must appear in that order. There may be // multiple IDAT chunks, and IDAT chunks must be sequential (i.e. they may not // have any other chunks between them). -// http://www.w3.org/TR/PNG/#5ChunkOrdering +// https://www.w3.org/TR/PNG/#5ChunkOrdering const ( dsStart = iota dsSeenIHDR diff --git a/libgo/go/index/suffixarray/suffixarray.go b/libgo/go/index/suffixarray/suffixarray.go index c59ae6e..0961ac4 100644 --- a/libgo/go/index/suffixarray/suffixarray.go +++ b/libgo/go/index/suffixarray/suffixarray.go @@ -107,7 +107,7 @@ func (x *Index) Read(r io.Reader) error { // allocate space if 2*n < cap(x.data) || cap(x.data) < n { - // new data is significantly smaller or larger then + // new data is significantly smaller or larger than // existing buffers - allocate new ones x.data = make([]byte, n) x.sa = make([]int, n) diff --git a/libgo/go/internal/bytealg/bytealg.c b/libgo/go/internal/bytealg/bytealg.c new file mode 100644 index 0000000..39c060f --- /dev/null +++ b/libgo/go/internal/bytealg/bytealg.c @@ -0,0 +1,116 @@ +/* bytealg.c -- gccgo implementations of bytealg functions + + Copyright 2018 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 <string.h> + +#include "runtime.h" +#include "array.h" + +intgo Compare(struct __go_open_array, struct __go_open_array) + __asm__(GOSYM_PREFIX "internal_bytealg.Compare") + __attribute__((no_split_stack)); + +intgo Compare(struct __go_open_array a, struct __go_open_array b) +{ + intgo l; + + l = a.__count; + if (b.__count < l) { + l = b.__count; + } + if (l > 0 && a.__values != b.__values) { + int i; + + i = __builtin_memcmp(a.__values, b.__values, (size_t)(l)); + if (i != 0) { + return i; + } + } + if (a.__count < b.__count) { + return -1; + } + if (a.__count > b.__count) { + return +1; + } + return 0; +} + +_Bool Equal(struct __go_open_array, struct __go_open_array) + __asm__(GOSYM_PREFIX "internal_bytealg.Equal") + __attribute__((no_split_stack)); + +_Bool Equal(struct __go_open_array a, struct __go_open_array b) +{ + intgo l; + + l = a.__count; + if (l != b.__count) { + return 0; + } + return __builtin_memcmp(a.__values, b.__values, l) == 0; +} + +intgo IndexByte(struct __go_open_array, byte) + __asm__(GOSYM_PREFIX "internal_bytealg.IndexByte") + __attribute__((no_split_stack)); + +intgo IndexByte(struct __go_open_array b, byte c) +{ + const byte *p; + + p = __builtin_memchr(b.__values, c, (size_t)(b.__count)); + if (p == nil) { + return -1; + } + return p - (byte *)(b.__values); +} + + +intgo IndexByteString(String, byte) + __asm__(GOSYM_PREFIX "internal_bytealg.IndexByteString") + __attribute__((no_split_stack)); + +intgo IndexByteString(String s, byte c) +{ + const byte *p; + + p = __builtin_memchr(s.str, c, (size_t)(s.len)); + if (p == nil) { + return -1; + } + return p - s.str; +} + +intgo Index(struct __go_open_array, struct __go_open_array) + __asm__(GOSYM_PREFIX "internal_bytealg.Index") + __attribute__((no_split_stack)); + +intgo Index(struct __go_open_array a, struct __go_open_array b) +{ + const byte* p; + + p = memmem(a.__values, a.__count, b.__values, b.__count); + if (p == nil) { + return -1; + } + return p - (const byte*)(a.__values); +} + +intgo IndexString(String, String) + __asm__(GOSYM_PREFIX "internal_bytealg.IndexString") + __attribute__((no_split_stack)); + +intgo IndexString(String a, String b) +{ + const byte* p; + + p = memmem(a.str, a.len, b.str, b.len); + if (p == nil) { + return -1; + } + return p - a.str; +} diff --git a/libgo/go/internal/bytealg/bytealg.go b/libgo/go/internal/bytealg/bytealg.go new file mode 100644 index 0000000..b91eb00 --- /dev/null +++ b/libgo/go/internal/bytealg/bytealg.go @@ -0,0 +1,24 @@ +// Copyright 2018 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 ignore_for_gccgo + +package bytealg + +import ( + "internal/cpu" + "unsafe" +) + +// Offsets into internal/cpu records for use in assembly. +const ( + x86_HasSSE2 = unsafe.Offsetof(cpu.X86.HasSSE2) + x86_HasSSE42 = unsafe.Offsetof(cpu.X86.HasSSE42) + x86_HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2) + x86_HasPOPCNT = unsafe.Offsetof(cpu.X86.HasPOPCNT) + s390x_HasVX = unsafe.Offsetof(cpu.S390X.HasVX) +) + +// MaxLen is the maximum length of the string to be searched for (argument b) in Index. +var MaxLen int = 32 diff --git a/libgo/go/internal/bytealg/compare_generic.go b/libgo/go/internal/bytealg/compare_generic.go new file mode 100644 index 0000000..bb48833 --- /dev/null +++ b/libgo/go/internal/bytealg/compare_generic.go @@ -0,0 +1,89 @@ +// Copyright 2018 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 ignore_for_gccgo +// +build !386,!amd64,!amd64p32,!s390x,!arm,!arm64,!ppc64,!ppc64le,!mips,!mipsle,!wasm + +package bytealg + +import _ "unsafe" // for go:linkname + +func Compare(a, b []byte) int { + l := len(a) + if len(b) < l { + l = len(b) + } + if l == 0 || &a[0] == &b[0] { + goto samebytes + } + for i := 0; i < l; i++ { + c1, c2 := a[i], b[i] + if c1 < c2 { + return -1 + } + if c1 > c2 { + return +1 + } + } +samebytes: + if len(a) < len(b) { + return -1 + } + if len(a) > len(b) { + return +1 + } + return 0 +} + +//go:linkname bytes_Compare bytes.Compare +func bytes_Compare(a, b []byte) int { + l := len(a) + if len(b) < l { + l = len(b) + } + if l == 0 || &a[0] == &b[0] { + goto samebytes + } + for i := 0; i < l; i++ { + c1, c2 := a[i], b[i] + if c1 < c2 { + return -1 + } + if c1 > c2 { + return +1 + } + } +samebytes: + if len(a) < len(b) { + return -1 + } + if len(a) > len(b) { + return +1 + } + return 0 +} + +//go:linkname runtime_cmpstring runtime.cmpstring +func runtime_cmpstring(a, b string) int { + l := len(a) + if len(b) < l { + l = len(b) + } + for i := 0; i < l; i++ { + c1, c2 := a[i], b[i] + if c1 < c2 { + return -1 + } + if c1 > c2 { + return +1 + } + } + if len(a) < len(b) { + return -1 + } + if len(a) > len(b) { + return +1 + } + return 0 +} diff --git a/libgo/go/internal/bytealg/compare_native.go b/libgo/go/internal/bytealg/compare_native.go new file mode 100644 index 0000000..2c317e2 --- /dev/null +++ b/libgo/go/internal/bytealg/compare_native.go @@ -0,0 +1,10 @@ +// Copyright 2018 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 386 amd64 amd64p32 s390x arm arm64 ppc64 ppc64le mips mipsle wasm + +package bytealg + +//go:noescape +func Compare(a, b []byte) int diff --git a/libgo/go/internal/bytealg/count_generic.go b/libgo/go/internal/bytealg/count_generic.go new file mode 100644 index 0000000..5a06430 --- /dev/null +++ b/libgo/go/internal/bytealg/count_generic.go @@ -0,0 +1,27 @@ +// Copyright 2018 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 !amd64,!arm64 + +package bytealg + +func Count(b []byte, c byte) int { + n := 0 + for _, x := range b { + if x == c { + n++ + } + } + return n +} + +func CountString(s string, c byte) int { + n := 0 + for i := 0; i < len(s); i++ { + if s[i] == c { + n++ + } + } + return n +} diff --git a/libgo/go/internal/bytealg/count_native.go b/libgo/go/internal/bytealg/count_native.go new file mode 100644 index 0000000..5ea5c28 --- /dev/null +++ b/libgo/go/internal/bytealg/count_native.go @@ -0,0 +1,34 @@ +// Copyright 2018 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 ignore_for_gccgo +// +build amd64 arm64 + +package bytealg + +//go:noescape +func Count(b []byte, c byte) int + +//go:noescape +func CountString(s string, c byte) int + +// A backup implementation to use by assembly. +func countGeneric(b []byte, c byte) int { + n := 0 + for _, x := range b { + if x == c { + n++ + } + } + return n +} +func countGenericString(s string, c byte) int { + n := 0 + for i := 0; i < len(s); i++ { + if s[i] == c { + n++ + } + } + return n +} diff --git a/libgo/go/internal/bytealg/equal_native.go b/libgo/go/internal/bytealg/equal_native.go new file mode 100644 index 0000000..b5c4530 --- /dev/null +++ b/libgo/go/internal/bytealg/equal_native.go @@ -0,0 +1,14 @@ +// Copyright 2018 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 bytealg + +// Note: there's no equal_generic.go because every platform must implement at least memequal_varlen in assembly. + +//go:noescape +func Equal(a, b []byte) bool + +// The compiler generates calls to runtime.memequal and runtime.memequal_varlen. +// In addition, the runtime calls runtime.memequal explicitly. +// Those functions are implemented in this package. diff --git a/libgo/go/internal/bytealg/gccgo.go b/libgo/go/internal/bytealg/gccgo.go new file mode 100644 index 0000000..8c78b63 --- /dev/null +++ b/libgo/go/internal/bytealg/gccgo.go @@ -0,0 +1,12 @@ +// Copyright 2018 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 gccgo + +package bytealg + +// MaxLen is the maximum length of the string to be searched for (argument b) in Index. +var MaxLen int = 32 + +const MaxBruteForce = 64 diff --git a/libgo/go/internal/bytealg/index_amd64.go b/libgo/go/internal/bytealg/index_amd64.go new file mode 100644 index 0000000..dd6a6cf --- /dev/null +++ b/libgo/go/internal/bytealg/index_amd64.go @@ -0,0 +1,28 @@ +// Copyright 2018 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 ignore_for_gccgo + +package bytealg + +import "internal/cpu" + +const MaxBruteForce = 64 + +func init() { + if cpu.X86.HasAVX2 { + MaxLen = 63 + } else { + MaxLen = 31 + } +} + +// Cutover reports the number of failures of IndexByte we should tolerate +// before switching over to Index. +// n is the number of bytes processed so far. +// See the bytes.Index implementation for details. +func Cutover(n int) int { + // 1 error per 8 characters, plus a few slop to start. + return (n + 16) / 8 +} diff --git a/libgo/go/internal/bytealg/index_arm64.go b/libgo/go/internal/bytealg/index_arm64.go new file mode 100644 index 0000000..51bdf16 --- /dev/null +++ b/libgo/go/internal/bytealg/index_arm64.go @@ -0,0 +1,25 @@ +// Copyright 2018 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 ignore_for_gccgo + +package bytealg + +// Empirical data shows that using IndexShortStr can get better +// performance when len(s) <= 16. +const MaxBruteForce = 16 + +func init() { + // Optimize cases where the length of the substring is less than 32 bytes + MaxLen = 32 +} + +// Cutover reports the number of failures of IndexByte we should tolerate +// before switching over to IndexShortStr. +// n is the number of bytes processed so far. +// See the bytes.Index implementation for details. +func Cutover(n int) int { + // 1 error per 16 characters, plus a few slop to start. + return 4 + n>>4 +} diff --git a/libgo/go/internal/bytealg/index_generic.go b/libgo/go/internal/bytealg/index_generic.go new file mode 100644 index 0000000..c595c23 --- /dev/null +++ b/libgo/go/internal/bytealg/index_generic.go @@ -0,0 +1,30 @@ +// Copyright 2018 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 ignore_for_gccgo +// +build !amd64,!arm64,!s390x + +package bytealg + +const MaxBruteForce = 0 + +// Index returns the index of the first instance of b in a, or -1 if b is not present in a. +// Requires 2 <= len(b) <= MaxLen. +func Index(a, b []byte) int { + panic("unimplemented") +} + +// IndexString returns the index of the first instance of b in a, or -1 if b is not present in a. +// Requires 2 <= len(b) <= MaxLen. +func IndexString(a, b string) int { + panic("unimplemented") +} + +// Cutover reports the number of failures of IndexByte we should tolerate +// before switching over to Index. +// n is the number of bytes processed so far. +// See the bytes.Index implementation for details. +func Cutover(n int) int { + panic("unimplemented") +} diff --git a/libgo/go/internal/bytealg/index_native.go b/libgo/go/internal/bytealg/index_native.go new file mode 100644 index 0000000..26f4769 --- /dev/null +++ b/libgo/go/internal/bytealg/index_native.go @@ -0,0 +1,24 @@ +// Copyright 2018 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 amd64 arm64 s390x + +package bytealg + +//go:noescape + +// Index returns the index of the first instance of b in a, or -1 if b is not present in a. +// Requires 2 <= len(b) <= MaxLen. +func Index(a, b []byte) int + +//go:noescape + +// IndexString returns the index of the first instance of b in a, or -1 if b is not present in a. +// Requires 2 <= len(b) <= MaxLen. +func IndexString(a, b string) int + +func Cutover(n int) int { + // 1 error per 8 characters, plus a few slop to start. + return (n + 16) / 8 +} diff --git a/libgo/go/internal/bytealg/index_s390x.go b/libgo/go/internal/bytealg/index_s390x.go new file mode 100644 index 0000000..ee076a0 --- /dev/null +++ b/libgo/go/internal/bytealg/index_s390x.go @@ -0,0 +1,33 @@ +// Copyright 2018 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 ignore_for_gcco + +package bytealg + +import "internal/cpu" + +const MaxBruteForce = 64 + +func init() { + // Note: we're kind of lucky that this flag is available at this point. + // The runtime sets HasVX when processing auxv records, and that happens + // to happen *before* running the init functions of packages that + // the runtime depends on. + // TODO: it would really be nicer for internal/cpu to figure out this + // flag by itself. Then we wouldn't need to depend on quirks of + // early startup initialization order. + if cpu.S390X.HasVX { + MaxLen = 64 + } +} + +// Cutover reports the number of failures of IndexByte we should tolerate +// before switching over to Index. +// n is the number of bytes processed so far. +// See the bytes.Index implementation for details. +func Cutover(n int) int { + // 1 error per 8 characters, plus a few slop to start. + return (n + 16) / 8 +} diff --git a/libgo/go/internal/bytealg/indexbyte_generic.go b/libgo/go/internal/bytealg/indexbyte_generic.go new file mode 100644 index 0000000..890cd42 --- /dev/null +++ b/libgo/go/internal/bytealg/indexbyte_generic.go @@ -0,0 +1,48 @@ +// Copyright 2018 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. + +// +ignore_for_gccgo +// +build !386,!amd64,!amd64p32,!s390x,!arm,!arm64,!ppc64,!ppc64le,!mips,!mipsle,!mips64,!mips64le,!wasm + +package bytealg + +import _ "unsafe" // for go:linkname + +func IndexByte(b []byte, c byte) int { + for i, x := range b { + if x == c { + return i + } + } + return -1 +} + +func IndexByteString(s string, c byte) int { + for i := 0; i < len(s); i++ { + if s[i] == c { + return i + } + } + return -1 +} + +//go:linkname bytes_IndexByte bytes.IndexByte +func bytes_IndexByte(b []byte, c byte) int { + for i, x := range b { + if x == c { + return i + } + } + return -1 +} + +//go:linkname strings_IndexByte strings.IndexByte +func strings_IndexByte(s string, c byte) int { + for i := 0; i < len(s); i++ { + if s[i] == c { + return i + } + } + return -1 +} diff --git a/libgo/go/internal/bytealg/indexbyte_native.go b/libgo/go/internal/bytealg/indexbyte_native.go new file mode 100644 index 0000000..5cf6fed --- /dev/null +++ b/libgo/go/internal/bytealg/indexbyte_native.go @@ -0,0 +1,13 @@ +// Copyright 2018 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 386 amd64 amd64p32 s390x arm arm64 ppc64 ppc64le mips mipsle mips64 mips64le wasm + +package bytealg + +//go:noescape +func IndexByte(b []byte, c byte) int + +//go:noescape +func IndexByteString(s string, c byte) int diff --git a/libgo/go/internal/cpu/cpu.go b/libgo/go/internal/cpu/cpu.go index 22fc561..9567a4f 100644 --- a/libgo/go/internal/cpu/cpu.go +++ b/libgo/go/internal/cpu/cpu.go @@ -6,6 +6,11 @@ // used by the Go standard library. package cpu +// DebugOptions is set to true by the runtime if go was compiled with GOEXPERIMENT=debugcpu +// and GOOS is Linux or Darwin. +// This should not be changed after it is initialized. +var DebugOptions bool + var X86 x86 // The booleans in x86 contain the correspondingly named cpuid feature bit. @@ -37,9 +42,9 @@ var PPC64 ppc64 // For ppc64x, it is safe to check only for ISA level starting on ISA v3.00, // since there are no optional categories. There are some exceptions that also -// require kernel support to work (darn, scv), so there are capability bits for +// require kernel support to work (darn, scv), so there are feature bits for // those as well. The minimum processor requirement is POWER8 (ISA 2.07), so we -// maintain some of the old capability checks for optional categories for +// maintain some of the old feature checks for optional categories for // safety. // The struct is padded to avoid false sharing. type ppc64 struct { @@ -63,15 +68,126 @@ var ARM64 arm64 // The booleans in arm64 contain the correspondingly named cpu feature bit. // The struct is padded to avoid false sharing. type arm64 struct { - _ [CacheLineSize]byte - HasFP bool - HasASIMD bool - HasEVTSTRM bool - HasAES bool - HasPMULL bool - HasSHA1 bool - HasSHA2 bool - HasCRC32 bool - HasATOMICS bool - _ [CacheLineSize]byte + _ [CacheLineSize]byte + HasFP bool + HasASIMD bool + HasEVTSTRM bool + HasAES bool + HasPMULL bool + HasSHA1 bool + HasSHA2 bool + HasCRC32 bool + HasATOMICS bool + HasFPHP bool + HasASIMDHP bool + HasCPUID bool + HasASIMDRDM bool + HasJSCVT bool + HasFCMA bool + HasLRCPC bool + HasDCPOP bool + HasSHA3 bool + HasSM3 bool + HasSM4 bool + HasASIMDDP bool + HasSHA512 bool + HasSVE bool + HasASIMDFHM bool + _ [CacheLineSize]byte +} + +var S390X s390x + +type s390x struct { + _ [CacheLineSize]byte + HasZArch bool // z architecture mode is active [mandatory] + HasSTFLE bool // store facility list extended [mandatory] + HasLDisp bool // long (20-bit) displacements [mandatory] + HasEImm bool // 32-bit immediates [mandatory] + HasDFP bool // decimal floating point + HasETF3Enhanced bool // ETF-3 enhanced + HasMSA bool // message security assist (CPACF) + HasAES bool // KM-AES{128,192,256} functions + HasAESCBC bool // KMC-AES{128,192,256} functions + HasAESCTR bool // KMCTR-AES{128,192,256} functions + HasAESGCM bool // KMA-GCM-AES{128,192,256} functions + HasGHASH bool // KIMD-GHASH function + HasSHA1 bool // K{I,L}MD-SHA-1 functions + HasSHA256 bool // K{I,L}MD-SHA-256 functions + HasSHA512 bool // K{I,L}MD-SHA-512 functions + HasVX bool // vector facility. Note: the runtime sets this when it processes auxv records. + _ [CacheLineSize]byte +} + +// Initialize examines the processor and sets the relevant variables above. +// This is called by the runtime package early in program initialization, +// before normal init functions are run. env is set by runtime on Linux and Darwin +// if go was compiled with GOEXPERIMENT=debugcpu. +func Initialize(env string) { + doinit() + processOptions(env) +} + +// options contains the cpu debug options that can be used in GODEBUGCPU. +// Options are arch dependent and are added by the arch specific doinit functions. +// Features that are mandatory for the specific GOARCH should not be added to options +// (e.g. SSE2 on amd64). +var options []option + +// Option names should be lower case. e.g. avx instead of AVX. +type option struct { + Name string + Feature *bool +} + +// processOptions disables CPU feature values based on the parsed env string. +// The env string is expected to be of the form feature1=0,feature2=0... +// where feature names is one of the architecture specifc list stored in the +// cpu packages options variable. If env contains all=0 then all capabilities +// referenced through the options variable are disabled. Other feature +// names and values other than 0 are silently ignored. +func processOptions(env string) { +field: + for env != "" { + field := "" + i := indexByte(env, ',') + if i < 0 { + field, env = env, "" + } else { + field, env = env[:i], env[i+1:] + } + i = indexByte(field, '=') + if i < 0 { + continue + } + key, value := field[:i], field[i+1:] + + // Only allow turning off CPU features by specifying '0'. + if value == "0" { + if key == "all" { + for _, v := range options { + *v.Feature = false + } + return + } else { + for _, v := range options { + if v.Name == key { + *v.Feature = false + continue field + } + } + } + } + } +} + +// indexByte returns the index of the first instance of c in s, +// or -1 if c is not present in s. +func indexByte(s string, c byte) int { + for i := 0; i < len(s); i++ { + if s[i] == c { + return i + } + } + return -1 } diff --git a/libgo/go/internal/cpu/cpu_386.go b/libgo/go/internal/cpu/cpu_386.go new file mode 100644 index 0000000..561c81f --- /dev/null +++ b/libgo/go/internal/cpu/cpu_386.go @@ -0,0 +1,7 @@ +// Copyright 2018 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 cpu + +const GOARCH = "386" diff --git a/libgo/go/internal/cpu/cpu_amd64.go b/libgo/go/internal/cpu/cpu_amd64.go new file mode 100644 index 0000000..9b00153 --- /dev/null +++ b/libgo/go/internal/cpu/cpu_amd64.go @@ -0,0 +1,7 @@ +// Copyright 2018 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 cpu + +const GOARCH = "amd64" diff --git a/libgo/go/internal/cpu/cpu_amd64p32.go b/libgo/go/internal/cpu/cpu_amd64p32.go new file mode 100644 index 0000000..177b14e --- /dev/null +++ b/libgo/go/internal/cpu/cpu_amd64p32.go @@ -0,0 +1,7 @@ +// Copyright 2018 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 cpu + +const GOARCH = "amd64p32" diff --git a/libgo/go/internal/cpu/cpu_arm64.go b/libgo/go/internal/cpu/cpu_arm64.go index e1278a1..009f2a2 100644 --- a/libgo/go/internal/cpu/cpu_arm64.go +++ b/libgo/go/internal/cpu/cpu_arm64.go @@ -2,42 +2,99 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build arm64 - package cpu const CacheLineSize = 64 // arm64 doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2. -// These are linknamed in runtime/os_linux_arm64.go and are initialized by -// archauxv(). -var arm64_hwcap uint -var arm64_hwcap2 uint +// These are initialized by archauxv in runtime/os_linux_arm64.go. +// These should not be changed after they are initialized. +var HWCap uint +var HWCap2 uint // HWCAP/HWCAP2 bits. These are exposed by Linux. const ( - _ARM64_FEATURE_HAS_FP = (1 << 0) - _ARM64_FEATURE_HAS_ASIMD = (1 << 1) - _ARM64_FEATURE_HAS_EVTSTRM = (1 << 2) - _ARM64_FEATURE_HAS_AES = (1 << 3) - _ARM64_FEATURE_HAS_PMULL = (1 << 4) - _ARM64_FEATURE_HAS_SHA1 = (1 << 5) - _ARM64_FEATURE_HAS_SHA2 = (1 << 6) - _ARM64_FEATURE_HAS_CRC32 = (1 << 7) - _ARM64_FEATURE_HAS_ATOMICS = (1 << 8) + hwcap_FP = (1 << 0) + hwcap_ASIMD = (1 << 1) + hwcap_EVTSTRM = (1 << 2) + hwcap_AES = (1 << 3) + hwcap_PMULL = (1 << 4) + hwcap_SHA1 = (1 << 5) + hwcap_SHA2 = (1 << 6) + hwcap_CRC32 = (1 << 7) + hwcap_ATOMICS = (1 << 8) + hwcap_FPHP = (1 << 9) + hwcap_ASIMDHP = (1 << 10) + hwcap_CPUID = (1 << 11) + hwcap_ASIMDRDM = (1 << 12) + hwcap_JSCVT = (1 << 13) + hwcap_FCMA = (1 << 14) + hwcap_LRCPC = (1 << 15) + hwcap_DCPOP = (1 << 16) + hwcap_SHA3 = (1 << 17) + hwcap_SM3 = (1 << 18) + hwcap_SM4 = (1 << 19) + hwcap_ASIMDDP = (1 << 20) + hwcap_SHA512 = (1 << 21) + hwcap_SVE = (1 << 22) + hwcap_ASIMDFHM = (1 << 23) ) -func init() { +func doinit() { + options = []option{ + {"evtstrm", &ARM64.HasEVTSTRM}, + {"aes", &ARM64.HasAES}, + {"pmull", &ARM64.HasPMULL}, + {"sha1", &ARM64.HasSHA1}, + {"sha2", &ARM64.HasSHA2}, + {"crc32", &ARM64.HasCRC32}, + {"atomics", &ARM64.HasATOMICS}, + {"fphp", &ARM64.HasFPHP}, + {"asimdhp", &ARM64.HasASIMDHP}, + {"cpuid", &ARM64.HasCPUID}, + {"asimdrdm", &ARM64.HasASIMDRDM}, + {"jscvt", &ARM64.HasJSCVT}, + {"fcma", &ARM64.HasFCMA}, + {"lrcpc", &ARM64.HasLRCPC}, + {"dcpop", &ARM64.HasDCPOP}, + {"sha3", &ARM64.HasSHA3}, + {"sm3", &ARM64.HasSM3}, + {"sm4", &ARM64.HasSM4}, + {"asimddp", &ARM64.HasASIMDDP}, + {"sha512", &ARM64.HasSHA512}, + {"sve", &ARM64.HasSVE}, + {"asimdfhm", &ARM64.HasASIMDFHM}, + + // These capabilities should always be enabled on arm64: + // {"fp", &ARM64.HasFP}, + // {"asimd", &ARM64.HasASIMD}, + } + // HWCAP feature bits - ARM64.HasFP = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_FP) - ARM64.HasASIMD = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_ASIMD) - ARM64.HasEVTSTRM = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_EVTSTRM) - ARM64.HasAES = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_AES) - ARM64.HasPMULL = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_PMULL) - ARM64.HasSHA1 = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_SHA1) - ARM64.HasSHA2 = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_SHA2) - ARM64.HasCRC32 = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_CRC32) - ARM64.HasATOMICS = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_ATOMICS) + ARM64.HasFP = isSet(HWCap, hwcap_FP) + ARM64.HasASIMD = isSet(HWCap, hwcap_ASIMD) + ARM64.HasEVTSTRM = isSet(HWCap, hwcap_EVTSTRM) + ARM64.HasAES = isSet(HWCap, hwcap_AES) + ARM64.HasPMULL = isSet(HWCap, hwcap_PMULL) + ARM64.HasSHA1 = isSet(HWCap, hwcap_SHA1) + ARM64.HasSHA2 = isSet(HWCap, hwcap_SHA2) + ARM64.HasCRC32 = isSet(HWCap, hwcap_CRC32) + ARM64.HasATOMICS = isSet(HWCap, hwcap_ATOMICS) + ARM64.HasFPHP = isSet(HWCap, hwcap_FPHP) + ARM64.HasASIMDHP = isSet(HWCap, hwcap_ASIMDHP) + ARM64.HasCPUID = isSet(HWCap, hwcap_CPUID) + ARM64.HasASIMDRDM = isSet(HWCap, hwcap_ASIMDRDM) + ARM64.HasJSCVT = isSet(HWCap, hwcap_JSCVT) + ARM64.HasFCMA = isSet(HWCap, hwcap_FCMA) + ARM64.HasLRCPC = isSet(HWCap, hwcap_LRCPC) + ARM64.HasDCPOP = isSet(HWCap, hwcap_DCPOP) + ARM64.HasSHA3 = isSet(HWCap, hwcap_SHA3) + ARM64.HasSM3 = isSet(HWCap, hwcap_SM3) + ARM64.HasSM4 = isSet(HWCap, hwcap_SM4) + ARM64.HasASIMDDP = isSet(HWCap, hwcap_ASIMDDP) + ARM64.HasSHA512 = isSet(HWCap, hwcap_SHA512) + ARM64.HasSVE = isSet(HWCap, hwcap_SVE) + ARM64.HasASIMDFHM = isSet(HWCap, hwcap_ASIMDFHM) } func isSet(hwc uint, value uint) bool { diff --git a/libgo/go/internal/cpu/cpu_arm64_test.go b/libgo/go/internal/cpu/cpu_arm64_test.go new file mode 100644 index 0000000..f4c419a --- /dev/null +++ b/libgo/go/internal/cpu/cpu_arm64_test.go @@ -0,0 +1,26 @@ +// Copyright 2018 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 cpu_test + +import ( + . "internal/cpu" + "runtime" + "testing" +) + +func TestARM64minimalFeatures(t *testing.T) { + switch runtime.GOOS { + case "linux", "android": + default: + t.Skipf("%s/arm64 is not supported", runtime.GOOS) + } + + if !ARM64.HasASIMD { + t.Fatalf("HasASIMD expected true, got false") + } + if !ARM64.HasFP { + t.Fatalf("HasFP expected true, got false") + } +} diff --git a/libgo/go/internal/cpu/cpu_gccgo.c b/libgo/go/internal/cpu/cpu_gccgo.c new file mode 100644 index 0000000..6625ddc --- /dev/null +++ b/libgo/go/internal/cpu/cpu_gccgo.c @@ -0,0 +1,66 @@ +// Copyright 2018 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 <stdint.h> + +#if defined(__i386__) || defined(__x86_64__) +#include <cpuid.h> +#include <x86intrin.h> +#endif + +#include "runtime.h" + +#if defined(__i386__) || defined(__x86_64__) + +struct cpuid_ret { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; +}; + +struct cpuid_ret cpuid(uint32_t, uint32_t) + __asm__(GOSYM_PREFIX "internal_cpu.cpuid") + __attribute__((no_split_stack)); + +struct cpuid_ret cpuid(uint32_t eaxArg, uint32_t ecxArg) { + unsigned int eax = 0; + unsigned int ebx = 0; + unsigned int ecx = 0; + unsigned int edx = 0; + struct cpuid_ret ret; + + __get_cpuid_count(eaxArg, ecxArg, &eax, &ebx, &ecx, &edx); + ret.eax = (uint32_t)(eax); + ret.ebx = (uint32_t)(ebx); + ret.ecx = (uint32_t)(ecx); + ret.edx = (uint32_t)(edx); + return ret; +} + +struct xgetbv_ret { + uint32_t eax; + uint32_t edx; +}; + +struct xgetbv_ret xgetbv(void) + __asm__(GOSYM_PREFIX "internal_cpu.xgetbv") + __attribute__((no_split_stack)); + +#pragma GCC push_options +#pragma GCC target("xsave") + +struct xgetbv_ret xgetbv(void) { + long long r; + struct xgetbv_ret ret; + + r = _xgetbv(0); + ret.eax = r & 0xffffffff; + ret.edx = r >> 32; + return ret; +} + +#pragma GCC pop_options + +#endif /* defined(__i386__) || defined(__x86_64__) */ diff --git a/libgo/go/internal/cpu/cpu_no_init.go b/libgo/go/internal/cpu/cpu_no_init.go new file mode 100644 index 0000000..1be4f29 --- /dev/null +++ b/libgo/go/internal/cpu/cpu_no_init.go @@ -0,0 +1,16 @@ +// Copyright 2018 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 !386 +// +build !amd64 +// +build !amd64p32 +// +build !arm64 +// +build !ppc64 +// +build !ppc64le +// +build !s390x + +package cpu + +func doinit() { +} diff --git a/libgo/go/internal/cpu/cpu_ppc64x.go b/libgo/go/internal/cpu/cpu_ppc64x.go index 7f09372..d3f02ef 100644 --- a/libgo/go/internal/cpu/cpu_ppc64x.go +++ b/libgo/go/internal/cpu/cpu_ppc64x.go @@ -9,10 +9,10 @@ package cpu const CacheLineSize = 128 // ppc64x doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2. -// These are linknamed in runtime/os_linux_ppc64x.go and are initialized by -// archauxv(). -var ppc64x_hwcap uint -var ppc64x_hwcap2 uint +// These are initialized by archauxv in runtime/os_linux_ppc64x.go. +// These should not be changed after they are initialized. +var HWCap uint +var HWCap2 uint // HWCAP/HWCAP2 bits. These are exposed by the kernel. const ( @@ -32,21 +32,35 @@ const ( _PPC_FEATURE2_SCV = 0x00100000 ) -func init() { +func doinit() { + options = []option{ + {"htm", &PPC64.HasHTM}, + {"htmnosc", &PPC64.HasHTMNOSC}, + {"darn", &PPC64.HasDARN}, + {"scv", &PPC64.HasSCV}, + + // These capabilities should always be enabled on ppc64 and ppc64le: + // {"vmx", &PPC64.HasVMX}, + // {"dfp", &PPC64.HasDFP}, + // {"vsx", &PPC64.HasVSX}, + // {"isel", &PPC64.HasISEL}, + // {"vcrypto", &PPC64.HasVCRYPTO}, + } + // HWCAP feature bits - PPC64.HasVMX = isSet(ppc64x_hwcap, _PPC_FEATURE_HAS_ALTIVEC) - PPC64.HasDFP = isSet(ppc64x_hwcap, _PPC_FEATURE_HAS_DFP) - PPC64.HasVSX = isSet(ppc64x_hwcap, _PPC_FEATURE_HAS_VSX) + PPC64.HasVMX = isSet(HWCap, _PPC_FEATURE_HAS_ALTIVEC) + PPC64.HasDFP = isSet(HWCap, _PPC_FEATURE_HAS_DFP) + PPC64.HasVSX = isSet(HWCap, _PPC_FEATURE_HAS_VSX) // HWCAP2 feature bits - PPC64.IsPOWER8 = isSet(ppc64x_hwcap2, _PPC_FEATURE2_ARCH_2_07) - PPC64.HasHTM = isSet(ppc64x_hwcap2, _PPC_FEATURE2_HAS_HTM) - PPC64.HasISEL = isSet(ppc64x_hwcap2, _PPC_FEATURE2_HAS_ISEL) - PPC64.HasVCRYPTO = isSet(ppc64x_hwcap2, _PPC_FEATURE2_HAS_VEC_CRYPTO) - PPC64.HasHTMNOSC = isSet(ppc64x_hwcap2, _PPC_FEATURE2_HTM_NOSC) - PPC64.IsPOWER9 = isSet(ppc64x_hwcap2, _PPC_FEATURE2_ARCH_3_00) - PPC64.HasDARN = isSet(ppc64x_hwcap2, _PPC_FEATURE2_DARN) - PPC64.HasSCV = isSet(ppc64x_hwcap2, _PPC_FEATURE2_SCV) + PPC64.IsPOWER8 = isSet(HWCap2, _PPC_FEATURE2_ARCH_2_07) + PPC64.HasHTM = isSet(HWCap2, _PPC_FEATURE2_HAS_HTM) + PPC64.HasISEL = isSet(HWCap2, _PPC_FEATURE2_HAS_ISEL) + PPC64.HasVCRYPTO = isSet(HWCap2, _PPC_FEATURE2_HAS_VEC_CRYPTO) + PPC64.HasHTMNOSC = isSet(HWCap2, _PPC_FEATURE2_HTM_NOSC) + PPC64.IsPOWER9 = isSet(HWCap2, _PPC_FEATURE2_ARCH_3_00) + PPC64.HasDARN = isSet(HWCap2, _PPC_FEATURE2_DARN) + PPC64.HasSCV = isSet(HWCap2, _PPC_FEATURE2_SCV) } func isSet(hwc uint, value uint) bool { diff --git a/libgo/go/internal/cpu/cpu_ppc64x_test.go b/libgo/go/internal/cpu/cpu_ppc64x_test.go new file mode 100644 index 0000000..9c43d1e --- /dev/null +++ b/libgo/go/internal/cpu/cpu_ppc64x_test.go @@ -0,0 +1,33 @@ +// Copyright 2018 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 ppc64 ppc64le + +package cpu_test + +import ( + . "internal/cpu" + "testing" +) + +func TestPPC64minimalFeatures(t *testing.T) { + if !PPC64.IsPOWER8 { + t.Fatalf("IsPOWER8 expected true, got false") + } + if !PPC64.HasVMX { + t.Fatalf("HasVMX expected true, got false") + } + if !PPC64.HasDFP { + t.Fatalf("HasDFP expected true, got false") + } + if !PPC64.HasVSX { + t.Fatalf("HasVSX expected true, got false") + } + if !PPC64.HasISEL { + t.Fatalf("HasISEL expected true, got false") + } + if !PPC64.HasVCRYPTO { + t.Fatalf("HasVCRYPTO expected true, got false") + } +} diff --git a/libgo/go/internal/cpu/cpu_s390x.go b/libgo/go/internal/cpu/cpu_s390x.go index 4455809..9dedb4c 100644 --- a/libgo/go/internal/cpu/cpu_s390x.go +++ b/libgo/go/internal/cpu/cpu_s390x.go @@ -5,3 +5,149 @@ package cpu const CacheLineSize = 256 + +// bitIsSet reports whether the bit at index is set. The bit index +// is in big endian order, so bit index 0 is the leftmost bit. +func bitIsSet(bits []uint64, index uint) bool { + return bits[index/64]&((1<<63)>>(index%64)) != 0 +} + +// function is the function code for the named function. +type function uint8 + +const ( + // KM{,A,C,CTR} function codes + aes128 function = 18 // AES-128 + aes192 = 19 // AES-192 + aes256 = 20 // AES-256 + + // K{I,L}MD function codes + sha1 = 1 // SHA-1 + sha256 = 2 // SHA-256 + sha512 = 3 // SHA-512 + + // KLMD function codes + ghash = 65 // GHASH +) + +// queryResult contains the result of a Query function +// call. Bits are numbered in big endian order so the +// leftmost bit (the MSB) is at index 0. +type queryResult struct { + bits [2]uint64 +} + +// Has reports whether the given functions are present. +func (q *queryResult) Has(fns ...function) bool { + if len(fns) == 0 { + panic("no function codes provided") + } + for _, f := range fns { + if !bitIsSet(q.bits[:], uint(f)) { + return false + } + } + return true +} + +// facility is a bit index for the named facility. +type facility uint8 + +const ( + // mandatory facilities + zarch facility = 1 // z architecture mode is active + stflef = 7 // store-facility-list-extended + ldisp = 18 // long-displacement + eimm = 21 // extended-immediate + + // miscellaneous facilities + dfp = 42 // decimal-floating-point + etf3eh = 30 // extended-translation 3 enhancement + + // cryptography facilities + msa = 17 // message-security-assist + msa3 = 76 // message-security-assist extension 3 + msa4 = 77 // message-security-assist extension 4 + msa5 = 57 // message-security-assist extension 5 + msa8 = 146 // message-security-assist extension 8 + + // Note: vx and highgprs are excluded because they require + // kernel support and so must be fetched from HWCAP. +) + +// facilityList contains the result of an STFLE call. +// Bits are numbered in big endian order so the +// leftmost bit (the MSB) is at index 0. +type facilityList struct { + bits [4]uint64 +} + +// Has reports whether the given facilities are present. +func (s *facilityList) Has(fs ...facility) bool { + if len(fs) == 0 { + panic("no facility bits provided") + } + for _, f := range fs { + if !bitIsSet(s.bits[:], uint(f)) { + return false + } + } + return true +} + +// The following feature detection functions are defined in cpu_s390x.s. +// They are likely to be expensive to call so the results should be cached. +func stfle() facilityList { panic("not implemented for gccgo") } +func kmQuery() queryResult { panic("not implemented for gccgo") } +func kmcQuery() queryResult { panic("not implemented for gccgo") } +func kmctrQuery() queryResult { panic("not implemented for gccgo") } +func kmaQuery() queryResult { panic("not implemented for gccgo") } +func kimdQuery() queryResult { panic("not implemented for gccgo") } +func klmdQuery() queryResult { panic("not implemented for gccgo") } + +func doinit() { + options = []option{ + {"zarch", &S390X.HasZArch}, + {"stfle", &S390X.HasSTFLE}, + {"ldisp", &S390X.HasLDisp}, + {"msa", &S390X.HasMSA}, + {"eimm", &S390X.HasEImm}, + {"dfp", &S390X.HasDFP}, + {"etf3eh", &S390X.HasETF3Enhanced}, + {"vx", &S390X.HasVX}, + } + + aes := []function{aes128, aes192, aes256} + facilities := stfle() + + S390X.HasZArch = facilities.Has(zarch) + S390X.HasSTFLE = facilities.Has(stflef) + S390X.HasLDisp = facilities.Has(ldisp) + S390X.HasEImm = facilities.Has(eimm) + S390X.HasDFP = facilities.Has(dfp) + S390X.HasETF3Enhanced = facilities.Has(etf3eh) + S390X.HasMSA = facilities.Has(msa) + + if S390X.HasMSA { + // cipher message + km, kmc := kmQuery(), kmcQuery() + S390X.HasAES = km.Has(aes...) + S390X.HasAESCBC = kmc.Has(aes...) + if facilities.Has(msa4) { + kmctr := kmctrQuery() + S390X.HasAESCTR = kmctr.Has(aes...) + } + if facilities.Has(msa8) { + kma := kmaQuery() + S390X.HasAESGCM = kma.Has(aes...) + } + + // compute message digest + kimd := kimdQuery() // intermediate (no padding) + klmd := klmdQuery() // last (padding) + S390X.HasSHA1 = kimd.Has(sha1) && klmd.Has(sha1) + S390X.HasSHA256 = kimd.Has(sha256) && klmd.Has(sha256) + S390X.HasSHA512 = kimd.Has(sha512) && klmd.Has(sha512) + S390X.HasGHASH = kimd.Has(ghash) // KLMD-GHASH does not exist + } +} diff --git a/libgo/go/internal/cpu/cpu_s390x_test.go b/libgo/go/internal/cpu/cpu_s390x_test.go new file mode 100644 index 0000000..d910bbe --- /dev/null +++ b/libgo/go/internal/cpu/cpu_s390x_test.go @@ -0,0 +1,63 @@ +// Copyright 2018 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 cpu_test + +import ( + "errors" + . "internal/cpu" + "io/ioutil" + "regexp" + "testing" +) + +func getFeatureList() ([]string, error) { + cpuinfo, err := ioutil.ReadFile("/proc/cpuinfo") + if err != nil { + return nil, err + } + r := regexp.MustCompile("features\\s*:\\s*(.*)") + b := r.FindSubmatch(cpuinfo) + if len(b) < 2 { + return nil, errors.New("no feature list in /proc/cpuinfo") + } + return regexp.MustCompile("\\s+").Split(string(b[1]), -1), nil +} + +func TestS390XAgainstCPUInfo(t *testing.T) { + // mapping of linux feature strings to S390X fields + mapping := make(map[string]*bool) + for _, option := range Options { + mapping[option.Name] = option.Feature + } + + // these must be true on the machines Go supports + mandatory := make(map[string]bool) + mandatory["zarch"] = false + mandatory["eimm"] = false + mandatory["ldisp"] = false + mandatory["stfle"] = false + + features, err := getFeatureList() + if err != nil { + t.Error(err) + } + for _, feature := range features { + if _, ok := mandatory[feature]; ok { + mandatory[feature] = true + } + if flag, ok := mapping[feature]; ok { + if !*flag { + t.Errorf("feature '%v' not detected", feature) + } + } else { + t.Logf("no entry for '%v'", feature) + } + } + for k, v := range mandatory { + if !v { + t.Errorf("mandatory feature '%v' not detected", k) + } + } +} diff --git a/libgo/go/internal/cpu/cpu_test.go b/libgo/go/internal/cpu/cpu_test.go index 07b0243..d4115a1 100644 --- a/libgo/go/internal/cpu/cpu_test.go +++ b/libgo/go/internal/cpu/cpu_test.go @@ -5,46 +5,52 @@ package cpu_test import ( - "internal/cpu" - "runtime" + . "internal/cpu" + "internal/testenv" + "os" + "os/exec" + "strings" "testing" ) -func TestAMD64minimalFeatures(t *testing.T) { - if runtime.GOARCH == "amd64" { - if !cpu.X86.HasSSE2 { - t.Fatalf("HasSSE2 expected true, got false") - } +func MustHaveDebugOptionsEnabled(t *testing.T) { + if !DebugOptions { + t.Skipf("skipping test: cpu feature options not enabled") } } -func TestAVX2hasAVX(t *testing.T) { - if runtime.GOARCH == "amd64" { - if cpu.X86.HasAVX2 && !cpu.X86.HasAVX { - t.Fatalf("HasAVX expected true, got false") - } +func runDebugOptionsTest(t *testing.T, test string, options string) { + MustHaveDebugOptionsEnabled(t) + + testenv.MustHaveExec(t) + + env := "GODEBUGCPU=" + options + + cmd := exec.Command(os.Args[0], "-test.run="+test) + cmd.Env = append(cmd.Env, env) + + output, err := cmd.CombinedOutput() + got := strings.TrimSpace(string(output)) + want := "PASS" + if err != nil || got != want { + t.Fatalf("%s with %s: want %s, got %v", test, env, want, got) } } -func TestPPC64minimalFeatures(t *testing.T) { - if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" { - if !cpu.PPC64.IsPOWER8 { - t.Fatalf("IsPOWER8 expected true, got false") - } - if !cpu.PPC64.HasVMX { - t.Fatalf("HasVMX expected true, got false") - } - if !cpu.PPC64.HasDFP { - t.Fatalf("HasDFP expected true, got false") - } - if !cpu.PPC64.HasVSX { - t.Fatalf("HasVSX expected true, got false") - } - if !cpu.PPC64.HasISEL { - t.Fatalf("HasISEL expected true, got false") - } - if !cpu.PPC64.HasVCRYPTO { - t.Fatalf("HasVCRYPTO expected true, got false") +func TestDisableAllCapabilities(t *testing.T) { + runDebugOptionsTest(t, "TestAllCapabilitiesDisabled", "all=0") +} + +func TestAllCapabilitiesDisabled(t *testing.T) { + MustHaveDebugOptionsEnabled(t) + + if os.Getenv("GODEBUGCPU") != "all=0" { + t.Skipf("skipping test: GODEBUGCPU=all=0 not set") + } + + for _, o := range Options { + if got := *o.Feature; got != false { + t.Errorf("%v: expected false, got %v", o.Name, got) } } } diff --git a/libgo/go/internal/cpu/cpu_wasm.go b/libgo/go/internal/cpu/cpu_wasm.go new file mode 100644 index 0000000..1107a7a --- /dev/null +++ b/libgo/go/internal/cpu/cpu_wasm.go @@ -0,0 +1,7 @@ +// Copyright 2018 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 cpu + +const CacheLineSize = 64 diff --git a/libgo/go/internal/cpu/cpu_x86.go b/libgo/go/internal/cpu/cpu_x86.go index 34c632f..7d9d3aa 100644 --- a/libgo/go/internal/cpu/cpu_x86.go +++ b/libgo/go/internal/cpu/cpu_x86.go @@ -14,7 +14,56 @@ func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) // xgetbv with ecx = 0 is implemented in cpu_x86.s. func xgetbv() (eax, edx uint32) -func init() { +const ( + // edx bits + cpuid_SSE2 = 1 << 26 + + // ecx bits + cpuid_SSE3 = 1 << 0 + cpuid_PCLMULQDQ = 1 << 1 + cpuid_SSSE3 = 1 << 9 + cpuid_FMA = 1 << 12 + cpuid_SSE41 = 1 << 19 + cpuid_SSE42 = 1 << 20 + cpuid_POPCNT = 1 << 23 + cpuid_AES = 1 << 25 + cpuid_OSXSAVE = 1 << 27 + cpuid_AVX = 1 << 28 + + // ebx bits + cpuid_BMI1 = 1 << 3 + cpuid_AVX2 = 1 << 5 + cpuid_BMI2 = 1 << 8 + cpuid_ERMS = 1 << 9 + cpuid_ADX = 1 << 19 +) + +func doinit() { + options = []option{ + {"adx", &X86.HasADX}, + {"aes", &X86.HasAES}, + {"avx", &X86.HasAVX}, + {"avx2", &X86.HasAVX2}, + {"bmi1", &X86.HasBMI1}, + {"bmi2", &X86.HasBMI2}, + {"erms", &X86.HasERMS}, + {"fma", &X86.HasFMA}, + {"pclmulqdq", &X86.HasPCLMULQDQ}, + {"popcnt", &X86.HasPOPCNT}, + {"sse3", &X86.HasSSE3}, + {"sse41", &X86.HasSSE41}, + {"sse42", &X86.HasSSE42}, + {"ssse3", &X86.HasSSSE3}, + + // sse2 set as last element so it can easily be removed again. See code below. + {"sse2", &X86.HasSSE2}, + } + + // Remove sse2 from options on amd64(p32) because SSE2 is a mandatory feature for these GOARCHs. + if GOARCH == "amd64" || GOARCH == "amd64p32" { + options = options[:len(options)-1] + } + maxID, _, _, _ := cpuid(0, 0) if maxID < 1 { @@ -22,40 +71,40 @@ func init() { } _, _, ecx1, edx1 := cpuid(1, 0) - X86.HasSSE2 = isSet(26, edx1) - - X86.HasSSE3 = isSet(0, ecx1) - X86.HasPCLMULQDQ = isSet(1, ecx1) - X86.HasSSSE3 = isSet(9, ecx1) - X86.HasFMA = isSet(12, ecx1) - X86.HasSSE41 = isSet(19, ecx1) - X86.HasSSE42 = isSet(20, ecx1) - X86.HasPOPCNT = isSet(23, ecx1) - X86.HasAES = isSet(25, ecx1) - X86.HasOSXSAVE = isSet(27, ecx1) + X86.HasSSE2 = isSet(edx1, cpuid_SSE2) + + X86.HasSSE3 = isSet(ecx1, cpuid_SSE3) + X86.HasPCLMULQDQ = isSet(ecx1, cpuid_PCLMULQDQ) + X86.HasSSSE3 = isSet(ecx1, cpuid_SSSE3) + X86.HasFMA = isSet(ecx1, cpuid_FMA) + X86.HasSSE41 = isSet(ecx1, cpuid_SSE41) + X86.HasSSE42 = isSet(ecx1, cpuid_SSE42) + X86.HasPOPCNT = isSet(ecx1, cpuid_POPCNT) + X86.HasAES = isSet(ecx1, cpuid_AES) + X86.HasOSXSAVE = isSet(ecx1, cpuid_OSXSAVE) osSupportsAVX := false // For XGETBV, OSXSAVE bit is required and sufficient. if X86.HasOSXSAVE { eax, _ := xgetbv() // Check if XMM and YMM registers have OS support. - osSupportsAVX = isSet(1, eax) && isSet(2, eax) + osSupportsAVX = isSet(eax, 1<<1) && isSet(eax, 1<<2) } - X86.HasAVX = isSet(28, ecx1) && osSupportsAVX + X86.HasAVX = isSet(ecx1, cpuid_AVX) && osSupportsAVX if maxID < 7 { return } _, ebx7, _, _ := cpuid(7, 0) - X86.HasBMI1 = isSet(3, ebx7) - X86.HasAVX2 = isSet(5, ebx7) && osSupportsAVX - X86.HasBMI2 = isSet(8, ebx7) - X86.HasERMS = isSet(9, ebx7) - X86.HasADX = isSet(19, ebx7) + X86.HasBMI1 = isSet(ebx7, cpuid_BMI1) + X86.HasAVX2 = isSet(ebx7, cpuid_AVX2) && osSupportsAVX + X86.HasBMI2 = isSet(ebx7, cpuid_BMI2) + X86.HasERMS = isSet(ebx7, cpuid_ERMS) + X86.HasADX = isSet(ebx7, cpuid_ADX) } -func isSet(bitpos uint, value uint32) bool { - return value&(1<<bitpos) != 0 +func isSet(hwc uint32, value uint32) bool { + return hwc&value != 0 } diff --git a/libgo/go/internal/cpu/cpu_x86_test.go b/libgo/go/internal/cpu/cpu_x86_test.go new file mode 100644 index 0000000..d03306c --- /dev/null +++ b/libgo/go/internal/cpu/cpu_x86_test.go @@ -0,0 +1,47 @@ +// Copyright 2018 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 386 amd64 amd64p32 + +package cpu_test + +import ( + . "internal/cpu" + "os" + "runtime" + "testing" +) + +func TestAMD64minimalFeatures(t *testing.T) { + if runtime.GOARCH != "amd64" { + return + } + + if !X86.HasSSE2 { + t.Fatalf("HasSSE2 expected true, got false") + } +} + +func TestX86ifAVX2hasAVX(t *testing.T) { + if X86.HasAVX2 && !X86.HasAVX { + t.Fatalf("HasAVX expected true when HasAVX2 is true, got false") + } +} + +func TestDisableSSE2(t *testing.T) { + runDebugOptionsTest(t, "TestSSE2DebugOption", "sse2=0") +} + +func TestSSE2DebugOption(t *testing.T) { + MustHaveDebugOptionsEnabled(t) + + if os.Getenv("GODEBUGCPU") != "sse2=0" { + t.Skipf("skipping test: GODEBUGCPU=sse2=0 not set") + } + + want := runtime.GOARCH != "386" // SSE2 can only be disabled on 386. + if got := X86.HasSSE2; got != want { + t.Errorf("X86.HasSSE2 on %s expected %v, got %v", runtime.GOARCH, want, got) + } +} diff --git a/libgo/go/internal/cpu/export_test.go b/libgo/go/internal/cpu/export_test.go new file mode 100644 index 0000000..91bfc1b --- /dev/null +++ b/libgo/go/internal/cpu/export_test.go @@ -0,0 +1,9 @@ +// Copyright 2018 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 cpu + +var ( + Options = options +) diff --git a/libgo/go/internal/goroot/gc.go b/libgo/go/internal/goroot/gc.go new file mode 100644 index 0000000..ce4c1e5 --- /dev/null +++ b/libgo/go/internal/goroot/gc.go @@ -0,0 +1,141 @@ +// Copyright 2018 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 gc + +package goroot + +import ( + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "sync" +) + +// IsStandardPackage returns whether path is a standard package, +// given goroot and compiler. +func IsStandardPackage(goroot, compiler, path string) bool { + switch compiler { + case "gc": + dir := filepath.Join(goroot, "src", path) + _, err := os.Stat(dir) + return err == nil + case "gccgo": + return gccgoSearch.isStandard(path) + default: + panic("unknown compiler " + compiler) + } +} + +// gccgoSearch holds the gccgo search directories. +type gccgoDirs struct { + once sync.Once + dirs []string +} + +// gccgoSearch is used to check whether a gccgo package exists in the +// standard library. +var gccgoSearch gccgoDirs + +// init finds the gccgo search directories. If this fails it leaves dirs == nil. +func (gd *gccgoDirs) init() { + gccgo := os.Getenv("GCCGO") + if gccgo == "" { + gccgo = "gccgo" + } + bin, err := exec.LookPath(gccgo) + if err != nil { + return + } + + allDirs, err := exec.Command(bin, "-print-search-dirs").Output() + if err != nil { + return + } + versionB, err := exec.Command(bin, "-dumpversion").Output() + if err != nil { + return + } + version := strings.TrimSpace(string(versionB)) + machineB, err := exec.Command(bin, "-dumpmachine").Output() + if err != nil { + return + } + machine := strings.TrimSpace(string(machineB)) + + dirsEntries := strings.Split(string(allDirs), "\n") + const prefix = "libraries: =" + var dirs []string + for _, dirEntry := range dirsEntries { + if strings.HasPrefix(dirEntry, prefix) { + dirs = filepath.SplitList(strings.TrimPrefix(dirEntry, prefix)) + break + } + } + if len(dirs) == 0 { + return + } + + var lastDirs []string + for _, dir := range dirs { + goDir := filepath.Join(dir, "go", version) + if fi, err := os.Stat(goDir); err == nil && fi.IsDir() { + gd.dirs = append(gd.dirs, goDir) + goDir = filepath.Join(goDir, machine) + if fi, err = os.Stat(goDir); err == nil && fi.IsDir() { + gd.dirs = append(gd.dirs, goDir) + } + } + if fi, err := os.Stat(dir); err == nil && fi.IsDir() { + lastDirs = append(lastDirs, dir) + } + } + gd.dirs = append(gd.dirs, lastDirs...) +} + +// isStandard returns whether path is a standard library for gccgo. +func (gd *gccgoDirs) isStandard(path string) bool { + // Quick check: if the first path component has a '.', it's not + // in the standard library. This skips most GOPATH directories. + i := strings.Index(path, "/") + if i < 0 { + i = len(path) + } + if strings.Contains(path[:i], ".") { + return false + } + + if path == "unsafe" { + // Special case. + return true + } + + gd.once.Do(gd.init) + if gd.dirs == nil { + // We couldn't find the gccgo search directories. + // Best guess, since the first component did not contain + // '.', is that this is a standard library package. + return true + } + + for _, dir := range gd.dirs { + full := filepath.Join(dir, path) + pkgdir, pkg := filepath.Split(full) + for _, p := range [...]string{ + full, + full + ".gox", + pkgdir + "lib" + pkg + ".so", + pkgdir + "lib" + pkg + ".a", + full + ".o", + } { + if fi, err := os.Stat(p); err == nil && !fi.IsDir() { + return true + } + } + } + + return false +} diff --git a/libgo/go/internal/goroot/gccgo.go b/libgo/go/internal/goroot/gccgo.go new file mode 100644 index 0000000..098e77d --- /dev/null +++ b/libgo/go/internal/goroot/gccgo.go @@ -0,0 +1,27 @@ +// Copyright 2018 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 gccgo + +package goroot + +import ( + "os" + "path/filepath" +) + +// IsStandardPackage returns whether path is a standard package, +// given goroot and compiler. +func IsStandardPackage(goroot, compiler, path string) bool { + switch compiler { + case "gc": + dir := filepath.Join(goroot, "src", path) + _, err := os.Stat(dir) + return err == nil + case "gccgo": + return stdpkg[path] + default: + panic("unknown compiler " + compiler) + } +} diff --git a/libgo/go/internal/poll/fd_mutex.go b/libgo/go/internal/poll/fd_mutex.go index 2ba7de7..0a8ee6f 100644 --- a/libgo/go/internal/poll/fd_mutex.go +++ b/libgo/go/internal/poll/fd_mutex.go @@ -34,6 +34,8 @@ const ( mutexWMask = (1<<20 - 1) << 43 ) +const overflowMsg = "too many concurrent operations on a single file or socket (max 1048575)" + // Read operations must do rwlock(true)/rwunlock(true). // // Write operations must do rwlock(false)/rwunlock(false). @@ -56,7 +58,7 @@ func (mu *fdMutex) incref() bool { } new := old + mutexRef if new&mutexRefMask == 0 { - panic("inconsistent poll.fdMutex") + panic(overflowMsg) } if atomic.CompareAndSwapUint64(&mu.state, old, new) { return true @@ -75,7 +77,7 @@ func (mu *fdMutex) increfAndClose() bool { // Mark as closed and acquire a reference. new := (old | mutexClosed) + mutexRef if new&mutexRefMask == 0 { - panic("inconsistent poll.fdMutex") + panic(overflowMsg) } // Remove all read and write waiters. new &^= mutexRMask | mutexWMask @@ -136,13 +138,13 @@ func (mu *fdMutex) rwlock(read bool) bool { // Lock is free, acquire it. new = (old | mutexBit) + mutexRef if new&mutexRefMask == 0 { - panic("inconsistent poll.fdMutex") + panic(overflowMsg) } } else { // Wait for lock. new = old + mutexWait if new&mutexMask == 0 { - panic("inconsistent poll.fdMutex") + panic(overflowMsg) } } if atomic.CompareAndSwapUint64(&mu.state, old, new) { diff --git a/libgo/go/internal/poll/fd_mutex_test.go b/libgo/go/internal/poll/fd_mutex_test.go index bab81c6..2c53c45 100644 --- a/libgo/go/internal/poll/fd_mutex_test.go +++ b/libgo/go/internal/poll/fd_mutex_test.go @@ -8,6 +8,7 @@ import ( . "internal/poll" "math/rand" "runtime" + "strings" "testing" "time" ) @@ -121,6 +122,27 @@ func TestMutexPanic(t *testing.T) { mu.RWUnlock(false) } +func TestMutexOverflowPanic(t *testing.T) { + defer func() { + r := recover() + if r == nil { + t.Fatal("did not panic") + } + msg, ok := r.(string) + if !ok { + t.Fatalf("unexpected panic type %T", r) + } + if !strings.Contains(msg, "too many") || strings.Contains(msg, "inconsistent") { + t.Fatalf("wrong panic message %q", msg) + } + }() + + var mu1 FDMutex + for i := 0; i < 1<<21; i++ { + mu1.Incref() + } +} + func TestMutexStress(t *testing.T) { P := 8 N := int(1e6) diff --git a/libgo/go/internal/poll/fd_poll_nacl.go b/libgo/go/internal/poll/fd_poll_nacljs.go index 2df3003..832dddb 100644 --- a/libgo/go/internal/poll/fd_poll_nacl.go +++ b/libgo/go/internal/poll/fd_poll_nacljs.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build nacl js,wasm + package poll import ( @@ -40,6 +42,9 @@ func (pd *pollDesc) wait(mode int, isFile bool) error { if pd.closing { return errClosing(isFile) } + if isFile { // TODO(neelance): wasm: Use callbacks from JS to block until the read/write finished. + return nil + } return ErrTimeout } diff --git a/libgo/go/internal/poll/fd_posix.go b/libgo/go/internal/poll/fd_posix.go index 4e6e355..23ad7a0 100644 --- a/libgo/go/internal/poll/fd_posix.go +++ b/libgo/go/internal/poll/fd_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package poll diff --git a/libgo/go/internal/poll/fd_unix.go b/libgo/go/internal/poll/fd_unix.go index cfd441e..28bc32f 100644 --- a/libgo/go/internal/poll/fd_unix.go +++ b/libgo/go/internal/poll/fd_unix.go @@ -2,13 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package poll import ( "io" "runtime" + "sync/atomic" "syscall" ) @@ -30,6 +31,9 @@ type FD struct { // Semaphore signaled when file is closed. csema uint32 + // Non-zero if this file has been set to blocking mode. + isBlocking uint32 + // Whether this is a streaming descriptor, as opposed to a // packet-based descriptor like a UDP socket. Immutable. IsStream bool @@ -40,9 +44,6 @@ type FD struct { // Whether this is a file rather than a network socket. isFile bool - - // Whether this file has been set to blocking mode. - isBlocking bool } // Init initializes the FD. The Sysfd field should already be set. @@ -56,10 +57,16 @@ func (fd *FD) Init(net string, pollable bool) error { fd.isFile = true } if !pollable { - fd.isBlocking = true + fd.isBlocking = 1 return nil } - return fd.pd.init(fd) + err := fd.pd.init(fd) + if err != nil { + // If we could not initialize the runtime poller, + // assume we are using blocking mode. + fd.isBlocking = 1 + } + return err } // Destroy closes the file descriptor. This is called when there are @@ -96,7 +103,9 @@ func (fd *FD) Close() error { // reference, it is already closed. Only wait if the file has // not been set to blocking mode, as otherwise any current I/O // may be blocking, and that would block the Close. - if !fd.isBlocking { + // No need for an atomic read of isBlocking, increfAndClose means + // we have exclusive access to fd. + if fd.isBlocking == 0 { runtime_Semacquire(&fd.csema) } @@ -118,7 +127,10 @@ func (fd *FD) SetBlocking() error { return err } defer fd.decref() - fd.isBlocking = true + // Atomic store so that concurrent calls to SetBlocking + // do not cause a race condition. isBlocking only ever goes + // from 0 to 1 so there is no real race here. + atomic.StoreUint32(&fd.isBlocking, 1) return syscall.SetNonblock(fd.Sysfd, false) } @@ -433,6 +445,59 @@ func (fd *FD) Fstat(s *syscall.Stat_t) error { return syscall.Fstat(fd.Sysfd, s) } +// Use a helper function to call fcntl. This is defined in C in +// libgo/runtime. +//extern __go_fcntl_uintptr +func fcntl(uintptr, uintptr, uintptr) (uintptr, uintptr) + +// tryDupCloexec indicates whether F_DUPFD_CLOEXEC should be used. +// If the kernel doesn't support it, this is set to 0. +var tryDupCloexec = int32(1) + +// DupCloseOnExec dups fd and marks it close-on-exec. +func DupCloseOnExec(fd int) (int, string, error) { + if atomic.LoadInt32(&tryDupCloexec) == 1 { + syscall.Entersyscall() + r0, errno := fcntl(uintptr(fd), syscall.F_DUPFD_CLOEXEC, 0) + syscall.Exitsyscall() + e1 := syscall.Errno(errno) + switch e1 { + case 0: + return int(r0), "", nil + case syscall.EINVAL, syscall.ENOSYS: + // Old kernel, or js/wasm (which returns + // ENOSYS). Fall back to the portable way from + // now on. + atomic.StoreInt32(&tryDupCloexec, 0) + default: + return -1, "fcntl", e1 + } + } + return dupCloseOnExecOld(fd) +} + +// dupCloseOnExecUnixOld is the traditional way to dup an fd and +// set its O_CLOEXEC bit, using two system calls. +func dupCloseOnExecOld(fd int) (int, string, error) { + syscall.ForkLock.RLock() + defer syscall.ForkLock.RUnlock() + newfd, err := syscall.Dup(fd) + if err != nil { + return -1, "dup", err + } + syscall.CloseOnExec(newfd) + return newfd, "", nil +} + +// Dup duplicates the file descriptor. +func (fd *FD) Dup() (int, string, error) { + if err := fd.incref(); err != nil { + return -1, "", err + } + defer fd.decref() + return DupCloseOnExec(fd.Sysfd) +} + // On Unix variants only, expose the IO event for the net code. // WaitWrite waits until data can be read from fd. diff --git a/libgo/go/internal/poll/fd_windows.go b/libgo/go/internal/poll/fd_windows.go index 187908b..d04d332 100644 --- a/libgo/go/internal/poll/fd_windows.go +++ b/libgo/go/internal/poll/fd_windows.go @@ -34,19 +34,18 @@ var ( var canCancelIO bool // determines if CancelIoEx API is present -// This package uses SetFileCompletionNotificationModes Windows API -// to skip calling GetQueuedCompletionStatus if an IO operation completes -// synchronously. Unfortuently SetFileCompletionNotificationModes is not -// available on Windows XP. Also there is a known bug where -// SetFileCompletionNotificationModes crashes on some systems -// (see http://support.microsoft.com/kb/2568167 for details). +// This package uses the SetFileCompletionNotificationModes Windows +// API to skip calling GetQueuedCompletionStatus if an IO operation +// completes synchronously. There is a known bug where +// SetFileCompletionNotificationModes crashes on some systems (see +// https://support.microsoft.com/kb/2568167 for details). var useSetFileCompletionNotificationModes bool // determines is SetFileCompletionNotificationModes is present and safe to use // checkSetFileCompletionNotificationModes verifies that // SetFileCompletionNotificationModes Windows API is present // on the system and is safe to use. -// See http://support.microsoft.com/kb/2568167 for details. +// See https://support.microsoft.com/kb/2568167 for details. func checkSetFileCompletionNotificationModes() { err := syscall.LoadSetFileCompletionNotificationModes() if err != nil { @@ -225,6 +224,10 @@ func (s *ioSrv) ExecIO(o *operation, submit func(o *operation) error) (int, erro // All is good. Extract our IO results and return. if o.errno != 0 { err = syscall.Errno(o.errno) + // More data available. Return back the size of received data. + if err == syscall.ERROR_MORE_DATA || err == windows.WSAEMSGSIZE { + return int(o.qty), err + } return 0, err } return int(o.qty), nil @@ -379,7 +382,7 @@ func (fd *FD) Init(net string, pollable bool) (string, error) { // We do not use events, so we can skip them always. flags := uint8(syscall.FILE_SKIP_SET_EVENT_ON_HANDLE) // It's not safe to skip completion notifications for UDP: - // http://blogs.technet.com/b/winserverperformance/archive/2008/06/26/designing-applications-for-high-performance-part-iii.aspx + // https://blogs.technet.com/b/winserverperformance/archive/2008/06/26/designing-applications-for-high-performance-part-iii.aspx if net == "tcp" { flags |= syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS } @@ -909,12 +912,46 @@ func (fd *FD) RawControl(f func(uintptr)) error { // RawRead invokes the user-defined function f for a read operation. func (fd *FD) RawRead(f func(uintptr) bool) error { - return errors.New("not implemented") + if err := fd.readLock(); err != nil { + return err + } + defer fd.readUnlock() + for { + if f(uintptr(fd.Sysfd)) { + return nil + } + + // Use a zero-byte read as a way to get notified when this + // socket is readable. h/t https://stackoverflow.com/a/42019668/332798 + o := &fd.rop + o.InitBuf(nil) + if !fd.IsStream { + o.flags |= windows.MSG_PEEK + } + _, err := rsrv.ExecIO(o, func(o *operation) error { + return syscall.WSARecv(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, &o.o, nil) + }) + if err == windows.WSAEMSGSIZE { + // expected with a 0-byte peek, ignore. + } else if err != nil { + return err + } + } } // RawWrite invokes the user-defined function f for a write operation. func (fd *FD) RawWrite(f func(uintptr) bool) error { - return errors.New("not implemented") + if err := fd.writeLock(); err != nil { + return err + } + defer fd.writeUnlock() + + if f(uintptr(fd.Sysfd)) { + return nil + } + + // TODO(tmm1): find a way to detect socket writability + return syscall.EWINDOWS } func sockaddrToRaw(sa syscall.Sockaddr) (unsafe.Pointer, int32, error) { diff --git a/libgo/go/internal/poll/hook_cloexec.go b/libgo/go/internal/poll/hook_cloexec.go index 73df6ed..5c93bda 100644 --- a/libgo/go/internal/poll/hook_cloexec.go +++ b/libgo/go/internal/poll/hook_cloexec.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build dragonfly freebsd linux +// +build dragonfly freebsd linux netbsd openbsd package poll diff --git a/libgo/go/internal/poll/hook_unix.go b/libgo/go/internal/poll/hook_unix.go index 4cf36cc..a7512b1 100644 --- a/libgo/go/internal/poll/hook_unix.go +++ b/libgo/go/internal/poll/hook_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package poll diff --git a/libgo/go/internal/poll/sendfile_windows.go b/libgo/go/internal/poll/sendfile_windows.go index 4a15b75..dc93e85 100644 --- a/libgo/go/internal/poll/sendfile_windows.go +++ b/libgo/go/internal/poll/sendfile_windows.go @@ -25,6 +25,16 @@ func SendFile(fd *FD, src syscall.Handle, n int64) (int64, error) { o := &fd.wop o.qty = uint32(n) o.handle = src + + // TODO(brainman): skip calling syscall.Seek if OS allows it + curpos, err := syscall.Seek(o.handle, 0, 1) + if err != nil { + return 0, err + } + + o.o.Offset = uint32(curpos) + o.o.OffsetHigh = uint32(curpos >> 32) + done, err := wsrv.ExecIO(o, func(o *operation) error { return syscall.TransmitFile(o.fd.Sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND) }) diff --git a/libgo/go/internal/poll/sock_cloexec.go b/libgo/go/internal/poll/sock_cloexec.go index 0d5c8bd..691cb8e 100644 --- a/libgo/go/internal/poll/sock_cloexec.go +++ b/libgo/go/internal/poll/sock_cloexec.go @@ -5,7 +5,7 @@ // This file implements sysSocket and accept for platforms that // provide a fast path for setting SetNonblock and CloseOnExec. -// +build dragonfly freebsd linux +// +build dragonfly freebsd linux netbsd openbsd package poll diff --git a/libgo/go/internal/poll/splice_linux.go b/libgo/go/internal/poll/splice_linux.go new file mode 100644 index 0000000..aa237e5 --- /dev/null +++ b/libgo/go/internal/poll/splice_linux.go @@ -0,0 +1,183 @@ +// Copyright 2018 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 poll + +import ( + "sync/atomic" + "syscall" + "unsafe" +) + +const ( + // spliceNonblock makes calls to splice(2) non-blocking. + spliceNonblock = 0x2 + + // maxSpliceSize is the maximum amount of data Splice asks + // the kernel to move in a single call to splice(2). + maxSpliceSize = 4 << 20 +) + +// Splice transfers at most remain bytes of data from src to dst, using the +// splice system call to minimize copies of data from and to userspace. +// +// Splice creates a temporary pipe, to serve as a buffer for the data transfer. +// src and dst must both be stream-oriented sockets. +// +// If err != nil, sc is the system call which caused the error. +func Splice(dst, src *FD, remain int64) (written int64, handled bool, sc string, err error) { + prfd, pwfd, sc, err := newTempPipe() + if err != nil { + return 0, false, sc, err + } + defer destroyTempPipe(prfd, pwfd) + // From here on, the operation should be considered handled, + // even if Splice doesn't transfer any data. + var inPipe, n int + for err == nil && remain > 0 { + max := maxSpliceSize + if int64(max) > remain { + max = int(remain) + } + inPipe, err = spliceDrain(pwfd, src, max) + // spliceDrain should never return EAGAIN, so if err != nil, + // Splice cannot continue. If inPipe == 0 && err == nil, + // src is at EOF, and the transfer is complete. + if err != nil || (inPipe == 0 && err == nil) { + break + } + n, err = splicePump(dst, prfd, inPipe) + if n > 0 { + written += int64(n) + remain -= int64(n) + } + } + if err != nil { + return written, true, "splice", err + } + return written, true, "", nil +} + +// spliceDrain moves data from a socket to a pipe. +// +// Invariant: when entering spliceDrain, the pipe is empty. It is either in its +// initial state, or splicePump has emptied it previously. +// +// Given this, spliceDrain can reasonably assume that the pipe is ready for +// writing, so if splice returns EAGAIN, it must be because the socket is not +// ready for reading. +// +// If spliceDrain returns (0, nil), src is at EOF. +func spliceDrain(pipefd int, sock *FD, max int) (int, error) { + if err := sock.readLock(); err != nil { + return 0, err + } + defer sock.readUnlock() + if err := sock.pd.prepareRead(sock.isFile); err != nil { + return 0, err + } + for { + n, err := splice(pipefd, sock.Sysfd, max, spliceNonblock) + if err != syscall.EAGAIN { + return n, err + } + if err := sock.pd.waitRead(sock.isFile); err != nil { + return n, err + } + } +} + +// splicePump moves all the buffered data from a pipe to a socket. +// +// Invariant: when entering splicePump, there are exactly inPipe +// bytes of data in the pipe, from a previous call to spliceDrain. +// +// By analogy to the condition from spliceDrain, splicePump +// only needs to poll the socket for readiness, if splice returns +// EAGAIN. +// +// If splicePump cannot move all the data in a single call to +// splice(2), it loops over the buffered data until it has written +// all of it to the socket. This behavior is similar to the Write +// step of an io.Copy in userspace. +func splicePump(sock *FD, pipefd int, inPipe int) (int, error) { + if err := sock.writeLock(); err != nil { + return 0, err + } + defer sock.writeUnlock() + if err := sock.pd.prepareWrite(sock.isFile); err != nil { + return 0, err + } + written := 0 + for inPipe > 0 { + n, err := splice(sock.Sysfd, pipefd, inPipe, spliceNonblock) + // Here, the condition n == 0 && err == nil should never be + // observed, since Splice controls the write side of the pipe. + if n > 0 { + inPipe -= n + written += n + continue + } + if err != syscall.EAGAIN { + return written, err + } + if err := sock.pd.waitWrite(sock.isFile); err != nil { + return written, err + } + } + return written, nil +} + +// splice wraps the splice system call. Since the current implementation +// only uses splice on sockets and pipes, the offset arguments are unused. +// splice returns int instead of int64, because callers never ask it to +// move more data in a single call than can fit in an int32. +func splice(out int, in int, max int, flags int) (int, error) { + n, err := syscall.Splice(in, nil, out, nil, max, flags) + return int(n), err +} + +var disableSplice unsafe.Pointer + +// newTempPipe sets up a temporary pipe for a splice operation. +func newTempPipe() (prfd, pwfd int, sc string, err error) { + p := (*bool)(atomic.LoadPointer(&disableSplice)) + if p != nil && *p { + return -1, -1, "splice", syscall.EINVAL + } + + var fds [2]int + // pipe2 was added in 2.6.27 and our minimum requirement is 2.6.23, so it + // might not be implemented. Falling back to pipe is possible, but prior to + // 2.6.29 splice returns -EAGAIN instead of 0 when the connection is + // closed. + const flags = syscall.O_CLOEXEC | syscall.O_NONBLOCK + if err := syscall.Pipe2(fds[:], flags); err != nil { + return -1, -1, "pipe2", err + } + + if p == nil { + p = new(bool) + defer atomic.StorePointer(&disableSplice, unsafe.Pointer(p)) + + // F_GETPIPE_SZ was added in 2.6.35, which does not have the -EAGAIN bug. + if _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fds[0]), syscall.F_GETPIPE_SZ, 0); errno != 0 { + *p = true + destroyTempPipe(fds[0], fds[1]) + return -1, -1, "fcntl", errno + } + } + + return fds[0], fds[1], "", nil +} + +// destroyTempPipe destroys a temporary pipe. +func destroyTempPipe(prfd, pwfd int) error { + err := CloseFunc(prfd) + err1 := CloseFunc(pwfd) + if err == nil { + return err1 + } + return err +} diff --git a/libgo/go/internal/poll/sys_cloexec.go b/libgo/go/internal/poll/sys_cloexec.go index fb5f2bc..64e4612 100644 --- a/libgo/go/internal/poll/sys_cloexec.go +++ b/libgo/go/internal/poll/sys_cloexec.go @@ -5,7 +5,7 @@ // This file implements sysSocket and accept for platforms that do not // provide a fast path for setting SetNonblock and CloseOnExec. -// +build aix darwin nacl netbsd openbsd solaris +// +build aix darwin js,wasm nacl solaris package poll diff --git a/libgo/go/internal/syscall/unix/nonblocking.go b/libgo/go/internal/syscall/unix/nonblocking.go new file mode 100644 index 0000000..da7f8bd --- /dev/null +++ b/libgo/go/internal/syscall/unix/nonblocking.go @@ -0,0 +1,23 @@ +// Copyright 2018 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 dragonfly freebsd linux netbsd openbsd solaris + +package unix + +import ( + "syscall" + _ "unsafe" // for go:linkname +) + +//extern __go_fcntl +func syscall_fcntl(fd int32, cmd int32, arg int32) int32 + +func IsNonblock(fd int) (nonblocking bool, err error) { + flag := syscall_fcntl(int32(fd), syscall.F_GETFL, 0) + if flag < 0 { + return false, syscall.GetErrno() + } + return flag&syscall.O_NONBLOCK != 0, nil +} diff --git a/libgo/go/internal/syscall/unix/nonblocking_js.go b/libgo/go/internal/syscall/unix/nonblocking_js.go new file mode 100644 index 0000000..a360b53 --- /dev/null +++ b/libgo/go/internal/syscall/unix/nonblocking_js.go @@ -0,0 +1,11 @@ +// Copyright 2018 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 js,wasm + +package unix + +func IsNonblock(fd int) (nonblocking bool, err error) { + return false, nil +} diff --git a/libgo/go/internal/syscall/unix/nonblocking_nacl.go b/libgo/go/internal/syscall/unix/nonblocking_nacl.go new file mode 100644 index 0000000..ff67c75 --- /dev/null +++ b/libgo/go/internal/syscall/unix/nonblocking_nacl.go @@ -0,0 +1,9 @@ +// Copyright 2018 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 unix + +func IsNonblock(fd int) (nonblocking bool, err error) { + return false, nil +} diff --git a/libgo/go/internal/syscall/windows/exec_windows_test.go b/libgo/go/internal/syscall/windows/exec_windows_test.go index 94fd95b..283d7ce 100644 --- a/libgo/go/internal/syscall/windows/exec_windows_test.go +++ b/libgo/go/internal/syscall/windows/exec_windows_test.go @@ -17,10 +17,6 @@ import ( ) func TestRunAtLowIntegrity(t *testing.T) { - if isWindowsXP(t) { - t.Skip("Windows XP does not support windows integrity levels") - } - if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { wil, err := getProcessIntegrityLevel() if err != nil { @@ -56,15 +52,6 @@ func TestRunAtLowIntegrity(t *testing.T) { } } -func isWindowsXP(t *testing.T) bool { - v, err := syscall.GetVersion() - if err != nil { - t.Fatalf("GetVersion failed: %v", err) - } - major := byte(v) - return major < 6 -} - const ( sidWilLow = `S-1-16-4096` ) diff --git a/libgo/go/internal/syscall/windows/registry/key.go b/libgo/go/internal/syscall/windows/registry/key.go index 62144d3..cc3d0c7 100644 --- a/libgo/go/internal/syscall/windows/registry/key.go +++ b/libgo/go/internal/syscall/windows/registry/key.go @@ -93,12 +93,10 @@ func OpenKey(k Key, path string, access uint32) (Key, error) { // The parameter n controls the number of returned names, // analogous to the way os.File.Readdirnames works. func (k Key) ReadSubKeyNames(n int) ([]string, error) { - ki, err := k.Stat() - if err != nil { - return nil, err - } - names := make([]string, 0, ki.SubKeyCount) - buf := make([]uint16, ki.MaxSubKeyLen+1) // extra room for terminating zero byte + names := make([]string, 0) + // Registry key size limit is 255 bytes and described there: + // https://msdn.microsoft.com/library/windows/desktop/ms724872.aspx + buf := make([]uint16, 256) //plus extra room for terminating zero byte loopItems: for i := uint32(0); ; i++ { if n > 0 { diff --git a/libgo/go/internal/syscall/windows/registry/registry_test.go b/libgo/go/internal/syscall/windows/registry/registry_test.go index 56069d7..c77329c 100644 --- a/libgo/go/internal/syscall/windows/registry/registry_test.go +++ b/libgo/go/internal/syscall/windows/registry/registry_test.go @@ -28,7 +28,7 @@ func randKeyName(prefix string) string { } func TestReadSubKeyNames(t *testing.T) { - k, err := registry.OpenKey(registry.CLASSES_ROOT, "TypeLib", registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE) + k, err := registry.OpenKey(registry.CLASSES_ROOT, "TypeLib", registry.ENUMERATE_SUB_KEYS) if err != nil { t.Fatal(err) } diff --git a/libgo/go/internal/syscall/windows/registry/zsyscall_windows.go b/libgo/go/internal/syscall/windows/registry/zsyscall_windows.go index a3a1f5f..c3f3a8a 100644 --- a/libgo/go/internal/syscall/windows/registry/zsyscall_windows.go +++ b/libgo/go/internal/syscall/windows/registry/zsyscall_windows.go @@ -1,4 +1,4 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// Code generated by 'go generate'; DO NOT EDIT. package registry diff --git a/libgo/go/internal/syscall/windows/security_windows.go b/libgo/go/internal/syscall/windows/security_windows.go index 14ea425..4a2dfc0 100644 --- a/libgo/go/internal/syscall/windows/security_windows.go +++ b/libgo/go/internal/syscall/windows/security_windows.go @@ -81,3 +81,48 @@ const ( TokenPrimary TokenType = 1 TokenImpersonation TokenType = 2 ) + +//sys GetProfilesDirectory(dir *uint16, dirLen *uint32) (err error) = userenv.GetProfilesDirectoryW + +const ( + LG_INCLUDE_INDIRECT = 0x1 + MAX_PREFERRED_LENGTH = 0xFFFFFFFF +) + +type LocalGroupUserInfo0 struct { + Name *uint16 +} + +type UserInfo4 struct { + Name *uint16 + Password *uint16 + PasswordAge uint32 + Priv uint32 + HomeDir *uint16 + Comment *uint16 + Flags uint32 + ScriptPath *uint16 + AuthFlags uint32 + FullName *uint16 + UsrComment *uint16 + Parms *uint16 + Workstations *uint16 + LastLogon uint32 + LastLogoff uint32 + AcctExpires uint32 + MaxStorage uint32 + UnitsPerWeek uint32 + LogonHours *byte + BadPwCount uint32 + NumLogons uint32 + LogonServer *uint16 + CountryCode uint32 + CodePage uint32 + UserSid *syscall.SID + PrimaryGroupID uint32 + Profile *uint16 + HomeDirDrive *uint16 + PasswordExpired uint32 +} + +//sys NetUserGetLocalGroups(serverName *uint16, userName *uint16, level uint32, flags uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32) (neterr error) = netapi32.NetUserGetLocalGroups diff --git a/libgo/go/internal/syscall/windows/symlink_windows.go b/libgo/go/internal/syscall/windows/symlink_windows.go new file mode 100644 index 0000000..cc2163e --- /dev/null +++ b/libgo/go/internal/syscall/windows/symlink_windows.go @@ -0,0 +1,14 @@ +// Copyright 2018 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 windows + +import "syscall" + +const ( + ERROR_INVALID_PARAMETER syscall.Errno = 87 + + // symlink support for CreateSymbolicLink() starting with Windows 10 (1703, v10.0.14972) + SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2 +) diff --git a/libgo/go/internal/syscall/windows/syscall_windows.go b/libgo/go/internal/syscall/windows/syscall_windows.go index b531f89..66fe932 100644 --- a/libgo/go/internal/syscall/windows/syscall_windows.go +++ b/libgo/go/internal/syscall/windows/syscall_windows.go @@ -12,6 +12,7 @@ import ( const ( ERROR_SHARING_VIOLATION syscall.Errno = 32 + ERROR_INVALID_NAME syscall.Errno = 123 ERROR_NO_UNICODE_TRANSLATION syscall.Errno = 1113 ) @@ -122,6 +123,7 @@ const ( WSAEMSGSIZE syscall.Errno = 10040 + MSG_PEEK = 0x2 MSG_TRUNC = 0x0100 MSG_CTRUNC = 0x0200 diff --git a/libgo/go/internal/syscall/windows/zsyscall_windows.go b/libgo/go/internal/syscall/windows/zsyscall_windows.go index bdca80c..550a8a5 100644 --- a/libgo/go/internal/syscall/windows/zsyscall_windows.go +++ b/libgo/go/internal/syscall/windows/zsyscall_windows.go @@ -1,4 +1,4 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// Code generated by 'go generate'; DO NOT EDIT. package windows @@ -41,6 +41,7 @@ var ( modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll")) modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll")) modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll")) + moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll")) modpsapi = syscall.NewLazyDLL(sysdll.Add("psapi.dll")) procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") @@ -62,6 +63,8 @@ var ( procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx") procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") + procGetProfilesDirectoryW = moduserenv.NewProc("GetProfilesDirectoryW") + procNetUserGetLocalGroups = modnetapi32.NewProc("NetUserGetLocalGroups") procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") ) @@ -287,6 +290,26 @@ func SetTokenInformation(tokenHandle syscall.Token, tokenInformationClass uint32 return } +func GetProfilesDirectory(dir *uint16, dirLen *uint32) (err error) { + r1, _, e1 := syscall.Syscall(procGetProfilesDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(dir)), uintptr(unsafe.Pointer(dirLen)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func NetUserGetLocalGroups(serverName *uint16, userName *uint16, level uint32, flags uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32) (neterr error) { + r0, _, _ := syscall.Syscall9(procNetUserGetLocalGroups.Addr(), 8, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(userName)), uintptr(level), uintptr(flags), uintptr(unsafe.Pointer(buf)), uintptr(prefMaxLen), uintptr(unsafe.Pointer(entriesRead)), uintptr(unsafe.Pointer(totalEntries)), 0) + if r0 != 0 { + neterr = syscall.Errno(r0) + } + return +} + func GetProcessMemoryInfo(handle syscall.Handle, memCounters *PROCESS_MEMORY_COUNTERS, cb uint32) (err error) { r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(memCounters)), uintptr(cb)) if r1 == 0 { diff --git a/libgo/go/internal/testenv/testenv.go b/libgo/go/internal/testenv/testenv.go index 87d01d2..1518690 100644 --- a/libgo/go/internal/testenv/testenv.go +++ b/libgo/go/internal/testenv/testenv.go @@ -48,7 +48,7 @@ func HasGoBuild() bool { return false } switch runtime.GOOS { - case "android", "nacl": + case "android", "nacl", "js": return false case "darwin": if strings.HasPrefix(runtime.GOARCH, "arm") { @@ -128,7 +128,7 @@ func GoTool() (string, error) { // using os.StartProcess or (more commonly) exec.Command. func HasExec() bool { switch runtime.GOOS { - case "nacl": + case "nacl", "js": return false case "darwin": if strings.HasPrefix(runtime.GOARCH, "arm") { @@ -166,13 +166,16 @@ func MustHaveExec(t testing.TB) { // HasExternalNetwork reports whether the current system can use // external (non-localhost) networks. func HasExternalNetwork() bool { - return !testing.Short() + return !testing.Short() && runtime.GOOS != "nacl" && runtime.GOOS != "js" } // MustHaveExternalNetwork checks that the current system can use // external (non-localhost) networks. // If not, MustHaveExternalNetwork calls t.Skip with an explanation. func MustHaveExternalNetwork(t testing.TB) { + if runtime.GOOS == "nacl" || runtime.GOOS == "js" { + t.Skipf("skipping test: no external network on %s", runtime.GOOS) + } if testing.Short() { t.Skipf("skipping test: no external network in -short mode") } diff --git a/libgo/go/internal/testenv/testenv_notwin.go b/libgo/go/internal/testenv/testenv_notwin.go index 3853973..d8ce6cd 100644 --- a/libgo/go/internal/testenv/testenv_notwin.go +++ b/libgo/go/internal/testenv/testenv_notwin.go @@ -18,7 +18,3 @@ func hasSymlink() (ok bool, reason string) { return true, "" } - -func IsWindowsXP() bool { - return false -} diff --git a/libgo/go/internal/testenv/testenv_windows.go b/libgo/go/internal/testenv/testenv_windows.go index 4a7da5f..eb8d6ac 100644 --- a/libgo/go/internal/testenv/testenv_windows.go +++ b/libgo/go/internal/testenv/testenv_windows.go @@ -46,12 +46,3 @@ func hasSymlink() (ok bool, reason string) { return false, "" } - -func IsWindowsXP() bool { - v, err := syscall.GetVersion() - if err != nil { - panic("GetVersion failed: " + err.Error()) - } - major := byte(v) - return major < 6 -} diff --git a/libgo/go/internal/trace/goroutines.go b/libgo/go/internal/trace/goroutines.go index 923a157..2d7d3aa 100644 --- a/libgo/go/internal/trace/goroutines.go +++ b/libgo/go/internal/trace/goroutines.go @@ -4,7 +4,9 @@ package trace -// GDesc contains statistics about execution of a single goroutine. +import "sort" + +// GDesc contains statistics and execution details of a single goroutine. type GDesc struct { ID uint64 Name string @@ -13,6 +15,37 @@ type GDesc struct { StartTime int64 EndTime int64 + // List of regions in the goroutine, sorted based on the start time. + Regions []*UserRegionDesc + + // Statistics of execution time during the goroutine execution. + GExecutionStat + + *gdesc // private part. +} + +// UserRegionDesc represents a region and goroutine execution stats +// while the region was active. +type UserRegionDesc struct { + TaskID uint64 + Name string + + // Region start event. Normally EvUserRegion start event or nil, + // but can be EvGoCreate event if the region is a synthetic + // region representing task inheritance from the parent goroutine. + Start *Event + + // Region end event. Normally EvUserRegion end event or nil, + // but can be EvGoStop or EvGoEnd event if the goroutine + // terminated without explicitely ending the region. + End *Event + + GExecutionStat +} + +// GExecutionStat contains statistics about a goroutine's execution +// during a period of time. +type GExecutionStat struct { ExecTime int64 SchedWaitTime int64 IOTime int64 @@ -21,8 +54,84 @@ type GDesc struct { GCTime int64 SweepTime int64 TotalTime int64 +} + +// sub returns the stats v-s. +func (s GExecutionStat) sub(v GExecutionStat) (r GExecutionStat) { + r = s + r.ExecTime -= v.ExecTime + r.SchedWaitTime -= v.SchedWaitTime + r.IOTime -= v.IOTime + r.BlockTime -= v.BlockTime + r.SyscallTime -= v.SyscallTime + r.GCTime -= v.GCTime + r.SweepTime -= v.SweepTime + r.TotalTime -= v.TotalTime + return r +} + +// snapshotStat returns the snapshot of the goroutine execution statistics. +// This is called as we process the ordered trace event stream. lastTs and +// activeGCStartTime are used to process pending statistics if this is called +// before any goroutine end event. +func (g *GDesc) snapshotStat(lastTs, activeGCStartTime int64) (ret GExecutionStat) { + ret = g.GExecutionStat + + if g.gdesc == nil { + return ret // finalized GDesc. No pending state. + } + + if activeGCStartTime != 0 { // terminating while GC is active + if g.CreationTime < activeGCStartTime { + ret.GCTime += lastTs - activeGCStartTime + } else { + // The goroutine's lifetime completely overlaps + // with a GC. + ret.GCTime += lastTs - g.CreationTime + } + } + + if g.TotalTime == 0 { + ret.TotalTime = lastTs - g.CreationTime + } + + if g.lastStartTime != 0 { + ret.ExecTime += lastTs - g.lastStartTime + } + if g.blockNetTime != 0 { + ret.IOTime += lastTs - g.blockNetTime + } + if g.blockSyncTime != 0 { + ret.BlockTime += lastTs - g.blockSyncTime + } + if g.blockSyscallTime != 0 { + ret.SyscallTime += lastTs - g.blockSyscallTime + } + if g.blockSchedTime != 0 { + ret.SchedWaitTime += lastTs - g.blockSchedTime + } + if g.blockSweepTime != 0 { + ret.SweepTime += lastTs - g.blockSweepTime + } + return ret +} - *gdesc // private part +// finalize is called when processing a goroutine end event or at +// the end of trace processing. This finalizes the execution stat +// and any active regions in the goroutine, in which case trigger is nil. +func (g *GDesc) finalize(lastTs, activeGCStartTime int64, trigger *Event) { + if trigger != nil { + g.EndTime = trigger.Ts + } + finalStat := g.snapshotStat(lastTs, activeGCStartTime) + + g.GExecutionStat = finalStat + for _, s := range g.activeRegions { + s.End = trigger + s.GExecutionStat = finalStat.sub(s.GExecutionStat) + g.Regions = append(g.Regions, s) + } + *(g.gdesc) = gdesc{} } // gdesc is a private part of GDesc that is required only during analysis. @@ -34,19 +143,34 @@ type gdesc struct { blockSweepTime int64 blockGCTime int64 blockSchedTime int64 + + activeRegions []*UserRegionDesc // stack of active regions } // GoroutineStats generates statistics for all goroutines in the trace. func GoroutineStats(events []*Event) map[uint64]*GDesc { gs := make(map[uint64]*GDesc) var lastTs int64 - var gcStartTime int64 + var gcStartTime int64 // gcStartTime == 0 indicates gc is inactive. for _, ev := range events { lastTs = ev.Ts switch ev.Type { case EvGoCreate: g := &GDesc{ID: ev.Args[0], CreationTime: ev.Ts, gdesc: new(gdesc)} g.blockSchedTime = ev.Ts + // When a goroutine is newly created, inherit the + // task of the active region. For ease handling of + // this case, we create a fake region description with + // the task id. + if creatorG := gs[ev.G]; creatorG != nil && len(creatorG.gdesc.activeRegions) > 0 { + regions := creatorG.gdesc.activeRegions + s := regions[len(regions)-1] + if s.TaskID != 0 { + g.gdesc.activeRegions = []*UserRegionDesc{ + {TaskID: s.TaskID, Start: ev}, + } + } + } gs[g.ID] = g case EvGoStart, EvGoStartLabel: g := gs[ev.G] @@ -64,28 +188,31 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc { } case EvGoEnd, EvGoStop: g := gs[ev.G] - g.ExecTime += ev.Ts - g.lastStartTime - g.TotalTime = ev.Ts - g.CreationTime - g.EndTime = ev.Ts + g.finalize(ev.Ts, gcStartTime, ev) case EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime + g.lastStartTime = 0 g.blockSyncTime = ev.Ts case EvGoSched, EvGoPreempt: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime + g.lastStartTime = 0 g.blockSchedTime = ev.Ts case EvGoSleep, EvGoBlock: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime + g.lastStartTime = 0 case EvGoBlockNet: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime + g.lastStartTime = 0 g.blockNetTime = ev.Ts case EvGoBlockGC: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime + g.lastStartTime = 0 g.blockGCTime = ev.Ts case EvGoUnblock: g := gs[ev.Args[0]] @@ -101,6 +228,7 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc { case EvGoSysBlock: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime + g.lastStartTime = 0 g.blockSyscallTime = ev.Ts case EvGoSysExit: g := gs[ev.G] @@ -125,36 +253,62 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc { gcStartTime = ev.Ts case EvGCDone: for _, g := range gs { - if g.EndTime == 0 { + if g.EndTime != 0 { + continue + } + if gcStartTime < g.CreationTime { + g.GCTime += ev.Ts - g.CreationTime + } else { g.GCTime += ev.Ts - gcStartTime } } + gcStartTime = 0 // indicates gc is inactive. + case EvUserRegion: + g := gs[ev.G] + switch mode := ev.Args[1]; mode { + case 0: // region start + g.activeRegions = append(g.activeRegions, &UserRegionDesc{ + Name: ev.SArgs[0], + TaskID: ev.Args[0], + Start: ev, + GExecutionStat: g.snapshotStat(lastTs, gcStartTime), + }) + case 1: // region end + var sd *UserRegionDesc + if regionStk := g.activeRegions; len(regionStk) > 0 { + n := len(regionStk) + sd = regionStk[n-1] + regionStk = regionStk[:n-1] // pop + g.activeRegions = regionStk + } else { + sd = &UserRegionDesc{ + Name: ev.SArgs[0], + TaskID: ev.Args[0], + } + } + sd.GExecutionStat = g.snapshotStat(lastTs, gcStartTime).sub(sd.GExecutionStat) + sd.End = ev + g.Regions = append(g.Regions, sd) + } } } for _, g := range gs { - if g.TotalTime == 0 { - g.TotalTime = lastTs - g.CreationTime - } - if g.EndTime == 0 { - g.EndTime = lastTs - } - if g.blockNetTime != 0 { - g.IOTime += lastTs - g.blockNetTime - g.blockNetTime = 0 - } - if g.blockSyncTime != 0 { - g.BlockTime += lastTs - g.blockSyncTime - g.blockSyncTime = 0 - } - if g.blockSyscallTime != 0 { - g.SyscallTime += lastTs - g.blockSyscallTime - g.blockSyscallTime = 0 - } - if g.blockSchedTime != 0 { - g.SchedWaitTime += lastTs - g.blockSchedTime - g.blockSchedTime = 0 - } + g.finalize(lastTs, gcStartTime, nil) + + // sort based on region start time + sort.Slice(g.Regions, func(i, j int) bool { + x := g.Regions[i].Start + y := g.Regions[j].Start + if x == nil { + return true + } + if y == nil { + return false + } + return x.Ts < y.Ts + }) + g.gdesc = nil } diff --git a/libgo/go/internal/trace/parser.go b/libgo/go/internal/trace/parser.go index c7954f0..c371ff3 100644 --- a/libgo/go/internal/trace/parser.go +++ b/libgo/go/internal/trace/parser.go @@ -55,6 +55,8 @@ type Event struct { // for blocking GoSysCall: the associated GoSysExit // for GoSysExit: the next GoStart // for GCMarkAssistStart: the associated GCMarkAssistDone + // for UserTaskCreate: the UserTaskEnd + // for UserRegion: if the start region, the corresponding UserRegion end event Link *Event } @@ -106,10 +108,7 @@ func parse(r io.Reader, bin string) (int, ParseResult, error) { if err != nil { return 0, ParseResult{}, err } - events, err = removeFutile(events) - if err != nil { - return 0, ParseResult{}, err - } + events = removeFutile(events) err = postProcessTrace(ver, events) if err != nil { return 0, ParseResult{}, err @@ -130,9 +129,10 @@ func parse(r io.Reader, bin string) (int, ParseResult, error) { // rawEvent is a helper type used during parsing. type rawEvent struct { - off int - typ byte - args []uint64 + off int + typ byte + args []uint64 + sargs []string } // readTrace does wire-format parsing and verification. @@ -150,7 +150,7 @@ func readTrace(r io.Reader) (ver int, events []rawEvent, strings map[uint64]stri return } switch ver { - case 1005, 1007, 1008, 1009, 1010: + case 1005, 1007, 1008, 1009, 1010, 1011: // Note: When adding a new version, add canned traces // from the old version to the test suite using mkcanned.bash. break @@ -259,11 +259,34 @@ func readTrace(r io.Reader) (ver int, events []rawEvent, strings map[uint64]stri return } } + switch ev.typ { + case EvUserLog: // EvUserLog records are followed by a value string of length ev.args[len(ev.args)-1] + var s string + s, off, err = readStr(r, off) + ev.sargs = append(ev.sargs, s) + } events = append(events, ev) } return } +func readStr(r io.Reader, off0 int) (s string, off int, err error) { + var sz uint64 + sz, off, err = readVal(r, off0) + if err != nil || sz == 0 { + return "", off, err + } + if sz > 1e6 { + return "", off, fmt.Errorf("string at offset %d is too large (len=%d)", off, sz) + } + buf := make([]byte, sz) + n, err := io.ReadFull(r, buf) + if err != nil || sz != uint64(n) { + return "", off + n, fmt.Errorf("failed to read trace at offset %d: read %v, want %v, error %v", off, n, sz, err) + } + return string(buf), off + n, nil +} + // parseHeader parses trace header of the form "go 1.7 trace\x00\x00\x00\x00" // and returns parsed version as 1007. func parseHeader(buf []byte) (int, error) { @@ -416,6 +439,15 @@ func parseEvents(ver int, rawEvents []rawEvent, strings map[uint64]string) (even lastG = 0 case EvGoSysExit, EvGoWaiting, EvGoInSyscall: e.G = e.Args[0] + case EvUserTaskCreate: + // e.Args 0: taskID, 1:parentID, 2:nameID + e.SArgs = []string{strings[e.Args[2]]} + case EvUserRegion: + // e.Args 0: taskID, 1: mode, 2:nameID + e.SArgs = []string{strings[e.Args[2]]} + case EvUserLog: + // e.Args 0: taskID, 1:keyID, 2: stackID + e.SArgs = []string{strings[e.Args[1]], raw.sargs[0]} } batches[lastP] = append(batches[lastP], e) } @@ -470,7 +502,7 @@ func parseEvents(ver int, rawEvents []rawEvent, strings map[uint64]string) (even // ahead and acquired the mutex before the first goroutine is scheduled, // so the first goroutine has to block again. Such wakeups happen on buffered // channels and sync.Mutex, but are generally not interesting for end user. -func removeFutile(events []*Event) ([]*Event, error) { +func removeFutile(events []*Event) []*Event { // Two non-trivial aspects: // 1. A goroutine can be preempted during a futile wakeup and migrate to another P. // We want to remove all of that. @@ -517,7 +549,7 @@ func removeFutile(events []*Event) ([]*Event, error) { newEvents = append(newEvents, ev) } } - return newEvents, nil + return newEvents } // ErrTimeOrder is returned by Parse when the trace contains @@ -551,6 +583,8 @@ func postProcessTrace(ver int, events []*Event) error { gs := make(map[uint64]gdesc) ps := make(map[int]pdesc) + tasks := make(map[uint64]*Event) // task id to task creation events + activeRegions := make(map[uint64][]*Event) // goroutine id to stack of regions gs[0] = gdesc{state: gRunning} var evGC, evSTW *Event @@ -695,6 +729,15 @@ func postProcessTrace(ver int, events []*Event) error { g.evStart = nil g.state = gDead p.g = 0 + + if ev.Type == EvGoEnd { // flush all active regions + regions := activeRegions[ev.G] + for _, s := range regions { + s.Link = ev + } + delete(activeRegions, ev.G) + } + case EvGoSched, EvGoPreempt: if err := checkRunning(p, g, ev, false); err != nil { return err @@ -756,6 +799,42 @@ func postProcessTrace(ver int, events []*Event) error { g.evStart.Link = ev g.evStart = nil p.g = 0 + case EvUserTaskCreate: + taskid := ev.Args[0] + if prevEv, ok := tasks[taskid]; ok { + return fmt.Errorf("task id conflicts (id:%d), %q vs %q", taskid, ev, prevEv) + } + tasks[ev.Args[0]] = ev + case EvUserTaskEnd: + taskid := ev.Args[0] + if taskCreateEv, ok := tasks[taskid]; ok { + taskCreateEv.Link = ev + delete(tasks, taskid) + } + case EvUserRegion: + mode := ev.Args[1] + regions := activeRegions[ev.G] + if mode == 0 { // region start + activeRegions[ev.G] = append(regions, ev) // push + } else if mode == 1 { // region end + n := len(regions) + if n > 0 { // matching region start event is in the trace. + s := regions[n-1] + if s.Args[0] != ev.Args[0] || s.SArgs[0] != ev.SArgs[0] { // task id, region name mismatch + return fmt.Errorf("misuse of region in goroutine %d: span end %q when the inner-most active span start event is %q", ev.G, ev, s) + } + // Link region start event with span end event + s.Link = ev + + if n > 1 { + activeRegions[ev.G] = regions[:n-1] + } else { + delete(activeRegions, ev.G) + } + } + } else { + return fmt.Errorf("invalid user region mode: %q", ev) + } } gs[ev.G] = g @@ -869,12 +948,20 @@ func Print(events []*Event) { // PrintEvent dumps the event to stdout. For debugging. func PrintEvent(ev *Event) { + fmt.Printf("%s\n", ev) +} + +func (ev *Event) String() string { desc := EventDescriptions[ev.Type] - fmt.Printf("%v %v p=%v g=%v off=%v", ev.Ts, desc.Name, ev.P, ev.G, ev.Off) + w := new(bytes.Buffer) + fmt.Fprintf(w, "%v %v p=%v g=%v off=%v", ev.Ts, desc.Name, ev.P, ev.G, ev.Off) for i, a := range desc.Args { - fmt.Printf(" %v=%v", a, ev.Args[i]) + fmt.Fprintf(w, " %v=%v", a, ev.Args[i]) + } + for i, a := range desc.SArgs { + fmt.Fprintf(w, " %v=%v", a, ev.SArgs[i]) } - fmt.Printf("\n") + return w.String() } // argNum returns total number of args for the event accounting for timestamps, @@ -967,7 +1054,11 @@ const ( EvGoBlockGC = 42 // goroutine blocks on GC assist [timestamp, stack] EvGCMarkAssistStart = 43 // GC mark assist start [timestamp, stack] EvGCMarkAssistDone = 44 // GC mark assist done [timestamp] - EvCount = 45 + EvUserTaskCreate = 45 // trace.NewContext [timestamp, internal task id, internal parent id, stack, name string] + EvUserTaskEnd = 46 // end of task [timestamp, internal task id, stack] + EvUserRegion = 47 // trace.WithRegion [timestamp, internal task id, mode(0:start, 1:end), stack, name string] + EvUserLog = 48 // trace.Log [timestamp, internal id, key string id, stack, value string] + EvCount = 49 ) var EventDescriptions = [EvCount]struct { @@ -975,50 +1066,55 @@ var EventDescriptions = [EvCount]struct { minVersion int Stack bool Args []string + SArgs []string // string arguments }{ - EvNone: {"None", 1005, false, []string{}}, - EvBatch: {"Batch", 1005, false, []string{"p", "ticks"}}, // in 1.5 format it was {"p", "seq", "ticks"} - EvFrequency: {"Frequency", 1005, false, []string{"freq"}}, // in 1.5 format it was {"freq", "unused"} - EvStack: {"Stack", 1005, false, []string{"id", "siz"}}, - EvGomaxprocs: {"Gomaxprocs", 1005, true, []string{"procs"}}, - EvProcStart: {"ProcStart", 1005, false, []string{"thread"}}, - EvProcStop: {"ProcStop", 1005, false, []string{}}, - EvGCStart: {"GCStart", 1005, true, []string{"seq"}}, // in 1.5 format it was {} - EvGCDone: {"GCDone", 1005, false, []string{}}, - EvGCSTWStart: {"GCSTWStart", 1005, false, []string{"kind"}}, // <= 1.9, args was {} (implicitly {0}) - EvGCSTWDone: {"GCSTWDone", 1005, false, []string{}}, - EvGCSweepStart: {"GCSweepStart", 1005, true, []string{}}, - EvGCSweepDone: {"GCSweepDone", 1005, false, []string{"swept", "reclaimed"}}, // before 1.9, format was {} - EvGoCreate: {"GoCreate", 1005, true, []string{"g", "stack"}}, - EvGoStart: {"GoStart", 1005, false, []string{"g", "seq"}}, // in 1.5 format it was {"g"} - EvGoEnd: {"GoEnd", 1005, false, []string{}}, - EvGoStop: {"GoStop", 1005, true, []string{}}, - EvGoSched: {"GoSched", 1005, true, []string{}}, - EvGoPreempt: {"GoPreempt", 1005, true, []string{}}, - EvGoSleep: {"GoSleep", 1005, true, []string{}}, - EvGoBlock: {"GoBlock", 1005, true, []string{}}, - EvGoUnblock: {"GoUnblock", 1005, true, []string{"g", "seq"}}, // in 1.5 format it was {"g"} - EvGoBlockSend: {"GoBlockSend", 1005, true, []string{}}, - EvGoBlockRecv: {"GoBlockRecv", 1005, true, []string{}}, - EvGoBlockSelect: {"GoBlockSelect", 1005, true, []string{}}, - EvGoBlockSync: {"GoBlockSync", 1005, true, []string{}}, - EvGoBlockCond: {"GoBlockCond", 1005, true, []string{}}, - EvGoBlockNet: {"GoBlockNet", 1005, true, []string{}}, - EvGoSysCall: {"GoSysCall", 1005, true, []string{}}, - EvGoSysExit: {"GoSysExit", 1005, false, []string{"g", "seq", "ts"}}, - EvGoSysBlock: {"GoSysBlock", 1005, false, []string{}}, - EvGoWaiting: {"GoWaiting", 1005, false, []string{"g"}}, - EvGoInSyscall: {"GoInSyscall", 1005, false, []string{"g"}}, - EvHeapAlloc: {"HeapAlloc", 1005, false, []string{"mem"}}, - EvNextGC: {"NextGC", 1005, false, []string{"mem"}}, - EvTimerGoroutine: {"TimerGoroutine", 1005, false, []string{"g"}}, // in 1.5 format it was {"g", "unused"} - EvFutileWakeup: {"FutileWakeup", 1005, false, []string{}}, - EvString: {"String", 1007, false, []string{}}, - EvGoStartLocal: {"GoStartLocal", 1007, false, []string{"g"}}, - EvGoUnblockLocal: {"GoUnblockLocal", 1007, true, []string{"g"}}, - EvGoSysExitLocal: {"GoSysExitLocal", 1007, false, []string{"g", "ts"}}, - EvGoStartLabel: {"GoStartLabel", 1008, false, []string{"g", "seq", "label"}}, - EvGoBlockGC: {"GoBlockGC", 1008, true, []string{}}, - EvGCMarkAssistStart: {"GCMarkAssistStart", 1009, true, []string{}}, - EvGCMarkAssistDone: {"GCMarkAssistDone", 1009, false, []string{}}, + EvNone: {"None", 1005, false, []string{}, nil}, + EvBatch: {"Batch", 1005, false, []string{"p", "ticks"}, nil}, // in 1.5 format it was {"p", "seq", "ticks"} + EvFrequency: {"Frequency", 1005, false, []string{"freq"}, nil}, // in 1.5 format it was {"freq", "unused"} + EvStack: {"Stack", 1005, false, []string{"id", "siz"}, nil}, + EvGomaxprocs: {"Gomaxprocs", 1005, true, []string{"procs"}, nil}, + EvProcStart: {"ProcStart", 1005, false, []string{"thread"}, nil}, + EvProcStop: {"ProcStop", 1005, false, []string{}, nil}, + EvGCStart: {"GCStart", 1005, true, []string{"seq"}, nil}, // in 1.5 format it was {} + EvGCDone: {"GCDone", 1005, false, []string{}, nil}, + EvGCSTWStart: {"GCSTWStart", 1005, false, []string{"kindid"}, []string{"kind"}}, // <= 1.9, args was {} (implicitly {0}) + EvGCSTWDone: {"GCSTWDone", 1005, false, []string{}, nil}, + EvGCSweepStart: {"GCSweepStart", 1005, true, []string{}, nil}, + EvGCSweepDone: {"GCSweepDone", 1005, false, []string{"swept", "reclaimed"}, nil}, // before 1.9, format was {} + EvGoCreate: {"GoCreate", 1005, true, []string{"g", "stack"}, nil}, + EvGoStart: {"GoStart", 1005, false, []string{"g", "seq"}, nil}, // in 1.5 format it was {"g"} + EvGoEnd: {"GoEnd", 1005, false, []string{}, nil}, + EvGoStop: {"GoStop", 1005, true, []string{}, nil}, + EvGoSched: {"GoSched", 1005, true, []string{}, nil}, + EvGoPreempt: {"GoPreempt", 1005, true, []string{}, nil}, + EvGoSleep: {"GoSleep", 1005, true, []string{}, nil}, + EvGoBlock: {"GoBlock", 1005, true, []string{}, nil}, + EvGoUnblock: {"GoUnblock", 1005, true, []string{"g", "seq"}, nil}, // in 1.5 format it was {"g"} + EvGoBlockSend: {"GoBlockSend", 1005, true, []string{}, nil}, + EvGoBlockRecv: {"GoBlockRecv", 1005, true, []string{}, nil}, + EvGoBlockSelect: {"GoBlockSelect", 1005, true, []string{}, nil}, + EvGoBlockSync: {"GoBlockSync", 1005, true, []string{}, nil}, + EvGoBlockCond: {"GoBlockCond", 1005, true, []string{}, nil}, + EvGoBlockNet: {"GoBlockNet", 1005, true, []string{}, nil}, + EvGoSysCall: {"GoSysCall", 1005, true, []string{}, nil}, + EvGoSysExit: {"GoSysExit", 1005, false, []string{"g", "seq", "ts"}, nil}, + EvGoSysBlock: {"GoSysBlock", 1005, false, []string{}, nil}, + EvGoWaiting: {"GoWaiting", 1005, false, []string{"g"}, nil}, + EvGoInSyscall: {"GoInSyscall", 1005, false, []string{"g"}, nil}, + EvHeapAlloc: {"HeapAlloc", 1005, false, []string{"mem"}, nil}, + EvNextGC: {"NextGC", 1005, false, []string{"mem"}, nil}, + EvTimerGoroutine: {"TimerGoroutine", 1005, false, []string{"g"}, nil}, // in 1.5 format it was {"g", "unused"} + EvFutileWakeup: {"FutileWakeup", 1005, false, []string{}, nil}, + EvString: {"String", 1007, false, []string{}, nil}, + EvGoStartLocal: {"GoStartLocal", 1007, false, []string{"g"}, nil}, + EvGoUnblockLocal: {"GoUnblockLocal", 1007, true, []string{"g"}, nil}, + EvGoSysExitLocal: {"GoSysExitLocal", 1007, false, []string{"g", "ts"}, nil}, + EvGoStartLabel: {"GoStartLabel", 1008, false, []string{"g", "seq", "labelid"}, []string{"label"}}, + EvGoBlockGC: {"GoBlockGC", 1008, true, []string{}, nil}, + EvGCMarkAssistStart: {"GCMarkAssistStart", 1009, true, []string{}, nil}, + EvGCMarkAssistDone: {"GCMarkAssistDone", 1009, false, []string{}, nil}, + EvUserTaskCreate: {"UserTaskCreate", 1011, true, []string{"taskid", "pid", "typeid"}, []string{"name"}}, + EvUserTaskEnd: {"UserTaskEnd", 1011, true, []string{"taskid"}, nil}, + EvUserRegion: {"UserRegion", 1011, true, []string{"taskid", "mode", "typeid"}, []string{"name"}}, + EvUserLog: {"UserLog", 1011, true, []string{"id", "keyid"}, []string{"category", "message"}}, } diff --git a/libgo/go/io/io.go b/libgo/go/io/io.go index 27482de..72b7581 100644 --- a/libgo/go/io/io.go +++ b/libgo/go/io/io.go @@ -300,6 +300,7 @@ func WriteString(w Writer, s string) (n int, err error) { // ReadAtLeast returns ErrUnexpectedEOF. // If min is greater than the length of buf, ReadAtLeast returns ErrShortBuffer. // On return, n >= min if and only if err == nil. +// If r returns an error having read at least min bytes, the error is dropped. func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) { if len(buf) < min { return 0, ErrShortBuffer @@ -323,6 +324,7 @@ func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) { // If an EOF happens after reading some but not all the bytes, // ReadFull returns ErrUnexpectedEOF. // On return, n == len(buf) if and only if err == nil. +// If r returns an error having read at least len(buf) bytes, the error is dropped. func ReadFull(r Reader, buf []byte) (n int, err error) { return ReadAtLeast(r, buf, len(buf)) } @@ -385,15 +387,15 @@ func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) { if rt, ok := dst.(ReaderFrom); ok { return rt.ReadFrom(src) } - size := 32 * 1024 - if l, ok := src.(*LimitedReader); ok && int64(size) > l.N { - if l.N < 1 { - size = 1 - } else { - size = int(l.N) - } - } if buf == nil { + size := 32 * 1024 + if l, ok := src.(*LimitedReader); ok && int64(size) > l.N { + if l.N < 1 { + size = 1 + } else { + size = int(l.N) + } + } buf = make([]byte, size) } for { diff --git a/libgo/go/io/ioutil/example_test.go b/libgo/go/io/ioutil/example_test.go index 6edfec1..4cc7a7f 100644 --- a/libgo/go/io/ioutil/example_test.go +++ b/libgo/go/io/ioutil/example_test.go @@ -72,6 +72,24 @@ func ExampleTempFile() { } } +func ExampleTempFile_suffix() { + content := []byte("temporary file's content") + tmpfile, err := ioutil.TempFile("", "example.*.txt") + if err != nil { + log.Fatal(err) + } + + defer os.Remove(tmpfile.Name()) // clean up + + if _, err := tmpfile.Write(content); err != nil { + tmpfile.Close() + log.Fatal(err) + } + if err := tmpfile.Close(); err != nil { + log.Fatal(err) + } +} + func ExampleReadFile() { content, err := ioutil.ReadFile("testdata/hello") if err != nil { diff --git a/libgo/go/io/ioutil/tempfile.go b/libgo/go/io/ioutil/tempfile.go index e5e315c..ba8783b 100644 --- a/libgo/go/io/ioutil/tempfile.go +++ b/libgo/go/io/ioutil/tempfile.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "strconv" + "strings" "sync" "time" ) @@ -23,7 +24,7 @@ func reseed() uint32 { return uint32(time.Now().UnixNano() + int64(os.Getpid())) } -func nextSuffix() string { +func nextRandom() string { randmu.Lock() r := rand if r == 0 { @@ -35,23 +36,32 @@ func nextSuffix() string { return strconv.Itoa(int(1e9 + r%1e9))[1:] } -// TempFile creates a new temporary file in the directory dir -// with a name beginning with prefix, opens the file for reading -// and writing, and returns the resulting *os.File. +// TempFile creates a new temporary file in the directory dir, +// opens the file for reading and writing, and returns the resulting *os.File. +// The filename is generated by taking pattern and adding a random +// string to the end. If pattern includes a "*", the random string +// replaces the last "*". // If dir is the empty string, TempFile uses the default directory // for temporary files (see os.TempDir). // Multiple programs calling TempFile simultaneously // will not choose the same file. The caller can use f.Name() // to find the pathname of the file. It is the caller's responsibility // to remove the file when no longer needed. -func TempFile(dir, prefix string) (f *os.File, err error) { +func TempFile(dir, pattern string) (f *os.File, err error) { if dir == "" { dir = os.TempDir() } + var prefix, suffix string + if pos := strings.LastIndex(pattern, "*"); pos != -1 { + prefix, suffix = pattern[:pos], pattern[pos+1:] + } else { + prefix = pattern + } + nconflict := 0 for i := 0; i < 10000; i++ { - name := filepath.Join(dir, prefix+nextSuffix()) + name := filepath.Join(dir, prefix+nextRandom()+suffix) f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) if os.IsExist(err) { if nconflict++; nconflict > 10 { @@ -80,7 +90,7 @@ func TempDir(dir, prefix string) (name string, err error) { nconflict := 0 for i := 0; i < 10000; i++ { - try := filepath.Join(dir, prefix+nextSuffix()) + try := filepath.Join(dir, prefix+nextRandom()) err = os.Mkdir(try, 0700) if os.IsExist(err) { if nconflict++; nconflict > 10 { diff --git a/libgo/go/io/ioutil/tempfile_test.go b/libgo/go/io/ioutil/tempfile_test.go index 9d54bad..0758890 100644 --- a/libgo/go/io/ioutil/tempfile_test.go +++ b/libgo/go/io/ioutil/tempfile_test.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "regexp" + "strings" "testing" ) @@ -23,18 +24,26 @@ func TestTempFile(t *testing.T) { if f != nil || err == nil { t.Errorf("TempFile(%q, `foo`) = %v, %v", nonexistentDir, f, err) } +} - dir = os.TempDir() - f, err = TempFile(dir, "ioutil_test") - if f == nil || err != nil { - t.Errorf("TempFile(dir, `ioutil_test`) = %v, %v", f, err) +func TestTempFile_pattern(t *testing.T) { + tests := []struct{ pattern, prefix, suffix string }{ + {"ioutil_test", "ioutil_test", ""}, + {"ioutil_test*", "ioutil_test", ""}, + {"ioutil_test*xyz", "ioutil_test", "xyz"}, } - if f != nil { + for _, test := range tests { + f, err := TempFile("", test.pattern) + if err != nil { + t.Errorf("TempFile(..., %q) error: %v", test.pattern, err) + continue + } + defer os.Remove(f.Name()) + base := filepath.Base(f.Name()) f.Close() - os.Remove(f.Name()) - re := regexp.MustCompile("^" + regexp.QuoteMeta(filepath.Join(dir, "ioutil_test")) + "[0-9]+$") - if !re.MatchString(f.Name()) { - t.Errorf("TempFile(`"+dir+"`, `ioutil_test`) created bad name %s", f.Name()) + if !(strings.HasPrefix(base, test.prefix) && strings.HasSuffix(base, test.suffix)) { + t.Errorf("TempFile pattern %q created bad name %q; want prefix %q & suffix %q", + test.pattern, base, test.prefix, test.suffix) } } } diff --git a/libgo/go/log/syslog/syslog_test.go b/libgo/go/log/syslog/syslog_test.go index 1263be6..447654a 100644 --- a/libgo/go/log/syslog/syslog_test.go +++ b/libgo/go/log/syslog/syslog_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !windows,!nacl,!plan9 +// +build !windows,!nacl,!plan9,!js package syslog @@ -214,6 +214,9 @@ func TestNew(t *testing.T) { s, err := New(LOG_INFO|LOG_USER, "the_tag") if err != nil { + if err.Error() == "Unix syslog delivery error" { + t.Skip("skipping: syslogd not running") + } t.Fatalf("New() failed: %s", err) } // Don't send any messages. @@ -226,6 +229,9 @@ func TestNewLogger(t *testing.T) { } f, err := NewLogger(LOG_USER|LOG_INFO, 0) if f == nil { + if err.Error() == "Unix syslog delivery error" { + t.Skip("skipping: syslogd not running") + } t.Error(err) } } @@ -244,6 +250,9 @@ func TestDial(t *testing.T) { } l, err := Dial("", "", LOG_USER|LOG_ERR, "syslog_test") if err != nil { + if err.Error() == "Unix syslog delivery error" { + t.Skip("skipping: syslogd not running") + } t.Fatalf("Dial() failed: %s", err) } l.Close() diff --git a/libgo/go/math/all_test.go b/libgo/go/math/all_test.go index 98437b0..00f2058 100644 --- a/libgo/go/math/all_test.go +++ b/libgo/go/math/all_test.go @@ -25,7 +25,7 @@ var vf = []float64{ } // The expected results below were computed by the high precision calculators -// at http://keisan.casio.com/. More exact input values (array vf[], above) +// at https://keisan.casio.com/. More exact input values (array vf[], above) // were obtained by printing them with "%.26f". The answers were calculated // to 26 digits (by using the "Digit number" drop-down control of each // calculator). @@ -946,6 +946,8 @@ var vferfSC = []float64{ 0, Inf(1), NaN(), + -1000, + 1000, } var erfSC = []float64{ -1, @@ -953,17 +955,23 @@ var erfSC = []float64{ 0, 1, NaN(), + -1, + 1, } var vferfcSC = []float64{ Inf(-1), Inf(1), NaN(), + -1000, + 1000, } var erfcSC = []float64{ 2, 0, NaN(), + 2, + 0, } var vferfinvSC = []float64{ @@ -1012,6 +1020,10 @@ var vfexpSC = []float64{ 1.48852223e+09, 1.4885222e+09, 1, + // near zero + 3.725290298461915e-09, + // denormal + -740, } var expSC = []float64{ 0, @@ -1023,6 +1035,8 @@ var expSC = []float64{ Inf(1), Inf(1), 2.718281828459045, + 1.0000000037252903, + 4.2e-322, } var vfexp2SC = []float64{ @@ -1033,6 +1047,10 @@ var vfexp2SC = []float64{ NaN(), // smallest float64 that overflows Exp2(x) 1024, + // near underflow + -1.07399999999999e+03, + // near zero + 3.725290298461915e-09, } var exp2SC = []float64{ 0, @@ -1041,6 +1059,8 @@ var exp2SC = []float64{ Inf(1), NaN(), Inf(1), + 5e-324, + 1.0000000025821745, } var vfexpm1SC = []float64{ @@ -1955,6 +1975,8 @@ var vfldexpBC = []fi{ {-1, -1075}, {1, 1024}, {-1, 1024}, + {1.0000000000000002, -1075}, + {1, -1075}, } var ldexpBC = []float64{ SmallestNonzeroFloat64, @@ -1965,6 +1987,8 @@ var ldexpBC = []float64{ Copysign(0, -1), Inf(1), Inf(-1), + SmallestNonzeroFloat64, + 0, } var logbBC = []float64{ @@ -2316,7 +2340,7 @@ func testExp2(t *testing.T, Exp2 func(float64) float64, name string) { } for i := 0; i < len(vfexp2SC); i++ { if f := Exp2(vfexp2SC[i]); !alike(exp2SC[i], f) { - t.Errorf("%s(%g) = %g, want %g", name, vfexpSC[i], f, expSC[i]) + t.Errorf("%s(%g) = %g, want %g", name, vfexp2SC[i], f, exp2SC[i]) } } for n := -1074; n < 1024; n++ { @@ -2419,6 +2443,10 @@ func TestMod(t *testing.T) { t.Errorf("Mod(%g, %g) = %g, want %g", vffmodSC[i][0], vffmodSC[i][1], f, fmodSC[i]) } } + // verify precision of result for extreme inputs + if f := Mod(5.9790119248836734e+200, 1.1258465975523544); 0.6447968302508578 != f { + t.Errorf("Remainder(5.9790119248836734e+200, 1.1258465975523544) = %g, want 0.6447968302508578", f) + } } func TestFrexp(t *testing.T) { @@ -2760,6 +2788,10 @@ func TestRemainder(t *testing.T) { t.Errorf("Remainder(%g, %g) = %g, want %g", vffmodSC[i][0], vffmodSC[i][1], f, fmodSC[i]) } } + // verify precision of result for extreme inputs + if f := Remainder(5.9790119248836734e+200, 1.1258465975523544); -0.4810497673014966 != f { + t.Errorf("Remainder(5.9790119248836734e+200, 1.1258465975523544) = %g, want -0.4810497673014966", f) + } } func TestRound(t *testing.T) { diff --git a/libgo/go/math/big/accuracy_string.go b/libgo/go/math/big/accuracy_string.go index 24ef7f1..1501ace 100644 --- a/libgo/go/math/big/accuracy_string.go +++ b/libgo/go/math/big/accuracy_string.go @@ -1,8 +1,8 @@ -// generated by stringer -type=Accuracy; DO NOT EDIT +// Code generated by "stringer -type=Accuracy"; DO NOT EDIT. package big -import "fmt" +import "strconv" const _Accuracy_name = "BelowExactAbove" @@ -10,8 +10,8 @@ var _Accuracy_index = [...]uint8{0, 5, 10, 15} func (i Accuracy) String() string { i -= -1 - if i < 0 || i+1 >= Accuracy(len(_Accuracy_index)) { - return fmt.Sprintf("Accuracy(%d)", i+-1) + if i < 0 || i >= Accuracy(len(_Accuracy_index)-1) { + return "Accuracy(" + strconv.FormatInt(int64(i+-1), 10) + ")" } return _Accuracy_name[_Accuracy_index[i]:_Accuracy_index[i+1]] } diff --git a/libgo/go/math/big/arith_amd64.go b/libgo/go/math/big/arith_amd64.go new file mode 100644 index 0000000..35164f3 --- /dev/null +++ b/libgo/go/math/big/arith_amd64.go @@ -0,0 +1,12 @@ +// Copyright 2017 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 ignore_for_gccgo +// +build !math_big_pure_go + +package big + +import "internal/cpu" + +var support_adx = cpu.X86.HasADX && cpu.X86.HasBMI2 diff --git a/libgo/go/math/big/arith_test.go b/libgo/go/math/big/arith_test.go index 13b0436..cf386b3 100644 --- a/libgo/go/math/big/arith_test.go +++ b/libgo/go/math/big/arith_test.go @@ -142,6 +142,23 @@ func BenchmarkAddVV(b *testing.B) { } } +func BenchmarkSubVV(b *testing.B) { + for _, n := range benchSizes { + if isRaceBuilder && n > 1e3 { + continue + } + x := rndV(n) + y := rndV(n) + z := make([]Word, n) + b.Run(fmt.Sprint(n), func(b *testing.B) { + b.SetBytes(int64(n * _W)) + for i := 0; i < b.N; i++ { + subVV(z, x, y) + } + }) + } +} + type funVW func(z, x []Word, y Word) (c Word) type argVW struct { z, x nat @@ -255,6 +272,23 @@ func BenchmarkAddVW(b *testing.B) { } } +func BenchmarkSubVW(b *testing.B) { + for _, n := range benchSizes { + if isRaceBuilder && n > 1e3 { + continue + } + x := rndV(n) + y := rndW() + z := make([]Word, n) + b.Run(fmt.Sprint(n), func(b *testing.B) { + b.SetBytes(int64(n * _S)) + for i := 0; i < b.N; i++ { + subVW(z, x, y) + } + }) + } +} + type funVWW func(z, x []Word, y, r Word) (c Word) type argVWW struct { z, x nat diff --git a/libgo/go/math/big/calibrate_test.go b/libgo/go/math/big/calibrate_test.go index 2b96e74..4fa663f 100644 --- a/libgo/go/math/big/calibrate_test.go +++ b/libgo/go/math/big/calibrate_test.go @@ -28,24 +28,32 @@ import ( var calibrate = flag.Bool("calibrate", false, "run calibration test") +const ( + sqrModeMul = "mul(x, x)" + sqrModeBasic = "basicSqr(x)" + sqrModeKaratsuba = "karatsubaSqr(x)" +) + func TestCalibrate(t *testing.T) { - if *calibrate { - computeKaratsubaThresholds() - - // compute basicSqrThreshold where overhead becomes negligible - minSqr := computeSqrThreshold(10, 30, 1, 3) - // compute karatsubaSqrThreshold where karatsuba is faster - maxSqr := computeSqrThreshold(300, 500, 10, 3) - if minSqr != 0 { - fmt.Printf("found basicSqrThreshold = %d\n", minSqr) - } else { - fmt.Println("no basicSqrThreshold found") - } - if maxSqr != 0 { - fmt.Printf("found karatsubaSqrThreshold = %d\n", maxSqr) - } else { - fmt.Println("no karatsubaSqrThreshold found") - } + if !*calibrate { + return + } + + computeKaratsubaThresholds() + + // compute basicSqrThreshold where overhead becomes negligible + minSqr := computeSqrThreshold(10, 30, 1, 3, sqrModeMul, sqrModeBasic) + // compute karatsubaSqrThreshold where karatsuba is faster + maxSqr := computeSqrThreshold(200, 500, 10, 3, sqrModeBasic, sqrModeKaratsuba) + if minSqr != 0 { + fmt.Printf("found basicSqrThreshold = %d\n", minSqr) + } else { + fmt.Println("no basicSqrThreshold found") + } + if maxSqr != 0 { + fmt.Printf("found karatsubaSqrThreshold = %d\n", maxSqr) + } else { + fmt.Println("no karatsubaSqrThreshold found") } } @@ -109,16 +117,17 @@ func computeKaratsubaThresholds() { } } -func measureBasicSqr(words, nruns int, enable bool) time.Duration { +func measureSqr(words, nruns int, mode string) time.Duration { // more runs for better statistics initBasicSqr, initKaratsubaSqr := basicSqrThreshold, karatsubaSqrThreshold - if enable { - // set thresholds to use basicSqr at this number of words + switch mode { + case sqrModeMul: + basicSqrThreshold = words + 1 + case sqrModeBasic: basicSqrThreshold, karatsubaSqrThreshold = words-1, words+1 - } else { - // set thresholds to disable basicSqr for any number of words - basicSqrThreshold, karatsubaSqrThreshold = -1, -1 + case sqrModeKaratsuba: + karatsubaSqrThreshold = words - 1 } var testval int64 @@ -133,18 +142,18 @@ func measureBasicSqr(words, nruns int, enable bool) time.Duration { return time.Duration(testval) } -func computeSqrThreshold(from, to, step, nruns int) int { - fmt.Println("Calibrating thresholds for basicSqr via benchmarks of z.mul(x,x)") +func computeSqrThreshold(from, to, step, nruns int, lower, upper string) int { + fmt.Printf("Calibrating threshold between %s and %s\n", lower, upper) fmt.Printf("Looking for a timing difference for x between %d - %d words by %d step\n", from, to, step) var initPos bool var threshold int for i := from; i <= to; i += step { - baseline := measureBasicSqr(i, nruns, false) - testval := measureBasicSqr(i, nruns, true) + baseline := measureSqr(i, nruns, lower) + testval := measureSqr(i, nruns, upper) pos := baseline > testval delta := baseline - testval percent := delta * 100 / baseline - fmt.Printf("words = %3d deltaT = %10s (%4d%%) is basicSqr better: %v", i, delta, percent, pos) + fmt.Printf("words = %3d deltaT = %10s (%4d%%) is %s better: %v", i, delta, percent, upper, pos) if i == from { initPos = pos } diff --git a/libgo/go/math/big/float.go b/libgo/go/math/big/float.go index c042854..55b93c8 100644 --- a/libgo/go/math/big/float.go +++ b/libgo/go/math/big/float.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // This file implements multi-precision floating-point numbers. -// Like in the GNU MPFR library (http://www.mpfr.org/), operands +// Like in the GNU MPFR library (https://www.mpfr.org/), operands // can be of mixed precision. Unlike MPFR, the rounding mode is // not specified with each operation, but with each operand. The // rounding mode of the result operand determines the rounding @@ -1429,8 +1429,6 @@ func (x *Float) ucmp(y *Float) int { // z's accuracy reports the result error relative to the exact (not rounded) // result. Add panics with ErrNaN if x and y are infinities with opposite // signs. The value of z is undefined in that case. -// -// BUG(gri) When rounding ToNegativeInf, the sign of Float values rounded to 0 is incorrect. func (z *Float) Add(x, y *Float) *Float { if debugFloat { x.validate() @@ -1466,6 +1464,9 @@ func (z *Float) Add(x, y *Float) *Float { z.usub(y, x) } } + if z.form == zero && z.mode == ToNegativeInf && z.acc == Exact { + z.neg = true + } return z } @@ -1530,6 +1531,9 @@ func (z *Float) Sub(x, y *Float) *Float { z.usub(y, x) } } + if z.form == zero && z.mode == ToNegativeInf && z.acc == Exact { + z.neg = true + } return z } diff --git a/libgo/go/math/big/float_test.go b/libgo/go/math/big/float_test.go index 5fd49bb..7d6bf03 100644 --- a/libgo/go/math/big/float_test.go +++ b/libgo/go/math/big/float_test.go @@ -1007,9 +1007,9 @@ func TestFloatFloat64(t *testing.T) { {"0x.fffffffffffffp-1022", smallestNormalFloat64 - math.SmallestNonzeroFloat64, Exact}, {"4503599627370495p-1074", smallestNormalFloat64 - math.SmallestNonzeroFloat64, Exact}, - // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ + // https://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ {"2.2250738585072011e-308", 2.225073858507201e-308, Below}, - // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ + // https://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ {"2.2250738585072012e-308", 2.2250738585072014e-308, Above}, } { for i := 0; i < 2; i++ { @@ -1258,6 +1258,31 @@ func TestFloatAdd(t *testing.T) { } } +// TestFloatAddRoundZero tests Float.Add/Sub rounding when the result is exactly zero. +// x + (-x) or x - x for non-zero x should be +0 in all cases except when +// the rounding mode is ToNegativeInf in which case it should be -0. +func TestFloatAddRoundZero(t *testing.T) { + for _, mode := range [...]RoundingMode{ToNearestEven, ToNearestAway, ToZero, AwayFromZero, ToPositiveInf, ToNegativeInf} { + x := NewFloat(5.0) + y := new(Float).Neg(x) + want := NewFloat(0.0) + if mode == ToNegativeInf { + want.Neg(want) + } + got := new(Float).SetMode(mode) + got.Add(x, y) + if got.Cmp(want) != 0 || got.neg != (mode == ToNegativeInf) { + t.Errorf("%s:\n\t %v\n\t+ %v\n\t= %v\n\twant %v", + mode, x, y, got, want) + } + got.Sub(x, x) + if got.Cmp(want) != 0 || got.neg != (mode == ToNegativeInf) { + t.Errorf("%v:\n\t %v\n\t- %v\n\t= %v\n\twant %v", + mode, x, x, got, want) + } + } +} + // TestFloatAdd32 tests that Float.Add/Sub of numbers with // 24bit mantissa behaves like float32 addition/subtraction // (excluding denormal numbers). @@ -1372,7 +1397,7 @@ func TestFloatMul(t *testing.T) { got.Mul(x, y) want := zbits.round(prec, mode) if got.Cmp(want) != 0 { - t.Errorf("i = %d, prec = %d, %s:\n\t %s %v\n\t* %s %v\n\t= %s\n\twant %s", + t.Errorf("i = %d, prec = %d, %s:\n\t %v %v\n\t* %v %v\n\t= %v\n\twant %v", i, prec, mode, x, xbits, y, ybits, got, want) } @@ -1382,7 +1407,7 @@ func TestFloatMul(t *testing.T) { got.Quo(z, x) want = ybits.round(prec, mode) if got.Cmp(want) != 0 { - t.Errorf("i = %d, prec = %d, %s:\n\t %s %v\n\t/ %s %v\n\t= %s\n\twant %s", + t.Errorf("i = %d, prec = %d, %s:\n\t %v %v\n\t/ %v %v\n\t= %v\n\twant %v", i, prec, mode, z, zbits, x, xbits, got, want) } } @@ -1465,13 +1490,13 @@ func TestIssue6866(t *testing.T) { z2.Sub(two, p) if z1.Cmp(z2) != 0 { - t.Fatalf("prec %d: got z1 = %s != z2 = %s; want z1 == z2\n", prec, z1, z2) + t.Fatalf("prec %d: got z1 = %v != z2 = %v; want z1 == z2\n", prec, z1, z2) } if z1.Sign() != 0 { - t.Errorf("prec %d: got z1 = %s; want 0", prec, z1) + t.Errorf("prec %d: got z1 = %v; want 0", prec, z1) } if z2.Sign() != 0 { - t.Errorf("prec %d: got z2 = %s; want 0", prec, z2) + t.Errorf("prec %d: got z2 = %v; want 0", prec, z2) } } } diff --git a/libgo/go/math/big/floatconv_test.go b/libgo/go/math/big/floatconv_test.go index 6d0f17d..269e265 100644 --- a/libgo/go/math/big/floatconv_test.go +++ b/libgo/go/math/big/floatconv_test.go @@ -286,9 +286,9 @@ func TestFloat64Text(t *testing.T) { {0.5, 'f', 0, "0"}, {1.5, 'f', 0, "2"}, - // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ + // https://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ {2.2250738585072012e-308, 'g', -1, "2.2250738585072014e-308"}, - // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ + // https://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ {2.2250738585072011e-308, 'g', -1, "2.225073858507201e-308"}, // Issue 2625. diff --git a/libgo/go/math/big/int.go b/libgo/go/math/big/int.go index 0eda9cd..47a288a 100644 --- a/libgo/go/math/big/int.go +++ b/libgo/go/math/big/int.go @@ -442,24 +442,28 @@ func (x *Int) BitLen() int { } // Exp sets z = x**y mod |m| (i.e. the sign of m is ignored), and returns z. -// If y <= 0, the result is 1 mod |m|; if m == nil or m == 0, z = x**y. +// If m == nil or m == 0, z = x**y unless y <= 0 then z = 1. // // Modular exponentation of inputs of a particular size is not a // cryptographically constant-time operation. func (z *Int) Exp(x, y, m *Int) *Int { // See Knuth, volume 2, section 4.6.3. - var yWords nat - if !y.neg { - yWords = y.abs + xWords := x.abs + if y.neg { + if m == nil || len(m.abs) == 0 { + return z.SetInt64(1) + } + // for y < 0: x**y mod m == (x**(-1))**|y| mod m + xWords = new(Int).ModInverse(x, m).abs } - // y >= 0 + yWords := y.abs var mWords nat if m != nil { mWords = m.abs // m.abs may be nil for m == 0 } - z.abs = z.abs.expNN(x.abs, yWords, mWords) + z.abs = z.abs.expNN(xWords, yWords, mWords) z.neg = len(z.abs) > 0 && x.neg && len(yWords) > 0 && yWords[0]&1 == 1 // 0 has no sign if z.neg && len(mWords) > 0 { // make modulus result positive @@ -485,162 +489,223 @@ func (z *Int) GCD(x, y, a, b *Int) *Int { } return z } - if x == nil && y == nil { - return z.lehmerGCD(a, b) - } - A := new(Int).Set(a) - B := new(Int).Set(b) - - X := new(Int) - lastX := new(Int).SetInt64(1) + return z.lehmerGCD(x, y, a, b) +} - q := new(Int) - temp := new(Int) +// lehmerSimulate attempts to simulate several Euclidean update steps +// using the leading digits of A and B. It returns u0, u1, v0, v1 +// such that A and B can be updated as: +// A = u0*A + v0*B +// B = u1*A + v1*B +// Requirements: A >= B and len(B.abs) >= 2 +// Since we are calculating with full words to avoid overflow, +// we use 'even' to track the sign of the cosequences. +// For even iterations: u0, v1 >= 0 && u1, v0 <= 0 +// For odd iterations: u0, v1 <= 0 && u1, v0 >= 0 +func lehmerSimulate(A, B *Int) (u0, u1, v0, v1 Word, even bool) { + // initialize the digits + var a1, a2, u2, v2 Word + + m := len(B.abs) // m >= 2 + n := len(A.abs) // n >= m >= 2 + + // extract the top Word of bits from A and B + h := nlz(A.abs[n-1]) + a1 = A.abs[n-1]<<h | A.abs[n-2]>>(_W-h) + // B may have implicit zero words in the high bits if the lengths differ + switch { + case n == m: + a2 = B.abs[n-1]<<h | B.abs[n-2]>>(_W-h) + case n == m+1: + a2 = B.abs[n-2] >> (_W - h) + default: + a2 = 0 + } - r := new(Int) - for len(B.abs) > 0 { - q, r = q.QuoRem(A, B, r) + // Since we are calculating with full words to avoid overflow, + // we use 'even' to track the sign of the cosequences. + // For even iterations: u0, v1 >= 0 && u1, v0 <= 0 + // For odd iterations: u0, v1 <= 0 && u1, v0 >= 0 + // The first iteration starts with k=1 (odd). + even = false + // variables to track the cosequences + u0, u1, u2 = 0, 1, 0 + v0, v1, v2 = 0, 0, 1 + + // Calculate the quotient and cosequences using Collins' stopping condition. + // Note that overflow of a Word is not possible when computing the remainder + // sequence and cosequences since the cosequence size is bounded by the input size. + // See section 4.2 of Jebelean for details. + for a2 >= v2 && a1-a2 >= v1+v2 { + q, r := a1/a2, a1%a2 + a1, a2 = a2, r + u0, u1, u2 = u1, u2, u1+q*u2 + v0, v1, v2 = v1, v2, v1+q*v2 + even = !even + } + return +} - A, B, r = B, r, A +// lehmerUpdate updates the inputs A and B such that: +// A = u0*A + v0*B +// B = u1*A + v1*B +// where the signs of u0, u1, v0, v1 are given by even +// For even == true: u0, v1 >= 0 && u1, v0 <= 0 +// For even == false: u0, v1 <= 0 && u1, v0 >= 0 +// q, r, s, t are temporary variables to avoid allocations in the multiplication +func lehmerUpdate(A, B, q, r, s, t *Int, u0, u1, v0, v1 Word, even bool) { + + t.abs = t.abs.setWord(u0) + s.abs = s.abs.setWord(v0) + t.neg = !even + s.neg = even + + t.Mul(A, t) + s.Mul(B, s) + + r.abs = r.abs.setWord(u1) + q.abs = q.abs.setWord(v1) + r.neg = even + q.neg = !even + + r.Mul(A, r) + q.Mul(B, q) + + A.Add(t, s) + B.Add(r, q) +} - temp.Set(X) - X.Mul(X, q) - X.Sub(lastX, X) - lastX.Set(temp) - } +// euclidUpdate performs a single step of the Euclidean GCD algorithm +// if extended is true, it also updates the cosequence Ua, Ub +func euclidUpdate(A, B, Ua, Ub, q, r, s, t *Int, extended bool) { + q, r = q.QuoRem(A, B, r) - if x != nil { - *x = *lastX - } + *A, *B, *r = *B, *r, *A - if y != nil { - // y = (z - a*x)/b - y.Mul(a, lastX) - y.Sub(A, y) - y.Div(y, b) + if extended { + // Ua, Ub = Ub, Ua - q*Ub + t.Set(Ub) + s.Mul(Ub, q) + Ub.Sub(Ua, s) + Ua.Set(t) } - - *z = *A - return z } // lehmerGCD sets z to the greatest common divisor of a and b, // which both must be > 0, and returns z. +// If x or y are not nil, their values are set such that z = a*x + b*y. // See Knuth, The Art of Computer Programming, Vol. 2, Section 4.5.2, Algorithm L. // This implementation uses the improved condition by Collins requiring only one // quotient and avoiding the possibility of single Word overflow. // See Jebelean, "Improving the multiprecision Euclidean algorithm", // Design and Implementation of Symbolic Computation Systems, pp 45-58. -func (z *Int) lehmerGCD(a, b *Int) *Int { - // ensure a >= b - if a.abs.cmp(b.abs) < 0 { - a, b = b, a - } +// The cosequences are updated according to Algorithm 10.45 from +// Cohen et al. "Handbook of Elliptic and Hyperelliptic Curve Cryptography" pp 192. +func (z *Int) lehmerGCD(x, y, a, b *Int) *Int { + var A, B, Ua, Ub *Int - // don't destroy incoming values of a and b - B := new(Int).Set(b) // must be set first in case b is an alias of z - A := z.Set(a) + A = new(Int).Set(a) + B = new(Int).Set(b) + + extended := x != nil || y != nil + + if extended { + // Ua (Ub) tracks how many times input a has been accumulated into A (B). + Ua = new(Int).SetInt64(1) + Ub = new(Int) + } // temp variables for multiprecision update - t := new(Int) + q := new(Int) r := new(Int) s := new(Int) - w := new(Int) + t := new(Int) + + // ensure A >= B + if A.abs.cmp(B.abs) < 0 { + A, B = B, A + Ub, Ua = Ua, Ub + } // loop invariant A >= B for len(B.abs) > 1 { - // initialize the digits - var a1, a2, u0, u1, u2, v0, v1, v2 Word - - m := len(B.abs) // m >= 2 - n := len(A.abs) // n >= m >= 2 - - // extract the top Word of bits from A and B - h := nlz(A.abs[n-1]) - a1 = (A.abs[n-1] << h) | (A.abs[n-2] >> (_W - h)) - // B may have implicit zero words in the high bits if the lengths differ - switch { - case n == m: - a2 = (B.abs[n-1] << h) | (B.abs[n-2] >> (_W - h)) - case n == m+1: - a2 = (B.abs[n-2] >> (_W - h)) - default: - a2 = 0 - } - - // Since we are calculating with full words to avoid overflow, - // we use 'even' to track the sign of the cosequences. - // For even iterations: u0, v1 >= 0 && u1, v0 <= 0 - // For odd iterations: u0, v1 <= 0 && u1, v0 >= 0 - // The first iteration starts with k=1 (odd). - even := false - // variables to track the cosequences - u0, u1, u2 = 0, 1, 0 - v0, v1, v2 = 0, 0, 1 - - // Calculate the quotient and cosequences using Collins' stopping condition. - // Note that overflow of a Word is not possible when computing the remainder - // sequence and cosequences since the cosequence size is bounded by the input size. - // See section 4.2 of Jebelean for details. - for a2 >= v2 && a1-a2 >= v1+v2 { - q := a1 / a2 - a1, a2 = a2, a1-q*a2 - u0, u1, u2 = u1, u2, u1+q*u2 - v0, v1, v2 = v1, v2, v1+q*v2 - even = !even - } + // Attempt to calculate in single-precision using leading words of A and B. + u0, u1, v0, v1, even := lehmerSimulate(A, B) - // multiprecision step + // multiprecision Step if v0 != 0 { - // simulate the effect of the single precision steps using the cosequences + // Simulate the effect of the single-precision steps using the cosequences. // A = u0*A + v0*B // B = u1*A + v1*B + lehmerUpdate(A, B, q, r, s, t, u0, u1, v0, v1, even) - t.abs = t.abs.setWord(u0) - s.abs = s.abs.setWord(v0) - t.neg = !even - s.neg = even - - t.Mul(A, t) - s.Mul(B, s) - - r.abs = r.abs.setWord(u1) - w.abs = w.abs.setWord(v1) - r.neg = even - w.neg = !even - - r.Mul(A, r) - w.Mul(B, w) - - A.Add(t, s) - B.Add(r, w) + if extended { + // Ua = u0*Ua + v0*Ub + // Ub = u1*Ua + v1*Ub + lehmerUpdate(Ua, Ub, q, r, s, t, u0, u1, v0, v1, even) + } } else { - // single-digit calculations failed to simluate any quotients - // do a standard Euclidean step - t.Rem(A, B) - A, B, t = B, t, A + // Single-digit calculations failed to simulate any quotients. + // Do a standard Euclidean step. + euclidUpdate(A, B, Ua, Ub, q, r, s, t, extended) } } if len(B.abs) > 0 { - // standard Euclidean algorithm base case for B a single Word + // extended Euclidean algorithm base case if B is a single Word if len(A.abs) > 1 { - // A is longer than a single Word - t.Rem(A, B) - A, B, t = B, t, A + // A is longer than a single Word, so one update is needed. + euclidUpdate(A, B, Ua, Ub, q, r, s, t, extended) } if len(B.abs) > 0 { - // A and B are both a single Word - a1, a2 := A.abs[0], B.abs[0] - for a2 != 0 { - a1, a2 = a2, a1%a2 + // A and B are both a single Word. + aWord, bWord := A.abs[0], B.abs[0] + if extended { + var ua, ub, va, vb Word + ua, ub = 1, 0 + va, vb = 0, 1 + even := true + for bWord != 0 { + q, r := aWord/bWord, aWord%bWord + aWord, bWord = bWord, r + ua, ub = ub, ua+q*ub + va, vb = vb, va+q*vb + even = !even + } + + t.abs = t.abs.setWord(ua) + s.abs = s.abs.setWord(va) + t.neg = !even + s.neg = even + + t.Mul(Ua, t) + s.Mul(Ub, s) + + Ua.Add(t, s) + } else { + for bWord != 0 { + aWord, bWord = bWord, aWord%bWord + } } - A.abs[0] = a1 + A.abs[0] = aWord } } + + if x != nil { + *x = *Ua + } + + if y != nil { + // y = (z - a*x)/b + y.Mul(a, Ua) + y.Sub(A, y) + y.Div(y, b) + } + *z = *A + return z } @@ -659,20 +724,33 @@ func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int { } // ModInverse sets z to the multiplicative inverse of g in the ring ℤ/nℤ -// and returns z. If g and n are not relatively prime, the result is undefined. +// and returns z. If g and n are not relatively prime, g has no multiplicative +// inverse in the ring ℤ/nℤ. In this case, z is unchanged and the return value +// is nil. func (z *Int) ModInverse(g, n *Int) *Int { + // GCD expects parameters a and b to be > 0. + if n.neg { + var n2 Int + n = n2.Neg(n) + } if g.neg { - // GCD expects parameters a and b to be > 0. var g2 Int g = g2.Mod(g, n) } - var d Int - d.GCD(z, nil, g, n) - // x and y are such that g*x + n*y = d. Since g and n are - // relatively prime, d = 1. Taking that modulo n results in - // g*x = 1, therefore x is the inverse element. - if z.neg { - z.Add(z, n) + var d, x Int + d.GCD(&x, nil, g, n) + + // if and only if d==1, g and n are relatively prime + if d.Cmp(intOne) != 0 { + return nil + } + + // x and y are such that g*x + n*y = 1, therefore x is the inverse element, + // but it may be negative, so convert to the range 0 <= z < |n| + if x.neg { + z.Add(&x, n) + } else { + z.Set(&x) } return z } @@ -745,6 +823,30 @@ func (z *Int) modSqrt3Mod4Prime(x, p *Int) *Int { return z } +// modSqrt5Mod8 uses Atkin's observation that 2 is not a square mod p +// alpha == (2*a)^((p-5)/8) mod p +// beta == 2*a*alpha^2 mod p is a square root of -1 +// b == a*alpha*(beta-1) mod p is a square root of a +// to calculate the square root of any quadratic residue mod p quickly for 5 +// mod 8 primes. +func (z *Int) modSqrt5Mod8Prime(x, p *Int) *Int { + // p == 5 mod 8 implies p = e*8 + 5 + // e is the quotient and 5 the remainder on division by 8 + e := new(Int).Rsh(p, 3) // e = (p - 5) / 8 + tx := new(Int).Lsh(x, 1) // tx = 2*x + alpha := new(Int).Exp(tx, e, p) + beta := new(Int).Mul(alpha, alpha) + beta.Mod(beta, p) + beta.Mul(beta, tx) + beta.Mod(beta, p) + beta.Sub(beta, intOne) + beta.Mul(beta, x) + beta.Mod(beta, p) + beta.Mul(beta, alpha) + z.Mod(beta, p) + return z +} + // modSqrtTonelliShanks uses the Tonelli-Shanks algorithm to find the square // root of a quadratic residue modulo any prime. func (z *Int) modSqrtTonelliShanks(x, p *Int) *Int { @@ -811,12 +913,17 @@ func (z *Int) ModSqrt(x, p *Int) *Int { x = new(Int).Mod(x, p) } - // Check whether p is 3 mod 4, and if so, use the faster algorithm. - if len(p.abs) > 0 && p.abs[0]%4 == 3 { + switch { + case p.abs[0]%4 == 3: + // Check whether p is 3 mod 4, and if so, use the faster algorithm. return z.modSqrt3Mod4Prime(x, p) + case p.abs[0]%8 == 5: + // Check whether p is 5 mod 8, use Atkin's algorithm. + return z.modSqrt5Mod8Prime(x, p) + default: + // Otherwise, use Tonelli-Shanks. + return z.modSqrtTonelliShanks(x, p) } - // Otherwise, use Tonelli-Shanks. - return z.modSqrtTonelliShanks(x, p) } // Lsh sets z = x << n and returns z. diff --git a/libgo/go/math/big/int_test.go b/libgo/go/math/big/int_test.go index 270fec6..9930ed0 100644 --- a/libgo/go/math/big/int_test.go +++ b/libgo/go/math/big/int_test.go @@ -557,7 +557,7 @@ var expTests = []struct { {"0x8000000000000000", "3", "6719", "5447"}, {"0x8000000000000000", "1000", "6719", "1603"}, {"0x8000000000000000", "1000000", "6719", "3199"}, - {"0x8000000000000000", "-1000000", "6719", "1"}, + {"0x8000000000000000", "-1000000", "6719", "3663"}, // 3663 = ModInverse(3199, 6719) Issue #25865 {"0xffffffffffffffffffffffffffffffff", "0x12345678123456781234567812345678123456789", "0x01112222333344445555666677778889", "0x36168FA1DB3AAE6C8CE647E137F97A"}, @@ -675,21 +675,43 @@ func checkGcd(aBytes, bBytes []byte) bool { return x.Cmp(d) == 0 } -// euclidGCD is a reference implementation of Euclid's GCD -// algorithm for testing against optimized algorithms. +// euclidExtGCD is a reference implementation of Euclid's +// extended GCD algorithm for testing against optimized algorithms. // Requirements: a, b > 0 -func euclidGCD(a, b *Int) *Int { - +func euclidExtGCD(a, b *Int) (g, x, y *Int) { A := new(Int).Set(a) B := new(Int).Set(b) - t := new(Int) + // A = Ua*a + Va*b + // B = Ub*a + Vb*b + Ua := new(Int).SetInt64(1) + Va := new(Int) + + Ub := new(Int) + Vb := new(Int).SetInt64(1) + + q := new(Int) + temp := new(Int) + + r := new(Int) for len(B.abs) > 0 { - // A, B = B, A % B - t.Rem(A, B) - A, B, t = B, t, A + q, r = q.QuoRem(A, B, r) + + A, B, r = B, r, A + + // Ua, Ub = Ub, Ua-q*Ub + temp.Set(Ub) + Ub.Mul(Ub, q) + Ub.Sub(Ua, Ub) + Ua.Set(temp) + + // Va, Vb = Vb, Va-q*Vb + temp.Set(Vb) + Vb.Mul(Vb, q) + Vb.Sub(Va, Vb) + Va.Set(temp) } - return A + return A, Ua, Va } func checkLehmerGcd(aBytes, bBytes []byte) bool { @@ -700,12 +722,28 @@ func checkLehmerGcd(aBytes, bBytes []byte) bool { return true // can only test positive arguments } - d := new(Int).lehmerGCD(a, b) - d0 := euclidGCD(a, b) + d := new(Int).lehmerGCD(nil, nil, a, b) + d0, _, _ := euclidExtGCD(a, b) return d.Cmp(d0) == 0 } +func checkLehmerExtGcd(aBytes, bBytes []byte) bool { + a := new(Int).SetBytes(aBytes) + b := new(Int).SetBytes(bBytes) + x := new(Int) + y := new(Int) + + if a.Sign() <= 0 || b.Sign() <= 0 { + return true // can only test positive arguments + } + + d := new(Int).lehmerGCD(x, y, a, b) + d0, x0, y0 := euclidExtGCD(a, b) + + return d.Cmp(d0) == 0 && x.Cmp(x0) == 0 && y.Cmp(y0) == 0 +} + var gcdTests = []struct { d, x, y, a, b string }{ @@ -785,6 +823,10 @@ func TestGcd(t *testing.T) { if err := quick.Check(checkLehmerGcd, nil); err != nil { t.Error(err) } + + if err := quick.Check(checkLehmerExtGcd, nil); err != nil { + t.Error(err) + } } type intShiftTest struct { @@ -1318,7 +1360,7 @@ func BenchmarkModSqrt225_Tonelli(b *testing.B) { } } -func BenchmarkModSqrt224_3Mod4(b *testing.B) { +func BenchmarkModSqrt225_3Mod4(b *testing.B) { p := tri(225) x := new(Int).SetUint64(2) for i := 0; i < b.N; i++ { @@ -1327,27 +1369,25 @@ func BenchmarkModSqrt224_3Mod4(b *testing.B) { } } -func BenchmarkModSqrt5430_Tonelli(b *testing.B) { - if isRaceBuilder { - b.Skip("skipping on race builder") - } - p := tri(5430) - x := new(Int).SetUint64(2) +func BenchmarkModSqrt231_Tonelli(b *testing.B) { + p := tri(231) + p.Sub(p, intOne) + p.Sub(p, intOne) // tri(231) - 2 is a prime == 5 mod 8 + x := new(Int).SetUint64(7) for i := 0; i < b.N; i++ { - x.SetUint64(2) + x.SetUint64(7) x.modSqrtTonelliShanks(x, p) } } -func BenchmarkModSqrt5430_3Mod4(b *testing.B) { - if isRaceBuilder { - b.Skip("skipping on race builder") - } - p := tri(5430) - x := new(Int).SetUint64(2) +func BenchmarkModSqrt231_5Mod8(b *testing.B) { + p := tri(231) + p.Sub(p, intOne) + p.Sub(p, intOne) // tri(231) - 2 is a prime == 5 mod 8 + x := new(Int).SetUint64(7) for i := 0; i < b.N; i++ { - x.SetUint64(2) - x.modSqrt3Mod4Prime(x, p) + x.SetUint64(7) + x.modSqrt5Mod8Prime(x, p) } } @@ -1409,19 +1449,21 @@ var modInverseTests = []struct { {"1234567", "458948883992"}, {"239487239847", "2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919"}, {"-10", "13"}, // issue #16984 + {"10", "-13"}, + {"-17", "-13"}, } func TestModInverse(t *testing.T) { var element, modulus, gcd, inverse Int one := NewInt(1) - for i, test := range modInverseTests { + for _, test := range modInverseTests { (&element).SetString(test.element, 10) (&modulus).SetString(test.modulus, 10) (&inverse).ModInverse(&element, &modulus) (&inverse).Mul(&inverse, &element) (&inverse).Mod(&inverse, &modulus) if (&inverse).Cmp(one) != 0 { - t.Errorf("#%d: failed (e·e^(-1)=%s)", i, &inverse) + t.Errorf("ModInverse(%d,%d)*%d%%%d=%d, not 1", &element, &modulus, &element, &modulus, &inverse) } } // exhaustive test for small values @@ -1443,6 +1485,17 @@ func TestModInverse(t *testing.T) { } } +func BenchmarkModInverse(b *testing.B) { + p := new(Int).SetInt64(1) // Mersenne prime 2**1279 -1 + p.abs = p.abs.shl(p.abs, 1279) + p.Sub(p, intOne) + x := new(Int).Sub(p, intOne) + z := new(Int) + for i := 0; i < b.N; i++ { + z.ModInverse(x, p) + } +} + // testModSqrt is a helper for TestModSqrt, // which checks that ModSqrt can compute a square-root of elt^2. func testModSqrt(t *testing.T, elt, mod, sq, sqrt *Int) bool { diff --git a/libgo/go/math/big/nat.go b/libgo/go/math/big/nat.go index 3bb818f..a6f79ed 100644 --- a/libgo/go/math/big/nat.go +++ b/libgo/go/math/big/nat.go @@ -5,10 +5,16 @@ // This file implements unsigned multi-precision integers (natural // numbers). They are the building blocks for the implementation // of signed integers, rationals, and floating-point numbers. +// +// Caution: This implementation relies on the function "alias" +// which assumes that (nat) slice capacities are never +// changed (no 3-operand slice expressions). If that +// changes, alias needs to be updated for correctness. package big import ( + "encoding/binary" "math/bits" "math/rand" "sync" @@ -207,18 +213,17 @@ func (z nat) montgomery(x, y, m nat, k Word, n int) nat { if len(x) != n || len(y) != n || len(m) != n { panic("math/big: mismatched montgomery number lengths") } - z = z.make(n) + z = z.make(n * 2) z.clear() var c Word for i := 0; i < n; i++ { d := y[i] - c2 := addMulVVW(z, x, d) - t := z[0] * k - c3 := addMulVVW(z, m, t) - copy(z, z[1:]) + c2 := addMulVVW(z[i:n+i], x, d) + t := z[i] * k + c3 := addMulVVW(z[i:n+i], m, t) cx := c + c2 cy := cx + c3 - z[n-1] = cy + z[n+i] = cy if cx < c2 || cy < c3 { c = 1 } else { @@ -226,9 +231,11 @@ func (z nat) montgomery(x, y, m nat, k Word, n int) nat { } } if c != 0 { - subVV(z, z, m) + subVV(z[:n], z[n:], m) + } else { + copy(z[:n], z[n:]) } - return z + return z[:n] } // Fast version of z[0:n+n>>1].add(z[0:n+n>>1], x[0:n]) w/o bounds checks. @@ -351,6 +358,10 @@ func karatsuba(z, x, y nat) { } // alias reports whether x and y share the same base array. +// Note: alias assumes that the capacity of underlying arrays +// is never changed for nat values; i.e. that there are +// no 3-operand slice expressions in this code (or worse, +// reflect-based operations to the same effect). func alias(x, y nat) bool { return cap(x) > 0 && cap(y) > 0 && &x[0:cap(x)][cap(x)-1] == &y[0:cap(y)][cap(y)-1] } @@ -377,12 +388,12 @@ func max(x, y int) int { } // karatsubaLen computes an approximation to the maximum k <= n such that -// k = p<<i for a number p <= karatsubaThreshold and an i >= 0. Thus, the +// k = p<<i for a number p <= threshold and an i >= 0. Thus, the // result is the largest number that can be divided repeatedly by 2 before -// becoming about the value of karatsubaThreshold. -func karatsubaLen(n int) int { +// becoming about the value of threshold. +func karatsubaLen(n, threshold int) int { i := uint(0) - for n > karatsubaThreshold { + for n > threshold { n >>= 1 i++ } @@ -422,7 +433,7 @@ func (z nat) mul(x, y nat) nat { // y = yh*b + y0 (0 <= y0 < b) // b = 1<<(_W*k) ("base" of digits xi, yi) // - k := karatsubaLen(n) + k := karatsubaLen(n, karatsubaThreshold) // k <= n // multiply x0 and y0 via Karatsuba @@ -475,8 +486,8 @@ func (z nat) mul(x, y nat) nat { // basicSqr sets z = x*x and is asymptotically faster than basicMul // by about a factor of 2, but slower for small arguments due to overhead. -// Requirements: len(x) > 0, len(z) >= 2*len(x) -// The (non-normalized) result is placed in z[0 : 2 * len(x)]. +// Requirements: len(x) > 0, len(z) == 2*len(x) +// The (non-normalized) result is placed in z. func basicSqr(z, x nat) { n := len(x) t := make(nat, 2*n) // temporary variable to hold the products @@ -492,11 +503,47 @@ func basicSqr(z, x nat) { addVV(z, z, t) // combine the result } +// karatsubaSqr squares x and leaves the result in z. +// len(x) must be a power of 2 and len(z) >= 6*len(x). +// The (non-normalized) result is placed in z[0 : 2*len(x)]. +// +// The algorithm and the layout of z are the same as for karatsuba. +func karatsubaSqr(z, x nat) { + n := len(x) + + if n&1 != 0 || n < karatsubaSqrThreshold || n < 2 { + basicSqr(z[:2*n], x) + return + } + + n2 := n >> 1 + x1, x0 := x[n2:], x[0:n2] + + karatsubaSqr(z, x0) + karatsubaSqr(z[n:], x1) + + // s = sign(xd*yd) == -1 for xd != 0; s == 1 for xd == 0 + xd := z[2*n : 2*n+n2] + if subVV(xd, x1, x0) != 0 { + subVV(xd, x0, x1) + } + + p := z[n*3:] + karatsubaSqr(p, xd) + + r := z[n*4:] + copy(r, z[:n*2]) + + karatsubaAdd(z[n2:], r, n) + karatsubaAdd(z[n2:], r[n:], n) + karatsubaSub(z[n2:], p, n) // s == -1 for p != 0; s == 1 for p == 0 +} + // Operands that are shorter than basicSqrThreshold are squared using // "grade school" multiplication; for operands longer than karatsubaSqrThreshold -// the Karatsuba algorithm is used. +// we use the Karatsuba algorithm optimized for x == y. var basicSqrThreshold = 20 // computed by calibrate_test.go -var karatsubaSqrThreshold = 400 // computed by calibrate_test.go +var karatsubaSqrThreshold = 260 // computed by calibrate_test.go // z = x*x func (z nat) sqr(x nat) nat { @@ -514,18 +561,43 @@ func (z nat) sqr(x nat) nat { if alias(z, x) { z = nil // z is an alias for x - cannot reuse } - z = z.make(2 * n) if n < basicSqrThreshold { + z = z.make(2 * n) basicMul(z, x, x) return z.norm() } if n < karatsubaSqrThreshold { + z = z.make(2 * n) basicSqr(z, x) return z.norm() } - return z.mul(x, x) + // Use Karatsuba multiplication optimized for x == y. + // The algorithm and layout of z are the same as for mul. + + // z = (x1*b + x0)^2 = x1^2*b^2 + 2*x1*x0*b + x0^2 + + k := karatsubaLen(n, karatsubaSqrThreshold) + + x0 := x[0:k] + z = z.make(max(6*k, 2*n)) + karatsubaSqr(z, x0) // z = x0^2 + z = z[0 : 2*n] + z[2*k:].clear() + + if k < n { + var t nat + x0 := x0.norm() + x1 := x[k:] + t = t.mul(x0, x1) + addAt(z, t, k) + addAt(z, t, k) // z = 2*x1*x0*b + x0^2 + t = t.sqr(x1) + addAt(z, t, 2*k) // z = x1^2*b^2 + 2*x1*x0*b + x0^2 + } + + return z.norm() } // mulRange computes the product of all the unsigned integers in the @@ -718,8 +790,21 @@ func (x nat) trailingZeroBits() uint { return i*_W + uint(bits.TrailingZeros(uint(x[i]))) } +func same(x, y nat) bool { + return len(x) == len(y) && len(x) > 0 && &x[0] == &y[0] +} + // z = x << s func (z nat) shl(x nat, s uint) nat { + if s == 0 { + if same(z, x) { + return z + } + if !alias(z, x) { + return z.set(x) + } + } + m := len(x) if m == 0 { return z[:0] @@ -736,6 +821,15 @@ func (z nat) shl(x nat, s uint) nat { // z = x >> s func (z nat) shr(x nat, s uint) nat { + if s == 0 { + if same(z, x) { + return z + } + if !alias(z, x) { + return z.set(x) + } + } + m := len(x) n := m - int(s/_W) if n <= 0 { @@ -952,7 +1046,7 @@ func (z nat) expNN(x, y, m nat) nat { // x**1 mod m == x mod m if len(y) == 1 && y[0] == 1 && len(m) != 0 { - _, z = z.div(z, x, m) + _, z = nat(nil).div(z, x, m) return z } // y > 1 @@ -1125,7 +1219,7 @@ func (z nat) expNNMontgomery(x, y, m nat) nat { // RR = 2**(2*_W*len(m)) mod m RR := nat(nil).setWord(1) zz := nat(nil).shl(RR, uint(2*numWords*_W)) - _, RR = RR.div(RR, zz, m) + _, RR = nat(nil).div(RR, zz, m) if len(RR) < numWords { zz = zz.make(numWords) copy(zz, RR) @@ -1208,25 +1302,31 @@ func (z nat) bytes(buf []byte) (i int) { return } +// bigEndianWord returns the contents of buf interpreted as a big-endian encoded Word value. +func bigEndianWord(buf []byte) Word { + if _W == 64 { + return Word(binary.BigEndian.Uint64(buf)) + } + return Word(binary.BigEndian.Uint32(buf)) +} + // setBytes interprets buf as the bytes of a big-endian unsigned // integer, sets z to that value, and returns z. func (z nat) setBytes(buf []byte) nat { z = z.make((len(buf) + _S - 1) / _S) - k := 0 - s := uint(0) - var d Word - for i := len(buf); i > 0; i-- { - d |= Word(buf[i-1]) << s - if s += 8; s == _S*8 { - z[k] = d - k++ - s = 0 - d = 0 - } + i := len(buf) + for k := 0; i >= _S; k++ { + z[k] = bigEndianWord(buf[i-_S : i]) + i -= _S } - if k < len(z) { - z[k] = d + if i > 0 { + var d Word + for s := uint(0); i > 0; s += 8 { + d |= Word(buf[i-1]) << s + i-- + } + z[len(z)-1] = d } return z.norm() diff --git a/libgo/go/math/big/nat_test.go b/libgo/go/math/big/nat_test.go index c25cdf0..3c79495 100644 --- a/libgo/go/math/big/nat_test.go +++ b/libgo/go/math/big/nat_test.go @@ -267,6 +267,34 @@ func TestShiftRight(t *testing.T) { } } +func BenchmarkZeroShifts(b *testing.B) { + x := rndNat(800) + + b.Run("Shl", func(b *testing.B) { + for i := 0; i < b.N; i++ { + var z nat + z.shl(x, 0) + } + }) + b.Run("ShlSame", func(b *testing.B) { + for i := 0; i < b.N; i++ { + x.shl(x, 0) + } + }) + + b.Run("Shr", func(b *testing.B) { + for i := 0; i < b.N; i++ { + var z nat + z.shr(x, 0) + } + }) + b.Run("ShrSame", func(b *testing.B) { + for i := 0; i < b.N; i++ { + x.shr(x, 0) + } + }) +} + type modWTest struct { in string dividend string @@ -620,26 +648,26 @@ func TestSticky(t *testing.T) { } } -func testBasicSqr(t *testing.T, x nat) { +func testSqr(t *testing.T, x nat) { got := make(nat, 2*len(x)) want := make(nat, 2*len(x)) - basicSqr(got, x) - basicMul(want, x, x) + got = got.sqr(x) + want = want.mul(x, x) if got.cmp(want) != 0 { t.Errorf("basicSqr(%v), got %v, want %v", x, got, want) } } -func TestBasicSqr(t *testing.T) { +func TestSqr(t *testing.T) { for _, a := range prodNN { if a.x != nil { - testBasicSqr(t, a.x) + testSqr(t, a.x) } if a.y != nil { - testBasicSqr(t, a.y) + testSqr(t, a.y) } if a.z != nil { - testBasicSqr(t, a.z) + testSqr(t, a.z) } } } @@ -665,3 +693,22 @@ func BenchmarkNatSqr(b *testing.B) { }) } } + +func BenchmarkNatSetBytes(b *testing.B) { + const maxLength = 128 + lengths := []int{ + // No remainder: + 8, 24, maxLength, + // With remainder: + 7, 23, maxLength - 1, + } + n := make(nat, maxLength/_W) // ensure n doesn't need to grow during the test + buf := make([]byte, maxLength) + for _, l := range lengths { + b.Run(fmt.Sprint(l), func(b *testing.B) { + for i := 0; i < b.N; i++ { + n.setBytes(buf[:l]) + } + }) + } +} diff --git a/libgo/go/math/big/prime.go b/libgo/go/math/big/prime.go index 848affb..4c2c152 100644 --- a/libgo/go/math/big/prime.go +++ b/libgo/go/math/big/prime.go @@ -131,11 +131,11 @@ NextRandom: // // Baillie and Wagstaff, "Lucas Pseudoprimes", Mathematics of Computation 35(152), // October 1980, pp. 1391-1417, especially page 1401. -// http://www.ams.org/journals/mcom/1980-35-152/S0025-5718-1980-0583518-6/S0025-5718-1980-0583518-6.pdf +// https://www.ams.org/journals/mcom/1980-35-152/S0025-5718-1980-0583518-6/S0025-5718-1980-0583518-6.pdf // // Grantham, "Frobenius Pseudoprimes", Mathematics of Computation 70(234), // March 2000, pp. 873-891. -// http://www.ams.org/journals/mcom/2001-70-234/S0025-5718-00-01197-2/S0025-5718-00-01197-2.pdf +// https://www.ams.org/journals/mcom/2001-70-234/S0025-5718-00-01197-2/S0025-5718-00-01197-2.pdf // // Baillie, "Extra strong Lucas pseudoprimes", OEIS A217719, https://oeis.org/A217719. // diff --git a/libgo/go/math/big/prime_test.go b/libgo/go/math/big/prime_test.go index 7760519..bf50f34 100644 --- a/libgo/go/math/big/prime_test.go +++ b/libgo/go/math/big/prime_test.go @@ -29,13 +29,13 @@ var primes = []string{ "98920366548084643601728869055592650835572950932266967461790948584315647051443", "94560208308847015747498523884063394671606671904944666360068158221458669711639", - // http://primes.utm.edu/lists/small/small3.html + // https://primes.utm.edu/lists/small/small3.html "449417999055441493994709297093108513015373787049558499205492347871729927573118262811508386655998299074566974373711472560655026288668094291699357843464363003144674940345912431129144354948751003607115263071543163", "230975859993204150666423538988557839555560243929065415434980904258310530753006723857139742334640122533598517597674807096648905501653461687601339782814316124971547968912893214002992086353183070342498989426570593", "5521712099665906221540423207019333379125265462121169655563495403888449493493629943498064604536961775110765377745550377067893607246020694972959780839151452457728855382113555867743022746090187341871655890805971735385789993", "203956878356401977405765866929034577280193993314348263094772646453283062722701277632936616063144088173312372882677123879538709400158306567338328279154499698366071906766440037074217117805690872792848149112022286332144876183376326512083574821647933992961249917319836219304274280243803104015000563790123", - // ECC primes: http://tools.ietf.org/html/draft-ladd-safecurves-02 + // ECC primes: https://tools.ietf.org/html/draft-ladd-safecurves-02 "3618502788666131106986593281521497120414687020801267626233049500247285301239", // Curve1174: 2^251-9 "57896044618658097711785492504343953926634992332820282019728792003956564819949", // Curve25519: 2^255-19 "9850501549098619803069760025035903451269934817616361666987073351061430442874302652853566563721228910201656997576599", // E-382: 2^382-105 diff --git a/libgo/go/math/big/rat.go b/libgo/go/math/big/rat.go index b33fc69..46d58fc 100644 --- a/libgo/go/math/big/rat.go +++ b/libgo/go/math/big/rat.go @@ -422,7 +422,7 @@ func (z *Rat) norm() *Rat { neg := z.a.neg z.a.neg = false z.b.neg = false - if f := NewInt(0).lehmerGCD(&z.a, &z.b); f.Cmp(intOne) != 0 { + if f := NewInt(0).lehmerGCD(nil, nil, &z.a, &z.b); f.Cmp(intOne) != 0 { z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f.abs) z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs) if z.b.abs.cmp(natOne) == 0 { diff --git a/libgo/go/math/big/ratconv_test.go b/libgo/go/math/big/ratconv_test.go index 56ac8d7..fe8b8b6 100644 --- a/libgo/go/math/big/ratconv_test.go +++ b/libgo/go/math/big/ratconv_test.go @@ -300,9 +300,9 @@ var float64inputs = []string{ // "1e-18446744073709551616", // "1e+18446744073709551616", - // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ + // https://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ "2.2250738585072012e-308", - // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ + // https://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ "2.2250738585072011e-308", // A very large number (initially wrongly parsed by the fast algorithm). diff --git a/libgo/go/math/big/roundingmode_string.go b/libgo/go/math/big/roundingmode_string.go index 05024b8..c7629eb 100644 --- a/libgo/go/math/big/roundingmode_string.go +++ b/libgo/go/math/big/roundingmode_string.go @@ -1,16 +1,16 @@ -// generated by stringer -type=RoundingMode; DO NOT EDIT +// Code generated by "stringer -type=RoundingMode"; DO NOT EDIT. package big -import "fmt" +import "strconv" const _RoundingMode_name = "ToNearestEvenToNearestAwayToZeroAwayFromZeroToNegativeInfToPositiveInf" var _RoundingMode_index = [...]uint8{0, 13, 26, 32, 44, 57, 70} func (i RoundingMode) String() string { - if i+1 >= RoundingMode(len(_RoundingMode_index)) { - return fmt.Sprintf("RoundingMode(%d)", i) + if i >= RoundingMode(len(_RoundingMode_index)-1) { + return "RoundingMode(" + strconv.FormatInt(int64(i), 10) + ")" } return _RoundingMode_name[_RoundingMode_index[i]:_RoundingMode_index[i+1]] } diff --git a/libgo/go/math/big/sqrt.go b/libgo/go/math/big/sqrt.go index 00433cf..b989649 100644 --- a/libgo/go/math/big/sqrt.go +++ b/libgo/go/math/big/sqrt.go @@ -128,18 +128,21 @@ func (z *Float) sqrtInverse(x *Float) { // g(t) = f(t)/f'(t) = -½t(1 - xt²) // and the next guess is given by // t2 = t - g(t) = ½t(3 - xt²) - u := new(Float) + u := newFloat(z.prec) + v := newFloat(z.prec) ng := func(t *Float) *Float { u.prec = t.prec + v.prec = t.prec u.Mul(t, t) // u = t² u.Mul(x, u) // = xt² - u.Sub(three, u) // = 3 - xt² - u.Mul(t, u) // = t(3 - xt²) + v.Sub(three, u) // v = 3 - xt² + u.Mul(t, v) // u = t(3 - xt²) return t.Mul(half, u) // = ½t(3 - xt²) } xf, _ := x.Float64() - sqi := NewFloat(1 / math.Sqrt(xf)) + sqi := newFloat(z.prec) + sqi.SetFloat64(1 / math.Sqrt(xf)) for prec := z.prec + 32; sqi.prec < prec; { sqi.prec *= 2 sqi = ng(sqi) @@ -149,3 +152,12 @@ func (z *Float) sqrtInverse(x *Float) { // x/√x = √x z.Mul(x, sqi) } + +// newFloat returns a new *Float with space for twice the given +// precision. +func newFloat(prec2 uint32) *Float { + z := new(Float) + // nat.make ensures the slice length is > 0 + z.mant = z.mant.make(int(prec2/_W) * 2) + return z +} diff --git a/libgo/go/math/bits/bits_test.go b/libgo/go/math/bits/bits_test.go index ba05210..5c34f6d 100644 --- a/libgo/go/math/bits/bits_test.go +++ b/libgo/go/math/bits/bits_test.go @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package bits +package bits_test import ( + . "math/bits" "testing" "unsafe" ) @@ -83,7 +84,7 @@ func TestLeadingZeros(t *testing.T) { // Exported (global) variable serving as input for some // of the benchmarks to ensure side-effect free calls // are not optimized away. -var Input uint64 = deBruijn64 +var Input uint64 = DeBruijn64 // Exported (global) variable to store function results // during benchmarking to ensure side-effect free calls @@ -333,7 +334,7 @@ func BenchmarkOnesCount64(b *testing.B) { } func TestRotateLeft(t *testing.T) { - var m uint64 = deBruijn64 + var m uint64 = DeBruijn64 for k := uint(0); k < 128; k++ { x8 := uint8(m) diff --git a/libgo/go/math/bits/export_test.go b/libgo/go/math/bits/export_test.go new file mode 100644 index 0000000..8c6f933 --- /dev/null +++ b/libgo/go/math/bits/export_test.go @@ -0,0 +1,7 @@ +// Copyright 2018 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 bits + +const DeBruijn64 = deBruijn64 diff --git a/libgo/go/math/cmplx/cmath_test.go b/libgo/go/math/cmplx/cmath_test.go index 8d70562..80c3b33 100644 --- a/libgo/go/math/cmplx/cmath_test.go +++ b/libgo/go/math/cmplx/cmath_test.go @@ -39,7 +39,7 @@ var vc = []complex128{ } // The expected results below were computed by the high precision calculators -// at http://keisan.casio.com/. More exact input values (array vc[], above) +// at https://keisan.casio.com/. More exact input values (array vc[], above) // were obtained by printing them with "%.26f". The answers were calculated // to 26 digits (by using the "Digit number" drop-down control of each // calculator). diff --git a/libgo/go/math/erfinv.go b/libgo/go/math/erfinv.go index 21b5578..ee423d3 100644 --- a/libgo/go/math/erfinv.go +++ b/libgo/go/math/erfinv.go @@ -10,7 +10,7 @@ package math // This implementation is based on the rational approximation // of percentage points of normal distribution available from -// http://www.jstor.org/stable/2347330. +// https://www.jstor.org/stable/2347330. const ( // Coefficients for approximation to erf in |x| <= 0.85 diff --git a/libgo/go/math/example_test.go b/libgo/go/math/example_test.go index feaf9d8..a1f764b 100644 --- a/libgo/go/math/example_test.go +++ b/libgo/go/math/example_test.go @@ -89,3 +89,27 @@ func ExampleSqrt() { fmt.Printf("%.1f", c) // Output: 5.0 } + +func ExampleCeil() { + c := math.Ceil(1.49) + fmt.Printf("%.1f", c) + // Output: 2.0 +} + +func ExampleFloor() { + c := math.Floor(1.51) + fmt.Printf("%.1f", c) + // Output: 1.0 +} + +func ExamplePow() { + c := math.Pow(2, 3) + fmt.Printf("%.1f", c) + // Output: 8.0 +} + +func ExamplePow10() { + c := math.Pow10(2) + fmt.Printf("%.1f", c) + // Output: 100.0 +} diff --git a/libgo/go/math/floor_asm.go b/libgo/go/math/floor_asm.go deleted file mode 100644 index 761349a..0000000 --- a/libgo/go/math/floor_asm.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2015 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 ignore -// -build amd64 amd64p32 - -package math - -import "internal/cpu" - -var useSSE41 = cpu.X86.HasSSE41 diff --git a/libgo/go/math/hypot.go b/libgo/go/math/hypot.go index 59b9c74..844159a 100644 --- a/libgo/go/math/hypot.go +++ b/libgo/go/math/hypot.go @@ -28,12 +28,7 @@ func hypot(p, q float64) float64 { case IsNaN(p) || IsNaN(q): return NaN() } - if p < 0 { - p = -p - } - if q < 0 { - q = -q - } + p, q = Abs(p), Abs(q) if p < q { p, q = q, p } diff --git a/libgo/go/math/j0.go b/libgo/go/math/j0.go index fe26791..5523fc3 100644 --- a/libgo/go/math/j0.go +++ b/libgo/go/math/j0.go @@ -99,9 +99,7 @@ func J0(x float64) float64 { return 1 } - if x < 0 { - x = -x - } + x = Abs(x) if x >= 2 { s, c := Sincos(x) ss := s - c diff --git a/libgo/go/math/ldexp.go b/libgo/go/math/ldexp.go index e91a090c..8775cdb 100644 --- a/libgo/go/math/ldexp.go +++ b/libgo/go/math/ldexp.go @@ -37,7 +37,7 @@ func ldexp(frac float64, exp int) float64 { exp += e x := Float64bits(frac) exp += int(x>>shift)&mask - bias - if exp < -1074 { + if exp < -1075 { return Copysign(0, frac) // underflow } if exp > 1023 { // overflow @@ -48,8 +48,8 @@ func ldexp(frac float64, exp int) float64 { } var m float64 = 1 if exp < -1022 { // denormal - exp += 52 - m = 1.0 / (1 << 52) // 2**-52 + exp += 53 + m = 1.0 / (1 << 53) // 2**-53 } x &^= mask << shift x |= uint64(exp+bias) << shift diff --git a/libgo/go/math/lgamma.go b/libgo/go/math/lgamma.go index 19ac3ffa..7af5871 100644 --- a/libgo/go/math/lgamma.go +++ b/libgo/go/math/lgamma.go @@ -103,7 +103,7 @@ var _lgamA = [...]float64{ 4.48640949618915160150e-05, // 0x3F07858E90A45837 } var _lgamR = [...]float64{ - 1.0, // placeholder + 1.0, // placeholder 1.39200533467621045958e+00, // 0x3FF645A762C4AB74 7.21935547567138069525e-01, // 0x3FE71A1893D3DCDC 1.71933865632803078993e-01, // 0x3FC601EDCCFBDF27 diff --git a/libgo/go/math/rand/exp.go b/libgo/go/math/rand/exp.go index 4bc110f..5a8d946 100644 --- a/libgo/go/math/rand/exp.go +++ b/libgo/go/math/rand/exp.go @@ -13,7 +13,7 @@ import ( * * See "The Ziggurat Method for Generating Random Variables" * (Marsaglia & Tsang, 2000) - * http://www.jstatsoft.org/v05/i08/paper [pdf] + * https://www.jstatsoft.org/v05/i08/paper [pdf] */ const ( diff --git a/libgo/go/math/rand/normal.go b/libgo/go/math/rand/normal.go index ba4ea54..2c5a7aa 100644 --- a/libgo/go/math/rand/normal.go +++ b/libgo/go/math/rand/normal.go @@ -27,9 +27,9 @@ func absInt32(i int32) uint32 { return uint32(i) } -// NormFloat64 returns a normally distributed float64 in the range -// [-math.MaxFloat64, +math.MaxFloat64] with -// standard normal distribution (mean = 0, stddev = 1). +// NormFloat64 returns a normally distributed float64 in +// the range -math.MaxFloat64 through +math.MaxFloat64 inclusive, +// with standard normal distribution (mean = 0, stddev = 1). // To produce a different normal distribution, callers can // adjust the output using: // diff --git a/libgo/go/math/rand/rand.go b/libgo/go/math/rand/rand.go index 147c92f..04382e6 100644 --- a/libgo/go/math/rand/rand.go +++ b/libgo/go/math/rand/rand.go @@ -11,6 +11,9 @@ // The default Source is safe for concurrent use by multiple goroutines, but // Sources created by NewSource are not. // +// Mathematical interval notation such as [0, n) is used throughout the +// documentation for this package. +// // For random numbers suitable for security-sensitive work, see the crypto/rand // package. package rand diff --git a/libgo/go/math/rand/regress_test.go b/libgo/go/math/rand/regress_test.go index e31e6c5..1f30be8 100644 --- a/libgo/go/math/rand/regress_test.go +++ b/libgo/go/math/rand/regress_test.go @@ -121,219 +121,219 @@ func TestRegress(t *testing.T) { } var regressGolden = []interface{}{ - float64(4.668112973579268), // ExpFloat64() - float64(0.1601593871172866), // ExpFloat64() - float64(3.0465834105636), // ExpFloat64() - float64(0.06385839451671879), // ExpFloat64() - float64(1.8578917487258961), // ExpFloat64() - float64(0.784676123472182), // ExpFloat64() - float64(0.11225477361256932), // ExpFloat64() - float64(0.20173283329802255), // ExpFloat64() - float64(0.3468619496201105), // ExpFloat64() - float64(0.35601103454384536), // ExpFloat64() - float64(0.888376329507869), // ExpFloat64() - float64(1.4081362450365698), // ExpFloat64() - float64(1.0077753823151994), // ExpFloat64() - float64(0.23594100766227588), // ExpFloat64() - float64(2.777245612300007), // ExpFloat64() - float64(0.5202997830662377), // ExpFloat64() - float64(1.2842705247770294), // ExpFloat64() - float64(0.030307408362776206), // ExpFloat64() - float64(2.204156824853721), // ExpFloat64() - float64(2.09891923895058), // ExpFloat64() - float32(0.94519615), // Float32() - float32(0.24496509), // Float32() - float32(0.65595627), // Float32() - float32(0.05434384), // Float32() - float32(0.3675872), // Float32() - float32(0.28948045), // Float32() - float32(0.1924386), // Float32() - float32(0.65533215), // Float32() - float32(0.8971697), // Float32() - float32(0.16735445), // Float32() - float32(0.28858566), // Float32() - float32(0.9026048), // Float32() - float32(0.84978026), // Float32() - float32(0.2730468), // Float32() - float32(0.6090802), // Float32() - float32(0.253656), // Float32() - float32(0.7746542), // Float32() - float32(0.017480763), // Float32() - float32(0.78707397), // Float32() - float32(0.7993937), // Float32() - float64(0.9451961492941164), // Float64() - float64(0.24496508529377975), // Float64() - float64(0.6559562651954052), // Float64() - float64(0.05434383959970039), // Float64() - float64(0.36758720663245853), // Float64() - float64(0.2894804331565928), // Float64() - float64(0.19243860967493215), // Float64() - float64(0.6553321508148324), // Float64() - float64(0.897169713149801), // Float64() - float64(0.16735444255905835), // Float64() - float64(0.2885856518054551), // Float64() - float64(0.9026048462705047), // Float64() - float64(0.8497802817628735), // Float64() - float64(0.2730468047134829), // Float64() - float64(0.6090801919903561), // Float64() - float64(0.25365600644283687), // Float64() - float64(0.7746542391859803), // Float64() - float64(0.017480762156647272), // Float64() - float64(0.7870739563039942), // Float64() - float64(0.7993936979594545), // Float64() - int64(8717895732742165505), // Int() - int64(2259404117704393152), // Int() - int64(6050128673802995827), // Int() - int64(501233450539197794), // Int() - int64(3390393562759376202), // Int() - int64(2669985732393126063), // Int() - int64(1774932891286980153), // Int() - int64(6044372234677422456), // Int() - int64(8274930044578894929), // Int() - int64(1543572285742637646), // Int() - int64(2661732831099943416), // Int() - int64(8325060299420976708), // Int() - int64(7837839688282259259), // Int() - int64(2518412263346885298), // Int() - int64(5617773211005988520), // Int() - int64(2339563716805116249), // Int() - int64(7144924247938981575), // Int() - int64(161231572858529631), // Int() - int64(7259475919510918339), // Int() - int64(7373105480197164748), // Int() - int32(2029793274), // Int31() - int32(526058514), // Int31() - int32(1408655353), // Int31() - int32(116702506), // Int31() - int32(789387515), // Int31() - int32(621654496), // Int31() - int32(413258767), // Int31() - int32(1407315077), // Int31() - int32(1926657288), // Int31() - int32(359390928), // Int31() - int32(619732968), // Int31() - int32(1938329147), // Int31() - int32(1824889259), // Int31() - int32(586363548), // Int31() - int32(1307989752), // Int31() - int32(544722126), // Int31() - int32(1663557311), // Int31() - int32(37539650), // Int31() - int32(1690228450), // Int31() - int32(1716684894), // Int31() - int32(0), // Int31n(1) - int32(4), // Int31n(10) - int32(25), // Int31n(32) - int32(310570), // Int31n(1048576) - int32(857611), // Int31n(1048577) - int32(621654496), // Int31n(1000000000) - int32(413258767), // Int31n(1073741824) - int32(1407315077), // Int31n(2147483646) - int32(1926657288), // Int31n(2147483647) - int32(0), // Int31n(1) - int32(8), // Int31n(10) - int32(27), // Int31n(32) - int32(367019), // Int31n(1048576) - int32(209005), // Int31n(1048577) - int32(307989752), // Int31n(1000000000) - int32(544722126), // Int31n(1073741824) - int32(1663557311), // Int31n(2147483646) - int32(37539650), // Int31n(2147483647) - int32(0), // Int31n(1) - int32(4), // Int31n(10) - int64(8717895732742165505), // Int63() - int64(2259404117704393152), // Int63() - int64(6050128673802995827), // Int63() - int64(501233450539197794), // Int63() - int64(3390393562759376202), // Int63() - int64(2669985732393126063), // Int63() - int64(1774932891286980153), // Int63() - int64(6044372234677422456), // Int63() - int64(8274930044578894929), // Int63() - int64(1543572285742637646), // Int63() - int64(2661732831099943416), // Int63() - int64(8325060299420976708), // Int63() - int64(7837839688282259259), // Int63() - int64(2518412263346885298), // Int63() - int64(5617773211005988520), // Int63() - int64(2339563716805116249), // Int63() - int64(7144924247938981575), // Int63() - int64(161231572858529631), // Int63() - int64(7259475919510918339), // Int63() - int64(7373105480197164748), // Int63() - int64(0), // Int63n(1) - int64(2), // Int63n(10) - int64(19), // Int63n(32) - int64(959842), // Int63n(1048576) - int64(688912), // Int63n(1048577) - int64(393126063), // Int63n(1000000000) - int64(89212473), // Int63n(1073741824) - int64(834026388), // Int63n(2147483646) - int64(1577188963), // Int63n(2147483647) - int64(543572285742637646), // Int63n(1000000000000000000) - int64(355889821886249464), // Int63n(1152921504606846976) - int64(8325060299420976708), // Int63n(9223372036854775806) - int64(7837839688282259259), // Int63n(9223372036854775807) - int64(0), // Int63n(1) - int64(0), // Int63n(10) - int64(25), // Int63n(32) - int64(679623), // Int63n(1048576) - int64(882178), // Int63n(1048577) - int64(510918339), // Int63n(1000000000) - int64(782454476), // Int63n(1073741824) - int64(0), // Intn(1) - int64(4), // Intn(10) - int64(25), // Intn(32) - int64(310570), // Intn(1048576) - int64(857611), // Intn(1048577) - int64(621654496), // Intn(1000000000) - int64(413258767), // Intn(1073741824) - int64(1407315077), // Intn(2147483646) - int64(1926657288), // Intn(2147483647) - int64(543572285742637646), // Intn(1000000000000000000) - int64(355889821886249464), // Intn(1152921504606846976) - int64(8325060299420976708), // Intn(9223372036854775806) - int64(7837839688282259259), // Intn(9223372036854775807) - int64(0), // Intn(1) - int64(2), // Intn(10) - int64(14), // Intn(32) - int64(515775), // Intn(1048576) - int64(839455), // Intn(1048577) - int64(690228450), // Intn(1000000000) - int64(642943070), // Intn(1073741824) - float64(-0.28158587086436215), // NormFloat64() - float64(0.570933095808067), // NormFloat64() - float64(-1.6920196326157044), // NormFloat64() - float64(0.1996229111693099), // NormFloat64() - float64(1.9195199291234621), // NormFloat64() - float64(0.8954838794918353), // NormFloat64() - float64(0.41457072128813166), // NormFloat64() - float64(-0.48700161491544713), // NormFloat64() - float64(-0.1684059662402393), // NormFloat64() - float64(0.37056410998929545), // NormFloat64() - float64(1.0156889027029008), // NormFloat64() - float64(-0.5174422210625114), // NormFloat64() - float64(-0.5565834214413804), // NormFloat64() - float64(0.778320596648391), // NormFloat64() - float64(-1.8970718197702225), // NormFloat64() - float64(0.5229525761688676), // NormFloat64() - float64(-1.5515595563231523), // NormFloat64() - float64(0.0182029289376123), // NormFloat64() - float64(-0.6820951356608795), // NormFloat64() - float64(-0.5987943422687668), // NormFloat64() - []int{}, // Perm(0) - []int{0}, // Perm(1) - []int{0, 4, 1, 3, 2}, // Perm(5) - []int{3, 1, 0, 4, 7, 5, 2, 6}, // Perm(8) - []int{5, 0, 3, 6, 7, 4, 2, 1, 8}, // Perm(9) - []int{4, 5, 0, 2, 6, 9, 3, 1, 8, 7}, // Perm(10) + float64(4.668112973579268), // ExpFloat64() + float64(0.1601593871172866), // ExpFloat64() + float64(3.0465834105636), // ExpFloat64() + float64(0.06385839451671879), // ExpFloat64() + float64(1.8578917487258961), // ExpFloat64() + float64(0.784676123472182), // ExpFloat64() + float64(0.11225477361256932), // ExpFloat64() + float64(0.20173283329802255), // ExpFloat64() + float64(0.3468619496201105), // ExpFloat64() + float64(0.35601103454384536), // ExpFloat64() + float64(0.888376329507869), // ExpFloat64() + float64(1.4081362450365698), // ExpFloat64() + float64(1.0077753823151994), // ExpFloat64() + float64(0.23594100766227588), // ExpFloat64() + float64(2.777245612300007), // ExpFloat64() + float64(0.5202997830662377), // ExpFloat64() + float64(1.2842705247770294), // ExpFloat64() + float64(0.030307408362776206), // ExpFloat64() + float64(2.204156824853721), // ExpFloat64() + float64(2.09891923895058), // ExpFloat64() + float32(0.94519615), // Float32() + float32(0.24496509), // Float32() + float32(0.65595627), // Float32() + float32(0.05434384), // Float32() + float32(0.3675872), // Float32() + float32(0.28948045), // Float32() + float32(0.1924386), // Float32() + float32(0.65533215), // Float32() + float32(0.8971697), // Float32() + float32(0.16735445), // Float32() + float32(0.28858566), // Float32() + float32(0.9026048), // Float32() + float32(0.84978026), // Float32() + float32(0.2730468), // Float32() + float32(0.6090802), // Float32() + float32(0.253656), // Float32() + float32(0.7746542), // Float32() + float32(0.017480763), // Float32() + float32(0.78707397), // Float32() + float32(0.7993937), // Float32() + float64(0.9451961492941164), // Float64() + float64(0.24496508529377975), // Float64() + float64(0.6559562651954052), // Float64() + float64(0.05434383959970039), // Float64() + float64(0.36758720663245853), // Float64() + float64(0.2894804331565928), // Float64() + float64(0.19243860967493215), // Float64() + float64(0.6553321508148324), // Float64() + float64(0.897169713149801), // Float64() + float64(0.16735444255905835), // Float64() + float64(0.2885856518054551), // Float64() + float64(0.9026048462705047), // Float64() + float64(0.8497802817628735), // Float64() + float64(0.2730468047134829), // Float64() + float64(0.6090801919903561), // Float64() + float64(0.25365600644283687), // Float64() + float64(0.7746542391859803), // Float64() + float64(0.017480762156647272), // Float64() + float64(0.7870739563039942), // Float64() + float64(0.7993936979594545), // Float64() + int64(8717895732742165505), // Int() + int64(2259404117704393152), // Int() + int64(6050128673802995827), // Int() + int64(501233450539197794), // Int() + int64(3390393562759376202), // Int() + int64(2669985732393126063), // Int() + int64(1774932891286980153), // Int() + int64(6044372234677422456), // Int() + int64(8274930044578894929), // Int() + int64(1543572285742637646), // Int() + int64(2661732831099943416), // Int() + int64(8325060299420976708), // Int() + int64(7837839688282259259), // Int() + int64(2518412263346885298), // Int() + int64(5617773211005988520), // Int() + int64(2339563716805116249), // Int() + int64(7144924247938981575), // Int() + int64(161231572858529631), // Int() + int64(7259475919510918339), // Int() + int64(7373105480197164748), // Int() + int32(2029793274), // Int31() + int32(526058514), // Int31() + int32(1408655353), // Int31() + int32(116702506), // Int31() + int32(789387515), // Int31() + int32(621654496), // Int31() + int32(413258767), // Int31() + int32(1407315077), // Int31() + int32(1926657288), // Int31() + int32(359390928), // Int31() + int32(619732968), // Int31() + int32(1938329147), // Int31() + int32(1824889259), // Int31() + int32(586363548), // Int31() + int32(1307989752), // Int31() + int32(544722126), // Int31() + int32(1663557311), // Int31() + int32(37539650), // Int31() + int32(1690228450), // Int31() + int32(1716684894), // Int31() + int32(0), // Int31n(1) + int32(4), // Int31n(10) + int32(25), // Int31n(32) + int32(310570), // Int31n(1048576) + int32(857611), // Int31n(1048577) + int32(621654496), // Int31n(1000000000) + int32(413258767), // Int31n(1073741824) + int32(1407315077), // Int31n(2147483646) + int32(1926657288), // Int31n(2147483647) + int32(0), // Int31n(1) + int32(8), // Int31n(10) + int32(27), // Int31n(32) + int32(367019), // Int31n(1048576) + int32(209005), // Int31n(1048577) + int32(307989752), // Int31n(1000000000) + int32(544722126), // Int31n(1073741824) + int32(1663557311), // Int31n(2147483646) + int32(37539650), // Int31n(2147483647) + int32(0), // Int31n(1) + int32(4), // Int31n(10) + int64(8717895732742165505), // Int63() + int64(2259404117704393152), // Int63() + int64(6050128673802995827), // Int63() + int64(501233450539197794), // Int63() + int64(3390393562759376202), // Int63() + int64(2669985732393126063), // Int63() + int64(1774932891286980153), // Int63() + int64(6044372234677422456), // Int63() + int64(8274930044578894929), // Int63() + int64(1543572285742637646), // Int63() + int64(2661732831099943416), // Int63() + int64(8325060299420976708), // Int63() + int64(7837839688282259259), // Int63() + int64(2518412263346885298), // Int63() + int64(5617773211005988520), // Int63() + int64(2339563716805116249), // Int63() + int64(7144924247938981575), // Int63() + int64(161231572858529631), // Int63() + int64(7259475919510918339), // Int63() + int64(7373105480197164748), // Int63() + int64(0), // Int63n(1) + int64(2), // Int63n(10) + int64(19), // Int63n(32) + int64(959842), // Int63n(1048576) + int64(688912), // Int63n(1048577) + int64(393126063), // Int63n(1000000000) + int64(89212473), // Int63n(1073741824) + int64(834026388), // Int63n(2147483646) + int64(1577188963), // Int63n(2147483647) + int64(543572285742637646), // Int63n(1000000000000000000) + int64(355889821886249464), // Int63n(1152921504606846976) + int64(8325060299420976708), // Int63n(9223372036854775806) + int64(7837839688282259259), // Int63n(9223372036854775807) + int64(0), // Int63n(1) + int64(0), // Int63n(10) + int64(25), // Int63n(32) + int64(679623), // Int63n(1048576) + int64(882178), // Int63n(1048577) + int64(510918339), // Int63n(1000000000) + int64(782454476), // Int63n(1073741824) + int64(0), // Intn(1) + int64(4), // Intn(10) + int64(25), // Intn(32) + int64(310570), // Intn(1048576) + int64(857611), // Intn(1048577) + int64(621654496), // Intn(1000000000) + int64(413258767), // Intn(1073741824) + int64(1407315077), // Intn(2147483646) + int64(1926657288), // Intn(2147483647) + int64(543572285742637646), // Intn(1000000000000000000) + int64(355889821886249464), // Intn(1152921504606846976) + int64(8325060299420976708), // Intn(9223372036854775806) + int64(7837839688282259259), // Intn(9223372036854775807) + int64(0), // Intn(1) + int64(2), // Intn(10) + int64(14), // Intn(32) + int64(515775), // Intn(1048576) + int64(839455), // Intn(1048577) + int64(690228450), // Intn(1000000000) + int64(642943070), // Intn(1073741824) + float64(-0.28158587086436215), // NormFloat64() + float64(0.570933095808067), // NormFloat64() + float64(-1.6920196326157044), // NormFloat64() + float64(0.1996229111693099), // NormFloat64() + float64(1.9195199291234621), // NormFloat64() + float64(0.8954838794918353), // NormFloat64() + float64(0.41457072128813166), // NormFloat64() + float64(-0.48700161491544713), // NormFloat64() + float64(-0.1684059662402393), // NormFloat64() + float64(0.37056410998929545), // NormFloat64() + float64(1.0156889027029008), // NormFloat64() + float64(-0.5174422210625114), // NormFloat64() + float64(-0.5565834214413804), // NormFloat64() + float64(0.778320596648391), // NormFloat64() + float64(-1.8970718197702225), // NormFloat64() + float64(0.5229525761688676), // NormFloat64() + float64(-1.5515595563231523), // NormFloat64() + float64(0.0182029289376123), // NormFloat64() + float64(-0.6820951356608795), // NormFloat64() + float64(-0.5987943422687668), // NormFloat64() + []int{}, // Perm(0) + []int{0}, // Perm(1) + []int{0, 4, 1, 3, 2}, // Perm(5) + []int{3, 1, 0, 4, 7, 5, 2, 6}, // Perm(8) + []int{5, 0, 3, 6, 7, 4, 2, 1, 8}, // Perm(9) + []int{4, 5, 0, 2, 6, 9, 3, 1, 8, 7}, // Perm(10) []int{14, 2, 0, 8, 3, 5, 13, 12, 1, 4, 6, 7, 11, 9, 15, 10}, // Perm(16) - []int{}, // Perm(0) - []int{0}, // Perm(1) - []int{3, 0, 1, 2, 4}, // Perm(5) - []int{5, 1, 2, 0, 4, 7, 3, 6}, // Perm(8) - []int{4, 0, 6, 8, 1, 5, 2, 7, 3}, // Perm(9) - []int{8, 6, 1, 7, 5, 4, 3, 2, 9, 0}, // Perm(10) + []int{}, // Perm(0) + []int{0}, // Perm(1) + []int{3, 0, 1, 2, 4}, // Perm(5) + []int{5, 1, 2, 0, 4, 7, 3, 6}, // Perm(8) + []int{4, 0, 6, 8, 1, 5, 2, 7, 3}, // Perm(9) + []int{8, 6, 1, 7, 5, 4, 3, 2, 9, 0}, // Perm(10) []int{0, 3, 13, 2, 15, 4, 10, 1, 8, 14, 7, 6, 12, 9, 5, 11}, // Perm(16) []int{}, // Perm(0) []int{0}, // Perm(1) @@ -346,7 +346,7 @@ var regressGolden = []interface{}{ []byte{0x41, 0xd3, 0xff, 0x12, 0x4, 0x5b, 0x73, 0xc8}, // Read([0 0 0 0 0 0 0 0]) []byte{0x6e, 0x4f, 0xf9, 0x5f, 0xf6, 0x62, 0xa5, 0xee, 0xe8}, // Read([0 0 0 0 0 0 0 0 0]) []byte{0x2a, 0xbd, 0xf4, 0x4a, 0x2d, 0xb, 0x75, 0xfb, 0x18, 0xd}, // Read([0 0 0 0 0 0 0 0 0 0]) - []byte{0xaf}, // Read([0]) + []byte{0xaf}, // Read([0]) []byte{0x48, 0xa7, 0x9e, 0xe0, 0xb1, 0xd, 0x39}, // Read([0 0 0 0 0 0 0]) []byte{0x46, 0x51, 0x85, 0xf, 0xd4, 0xa1, 0x78, 0x89}, // Read([0 0 0 0 0 0 0 0]) []byte{0x2e, 0xe2, 0x85, 0xec, 0xe1, 0x51, 0x14, 0x55, 0x78}, // Read([0 0 0 0 0 0 0 0 0]) @@ -356,49 +356,49 @@ var regressGolden = []interface{}{ []byte{0xc6, 0xb1, 0xf8, 0x3b, 0x8e, 0x88, 0x3b, 0xbf}, // Read([0 0 0 0 0 0 0 0]) []byte{0x85, 0x7a, 0xab, 0x99, 0xc5, 0xb2, 0x52, 0xc7, 0x42}, // Read([0 0 0 0 0 0 0 0 0]) []byte{0x9c, 0x32, 0xf3, 0xa8, 0xae, 0xb7, 0x9e, 0xf8, 0x56, 0xf6}, // Read([0 0 0 0 0 0 0 0 0 0]) - []byte{0x59}, // Read([0]) + []byte{0x59}, // Read([0]) []byte{0xc1, 0x8f, 0xd, 0xce, 0xcc, 0x77, 0xc7}, // Read([0 0 0 0 0 0 0]) []byte{0x5e, 0x7a, 0x81, 0xbf, 0xde, 0x27, 0x5f, 0x67}, // Read([0 0 0 0 0 0 0 0]) []byte{0xcf, 0xe2, 0x42, 0xcf, 0x3c, 0xc3, 0x54, 0xf3, 0xed}, // Read([0 0 0 0 0 0 0 0 0]) []byte{0xe2, 0xd6, 0xbe, 0xcc, 0x4e, 0xa3, 0xae, 0x5e, 0x88, 0x52}, // Read([0 0 0 0 0 0 0 0 0 0]) - uint32(4059586549), // Uint32() - uint32(1052117029), // Uint32() - uint32(2817310706), // Uint32() - uint32(233405013), // Uint32() - uint32(1578775030), // Uint32() - uint32(1243308993), // Uint32() - uint32(826517535), // Uint32() - uint32(2814630155), // Uint32() - uint32(3853314576), // Uint32() - uint32(718781857), // Uint32() - uint32(1239465936), // Uint32() - uint32(3876658295), // Uint32() - uint32(3649778518), // Uint32() - uint32(1172727096), // Uint32() - uint32(2615979505), // Uint32() - uint32(1089444252), // Uint32() - uint32(3327114623), // Uint32() - uint32(75079301), // Uint32() - uint32(3380456901), // Uint32() - uint32(3433369789), // Uint32() - uint64(8717895732742165505), // Uint64() - uint64(2259404117704393152), // Uint64() - uint64(6050128673802995827), // Uint64() - uint64(9724605487393973602), // Uint64() - uint64(12613765599614152010), // Uint64() - uint64(11893357769247901871), // Uint64() - uint64(1774932891286980153), // Uint64() - uint64(15267744271532198264), // Uint64() - uint64(17498302081433670737), // Uint64() - uint64(1543572285742637646), // Uint64() - uint64(11885104867954719224), // Uint64() - uint64(17548432336275752516), // Uint64() - uint64(7837839688282259259), // Uint64() - uint64(2518412263346885298), // Uint64() - uint64(5617773211005988520), // Uint64() - uint64(11562935753659892057), // Uint64() - uint64(16368296284793757383), // Uint64() - uint64(161231572858529631), // Uint64() - uint64(16482847956365694147), // Uint64() - uint64(16596477517051940556), // Uint64() + uint32(4059586549), // Uint32() + uint32(1052117029), // Uint32() + uint32(2817310706), // Uint32() + uint32(233405013), // Uint32() + uint32(1578775030), // Uint32() + uint32(1243308993), // Uint32() + uint32(826517535), // Uint32() + uint32(2814630155), // Uint32() + uint32(3853314576), // Uint32() + uint32(718781857), // Uint32() + uint32(1239465936), // Uint32() + uint32(3876658295), // Uint32() + uint32(3649778518), // Uint32() + uint32(1172727096), // Uint32() + uint32(2615979505), // Uint32() + uint32(1089444252), // Uint32() + uint32(3327114623), // Uint32() + uint32(75079301), // Uint32() + uint32(3380456901), // Uint32() + uint32(3433369789), // Uint32() + uint64(8717895732742165505), // Uint64() + uint64(2259404117704393152), // Uint64() + uint64(6050128673802995827), // Uint64() + uint64(9724605487393973602), // Uint64() + uint64(12613765599614152010), // Uint64() + uint64(11893357769247901871), // Uint64() + uint64(1774932891286980153), // Uint64() + uint64(15267744271532198264), // Uint64() + uint64(17498302081433670737), // Uint64() + uint64(1543572285742637646), // Uint64() + uint64(11885104867954719224), // Uint64() + uint64(17548432336275752516), // Uint64() + uint64(7837839688282259259), // Uint64() + uint64(2518412263346885298), // Uint64() + uint64(5617773211005988520), // Uint64() + uint64(11562935753659892057), // Uint64() + uint64(16368296284793757383), // Uint64() + uint64(161231572858529631), // Uint64() + uint64(16482847956365694147), // Uint64() + uint64(16596477517051940556), // Uint64() } diff --git a/libgo/go/math/rand/rng.go b/libgo/go/math/rand/rng.go index f922417..f305df1 100644 --- a/libgo/go/math/rand/rng.go +++ b/libgo/go/math/rand/rng.go @@ -12,19 +12,16 @@ package rand */ const ( - _LEN = 607 - _TAP = 273 - _MAX = 1 << 63 - _MASK = _MAX - 1 - _A = 48271 - _M = (1 << 31) - 1 - _Q = 44488 - _R = 3399 + rngLen = 607 + rngTap = 273 + rngMax = 1 << 63 + rngMask = rngMax - 1 + int32max = (1 << 31) - 1 ) var ( - // Used for seeding. See gen_cooked.go for details. - rng_cooked [_LEN]int64 = [...]int64{ + // rngCooked used for seeding. See gen_cooked.go for details. + rngCooked [rngLen]int64 = [...]int64{ -4181792142133755926, -4576982950128230565, 1395769623340756751, 5333664234075297259, -6347679516498800754, 9033628115061424579, 7143218595135194537, 4812947590706362721, 7937252194349799378, 5307299880338848416, 8209348851763925077, -7107630437535961764, @@ -181,18 +178,24 @@ var ( ) type rngSource struct { - tap int // index into vec - feed int // index into vec - vec [_LEN]int64 // current feedback register + tap int // index into vec + feed int // index into vec + vec [rngLen]int64 // current feedback register } // seed rng x[n+1] = 48271 * x[n] mod (2**31 - 1) func seedrand(x int32) int32 { - hi := x / _Q - lo := x % _Q - x = _A*lo - _R*hi + const ( + A = 48271 + Q = 44488 + R = 3399 + ) + + hi := x / Q + lo := x % Q + x = A*lo - R*hi if x < 0 { - x += _M + x += int32max } return x } @@ -200,18 +203,18 @@ func seedrand(x int32) int32 { // Seed uses the provided seed value to initialize the generator to a deterministic state. func (rng *rngSource) Seed(seed int64) { rng.tap = 0 - rng.feed = _LEN - _TAP + rng.feed = rngLen - rngTap - seed = seed % _M + seed = seed % int32max if seed < 0 { - seed += _M + seed += int32max } if seed == 0 { seed = 89482311 } x := int32(seed) - for i := -20; i < _LEN; i++ { + for i := -20; i < rngLen; i++ { x = seedrand(x) if i >= 0 { var u int64 @@ -220,7 +223,7 @@ func (rng *rngSource) Seed(seed int64) { u ^= int64(x) << 20 x = seedrand(x) u ^= int64(x) - u ^= rng_cooked[i] + u ^= rngCooked[i] rng.vec[i] = u } } @@ -228,19 +231,19 @@ func (rng *rngSource) Seed(seed int64) { // Int63 returns a non-negative pseudo-random 63-bit integer as an int64. func (rng *rngSource) Int63() int64 { - return int64(rng.Uint64() & _MASK) + return int64(rng.Uint64() & rngMask) } // Uint64 returns a non-negative pseudo-random 64-bit integer as an uint64. func (rng *rngSource) Uint64() uint64 { rng.tap-- if rng.tap < 0 { - rng.tap += _LEN + rng.tap += rngLen } rng.feed-- if rng.feed < 0 { - rng.feed += _LEN + rng.feed += rngLen } x := rng.vec[rng.feed] + rng.vec[rng.tap] diff --git a/libgo/go/math/sin.go b/libgo/go/math/sin.go index 75579ab..e3166e2 100644 --- a/libgo/go/math/sin.go +++ b/libgo/go/math/sin.go @@ -137,9 +137,7 @@ func cos(x float64) float64 { // make argument positive sign := false - if x < 0 { - x = -x - } + x = Abs(x) 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 diff --git a/libgo/go/math/sinh.go b/libgo/go/math/sinh.go index 139b911..9dff71e 100644 --- a/libgo/go/math/sinh.go +++ b/libgo/go/math/sinh.go @@ -43,10 +43,11 @@ func Sinh(x float64) float64 { var temp float64 switch true { case x > 21: - temp = Exp(x) / 2 + temp = Exp(x) * 0.5 case x > 0.5: - temp = (Exp(x) - Exp(-x)) / 2 + ex := Exp(x) + temp = (ex - 1/ex) * 0.5 default: sq := x * x @@ -67,11 +68,10 @@ func Sinh(x float64) float64 { // Cosh(±Inf) = +Inf // Cosh(NaN) = NaN func Cosh(x float64) float64 { - if x < 0 { - x = -x - } + x = Abs(x) if x > 21 { - return Exp(x) / 2 + return Exp(x) * 0.5 } - return (Exp(x) + Exp(-x)) / 2 + ex := Exp(x) + return (ex + 1/ex) * 0.5 } diff --git a/libgo/go/mime/encodedword.go b/libgo/go/mime/encodedword.go index 99eb432..58f60da 100644 --- a/libgo/go/mime/encodedword.go +++ b/libgo/go/mime/encodedword.go @@ -11,7 +11,6 @@ import ( "fmt" "io" "strings" - "sync" "unicode" "unicode/utf8" ) @@ -51,16 +50,19 @@ func needsEncoding(s string) bool { // encodeWord encodes a string into an encoded-word. func (e WordEncoder) encodeWord(charset, s string) string { - buf := getBuffer() - defer putBuffer(buf) + var buf strings.Builder + // Could use a hint like len(s)*3, but that's not enough for cases + // with word splits and too much for simpler inputs. + // 48 is close to maxEncodedWordLen/2, but adjusted to allocator size class. + buf.Grow(48) - e.openWord(buf, charset) + e.openWord(&buf, charset) if e == BEncoding { - e.bEncode(buf, charset, s) + e.bEncode(&buf, charset, s) } else { - e.qEncode(buf, charset, s) + e.qEncode(&buf, charset, s) } - closeWord(buf) + closeWord(&buf) return buf.String() } @@ -77,7 +79,7 @@ const ( var maxBase64Len = base64.StdEncoding.DecodedLen(maxContentLen) // bEncode encodes s using base64 encoding and writes it to buf. -func (e WordEncoder) bEncode(buf *bytes.Buffer, charset, s string) { +func (e WordEncoder) bEncode(buf *strings.Builder, charset, s string) { w := base64.NewEncoder(base64.StdEncoding, buf) // If the charset is not UTF-8 or if the content is short, do not bother // splitting the encoded-word. @@ -109,7 +111,7 @@ func (e WordEncoder) bEncode(buf *bytes.Buffer, charset, s string) { // qEncode encodes s using Q encoding and writes it to buf. It splits the // encoded-words when necessary. -func (e WordEncoder) qEncode(buf *bytes.Buffer, charset, s string) { +func (e WordEncoder) qEncode(buf *strings.Builder, charset, s string) { // We only split encoded-words when the charset is UTF-8. if !isUTF8(charset) { writeQString(buf, s) @@ -139,7 +141,7 @@ func (e WordEncoder) qEncode(buf *bytes.Buffer, charset, s string) { } // writeQString encodes s using Q encoding and writes it to buf. -func writeQString(buf *bytes.Buffer, s string) { +func writeQString(buf *strings.Builder, s string) { for i := 0; i < len(s); i++ { switch b := s[i]; { case b == ' ': @@ -155,7 +157,7 @@ func writeQString(buf *bytes.Buffer, s string) { } // openWord writes the beginning of an encoded-word into buf. -func (e WordEncoder) openWord(buf *bytes.Buffer, charset string) { +func (e WordEncoder) openWord(buf *strings.Builder, charset string) { buf.WriteString("=?") buf.WriteString(charset) buf.WriteByte('?') @@ -164,12 +166,12 @@ func (e WordEncoder) openWord(buf *bytes.Buffer, charset string) { } // closeWord writes the end of an encoded-word into buf. -func closeWord(buf *bytes.Buffer) { +func closeWord(buf *strings.Builder) { buf.WriteString("?=") } // splitWord closes the current encoded-word and opens a new one. -func (e WordEncoder) splitWord(buf *bytes.Buffer, charset string) { +func (e WordEncoder) splitWord(buf *strings.Builder, charset string) { closeWord(buf) buf.WriteByte(' ') e.openWord(buf, charset) @@ -224,10 +226,9 @@ func (d *WordDecoder) Decode(word string) (string, error) { return "", err } - buf := getBuffer() - defer putBuffer(buf) + var buf strings.Builder - if err := d.convert(buf, charset, content); err != nil { + if err := d.convert(&buf, charset, content); err != nil { return "", err } @@ -243,8 +244,7 @@ func (d *WordDecoder) DecodeHeader(header string) (string, error) { return header, nil } - buf := getBuffer() - defer putBuffer(buf) + var buf strings.Builder buf.WriteString(header[:i]) header = header[i:] @@ -296,7 +296,7 @@ func (d *WordDecoder) DecodeHeader(header string) (string, error) { buf.WriteString(header[:start]) } - if err := d.convert(buf, charset, content); err != nil { + if err := d.convert(&buf, charset, content); err != nil { return "", err } @@ -322,7 +322,7 @@ func decode(encoding byte, text string) ([]byte, error) { } } -func (d *WordDecoder) convert(buf *bytes.Buffer, charset string, content []byte) error { +func (d *WordDecoder) convert(buf *strings.Builder, charset string, content []byte) error { switch { case strings.EqualFold("utf-8", charset): buf.Write(content) @@ -346,7 +346,7 @@ func (d *WordDecoder) convert(buf *bytes.Buffer, charset string, content []byte) if err != nil { return err } - if _, err = buf.ReadFrom(r); err != nil { + if _, err = io.Copy(buf, r); err != nil { return err } } @@ -422,21 +422,3 @@ func fromHex(b byte) (byte, error) { } return 0, fmt.Errorf("mime: invalid hex byte %#02x", b) } - -var bufPool = sync.Pool{ - New: func() interface{} { - return new(bytes.Buffer) - }, -} - -func getBuffer() *bytes.Buffer { - return bufPool.Get().(*bytes.Buffer) -} - -func putBuffer(buf *bytes.Buffer) { - if buf.Len() > 1024 { - return - } - buf.Reset() - bufPool.Put(buf) -} diff --git a/libgo/go/mime/mediatype.go b/libgo/go/mime/mediatype.go index 426d417..ea2bbac 100644 --- a/libgo/go/mime/mediatype.go +++ b/libgo/go/mime/mediatype.go @@ -5,7 +5,6 @@ package mime import ( - "bytes" "errors" "fmt" "sort" @@ -19,7 +18,7 @@ import ( // When any of the arguments result in a standard violation then // FormatMediaType returns the empty string. func FormatMediaType(t string, param map[string]string) string { - var b bytes.Buffer + var b strings.Builder if slash := strings.Index(t, "/"); slash == -1 { if !isToken(t) { return "" @@ -167,7 +166,7 @@ func ParseMediaType(v string) (mediatype string, params map[string]string, err e // Stitch together any continuations or things with stars // (i.e. RFC 2231 things with stars: "foo*0" or "foo*") - var buf bytes.Buffer + var buf strings.Builder for key, pieceMap := range continuation { singlePartKey := key + "*" if v, ok := pieceMap[singlePartKey]; ok { @@ -265,7 +264,7 @@ func consumeValue(v string) (value, rest string) { } // parse a quoted-string - buffer := new(bytes.Buffer) + buffer := new(strings.Builder) for i := 1; i < len(v); i++ { r := v[i] if r == '"' { diff --git a/libgo/go/mime/multipart/formdata.go b/libgo/go/mime/multipart/formdata.go index 2a4ebdd4..832d0ad 100644 --- a/libgo/go/mime/multipart/formdata.go +++ b/libgo/go/mime/multipart/formdata.go @@ -58,8 +58,7 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { var b bytes.Buffer - _, hasContentTypeHeader := p.Header["Content-Type"] - if !hasContentTypeHeader && filename == "" { + if filename == "" { // value, store as string in memory n, err := io.CopyN(&b, p, maxValueBytes+1) if err != nil && err != io.EOF { diff --git a/libgo/go/mime/multipart/formdata_test.go b/libgo/go/mime/multipart/formdata_test.go index 69333d3..2d6a830 100644 --- a/libgo/go/mime/multipart/formdata_test.go +++ b/libgo/go/mime/multipart/formdata_test.go @@ -47,12 +47,24 @@ func TestReadFormWithNamelessFile(t *testing.T) { } defer f.RemoveAll() - fd := testFile(t, f.File["hiddenfile"][0], "", filebContents) - if _, ok := fd.(sectionReadCloser); !ok { - t.Errorf("file has unexpected underlying type %T", fd) + if g, e := f.Value["hiddenfile"][0], filebContents; g != e { + t.Errorf("hiddenfile value = %q, want %q", g, e) } - fd.Close() +} +func TestReadFormWithTextContentType(t *testing.T) { + // From https://github.com/golang/go/issues/24041 + b := strings.NewReader(strings.Replace(messageWithTextContentType, "\n", "\r\n", -1)) + r := NewReader(b, boundary) + f, err := r.ReadForm(25) + if err != nil { + t.Fatal("ReadForm:", err) + } + defer f.RemoveAll() + + if g, e := f.Value["texta"][0], textaValue; g != e { + t.Errorf("texta value = %q, want %q", g, e) + } } func testFile(t *testing.T, fh *FileHeader, efn, econtent string) File { @@ -94,6 +106,15 @@ Content-Type: text/plain --MyBoundary-- ` +const messageWithTextContentType = ` +--MyBoundary +Content-Disposition: form-data; name="texta" +Content-Type: text/plain + +` + textaValue + ` +--MyBoundary +` + const message = ` --MyBoundary Content-Disposition: form-data; name="filea"; filename="filea.txt" diff --git a/libgo/go/mime/multipart/multipart.go b/libgo/go/mime/multipart/multipart.go index 1954808..0993fb7 100644 --- a/libgo/go/mime/multipart/multipart.go +++ b/libgo/go/mime/multipart/multipart.go @@ -61,7 +61,7 @@ type Part struct { // FormName returns the name parameter if p has a Content-Disposition // of type "form-data". Otherwise it returns the empty string. func (p *Part) FormName() string { - // See http://tools.ietf.org/html/rfc2183 section 2 for EBNF + // See https://tools.ietf.org/html/rfc2183 section 2 for EBNF // of Content-Disposition value format. if p.dispositionParams == nil { p.parseContentDisposition() @@ -303,7 +303,9 @@ func (r *Reader) NextPart() (*Part, error) { if r.currentPart != nil { r.currentPart.Close() } - + if string(r.dashBoundary) == "--" { + return nil, fmt.Errorf("multipart: boundary is empty") + } expectNewPart := false for { line, err := r.bufReader.ReadSlice('\n') @@ -370,7 +372,7 @@ func (mr *Reader) isFinalBoundary(line []byte) bool { } func (mr *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) { - // http://tools.ietf.org/html/rfc2046#section-5.1 + // https://tools.ietf.org/html/rfc2046#section-5.1 // The boundary delimiter line is then defined as a line // consisting entirely of two hyphen characters ("-", // decimal value 45) followed by the boundary parameter diff --git a/libgo/go/mime/multipart/multipart_test.go b/libgo/go/mime/multipart/multipart_test.go index 7fbee90..abe1cc8 100644 --- a/libgo/go/mime/multipart/multipart_test.go +++ b/libgo/go/mime/multipart/multipart_test.go @@ -880,3 +880,11 @@ func roundTripParseTest() parseTest { t.sep = w.Boundary() return t } + +func TestNoBoundary(t *testing.T) { + mr := NewReader(strings.NewReader(""), "") + _, err := mr.NextPart() + if got, want := fmt.Sprint(err), "multipart: boundary is empty"; got != want { + t.Errorf("NextPart error = %v; want %v", got, want) + } +} diff --git a/libgo/go/mime/quotedprintable/reader.go b/libgo/go/mime/quotedprintable/reader.go index b142240..4239625 100644 --- a/libgo/go/mime/quotedprintable/reader.go +++ b/libgo/go/mime/quotedprintable/reader.go @@ -123,6 +123,10 @@ func (r *Reader) Read(p []byte) (n int, err error) { r.line = r.line[2:] // 2 of the 3; other 1 is done below case b == '\t' || b == '\r' || b == '\n': break + case b >= 0x80: + // As an extension to RFC 2045, we accept + // values >= 0x80 without complaint. Issue 22597. + break case b < ' ' || b > '~': return n, fmt.Errorf("quotedprintable: invalid unescaped byte 0x%02x in body", b) } diff --git a/libgo/go/mime/quotedprintable/reader_test.go b/libgo/go/mime/quotedprintable/reader_test.go index ca016f9..f870bda 100644 --- a/libgo/go/mime/quotedprintable/reader_test.go +++ b/libgo/go/mime/quotedprintable/reader_test.go @@ -37,7 +37,7 @@ func TestReader(t *testing.T) { {in: " A B =\n C ", want: " A B C"}, // lax. treating LF as CRLF {in: "foo=\nbar", want: "foobar"}, {in: "foo\x00bar", want: "foo", err: "quotedprintable: invalid unescaped byte 0x00 in body"}, - {in: "foo bar\xff", want: "foo bar", err: "quotedprintable: invalid unescaped byte 0xff in body"}, + {in: "foo bar\xff", want: "foo bar\xff"}, // Equal sign. {in: "=3D30\n", want: "=30\n"}, @@ -65,6 +65,8 @@ func TestReader(t *testing.T) { // Example from RFC 2045: {in: "Now's the time =\n" + "for all folk to come=\n" + " to the aid of their country.", want: "Now's the time for all folk to come to the aid of their country."}, + {in: "accept UTF-8 right quotation mark: ’", + want: "accept UTF-8 right quotation mark: ’"}, } for _, tt := range tests { var buf bytes.Buffer diff --git a/libgo/go/mime/quotedprintable/writer_test.go b/libgo/go/mime/quotedprintable/writer_test.go index a9b77b3..d494c1e 100644 --- a/libgo/go/mime/quotedprintable/writer_test.go +++ b/libgo/go/mime/quotedprintable/writer_test.go @@ -138,7 +138,7 @@ func TestRoundTrip(t *testing.T) { } } -// From http://fr.wikipedia.org/wiki/Quoted-Printable +// From https://fr.wikipedia.org/wiki/Quoted-Printable var testMsg = []byte("Quoted-Printable (QP) est un format d'encodage de données codées sur 8 bits, qui utilise exclusivement les caractères alphanumériques imprimables du code ASCII (7 bits).\r\n" + "\r\n" + "En effet, les différents codages comprennent de nombreux caractères qui ne sont pas représentables en ASCII (par exemple les caractères accentués), ainsi que des caractères dits « non-imprimables ».\r\n" + diff --git a/libgo/go/mime/type.go b/libgo/go/mime/type.go index 78fc6b6..64e26ff 100644 --- a/libgo/go/mime/type.go +++ b/libgo/go/mime/type.go @@ -62,7 +62,8 @@ var builtinTypesLower = map[string]string{ ".htm": "text/html; charset=utf-8", ".html": "text/html; charset=utf-8", ".jpg": "image/jpeg", - ".js": "application/x-javascript", + ".js": "application/javascript", + ".wasm": "application/wasm", ".pdf": "application/pdf", ".png": "image/png", ".svg": "image/svg+xml", diff --git a/libgo/go/mime/type_unix.go b/libgo/go/mime/type_unix.go index 8e177ca..dfc1f88 100644 --- a/libgo/go/mime/type_unix.go +++ b/libgo/go/mime/type_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package mime diff --git a/libgo/go/net/cgo_unix.go b/libgo/go/net/cgo_unix.go index 5ea13bc..a4be3ba 100644 --- a/libgo/go/net/cgo_unix.go +++ b/libgo/go/net/cgo_unix.go @@ -278,7 +278,7 @@ func cgoLookupPTR(ctx context.Context, addr string) (names []string, err error, var zone string ip := parseIPv4(addr) if ip == nil { - ip, zone = parseIPv6(addr, true) + ip, zone = parseIPv6Zone(addr) } if ip == nil { return nil, &DNSError{Err: "invalid address", Name: addr}, true diff --git a/libgo/go/net/conf.go b/libgo/go/net/conf.go index a798699..6cc4a99 100644 --- a/libgo/go/net/conf.go +++ b/libgo/go/net/conf.go @@ -114,18 +114,19 @@ func initConfVal() { // canUseCgo reports whether calling cgo functions is allowed // for non-hostname lookups. func (c *conf) canUseCgo() bool { - return c.hostLookupOrder("") == hostLookupCgo + return c.hostLookupOrder(nil, "") == hostLookupCgo } // hostLookupOrder determines which strategy to use to resolve hostname. -func (c *conf) hostLookupOrder(hostname string) (ret hostLookupOrder) { +// The provided Resolver is optional. nil means to not consider its options. +func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrder) { if c.dnsDebugLevel > 1 { defer func() { print("go package net: hostLookupOrder(", hostname, ") = ", ret.String(), "\n") }() } fallbackOrder := hostLookupCgo - if c.netGo { + if c.netGo || r.preferGo() { fallbackOrder = hostLookupFilesDNS } if c.forceCgoLookupHost || c.resolv.unknownOpt || c.goos == "android" { @@ -148,7 +149,7 @@ func (c *conf) hostLookupOrder(hostname string) (ret hostLookupOrder) { } lookup := c.resolv.lookup if len(lookup) == 0 { - // http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5 + // https://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5 // "If the lookup keyword is not used in the // system's resolv.conf file then the assumed // order is 'bind file'" @@ -202,7 +203,7 @@ func (c *conf) hostLookupOrder(hostname string) (ret hostLookupOrder) { } if c.goos == "linux" { // glibc says the default is "dns [!UNAVAIL=return] files" - // http://www.gnu.org/software/libc/manual/html_node/Notes-on-NSS-Configuration-File.html. + // https://www.gnu.org/software/libc/manual/html_node/Notes-on-NSS-Configuration-File.html. return hostLookupDNSFiles } return hostLookupFilesDNS diff --git a/libgo/go/net/conf_test.go b/libgo/go/net/conf_test.go index 17d03f4..3c7403e 100644 --- a/libgo/go/net/conf_test.go +++ b/libgo/go/net/conf_test.go @@ -33,6 +33,7 @@ func TestConfHostLookupOrder(t *testing.T) { tests := []struct { name string c *conf + resolver *Resolver hostTests []nssHostTest }{ { @@ -170,7 +171,7 @@ func TestConfHostLookupOrder(t *testing.T) { hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupDNSFiles}}, }, // glibc lacking an nsswitch.conf, per - // http://www.gnu.org/software/libc/manual/html_node/Notes-on-NSS-Configuration-File.html + // https://www.gnu.org/software/libc/manual/html_node/Notes-on-NSS-Configuration-File.html { name: "linux_no_nsswitch.conf", c: &conf{ @@ -322,6 +323,21 @@ func TestConfHostLookupOrder(t *testing.T) { {"x.com", "myhostname", hostLookupCgo}, }, }, + // Issue 24393: make sure "Resolver.PreferGo = true" acts like netgo. + { + name: "resolver-prefergo", + resolver: &Resolver{PreferGo: true}, + c: &conf{ + goos: "darwin", + forceCgoLookupHost: true, // always true for darwin + resolv: defaultResolvConf, + nss: nssStr(""), + netCgo: true, + }, + hostTests: []nssHostTest{ + {"localhost", "myhostname", hostLookupFilesDNS}, + }, + }, } origGetHostname := getHostname @@ -331,7 +347,7 @@ func TestConfHostLookupOrder(t *testing.T) { for _, ht := range tt.hostTests { getHostname = func() (string, error) { return ht.localhost, nil } - gotOrder := tt.c.hostLookupOrder(ht.host) + gotOrder := tt.c.hostLookupOrder(tt.resolver, ht.host) if gotOrder != ht.want { t.Errorf("%s: hostLookupOrder(%q) = %v; want %v", tt.name, ht.host, gotOrder, ht.want) } diff --git a/libgo/go/net/conn_test.go b/libgo/go/net/conn_test.go index 16cf69e..6854898 100644 --- a/libgo/go/net/conn_test.go +++ b/libgo/go/net/conn_test.go @@ -5,6 +5,8 @@ // This file implements API tests across platforms and will never have a build // tag. +// +build !js + package net import ( diff --git a/libgo/go/net/dial.go b/libgo/go/net/dial.go index f8b4aa2..b1a5ca7 100644 --- a/libgo/go/net/dial.go +++ b/libgo/go/net/dial.go @@ -8,6 +8,7 @@ import ( "context" "internal/nettrace" "internal/poll" + "syscall" "time" ) @@ -70,6 +71,14 @@ type Dialer struct { // // Deprecated: Use DialContext instead. Cancel <-chan struct{} + + // If Control is not nil, it is called after creating the network + // connection but before actually dialing. + // + // Network and address parameters passed to Control method are not + // necessarily the ones passed to Dial. For example, passing "tcp" to Dial + // will cause the Control function to be called with "tcp4" or "tcp6". + Control func(network, address string, c syscall.RawConn) error } func minNonzeroTime(a, b time.Time) time.Time { @@ -306,8 +315,8 @@ func DialTimeout(network, address string, timeout time.Duration) (Conn, error) { return d.Dial(network, address) } -// dialParam contains a Dial's parameters and configuration. -type dialParam struct { +// sysDialer contains a Dial's parameters and configuration. +type sysDialer struct { Dialer network, address string } @@ -377,7 +386,7 @@ func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn return nil, &OpError{Op: "dial", Net: network, Source: nil, Addr: nil, Err: err} } - dp := &dialParam{ + sd := &sysDialer{ Dialer: *d, network: network, address: address, @@ -392,9 +401,9 @@ func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn var c Conn if len(fallbacks) > 0 { - c, err = dialParallel(ctx, dp, primaries, fallbacks) + c, err = sd.dialParallel(ctx, primaries, fallbacks) } else { - c, err = dialSerial(ctx, dp, primaries) + c, err = sd.dialSerial(ctx, primaries) } if err != nil { return nil, err @@ -412,9 +421,9 @@ func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn // head start. It returns the first established connection and // closes the others. Otherwise it returns an error from the first // primary address. -func dialParallel(ctx context.Context, dp *dialParam, primaries, fallbacks addrList) (Conn, error) { +func (sd *sysDialer) dialParallel(ctx context.Context, primaries, fallbacks addrList) (Conn, error) { if len(fallbacks) == 0 { - return dialSerial(ctx, dp, primaries) + return sd.dialSerial(ctx, primaries) } returned := make(chan struct{}) @@ -433,7 +442,7 @@ func dialParallel(ctx context.Context, dp *dialParam, primaries, fallbacks addrL if !primary { ras = fallbacks } - c, err := dialSerial(ctx, dp, ras) + c, err := sd.dialSerial(ctx, ras) select { case results <- dialResult{Conn: c, error: err, primary: primary, done: true}: case <-returned: @@ -451,7 +460,7 @@ func dialParallel(ctx context.Context, dp *dialParam, primaries, fallbacks addrL go startRacer(primaryCtx, true) // Start the timer for the fallback racer. - fallbackTimer := time.NewTimer(dp.fallbackDelay()) + fallbackTimer := time.NewTimer(sd.fallbackDelay()) defer fallbackTimer.Stop() for { @@ -486,13 +495,13 @@ func dialParallel(ctx context.Context, dp *dialParam, primaries, fallbacks addrL // dialSerial connects to a list of addresses in sequence, returning // either the first successful connection, or the first error. -func dialSerial(ctx context.Context, dp *dialParam, ras addrList) (Conn, error) { +func (sd *sysDialer) dialSerial(ctx context.Context, ras addrList) (Conn, error) { var firstErr error // The error from the first address is most relevant. for i, ra := range ras { select { case <-ctx.Done(): - return nil, &OpError{Op: "dial", Net: dp.network, Source: dp.LocalAddr, Addr: ra, Err: mapErr(ctx.Err())} + return nil, &OpError{Op: "dial", Net: sd.network, Source: sd.LocalAddr, Addr: ra, Err: mapErr(ctx.Err())} default: } @@ -501,7 +510,7 @@ func dialSerial(ctx context.Context, dp *dialParam, ras addrList) (Conn, error) if err != nil { // Ran out of time. if firstErr == nil { - firstErr = &OpError{Op: "dial", Net: dp.network, Source: dp.LocalAddr, Addr: ra, Err: err} + firstErr = &OpError{Op: "dial", Net: sd.network, Source: sd.LocalAddr, Addr: ra, Err: err} } break } @@ -512,7 +521,7 @@ func dialSerial(ctx context.Context, dp *dialParam, ras addrList) (Conn, error) defer cancel() } - c, err := dialSingle(dialCtx, dp, ra) + c, err := sd.dialSingle(dialCtx, ra) if err == nil { return c, nil } @@ -522,47 +531,126 @@ func dialSerial(ctx context.Context, dp *dialParam, ras addrList) (Conn, error) } if firstErr == nil { - firstErr = &OpError{Op: "dial", Net: dp.network, Source: nil, Addr: nil, Err: errMissingAddress} + firstErr = &OpError{Op: "dial", Net: sd.network, Source: nil, Addr: nil, Err: errMissingAddress} } return nil, firstErr } // dialSingle attempts to establish and returns a single connection to // the destination address. -func dialSingle(ctx context.Context, dp *dialParam, ra Addr) (c Conn, err error) { +func (sd *sysDialer) dialSingle(ctx context.Context, ra Addr) (c Conn, err error) { trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace) if trace != nil { raStr := ra.String() if trace.ConnectStart != nil { - trace.ConnectStart(dp.network, raStr) + trace.ConnectStart(sd.network, raStr) } if trace.ConnectDone != nil { - defer func() { trace.ConnectDone(dp.network, raStr, err) }() + defer func() { trace.ConnectDone(sd.network, raStr, err) }() } } - la := dp.LocalAddr + la := sd.LocalAddr switch ra := ra.(type) { case *TCPAddr: la, _ := la.(*TCPAddr) - c, err = dialTCP(ctx, dp.network, la, ra) + c, err = sd.dialTCP(ctx, la, ra) case *UDPAddr: la, _ := la.(*UDPAddr) - c, err = dialUDP(ctx, dp.network, la, ra) + c, err = sd.dialUDP(ctx, la, ra) case *IPAddr: la, _ := la.(*IPAddr) - c, err = dialIP(ctx, dp.network, la, ra) + c, err = sd.dialIP(ctx, la, ra) case *UnixAddr: la, _ := la.(*UnixAddr) - c, err = dialUnix(ctx, dp.network, la, ra) + c, err = sd.dialUnix(ctx, la, ra) + default: + return nil, &OpError{Op: "dial", Net: sd.network, Source: la, Addr: ra, Err: &AddrError{Err: "unexpected address type", Addr: sd.address}} + } + if err != nil { + return nil, &OpError{Op: "dial", Net: sd.network, Source: la, Addr: ra, Err: err} // c is non-nil interface containing nil pointer + } + return c, nil +} + +// ListenConfig contains options for listening to an address. +type ListenConfig struct { + // If Control is not nil, it is called after creating the network + // connection but before binding it to the operating system. + // + // Network and address parameters passed to Control method are not + // necessarily the ones passed to Listen. For example, passing "tcp" to + // Listen will cause the Control function to be called with "tcp4" or "tcp6". + Control func(network, address string, c syscall.RawConn) error +} + +// Listen announces on the local network address. +// +// See func Listen for a description of the network and address +// parameters. +func (lc *ListenConfig) Listen(ctx context.Context, network, address string) (Listener, error) { + addrs, err := DefaultResolver.resolveAddrList(ctx, "listen", network, address, nil) + if err != nil { + return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: err} + } + sl := &sysListener{ + ListenConfig: *lc, + network: network, + address: address, + } + var l Listener + la := addrs.first(isIPv4) + switch la := la.(type) { + case *TCPAddr: + l, err = sl.listenTCP(ctx, la) + case *UnixAddr: + l, err = sl.listenUnix(ctx, la) + default: + return nil, &OpError{Op: "listen", Net: sl.network, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: address}} + } + if err != nil { + return nil, &OpError{Op: "listen", Net: sl.network, Source: nil, Addr: la, Err: err} // l is non-nil interface containing nil pointer + } + return l, nil +} + +// ListenPacket announces on the local network address. +// +// See func ListenPacket for a description of the network and address +// parameters. +func (lc *ListenConfig) ListenPacket(ctx context.Context, network, address string) (PacketConn, error) { + addrs, err := DefaultResolver.resolveAddrList(ctx, "listen", network, address, nil) + if err != nil { + return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: err} + } + sl := &sysListener{ + ListenConfig: *lc, + network: network, + address: address, + } + var c PacketConn + la := addrs.first(isIPv4) + switch la := la.(type) { + case *UDPAddr: + c, err = sl.listenUDP(ctx, la) + case *IPAddr: + c, err = sl.listenIP(ctx, la) + case *UnixAddr: + c, err = sl.listenUnixgram(ctx, la) default: - return nil, &OpError{Op: "dial", Net: dp.network, Source: la, Addr: ra, Err: &AddrError{Err: "unexpected address type", Addr: dp.address}} + return nil, &OpError{Op: "listen", Net: sl.network, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: address}} } if err != nil { - return nil, &OpError{Op: "dial", Net: dp.network, Source: la, Addr: ra, Err: err} // c is non-nil interface containing nil pointer + return nil, &OpError{Op: "listen", Net: sl.network, Source: nil, Addr: la, Err: err} // c is non-nil interface containing nil pointer } return c, nil } +// sysListener contains a Listen's parameters and configuration. +type sysListener struct { + ListenConfig + network, address string +} + // Listen announces on the local network address. // // The network must be "tcp", "tcp4", "tcp6", "unix" or "unixpacket". @@ -582,23 +670,8 @@ func dialSingle(ctx context.Context, dp *dialParam, ra Addr) (c Conn, err error) // See func Dial for a description of the network and address // parameters. func Listen(network, address string) (Listener, error) { - addrs, err := DefaultResolver.resolveAddrList(context.Background(), "listen", network, address, nil) - if err != nil { - return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: err} - } - var l Listener - switch la := addrs.first(isIPv4).(type) { - case *TCPAddr: - l, err = ListenTCP(network, la) - case *UnixAddr: - l, err = ListenUnix(network, la) - default: - return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: address}} - } - if err != nil { - return nil, err // l is non-nil interface containing nil pointer - } - return l, nil + var lc ListenConfig + return lc.Listen(context.Background(), network, address) } // ListenPacket announces on the local network address. @@ -624,23 +697,6 @@ func Listen(network, address string) (Listener, error) { // See func Dial for a description of the network and address // parameters. func ListenPacket(network, address string) (PacketConn, error) { - addrs, err := DefaultResolver.resolveAddrList(context.Background(), "listen", network, address, nil) - if err != nil { - return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: err} - } - var l PacketConn - switch la := addrs.first(isIPv4).(type) { - case *UDPAddr: - l, err = ListenUDP(network, la) - case *IPAddr: - l, err = ListenIP(network, la) - case *UnixAddr: - l, err = ListenUnixgram(network, la) - default: - return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: address}} - } - if err != nil { - return nil, err // l is non-nil interface containing nil pointer - } - return l, nil + var lc ListenConfig + return lc.ListenPacket(context.Background(), network, address) } diff --git a/libgo/go/net/dial_test.go b/libgo/go/net/dial_test.go index b5f1dc9..00a84d1 100644 --- a/libgo/go/net/dial_test.go +++ b/libgo/go/net/dial_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( @@ -142,8 +144,9 @@ const ( // In some environments, the slow IPs may be explicitly unreachable, and fail // more quickly than expected. This test hook prevents dialTCP from returning // before the deadline. -func slowDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) { - c, err := doDialTCP(ctx, net, laddr, raddr) +func slowDialTCP(ctx context.Context, network string, laddr, raddr *TCPAddr) (*TCPConn, error) { + sd := &sysDialer{network: network, address: raddr.String()} + c, err := sd.doDialTCP(ctx, laddr, raddr) if ParseIP(slowDst4).Equal(raddr.IP) || ParseIP(slowDst6).Equal(raddr.IP) { // Wait for the deadline, or indefinitely if none exists. <-ctx.Done() @@ -295,12 +298,12 @@ func TestDialParallel(t *testing.T) { FallbackDelay: fallbackDelay, } startTime := time.Now() - dp := &dialParam{ + sd := &sysDialer{ Dialer: d, network: "tcp", address: "?", } - c, err := dialParallel(context.Background(), dp, primaries, fallbacks) + c, err := sd.dialParallel(context.Background(), primaries, fallbacks) elapsed := time.Since(startTime) if c != nil { @@ -331,7 +334,7 @@ func TestDialParallel(t *testing.T) { wg.Done() }() startTime = time.Now() - c, err = dialParallel(ctx, dp, primaries, fallbacks) + c, err = sd.dialParallel(ctx, primaries, fallbacks) if c != nil { c.Close() } @@ -467,13 +470,14 @@ func TestDialParallelSpuriousConnection(t *testing.T) { // Now ignore the provided context (which will be canceled) and use a // different one to make sure this completes with a valid connection, // which we hope to be closed below: - return doDialTCP(context.Background(), net, laddr, raddr) + sd := &sysDialer{network: net, address: raddr.String()} + return sd.doDialTCP(context.Background(), laddr, raddr) } d := Dialer{ FallbackDelay: fallbackDelay, } - dp := &dialParam{ + sd := &sysDialer{ Dialer: d, network: "tcp", address: "?", @@ -488,7 +492,7 @@ func TestDialParallelSpuriousConnection(t *testing.T) { } // dialParallel returns one connection (and closes the other.) - c, err := dialParallel(context.Background(), dp, makeAddr("127.0.0.1"), makeAddr("::1")) + c, err := sd.dialParallel(context.Background(), makeAddr("127.0.0.1"), makeAddr("::1")) if err != nil { t.Fatal(err) } @@ -749,9 +753,8 @@ func TestDialCancel(t *testing.T) { switch testenv.Builder() { case "linux-arm64-buildlet": t.Skip("skipping on linux-arm64-buildlet; incompatible network config? issue 15191") - case "": - testenv.MustHaveExternalNetwork(t) } + mustHaveExternalNetwork(t) if runtime.GOOS == "nacl" { // nacl doesn't have external network access. @@ -897,9 +900,7 @@ func TestCancelAfterDial(t *testing.T) { // if the machine has halfway configured IPv6 such that it can bind on // "::" not connect back to that same address. func TestDialListenerAddr(t *testing.T) { - if testenv.Builder() == "" { - testenv.MustHaveExternalNetwork(t) - } + mustHaveExternalNetwork(t) ln, err := Listen("tcp", ":0") if err != nil { t.Fatal(err) @@ -912,3 +913,64 @@ func TestDialListenerAddr(t *testing.T) { } c.Close() } + +func TestDialerControl(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9": + t.Skipf("not supported on %s", runtime.GOOS) + } + + t.Run("StreamDial", func(t *testing.T) { + for _, network := range []string{"tcp", "tcp4", "tcp6", "unix", "unixpacket"} { + if !testableNetwork(network) { + continue + } + ln, err := newLocalListener(network) + if err != nil { + t.Error(err) + continue + } + defer ln.Close() + d := Dialer{Control: controlOnConnSetup} + c, err := d.Dial(network, ln.Addr().String()) + if err != nil { + t.Error(err) + continue + } + c.Close() + } + }) + t.Run("PacketDial", func(t *testing.T) { + for _, network := range []string{"udp", "udp4", "udp6", "unixgram"} { + if !testableNetwork(network) { + continue + } + c1, err := newLocalPacketListener(network) + if err != nil { + t.Error(err) + continue + } + if network == "unixgram" { + defer os.Remove(c1.LocalAddr().String()) + } + defer c1.Close() + d := Dialer{Control: controlOnConnSetup} + c2, err := d.Dial(network, c1.LocalAddr().String()) + if err != nil { + t.Error(err) + continue + } + c2.Close() + } + }) +} + +// mustHaveExternalNetwork is like testenv.MustHaveExternalNetwork +// except that it won't skip testing on non-iOS builders. +func mustHaveExternalNetwork(t *testing.T) { + t.Helper() + ios := runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") + if testenv.Builder() == "" || ios { + testenv.MustHaveExternalNetwork(t) + } +} diff --git a/libgo/go/net/dial_unix_test.go b/libgo/go/net/dial_unix_test.go index d5c6dde2..3cfc9d8 100644 --- a/libgo/go/net/dial_unix_test.go +++ b/libgo/go/net/dial_unix_test.go @@ -102,7 +102,8 @@ func TestDialContextCancelRace(t *testing.T) { if !ok || oe.Op != "dial" { t.Fatalf("Dial error = %#v; want dial *OpError", err) } - if oe.Err != ctx.Err() { - t.Errorf("DialContext = (%v, %v); want OpError with error %v", c, err, ctx.Err()) + + if oe.Err != errCanceled { + t.Errorf("DialContext = (%v, %v); want OpError with error %v", c, err, errCanceled) } } diff --git a/libgo/go/net/dnsclient.go b/libgo/go/net/dnsclient.go index 2ab5639..2e4bffa 100644 --- a/libgo/go/net/dnsclient.go +++ b/libgo/go/net/dnsclient.go @@ -7,6 +7,8 @@ package net import ( "math/rand" "sort" + + "golang_org/x/net/dns/dnsmessage" ) // reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP @@ -35,71 +37,13 @@ func reverseaddr(addr string) (arpa string, err error) { return string(buf), nil } -// Find answer for name in dns message. -// On return, if err == nil, addrs != nil. -func answer(name, server string, dns *dnsMsg, qtype uint16) (cname string, addrs []dnsRR, err error) { - addrs = make([]dnsRR, 0, len(dns.answer)) - - if dns.rcode == dnsRcodeNameError { - return "", nil, &DNSError{Err: errNoSuchHost.Error(), Name: name, Server: server} - } - if dns.rcode != dnsRcodeSuccess { - // None of the error codes make sense - // for the query we sent. If we didn't get - // a name error and we didn't get success, - // the server is behaving incorrectly or - // having temporary trouble. - err := &DNSError{Err: "server misbehaving", Name: name, Server: server} - if dns.rcode == dnsRcodeServerFailure { - err.IsTemporary = true - } - return "", nil, err - } - - // Look for the name. - // Presotto says it's okay to assume that servers listed in - // /etc/resolv.conf are recursive resolvers. - // We asked for recursion, so it should have included - // all the answers we need in this one packet. -Cname: - for cnameloop := 0; cnameloop < 10; cnameloop++ { - addrs = addrs[0:0] - for _, rr := range dns.answer { - if _, justHeader := rr.(*dnsRR_Header); justHeader { - // Corrupt record: we only have a - // header. That header might say it's - // of type qtype, but we don't - // actually have it. Skip. - continue - } - h := rr.Header() - if h.Class == dnsClassINET && equalASCIILabel(h.Name, name) { - switch h.Rrtype { - case qtype: - addrs = append(addrs, rr) - case dnsTypeCNAME: - // redirect to cname - name = rr.(*dnsRR_CNAME).Cname - continue Cname - } - } - } - if len(addrs) == 0 { - return "", nil, &DNSError{Err: errNoSuchHost.Error(), Name: name, Server: server} - } - return name, addrs, nil - } - - return "", nil, &DNSError{Err: "too many redirects", Name: name, Server: server} -} - -func equalASCIILabel(x, y string) bool { - if len(x) != len(y) { +func equalASCIIName(x, y dnsmessage.Name) bool { + if x.Length != y.Length { return false } - for i := 0; i < len(x); i++ { - a := x[i] - b := y[i] + for i := 0; i < int(x.Length); i++ { + a := x.Data[i] + b := y.Data[i] if 'A' <= a && a <= 'Z' { a += 0x20 } diff --git a/libgo/go/net/dnsclient_test.go b/libgo/go/net/dnsclient_test.go index 7308fb0..3ab2b83 100644 --- a/libgo/go/net/dnsclient_test.go +++ b/libgo/go/net/dnsclient_test.go @@ -67,51 +67,3 @@ func testWeighting(t *testing.T, margin float64) { func TestWeighting(t *testing.T) { testWeighting(t, 0.05) } - -// Issue 8434: verify that Temporary returns true on an error when rcode -// is SERVFAIL -func TestIssue8434(t *testing.T) { - msg := &dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - rcode: dnsRcodeServerFailure, - }, - } - - _, _, err := answer("golang.org", "foo:53", msg, uint16(dnsTypeSRV)) - if err == nil { - t.Fatal("expected an error") - } - if ne, ok := err.(Error); !ok { - t.Fatalf("err = %#v; wanted something supporting net.Error", err) - } else if !ne.Temporary() { - t.Fatalf("Temporary = false for err = %#v; want Temporary == true", err) - } - if de, ok := err.(*DNSError); !ok { - t.Fatalf("err = %#v; wanted a *net.DNSError", err) - } else if !de.IsTemporary { - t.Fatalf("IsTemporary = false for err = %#v; want IsTemporary == true", err) - } -} - -// Issue 12778: verify that NXDOMAIN without RA bit errors as -// "no such host" and not "server misbehaving" -func TestIssue12778(t *testing.T) { - msg := &dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - rcode: dnsRcodeNameError, - recursion_available: false, - }, - } - - _, _, err := answer("golang.org", "foo:53", msg, uint16(dnsTypeSRV)) - if err == nil { - t.Fatal("expected an error") - } - de, ok := err.(*DNSError) - if !ok { - t.Fatalf("err = %#v; wanted a *net.DNSError", err) - } - if de.Err != errNoSuchHost.Error() { - t.Fatalf("Err = %#v; wanted %q", de.Err, errNoSuchHost.Error()) - } -} diff --git a/libgo/go/net/dnsclient_unix.go b/libgo/go/net/dnsclient_unix.go index 73a507e..6ec2f44 100644 --- a/libgo/go/net/dnsclient_unix.go +++ b/libgo/go/net/dnsclient_unix.go @@ -23,142 +23,225 @@ import ( "os" "sync" "time" -) - -// A dnsConn represents a DNS transport endpoint. -type dnsConn interface { - io.Closer - SetDeadline(time.Time) error + "golang_org/x/net/dns/dnsmessage" +) - // dnsRoundTrip executes a single DNS transaction, returning a - // DNS response message for the provided DNS query message. - dnsRoundTrip(query *dnsMsg) (*dnsMsg, error) +func newRequest(q dnsmessage.Question) (id uint16, udpReq, tcpReq []byte, err error) { + id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano()) + b := dnsmessage.NewBuilder(make([]byte, 2, 514), dnsmessage.Header{ID: id, RecursionDesired: true}) + b.EnableCompression() + if err := b.StartQuestions(); err != nil { + return 0, nil, nil, err + } + if err := b.Question(q); err != nil { + return 0, nil, nil, err + } + tcpReq, err = b.Finish() + udpReq = tcpReq[2:] + l := len(tcpReq) - 2 + tcpReq[0] = byte(l >> 8) + tcpReq[1] = byte(l) + return id, udpReq, tcpReq, err } -// dnsPacketConn implements the dnsConn interface for RFC 1035's -// "UDP usage" transport mechanism. Conn is a packet-oriented connection, -// such as a *UDPConn. -type dnsPacketConn struct { - Conn +func checkResponse(reqID uint16, reqQues dnsmessage.Question, respHdr dnsmessage.Header, respQues dnsmessage.Question) bool { + if !respHdr.Response { + return false + } + if reqID != respHdr.ID { + return false + } + if reqQues.Type != respQues.Type || reqQues.Class != respQues.Class || !equalASCIIName(reqQues.Name, respQues.Name) { + return false + } + return true } -func (c *dnsPacketConn) dnsRoundTrip(query *dnsMsg) (*dnsMsg, error) { - b, ok := query.Pack() - if !ok { - return nil, errors.New("cannot marshal DNS message") - } +func dnsPacketRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) { if _, err := c.Write(b); err != nil { - return nil, err + return dnsmessage.Parser{}, dnsmessage.Header{}, err } b = make([]byte, 512) // see RFC 1035 for { n, err := c.Read(b) if err != nil { - return nil, err + return dnsmessage.Parser{}, dnsmessage.Header{}, err } - resp := &dnsMsg{} - if !resp.Unpack(b[:n]) || !resp.IsResponseTo(query) { - // Ignore invalid responses as they may be malicious - // forgery attempts. Instead continue waiting until - // timeout. See golang.org/issue/13281. + var p dnsmessage.Parser + // Ignore invalid responses as they may be malicious + // forgery attempts. Instead continue waiting until + // timeout. See golang.org/issue/13281. + h, err := p.Start(b[:n]) + if err != nil { + continue + } + q, err := p.Question() + if err != nil || !checkResponse(id, query, h, q) { continue } - return resp, nil + return p, h, nil } } -// dnsStreamConn implements the dnsConn interface for RFC 1035's -// "TCP usage" transport mechanism. Conn is a stream-oriented connection, -// such as a *TCPConn. -type dnsStreamConn struct { - Conn -} - -func (c *dnsStreamConn) dnsRoundTrip(query *dnsMsg) (*dnsMsg, error) { - b, ok := query.Pack() - if !ok { - return nil, errors.New("cannot marshal DNS message") - } - l := len(b) - b = append([]byte{byte(l >> 8), byte(l)}, b...) +func dnsStreamRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) { if _, err := c.Write(b); err != nil { - return nil, err + return dnsmessage.Parser{}, dnsmessage.Header{}, err } b = make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035 if _, err := io.ReadFull(c, b[:2]); err != nil { - return nil, err + return dnsmessage.Parser{}, dnsmessage.Header{}, err } - l = int(b[0])<<8 | int(b[1]) + l := int(b[0])<<8 | int(b[1]) if l > len(b) { b = make([]byte, l) } n, err := io.ReadFull(c, b[:l]) if err != nil { - return nil, err + return dnsmessage.Parser{}, dnsmessage.Header{}, err } - resp := &dnsMsg{} - if !resp.Unpack(b[:n]) { - return nil, errors.New("cannot unmarshal DNS message") + var p dnsmessage.Parser + h, err := p.Start(b[:n]) + if err != nil { + return dnsmessage.Parser{}, dnsmessage.Header{}, errors.New("cannot unmarshal DNS message") } - if !resp.IsResponseTo(query) { - return nil, errors.New("invalid DNS response") + q, err := p.Question() + if err != nil { + return dnsmessage.Parser{}, dnsmessage.Header{}, errors.New("cannot unmarshal DNS message") + } + if !checkResponse(id, query, h, q) { + return dnsmessage.Parser{}, dnsmessage.Header{}, errors.New("invalid DNS response") } - return resp, nil + return p, h, nil } // exchange sends a query on the connection and hopes for a response. -func (r *Resolver) exchange(ctx context.Context, server, name string, qtype uint16, timeout time.Duration) (*dnsMsg, error) { - out := dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - recursion_desired: true, - }, - question: []dnsQuestion{ - {name, qtype, dnsClassINET}, - }, +func (r *Resolver) exchange(ctx context.Context, server string, q dnsmessage.Question, timeout time.Duration) (dnsmessage.Parser, dnsmessage.Header, error) { + q.Class = dnsmessage.ClassINET + id, udpReq, tcpReq, err := newRequest(q) + if err != nil { + return dnsmessage.Parser{}, dnsmessage.Header{}, errors.New("cannot marshal DNS message") } for _, network := range []string{"udp", "tcp"} { - // TODO(mdempsky): Refactor so defers from UDP-based - // exchanges happen before TCP-based exchange. - ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout)) defer cancel() c, err := r.dial(ctx, network, server) if err != nil { - return nil, err + return dnsmessage.Parser{}, dnsmessage.Header{}, err } - defer c.Close() if d, ok := ctx.Deadline(); ok && !d.IsZero() { c.SetDeadline(d) } - out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano()) - in, err := c.dnsRoundTrip(&out) + var p dnsmessage.Parser + var h dnsmessage.Header + if _, ok := c.(PacketConn); ok { + p, h, err = dnsPacketRoundTrip(c, id, q, udpReq) + } else { + p, h, err = dnsStreamRoundTrip(c, id, q, tcpReq) + } + c.Close() if err != nil { - return nil, mapErr(err) + return dnsmessage.Parser{}, dnsmessage.Header{}, mapErr(err) } - if in.truncated { // see RFC 5966 + if err := p.SkipQuestion(); err != dnsmessage.ErrSectionDone { + return dnsmessage.Parser{}, dnsmessage.Header{}, errors.New("invalid DNS response") + } + if h.Truncated { // see RFC 5966 continue } - return in, nil + return p, h, nil + } + return dnsmessage.Parser{}, dnsmessage.Header{}, errors.New("no answer from DNS server") +} + +// checkHeader performs basic sanity checks on the header. +func checkHeader(p *dnsmessage.Parser, h dnsmessage.Header, name, server string) error { + _, err := p.AnswerHeader() + if err != nil && err != dnsmessage.ErrSectionDone { + return &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + + // libresolv continues to the next server when it receives + // an invalid referral response. See golang.org/issue/15434. + if h.RCode == dnsmessage.RCodeSuccess && !h.Authoritative && !h.RecursionAvailable && err == dnsmessage.ErrSectionDone { + return &DNSError{Err: "lame referral", Name: name, Server: server} + } + + if h.RCode != dnsmessage.RCodeSuccess && h.RCode != dnsmessage.RCodeNameError { + // None of the error codes make sense + // for the query we sent. If we didn't get + // a name error and we didn't get success, + // the server is behaving incorrectly or + // having temporary trouble. + err := &DNSError{Err: "server misbehaving", Name: name, Server: server} + if h.RCode == dnsmessage.RCodeServerFailure { + err.IsTemporary = true + } + return err + } + + return nil +} + +func skipToAnswer(p *dnsmessage.Parser, qtype dnsmessage.Type, name, server string) error { + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + return &DNSError{ + Err: errNoSuchHost.Error(), + Name: name, + Server: server, + } + } + if err != nil { + return &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type == qtype { + return nil + } + if err := p.SkipAnswer(); err != nil { + return &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } } - return nil, errors.New("no answer from DNS server") } // Do a lookup for a single name, which must be rooted // (otherwise answer will not find the answers). -func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, error) { +func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype dnsmessage.Type) (dnsmessage.Parser, string, error) { var lastErr error serverOffset := cfg.serverOffset() sLen := uint32(len(cfg.servers)) + n, err := dnsmessage.NewName(name) + if err != nil { + return dnsmessage.Parser{}, "", errors.New("cannot marshal DNS message") + } + q := dnsmessage.Question{ + Name: n, + Type: qtype, + Class: dnsmessage.ClassINET, + } + for i := 0; i < cfg.attempts; i++ { for j := uint32(0); j < sLen; j++ { server := cfg.servers[(serverOffset+j)%sLen] - msg, err := r.exchange(ctx, server, name, qtype, cfg.timeout) + p, h, err := r.exchange(ctx, server, q, cfg.timeout) if err != nil { lastErr = &DNSError{ Err: err.Error(), @@ -175,41 +258,26 @@ func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string, } continue } - // libresolv continues to the next server when it receives - // an invalid referral response. See golang.org/issue/15434. - if msg.rcode == dnsRcodeSuccess && !msg.authoritative && !msg.recursion_available && len(msg.answer) == 0 && len(msg.extra) == 0 { - lastErr = &DNSError{Err: "lame referral", Name: name, Server: server} + + // The name does not exist, so trying another server won't help. + // + // TODO: indicate this in a more obvious way, such as a field on DNSError? + if h.RCode == dnsmessage.RCodeNameError { + return dnsmessage.Parser{}, "", &DNSError{Err: errNoSuchHost.Error(), Name: name, Server: server} + } + + lastErr = checkHeader(&p, h, name, server) + if lastErr != nil { continue } - cname, rrs, err := answer(name, server, msg, qtype) - // If answer errored for rcodes dnsRcodeSuccess or dnsRcodeNameError, - // it means the response in msg was not useful and trying another - // server probably won't help. Return now in those cases. - // TODO: indicate this in a more obvious way, such as a field on DNSError? - if err == nil || msg.rcode == dnsRcodeSuccess || msg.rcode == dnsRcodeNameError { - return cname, rrs, err + + lastErr = skipToAnswer(&p, qtype, name, server) + if lastErr == nil { + return p, server, nil } - lastErr = err } } - return "", nil, lastErr -} - -// addrRecordList converts and returns a list of IP addresses from DNS -// address records (both A and AAAA). Other record types are ignored. -func addrRecordList(rrs []dnsRR) []IPAddr { - addrs := make([]IPAddr, 0, 4) - for _, rr := range rrs { - switch rr := rr.(type) { - case *dnsRR_A: - addrs = append(addrs, IPAddr{IP: IPv4(byte(rr.A>>24), byte(rr.A>>16), byte(rr.A>>8), byte(rr.A))}) - case *dnsRR_AAAA: - ip := make(IP, IPv6len) - copy(ip, rr.AAAA[:]) - addrs = append(addrs, IPAddr{IP: ip}) - } - } - return addrs + return dnsmessage.Parser{}, "", lastErr } // A resolverConfig represents a DNS stub resolver configuration. @@ -287,37 +355,45 @@ func (conf *resolverConfig) releaseSema() { <-conf.ch } -func (r *Resolver) lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs []dnsRR, err error) { +func (r *Resolver) lookup(ctx context.Context, name string, qtype dnsmessage.Type) (dnsmessage.Parser, string, error) { if !isDomainName(name) { // We used to use "invalid domain name" as the error, // but that is a detail of the specific lookup mechanism. // Other lookups might allow broader name syntax // (for example Multicast DNS allows UTF-8; see RFC 6762). // For consistency with libc resolvers, report no such host. - return "", nil, &DNSError{Err: errNoSuchHost.Error(), Name: name} + return dnsmessage.Parser{}, "", &DNSError{Err: errNoSuchHost.Error(), Name: name} } resolvConf.tryUpdate("/etc/resolv.conf") resolvConf.mu.RLock() conf := resolvConf.dnsConfig resolvConf.mu.RUnlock() + var ( + p dnsmessage.Parser + server string + err error + ) for _, fqdn := range conf.nameList(name) { - cname, rrs, err = r.tryOneName(ctx, conf, fqdn, qtype) + p, server, err = r.tryOneName(ctx, conf, fqdn, qtype) if err == nil { break } - if nerr, ok := err.(Error); ok && nerr.Temporary() && r.StrictErrors { + if nerr, ok := err.(Error); ok && nerr.Temporary() && r.strictErrors() { // If we hit a temporary error with StrictErrors enabled, // stop immediately instead of trying more names. break } } + if err == nil { + return p, server, nil + } if err, ok := err.(*DNSError); ok { // Show original name passed to lookup, not suffixed one. // In general we might have tried many suffixes; showing // just one is misleading. See also golang.org/issue/6324. err.Name = name } - return + return dnsmessage.Parser{}, "", err } // avoidDNS reports whether this is a hostname for which we should not @@ -449,48 +525,48 @@ func goLookupIPFiles(name string) (addrs []IPAddr) { // goLookupIP is the native Go implementation of LookupIP. // The libc versions are in cgo_*.go. func (r *Resolver) goLookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) { - order := systemConf().hostLookupOrder(host) + order := systemConf().hostLookupOrder(r, host) addrs, _, err = r.goLookupIPCNAMEOrder(ctx, host, order) return } -func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, cname string, err error) { +func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, cname dnsmessage.Name, err error) { if order == hostLookupFilesDNS || order == hostLookupFiles { addrs = goLookupIPFiles(name) if len(addrs) > 0 || order == hostLookupFiles { - return addrs, name, nil + return addrs, dnsmessage.Name{}, nil } } if !isDomainName(name) { // See comment in func lookup above about use of errNoSuchHost. - return nil, "", &DNSError{Err: errNoSuchHost.Error(), Name: name} + return nil, dnsmessage.Name{}, &DNSError{Err: errNoSuchHost.Error(), Name: name} } resolvConf.tryUpdate("/etc/resolv.conf") resolvConf.mu.RLock() conf := resolvConf.dnsConfig resolvConf.mu.RUnlock() type racer struct { - cname string - rrs []dnsRR + p dnsmessage.Parser + server string error } lane := make(chan racer, 1) - qtypes := [...]uint16{dnsTypeA, dnsTypeAAAA} + qtypes := [...]dnsmessage.Type{dnsmessage.TypeA, dnsmessage.TypeAAAA} var lastErr error for _, fqdn := range conf.nameList(name) { for _, qtype := range qtypes { dnsWaitGroup.Add(1) - go func(qtype uint16) { - defer dnsWaitGroup.Done() - cname, rrs, err := r.tryOneName(ctx, conf, fqdn, qtype) - lane <- racer{cname, rrs, err} + go func(qtype dnsmessage.Type) { + p, server, err := r.tryOneName(ctx, conf, fqdn, qtype) + lane <- racer{p, server, err} + dnsWaitGroup.Done() }(qtype) } hitStrictError := false for range qtypes { racer := <-lane if racer.error != nil { - if nerr, ok := racer.error.(Error); ok && nerr.Temporary() && r.StrictErrors { + if nerr, ok := racer.error.(Error); ok && nerr.Temporary() && r.strictErrors() { // This error will abort the nameList loop. hitStrictError = true lastErr = racer.error @@ -500,9 +576,74 @@ func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order } continue } - addrs = append(addrs, addrRecordList(racer.rrs)...) - if cname == "" { - cname = racer.cname + + // Presotto says it's okay to assume that servers listed in + // /etc/resolv.conf are recursive resolvers. + // + // We asked for recursion, so it should have included all the + // answers we need in this one packet. + // + // Further, RFC 1035 section 4.3.1 says that "the recursive + // response to a query will be... The answer to the query, + // possibly preface by one or more CNAME RRs that specify + // aliases encountered on the way to an answer." + // + // Therefore, we should be able to assume that we can ignore + // CNAMEs and that the A and AAAA records we requested are + // for the canonical name. + + loop: + for { + h, err := racer.p.AnswerHeader() + if err != nil && err != dnsmessage.ErrSectionDone { + lastErr = &DNSError{ + Err: "cannot marshal DNS message", + Name: name, + Server: racer.server, + } + } + if err != nil { + break + } + switch h.Type { + case dnsmessage.TypeA: + a, err := racer.p.AResource() + if err != nil { + lastErr = &DNSError{ + Err: "cannot marshal DNS message", + Name: name, + Server: racer.server, + } + break loop + } + addrs = append(addrs, IPAddr{IP: IP(a.A[:])}) + + case dnsmessage.TypeAAAA: + aaaa, err := racer.p.AAAAResource() + if err != nil { + lastErr = &DNSError{ + Err: "cannot marshal DNS message", + Name: name, + Server: racer.server, + } + break loop + } + addrs = append(addrs, IPAddr{IP: IP(aaaa.AAAA[:])}) + + default: + if err := racer.p.SkipAnswer(); err != nil { + lastErr = &DNSError{ + Err: "cannot marshal DNS message", + Name: name, + Server: racer.server, + } + break loop + } + continue + } + if cname.Length == 0 && h.Name.Length != 0 { + cname = h.Name + } } } if hitStrictError { @@ -528,17 +669,17 @@ func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order addrs = goLookupIPFiles(name) } if len(addrs) == 0 && lastErr != nil { - return nil, "", lastErr + return nil, dnsmessage.Name{}, lastErr } } return addrs, cname, nil } // goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME. -func (r *Resolver) goLookupCNAME(ctx context.Context, host string) (cname string, err error) { - order := systemConf().hostLookupOrder(host) - _, cname, err = r.goLookupIPCNAMEOrder(ctx, host, order) - return +func (r *Resolver) goLookupCNAME(ctx context.Context, host string) (string, error) { + order := systemConf().hostLookupOrder(r, host) + _, cname, err := r.goLookupIPCNAMEOrder(ctx, host, order) + return cname.String(), err } // goLookupPTR is the native Go implementation of LookupAddr. @@ -555,13 +696,36 @@ func (r *Resolver) goLookupPTR(ctx context.Context, addr string) ([]string, erro if err != nil { return nil, err } - _, rrs, err := r.lookup(ctx, arpa, dnsTypePTR) + p, server, err := r.lookup(ctx, arpa, dnsmessage.TypePTR) if err != nil { return nil, err } - ptrs := make([]string, len(rrs)) - for i, rr := range rrs { - ptrs[i] = rr.(*dnsRR_PTR).Ptr + var ptrs []string + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return nil, &DNSError{ + Err: "cannot marshal DNS message", + Name: addr, + Server: server, + } + } + if h.Type != dnsmessage.TypePTR { + continue + } + ptr, err := p.PTRResource() + if err != nil { + return nil, &DNSError{ + Err: "cannot marshal DNS message", + Name: addr, + Server: server, + } + } + ptrs = append(ptrs, ptr.PTR.String()) + } return ptrs, nil } diff --git a/libgo/go/net/dnsclient_unix_test.go b/libgo/go/net/dnsclient_unix_test.go index 9e4015f..f1bb09d 100644 --- a/libgo/go/net/dnsclient_unix_test.go +++ b/libgo/go/net/dnsclient_unix_test.go @@ -19,42 +19,59 @@ import ( "sync" "testing" "time" + + "golang_org/x/net/dns/dnsmessage" ) var goResolver = Resolver{PreferGo: true} // Test address from 192.0.2.0/24 block, reserved by RFC 5737 for documentation. -const TestAddr uint32 = 0xc0000201 +var TestAddr = [4]byte{0xc0, 0x00, 0x02, 0x01} // Test address from 2001:db8::/32 block, reserved by RFC 3849 for documentation. var VarTestAddr6 = [16]byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} +func mustNewName(name string) dnsmessage.Name { + nn, err := dnsmessage.NewName(name) + if err != nil { + panic(fmt.Sprint("creating name: ", err)) + } + return nn +} + +func mustQuestion(name string, qtype dnsmessage.Type, class dnsmessage.Class) dnsmessage.Question { + return dnsmessage.Question{ + Name: mustNewName(name), + Type: qtype, + Class: class, + } +} + var dnsTransportFallbackTests = []struct { - server string - name string - qtype uint16 - timeout int - rcode int + server string + question dnsmessage.Question + timeout int + rcode dnsmessage.RCode }{ // Querying "com." with qtype=255 usually makes an answer // which requires more than 512 bytes. - {"8.8.8.8:53", "com.", dnsTypeALL, 2, dnsRcodeSuccess}, - {"8.8.4.4:53", "com.", dnsTypeALL, 4, dnsRcodeSuccess}, + {"8.8.8.8:53", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), 2, dnsmessage.RCodeSuccess}, + {"8.8.4.4:53", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), 4, dnsmessage.RCodeSuccess}, } func TestDNSTransportFallback(t *testing.T) { fake := fakeDNSServer{ - rh: func(n, _ string, q *dnsMsg, _ time.Time) (*dnsMsg, error) { - r := &dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - id: q.id, - response: true, - rcode: dnsRcodeSuccess, + rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { + r := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.Header.ID, + Response: true, + RCode: dnsmessage.RCodeSuccess, }, - question: q.question, + Questions: q.Questions, } if n == "udp" { - r.truncated = true + r.Header.Truncated = true } return r, nil }, @@ -63,15 +80,13 @@ func TestDNSTransportFallback(t *testing.T) { for _, tt := range dnsTransportFallbackTests { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - msg, err := r.exchange(ctx, tt.server, tt.name, tt.qtype, time.Second) + _, h, err := r.exchange(ctx, tt.server, tt.question, time.Second) if err != nil { t.Error(err) continue } - switch msg.rcode { - case tt.rcode: - default: - t.Errorf("got %v from %v; want %v", msg.rcode, tt.server, tt.rcode) + if h.RCode != tt.rcode { + t.Errorf("got %v from %v; want %v", h.RCode, tt.server, tt.rcode) continue } } @@ -80,39 +95,38 @@ func TestDNSTransportFallback(t *testing.T) { // See RFC 6761 for further information about the reserved, pseudo // domain names. var specialDomainNameTests = []struct { - name string - qtype uint16 - rcode int + question dnsmessage.Question + rcode dnsmessage.RCode }{ // Name resolution APIs and libraries should not recognize the // followings as special. - {"1.0.168.192.in-addr.arpa.", dnsTypePTR, dnsRcodeNameError}, - {"test.", dnsTypeALL, dnsRcodeNameError}, - {"example.com.", dnsTypeALL, dnsRcodeSuccess}, + {mustQuestion("1.0.168.192.in-addr.arpa.", dnsmessage.TypePTR, dnsmessage.ClassINET), dnsmessage.RCodeNameError}, + {mustQuestion("test.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError}, + {mustQuestion("example.com.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeSuccess}, // Name resolution APIs and libraries should recognize the // followings as special and should not send any queries. // Though, we test those names here for verifying negative // answers at DNS query-response interaction level. - {"localhost.", dnsTypeALL, dnsRcodeNameError}, - {"invalid.", dnsTypeALL, dnsRcodeNameError}, + {mustQuestion("localhost.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError}, + {mustQuestion("invalid.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError}, } func TestSpecialDomainName(t *testing.T) { - fake := fakeDNSServer{func(_, _ string, q *dnsMsg, _ time.Time) (*dnsMsg, error) { - r := &dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - id: q.id, - response: true, + fake := fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { + r := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.ID, + Response: true, }, - question: q.question, + Questions: q.Questions, } - switch q.question[0].Name { + switch q.Questions[0].Name.String() { case "example.com.": - r.rcode = dnsRcodeSuccess + r.Header.RCode = dnsmessage.RCodeSuccess default: - r.rcode = dnsRcodeNameError + r.Header.RCode = dnsmessage.RCodeNameError } return r, nil @@ -122,15 +136,13 @@ func TestSpecialDomainName(t *testing.T) { for _, tt := range specialDomainNameTests { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - msg, err := r.exchange(ctx, server, tt.name, tt.qtype, 3*time.Second) + _, h, err := r.exchange(ctx, server, tt.question, 3*time.Second) if err != nil { t.Error(err) continue } - switch msg.rcode { - case tt.rcode, dnsRcodeServerFailure: - default: - t.Errorf("got %v from %v; want %v", msg.rcode, server, tt.rcode) + if h.RCode != tt.rcode { + t.Errorf("got %v from %v; want %v", h.RCode, server, tt.rcode) continue } } @@ -177,24 +189,26 @@ func TestAvoidDNSName(t *testing.T) { } } -var fakeDNSServerSuccessful = fakeDNSServer{func(_, _ string, q *dnsMsg, _ time.Time) (*dnsMsg, error) { - r := &dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - id: q.id, - response: true, +var fakeDNSServerSuccessful = fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { + r := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.ID, + Response: true, }, - question: q.question, - } - if len(q.question) == 1 && q.question[0].Qtype == dnsTypeA { - r.answer = []dnsRR{ - &dnsRR_A{ - Hdr: dnsRR_Header{ - Name: q.question[0].Name, - Rrtype: dnsTypeA, - Class: dnsClassINET, - Rdlength: 4, + Questions: q.Questions, + } + if len(q.Questions) == 1 && q.Questions[0].Type == dnsmessage.TypeA { + r.Answers = []dnsmessage.Resource{ + { + Header: dnsmessage.ResourceHeader{ + Name: q.Questions[0].Name, + Type: dnsmessage.TypeA, + Class: dnsmessage.ClassINET, + Length: 4, + }, + Body: &dnsmessage.AResource{ + A: TestAddr, }, - A: TestAddr, }, } } @@ -459,54 +473,57 @@ var goLookupIPWithResolverConfigTests = []struct { func TestGoLookupIPWithResolverConfig(t *testing.T) { defer dnsWaitGroup.Wait() - - fake := fakeDNSServer{func(n, s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) { + fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { switch s { case "[2001:4860:4860::8888]:53", "8.8.8.8:53": break default: time.Sleep(10 * time.Millisecond) - return nil, poll.ErrTimeout + return dnsmessage.Message{}, poll.ErrTimeout } - r := &dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - id: q.id, - response: true, + r := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.ID, + Response: true, }, - question: q.question, + Questions: q.Questions, } - for _, question := range q.question { - switch question.Qtype { - case dnsTypeA: - switch question.Name { + for _, question := range q.Questions { + switch question.Type { + case dnsmessage.TypeA: + switch question.Name.String() { case "hostname.as112.net.": break case "ipv4.google.com.": - r.answer = append(r.answer, &dnsRR_A{ - Hdr: dnsRR_Header{ - Name: q.question[0].Name, - Rrtype: dnsTypeA, - Class: dnsClassINET, - Rdlength: 4, + r.Answers = append(r.Answers, dnsmessage.Resource{ + Header: dnsmessage.ResourceHeader{ + Name: q.Questions[0].Name, + Type: dnsmessage.TypeA, + Class: dnsmessage.ClassINET, + Length: 4, + }, + Body: &dnsmessage.AResource{ + A: TestAddr, }, - A: TestAddr, }) default: } - case dnsTypeAAAA: - switch question.Name { + case dnsmessage.TypeAAAA: + switch question.Name.String() { case "hostname.as112.net.": break case "ipv6.google.com.": - r.answer = append(r.answer, &dnsRR_AAAA{ - Hdr: dnsRR_Header{ - Name: q.question[0].Name, - Rrtype: dnsTypeAAAA, - Class: dnsClassINET, - Rdlength: 16, + r.Answers = append(r.Answers, dnsmessage.Resource{ + Header: dnsmessage.ResourceHeader{ + Name: q.Questions[0].Name, + Type: dnsmessage.TypeAAAA, + Class: dnsmessage.ClassINET, + Length: 16, + }, + Body: &dnsmessage.AAAAResource{ + AAAA: VarTestAddr6, }, - AAAA: VarTestAddr6, }) } } @@ -554,13 +571,13 @@ func TestGoLookupIPWithResolverConfig(t *testing.T) { func TestGoLookupIPOrderFallbackToFile(t *testing.T) { defer dnsWaitGroup.Wait() - fake := fakeDNSServer{func(n, s string, q *dnsMsg, tm time.Time) (*dnsMsg, error) { - r := &dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - id: q.id, - response: true, + fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, tm time.Time) (dnsmessage.Message, error) { + r := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.ID, + Response: true, }, - question: q.question, + Questions: q.Questions, } return r, nil }} @@ -624,20 +641,20 @@ func TestErrorForOriginalNameWhenSearching(t *testing.T) { t.Fatal(err) } - fake := fakeDNSServer{func(_, _ string, q *dnsMsg, _ time.Time) (*dnsMsg, error) { - r := &dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - id: q.id, - response: true, + fake := fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { + r := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.ID, + Response: true, }, - question: q.question, + Questions: q.Questions, } - switch q.question[0].Name { + switch q.Questions[0].Name.String() { case fqdn + ".servfail.": - r.rcode = dnsRcodeServerFailure + r.Header.RCode = dnsmessage.RCodeServerFailure default: - r.rcode = dnsRcodeNameError + r.Header.RCode = dnsmessage.RCodeNameError } return r, nil @@ -679,28 +696,30 @@ func TestIgnoreLameReferrals(t *testing.T) { t.Fatal(err) } - fake := fakeDNSServer{func(_, s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) { + fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { t.Log(s, q) - r := &dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - id: q.id, - response: true, + r := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.ID, + Response: true, }, - question: q.question, + Questions: q.Questions, } if s == "192.0.2.2:53" { - r.recursion_available = true - if q.question[0].Qtype == dnsTypeA { - r.answer = []dnsRR{ - &dnsRR_A{ - Hdr: dnsRR_Header{ - Name: q.question[0].Name, - Rrtype: dnsTypeA, - Class: dnsClassINET, - Rdlength: 4, + r.Header.RecursionAvailable = true + if q.Questions[0].Type == dnsmessage.TypeA { + r.Answers = []dnsmessage.Resource{ + { + Header: dnsmessage.ResourceHeader{ + Name: q.Questions[0].Name, + Type: dnsmessage.TypeA, + Class: dnsmessage.ClassINET, + Length: 4, + }, + Body: &dnsmessage.AResource{ + A: TestAddr, }, - A: TestAddr, }, } } @@ -727,6 +746,7 @@ func TestIgnoreLameReferrals(t *testing.T) { func BenchmarkGoLookupIP(b *testing.B) { testHookUninstaller.Do(uninstallTestHooks) ctx := context.Background() + b.ReportAllocs() for i := 0; i < b.N; i++ { goResolver.LookupIPAddr(ctx, "www.example.com") @@ -736,6 +756,7 @@ func BenchmarkGoLookupIP(b *testing.B) { func BenchmarkGoLookupIPNoSuchHost(b *testing.B) { testHookUninstaller.Do(uninstallTestHooks) ctx := context.Background() + b.ReportAllocs() for i := 0; i < b.N; i++ { goResolver.LookupIPAddr(ctx, "some.nonexistent") @@ -759,6 +780,7 @@ func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) { b.Fatal(err) } ctx := context.Background() + b.ReportAllocs() for i := 0; i < b.N; i++ { goResolver.LookupIPAddr(ctx, "www.example.com") @@ -766,20 +788,26 @@ func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) { } type fakeDNSServer struct { - rh func(n, s string, q *dnsMsg, t time.Time) (*dnsMsg, error) + rh func(n, s string, q dnsmessage.Message, t time.Time) (dnsmessage.Message, error) + alwaysTCP bool } func (server *fakeDNSServer) DialContext(_ context.Context, n, s string) (Conn, error) { - return &fakeDNSConn{nil, server, n, s, nil, time.Time{}}, nil + if server.alwaysTCP || n == "tcp" || n == "tcp4" || n == "tcp6" { + return &fakeDNSConn{tcp: true, server: server, n: n, s: s}, nil + } + return &fakeDNSPacketConn{fakeDNSConn: fakeDNSConn{tcp: false, server: server, n: n, s: s}}, nil } type fakeDNSConn struct { Conn + tcp bool server *fakeDNSServer n string s string - q *dnsMsg + q dnsmessage.Message t time.Time + buf []byte } func (f *fakeDNSConn) Close() error { @@ -787,15 +815,32 @@ func (f *fakeDNSConn) Close() error { } func (f *fakeDNSConn) Read(b []byte) (int, error) { + if len(f.buf) > 0 { + n := copy(b, f.buf) + f.buf = f.buf[n:] + return n, nil + } + resp, err := f.server.rh(f.n, f.s, f.q, f.t) if err != nil { return 0, err } - bb, ok := resp.Pack() - if !ok { - return 0, errors.New("cannot marshal DNS message") + bb := make([]byte, 2, 514) + bb, err = resp.AppendPack(bb) + if err != nil { + return 0, fmt.Errorf("cannot marshal DNS message: %v", err) + } + + if f.tcp { + l := len(bb) - 2 + bb[0] = byte(l >> 8) + bb[1] = byte(l) + f.buf = bb + return f.Read(b) } + + bb = bb[2:] if len(b) < len(bb) { return 0, errors.New("read would fragment DNS message") } @@ -804,27 +849,34 @@ func (f *fakeDNSConn) Read(b []byte) (int, error) { return len(bb), nil } -func (f *fakeDNSConn) ReadFrom(b []byte) (int, Addr, error) { - return 0, nil, nil -} - func (f *fakeDNSConn) Write(b []byte) (int, error) { - f.q = new(dnsMsg) - if !f.q.Unpack(b) { - return 0, errors.New("cannot unmarshal DNS message") + if f.tcp && len(b) >= 2 { + b = b[2:] + } + if f.q.Unpack(b) != nil { + return 0, fmt.Errorf("cannot unmarshal DNS message fake %s (%d)", f.n, len(b)) } return len(b), nil } -func (f *fakeDNSConn) WriteTo(b []byte, addr Addr) (int, error) { - return 0, nil -} - func (f *fakeDNSConn) SetDeadline(t time.Time) error { f.t = t return nil } +type fakeDNSPacketConn struct { + PacketConn + fakeDNSConn +} + +func (f *fakeDNSPacketConn) SetDeadline(t time.Time) error { + return f.fakeDNSConn.SetDeadline(t) +} + +func (f *fakeDNSPacketConn) Close() error { + return f.fakeDNSConn.Close() +} + // UDP round-tripper algorithm should ignore invalid DNS responses (issue 13281). func TestIgnoreDNSForgeries(t *testing.T) { c, s := Pipe() @@ -836,64 +888,75 @@ func TestIgnoreDNSForgeries(t *testing.T) { return } - msg := &dnsMsg{} - if !msg.Unpack(b[:n]) { - t.Error("invalid DNS query") + var msg dnsmessage.Message + if msg.Unpack(b[:n]) != nil { + t.Error("invalid DNS query:", err) return } s.Write([]byte("garbage DNS response packet")) - msg.response = true - msg.id++ // make invalid ID - b, ok := msg.Pack() - if !ok { - t.Error("failed to pack DNS response") + msg.Header.Response = true + msg.Header.ID++ // make invalid ID + + if b, err = msg.Pack(); err != nil { + t.Error("failed to pack DNS response:", err) return } s.Write(b) - msg.id-- // restore original ID - msg.answer = []dnsRR{ - &dnsRR_A{ - Hdr: dnsRR_Header{ - Name: "www.example.com.", - Rrtype: dnsTypeA, - Class: dnsClassINET, - Rdlength: 4, + msg.Header.ID-- // restore original ID + msg.Answers = []dnsmessage.Resource{ + { + Header: dnsmessage.ResourceHeader{ + Name: mustNewName("www.example.com."), + Type: dnsmessage.TypeA, + Class: dnsmessage.ClassINET, + Length: 4, + }, + Body: &dnsmessage.AResource{ + A: TestAddr, }, - A: TestAddr, }, } - b, ok = msg.Pack() - if !ok { - t.Error("failed to pack DNS response") + b, err = msg.Pack() + if err != nil { + t.Error("failed to pack DNS response:", err) return } s.Write(b) }() - msg := &dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - id: 42, + msg := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: 42, }, - question: []dnsQuestion{ + Questions: []dnsmessage.Question{ { - Name: "www.example.com.", - Qtype: dnsTypeA, - Qclass: dnsClassINET, + Name: mustNewName("www.example.com."), + Type: dnsmessage.TypeA, + Class: dnsmessage.ClassINET, }, }, } - dc := &dnsPacketConn{c} - resp, err := dc.dnsRoundTrip(msg) + b, err := msg.Pack() + if err != nil { + t.Fatal("Pack failed:", err) + } + + p, _, err := dnsPacketRoundTrip(c, 42, msg.Questions[0], b) if err != nil { - t.Fatalf("dnsRoundTripUDP failed: %v", err) + t.Fatalf("dnsPacketRoundTrip failed: %v", err) } - if got := resp.answer[0].(*dnsRR_A).A; got != TestAddr { + p.SkipAllQuestions() + as, err := p.AllAnswers() + if err != nil { + t.Fatal("AllAnswers failed:", err) + } + if got := as[0].Body.(*dnsmessage.AResource).A; got != TestAddr { t.Errorf("got address %v, want %v", got, TestAddr) } } @@ -918,7 +981,7 @@ func TestRetryTimeout(t *testing.T) { var deadline0 time.Time - fake := fakeDNSServer{func(_, s string, q *dnsMsg, deadline time.Time) (*dnsMsg, error) { + fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) { t.Log(s, q, deadline) if deadline.IsZero() { @@ -928,7 +991,7 @@ func TestRetryTimeout(t *testing.T) { if s == "192.0.2.1:53" { deadline0 = deadline time.Sleep(10 * time.Millisecond) - return nil, poll.ErrTimeout + return dnsmessage.Message{}, poll.ErrTimeout } if deadline.Equal(deadline0) { @@ -979,7 +1042,7 @@ func testRotate(t *testing.T, rotate bool, nameservers, wantServers []string) { } var usedServers []string - fake := fakeDNSServer{func(_, s string, q *dnsMsg, deadline time.Time) (*dnsMsg, error) { + fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) { usedServers = append(usedServers, s) return mockTXTResponse(q), nil }} @@ -997,22 +1060,24 @@ func testRotate(t *testing.T, rotate bool, nameservers, wantServers []string) { } } -func mockTXTResponse(q *dnsMsg) *dnsMsg { - r := &dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - id: q.id, - response: true, - recursion_available: true, +func mockTXTResponse(q dnsmessage.Message) dnsmessage.Message { + r := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.ID, + Response: true, + RecursionAvailable: true, }, - question: q.question, - answer: []dnsRR{ - &dnsRR_TXT{ - Hdr: dnsRR_Header{ - Name: q.question[0].Name, - Rrtype: dnsTypeTXT, - Class: dnsClassINET, + Questions: q.Questions, + Answers: []dnsmessage.Resource{ + { + Header: dnsmessage.ResourceHeader{ + Name: q.Questions[0].Name, + Type: dnsmessage.TypeTXT, + Class: dnsmessage.ClassINET, + }, + Body: &dnsmessage.TXTResource{ + TXT: []string{"ok"}, }, - Txt: "ok", }, }, } @@ -1080,22 +1145,22 @@ func TestStrictErrorsLookupIP(t *testing.T) { cases := []struct { desc string - resolveWhich func(quest *dnsQuestion) resolveWhichEnum + resolveWhich func(quest dnsmessage.Question) resolveWhichEnum wantStrictErr error wantLaxErr error wantIPs []string }{ { desc: "No errors", - resolveWhich: func(quest *dnsQuestion) resolveWhichEnum { + resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { return resolveOK }, wantIPs: []string{ip4, ip6}, }, { desc: "searchX error fails in strict mode", - resolveWhich: func(quest *dnsQuestion) resolveWhichEnum { - if quest.Name == searchX { + resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { + if quest.Name.String() == searchX { return resolveTimeout } return resolveOK @@ -1105,8 +1170,8 @@ func TestStrictErrorsLookupIP(t *testing.T) { }, { desc: "searchX IPv4-only timeout fails in strict mode", - resolveWhich: func(quest *dnsQuestion) resolveWhichEnum { - if quest.Name == searchX && quest.Qtype == dnsTypeA { + resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { + if quest.Name.String() == searchX && quest.Type == dnsmessage.TypeA { return resolveTimeout } return resolveOK @@ -1116,8 +1181,8 @@ func TestStrictErrorsLookupIP(t *testing.T) { }, { desc: "searchX IPv6-only servfail fails in strict mode", - resolveWhich: func(quest *dnsQuestion) resolveWhichEnum { - if quest.Name == searchX && quest.Qtype == dnsTypeAAAA { + resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { + if quest.Name.String() == searchX && quest.Type == dnsmessage.TypeAAAA { return resolveServfail } return resolveOK @@ -1127,8 +1192,8 @@ func TestStrictErrorsLookupIP(t *testing.T) { }, { desc: "searchY error always fails", - resolveWhich: func(quest *dnsQuestion) resolveWhichEnum { - if quest.Name == searchY { + resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { + if quest.Name.String() == searchY { return resolveTimeout } return resolveOK @@ -1138,8 +1203,8 @@ func TestStrictErrorsLookupIP(t *testing.T) { }, { desc: "searchY IPv4-only socket error fails in strict mode", - resolveWhich: func(quest *dnsQuestion) resolveWhichEnum { - if quest.Name == searchY && quest.Qtype == dnsTypeA { + resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { + if quest.Name.String() == searchY && quest.Type == dnsmessage.TypeA { return resolveOpError } return resolveOK @@ -1149,8 +1214,8 @@ func TestStrictErrorsLookupIP(t *testing.T) { }, { desc: "searchY IPv6-only timeout fails in strict mode", - resolveWhich: func(quest *dnsQuestion) resolveWhichEnum { - if quest.Name == searchY && quest.Qtype == dnsTypeAAAA { + resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { + if quest.Name.String() == searchY && quest.Type == dnsmessage.TypeAAAA { return resolveTimeout } return resolveOK @@ -1161,80 +1226,84 @@ func TestStrictErrorsLookupIP(t *testing.T) { } for i, tt := range cases { - fake := fakeDNSServer{func(_, s string, q *dnsMsg, deadline time.Time) (*dnsMsg, error) { + fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) { t.Log(s, q) - switch tt.resolveWhich(&q.question[0]) { + switch tt.resolveWhich(q.Questions[0]) { case resolveOK: // Handle below. case resolveOpError: - return nil, &OpError{Op: "write", Err: fmt.Errorf("socket on fire")} + return dnsmessage.Message{}, &OpError{Op: "write", Err: fmt.Errorf("socket on fire")} case resolveServfail: - return &dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - id: q.id, - response: true, - rcode: dnsRcodeServerFailure, + return dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.ID, + Response: true, + RCode: dnsmessage.RCodeServerFailure, }, - question: q.question, + Questions: q.Questions, }, nil case resolveTimeout: - return nil, poll.ErrTimeout + return dnsmessage.Message{}, poll.ErrTimeout default: t.Fatal("Impossible resolveWhich") } - switch q.question[0].Name { + switch q.Questions[0].Name.String() { case searchX, name + ".": // Return NXDOMAIN to utilize the search list. - return &dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - id: q.id, - response: true, - rcode: dnsRcodeNameError, + return dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.ID, + Response: true, + RCode: dnsmessage.RCodeNameError, }, - question: q.question, + Questions: q.Questions, }, nil case searchY: // Return records below. default: - return nil, fmt.Errorf("Unexpected Name: %v", q.question[0].Name) + return dnsmessage.Message{}, fmt.Errorf("Unexpected Name: %v", q.Questions[0].Name) } - r := &dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - id: q.id, - response: true, + r := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.ID, + Response: true, }, - question: q.question, + Questions: q.Questions, } - switch q.question[0].Qtype { - case dnsTypeA: - r.answer = []dnsRR{ - &dnsRR_A{ - Hdr: dnsRR_Header{ - Name: q.question[0].Name, - Rrtype: dnsTypeA, - Class: dnsClassINET, - Rdlength: 4, + switch q.Questions[0].Type { + case dnsmessage.TypeA: + r.Answers = []dnsmessage.Resource{ + { + Header: dnsmessage.ResourceHeader{ + Name: q.Questions[0].Name, + Type: dnsmessage.TypeA, + Class: dnsmessage.ClassINET, + Length: 4, + }, + Body: &dnsmessage.AResource{ + A: TestAddr, }, - A: TestAddr, }, } - case dnsTypeAAAA: - r.answer = []dnsRR{ - &dnsRR_AAAA{ - Hdr: dnsRR_Header{ - Name: q.question[0].Name, - Rrtype: dnsTypeAAAA, - Class: dnsClassINET, - Rdlength: 16, + case dnsmessage.TypeAAAA: + r.Answers = []dnsmessage.Resource{ + { + Header: dnsmessage.ResourceHeader{ + Name: q.Questions[0].Name, + Type: dnsmessage.TypeAAAA, + Class: dnsmessage.ClassINET, + Length: 16, + }, + Body: &dnsmessage.AAAAResource{ + AAAA: VarTestAddr6, }, - AAAA: VarTestAddr6, }, } default: - return nil, fmt.Errorf("Unexpected Qtype: %v", q.question[0].Qtype) + return dnsmessage.Message{}, fmt.Errorf("Unexpected Type: %v", q.Questions[0].Type) } return r, nil }} @@ -1295,22 +1364,22 @@ func TestStrictErrorsLookupTXT(t *testing.T) { const searchY = "test.y.golang.org." const txt = "Hello World" - fake := fakeDNSServer{func(_, s string, q *dnsMsg, deadline time.Time) (*dnsMsg, error) { + fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) { t.Log(s, q) - switch q.question[0].Name { + switch q.Questions[0].Name.String() { case searchX: - return nil, poll.ErrTimeout + return dnsmessage.Message{}, poll.ErrTimeout case searchY: return mockTXTResponse(q), nil default: - return nil, fmt.Errorf("Unexpected Name: %v", q.question[0].Name) + return dnsmessage.Message{}, fmt.Errorf("Unexpected Name: %v", q.Questions[0].Name) } }} for _, strict := range []bool{true, false} { r := Resolver{StrictErrors: strict, Dial: fake.DialContext} - _, rrs, err := r.lookup(context.Background(), name, dnsTypeTXT) + p, _, err := r.lookup(context.Background(), name, dnsmessage.TypeTXT) var wantErr error var wantRRs int if strict { @@ -1326,8 +1395,12 @@ func TestStrictErrorsLookupTXT(t *testing.T) { if !reflect.DeepEqual(err, wantErr) { t.Errorf("strict=%v: got err %#v; want %#v", strict, err, wantErr) } - if len(rrs) != wantRRs { - t.Errorf("strict=%v: got %v; want %v", strict, len(rrs), wantRRs) + a, err := p.AllAnswers() + if err != nil { + a = nil + } + if len(a) != wantRRs { + t.Errorf("strict=%v: got %v; want %v", strict, len(a), wantRRs) } } } @@ -1337,9 +1410,9 @@ func TestStrictErrorsLookupTXT(t *testing.T) { func TestDNSGoroutineRace(t *testing.T) { defer dnsWaitGroup.Wait() - fake := fakeDNSServer{func(n, s string, q *dnsMsg, t time.Time) (*dnsMsg, error) { + fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, t time.Time) (dnsmessage.Message, error) { time.Sleep(10 * time.Microsecond) - return nil, poll.ErrTimeout + return dnsmessage.Message{}, poll.ErrTimeout }} r := Resolver{PreferGo: true, Dial: fake.DialContext} @@ -1353,3 +1426,112 @@ func TestDNSGoroutineRace(t *testing.T) { t.Fatal("fake DNS lookup unexpectedly succeeded") } } + +// Issue 8434: verify that Temporary returns true on an error when rcode +// is SERVFAIL +func TestIssue8434(t *testing.T) { + msg := dnsmessage.Message{ + Header: dnsmessage.Header{ + RCode: dnsmessage.RCodeServerFailure, + }, + } + b, err := msg.Pack() + if err != nil { + t.Fatal("Pack failed:", err) + } + var p dnsmessage.Parser + h, err := p.Start(b) + if err != nil { + t.Fatal("Start failed:", err) + } + if err := p.SkipAllQuestions(); err != nil { + t.Fatal("SkipAllQuestions failed:", err) + } + + err = checkHeader(&p, h, "golang.org", "foo:53") + if err == nil { + t.Fatal("expected an error") + } + if ne, ok := err.(Error); !ok { + t.Fatalf("err = %#v; wanted something supporting net.Error", err) + } else if !ne.Temporary() { + t.Fatalf("Temporary = false for err = %#v; want Temporary == true", err) + } + if de, ok := err.(*DNSError); !ok { + t.Fatalf("err = %#v; wanted a *net.DNSError", err) + } else if !de.IsTemporary { + t.Fatalf("IsTemporary = false for err = %#v; want IsTemporary == true", err) + } +} + +// Issue 12778: verify that NXDOMAIN without RA bit errors as +// "no such host" and not "server misbehaving" +// +// Issue 25336: verify that NXDOMAIN errors fail fast. +func TestIssue12778(t *testing.T) { + lookups := 0 + fake := fakeDNSServer{ + rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { + lookups++ + return dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.ID, + Response: true, + RCode: dnsmessage.RCodeNameError, + RecursionAvailable: false, + }, + Questions: q.Questions, + }, nil + }, + } + r := Resolver{PreferGo: true, Dial: fake.DialContext} + + resolvConf.mu.RLock() + conf := resolvConf.dnsConfig + resolvConf.mu.RUnlock() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + _, _, err := r.tryOneName(ctx, conf, ".", dnsmessage.TypeALL) + + if lookups != 1 { + t.Errorf("got %d lookups, wanted 1", lookups) + } + + if err == nil { + t.Fatal("expected an error") + } + de, ok := err.(*DNSError) + if !ok { + t.Fatalf("err = %#v; wanted a *net.DNSError", err) + } + if de.Err != errNoSuchHost.Error() { + t.Fatalf("Err = %#v; wanted %q", de.Err, errNoSuchHost.Error()) + } +} + +// Issue 26573: verify that Conns that don't implement PacketConn are treated +// as streams even when udp was requested. +func TestDNSDialTCP(t *testing.T) { + fake := fakeDNSServer{ + rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { + r := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.Header.ID, + Response: true, + RCode: dnsmessage.RCodeSuccess, + }, + Questions: q.Questions, + } + return r, nil + }, + alwaysTCP: true, + } + r := Resolver{PreferGo: true, Dial: fake.DialContext} + ctx := context.Background() + _, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second) + if err != nil { + t.Fatal("exhange failed:", err) + } +} diff --git a/libgo/go/net/dnsconfig_unix.go b/libgo/go/net/dnsconfig_unix.go index 24487af..8ae5de6 100644 --- a/libgo/go/net/dnsconfig_unix.go +++ b/libgo/go/net/dnsconfig_unix.go @@ -73,7 +73,7 @@ func dnsReadConfig(filename string) *dnsConfig { // to look it up. if parseIPv4(f[1]) != nil { conf.servers = append(conf.servers, JoinHostPort(f[1], "53")) - } else if ip, _ := parseIPv6(f[1], true); ip != nil { + } else if ip, _ := parseIPv6Zone(f[1]); ip != nil { conf.servers = append(conf.servers, JoinHostPort(f[1], "53")) } } @@ -121,7 +121,7 @@ func dnsReadConfig(filename string) *dnsConfig { case "lookup": // OpenBSD option: - // http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5 + // https://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5 // "the legal space-separated values are: bind, file, yp" conf.lookup = f[1:] diff --git a/libgo/go/net/dnsmsg.go b/libgo/go/net/dnsmsg.go deleted file mode 100644 index 8f6c7b6..0000000 --- a/libgo/go/net/dnsmsg.go +++ /dev/null @@ -1,884 +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. - -// DNS packet assembly. See RFC 1035. -// -// This is intended to support name resolution during Dial. -// It doesn't have to be blazing fast. -// -// Each message structure has a Walk method that is used by -// a generic pack/unpack routine. Thus, if in the future we need -// to define new message structs, no new pack/unpack/printing code -// needs to be written. -// -// The first half of this file defines the DNS message formats. -// The second half implements the conversion to and from wire format. -// A few of the structure elements have string tags to aid the -// generic pack/unpack routines. -// -// TODO(rsc): There are enough names defined in this file that they're all -// prefixed with dns. Perhaps put this in its own package later. - -package net - -// Packet formats - -// Wire constants. -const ( - // valid dnsRR_Header.Rrtype and dnsQuestion.qtype - dnsTypeA = 1 - dnsTypeNS = 2 - dnsTypeMD = 3 - dnsTypeMF = 4 - dnsTypeCNAME = 5 - dnsTypeSOA = 6 - dnsTypeMB = 7 - dnsTypeMG = 8 - dnsTypeMR = 9 - dnsTypeNULL = 10 - dnsTypeWKS = 11 - dnsTypePTR = 12 - dnsTypeHINFO = 13 - dnsTypeMINFO = 14 - dnsTypeMX = 15 - dnsTypeTXT = 16 - dnsTypeAAAA = 28 - dnsTypeSRV = 33 - - // valid dnsQuestion.qtype only - dnsTypeAXFR = 252 - dnsTypeMAILB = 253 - dnsTypeMAILA = 254 - dnsTypeALL = 255 - - // valid dnsQuestion.qclass - dnsClassINET = 1 - dnsClassCSNET = 2 - dnsClassCHAOS = 3 - dnsClassHESIOD = 4 - dnsClassANY = 255 - - // dnsMsg.rcode - dnsRcodeSuccess = 0 - dnsRcodeFormatError = 1 - dnsRcodeServerFailure = 2 - dnsRcodeNameError = 3 - dnsRcodeNotImplemented = 4 - dnsRcodeRefused = 5 -) - -// A dnsStruct describes how to iterate over its fields to emulate -// reflective marshaling. -type dnsStruct interface { - // Walk iterates over fields of a structure and calls f - // with a reference to that field, the name of the field - // and a tag ("", "domain", "ipv4", "ipv6") specifying - // particular encodings. Possible concrete types - // for v are *uint16, *uint32, *string, or []byte, and - // *int, *bool in the case of dnsMsgHdr. - // Whenever f returns false, Walk must stop and return - // false, and otherwise return true. - Walk(f func(v interface{}, name, tag string) (ok bool)) (ok bool) -} - -// The wire format for the DNS packet header. -type dnsHeader struct { - Id uint16 - Bits uint16 - Qdcount, Ancount, Nscount, Arcount uint16 -} - -func (h *dnsHeader) Walk(f func(v interface{}, name, tag string) bool) bool { - return f(&h.Id, "Id", "") && - f(&h.Bits, "Bits", "") && - f(&h.Qdcount, "Qdcount", "") && - f(&h.Ancount, "Ancount", "") && - f(&h.Nscount, "Nscount", "") && - f(&h.Arcount, "Arcount", "") -} - -const ( - // dnsHeader.Bits - _QR = 1 << 15 // query/response (response=1) - _AA = 1 << 10 // authoritative - _TC = 1 << 9 // truncated - _RD = 1 << 8 // recursion desired - _RA = 1 << 7 // recursion available -) - -// DNS queries. -type dnsQuestion struct { - Name string - Qtype uint16 - Qclass uint16 -} - -func (q *dnsQuestion) Walk(f func(v interface{}, name, tag string) bool) bool { - return f(&q.Name, "Name", "domain") && - f(&q.Qtype, "Qtype", "") && - f(&q.Qclass, "Qclass", "") -} - -// DNS responses (resource records). -// There are many types of messages, -// but they all share the same header. -type dnsRR_Header struct { - Name string - Rrtype uint16 - Class uint16 - Ttl uint32 - Rdlength uint16 // length of data after header -} - -func (h *dnsRR_Header) Header() *dnsRR_Header { - return h -} - -func (h *dnsRR_Header) Walk(f func(v interface{}, name, tag string) bool) bool { - return f(&h.Name, "Name", "domain") && - f(&h.Rrtype, "Rrtype", "") && - f(&h.Class, "Class", "") && - f(&h.Ttl, "Ttl", "") && - f(&h.Rdlength, "Rdlength", "") -} - -type dnsRR interface { - dnsStruct - Header() *dnsRR_Header -} - -// Specific DNS RR formats for each query type. - -type dnsRR_CNAME struct { - Hdr dnsRR_Header - Cname string -} - -func (rr *dnsRR_CNAME) Header() *dnsRR_Header { - return &rr.Hdr -} - -func (rr *dnsRR_CNAME) Walk(f func(v interface{}, name, tag string) bool) bool { - return rr.Hdr.Walk(f) && f(&rr.Cname, "Cname", "domain") -} - -type dnsRR_MX struct { - Hdr dnsRR_Header - Pref uint16 - Mx string -} - -func (rr *dnsRR_MX) Header() *dnsRR_Header { - return &rr.Hdr -} - -func (rr *dnsRR_MX) Walk(f func(v interface{}, name, tag string) bool) bool { - return rr.Hdr.Walk(f) && f(&rr.Pref, "Pref", "") && f(&rr.Mx, "Mx", "domain") -} - -type dnsRR_NS struct { - Hdr dnsRR_Header - Ns string -} - -func (rr *dnsRR_NS) Header() *dnsRR_Header { - return &rr.Hdr -} - -func (rr *dnsRR_NS) Walk(f func(v interface{}, name, tag string) bool) bool { - return rr.Hdr.Walk(f) && f(&rr.Ns, "Ns", "domain") -} - -type dnsRR_PTR struct { - Hdr dnsRR_Header - Ptr string -} - -func (rr *dnsRR_PTR) Header() *dnsRR_Header { - return &rr.Hdr -} - -func (rr *dnsRR_PTR) Walk(f func(v interface{}, name, tag string) bool) bool { - return rr.Hdr.Walk(f) && f(&rr.Ptr, "Ptr", "domain") -} - -type dnsRR_SOA struct { - Hdr dnsRR_Header - Ns string - Mbox string - Serial uint32 - Refresh uint32 - Retry uint32 - Expire uint32 - Minttl uint32 -} - -func (rr *dnsRR_SOA) Header() *dnsRR_Header { - return &rr.Hdr -} - -func (rr *dnsRR_SOA) Walk(f func(v interface{}, name, tag string) bool) bool { - return rr.Hdr.Walk(f) && - f(&rr.Ns, "Ns", "domain") && - f(&rr.Mbox, "Mbox", "domain") && - f(&rr.Serial, "Serial", "") && - f(&rr.Refresh, "Refresh", "") && - f(&rr.Retry, "Retry", "") && - f(&rr.Expire, "Expire", "") && - f(&rr.Minttl, "Minttl", "") -} - -type dnsRR_TXT struct { - Hdr dnsRR_Header - Txt string // not domain name -} - -func (rr *dnsRR_TXT) Header() *dnsRR_Header { - return &rr.Hdr -} - -func (rr *dnsRR_TXT) Walk(f func(v interface{}, name, tag string) bool) bool { - if !rr.Hdr.Walk(f) { - return false - } - var n uint16 = 0 - for n < rr.Hdr.Rdlength { - var txt string - if !f(&txt, "Txt", "") { - return false - } - // more bytes than rr.Hdr.Rdlength said there would be - if rr.Hdr.Rdlength-n < uint16(len(txt))+1 { - return false - } - n += uint16(len(txt)) + 1 - rr.Txt += txt - } - return true -} - -type dnsRR_SRV struct { - Hdr dnsRR_Header - Priority uint16 - Weight uint16 - Port uint16 - Target string -} - -func (rr *dnsRR_SRV) Header() *dnsRR_Header { - return &rr.Hdr -} - -func (rr *dnsRR_SRV) Walk(f func(v interface{}, name, tag string) bool) bool { - return rr.Hdr.Walk(f) && - f(&rr.Priority, "Priority", "") && - f(&rr.Weight, "Weight", "") && - f(&rr.Port, "Port", "") && - f(&rr.Target, "Target", "domain") -} - -type dnsRR_A struct { - Hdr dnsRR_Header - A uint32 -} - -func (rr *dnsRR_A) Header() *dnsRR_Header { - return &rr.Hdr -} - -func (rr *dnsRR_A) Walk(f func(v interface{}, name, tag string) bool) bool { - return rr.Hdr.Walk(f) && f(&rr.A, "A", "ipv4") -} - -type dnsRR_AAAA struct { - Hdr dnsRR_Header - AAAA [16]byte -} - -func (rr *dnsRR_AAAA) Header() *dnsRR_Header { - return &rr.Hdr -} - -func (rr *dnsRR_AAAA) Walk(f func(v interface{}, name, tag string) bool) bool { - return rr.Hdr.Walk(f) && f(rr.AAAA[:], "AAAA", "ipv6") -} - -// Packing and unpacking. -// -// All the packers and unpackers take a (msg []byte, off int) -// and return (off1 int, ok bool). If they return ok==false, they -// also return off1==len(msg), so that the next unpacker will -// also fail. This lets us avoid checks of ok until the end of a -// packing sequence. - -// Map of constructors for each RR wire type. -var rr_mk = map[int]func() dnsRR{ - dnsTypeCNAME: func() dnsRR { return new(dnsRR_CNAME) }, - dnsTypeMX: func() dnsRR { return new(dnsRR_MX) }, - dnsTypeNS: func() dnsRR { return new(dnsRR_NS) }, - dnsTypePTR: func() dnsRR { return new(dnsRR_PTR) }, - dnsTypeSOA: func() dnsRR { return new(dnsRR_SOA) }, - dnsTypeTXT: func() dnsRR { return new(dnsRR_TXT) }, - dnsTypeSRV: func() dnsRR { return new(dnsRR_SRV) }, - dnsTypeA: func() dnsRR { return new(dnsRR_A) }, - dnsTypeAAAA: func() dnsRR { return new(dnsRR_AAAA) }, -} - -// Pack a domain name s into msg[off:]. -// Domain names are a sequence of counted strings -// split at the dots. They end with a zero-length string. -func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) { - // Add trailing dot to canonicalize name. - if n := len(s); n == 0 || s[n-1] != '.' { - s += "." - } - - // Allow root domain. - if s == "." { - msg[off] = 0 - off++ - return off, true - } - - // Each dot ends a segment of the name. - // We trade each dot byte for a length byte. - // There is also a trailing zero. - // Check that we have all the space we need. - tot := len(s) + 1 - if off+tot > len(msg) { - return len(msg), false - } - - // Emit sequence of counted strings, chopping at dots. - begin := 0 - for i := 0; i < len(s); i++ { - if s[i] == '.' { - if i-begin >= 1<<6 { // top two bits of length must be clear - return len(msg), false - } - if i-begin == 0 { - return len(msg), false - } - - msg[off] = byte(i - begin) - off++ - - for j := begin; j < i; j++ { - msg[off] = s[j] - off++ - } - begin = i + 1 - } - } - msg[off] = 0 - off++ - return off, true -} - -// Unpack a domain name. -// In addition to the simple sequences of counted strings above, -// domain names are allowed to refer to strings elsewhere in the -// packet, to avoid repeating common suffixes when returning -// many entries in a single domain. The pointers are marked -// by a length byte with the top two bits set. Ignoring those -// two bits, that byte and the next give a 14 bit offset from msg[0] -// where we should pick up the trail. -// Note that if we jump elsewhere in the packet, -// we return off1 == the offset after the first pointer we found, -// which is where the next record will start. -// In theory, the pointers are only allowed to jump backward. -// We let them jump anywhere and stop jumping after a while. -func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) { - s = "" - ptr := 0 // number of pointers followed -Loop: - for { - if off >= len(msg) { - return "", len(msg), false - } - c := int(msg[off]) - off++ - switch c & 0xC0 { - case 0x00: - if c == 0x00 { - // end of name - break Loop - } - // literal string - if off+c > len(msg) { - return "", len(msg), false - } - s += string(msg[off:off+c]) + "." - off += c - case 0xC0: - // pointer to somewhere else in msg. - // remember location after first ptr, - // since that's how many bytes we consumed. - // also, don't follow too many pointers -- - // maybe there's a loop. - if off >= len(msg) { - return "", len(msg), false - } - c1 := msg[off] - off++ - if ptr == 0 { - off1 = off - } - if ptr++; ptr > 10 { - return "", len(msg), false - } - off = (c^0xC0)<<8 | int(c1) - default: - // 0x80 and 0x40 are reserved - return "", len(msg), false - } - } - if len(s) == 0 { - s = "." - } - if ptr == 0 { - off1 = off - } - return s, off1, true -} - -// packStruct packs a structure into msg at specified offset off, and -// returns off1 such that msg[off:off1] is the encoded data. -func packStruct(any dnsStruct, msg []byte, off int) (off1 int, ok bool) { - ok = any.Walk(func(field interface{}, name, tag string) bool { - switch fv := field.(type) { - default: - println("net: dns: unknown packing type") - return false - case *uint16: - i := *fv - if off+2 > len(msg) { - return false - } - msg[off] = byte(i >> 8) - msg[off+1] = byte(i) - off += 2 - case *uint32: - i := *fv - msg[off] = byte(i >> 24) - msg[off+1] = byte(i >> 16) - msg[off+2] = byte(i >> 8) - msg[off+3] = byte(i) - off += 4 - case []byte: - n := len(fv) - if off+n > len(msg) { - return false - } - copy(msg[off:off+n], fv) - off += n - case *string: - s := *fv - switch tag { - default: - println("net: dns: unknown string tag", tag) - return false - case "domain": - off, ok = packDomainName(s, msg, off) - if !ok { - return false - } - case "": - // Counted string: 1 byte length. - if len(s) > 255 || off+1+len(s) > len(msg) { - return false - } - msg[off] = byte(len(s)) - off++ - off += copy(msg[off:], s) - } - } - return true - }) - if !ok { - return len(msg), false - } - return off, true -} - -// unpackStruct decodes msg[off:] into the given structure, and -// returns off1 such that msg[off:off1] is the encoded data. -func unpackStruct(any dnsStruct, msg []byte, off int) (off1 int, ok bool) { - ok = any.Walk(func(field interface{}, name, tag string) bool { - switch fv := field.(type) { - default: - println("net: dns: unknown packing type") - return false - case *uint16: - if off+2 > len(msg) { - return false - } - *fv = uint16(msg[off])<<8 | uint16(msg[off+1]) - off += 2 - case *uint32: - if off+4 > len(msg) { - return false - } - *fv = uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | - uint32(msg[off+2])<<8 | uint32(msg[off+3]) - off += 4 - case []byte: - n := len(fv) - if off+n > len(msg) { - return false - } - copy(fv, msg[off:off+n]) - off += n - case *string: - var s string - switch tag { - default: - println("net: dns: unknown string tag", tag) - return false - case "domain": - s, off, ok = unpackDomainName(msg, off) - if !ok { - return false - } - case "": - if off >= len(msg) || off+1+int(msg[off]) > len(msg) { - return false - } - n := int(msg[off]) - off++ - b := make([]byte, n) - for i := 0; i < n; i++ { - b[i] = msg[off+i] - } - off += n - s = string(b) - } - *fv = s - } - return true - }) - if !ok { - return len(msg), false - } - return off, true -} - -// Generic struct printer. Prints fields with tag "ipv4" or "ipv6" -// as IP addresses. -func printStruct(any dnsStruct) string { - s := "{" - i := 0 - any.Walk(func(val interface{}, name, tag string) bool { - i++ - if i > 1 { - s += ", " - } - s += name + "=" - switch tag { - case "ipv4": - i := *val.(*uint32) - s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() - case "ipv6": - i := val.([]byte) - s += IP(i).String() - default: - var i int64 - switch v := val.(type) { - default: - // can't really happen. - s += "<unknown type>" - return true - case *string: - s += *v - return true - case []byte: - s += string(v) - return true - case *bool: - if *v { - s += "true" - } else { - s += "false" - } - return true - case *int: - i = int64(*v) - case *uint: - i = int64(*v) - case *uint8: - i = int64(*v) - case *uint16: - i = int64(*v) - case *uint32: - i = int64(*v) - case *uint64: - i = int64(*v) - case *uintptr: - i = int64(*v) - } - s += itoa(int(i)) - } - return true - }) - s += "}" - return s -} - -// Resource record packer. -func packRR(rr dnsRR, msg []byte, off int) (off2 int, ok bool) { - var off1 int - // pack twice, once to find end of header - // and again to find end of packet. - // a bit inefficient but this doesn't need to be fast. - // off1 is end of header - // off2 is end of rr - off1, ok = packStruct(rr.Header(), msg, off) - if !ok { - return len(msg), false - } - off2, ok = packStruct(rr, msg, off) - if !ok { - return len(msg), false - } - // pack a third time; redo header with correct data length - rr.Header().Rdlength = uint16(off2 - off1) - packStruct(rr.Header(), msg, off) - return off2, true -} - -// Resource record unpacker. -func unpackRR(msg []byte, off int) (rr dnsRR, off1 int, ok bool) { - // unpack just the header, to find the rr type and length - var h dnsRR_Header - off0 := off - if off, ok = unpackStruct(&h, msg, off); !ok { - return nil, len(msg), false - } - end := off + int(h.Rdlength) - - // make an rr of that type and re-unpack. - // again inefficient but doesn't need to be fast. - mk, known := rr_mk[int(h.Rrtype)] - if !known { - return &h, end, true - } - rr = mk() - off, ok = unpackStruct(rr, msg, off0) - if off != end { - return &h, end, true - } - return rr, off, ok -} - -// Usable representation of a DNS packet. - -// A manually-unpacked version of (id, bits). -// This is in its own struct for easy printing. -type dnsMsgHdr struct { - id uint16 - response bool - opcode int - authoritative bool - truncated bool - recursion_desired bool - recursion_available bool - rcode int -} - -func (h *dnsMsgHdr) Walk(f func(v interface{}, name, tag string) bool) bool { - return f(&h.id, "id", "") && - f(&h.response, "response", "") && - f(&h.opcode, "opcode", "") && - f(&h.authoritative, "authoritative", "") && - f(&h.truncated, "truncated", "") && - f(&h.recursion_desired, "recursion_desired", "") && - f(&h.recursion_available, "recursion_available", "") && - f(&h.rcode, "rcode", "") -} - -type dnsMsg struct { - dnsMsgHdr - question []dnsQuestion - answer []dnsRR - ns []dnsRR - extra []dnsRR -} - -func (dns *dnsMsg) Pack() (msg []byte, ok bool) { - var dh dnsHeader - - // Convert convenient dnsMsg into wire-like dnsHeader. - dh.Id = dns.id - dh.Bits = uint16(dns.opcode)<<11 | uint16(dns.rcode) - if dns.recursion_available { - dh.Bits |= _RA - } - if dns.recursion_desired { - dh.Bits |= _RD - } - if dns.truncated { - dh.Bits |= _TC - } - if dns.authoritative { - dh.Bits |= _AA - } - if dns.response { - dh.Bits |= _QR - } - - // Prepare variable sized arrays. - question := dns.question - answer := dns.answer - ns := dns.ns - extra := dns.extra - - dh.Qdcount = uint16(len(question)) - dh.Ancount = uint16(len(answer)) - dh.Nscount = uint16(len(ns)) - dh.Arcount = uint16(len(extra)) - - // Could work harder to calculate message size, - // but this is far more than we need and not - // big enough to hurt the allocator. - msg = make([]byte, 2000) - - // Pack it in: header and then the pieces. - off := 0 - off, ok = packStruct(&dh, msg, off) - if !ok { - return nil, false - } - for i := 0; i < len(question); i++ { - off, ok = packStruct(&question[i], msg, off) - if !ok { - return nil, false - } - } - for i := 0; i < len(answer); i++ { - off, ok = packRR(answer[i], msg, off) - if !ok { - return nil, false - } - } - for i := 0; i < len(ns); i++ { - off, ok = packRR(ns[i], msg, off) - if !ok { - return nil, false - } - } - for i := 0; i < len(extra); i++ { - off, ok = packRR(extra[i], msg, off) - if !ok { - return nil, false - } - } - return msg[0:off], true -} - -func (dns *dnsMsg) Unpack(msg []byte) bool { - // Header. - var dh dnsHeader - off := 0 - var ok bool - if off, ok = unpackStruct(&dh, msg, off); !ok { - return false - } - dns.id = dh.Id - dns.response = (dh.Bits & _QR) != 0 - dns.opcode = int(dh.Bits>>11) & 0xF - dns.authoritative = (dh.Bits & _AA) != 0 - dns.truncated = (dh.Bits & _TC) != 0 - dns.recursion_desired = (dh.Bits & _RD) != 0 - dns.recursion_available = (dh.Bits & _RA) != 0 - dns.rcode = int(dh.Bits & 0xF) - - // Arrays. - dns.question = make([]dnsQuestion, dh.Qdcount) - dns.answer = make([]dnsRR, 0, dh.Ancount) - dns.ns = make([]dnsRR, 0, dh.Nscount) - dns.extra = make([]dnsRR, 0, dh.Arcount) - - var rec dnsRR - - for i := 0; i < len(dns.question); i++ { - off, ok = unpackStruct(&dns.question[i], msg, off) - if !ok { - return false - } - } - for i := 0; i < int(dh.Ancount); i++ { - rec, off, ok = unpackRR(msg, off) - if !ok { - return false - } - dns.answer = append(dns.answer, rec) - } - for i := 0; i < int(dh.Nscount); i++ { - rec, off, ok = unpackRR(msg, off) - if !ok { - return false - } - dns.ns = append(dns.ns, rec) - } - for i := 0; i < int(dh.Arcount); i++ { - rec, off, ok = unpackRR(msg, off) - if !ok { - return false - } - dns.extra = append(dns.extra, rec) - } - // if off != len(msg) { - // println("extra bytes in dns packet", off, "<", len(msg)); - // } - return true -} - -func (dns *dnsMsg) String() string { - s := "DNS: " + printStruct(&dns.dnsMsgHdr) + "\n" - if len(dns.question) > 0 { - s += "-- Questions\n" - for i := 0; i < len(dns.question); i++ { - s += printStruct(&dns.question[i]) + "\n" - } - } - if len(dns.answer) > 0 { - s += "-- Answers\n" - for i := 0; i < len(dns.answer); i++ { - s += printStruct(dns.answer[i]) + "\n" - } - } - if len(dns.ns) > 0 { - s += "-- Name servers\n" - for i := 0; i < len(dns.ns); i++ { - s += printStruct(dns.ns[i]) + "\n" - } - } - if len(dns.extra) > 0 { - s += "-- Extra\n" - for i := 0; i < len(dns.extra); i++ { - s += printStruct(dns.extra[i]) + "\n" - } - } - return s -} - -// IsResponseTo reports whether m is an acceptable response to query. -func (m *dnsMsg) IsResponseTo(query *dnsMsg) bool { - if !m.response { - return false - } - if m.id != query.id { - return false - } - if len(m.question) != len(query.question) { - return false - } - for i, q := range m.question { - q2 := query.question[i] - if !equalASCIILabel(q.Name, q2.Name) || q.Qtype != q2.Qtype || q.Qclass != q2.Qclass { - return false - } - } - return true -} diff --git a/libgo/go/net/dnsmsg_test.go b/libgo/go/net/dnsmsg_test.go deleted file mode 100644 index 2a25a21..0000000 --- a/libgo/go/net/dnsmsg_test.go +++ /dev/null @@ -1,481 +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 net - -import ( - "encoding/hex" - "reflect" - "testing" -) - -func TestStructPackUnpack(t *testing.T) { - want := dnsQuestion{ - Name: ".", - Qtype: dnsTypeA, - Qclass: dnsClassINET, - } - buf := make([]byte, 50) - n, ok := packStruct(&want, buf, 0) - if !ok { - t.Fatal("packing failed") - } - buf = buf[:n] - got := dnsQuestion{} - n, ok = unpackStruct(&got, buf, 0) - if !ok { - t.Fatal("unpacking failed") - } - if n != len(buf) { - t.Errorf("unpacked different amount than packed: got n = %d, want = %d", n, len(buf)) - } - if !reflect.DeepEqual(got, want) { - t.Errorf("got = %+v, want = %+v", got, want) - } -} - -func TestDomainNamePackUnpack(t *testing.T) { - tests := []struct { - in string - want string - ok bool - }{ - {"", ".", true}, - {".", ".", true}, - {"google..com", "", false}, - {"google.com", "google.com.", true}, - {"google..com.", "", false}, - {"google.com.", "google.com.", true}, - {".google.com.", "", false}, - {"www..google.com.", "", false}, - {"www.google.com.", "www.google.com.", true}, - } - - for _, test := range tests { - buf := make([]byte, 30) - n, ok := packDomainName(test.in, buf, 0) - if ok != test.ok { - t.Errorf("packing of %s: got ok = %t, want = %t", test.in, ok, test.ok) - continue - } - if !test.ok { - continue - } - buf = buf[:n] - got, n, ok := unpackDomainName(buf, 0) - if !ok { - t.Errorf("unpacking for %s failed", test.in) - continue - } - if n != len(buf) { - t.Errorf( - "unpacked different amount than packed for %s: got n = %d, want = %d", - test.in, - n, - len(buf), - ) - } - if got != test.want { - t.Errorf("unpacking packing of %s: got = %s, want = %s", test.in, got, test.want) - } - } -} - -func TestDNSPackUnpack(t *testing.T) { - want := dnsMsg{ - question: []dnsQuestion{{ - Name: ".", - Qtype: dnsTypeAAAA, - Qclass: dnsClassINET, - }}, - answer: []dnsRR{}, - ns: []dnsRR{}, - extra: []dnsRR{}, - } - b, ok := want.Pack() - if !ok { - t.Fatal("packing failed") - } - var got dnsMsg - ok = got.Unpack(b) - if !ok { - t.Fatal("unpacking failed") - } - if !reflect.DeepEqual(got, want) { - t.Errorf("got = %+v, want = %+v", got, want) - } -} - -func TestDNSParseSRVReply(t *testing.T) { - data, err := hex.DecodeString(dnsSRVReply) - if err != nil { - t.Fatal(err) - } - msg := new(dnsMsg) - ok := msg.Unpack(data) - if !ok { - t.Fatal("unpacking packet failed") - } - _ = msg.String() // exercise this code path - if g, e := len(msg.answer), 5; g != e { - t.Errorf("len(msg.answer) = %d; want %d", g, e) - } - for idx, rr := range msg.answer { - if g, e := rr.Header().Rrtype, uint16(dnsTypeSRV); g != e { - t.Errorf("rr[%d].Header().Rrtype = %d; want %d", idx, g, e) - } - if _, ok := rr.(*dnsRR_SRV); !ok { - t.Errorf("answer[%d] = %T; want *dnsRR_SRV", idx, rr) - } - } - for _, name := range [...]string{ - "_xmpp-server._tcp.google.com.", - "_XMPP-Server._TCP.Google.COM.", - "_XMPP-SERVER._TCP.GOOGLE.COM.", - } { - _, addrs, err := answer(name, "foo:53", msg, uint16(dnsTypeSRV)) - if err != nil { - t.Error(err) - } - if g, e := len(addrs), 5; g != e { - t.Errorf("len(addrs) = %d; want %d", g, e) - t.Logf("addrs = %#v", addrs) - } - } - // repack and unpack. - data2, ok := msg.Pack() - msg2 := new(dnsMsg) - msg2.Unpack(data2) - switch { - case !ok: - t.Error("failed to repack message") - case !reflect.DeepEqual(msg, msg2): - t.Error("repacked message differs from original") - } -} - -func TestDNSParseCorruptSRVReply(t *testing.T) { - data, err := hex.DecodeString(dnsSRVCorruptReply) - if err != nil { - t.Fatal(err) - } - msg := new(dnsMsg) - ok := msg.Unpack(data) - if !ok { - t.Fatal("unpacking packet failed") - } - _ = msg.String() // exercise this code path - if g, e := len(msg.answer), 5; g != e { - t.Errorf("len(msg.answer) = %d; want %d", g, e) - } - for idx, rr := range msg.answer { - if g, e := rr.Header().Rrtype, uint16(dnsTypeSRV); g != e { - t.Errorf("rr[%d].Header().Rrtype = %d; want %d", idx, g, e) - } - if idx == 4 { - if _, ok := rr.(*dnsRR_Header); !ok { - t.Errorf("answer[%d] = %T; want *dnsRR_Header", idx, rr) - } - } else { - if _, ok := rr.(*dnsRR_SRV); !ok { - t.Errorf("answer[%d] = %T; want *dnsRR_SRV", idx, rr) - } - } - } - _, addrs, err := answer("_xmpp-server._tcp.google.com.", "foo:53", msg, uint16(dnsTypeSRV)) - if err != nil { - t.Fatalf("answer: %v", err) - } - if g, e := len(addrs), 4; g != e { - t.Errorf("len(addrs) = %d; want %d", g, e) - t.Logf("addrs = %#v", addrs) - } -} - -func TestDNSParseTXTReply(t *testing.T) { - expectedTxt1 := "v=spf1 redirect=_spf.google.com" - expectedTxt2 := "v=spf1 ip4:69.63.179.25 ip4:69.63.178.128/25 ip4:69.63.184.0/25 " + - "ip4:66.220.144.128/25 ip4:66.220.155.0/24 " + - "ip4:69.171.232.0/25 ip4:66.220.157.0/25 " + - "ip4:69.171.244.0/24 mx -all" - - replies := []string{dnsTXTReply1, dnsTXTReply2} - expectedTxts := []string{expectedTxt1, expectedTxt2} - - for i := range replies { - data, err := hex.DecodeString(replies[i]) - if err != nil { - t.Fatal(err) - } - - msg := new(dnsMsg) - ok := msg.Unpack(data) - if !ok { - t.Errorf("test %d: unpacking packet failed", i) - continue - } - - if len(msg.answer) != 1 { - t.Errorf("test %d: len(rr.answer) = %d; want 1", i, len(msg.answer)) - continue - } - - rr := msg.answer[0] - rrTXT, ok := rr.(*dnsRR_TXT) - if !ok { - t.Errorf("test %d: answer[0] = %T; want *dnsRR_TXT", i, rr) - continue - } - - if rrTXT.Txt != expectedTxts[i] { - t.Errorf("test %d: Txt = %s; want %s", i, rrTXT.Txt, expectedTxts[i]) - } - } -} - -func TestDNSParseTXTCorruptDataLengthReply(t *testing.T) { - replies := []string{dnsTXTCorruptDataLengthReply1, dnsTXTCorruptDataLengthReply2} - - for i := range replies { - data, err := hex.DecodeString(replies[i]) - if err != nil { - t.Fatal(err) - } - - msg := new(dnsMsg) - ok := msg.Unpack(data) - if ok { - t.Errorf("test %d: expected to fail on unpacking corrupt packet", i) - } - } -} - -func TestDNSParseTXTCorruptTXTLengthReply(t *testing.T) { - replies := []string{dnsTXTCorruptTXTLengthReply1, dnsTXTCorruptTXTLengthReply2} - - for i := range replies { - data, err := hex.DecodeString(replies[i]) - if err != nil { - t.Fatal(err) - } - - msg := new(dnsMsg) - ok := msg.Unpack(data) - // Unpacking should succeed, but we should just get the header. - if !ok { - t.Errorf("test %d: unpacking packet failed", i) - continue - } - - if len(msg.answer) != 1 { - t.Errorf("test %d: len(rr.answer) = %d; want 1", i, len(msg.answer)) - continue - } - - rr := msg.answer[0] - if _, justHeader := rr.(*dnsRR_Header); !justHeader { - t.Errorf("test %d: rr = %T; expected *dnsRR_Header", i, rr) - } - } -} - -func TestIsResponseTo(t *testing.T) { - // Sample DNS query. - query := dnsMsg{ - dnsMsgHdr: dnsMsgHdr{ - id: 42, - }, - question: []dnsQuestion{ - { - Name: "www.example.com.", - Qtype: dnsTypeA, - Qclass: dnsClassINET, - }, - }, - } - - resp := query - resp.response = true - if !resp.IsResponseTo(&query) { - t.Error("got false, want true") - } - - badResponses := []dnsMsg{ - // Different ID. - { - dnsMsgHdr: dnsMsgHdr{ - id: 43, - response: true, - }, - question: []dnsQuestion{ - { - Name: "www.example.com.", - Qtype: dnsTypeA, - Qclass: dnsClassINET, - }, - }, - }, - - // Different query name. - { - dnsMsgHdr: dnsMsgHdr{ - id: 42, - response: true, - }, - question: []dnsQuestion{ - { - Name: "www.google.com.", - Qtype: dnsTypeA, - Qclass: dnsClassINET, - }, - }, - }, - - // Different query type. - { - dnsMsgHdr: dnsMsgHdr{ - id: 42, - response: true, - }, - question: []dnsQuestion{ - { - Name: "www.example.com.", - Qtype: dnsTypeAAAA, - Qclass: dnsClassINET, - }, - }, - }, - - // Different query class. - { - dnsMsgHdr: dnsMsgHdr{ - id: 42, - response: true, - }, - question: []dnsQuestion{ - { - Name: "www.example.com.", - Qtype: dnsTypeA, - Qclass: dnsClassCSNET, - }, - }, - }, - - // No questions. - { - dnsMsgHdr: dnsMsgHdr{ - id: 42, - response: true, - }, - }, - - // Extra questions. - { - dnsMsgHdr: dnsMsgHdr{ - id: 42, - response: true, - }, - question: []dnsQuestion{ - { - Name: "www.example.com.", - Qtype: dnsTypeA, - Qclass: dnsClassINET, - }, - { - Name: "www.golang.org.", - Qtype: dnsTypeAAAA, - Qclass: dnsClassINET, - }, - }, - }, - } - - for i := range badResponses { - if badResponses[i].IsResponseTo(&query) { - t.Errorf("%v: got true, want false", i) - } - } -} - -// Valid DNS SRV reply -const dnsSRVReply = "0901818000010005000000000c5f786d70702d736572766572045f74637006676f6f67" + - "6c6503636f6d0000210001c00c002100010000012c00210014000014950c786d70702d" + - "73657276657234016c06676f6f676c6503636f6d00c00c002100010000012c00210014" + - "000014950c786d70702d73657276657232016c06676f6f676c6503636f6d00c00c0021" + - "00010000012c00210014000014950c786d70702d73657276657233016c06676f6f676c" + - "6503636f6d00c00c002100010000012c00200005000014950b786d70702d7365727665" + - "72016c06676f6f676c6503636f6d00c00c002100010000012c00210014000014950c78" + - "6d70702d73657276657231016c06676f6f676c6503636f6d00" - -// Corrupt DNS SRV reply, with its final RR having a bogus length -// (perhaps it was truncated, or it's malicious) The mutation is the -// capital "FF" below, instead of the proper "21". -const dnsSRVCorruptReply = "0901818000010005000000000c5f786d70702d736572766572045f74637006676f6f67" + - "6c6503636f6d0000210001c00c002100010000012c00210014000014950c786d70702d" + - "73657276657234016c06676f6f676c6503636f6d00c00c002100010000012c00210014" + - "000014950c786d70702d73657276657232016c06676f6f676c6503636f6d00c00c0021" + - "00010000012c00210014000014950c786d70702d73657276657233016c06676f6f676c" + - "6503636f6d00c00c002100010000012c00200005000014950b786d70702d7365727665" + - "72016c06676f6f676c6503636f6d00c00c002100010000012c00FF0014000014950c78" + - "6d70702d73657276657231016c06676f6f676c6503636f6d00" - -// TXT reply with one <character-string> -const dnsTXTReply1 = "b3458180000100010004000505676d61696c03636f6d0000100001c00c001000010000012c00" + - "201f763d737066312072656469726563743d5f7370662e676f6f676c652e636f6dc00" + - "c0002000100025d4c000d036e733406676f6f676c65c012c00c0002000100025d4c00" + - "06036e7331c057c00c0002000100025d4c0006036e7333c057c00c0002000100025d4" + - "c0006036e7332c057c06c00010001000248b50004d8ef200ac09000010001000248b5" + - "0004d8ef220ac07e00010001000248b50004d8ef240ac05300010001000248b50004d" + - "8ef260a0000291000000000000000" - -// TXT reply with more than one <character-string>. -// See https://tools.ietf.org/html/rfc1035#section-3.3.14 -const dnsTXTReply2 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" + - "100000e1000af7f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" + - "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" + - "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" + - "343a36392e3137312e3233322e302f323520692e70343a36362e3232302e3135372e302f32352" + - "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" + - "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" + - "f0cc0fd0001000100025d15000445abff0c" - -// DataLength field should be sum of all TXT fields. In this case it's less. -const dnsTXTCorruptDataLengthReply1 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" + - "100000e1000967f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" + - "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" + - "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" + - "343a36392e3137312e3233322e302f323520692e70343a36362e3232302e3135372e302f32352" + - "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" + - "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" + - "f0cc0fd0001000100025d15000445abff0c" - -// Same as above but DataLength is more than sum of TXT fields. -const dnsTXTCorruptDataLengthReply2 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" + - "100000e1001227f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" + - "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" + - "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" + - "343a36392e3137312e3233322e302f323520692e70343a36362e3232302e3135372e302f32352" + - "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" + - "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" + - "f0cc0fd0001000100025d15000445abff0c" - -// TXT Length field is less than actual length. -const dnsTXTCorruptTXTLengthReply1 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" + - "100000e1000af7f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" + - "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" + - "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" + - "343a36392e3137312e3233322e302f323520691470343a36362e3232302e3135372e302f32352" + - "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" + - "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" + - "f0cc0fd0001000100025d15000445abff0c" - -// TXT Length field is more than actual length. -const dnsTXTCorruptTXTLengthReply2 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" + - "100000e1000af7f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" + - "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" + - "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" + - "343a36392e3137312e3233322e302f323520693370343a36362e3232302e3135372e302f32352" + - "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" + - "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" + - "f0cc0fd0001000100025d15000445abff0c" diff --git a/libgo/go/net/dnsname_test.go b/libgo/go/net/dnsname_test.go index e0f786d..806d875 100644 --- a/libgo/go/net/dnsname_test.go +++ b/libgo/go/net/dnsname_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( diff --git a/libgo/go/net/error_nacl.go b/libgo/go/net/error_nacl.go new file mode 100644 index 0000000..caad133 --- /dev/null +++ b/libgo/go/net/error_nacl.go @@ -0,0 +1,9 @@ +// Copyright 2018 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 + +func isConnError(err error) bool { + return false +} diff --git a/libgo/go/net/error_plan9.go b/libgo/go/net/error_plan9.go new file mode 100644 index 0000000..caad133 --- /dev/null +++ b/libgo/go/net/error_plan9.go @@ -0,0 +1,9 @@ +// Copyright 2018 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 + +func isConnError(err error) bool { + return false +} diff --git a/libgo/go/net/error_posix.go b/libgo/go/net/error_posix.go index d0ffaae..70efa4c 100644 --- a/libgo/go/net/error_posix.go +++ b/libgo/go/net/error_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package net diff --git a/libgo/go/net/error_test.go b/libgo/go/net/error_test.go index 9791e6f..e09670e 100644 --- a/libgo/go/net/error_test.go +++ b/libgo/go/net/error_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( diff --git a/libgo/go/net/error_unix.go b/libgo/go/net/error_unix.go new file mode 100644 index 0000000..b5a5829 --- /dev/null +++ b/libgo/go/net/error_unix.go @@ -0,0 +1,16 @@ +// Copyright 2018 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 dragonfly freebsd js linux netbsd openbsd solaris + +package net + +import "syscall" + +func isConnError(err error) bool { + if se, ok := err.(syscall.Errno); ok { + return se == syscall.ECONNRESET || se == syscall.ECONNABORTED + } + return false +} diff --git a/libgo/go/net/error_windows.go b/libgo/go/net/error_windows.go new file mode 100644 index 0000000..570b97b --- /dev/null +++ b/libgo/go/net/error_windows.go @@ -0,0 +1,14 @@ +// Copyright 2018 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 "syscall" + +func isConnError(err error) bool { + if se, ok := err.(syscall.Errno); ok { + return se == syscall.WSAECONNRESET || se == syscall.WSAECONNABORTED + } + return false +} diff --git a/libgo/go/net/external_test.go b/libgo/go/net/external_test.go index 38788ef..f3c69c4 100644 --- a/libgo/go/net/external_test.go +++ b/libgo/go/net/external_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( diff --git a/libgo/go/net/fd_plan9.go b/libgo/go/net/fd_plan9.go index 46ee5d9..da41bc0 100644 --- a/libgo/go/net/fd_plan9.go +++ b/libgo/go/net/fd_plan9.go @@ -9,6 +9,7 @@ import ( "io" "os" "syscall" + "time" ) // Network file descriptor. @@ -172,3 +173,15 @@ func setReadBuffer(fd *netFD, bytes int) error { func setWriteBuffer(fd *netFD, bytes int) error { return syscall.EPLAN9 } + +func (fd *netFD) SetDeadline(t time.Time) error { + return fd.pfd.SetDeadline(t) +} + +func (fd *netFD) SetReadDeadline(t time.Time) error { + return fd.pfd.SetReadDeadline(t) +} + +func (fd *netFD) SetWriteDeadline(t time.Time) error { + return fd.pfd.SetWriteDeadline(t) +} diff --git a/libgo/go/net/fd_unix.go b/libgo/go/net/fd_unix.go index 95d5e4f..e7ab9a4 100644 --- a/libgo/go/net/fd_unix.go +++ b/libgo/go/net/fd_unix.go @@ -11,8 +11,8 @@ import ( "internal/poll" "os" "runtime" - "sync/atomic" "syscall" + "time" ) // Network file descriptor. @@ -22,7 +22,7 @@ type netFD struct { // immutable until Close family int sotype int - isConnected bool + isConnected bool // handshake completed or use of association with peer net string laddr Addr raddr Addr @@ -121,7 +121,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (rsa sysc // == nil). Because we've now poisoned the connection // by making it unwritable, don't return a successful // dial. This was issue 16523. - ret = ctxErr + ret = mapErr(ctxErr) fd.Close() // prevent a leak } }() @@ -256,74 +256,26 @@ func (fd *netFD) accept() (netfd *netFD, err error) { return netfd, nil } -// Use a helper function to call fcntl. This is defined in C in -// libgo/runtime. -//extern __go_fcntl_uintptr -func fcntl(uintptr, uintptr, uintptr) (uintptr, uintptr) - -// tryDupCloexec indicates whether F_DUPFD_CLOEXEC should be used. -// If the kernel doesn't support it, this is set to 0. -var tryDupCloexec = int32(1) - -func dupCloseOnExec(fd int) (newfd int, err error) { - if atomic.LoadInt32(&tryDupCloexec) == 1 && syscall.F_DUPFD_CLOEXEC != 0 { - syscall.Entersyscall() - r0, errno := fcntl(uintptr(fd), syscall.F_DUPFD_CLOEXEC, 0) - syscall.Exitsyscall() - e1 := syscall.Errno(errno) - if runtime.GOOS == "darwin" && e1 == syscall.EBADF { - // On OS X 10.6 and below (but we only support - // >= 10.6), F_DUPFD_CLOEXEC is unsupported - // and fcntl there falls back (undocumented) - // to doing an ioctl instead, returning EBADF - // in this case because fd is not of the - // expected device fd type. Treat it as - // EINVAL instead, so we fall back to the - // normal dup path. - // TODO: only do this on 10.6 if we can detect 10.6 - // cheaply. - e1 = syscall.EINVAL - } - switch e1 { - case 0: - return int(r0), nil - case syscall.EINVAL: - // Old kernel. Fall back to the portable way - // from now on. - atomic.StoreInt32(&tryDupCloexec, 0) - default: - return -1, os.NewSyscallError("fcntl", e1) +func (fd *netFD) dup() (f *os.File, err error) { + ns, call, err := fd.pfd.Dup() + if err != nil { + if call != "" { + err = os.NewSyscallError(call, err) } + return nil, err } - return dupCloseOnExecOld(fd) -} -// dupCloseOnExecUnixOld is the traditional way to dup an fd and -// set its O_CLOEXEC bit, using two system calls. -func dupCloseOnExecOld(fd int) (newfd int, err error) { - syscall.ForkLock.RLock() - defer syscall.ForkLock.RUnlock() - newfd, err = syscall.Dup(fd) - if err != nil { - return -1, os.NewSyscallError("dup", err) - } - syscall.CloseOnExec(newfd) - return + return os.NewFile(uintptr(ns), fd.name()), nil } -func (fd *netFD) dup() (f *os.File, err error) { - ns, err := dupCloseOnExec(fd.pfd.Sysfd) - if err != nil { - return nil, err - } +func (fd *netFD) SetDeadline(t time.Time) error { + return fd.pfd.SetDeadline(t) +} - // We want blocking mode for the new fd, hence the double negative. - // This also puts the old fd into blocking mode, meaning that - // I/O will block the thread instead of letting us use the epoll server. - // Everything will still work, just with more threads. - if err = fd.pfd.SetBlocking(); err != nil { - return nil, os.NewSyscallError("setnonblock", err) - } +func (fd *netFD) SetReadDeadline(t time.Time) error { + return fd.pfd.SetReadDeadline(t) +} - return os.NewFile(uintptr(ns), fd.name()), nil +func (fd *netFD) SetWriteDeadline(t time.Time) error { + return fd.pfd.SetWriteDeadline(t) } diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go index e5f8da1..3cc4c7a6 100644 --- a/libgo/go/net/fd_windows.go +++ b/libgo/go/net/fd_windows.go @@ -10,6 +10,7 @@ import ( "os" "runtime" "syscall" + "time" "unsafe" ) @@ -31,7 +32,7 @@ type netFD struct { // immutable until Close family int sotype int - isConnected bool + isConnected bool // handshake completed or use of association with peer net string laddr Addr raddr Addr @@ -241,3 +242,15 @@ func (fd *netFD) dup() (*os.File, error) { // TODO: Implement this return nil, syscall.EWINDOWS } + +func (fd *netFD) SetDeadline(t time.Time) error { + return fd.pfd.SetDeadline(t) +} + +func (fd *netFD) SetReadDeadline(t time.Time) error { + return fd.pfd.SetReadDeadline(t) +} + +func (fd *netFD) SetWriteDeadline(t time.Time) error { + return fd.pfd.SetWriteDeadline(t) +} diff --git a/libgo/go/net/file.go b/libgo/go/net/file.go index 07099851..81a44e1 100644 --- a/libgo/go/net/file.go +++ b/libgo/go/net/file.go @@ -6,7 +6,7 @@ package net import "os" -// BUG(mikio): On NaCl and Windows, the FileConn, FileListener and +// BUG(mikio): On JS, NaCl and Windows, the FileConn, FileListener and // FilePacketConn functions are not implemented. type fileAddr string diff --git a/libgo/go/net/file_stub.go b/libgo/go/net/file_stub.go index 0f7460c..2256608 100644 --- a/libgo/go/net/file_stub.go +++ b/libgo/go/net/file_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build nacl +// +build nacl js,wasm package net diff --git a/libgo/go/net/file_test.go b/libgo/go/net/file_test.go index abf8b3a..cd71774 100644 --- a/libgo/go/net/file_test.go +++ b/libgo/go/net/file_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( @@ -291,3 +293,57 @@ func TestFilePacketConn(t *testing.T) { } } } + +// Issue 24483. +func TestFileCloseRace(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !testableNetwork("tcp") { + t.Skip("tcp not supported") + } + + handler := func(ls *localServer, ln Listener) { + c, err := ln.Accept() + if err != nil { + return + } + defer c.Close() + var b [1]byte + c.Read(b[:]) + } + + ls, err := newLocalServer("tcp") + if err != nil { + t.Fatal(err) + } + defer ls.teardown() + if err := ls.buildup(handler); err != nil { + t.Fatal(err) + } + + const tries = 100 + for i := 0; i < tries; i++ { + c1, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + tc := c1.(*TCPConn) + + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + f, err := tc.File() + if err == nil { + f.Close() + } + }() + go func() { + defer wg.Done() + c1.Close() + }() + wg.Wait() + } +} diff --git a/libgo/go/net/file_unix.go b/libgo/go/net/file_unix.go index 3655a89..452a079 100644 --- a/libgo/go/net/file_unix.go +++ b/libgo/go/net/file_unix.go @@ -13,8 +13,11 @@ import ( ) func dupSocket(f *os.File) (int, error) { - s, err := dupCloseOnExec(int(f.Fd())) + s, call, err := poll.DupCloseOnExec(int(f.Fd())) if err != nil { + if call != "" { + err = os.NewSyscallError(call, err) + } return -1, err } if err := syscall.SetNonblock(s, true); err != nil { diff --git a/libgo/go/net/hook_unix.go b/libgo/go/net/hook_unix.go index 7d58d0f..a156831 100644 --- a/libgo/go/net/hook_unix.go +++ b/libgo/go/net/hook_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package net diff --git a/libgo/go/net/hosts.go b/libgo/go/net/hosts.go index 9c101c6..ebc0353 100644 --- a/libgo/go/net/hosts.go +++ b/libgo/go/net/hosts.go @@ -16,7 +16,7 @@ func parseLiteralIP(addr string) string { var zone string ip = parseIPv4(addr) if ip == nil { - ip, zone = parseIPv6(addr, true) + ip, zone = parseIPv6Zone(addr) } if ip == nil { return "" diff --git a/libgo/go/net/http/cgi/child.go b/libgo/go/net/http/cgi/child.go index ec10108..da12ac3 100644 --- a/libgo/go/net/http/cgi/child.go +++ b/libgo/go/net/http/cgi/child.go @@ -102,7 +102,7 @@ func RequestFromMap(params map[string]string) (*http.Request, error) { } // There's apparently a de-facto standard for this. - // http://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636 + // https://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636 if s := params["HTTPS"]; s == "on" || s == "ON" || s == "1" { r.TLS = &tls.ConnectionState{HandshakeComplete: true} } diff --git a/libgo/go/net/http/cgi/host_test.go b/libgo/go/net/http/cgi/host_test.go index 1336300..25882de 100644 --- a/libgo/go/net/http/cgi/host_test.go +++ b/libgo/go/net/http/cgi/host_test.go @@ -503,6 +503,7 @@ func TestDirWindows(t *testing.T) { } func TestEnvOverride(t *testing.T) { + check(t) cgifile, _ := filepath.Abs("testdata/test.cgi") var perl string @@ -525,7 +526,7 @@ func TestEnvOverride(t *testing.T) { "PATH=/wibble"}, } expectedMap := map[string]string{ - "cwd": cwd, + "cwd": cwd, "env-SCRIPT_FILENAME": cgifile, "env-REQUEST_URI": "/foo/bar", "env-PATH": "/wibble", diff --git a/libgo/go/net/http/client.go b/libgo/go/net/http/client.go index 6f6024e..8f69a29 100644 --- a/libgo/go/net/http/client.go +++ b/libgo/go/net/http/client.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. -// HTTP client. See RFC 2616. +// HTTP client. See RFC 7230 through 7235. // // This is the high-level Client interface. // The low-level implementation is in transport.go. @@ -95,14 +95,12 @@ type Client struct { // A Timeout of zero means no timeout. // // The Client cancels requests to the underlying Transport - // using the Request.Cancel mechanism. Requests passed - // to Client.Do may still set Request.Cancel; both will - // cancel the request. + // as if the Request's Context ended. // // For compatibility, the Client will also use the deprecated // CancelRequest method on Transport if found. New - // RoundTripper implementations should use Request.Cancel - // instead of implementing CancelRequest. + // RoundTripper implementations should use the Request's Context + // for cancelation instead of implementing CancelRequest. Timeout time.Duration } @@ -129,8 +127,8 @@ type RoundTripper interface { // RoundTrip should not modify the request, except for // consuming and closing the Request's Body. RoundTrip may // read fields of the request in a separate goroutine. Callers - // should not mutate the request until the Response's Body has - // been closed. + // should not mutate or reuse the request until the Response's + // Body has been closed. // // RoundTrip must always close the body, including on errors, // but depending on the implementation may do so in a separate @@ -335,7 +333,7 @@ func setRequestCancel(req *Request, rt RoundTripper, deadline time.Time) (stopTi return stopTimer, timedOut.isSet } -// See 2 (end of page 4) http://www.ietf.org/rfc/rfc2617.txt +// See 2 (end of page 4) https://www.ietf.org/rfc/rfc2617.txt // "To receive authorization, the client sends the userid and password, // separated by a single colon (":") character, within a base64 // encoded string in the credentials." @@ -357,7 +355,9 @@ func basicAuth(username, password string) string { // // An error is returned if there were too many redirects or if there // was an HTTP protocol error. A non-2xx response doesn't cause an -// error. +// error. Any returned error will be of type *url.Error. The url.Error +// value's Timeout method will report true if request timed out or was +// canceled. // // When err is nil, resp always contains a non-nil resp.Body. // Caller should close resp.Body when done reading from it. @@ -382,7 +382,9 @@ func Get(url string) (resp *Response, err error) { // // An error is returned if the Client's CheckRedirect function fails // or if there was an HTTP protocol error. A non-2xx response doesn't -// cause an error. +// cause an error. Any returned error will be of type *url.Error. The +// url.Error value's Timeout method will report true if request timed +// out or was canceled. // // When err is nil, resp always contains a non-nil resp.Body. // Caller should close resp.Body when done reading from it. @@ -457,6 +459,15 @@ func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirect return redirectMethod, shouldRedirect, includeBody } +// urlErrorOp returns the (*url.Error).Op value to use for the +// provided (*Request).Method value. +func urlErrorOp(method string) string { + if method == "" { + return "Get" + } + return method[:1] + strings.ToLower(method[1:]) +} + // Do sends an HTTP request and returns an HTTP response, following // policy (such as redirects, cookies, auth) as configured on the // client. @@ -490,10 +501,26 @@ func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirect // provided that the Request.GetBody function is defined. // The NewRequest function automatically sets GetBody for common // standard library body types. +// +// Any returned error will be of type *url.Error. The url.Error +// value's Timeout method will report true if request timed out or was +// canceled. func (c *Client) Do(req *Request) (*Response, error) { + return c.do(req) +} + +var testHookClientDoResult func(retres *Response, reterr error) + +func (c *Client) do(req *Request) (retres *Response, reterr error) { + if testHookClientDoResult != nil { + defer func() { testHookClientDoResult(retres, reterr) }() + } if req.URL == nil { req.closeBody() - return nil, errors.New("http: nil Request.URL") + return nil, &url.Error{ + Op: urlErrorOp(req.Method), + Err: errors.New("http: nil Request.URL"), + } } var ( @@ -512,15 +539,14 @@ func (c *Client) Do(req *Request) (*Response, error) { if !reqBodyClosed { req.closeBody() } - method := valueOrDefault(reqs[0].Method, "GET") var urlStr string if resp != nil && resp.Request != nil { - urlStr = resp.Request.URL.String() + urlStr = stripPassword(resp.Request.URL) } else { - urlStr = req.URL.String() + urlStr = stripPassword(req.URL) } return &url.Error{ - Op: method[:1] + strings.ToLower(method[1:]), + Op: urlErrorOp(reqs[0].Method), URL: urlStr, Err: err, } @@ -617,6 +643,7 @@ func (c *Client) Do(req *Request) (*Response, error) { reqBodyClosed = true if !deadline.IsZero() && didTimeout() { err = &httpError{ + // TODO: early in cycle: s/Client.Timeout exceeded/timeout or context cancelation/ err: err.Error() + " (Client.Timeout exceeded while awaiting headers)", timeout: true, } @@ -718,7 +745,7 @@ func defaultCheckRedirect(req *Request, via []*Request) error { // // See the Client.Do method documentation for details on how redirects // are handled. -func Post(url string, contentType string, body io.Reader) (resp *Response, err error) { +func Post(url, contentType string, body io.Reader) (resp *Response, err error) { return DefaultClient.Post(url, contentType, body) } @@ -733,7 +760,7 @@ func Post(url string, contentType string, body io.Reader) (resp *Response, err e // // See the Client.Do method documentation for details on how redirects // are handled. -func (c *Client) Post(url string, contentType string, body io.Reader) (resp *Response, err error) { +func (c *Client) Post(url, contentType string, body io.Reader) (resp *Response, err error) { req, err := NewRequest("POST", url, body) if err != nil { return nil, err @@ -827,6 +854,7 @@ func (b *cancelTimerBody) Read(p []byte) (n int, err error) { } if b.reqDidTimeout() { err = &httpError{ + // TODO: early in cycle: s/Client.Timeout exceeded/timeout or context cancelation/ err: err.Error() + " (Client.Timeout exceeded while reading body)", timeout: true, } @@ -880,3 +908,12 @@ func isDomainOrSubdomain(sub, parent string) bool { } return sub[len(sub)-len(parent)-1] == '.' } + +func stripPassword(u *url.URL) string { + pass, passSet := u.User.Password() + if passSet { + return strings.Replace(u.String(), pass+"@", "***@", 1) + } + + return u.String() +} diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go index eea3b16..bfc793e 100644 --- a/libgo/go/net/http/client_test.go +++ b/libgo/go/net/http/client_test.go @@ -1162,6 +1162,40 @@ func TestBasicAuthHeadersPreserved(t *testing.T) { } +func TestStripPasswordFromError(t *testing.T) { + client := &Client{Transport: &recordingTransport{}} + testCases := []struct { + desc string + in string + out string + }{ + { + desc: "Strip password from error message", + in: "http://user:password@dummy.faketld/", + out: "Get http://user:***@dummy.faketld/: dummy impl", + }, + { + desc: "Don't Strip password from domain name", + in: "http://user:password@password.faketld/", + out: "Get http://user:***@password.faketld/: dummy impl", + }, + { + desc: "Don't Strip password from path", + in: "http://user:password@dummy.faketld/password", + out: "Get http://user:***@dummy.faketld/password: dummy impl", + }, + } + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + _, err := client.Get(tC.in) + if err.Error() != tC.out { + t.Errorf("Unexpected output for %q: expected %q, actual %q", + tC.in, tC.out, err.Error()) + } + }) + } +} + func TestClientTimeout_h1(t *testing.T) { testClientTimeout(t, h1Mode) } func TestClientTimeout_h2(t *testing.T) { testClientTimeout(t, h2Mode) } diff --git a/libgo/go/net/http/clientserver_test.go b/libgo/go/net/http/clientserver_test.go index 8f2e574..6513b2d 100644 --- a/libgo/go/net/http/clientserver_test.go +++ b/libgo/go/net/http/clientserver_test.go @@ -1236,7 +1236,6 @@ func TestH12_AutoGzipWithDumpResponse(t *testing.T) { h := w.Header() h.Set("Content-Encoding", "gzip") h.Set("Content-Length", "23") - h.Set("Connection", "keep-alive") io.WriteString(w, "\x1f\x8b\b\x00\x00\x00\x00\x00\x00\x00s\xf3\xf7\a\x00\xab'\xd4\x1a\x03\x00\x00\x00") }, EarlyCheckResponse: func(proto string, res *Response) { diff --git a/libgo/go/net/http/cookie.go b/libgo/go/net/http/cookie.go index 38b1b36..b1a6cef 100644 --- a/libgo/go/net/http/cookie.go +++ b/libgo/go/net/http/cookie.go @@ -5,7 +5,6 @@ package http import ( - "bytes" "log" "net" "strconv" @@ -16,7 +15,7 @@ import ( // A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an // HTTP response or the Cookie header of an HTTP request. // -// See http://tools.ietf.org/html/rfc6265 for details. +// See https://tools.ietf.org/html/rfc6265 for details. type Cookie struct { Name string Value string @@ -32,10 +31,25 @@ type Cookie struct { MaxAge int Secure bool HttpOnly bool + SameSite SameSite Raw string Unparsed []string // Raw text of unparsed attribute-value pairs } +// SameSite allows a server define a cookie attribute making it impossible to +// the browser send this cookie along with cross-site requests. The main goal +// is mitigate the risk of cross-origin information leakage, and provides some +// protection against cross-site request forgery attacks. +// +// See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details. +type SameSite int + +const ( + SameSiteDefaultMode SameSite = iota + 1 + SameSiteLaxMode + SameSiteStrictMode +) + // readSetCookies parses all "Set-Cookie" values from // the header h and returns the successfully parsed Cookies. func readSetCookies(h Header) []*Cookie { @@ -84,6 +98,17 @@ func readSetCookies(h Header) []*Cookie { continue } switch lowerAttr { + case "samesite": + lowerVal := strings.ToLower(val) + switch lowerVal { + case "lax": + c.SameSite = SameSiteLaxMode + case "strict": + c.SameSite = SameSiteStrictMode + default: + c.SameSite = SameSiteDefaultMode + } + continue case "secure": c.Secure = true continue @@ -143,7 +168,7 @@ func (c *Cookie) String() string { if c == nil || !isCookieNameValid(c.Name) { return "" } - var b bytes.Buffer + var b strings.Builder b.WriteString(sanitizeCookieName(c.Name)) b.WriteRune('=') b.WriteString(sanitizeCookieValue(c.Value)) @@ -168,17 +193,14 @@ func (c *Cookie) String() string { log.Printf("net/http: invalid Cookie.Domain %q; dropping domain attribute", c.Domain) } } + var buf [len(TimeFormat)]byte if validCookieExpires(c.Expires) { b.WriteString("; Expires=") - b2 := b.Bytes() - b.Reset() - b.Write(c.Expires.UTC().AppendFormat(b2, TimeFormat)) + b.Write(c.Expires.UTC().AppendFormat(buf[:0], TimeFormat)) } if c.MaxAge > 0 { b.WriteString("; Max-Age=") - b2 := b.Bytes() - b.Reset() - b.Write(strconv.AppendInt(b2, int64(c.MaxAge), 10)) + b.Write(strconv.AppendInt(buf[:0], int64(c.MaxAge), 10)) } else if c.MaxAge < 0 { b.WriteString("; Max-Age=0") } @@ -188,6 +210,14 @@ func (c *Cookie) String() string { if c.Secure { b.WriteString("; Secure") } + switch c.SameSite { + case SameSiteDefaultMode: + b.WriteString("; SameSite") + case SameSiteLaxMode: + b.WriteString("; SameSite=Lax") + case SameSiteStrictMode: + b.WriteString("; SameSite=Strict") + } return b.String() } @@ -311,7 +341,7 @@ func sanitizeCookieName(n string) string { return cookieNameSanitizer.Replace(n) } -// http://tools.ietf.org/html/rfc6265#section-4.1.1 +// https://tools.ietf.org/html/rfc6265#section-4.1.1 // cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) // cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E // ; US-ASCII characters excluding CTLs, diff --git a/libgo/go/net/http/cookie_test.go b/libgo/go/net/http/cookie_test.go index 9d199a3..022adaa 100644 --- a/libgo/go/net/http/cookie_test.go +++ b/libgo/go/net/http/cookie_test.go @@ -65,6 +65,18 @@ var writeSetCookiesTests = []struct { &Cookie{Name: "cookie-11", Value: "invalid-expiry", Expires: time.Date(1600, 1, 1, 1, 1, 1, 1, time.UTC)}, "cookie-11=invalid-expiry", }, + { + &Cookie{Name: "cookie-12", Value: "samesite-default", SameSite: SameSiteDefaultMode}, + "cookie-12=samesite-default; SameSite", + }, + { + &Cookie{Name: "cookie-13", Value: "samesite-lax", SameSite: SameSiteLaxMode}, + "cookie-13=samesite-lax; SameSite=Lax", + }, + { + &Cookie{Name: "cookie-14", Value: "samesite-strict", SameSite: SameSiteStrictMode}, + "cookie-14=samesite-strict; SameSite=Strict", + }, // The "special" cookies have values containing commas or spaces which // are disallowed by RFC 6265 but are common in the wild. { @@ -241,6 +253,33 @@ var readSetCookiesTests = []struct { Raw: "ASP.NET_SessionId=foo; path=/; HttpOnly", }}, }, + { + Header{"Set-Cookie": {"samesitedefault=foo; SameSite"}}, + []*Cookie{{ + Name: "samesitedefault", + Value: "foo", + SameSite: SameSiteDefaultMode, + Raw: "samesitedefault=foo; SameSite", + }}, + }, + { + Header{"Set-Cookie": {"samesitelax=foo; SameSite=Lax"}}, + []*Cookie{{ + Name: "samesitelax", + Value: "foo", + SameSite: SameSiteLaxMode, + Raw: "samesitelax=foo; SameSite=Lax", + }}, + }, + { + Header{"Set-Cookie": {"samesitestrict=foo; SameSite=Strict"}}, + []*Cookie{{ + Name: "samesitestrict", + Value: "foo", + SameSite: SameSiteStrictMode, + Raw: "samesitestrict=foo; SameSite=Strict", + }}, + }, // Make sure we can properly read back the Set-Cookie headers we create // for values containing spaces or commas: { diff --git a/libgo/go/net/http/cookiejar/jar.go b/libgo/go/net/http/cookiejar/jar.go index ef8c35b..9f19917 100644 --- a/libgo/go/net/http/cookiejar/jar.go +++ b/libgo/go/net/http/cookiejar/jar.go @@ -93,6 +93,7 @@ type entry struct { Value string Domain string Path string + SameSite string Secure bool HttpOnly bool Persistent bool @@ -418,6 +419,15 @@ func (j *Jar) newEntry(c *http.Cookie, now time.Time, defPath, host string) (e e e.Secure = c.Secure e.HttpOnly = c.HttpOnly + switch c.SameSite { + case http.SameSiteDefaultMode: + e.SameSite = "SameSite" + case http.SameSiteStrictMode: + e.SameSite = "SameSite=Strict" + case http.SameSiteLaxMode: + e.SameSite = "SameSite=Lax" + } + return e, false, nil } diff --git a/libgo/go/net/http/example_test.go b/libgo/go/net/http/example_test.go index 9de0893..53fb0bb 100644 --- a/libgo/go/net/http/example_test.go +++ b/libgo/go/net/http/example_test.go @@ -137,3 +137,25 @@ func ExampleServer_Shutdown() { <-idleConnsClosed } + +func ExampleListenAndServeTLS() { + http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { + io.WriteString(w, "Hello, TLS!\n") + }) + + // One can use generate_cert.go in crypto/tls to generate cert.pem and key.pem. + log.Printf("About to listen on 8443. Go to https://127.0.0.1:8443/") + err := http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil) + log.Fatal(err) +} + +func ExampleListenAndServe() { + // Hello world, the web server + + helloHandler := func(w http.ResponseWriter, req *http.Request) { + io.WriteString(w, "Hello, world!\n") + } + + http.HandleFunc("/hello", helloHandler) + log.Fatal(http.ListenAndServe(":8080", nil)) +} diff --git a/libgo/go/net/http/export_test.go b/libgo/go/net/http/export_test.go index 1825acd..bc0db53 100644 --- a/libgo/go/net/http/export_test.go +++ b/libgo/go/net/http/export_test.go @@ -9,7 +9,9 @@ package http import ( "context" + "fmt" "net" + "net/url" "sort" "sync" "testing" @@ -33,11 +35,28 @@ var ( Export_writeStatusLine = writeStatusLine ) +const MaxWriteWaitBeforeConnReuse = maxWriteWaitBeforeConnReuse + func init() { // We only want to pay for this cost during testing. // When not under test, these values are always nil // and never assigned to. testHookMu = new(sync.Mutex) + + testHookClientDoResult = func(res *Response, err error) { + if err != nil { + if _, ok := err.(*url.Error); !ok { + panic(fmt.Sprintf("unexpected Client.Do error of type %T; want *url.Error", err)) + } + } else { + if res == nil { + panic("Client.Do returned nil, nil") + } + if res.Body == nil { + panic("Client.Do returned nil res.Body and no error") + } + } + } } var ( @@ -76,9 +95,7 @@ func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler { } func ResetCachedEnvironment() { - httpProxyEnv.reset() - httpsProxyEnv.reset() - noProxyEnv.reset() + resetProxyConfig() } func (t *Transport) NumPendingRequestsForTesting() int { @@ -119,7 +136,7 @@ func (t *Transport) IdleConnStrsForTesting() []string { func (t *Transport) IdleConnStrsForTesting_h2() []string { var ret []string - noDialPool := t.h2transport.ConnPool.(http2noDialClientConnPool) + noDialPool := t.h2transport.(*http2Transport).ConnPool.(http2noDialClientConnPool) pool := noDialPool.http2clientConnPool pool.mu.Lock() @@ -135,9 +152,11 @@ func (t *Transport) IdleConnStrsForTesting_h2() []string { return ret } -func (t *Transport) IdleConnCountForTesting(cacheKey string) int { +func (t *Transport) IdleConnCountForTesting(scheme, addr string) int { t.idleMu.Lock() defer t.idleMu.Unlock() + key := connectMethodKey{"", scheme, addr} + cacheKey := key.String() for k, conns := range t.idleConn { if k.String() == cacheKey { return len(conns) @@ -162,13 +181,19 @@ func (t *Transport) RequestIdleConnChForTesting() { t.getIdleConnCh(connectMethod{nil, "http", "example.com"}) } -func (t *Transport) PutIdleTestConn() bool { +func (t *Transport) PutIdleTestConn(scheme, addr string) bool { c, _ := net.Pipe() + key := connectMethodKey{"", scheme, addr} + select { + case <-t.incHostConnCount(key): + default: + return false + } return t.tryPutIdleConn(&persistConn{ t: t, conn: c, // dummy closech: make(chan struct{}), // so it can be closed - cacheKey: connectMethodKey{"", "http", "example.com"}, + cacheKey: key, }) == nil } @@ -200,8 +225,8 @@ func (s *Server) ExportAllConnsIdle() bool { s.mu.Lock() defer s.mu.Unlock() for c := range s.activeConn { - st, ok := c.curState.Load().(ConnState) - if !ok || st != StateIdle { + st, unixSec := c.getState() + if unixSec == 0 || st != StateIdle { return false } } diff --git a/libgo/go/net/http/fcgi/fcgi.go b/libgo/go/net/http/fcgi/fcgi.go index 8f3449a..fb822f8 100644 --- a/libgo/go/net/http/fcgi/fcgi.go +++ b/libgo/go/net/http/fcgi/fcgi.go @@ -4,9 +4,8 @@ // Package fcgi implements the FastCGI protocol. // -// The protocol is not an official standard and the original -// documentation is no longer online. See the Internet Archive's -// mirror at: https://web.archive.org/web/20150420080736/http://www.fastcgi.com/drupal/node/6?q=node/22 +// See https://fast-cgi.github.io/ for an unofficial mirror of the +// original documentation. // // Currently only the responder role is supported. package fcgi diff --git a/libgo/go/net/http/fs.go b/libgo/go/net/http/fs.go index ecad14a..db44d6b 100644 --- a/libgo/go/net/http/fs.go +++ b/libgo/go/net/http/fs.go @@ -235,17 +235,17 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, } switch { case len(ranges) == 1: - // RFC 2616, Section 14.16: - // "When an HTTP message includes the content of a single - // range (for example, a response to a request for a - // single range, or to a request for a set of ranges - // that overlap without any holes), this content is - // transmitted with a Content-Range header, and a - // Content-Length header showing the number of bytes - // actually transferred. + // RFC 7233, Section 4.1: + // "If a single part is being transferred, the server + // generating the 206 response MUST generate a + // Content-Range header field, describing what range + // of the selected representation is enclosed, and a + // payload consisting of the range. // ... - // A response to a request for a single range MUST NOT - // be sent using the multipart/byteranges media type." + // A server MUST NOT generate a multipart response to + // a request for a single range, since a client that + // does not request multiple parts might not support + // multipart responses." ra := ranges[0] if _, err := content.Seek(ra.start, io.SeekStart); err != nil { Error(w, err.Error(), StatusRequestedRangeNotSatisfiable) @@ -650,15 +650,23 @@ func localRedirect(w ResponseWriter, r *Request, newPath string) { // file or directory. // // If the provided file or directory name is a relative path, it is -// interpreted relative to the current directory and may ascend to parent -// directories. If the provided name is constructed from user input, it -// should be sanitized before calling ServeFile. As a precaution, ServeFile -// will reject requests where r.URL.Path contains a ".." path element. +// interpreted relative to the current directory and may ascend to +// parent directories. If the provided name is constructed from user +// input, it should be sanitized before calling ServeFile. // -// As a special case, ServeFile redirects any request where r.URL.Path +// As a precaution, ServeFile will reject requests where r.URL.Path +// contains a ".." path element; this protects against callers who +// might unsafely use filepath.Join on r.URL.Path without sanitizing +// it and then use that filepath.Join result as the name argument. +// +// As another special case, ServeFile redirects any request where r.URL.Path // ends in "/index.html" to the same path, without the final // "index.html". To avoid such redirects either modify the path or // use ServeContent. +// +// Outside of those two special cases, ServeFile does not use +// r.URL.Path for selecting the file or directory to serve; only the +// file or directory provided in the name argument is used. func ServeFile(w ResponseWriter, r *Request, name string) { if containsDotDot(r.URL.Path) { // Too many programs use r.URL.Path to construct the argument to @@ -731,7 +739,7 @@ func (r httpRange) mimeHeader(contentType string, size int64) textproto.MIMEHead } } -// parseRange parses a Range header string as per RFC 2616. +// parseRange parses a Range header string as per RFC 7233. // errNoOverlap is returned if none of the ranges overlap. func parseRange(s string, size int64) ([]httpRange, error) { if s == "" { diff --git a/libgo/go/net/http/fs_test.go b/libgo/go/net/http/fs_test.go index de772f9..1d6380d 100644 --- a/libgo/go/net/http/fs_test.go +++ b/libgo/go/net/http/fs_test.go @@ -993,7 +993,7 @@ func TestServeContent(t *testing.T) { for _, method := range []string{"GET", "HEAD"} { //restore content in case it is consumed by previous method if content, ok := content.(*strings.Reader); ok { - content.Seek(io.SeekStart, 0) + content.Seek(0, io.SeekStart) } servec <- serveParam{ diff --git a/libgo/go/net/http/h2_bundle.go b/libgo/go/net/http/h2_bundle.go index 3671875..4268f2f 100644 --- a/libgo/go/net/http/h2_bundle.go +++ b/libgo/go/net/http/h2_bundle.go @@ -44,13 +44,13 @@ import ( "sync" "time" + "golang_org/x/net/http/httpguts" "golang_org/x/net/http2/hpack" "golang_org/x/net/idna" - "golang_org/x/net/lex/httplex" ) // A list of the possible cipher suite ids. Taken from -// http://www.iana.org/assignments/tls-parameters/tls-parameters.txt +// https://www.iana.org/assignments/tls-parameters/tls-parameters.txt const ( http2cipher_TLS_NULL_WITH_NULL_NULL uint16 = 0x0000 @@ -725,9 +725,31 @@ const ( http2noDialOnMiss = false ) +// shouldTraceGetConn reports whether getClientConn should call any +// ClientTrace.GetConn hook associated with the http.Request. +// +// This complexity is needed to avoid double calls of the GetConn hook +// during the back-and-forth between net/http and x/net/http2 (when the +// net/http.Transport is upgraded to also speak http2), as well as support +// the case where x/net/http2 is being used directly. +func (p *http2clientConnPool) shouldTraceGetConn(st http2clientConnIdleState) bool { + // If our Transport wasn't made via ConfigureTransport, always + // trace the GetConn hook if provided, because that means the + // http2 package is being used directly and it's the one + // dialing, as opposed to net/http. + if _, ok := p.t.ConnPool.(http2noDialClientConnPool); !ok { + return true + } + // Otherwise, only use the GetConn hook if this connection has + // been used previously for other requests. For fresh + // connections, the net/http package does the dialing. + return !st.freshConn +} + func (p *http2clientConnPool) getClientConn(req *Request, addr string, dialOnMiss bool) (*http2ClientConn, error) { if http2isConnectionCloseRequest(req) && dialOnMiss { // It gets its own connection. + http2traceGetConn(req, addr) const singleUse = true cc, err := p.t.dialClientConn(addr, singleUse) if err != nil { @@ -737,7 +759,10 @@ func (p *http2clientConnPool) getClientConn(req *Request, addr string, dialOnMis } p.mu.Lock() for _, cc := range p.conns[addr] { - if cc.CanTakeNewRequest() { + if st := cc.idleState(); st.canTakeNewRequest { + if p.shouldTraceGetConn(st) { + http2traceGetConn(req, addr) + } p.mu.Unlock() return cc, nil } @@ -746,6 +771,7 @@ func (p *http2clientConnPool) getClientConn(req *Request, addr string, dialOnMis p.mu.Unlock() return nil, http2ErrNoCachedConn } + http2traceGetConn(req, addr) call := p.getStartDialLocked(addr) p.mu.Unlock() <-call.done @@ -973,7 +999,7 @@ func http2configureTransport(t1 *Transport) (*http2Transport, error) { // registerHTTPSProtocol calls Transport.RegisterProtocol but // converting panics into errors. -func http2registerHTTPSProtocol(t *Transport, rt RoundTripper) (err error) { +func http2registerHTTPSProtocol(t *Transport, rt http2noDialH2RoundTripper) (err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("%v", e) @@ -985,10 +1011,12 @@ func http2registerHTTPSProtocol(t *Transport, rt RoundTripper) (err error) { // noDialH2RoundTripper is a RoundTripper which only tries to complete the request // if there's already has a cached connection to the host. -type http2noDialH2RoundTripper struct{ t *http2Transport } +// (The field is exported so it can be accessed via reflect from net/http; tested +// by TestNoDialH2RoundTripperType) +type http2noDialH2RoundTripper struct{ *http2Transport } func (rt http2noDialH2RoundTripper) RoundTrip(req *Request) (*Response, error) { - res, err := rt.t.RoundTrip(req) + res, err := rt.http2Transport.RoundTrip(req) if http2isNoCachedConnError(err) { return nil, ErrSkipAltProtocol } @@ -1290,12 +1318,12 @@ func (f *http2flow) take(n int32) { // add adds n bytes (positive or negative) to the flow control window. // It returns false if the sum would exceed 2^31-1. func (f *http2flow) add(n int32) bool { - remain := (1<<31 - 1) - f.n - if n > remain { - return false + sum := f.n + n + if (sum > n) == (f.n > 0) { + f.n = sum + return true } - f.n += n - return true + return false } const http2frameHeaderLen = 9 @@ -2016,32 +2044,67 @@ func (f *http2SettingsFrame) IsAck() bool { return f.http2FrameHeader.Flags.Has(http2FlagSettingsAck) } -func (f *http2SettingsFrame) Value(s http2SettingID) (v uint32, ok bool) { +func (f *http2SettingsFrame) Value(id http2SettingID) (v uint32, ok bool) { f.checkValid() - buf := f.p - for len(buf) > 0 { - settingID := http2SettingID(binary.BigEndian.Uint16(buf[:2])) - if settingID == s { - return binary.BigEndian.Uint32(buf[2:6]), true + for i := 0; i < f.NumSettings(); i++ { + if s := f.Setting(i); s.ID == id { + return s.Val, true } - buf = buf[6:] } return 0, false } +// Setting returns the setting from the frame at the given 0-based index. +// The index must be >= 0 and less than f.NumSettings(). +func (f *http2SettingsFrame) Setting(i int) http2Setting { + buf := f.p + return http2Setting{ + ID: http2SettingID(binary.BigEndian.Uint16(buf[i*6 : i*6+2])), + Val: binary.BigEndian.Uint32(buf[i*6+2 : i*6+6]), + } +} + +func (f *http2SettingsFrame) NumSettings() int { return len(f.p) / 6 } + +// HasDuplicates reports whether f contains any duplicate setting IDs. +func (f *http2SettingsFrame) HasDuplicates() bool { + num := f.NumSettings() + if num == 0 { + return false + } + // If it's small enough (the common case), just do the n^2 + // thing and avoid a map allocation. + if num < 10 { + for i := 0; i < num; i++ { + idi := f.Setting(i).ID + for j := i + 1; j < num; j++ { + idj := f.Setting(j).ID + if idi == idj { + return true + } + } + } + return false + } + seen := map[http2SettingID]bool{} + for i := 0; i < num; i++ { + id := f.Setting(i).ID + if seen[id] { + return true + } + seen[id] = true + } + return false +} + // ForeachSetting runs fn for each setting. // It stops and returns the first error. func (f *http2SettingsFrame) ForeachSetting(fn func(http2Setting) error) error { f.checkValid() - buf := f.p - for len(buf) > 0 { - if err := fn(http2Setting{ - http2SettingID(binary.BigEndian.Uint16(buf[:2])), - binary.BigEndian.Uint32(buf[2:6]), - }); err != nil { + for i := 0; i < f.NumSettings(); i++ { + if err := fn(f.Setting(i)); err != nil { return err } - buf = buf[6:] } return nil } @@ -2745,7 +2808,7 @@ func (fr *http2Framer) readMetaFrame(hf *http2HeadersFrame) (*http2MetaHeadersFr if http2VerboseLogs && fr.logReads { fr.debugReadLoggerf("http2: decoded hpack field %+v", hf) } - if !httplex.ValidHeaderFieldValue(hf.Value) { + if !httpguts.ValidHeaderFieldValue(hf.Value) { invalid = http2headerFieldValueError(hf.Value) } isPseudo := strings.HasPrefix(hf.Name, ":") @@ -2861,6 +2924,23 @@ func http2summarizeFrame(f http2Frame) string { return buf.String() } +func http2traceHasWroteHeaderField(trace *http2clientTrace) bool { + return trace != nil && trace.WroteHeaderField != nil +} + +func http2traceWroteHeaderField(trace *http2clientTrace, k, v string) { + if trace != nil && trace.WroteHeaderField != nil { + trace.WroteHeaderField(k, []string{v}) + } +} + +func http2traceGot1xxResponseFunc(trace *http2clientTrace) func(int, textproto.MIMEHeader) error { + if trace != nil { + return trace.Got1xxResponse + } + return nil +} + func http2transportExpectContinueTimeout(t1 *Transport) time.Duration { return t1.ExpectContinueTimeout } @@ -2869,6 +2949,8 @@ type http2contextContext interface { context.Context } +var http2errCanceled = context.Canceled + func http2serverConnBaseContext(c net.Conn, opts *http2ServeConnOpts) (ctx http2contextContext, cancel func()) { ctx, cancel = context.WithCancel(context.Background()) ctx = context.WithValue(ctx, LocalAddrContextKey, c.LocalAddr()) @@ -2899,6 +2981,14 @@ func (t *http2Transport) idleConnTimeout() time.Duration { func http2setResponseUncompressed(res *Response) { res.Uncompressed = true } +func http2traceGetConn(req *Request, hostPort string) { + trace := httptrace.ContextClientTrace(req.Context()) + if trace == nil || trace.GetConn == nil { + return + } + trace.GetConn(hostPort) +} + func http2traceGotConn(req *Request, cc *http2ClientConn) { trace := httptrace.ContextClientTrace(req.Context()) if trace == nil || trace.GotConn == nil { @@ -2956,6 +3046,11 @@ func (cc *http2ClientConn) Ping(ctx context.Context) error { return cc.ping(ctx) } +// Shutdown gracefully closes the client connection, waiting for running streams to complete. +func (cc *http2ClientConn) Shutdown(ctx context.Context) error { + return cc.shutdown(ctx) +} + func http2cloneTLSConfig(c *tls.Config) *tls.Config { c2 := c.Clone() c2.GetClientCertificate = c.GetClientCertificate // golang.org/issue/19264 @@ -3371,7 +3466,7 @@ var ( ) // validWireHeaderFieldName reports whether v is a valid header field -// name (key). See httplex.ValidHeaderName for the base rules. +// name (key). See httpguts.ValidHeaderName for the base rules. // // Further, http2 says: // "Just as in HTTP/1.x, header field names are strings of ASCII @@ -3383,7 +3478,7 @@ func http2validWireHeaderFieldName(v string) bool { return false } for _, r := range v { - if !httplex.IsTokenRune(r) { + if !httpguts.IsTokenRune(r) { return false } if 'A' <= r && r <= 'Z' { @@ -3505,7 +3600,7 @@ func http2mustUint31(v int32) uint32 { } // bodyAllowedForStatus reports whether a given response status code -// permits a body. See RFC 2616, section 4.4. +// permits a body. See RFC 7230, section 3.3. func http2bodyAllowedForStatus(status int) bool { switch { case status >= 100 && status <= 199: @@ -4096,7 +4191,7 @@ func (s *http2Server) ServeConn(c net.Conn, opts *http2ServeConnOpts) { // addresses during development. // // TODO: optionally enforce? Or enforce at the time we receive - // a new request, and verify the the ServerName matches the :authority? + // a new request, and verify the ServerName matches the :authority? // But that precludes proxy situations, perhaps. // // So for now, do nothing here again. @@ -5181,6 +5276,12 @@ func (sc *http2serverConn) processSettings(f *http2SettingsFrame) error { } return nil } + if f.NumSettings() > 100 || f.HasDuplicates() { + // This isn't actually in the spec, but hang up on + // suspiciously large settings frames or those with + // duplicate entries. + return http2ConnectionError(http2ErrCodeProtocol) + } if err := f.ForeachSetting(sc.processSetting); err != nil { return err } @@ -5269,6 +5370,12 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error { // type PROTOCOL_ERROR." return http2ConnectionError(http2ErrCodeProtocol) } + // RFC 7540, sec 6.1: If a DATA frame is received whose stream is not in + // "open" or "half-closed (local)" state, the recipient MUST respond with a + // stream error (Section 5.4.2) of type STREAM_CLOSED. + if state == http2stateClosed { + return http2streamError(id, http2ErrCodeStreamClosed) + } if st == nil || state != http2stateOpen || st.gotTrailerHeader || st.resetQueued { // This includes sending a RST_STREAM if the stream is // in stateHalfClosedLocal (which currently means that @@ -5302,7 +5409,10 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error { // Sender sending more than they'd declared? if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes { st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes)) - return http2streamError(id, http2ErrCodeStreamClosed) + // RFC 7540, sec 8.1.2.6: A request or response is also malformed if the + // value of a content-length header field does not equal the sum of the + // DATA frame payload lengths that form the body. + return http2streamError(id, http2ErrCodeProtocol) } if f.Length > 0 { // Check whether the client has flow control quota. @@ -5412,6 +5522,13 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error { // processing this frame. return nil } + // RFC 7540, sec 5.1: If an endpoint receives additional frames, other than + // WINDOW_UPDATE, PRIORITY, or RST_STREAM, for a stream that is in + // this state, it MUST respond with a stream error (Section 5.4.2) of + // type STREAM_CLOSED. + if st.state == http2stateHalfClosedRemote { + return http2streamError(id, http2ErrCodeStreamClosed) + } return st.processTrailerHeaders(f) } @@ -5512,7 +5629,7 @@ func (st *http2stream) processTrailerHeaders(f *http2MetaHeadersFrame) error { if st.trailer != nil { for _, hf := range f.RegularFields() { key := sc.canonicalHeader(hf.Name) - if !http2ValidTrailerHeader(key) { + if !httpguts.ValidTrailerHeader(key) { // TODO: send more details to the peer somehow. But http2 has // no way to send debug data at a stream level. Discuss with // HTTP folk. @@ -5979,8 +6096,8 @@ func (rws *http2responseWriterState) hasTrailers() bool { return len(rws.trailer // written in the trailers at the end of the response. func (rws *http2responseWriterState) declareTrailer(k string) { k = CanonicalHeaderKey(k) - if !http2ValidTrailerHeader(k) { - // Forbidden by RFC 2616 14.40. + if !httpguts.ValidTrailerHeader(k) { + // Forbidden by RFC 7230, section 4.1.2. rws.conn.logf("ignoring invalid trailer %q", k) return } @@ -6030,6 +6147,19 @@ func (rws *http2responseWriterState) writeChunk(p []byte) (n int, err error) { http2foreachHeaderElement(v, rws.declareTrailer) } + // "Connection" headers aren't allowed in HTTP/2 (RFC 7540, 8.1.2.2), + // but respect "Connection" == "close" to mean sending a GOAWAY and tearing + // down the TCP connection when idle, like we do for HTTP/1. + // TODO: remove more Connection-specific header fields here, in addition + // to "Connection". + if _, ok := rws.snapHeader["Connection"]; ok { + v := rws.snapHeader.Get("Connection") + delete(rws.snapHeader, "Connection") + if v == "close" { + rws.conn.startGracefulShutdown() + } + } + endStream := (rws.handlerDone && !rws.hasTrailers() && len(p) == 0) || isHeadResp err = rws.conn.writeHeaders(rws.stream, &http2writeResHeaders{ streamID: rws.stream.id, @@ -6101,7 +6231,7 @@ const http2TrailerPrefix = "Trailer:" // after the header has already been flushed. Because the Go // ResponseWriter interface has no way to set Trailers (only the // Header), and because we didn't want to expand the ResponseWriter -// interface, and because nobody used trailers, and because RFC 2616 +// interface, and because nobody used trailers, and because RFC 7230 // says you SHOULD (but not must) predeclare any trailers in the // header, the official ResponseWriter rules said trailers in Go must // be predeclared, and then we reuse the same ResponseWriter.Header() @@ -6485,7 +6615,7 @@ func (sc *http2serverConn) startPush(msg *http2startPushRequest) { } // foreachHeaderElement splits v according to the "#rule" construction -// in RFC 2616 section 2.1 and calls fn for each non-empty element. +// in RFC 7230 section 7 and calls fn for each non-empty element. func http2foreachHeaderElement(v string, fn func(string)) { v = textproto.TrimString(v) if v == "" { @@ -6533,41 +6663,6 @@ func http2new400Handler(err error) HandlerFunc { } } -// ValidTrailerHeader reports whether name is a valid header field name to appear -// in trailers. -// See: http://tools.ietf.org/html/rfc7230#section-4.1.2 -func http2ValidTrailerHeader(name string) bool { - name = CanonicalHeaderKey(name) - if strings.HasPrefix(name, "If-") || http2badTrailer[name] { - return false - } - return true -} - -var http2badTrailer = map[string]bool{ - "Authorization": true, - "Cache-Control": true, - "Connection": true, - "Content-Encoding": true, - "Content-Length": true, - "Content-Range": true, - "Content-Type": true, - "Expect": true, - "Host": true, - "Keep-Alive": true, - "Max-Forwards": true, - "Pragma": true, - "Proxy-Authenticate": true, - "Proxy-Authorization": true, - "Proxy-Connection": true, - "Range": true, - "Realm": true, - "Te": true, - "Trailer": true, - "Transfer-Encoding": true, - "Www-Authenticate": true, -} - // h1ServerKeepAlivesDisabled reports whether hs has its keep-alives // disabled. See comments on h1ServerShutdownChan above for why // the code is written this way. @@ -6709,6 +6804,7 @@ type http2ClientConn struct { cond *sync.Cond // hold mu; broadcast on flow/closed changes flow http2flow // our conn-level flow control quota (cs.flow is per stream) inflow http2flow // peer's conn-level flow control + closing bool closed bool wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back goAway *http2GoAwayFrame // if non-nil, the GoAwayFrame we received @@ -6761,9 +6857,10 @@ type http2clientStream struct { done chan struct{} // closed when stream remove from cc.streams map; close calls guarded by cc.mu // owned by clientConnReadLoop: - firstByte bool // got the first response byte - pastHeaders bool // got first MetaHeadersFrame (actual headers) - pastTrailers bool // got optional second MetaHeadersFrame (trailers) + firstByte bool // got the first response byte + pastHeaders bool // got first MetaHeadersFrame (actual headers) + pastTrailers bool // got optional second MetaHeadersFrame (trailers) + num1xx uint8 // number of 1xx responses seen trailer Header // accumulated trailers resTrailer *Header // client's Response.Trailer @@ -6787,6 +6884,17 @@ func http2awaitRequestCancel(req *Request, done <-chan struct{}) error { } } +var http2got1xxFuncForTests func(int, textproto.MIMEHeader) error + +// get1xxTraceFunc returns the value of request's httptrace.ClientTrace.Got1xxResponse func, +// if any. It returns nil if not set or if the Go version is too old. +func (cs *http2clientStream) get1xxTraceFunc() func(int, textproto.MIMEHeader) error { + if fn := http2got1xxFuncForTests; fn != nil { + return fn + } + return http2traceGot1xxResponseFunc(cs.trace) +} + // awaitRequestCancel waits for the user to cancel a request, its context to // expire, or for the request to be done (any way it might be removed from the // cc.streams map: peer reset, successful completion, TCP connection breakage, @@ -6856,10 +6964,12 @@ func (sew http2stickyErrWriter) Write(p []byte) (n int, err error) { return } -// noCachedConnError is the concrete type of ErrNoCachedConn, needs to be detected -// by net/http regardless of whether it's its bundled version (in h2_bundle.go with a rewritten type name) -// or from a user's x/net/http2. As such, as it has a unique method name (IsHTTP2NoCachedConnError) that -// net/http sniffs for via func isNoCachedConnError. +// noCachedConnError is the concrete type of ErrNoCachedConn, which +// needs to be detected by net/http regardless of whether it's its +// bundled version (in h2_bundle.go with a rewritten type name) or +// from a user's x/net/http2. As such, as it has a unique method name +// (IsHTTP2NoCachedConnError) that net/http sniffs for via func +// isNoCachedConnError. type http2noCachedConnError struct{} func (http2noCachedConnError) IsHTTP2NoCachedConnError() {} @@ -6870,9 +6980,7 @@ func (http2noCachedConnError) Error() string { return "http2: no cached connecti // or its equivalent renamed type in net/http2's h2_bundle.go. Both types // may coexist in the same running program. func http2isNoCachedConnError(err error) bool { - _, ok := err.(interface { - IsHTTP2NoCachedConnError() - }) + _, ok := err.(interface{ IsHTTP2NoCachedConnError() }) return ok } @@ -6974,27 +7082,36 @@ func http2shouldRetryRequest(req *Request, err error, afterBodyWrite bool) (*Req if !http2canRetryError(err) { return nil, err } - if !afterBodyWrite { - return req, nil - } // If the Body is nil (or http.NoBody), it's safe to reuse // this request and its Body. if req.Body == nil || http2reqBodyIsNoBody(req.Body) { return req, nil } - // Otherwise we depend on the Request having its GetBody - // func defined. + + // If the request body can be reset back to its original + // state via the optional req.GetBody, do that. getBody := http2reqGetBody(req) // Go 1.8: getBody = req.GetBody - if getBody == nil { - return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err) + if getBody != nil { + // TODO: consider a req.Body.Close here? or audit that all caller paths do? + body, err := getBody() + if err != nil { + return nil, err + } + newReq := *req + newReq.Body = body + return &newReq, nil } - body, err := getBody() - if err != nil { - return nil, err + + // The Request.Body can't reset back to the beginning, but we + // don't seem to have started to read from it yet, so reuse + // the request directly. The "afterBodyWrite" means the + // bodyWrite process has started, which becomes true before + // the first Read. + if !afterBodyWrite { + return req, nil } - newReq := *req - newReq.Body = body - return &newReq, nil + + return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err) } func http2canRetryError(err error) bool { @@ -7118,6 +7235,10 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client // henc in response to SETTINGS frames? cc.henc = hpack.NewEncoder(&cc.hbuf) + if t.AllowHTTP { + cc.nextStreamID = 3 + } + if cs, ok := c.(http2connectionStater); ok { state := cs.ConnectionState() cc.tlsState = &state @@ -7177,12 +7298,32 @@ func (cc *http2ClientConn) CanTakeNewRequest() bool { return cc.canTakeNewRequestLocked() } -func (cc *http2ClientConn) canTakeNewRequestLocked() bool { +// clientConnIdleState describes the suitability of a client +// connection to initiate a new RoundTrip request. +type http2clientConnIdleState struct { + canTakeNewRequest bool + freshConn bool // whether it's unused by any previous request +} + +func (cc *http2ClientConn) idleState() http2clientConnIdleState { + cc.mu.Lock() + defer cc.mu.Unlock() + return cc.idleStateLocked() +} + +func (cc *http2ClientConn) idleStateLocked() (st http2clientConnIdleState) { if cc.singleUse && cc.nextStreamID > 1 { - return false + return } - return cc.goAway == nil && !cc.closed && + st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing && int64(cc.nextStreamID)+int64(cc.pendingRequests) < math.MaxInt32 + st.freshConn = cc.nextStreamID == 1 && st.canTakeNewRequest + return +} + +func (cc *http2ClientConn) canTakeNewRequestLocked() bool { + st := cc.idleStateLocked() + return st.canTakeNewRequest } // onIdleTimeout is called from a time.AfterFunc goroutine. It will @@ -7212,6 +7353,88 @@ func (cc *http2ClientConn) closeIfIdle() { cc.tconn.Close() } +var http2shutdownEnterWaitStateHook = func() {} + +// Shutdown gracefully close the client connection, waiting for running streams to complete. +// Public implementation is in go17.go and not_go17.go +func (cc *http2ClientConn) shutdown(ctx http2contextContext) error { + if err := cc.sendGoAway(); err != nil { + return err + } + // Wait for all in-flight streams to complete or connection to close + done := make(chan error, 1) + cancelled := false // guarded by cc.mu + go func() { + cc.mu.Lock() + defer cc.mu.Unlock() + for { + if len(cc.streams) == 0 || cc.closed { + cc.closed = true + done <- cc.tconn.Close() + break + } + if cancelled { + break + } + cc.cond.Wait() + } + }() + http2shutdownEnterWaitStateHook() + select { + case err := <-done: + return err + case <-ctx.Done(): + cc.mu.Lock() + // Free the goroutine above + cancelled = true + cc.cond.Broadcast() + cc.mu.Unlock() + return ctx.Err() + } +} + +func (cc *http2ClientConn) sendGoAway() error { + cc.mu.Lock() + defer cc.mu.Unlock() + cc.wmu.Lock() + defer cc.wmu.Unlock() + if cc.closing { + // GOAWAY sent already + return nil + } + // Send a graceful shutdown frame to server + maxStreamID := cc.nextStreamID + if err := cc.fr.WriteGoAway(maxStreamID, http2ErrCodeNo, nil); err != nil { + return err + } + if err := cc.bw.Flush(); err != nil { + return err + } + // Prevent new requests + cc.closing = true + return nil +} + +// Close closes the client connection immediately. +// +// In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead. +func (cc *http2ClientConn) Close() error { + cc.mu.Lock() + defer cc.cond.Broadcast() + defer cc.mu.Unlock() + err := errors.New("http2: client connection force closed via ClientConn.Close") + for id, cs := range cc.streams { + select { + case cs.resc <- http2resAndError{err: err}: + default: + } + cs.bufPipe.CloseWithError(err) + delete(cc.streams, id) + } + cc.closed = true + return cc.tconn.Close() +} + const http2maxAllocFrameSize = 512 << 10 // frameBuffer returns a scratch buffer suitable for writing DATA frames. @@ -7294,7 +7517,7 @@ func http2checkConnHeaders(req *Request) error { if vv := req.Header["Transfer-Encoding"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "chunked") { return fmt.Errorf("http2: invalid Transfer-Encoding request header: %q", vv) } - if vv := req.Header["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "close" && vv[0] != "keep-alive") { + if vv := req.Header["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && !strings.EqualFold(vv[0], "close") && !strings.EqualFold(vv[0], "keep-alive")) { return fmt.Errorf("http2: invalid Connection request header: %q", vv) } return nil @@ -7517,6 +7740,9 @@ func (cc *http2ClientConn) awaitOpenSlotForRequest(req *Request) error { for { cc.lastActive = time.Now() if cc.closed || !cc.canTakeNewRequestLocked() { + if waitingForConn != nil { + close(waitingForConn) + } return http2errClientConnUnusable } if int64(len(cc.streams))+1 <= int64(cc.maxConcurrentStreams) { @@ -7740,7 +7966,7 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail if host == "" { host = req.URL.Host } - host, err := httplex.PunycodeHostPort(host) + host, err := httpguts.PunycodeHostPort(host) if err != nil { return nil, err } @@ -7765,11 +7991,11 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail // potentially pollute our hpack state. (We want to be able to // continue to reuse the hpack encoder for future requests) for k, vv := range req.Header { - if !httplex.ValidHeaderFieldName(k) { + if !httpguts.ValidHeaderFieldName(k) { return nil, fmt.Errorf("invalid HTTP header name %q", k) } for _, v := range vv { - if !httplex.ValidHeaderFieldValue(v) { + if !httpguts.ValidHeaderFieldValue(v) { return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k) } } @@ -7850,9 +8076,16 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail return nil, http2errRequestHeaderListSize } + trace := http2requestTrace(req) + traceHeaders := http2traceHasWroteHeaderField(trace) + // Header list size is ok. Write the headers. enumerateHeaders(func(name, value string) { - cc.writeHeader(strings.ToLower(name), value) + name = strings.ToLower(name) + cc.writeHeader(name, value) + if traceHeaders { + http2traceWroteHeaderField(trace, name, value) + } }) return cc.hbuf.Bytes(), nil @@ -8174,8 +8407,7 @@ func (rl *http2clientConnReadLoop) processHeaders(f *http2MetaHeadersFrame) erro // is the detail. // // As a special case, handleResponse may return (nil, nil) to skip the -// frame (currently only used for 100 expect continue). This special -// case is going away after Issue 13851 is fixed. +// frame (currently only used for 1xx responses). func (rl *http2clientConnReadLoop) handleResponse(cs *http2clientStream, f *http2MetaHeadersFrame) (*Response, error) { if f.Truncated { return nil, http2errResponseHeaderListSize @@ -8190,15 +8422,6 @@ func (rl *http2clientConnReadLoop) handleResponse(cs *http2clientStream, f *http return nil, errors.New("malformed response from server: malformed non-numeric status pseudo header") } - if statusCode == 100 { - http2traceGot100Continue(cs.trace) - if cs.on100 != nil { - cs.on100() // forces any write delay timer to fire - } - cs.pastHeaders = false // do it all again - return nil, nil - } - header := make(Header) res := &Response{ Proto: "HTTP/2.0", @@ -8223,6 +8446,27 @@ func (rl *http2clientConnReadLoop) handleResponse(cs *http2clientStream, f *http } } + if statusCode >= 100 && statusCode <= 199 { + cs.num1xx++ + const max1xxResponses = 5 // arbitrary bound on number of informational responses, same as net/http + if cs.num1xx > max1xxResponses { + return nil, errors.New("http2: too many 1xx informational responses") + } + if fn := cs.get1xxTraceFunc(); fn != nil { + if err := fn(statusCode, textproto.MIMEHeader(header)); err != nil { + return nil, err + } + } + if statusCode == 100 { + http2traceGot100Continue(cs.trace) + if cs.on100 != nil { + cs.on100() // forces any write delay timer to fire + } + } + cs.pastHeaders = false // do it all again + return nil, nil + } + streamEnded := f.StreamEnded() isHead := cs.req.Method == "HEAD" if !streamEnded || isHead { @@ -8810,7 +9054,7 @@ func (t *http2Transport) getBodyWriterState(cs *http2clientStream, body io.Reade } s.delay = t.expectContinueTimeout() if s.delay == 0 || - !httplex.HeaderValuesContainsToken( + !httpguts.HeaderValuesContainsToken( cs.req.Header["Expect"], "100-continue") { return @@ -8865,7 +9109,7 @@ func (s http2bodyWriterState) scheduleBodyWrite() { // isConnectionCloseRequest reports whether req should use its own // connection for a single request and then close the connection. func http2isConnectionCloseRequest(req *Request) bool { - return req.Close || httplex.HeaderValuesContainsToken(req.Header["Connection"], "close") + return req.Close || httpguts.HeaderValuesContainsToken(req.Header["Connection"], "close") } // writeFramer is implemented by any type that is used to write frames. @@ -9205,7 +9449,7 @@ func http2encodeHeaders(enc *hpack.Encoder, h Header, keys []string) { } isTE := k == "transfer-encoding" for _, v := range vv { - if !httplex.ValidHeaderFieldValue(v) { + if !httpguts.ValidHeaderFieldValue(v) { // TODO: return an error? golang.org/issue/14048 // For now just omit it. continue diff --git a/libgo/go/net/http/header.go b/libgo/go/net/http/header.go index 622ad28..461ae93 100644 --- a/libgo/go/net/http/header.go +++ b/libgo/go/net/http/header.go @@ -6,6 +6,7 @@ package http import ( "io" + "net/http/httptrace" "net/textproto" "sort" "strings" @@ -56,7 +57,11 @@ func (h Header) Del(key string) { // Write writes a header in wire format. func (h Header) Write(w io.Writer) error { - return h.WriteSubset(w, nil) + return h.write(w, nil) +} + +func (h Header) write(w io.Writer, trace *httptrace.ClientTrace) error { + return h.writeSubset(w, nil, trace) } func (h Header) clone() Header { @@ -145,11 +150,16 @@ func (h Header) sortedKeyValues(exclude map[string]bool) (kvs []keyValues, hs *h // WriteSubset writes a header in wire format. // If exclude is not nil, keys where exclude[key] == true are not written. func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error { + return h.writeSubset(w, exclude, nil) +} + +func (h Header) writeSubset(w io.Writer, exclude map[string]bool, trace *httptrace.ClientTrace) error { ws, ok := w.(writeStringer) if !ok { ws = stringWriter{w} } kvs, sorter := h.sortedKeyValues(exclude) + var formattedVals []string for _, kv := range kvs { for _, v := range kv.values { v = headerNewlineToSpace.Replace(v) @@ -160,6 +170,13 @@ func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error { return err } } + if trace != nil && trace.WroteHeaderField != nil { + formattedVals = append(formattedVals, v) + } + } + if trace != nil && trace.WroteHeaderField != nil { + trace.WroteHeaderField(kv.key, formattedVals) + formattedVals = nil } } headerSorterPool.Put(sorter) diff --git a/libgo/go/net/http/http.go b/libgo/go/net/http/http.go index b95ca89..ce0eceb 100644 --- a/libgo/go/net/http/http.go +++ b/libgo/go/net/http/http.go @@ -11,7 +11,7 @@ import ( "time" "unicode/utf8" - "golang_org/x/net/lex/httplex" + "golang_org/x/net/http/httpguts" ) // maxInt64 is the effective "infinite" value for the Server and @@ -47,7 +47,7 @@ func removeEmptyPort(host string) string { } func isNotToken(r rune) bool { - return !httplex.IsTokenRune(r) + return !httpguts.IsTokenRune(r) } func isASCII(s string) bool { diff --git a/libgo/go/net/http/httptest/httptest_test.go b/libgo/go/net/http/httptest/httptest_test.go index 4f9ecbd..ef7d943 100644 --- a/libgo/go/net/http/httptest/httptest_test.go +++ b/libgo/go/net/http/httptest/httptest_test.go @@ -16,15 +16,17 @@ import ( ) func TestNewRequest(t *testing.T) { - tests := [...]struct { + for _, tt := range [...]struct { + name string + method, uri string body io.Reader want *http.Request wantBody string }{ - // Empty method means GET: - 0: { + { + name: "Empty method means GET", method: "", uri: "/", body: nil, @@ -42,8 +44,8 @@ func TestNewRequest(t *testing.T) { wantBody: "", }, - // GET with full URL: - 1: { + { + name: "GET with full URL", method: "GET", uri: "http://foo.com/path/%2f/bar/", body: nil, @@ -66,8 +68,8 @@ func TestNewRequest(t *testing.T) { wantBody: "", }, - // GET with full https URL: - 2: { + { + name: "GET with full https URL", method: "GET", uri: "https://foo.com/path/", body: nil, @@ -94,8 +96,8 @@ func TestNewRequest(t *testing.T) { wantBody: "", }, - // Post with known length - 3: { + { + name: "Post with known length", method: "POST", uri: "/", body: strings.NewReader("foo"), @@ -114,8 +116,8 @@ func TestNewRequest(t *testing.T) { wantBody: "foo", }, - // Post with unknown length - 4: { + { + name: "Post with unknown length", method: "POST", uri: "/", body: struct{ io.Reader }{strings.NewReader("foo")}, @@ -134,8 +136,8 @@ func TestNewRequest(t *testing.T) { wantBody: "foo", }, - // OPTIONS * - 5: { + { + name: "OPTIONS *", method: "OPTIONS", uri: "*", want: &http.Request{ @@ -150,28 +152,29 @@ func TestNewRequest(t *testing.T) { RequestURI: "*", }, }, - } - for i, tt := range tests { - got := NewRequest(tt.method, tt.uri, tt.body) - slurp, err := ioutil.ReadAll(got.Body) - if err != nil { - t.Errorf("%d. ReadAll: %v", i, err) - } - if string(slurp) != tt.wantBody { - t.Errorf("%d. Body = %q; want %q", i, slurp, tt.wantBody) - } - got.Body = nil // before DeepEqual - if !reflect.DeepEqual(got.URL, tt.want.URL) { - t.Errorf("%d. Request.URL mismatch:\n got: %#v\nwant: %#v", i, got.URL, tt.want.URL) - } - if !reflect.DeepEqual(got.Header, tt.want.Header) { - t.Errorf("%d. Request.Header mismatch:\n got: %#v\nwant: %#v", i, got.Header, tt.want.Header) - } - if !reflect.DeepEqual(got.TLS, tt.want.TLS) { - t.Errorf("%d. Request.TLS mismatch:\n got: %#v\nwant: %#v", i, got.TLS, tt.want.TLS) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("%d. Request mismatch:\n got: %#v\nwant: %#v", i, got, tt.want) - } + } { + t.Run(tt.name, func(t *testing.T) { + got := NewRequest(tt.method, tt.uri, tt.body) + slurp, err := ioutil.ReadAll(got.Body) + if err != nil { + t.Errorf("ReadAll: %v", err) + } + if string(slurp) != tt.wantBody { + t.Errorf("Body = %q; want %q", slurp, tt.wantBody) + } + got.Body = nil // before DeepEqual + if !reflect.DeepEqual(got.URL, tt.want.URL) { + t.Errorf("Request.URL mismatch:\n got: %#v\nwant: %#v", got.URL, tt.want.URL) + } + if !reflect.DeepEqual(got.Header, tt.want.Header) { + t.Errorf("Request.Header mismatch:\n got: %#v\nwant: %#v", got.Header, tt.want.Header) + } + if !reflect.DeepEqual(got.TLS, tt.want.TLS) { + t.Errorf("Request.TLS mismatch:\n got: %#v\nwant: %#v", got.TLS, tt.want.TLS) + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Request mismatch:\n got: %#v\nwant: %#v", got, tt.want) + } + }) } } diff --git a/libgo/go/net/http/httptest/recorder.go b/libgo/go/net/http/httptest/recorder.go index 741f076..67f90b8 100644 --- a/libgo/go/net/http/httptest/recorder.go +++ b/libgo/go/net/http/httptest/recorder.go @@ -11,6 +11,8 @@ import ( "net/http" "strconv" "strings" + + "golang_org/x/net/http/httpguts" ) // ResponseRecorder is an implementation of http.ResponseWriter that @@ -25,9 +27,11 @@ type ResponseRecorder struct { Code int // HeaderMap contains the headers explicitly set by the Handler. + // It is an internal detail. // - // To get the implicit headers set by the server (such as - // automatic Content-Type), use the Result method. + // Deprecated: HeaderMap exists for historical compatibility + // and should not be used. To access the headers returned by a handler, + // use the Response.Header map as returned by the Result method. HeaderMap http.Header // Body is the buffer to which the Handler's Write calls are sent. @@ -180,21 +184,19 @@ func (rw *ResponseRecorder) Result() *http.Response { res.Status = fmt.Sprintf("%03d %s", res.StatusCode, http.StatusText(res.StatusCode)) if rw.Body != nil { res.Body = ioutil.NopCloser(bytes.NewReader(rw.Body.Bytes())) + } else { + res.Body = http.NoBody } res.ContentLength = parseContentLength(res.Header.Get("Content-Length")) if trailers, ok := rw.snapHeader["Trailer"]; ok { res.Trailer = make(http.Header, len(trailers)) for _, k := range trailers { - // TODO: use http2.ValidTrailerHeader, but we can't - // get at it easily because it's bundled into net/http - // unexported. This is good enough for now: - switch k { - case "Transfer-Encoding", "Content-Length", "Trailer": - // Ignore since forbidden by RFC 2616 14.40. + k = http.CanonicalHeaderKey(k) + if !httpguts.ValidTrailerHeader(k) { + // Ignore since forbidden by RFC 7230, section 4.1.2. continue } - k = http.CanonicalHeaderKey(k) vv, ok := rw.HeaderMap[k] if !ok { continue diff --git a/libgo/go/net/http/httptest/recorder_test.go b/libgo/go/net/http/httptest/recorder_test.go index a6259eb..0986554 100644 --- a/libgo/go/net/http/httptest/recorder_test.go +++ b/libgo/go/net/http/httptest/recorder_test.go @@ -7,6 +7,7 @@ package httptest import ( "fmt" "io" + "io/ioutil" "net/http" "testing" ) @@ -39,6 +40,19 @@ func TestRecorder(t *testing.T) { return nil } } + hasResultContents := func(want string) checkFunc { + return func(rec *ResponseRecorder) error { + contentBytes, err := ioutil.ReadAll(rec.Result().Body) + if err != nil { + return err + } + contents := string(contentBytes) + if contents != want { + return fmt.Errorf("Result().Body = %s; want %s", contents, want) + } + return nil + } + } hasContents := func(want string) checkFunc { return func(rec *ResponseRecorder) error { if rec.Body.String() != want { @@ -111,7 +125,7 @@ func TestRecorder(t *testing.T) { } } - tests := []struct { + for _, tt := range [...]struct { name string h func(w http.ResponseWriter, r *http.Request) checks []checkFunc @@ -273,16 +287,26 @@ func TestRecorder(t *testing.T) { }, check(hasStatus(200), hasContents("Some body"), hasContentLength(9)), }, - } - r, _ := http.NewRequest("GET", "http://foo.com/", nil) - for _, tt := range tests { - h := http.HandlerFunc(tt.h) - rec := NewRecorder() - h.ServeHTTP(rec, r) - for _, check := range tt.checks { - if err := check(rec); err != nil { - t.Errorf("%s: %v", tt.name, err) + { + "nil ResponseRecorder.Body", // Issue 26642 + func(w http.ResponseWriter, r *http.Request) { + w.(*ResponseRecorder).Body = nil + io.WriteString(w, "hi") + }, + check(hasResultContents("")), // check we don't crash reading the body + + }, + } { + t.Run(tt.name, func(t *testing.T) { + r, _ := http.NewRequest("GET", "http://foo.com/", nil) + h := http.HandlerFunc(tt.h) + rec := NewRecorder() + h.ServeHTTP(rec, r) + for _, check := range tt.checks { + if err := check(rec); err != nil { + t.Error(err) + } } - } + }) } } diff --git a/libgo/go/net/http/httptest/server.go b/libgo/go/net/http/httptest/server.go index e543672..f6bcf3a 100644 --- a/libgo/go/net/http/httptest/server.go +++ b/libgo/go/net/http/httptest/server.go @@ -7,7 +7,6 @@ package httptest import ( - "bytes" "crypto/tls" "crypto/x509" "flag" @@ -17,6 +16,7 @@ import ( "net/http" "net/http/internal" "os" + "strings" "sync" "time" ) @@ -224,7 +224,7 @@ func (s *Server) Close() { func (s *Server) logCloseHangDebugInfo() { s.mu.Lock() defer s.mu.Unlock() - var buf bytes.Buffer + var buf strings.Builder buf.WriteString("httptest.Server blocked in Close after 5 seconds, waiting for connections:\n") for c, st := range s.conns { fmt.Fprintf(&buf, " %T %p %v in state %v\n", c, c, c.RemoteAddr(), st) diff --git a/libgo/go/net/http/httptrace/trace.go b/libgo/go/net/http/httptrace/trace.go index ea7b38c..3a62741 100644 --- a/libgo/go/net/http/httptrace/trace.go +++ b/libgo/go/net/http/httptrace/trace.go @@ -11,6 +11,7 @@ import ( "crypto/tls" "internal/nettrace" "net" + "net/textproto" "reflect" "time" ) @@ -107,6 +108,12 @@ type ClientTrace struct { // Continue" response. Got100Continue func() + // Got1xxResponse is called for each 1xx informational response header + // returned before the final non-1xx response. Got1xxResponse is called + // for "100 Continue" responses, even if Got100Continue is also defined. + // If it returns an error, the client request is aborted with that error value. + Got1xxResponse func(code int, header textproto.MIMEHeader) error + // DNSStart is called when a DNS lookup begins. DNSStart func(DNSStartInfo) @@ -135,8 +142,13 @@ type ClientTrace struct { // failure. TLSHandshakeDone func(tls.ConnectionState, error) + // WroteHeaderField is called after the Transport has written + // each request header. At the time of this call the values + // might be buffered and not yet written to the network. + WroteHeaderField func(key string, value []string) + // WroteHeaders is called after the Transport has written - // the request headers. + // all request headers. WroteHeaders func() // Wait100Continue is called if the Request specified diff --git a/libgo/go/net/http/httputil/httputil.go b/libgo/go/net/http/httputil/httputil.go index 2e523e9..09ea74d 100644 --- a/libgo/go/net/http/httputil/httputil.go +++ b/libgo/go/net/http/httputil/httputil.go @@ -23,7 +23,9 @@ func NewChunkedReader(r io.Reader) io.Reader { // 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. +// sends the final 0-length chunk that marks the end of the stream but does +// not send the final CRLF that appears after trailers; trailers and the last +// CRLF must be written separately. // // NewChunkedWriter is not needed by normal applications. The http // package adds chunking automatically if handlers don't set a diff --git a/libgo/go/net/http/httputil/reverseproxy.go b/libgo/go/net/http/httputil/reverseproxy.go index b96bb21..1dddaa9 100644 --- a/libgo/go/net/http/httputil/reverseproxy.go +++ b/libgo/go/net/http/httputil/reverseproxy.go @@ -55,10 +55,23 @@ type ReverseProxy struct { // copying HTTP response bodies. BufferPool BufferPool - // ModifyResponse is an optional function that - // modifies the Response from the backend. - // If it returns an error, the proxy returns a StatusBadGateway error. + // ModifyResponse is an optional function that modifies the + // Response from the backend. It is called if the backend + // returns a response at all, with any HTTP status code. + // If the backend is unreachable, the optional ErrorHandler is + // called without any call to ModifyResponse. + // + // If ModifyResponse returns an error, ErrorHandler is called + // with its error value. If ErrorHandler is nil, its default + // implementation is used. ModifyResponse func(*http.Response) error + + // ErrorHandler is an optional function that handles errors + // reaching the backend or errors from ModifyResponse. + // + // If nil, the default is to log the provided error and return + // a 502 Status Bad Gateway response. + ErrorHandler func(http.ResponseWriter, *http.Request, error) } // A BufferPool is an interface for getting and returning temporary @@ -125,7 +138,10 @@ func cloneHeader(h http.Header) http.Header { } // Hop-by-hop headers. These are removed when sent to the backend. -// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html +// As of RFC 7230, hop-by-hop headers are required to appear in the +// Connection header field. These are the headers defined by the +// obsoleted RFC 2616 (section 13.5.1) and are used for backward +// compatibility. var hopHeaders = []string{ "Connection", "Proxy-Connection", // non-standard but still sent by libcurl and rejected by e.g. google @@ -133,11 +149,23 @@ var hopHeaders = []string{ "Proxy-Authenticate", "Proxy-Authorization", "Te", // canonicalized version of "TE" - "Trailer", // not Trailers per URL above; http://www.rfc-editor.org/errata_search.php?eid=4522 + "Trailer", // not Trailers per URL above; https://www.rfc-editor.org/errata_search.php?eid=4522 "Transfer-Encoding", "Upgrade", } +func (p *ReverseProxy) defaultErrorHandler(rw http.ResponseWriter, req *http.Request, err error) { + p.logf("http: proxy error: %v", err) + rw.WriteHeader(http.StatusBadGateway) +} + +func (p *ReverseProxy) getErrorHandler() func(http.ResponseWriter, *http.Request, error) { + if p.ErrorHandler != nil { + return p.ErrorHandler + } + return p.defaultErrorHandler +} + func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { transport := p.Transport if transport == nil { @@ -175,9 +203,20 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // important is "Connection" because we want a persistent // connection, regardless of what the client sent to us. for _, h := range hopHeaders { - if outreq.Header.Get(h) != "" { - outreq.Header.Del(h) + hv := outreq.Header.Get(h) + if hv == "" { + continue + } + if h == "Te" && hv == "trailers" { + // Issue 21096: tell backend applications that + // care about trailer support that we support + // trailers. (We do, but we don't go out of + // our way to advertise that unless the + // incoming client request thought it was + // worth mentioning) + continue } + outreq.Header.Del(h) } if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { @@ -192,8 +231,7 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { res, err := transport.RoundTrip(outreq) if err != nil { - p.logf("http: proxy error: %v", err) - rw.WriteHeader(http.StatusBadGateway) + p.getErrorHandler()(rw, outreq, err) return } @@ -205,9 +243,8 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if p.ModifyResponse != nil { if err := p.ModifyResponse(res); err != nil { - p.logf("http: proxy error: %v", err) - rw.WriteHeader(http.StatusBadGateway) res.Body.Close() + p.getErrorHandler()(rw, outreq, err) return } } @@ -234,7 +271,18 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { fl.Flush() } } - p.copyResponse(rw, res.Body) + err = p.copyResponse(rw, res.Body) + if err != nil { + defer res.Body.Close() + // Since we're streaming the response, if we run into an error all we can do + // is abort the request. Issue 23643: ReverseProxy should use ErrAbortHandler + // on read error while copying body. + if !shouldPanicOnCopyError(req) { + p.logf("suppressing panic for copyResponse error in test; copy error: %v", err) + return + } + panic(http.ErrAbortHandler) + } res.Body.Close() // close now, instead of defer, to populate res.Trailer if len(res.Trailer) == announcedTrailers { @@ -250,8 +298,30 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } } +var inOurTests bool // whether we're in our own tests + +// shouldPanicOnCopyError reports whether the reverse proxy should +// panic with http.ErrAbortHandler. This is the right thing to do by +// default, but Go 1.10 and earlier did not, so existing unit tests +// weren't expecting panics. Only panic in our own tests, or when +// running under the HTTP server. +func shouldPanicOnCopyError(req *http.Request) bool { + if inOurTests { + // Our tests know to handle this panic. + return true + } + if req.Context().Value(http.ServerContextKey) != nil { + // We seem to be running under an HTTP server, so + // it'll recover the panic. + return true + } + // Otherwise act like Go 1.10 and earlier to not break + // existing tests. + return false +} + // removeConnectionHeaders removes hop-by-hop headers listed in the "Connection" header of h. -// See RFC 2616, section 14.10. +// See RFC 7230, section 6.1 func removeConnectionHeaders(h http.Header) { if c := h.Get("Connection"); c != "" { for _, f := range strings.Split(c, ",") { @@ -262,7 +332,7 @@ func removeConnectionHeaders(h http.Header) { } } -func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) { +func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) error { if p.FlushInterval != 0 { if wf, ok := dst.(writeFlusher); ok { mlw := &maxLatencyWriter{ @@ -279,13 +349,14 @@ func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) { var buf []byte if p.BufferPool != nil { buf = p.BufferPool.Get() + defer p.BufferPool.Put(buf) } - p.copyBuffer(dst, src, buf) - if p.BufferPool != nil { - p.BufferPool.Put(buf) - } + _, err := p.copyBuffer(dst, src, buf) + return err } +// copyBuffer returns any write errors or non-EOF read errors, and the amount +// of bytes written. func (p *ReverseProxy) copyBuffer(dst io.Writer, src io.Reader, buf []byte) (int64, error) { if len(buf) == 0 { buf = make([]byte, 32*1024) @@ -309,6 +380,9 @@ func (p *ReverseProxy) copyBuffer(dst io.Writer, src io.Reader, buf []byte) (int } } if rerr != nil { + if rerr == io.EOF { + rerr = nil + } return written, rerr } } diff --git a/libgo/go/net/http/httputil/reverseproxy_test.go b/libgo/go/net/http/httputil/reverseproxy_test.go index 2232042..2f75b4e 100644 --- a/libgo/go/net/http/httputil/reverseproxy_test.go +++ b/libgo/go/net/http/httputil/reverseproxy_test.go @@ -17,6 +17,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "os" "reflect" "strconv" "strings" @@ -28,6 +29,7 @@ import ( const fakeHopHeader = "X-Fake-Hop-Header-For-Test" func init() { + inOurTests = true hopHeaders = append(hopHeaders, fakeHopHeader) } @@ -49,6 +51,9 @@ func TestReverseProxy(t *testing.T) { if c := r.Header.Get("Connection"); c != "" { t.Errorf("handler got Connection header value %q", c) } + if c := r.Header.Get("Te"); c != "trailers" { + t.Errorf("handler got Te header value %q; want 'trailers'", c) + } if c := r.Header.Get("Upgrade"); c != "" { t.Errorf("handler got Upgrade header value %q", c) } @@ -85,6 +90,7 @@ func TestReverseProxy(t *testing.T) { getReq, _ := http.NewRequest("GET", frontend.URL, nil) getReq.Host = "some-name" getReq.Header.Set("Connection", "close") + getReq.Header.Set("Te", "trailers") getReq.Header.Set("Proxy-Connection", "should be deleted") getReq.Header.Set("Upgrade", "foo") getReq.Close = true @@ -631,6 +637,93 @@ func TestReverseProxyModifyResponse(t *testing.T) { } } +type failingRoundTripper struct{} + +func (failingRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { + return nil, errors.New("some error") +} + +type staticResponseRoundTripper struct{ res *http.Response } + +func (rt staticResponseRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { + return rt.res, nil +} + +func TestReverseProxyErrorHandler(t *testing.T) { + tests := []struct { + name string + wantCode int + errorHandler func(http.ResponseWriter, *http.Request, error) + transport http.RoundTripper // defaults to failingRoundTripper + modifyResponse func(*http.Response) error + }{ + { + name: "default", + wantCode: http.StatusBadGateway, + }, + { + name: "errorhandler", + wantCode: http.StatusTeapot, + errorHandler: func(rw http.ResponseWriter, req *http.Request, err error) { rw.WriteHeader(http.StatusTeapot) }, + }, + { + name: "modifyresponse_noerr", + transport: staticResponseRoundTripper{ + &http.Response{StatusCode: 345, Body: http.NoBody}, + }, + modifyResponse: func(res *http.Response) error { + res.StatusCode++ + return nil + }, + errorHandler: func(rw http.ResponseWriter, req *http.Request, err error) { rw.WriteHeader(http.StatusTeapot) }, + wantCode: 346, + }, + { + name: "modifyresponse_err", + transport: staticResponseRoundTripper{ + &http.Response{StatusCode: 345, Body: http.NoBody}, + }, + modifyResponse: func(res *http.Response) error { + res.StatusCode++ + return errors.New("some error to trigger errorHandler") + }, + errorHandler: func(rw http.ResponseWriter, req *http.Request, err error) { rw.WriteHeader(http.StatusTeapot) }, + wantCode: http.StatusTeapot, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + target := &url.URL{ + Scheme: "http", + Host: "dummy.tld", + Path: "/", + } + rproxy := NewSingleHostReverseProxy(target) + rproxy.Transport = tt.transport + rproxy.ModifyResponse = tt.modifyResponse + if rproxy.Transport == nil { + rproxy.Transport = failingRoundTripper{} + } + rproxy.ErrorLog = log.New(ioutil.Discard, "", 0) // quiet for tests + if tt.errorHandler != nil { + rproxy.ErrorHandler = tt.errorHandler + } + frontendProxy := httptest.NewServer(rproxy) + defer frontendProxy.Close() + + resp, err := http.Get(frontendProxy.URL + "/test") + if err != nil { + t.Fatalf("failed to reach proxy: %v", err) + } + if g, e := resp.StatusCode, tt.wantCode; g != e { + t.Errorf("got res.StatusCode %d; expected %d", g, e) + } + resp.Body.Close() + }) + } +} + // Issue 16659: log errors from short read func TestReverseProxy_CopyBuffer(t *testing.T) { backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -649,18 +742,22 @@ func TestReverseProxy_CopyBuffer(t *testing.T) { var proxyLog bytes.Buffer rproxy := NewSingleHostReverseProxy(rpURL) rproxy.ErrorLog = log.New(&proxyLog, "", log.Lshortfile) - frontendProxy := httptest.NewServer(rproxy) + donec := make(chan bool, 1) + frontendProxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer func() { donec <- true }() + rproxy.ServeHTTP(w, r) + })) defer frontendProxy.Close() - resp, err := http.Get(frontendProxy.URL) - if err != nil { - t.Fatalf("failed to reach proxy: %v", err) - } - defer resp.Body.Close() - - if _, err := ioutil.ReadAll(resp.Body); err == nil { + if _, err = frontendProxy.Client().Get(frontendProxy.URL); err == nil { t.Fatalf("want non-nil error") } + // The race detector complains about the proxyLog usage in logf in copyBuffer + // and our usage below with proxyLog.Bytes() so we're explicitly using a + // channel to ensure that the ReverseProxy's ServeHTTP is done before we + // continue after Get. + <-donec + expected := []string{ "EOF", "read", @@ -740,6 +837,8 @@ func TestServeHTTPDeepCopy(t *testing.T) { // Issue 18327: verify we always do a deep copy of the Request.Header map // before any mutations. func TestClonesRequestHeaders(t *testing.T) { + log.SetOutput(ioutil.Discard) + defer log.SetOutput(os.Stderr) req, _ := http.NewRequest("GET", "http://foo.tld/", nil) req.RemoteAddr = "1.2.3.4:56789" rp := &ReverseProxy{ @@ -813,3 +912,37 @@ func (cc *checkCloser) Close() error { func (cc *checkCloser) Read(b []byte) (int, error) { return len(b), nil } + +// Issue 23643: panic on body copy error +func TestReverseProxy_PanicBodyError(t *testing.T) { + log.SetOutput(ioutil.Discard) + defer log.SetOutput(os.Stderr) + backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + out := "this call was relayed by the reverse proxy" + // Coerce a wrong content length to induce io.ErrUnexpectedEOF + w.Header().Set("Content-Length", fmt.Sprintf("%d", len(out)*2)) + fmt.Fprintln(w, out) + })) + defer backendServer.Close() + + rpURL, err := url.Parse(backendServer.URL) + if err != nil { + t.Fatal(err) + } + + rproxy := NewSingleHostReverseProxy(rpURL) + + // Ensure that the handler panics when the body read encounters an + // io.ErrUnexpectedEOF + defer func() { + err := recover() + if err == nil { + t.Fatal("handler should have panicked") + } + if err != http.ErrAbortHandler { + t.Fatal("expected ErrAbortHandler, got", err) + } + }() + req, _ := http.NewRequest("GET", "http://foo.tld/", nil) + rproxy.ServeHTTP(httptest.NewRecorder(), req) +} diff --git a/libgo/go/net/http/internal/chunked.go b/libgo/go/net/http/internal/chunked.go index 63f321d..f06e572 100644 --- a/libgo/go/net/http/internal/chunked.go +++ b/libgo/go/net/http/internal/chunked.go @@ -171,7 +171,9 @@ func removeChunkExtension(p []byte) ([]byte, error) { // 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. +// sends the final 0-length chunk that marks the end of the stream but does +// not send the final CRLF that appears after trailers; trailers and the last +// CRLF must be written separately. // // NewChunkedWriter is not needed by normal applications. The http // package adds chunking automatically if handlers don't set a diff --git a/libgo/go/net/http/main_test.go b/libgo/go/net/http/main_test.go index 21c8505..7936fb3 100644 --- a/libgo/go/net/http/main_test.go +++ b/libgo/go/net/http/main_test.go @@ -114,12 +114,12 @@ func afterTest(t testing.TB) { } var bad string badSubstring := map[string]string{ - ").readLoop(": "a Transport", - ").writeLoop(": "a Transport", + ").readLoop(": "a Transport", + ").writeLoop(": "a Transport", "created by net/http/httptest.(*Server).Start": "an httptest.Server", - "timeoutHandler": "a TimeoutHandler", - "net.(*netFD).connect(": "a timing out dial", - ").noteClientGone(": "a closenotifier sender", + "timeoutHandler": "a TimeoutHandler", + "net.(*netFD).connect(": "a timing out dial", + ").noteClientGone(": "a closenotifier sender", } var stacks string for i := 0; i < 4; i++ { diff --git a/libgo/go/net/http/pprof/pprof.go b/libgo/go/net/http/pprof/pprof.go index 77e0bcd..35b3285 100644 --- a/libgo/go/net/http/pprof/pprof.go +++ b/libgo/go/net/http/pprof/pprof.go @@ -26,7 +26,7 @@ // // Or to look at a 30-second CPU profile: // -// go tool pprof http://localhost:6060/debug/pprof/profile +// go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30 // // Or to look at the goroutine blocking profile, after calling // runtime.SetBlockProfileRate in your program: @@ -63,6 +63,7 @@ import ( "runtime" "runtime/pprof" "runtime/trace" + "sort" "strconv" "strings" "time" @@ -110,11 +111,12 @@ func serveError(w http.ResponseWriter, status int, txt string) { } // Profile responds with the pprof-formatted cpu profile. +// Profiling lasts for duration specified in seconds GET parameter, or for 30 seconds if not specified. // The package initialization registers it as /debug/pprof/profile. func Profile(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Content-Type-Options", "nosniff") - sec, _ := strconv.ParseInt(r.FormValue("seconds"), 10, 64) - if sec == 0 { + sec, err := strconv.ParseInt(r.FormValue("seconds"), 10, 64) + if sec <= 0 || err != nil { sec = 30 } @@ -243,6 +245,18 @@ func (name handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { p.WriteTo(w, debug) } +var profileDescriptions = map[string]string{ + "allocs": "A sampling of all past memory allocations", + "block": "Stack traces that led to blocking on synchronization primitives", + "cmdline": "The command line invocation of the current program", + "goroutine": "Stack traces of all current goroutines", + "heap": "A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample.", + "mutex": "Stack traces of holders of contended mutexes", + "profile": "CPU profile. You can specify the duration in the seconds GET parameter. After you get the profile file, use the go tool pprof command to investigate the profile.", + "threadcreate": "Stack traces that led to the creation of new OS threads", + "trace": "A trace of execution of the current program. You can specify the duration in the seconds GET parameter. After you get the trace file, use the go tool trace command to investigate the trace.", +} + // Index responds with the pprof-formatted profile named by the request. // For example, "/debug/pprof/heap" serves the "heap" profile. // Index responds to a request for "/debug/pprof/" with an HTML page @@ -256,7 +270,35 @@ func Index(w http.ResponseWriter, r *http.Request) { } } - profiles := pprof.Profiles() + type profile struct { + Name string + Href string + Desc string + Count int + } + var profiles []profile + for _, p := range pprof.Profiles() { + profiles = append(profiles, profile{ + Name: p.Name(), + Href: p.Name() + "?debug=1", + Desc: profileDescriptions[p.Name()], + Count: p.Count(), + }) + } + + // Adding other profiles exposed from within this package + for _, p := range []string{"cmdline", "profile", "trace"} { + profiles = append(profiles, profile{ + Name: p, + Href: p, + Desc: profileDescriptions[p], + }) + } + + sort.Slice(profiles, func(i, j int) bool { + return profiles[i].Name < profiles[j].Name + }) + if err := indexTmpl.Execute(w, profiles); err != nil { log.Print(err) } @@ -265,18 +307,35 @@ func Index(w http.ResponseWriter, r *http.Request) { var indexTmpl = template.Must(template.New("index").Parse(`<html> <head> <title>/debug/pprof/</title> +<style> +.profile-name{ + display:inline-block; + width:6rem; +} +</style> </head> <body> /debug/pprof/<br> <br> -profiles:<br> +Types of profiles available: <table> +<thead><td>Count</td><td>Profile</td></thead> {{range .}} -<tr><td align=right>{{.Count}}<td><a href="{{.Name}}?debug=1">{{.Name}}</a> + <tr> + <td>{{.Count}}</td><td><a href={{.Href}}>{{.Name}}</a></td> + </tr> {{end}} </table> -<br> -<a href="goroutine?debug=2">full goroutine stack dump</a><br> +<a href="goroutine?debug=2">full goroutine stack dump</a> +<br/> +<p> +Profile Descriptions: +<ul> +{{range .}} +<li><div class=profile-name>{{.Name}}:</div> {{.Desc}}</li> +{{end}} +</ul> +</p> </body> </html> `)) diff --git a/libgo/go/net/http/pprof/pprof_test.go b/libgo/go/net/http/pprof/pprof_test.go index 47dd35b..dbb6fef 100644 --- a/libgo/go/net/http/pprof/pprof_test.go +++ b/libgo/go/net/http/pprof/pprof_test.go @@ -9,9 +9,21 @@ import ( "io/ioutil" "net/http" "net/http/httptest" + "runtime/pprof" "testing" ) +// TestDescriptions checks that the profile names under runtime/pprof package +// have a key in the description map. +func TestDescriptions(t *testing.T) { + for _, p := range pprof.Profiles() { + _, ok := profileDescriptions[p.Name()] + if ok != true { + t.Errorf("%s does not exist in profileDescriptions map\n", p.Name()) + } + } +} + func TestHandlers(t *testing.T) { testCases := []struct { path string diff --git a/libgo/go/net/http/proxy_test.go b/libgo/go/net/http/proxy_test.go index f59a551..eef0ca8 100644 --- a/libgo/go/net/http/proxy_test.go +++ b/libgo/go/net/http/proxy_test.go @@ -13,37 +13,6 @@ import ( // TODO(mattn): // test ProxyAuth -var UseProxyTests = []struct { - host string - match bool -}{ - // Never proxy localhost: - {"localhost", false}, - {"127.0.0.1", false}, - {"127.0.0.2", false}, - {"[::1]", false}, - {"[::2]", true}, // not a loopback address - - {"barbaz.net", false}, // match as .barbaz.net - {"foobar.com", false}, // have a port but match - {"foofoobar.com", true}, // not match as a part of foobar.com - {"baz.com", true}, // not match as a part of barbaz.com - {"localhost.net", true}, // not match as suffix of address - {"local.localhost", true}, // not match as prefix as address - {"barbarbaz.net", true}, // not match because NO_PROXY have a '.' - {"www.foobar.com", false}, // match because NO_PROXY includes "foobar.com" -} - -func TestUseProxy(t *testing.T) { - ResetProxyEnv() - os.Setenv("NO_PROXY", "foobar.com, .barbaz.net") - for _, test := range UseProxyTests { - if useProxy(test.host+":80") != test.match { - t.Errorf("useProxy(%v) = %v, want %v", test.host, !test.match, test.match) - } - } -} - var cacheKeysTests = []struct { proxy string scheme string @@ -74,14 +43,8 @@ func TestCacheKeys(t *testing.T) { } func ResetProxyEnv() { - for _, v := range []string{"HTTP_PROXY", "http_proxy", "NO_PROXY", "no_proxy"} { + for _, v := range []string{"HTTP_PROXY", "http_proxy", "NO_PROXY", "no_proxy", "REQUEST_METHOD"} { os.Unsetenv(v) } ResetCachedEnvironment() } - -func TestInvalidNoProxy(t *testing.T) { - ResetProxyEnv() - os.Setenv("NO_PROXY", ":1") - useProxy("example.com:80") // should not panic -} diff --git a/libgo/go/net/http/readrequest_test.go b/libgo/go/net/http/readrequest_test.go index 22a9c2e..18eed34 100644 --- a/libgo/go/net/http/readrequest_test.go +++ b/libgo/go/net/http/readrequest_test.go @@ -126,7 +126,7 @@ var reqTests = []reqTest{ noError, }, - // Tests a bogus abs_path on the Request-Line (RFC 2616 section 5.1.2) + // Tests a bogus absolute-path on the Request-Line (RFC 7230 section 5.3.1) { "GET ../../../../etc/passwd HTTP/1.1\r\n" + "Host: test\r\n\r\n", diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go index c9642e5..a40b0a3 100644 --- a/libgo/go/net/http/request.go +++ b/libgo/go/net/http/request.go @@ -65,11 +65,19 @@ var ( // request's Content-Type is not multipart/form-data. ErrNotMultipart = &ProtocolError{"request Content-Type isn't multipart/form-data"} - // Deprecated: ErrHeaderTooLong is not used. + // Deprecated: ErrHeaderTooLong is no longer returned by + // anything in the net/http package. Callers should not + // compare errors against this variable. ErrHeaderTooLong = &ProtocolError{"header too long"} - // Deprecated: ErrShortBody is not used. + + // Deprecated: ErrShortBody is no longer returned by + // anything in the net/http package. Callers should not + // compare errors against this variable. ErrShortBody = &ProtocolError{"entity body too short"} - // Deprecated: ErrMissingContentLength is not used. + + // Deprecated: ErrMissingContentLength is no longer returned by + // anything in the net/http package. Callers should not + // compare errors against this variable. ErrMissingContentLength = &ProtocolError{"missing ContentLength in HEAD response"} ) @@ -110,7 +118,7 @@ type Request struct { // For server requests the URL is parsed from the URI // supplied on the Request-Line as stored in RequestURI. For // most requests, fields other than Path and RawQuery will be - // empty. (See RFC 2616, Section 5.1.2) + // empty. (See RFC 7230, Section 5.3) // // For client requests, the URL's Host specifies the server to // connect to, while the Request's Host field optionally @@ -207,13 +215,18 @@ type Request struct { // Transport.DisableKeepAlives were set. Close bool - // For server requests Host specifies the host on which the - // URL is sought. Per RFC 2616, this is either the value of - // the "Host" header or the host name given in the URL itself. + // For server requests Host specifies the host on which the URL + // is sought. Per RFC 7230, section 5.4, this is either the value + // of the "Host" header or the host name given in the URL itself. // It may be of the form "host:port". For international domain // names, Host may be in Punycode or Unicode form. Use // golang.org/x/net/idna to convert it to either format if // needed. + // To prevent DNS rebinding attacks, server Handlers should + // validate that the Host header has a value for which the + // Handler considers itself authoritative. The included + // ServeMux supports patterns registered to particular host + // names and thus protects its registered Handlers. // // For client requests Host optionally overrides the Host // header to send. If empty, the Request.Write method uses @@ -268,8 +281,8 @@ type Request struct { // This field is ignored by the HTTP client. RemoteAddr string - // RequestURI is the unmodified Request-URI of the - // Request-Line (RFC 2616, Section 5.1) as sent by the client + // RequestURI is the unmodified request-target of the + // Request-Line (RFC 7230, Section 3.1.1) as sent by the client // to a server. Usually the URL field should be used instead. // It is an error to set this field in an HTTP client request. RequestURI string @@ -326,6 +339,10 @@ func (r *Request) Context() context.Context { // WithContext returns a shallow copy of r with its context changed // to ctx. The provided ctx must be non-nil. +// +// For outgoing client request, the context controls the entire +// lifetime of a request and its response: obtaining a connection, +// sending the request, and reading the response headers and body. func (r *Request) WithContext(ctx context.Context) *Request { if ctx == nil { panic("nil context") @@ -411,7 +428,7 @@ var multipartByReader = &multipart.Form{ } // MultipartReader returns a MIME multipart reader if this is a -// multipart/form-data POST request, else returns nil and an error. +// multipart/form-data or a multipart/mixed POST request, else returns nil and an error. // Use this function instead of ParseMultipartForm to // process the request body as a stream. func (r *Request) MultipartReader() (*multipart.Reader, error) { @@ -422,16 +439,16 @@ func (r *Request) MultipartReader() (*multipart.Reader, error) { return nil, errors.New("http: multipart handled by ParseMultipartForm") } r.MultipartForm = multipartByReader - return r.multipartReader() + return r.multipartReader(true) } -func (r *Request) multipartReader() (*multipart.Reader, error) { +func (r *Request) multipartReader(allowMixed bool) (*multipart.Reader, error) { v := r.Header.Get("Content-Type") if v == "" { return nil, ErrNotMultipart } d, params, err := mime.ParseMediaType(v) - if err != nil || d != "multipart/form-data" { + if err != nil || !(d == "multipart/form-data" || allowMixed && d == "multipart/mixed") { return nil, ErrNotMultipart } boundary, ok := params["boundary"] @@ -481,7 +498,7 @@ func (r *Request) Write(w io.Writer) error { // WriteProxy is like Write but writes the request in the form // expected by an HTTP proxy. In particular, WriteProxy writes the // initial Request-URI line of the request with an absolute URI, per -// section 5.1.2 of RFC 2616, including the scheme and host. +// section 5.3 of RFC 7230, including the scheme and host. // In either case, WriteProxy also writes a Host header, using // either r.Host or r.URL.Host. func (r *Request) WriteProxy(w io.Writer) error { @@ -550,6 +567,9 @@ func (r *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, waitF if err != nil { return err } + if trace != nil && trace.WroteHeaderField != nil { + trace.WroteHeaderField("Host", []string{host}) + } // Use the defaultUserAgent unless the Header contains one, which // may be blank to not send the header. @@ -562,6 +582,9 @@ func (r *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, waitF if err != nil { return err } + if trace != nil && trace.WroteHeaderField != nil { + trace.WroteHeaderField("User-Agent", []string{userAgent}) + } } // Process Body,ContentLength,Close,Trailer @@ -569,18 +592,18 @@ func (r *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, waitF if err != nil { return err } - err = tw.WriteHeader(w) + err = tw.writeHeader(w, trace) if err != nil { return err } - err = r.Header.WriteSubset(w, reqWriteExcludeHeader) + err = r.Header.writeSubset(w, reqWriteExcludeHeader, trace) if err != nil { return err } if extraHeaders != nil { - err = extraHeaders.Write(w) + err = extraHeaders.write(w, trace) if err != nil { return err } @@ -619,7 +642,7 @@ func (r *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, waitF } // Write body and trailer - err = tw.WriteBody(w) + err = tw.writeBody(w) if err != nil { if tw.bodyReadError == err { err = requestBodyReadError{err} @@ -858,7 +881,8 @@ func (r *Request) BasicAuth() (username, password string, ok bool) { // "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true). func parseBasicAuth(auth string) (username, password string, ok bool) { const prefix = "Basic " - if !strings.HasPrefix(auth, prefix) { + // Case insensitive prefix match. See Issue 22736. + if len(auth) < len(prefix) || !strings.EqualFold(auth[:len(prefix)], prefix) { return } c, err := base64.StdEncoding.DecodeString(auth[len(prefix):]) @@ -910,6 +934,11 @@ func putTextprotoReader(r *textproto.Reader) { } // ReadRequest reads and parses an incoming request from b. +// +// ReadRequest is a low-level function and should only be used for +// specialized applications; most code should use the Server to read +// requests and handle them via the Handler interface. ReadRequest +// only supports HTTP/1.x requests. For HTTP/2, use golang.org/x/net/http2. func ReadRequest(b *bufio.Reader) (*Request, error) { return readRequest(b, deleteHostHeader) } @@ -979,7 +1008,7 @@ func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *Request, err erro } req.Header = Header(mimeHeader) - // RFC 2616: Must treat + // RFC 7230, section 5.3: Must treat // GET /index.html HTTP/1.1 // Host: www.google.com // and @@ -1094,8 +1123,8 @@ func parsePostForm(r *Request) (vs url.Values, err error) { return } ct := r.Header.Get("Content-Type") - // RFC 2616, section 7.2.1 - empty type - // SHOULD be treated as application/octet-stream + // RFC 7231, section 3.1.1.5 - empty type + // MAY be treated as application/octet-stream if ct == "" { ct = "application/octet-stream" } @@ -1207,7 +1236,7 @@ func (r *Request) ParseMultipartForm(maxMemory int64) error { return nil } - mr, err := r.multipartReader() + mr, err := r.multipartReader(false) if err != nil { return err } @@ -1248,8 +1277,8 @@ func (r *Request) FormValue(key string) string { return "" } -// PostFormValue returns the first value for the named component of the POST -// or PUT request body. URL query parameters are ignored. +// PostFormValue returns the first value for the named component of the POST, +// PATCH, or PUT request body. URL query parameters are ignored. // PostFormValue calls ParseMultipartForm and ParseForm if necessary and ignores // any errors returned by these functions. // If key is not present, PostFormValue returns the empty string. diff --git a/libgo/go/net/http/request_test.go b/libgo/go/net/http/request_test.go index 967156b..7a83ae5 100644 --- a/libgo/go/net/http/request_test.go +++ b/libgo/go/net/http/request_test.go @@ -91,8 +91,8 @@ type parseContentTypeTest struct { var parseContentTypeTests = []parseContentTypeTest{ {false, stringMap{"Content-Type": {"text/plain"}}}, - // Empty content type is legal - should be treated as - // application/octet-stream (RFC 2616, section 7.2.1) + // Empty content type is legal - may be treated as + // application/octet-stream (RFC 7231, section 3.1.1.5) {false, stringMap{}}, {true, stringMap{"Content-Type": {"text/plain; boundary="}}}, {false, stringMap{"Content-Type": {"application/unknown"}}}, @@ -143,6 +143,16 @@ func TestMultipartReader(t *testing.T) { t.Errorf("expected multipart; error: %v", err) } + req = &Request{ + Method: "POST", + Header: Header{"Content-Type": {`multipart/mixed; boundary="foo123"`}}, + Body: ioutil.NopCloser(new(bytes.Buffer)), + } + multipart, err = req.MultipartReader() + if multipart == nil { + t.Errorf("expected multipart; error: %v", err) + } + req.Header = Header{"Content-Type": {"text/plain"}} multipart, err = req.MultipartReader() if multipart != nil { @@ -597,6 +607,11 @@ var parseBasicAuthTests = []struct { ok bool }{ {"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true}, + + // Case doesn't matter: + {"BASIC " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true}, + {"basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true}, + {"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open:sesame")), "Aladdin", "open:sesame", true}, {"Basic " + base64.StdEncoding.EncodeToString([]byte(":")), "", "", true}, {"Basic" + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false}, diff --git a/libgo/go/net/http/response.go b/libgo/go/net/http/response.go index a91efcf..bf1e13c 100644 --- a/libgo/go/net/http/response.go +++ b/libgo/go/net/http/response.go @@ -39,7 +39,7 @@ type Response struct { // Header maps header keys to values. If the response had multiple // headers with the same key, they may be concatenated, with comma - // delimiters. (Section 4.2 of RFC 2616 requires that multiple headers + // delimiters. (RFC 7230, section 3.2.2 requires that multiple headers // be semantically equivalent to a comma-delimited sequence.) When // Header values are duplicated by other fields in this struct (e.g., // ContentLength, TransferEncoding, Trailer), the field values are @@ -201,7 +201,7 @@ func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) { return resp, nil } -// RFC 2616: Should treat +// RFC 7234, section 5.4: Should treat // Pragma: no-cache // like // Cache-Control: no-cache @@ -293,7 +293,7 @@ func (r *Response) Write(w io.Writer) error { if err != nil { return err } - err = tw.WriteHeader(w) + err = tw.writeHeader(w, nil) if err != nil { return err } @@ -319,7 +319,7 @@ func (r *Response) Write(w io.Writer) error { } // Write body and trailer - err = tw.WriteBody(w) + err = tw.writeBody(w) if err != nil { return err } diff --git a/libgo/go/net/http/response_test.go b/libgo/go/net/http/response_test.go index 1ea1961..c28b0cb 100644 --- a/libgo/go/net/http/response_test.go +++ b/libgo/go/net/http/response_test.go @@ -295,7 +295,7 @@ var respTests = []respTest{ }, // Status line without a Reason-Phrase, but trailing space. - // (permitted by RFC 2616) + // (permitted by RFC 7230, section 3.1.2) { "HTTP/1.0 303 \r\n\r\n", Response{ @@ -314,7 +314,7 @@ var respTests = []respTest{ }, // Status line without a Reason-Phrase, and no trailing space. - // (not permitted by RFC 2616, but we'll accept it anyway) + // (not permitted by RFC 7230, but we'll accept it anyway) { "HTTP/1.0 303\r\n\r\n", Response{ diff --git a/libgo/go/net/http/roundtrip.go b/libgo/go/net/http/roundtrip.go new file mode 100644 index 0000000..2ec736b --- /dev/null +++ b/libgo/go/net/http/roundtrip.go @@ -0,0 +1,18 @@ +// Copyright 2018 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 !js !wasm + +package http + +// RoundTrip implements the RoundTripper interface. +// +// For higher-level HTTP client support (such as handling of cookies +// and redirects), see Get, Post, and the Client type. +// +// Like the RoundTripper interface, the error types returned +// by RoundTrip are unspecified. +func (t *Transport) RoundTrip(req *Request) (*Response, error) { + return t.roundTrip(req) +} diff --git a/libgo/go/net/http/roundtrip_js.go b/libgo/go/net/http/roundtrip_js.go new file mode 100644 index 0000000..16b7b89 --- /dev/null +++ b/libgo/go/net/http/roundtrip_js.go @@ -0,0 +1,293 @@ +// Copyright 2018 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 js,wasm + +package http + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "strconv" + "strings" + "syscall/js" +) + +// jsFetchMode is a Request.Header map key that, if present, +// signals that the map entry is actually an option to the Fetch API mode setting. +// Valid values are: "cors", "no-cors", "same-origin", "navigate" +// The default is "same-origin". +// +// Reference: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters +const jsFetchMode = "js.fetch:mode" + +// jsFetchCreds is a Request.Header map key that, if present, +// signals that the map entry is actually an option to the Fetch API credentials setting. +// Valid values are: "omit", "same-origin", "include" +// The default is "same-origin". +// +// Reference: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters +const jsFetchCreds = "js.fetch:credentials" + +// RoundTrip implements the RoundTripper interface using the WHATWG Fetch API. +func (t *Transport) RoundTrip(req *Request) (*Response, error) { + if useFakeNetwork() { + return t.roundTrip(req) + } + + ac := js.Global().Get("AbortController") + if ac != js.Undefined() { + // Some browsers that support WASM don't necessarily support + // the AbortController. See + // https://developer.mozilla.org/en-US/docs/Web/API/AbortController#Browser_compatibility. + ac = ac.New() + } + + opt := js.Global().Get("Object").New() + // See https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch + // for options available. + opt.Set("method", req.Method) + opt.Set("credentials", "same-origin") + if h := req.Header.Get(jsFetchCreds); h != "" { + opt.Set("credentials", h) + req.Header.Del(jsFetchCreds) + } + if h := req.Header.Get(jsFetchMode); h != "" { + opt.Set("mode", h) + req.Header.Del(jsFetchMode) + } + if ac != js.Undefined() { + opt.Set("signal", ac.Get("signal")) + } + headers := js.Global().Get("Headers").New() + for key, values := range req.Header { + for _, value := range values { + headers.Call("append", key, value) + } + } + opt.Set("headers", headers) + + if req.Body != nil { + // TODO(johanbrandhorst): Stream request body when possible. + // See https://bugs.chromium.org/p/chromium/issues/detail?id=688906 for Blink issue. + // See https://bugzilla.mozilla.org/show_bug.cgi?id=1387483 for Firefox issue. + // See https://github.com/web-platform-tests/wpt/issues/7693 for WHATWG tests issue. + // See https://developer.mozilla.org/en-US/docs/Web/API/Streams_API for more details on the Streams API + // and browser support. + body, err := ioutil.ReadAll(req.Body) + if err != nil { + req.Body.Close() // RoundTrip must always close the body, including on errors. + return nil, err + } + req.Body.Close() + a := js.TypedArrayOf(body) + defer a.Release() + opt.Set("body", a) + } + respPromise := js.Global().Call("fetch", req.URL.String(), opt) + var ( + respCh = make(chan *Response, 1) + errCh = make(chan error, 1) + ) + success := js.NewCallback(func(args []js.Value) { + result := args[0] + header := Header{} + // https://developer.mozilla.org/en-US/docs/Web/API/Headers/entries + headersIt := result.Get("headers").Call("entries") + for { + n := headersIt.Call("next") + if n.Get("done").Bool() { + break + } + pair := n.Get("value") + key, value := pair.Index(0).String(), pair.Index(1).String() + ck := CanonicalHeaderKey(key) + header[ck] = append(header[ck], value) + } + + contentLength := int64(0) + if cl, err := strconv.ParseInt(header.Get("Content-Length"), 10, 64); err == nil { + contentLength = cl + } + + b := result.Get("body") + var body io.ReadCloser + if b != js.Undefined() { + body = &streamReader{stream: b.Call("getReader")} + } else { + // Fall back to using ArrayBuffer + // https://developer.mozilla.org/en-US/docs/Web/API/Body/arrayBuffer + body = &arrayReader{arrayPromise: result.Call("arrayBuffer")} + } + + select { + case respCh <- &Response{ + Status: result.Get("status").String() + " " + StatusText(result.Get("status").Int()), + StatusCode: result.Get("status").Int(), + Header: header, + ContentLength: contentLength, + Body: body, + Request: req, + }: + case <-req.Context().Done(): + } + }) + defer success.Release() + failure := js.NewCallback(func(args []js.Value) { + err := fmt.Errorf("net/http: fetch() failed: %s", args[0].String()) + select { + case errCh <- err: + case <-req.Context().Done(): + } + }) + defer failure.Release() + respPromise.Call("then", success, failure) + select { + case <-req.Context().Done(): + if ac != js.Undefined() { + // Abort the Fetch request + ac.Call("abort") + } + return nil, req.Context().Err() + case resp := <-respCh: + return resp, nil + case err := <-errCh: + return nil, err + } +} + +var errClosed = errors.New("net/http: reader is closed") + +// useFakeNetwork is used to determine whether the request is made +// by a test and should be made to use the fake in-memory network. +func useFakeNetwork() bool { + return len(os.Args) > 0 && strings.HasSuffix(os.Args[0], ".test") +} + +// streamReader implements an io.ReadCloser wrapper for ReadableStream. +// See https://fetch.spec.whatwg.org/#readablestream for more information. +type streamReader struct { + pending []byte + stream js.Value + err error // sticky read error +} + +func (r *streamReader) Read(p []byte) (n int, err error) { + if r.err != nil { + return 0, r.err + } + if len(r.pending) == 0 { + var ( + bCh = make(chan []byte, 1) + errCh = make(chan error, 1) + ) + success := js.NewCallback(func(args []js.Value) { + result := args[0] + if result.Get("done").Bool() { + errCh <- io.EOF + return + } + value := make([]byte, result.Get("value").Get("byteLength").Int()) + a := js.TypedArrayOf(value) + a.Call("set", result.Get("value")) + a.Release() + bCh <- value + }) + defer success.Release() + failure := js.NewCallback(func(args []js.Value) { + // Assumes it's a TypeError. See + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError + // for more information on this type. See + // https://streams.spec.whatwg.org/#byob-reader-read for the spec on + // the read method. + errCh <- errors.New(args[0].Get("message").String()) + }) + defer failure.Release() + r.stream.Call("read").Call("then", success, failure) + select { + case b := <-bCh: + r.pending = b + case err := <-errCh: + r.err = err + return 0, err + } + } + n = copy(p, r.pending) + r.pending = r.pending[n:] + return n, nil +} + +func (r *streamReader) Close() error { + // This ignores any error returned from cancel method. So far, I did not encounter any concrete + // situation where reporting the error is meaningful. Most users ignore error from resp.Body.Close(). + // If there's a need to report error here, it can be implemented and tested when that need comes up. + r.stream.Call("cancel") + if r.err == nil { + r.err = errClosed + } + return nil +} + +// arrayReader implements an io.ReadCloser wrapper for ArrayBuffer. +// https://developer.mozilla.org/en-US/docs/Web/API/Body/arrayBuffer. +type arrayReader struct { + arrayPromise js.Value + pending []byte + read bool + err error // sticky read error +} + +func (r *arrayReader) Read(p []byte) (n int, err error) { + if r.err != nil { + return 0, r.err + } + if !r.read { + r.read = true + var ( + bCh = make(chan []byte, 1) + errCh = make(chan error, 1) + ) + success := js.NewCallback(func(args []js.Value) { + // Wrap the input ArrayBuffer with a Uint8Array + uint8arrayWrapper := js.Global().Get("Uint8Array").New(args[0]) + value := make([]byte, uint8arrayWrapper.Get("byteLength").Int()) + a := js.TypedArrayOf(value) + a.Call("set", uint8arrayWrapper) + a.Release() + bCh <- value + }) + defer success.Release() + failure := js.NewCallback(func(args []js.Value) { + // Assumes it's a TypeError. See + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError + // for more information on this type. + // See https://fetch.spec.whatwg.org/#concept-body-consume-body for reasons this might error. + errCh <- errors.New(args[0].Get("message").String()) + }) + defer failure.Release() + r.arrayPromise.Call("then", success, failure) + select { + case b := <-bCh: + r.pending = b + case err := <-errCh: + return 0, err + } + } + if len(r.pending) == 0 { + return 0, io.EOF + } + n = copy(p, r.pending) + r.pending = r.pending[n:] + return n, nil +} + +func (r *arrayReader) Close() error { + if r.err == nil { + r.err = errClosed + } + return nil +} diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go index 9cbfe87..a438541 100644 --- a/libgo/go/net/http/serve_test.go +++ b/libgo/go/net/http/serve_test.go @@ -134,14 +134,15 @@ func reqBytes(req string) []byte { } type handlerTest struct { + logbuf bytes.Buffer handler Handler } func newHandlerTest(h Handler) handlerTest { - return handlerTest{h} + return handlerTest{handler: h} } -func (ht handlerTest) rawResponse(req string) string { +func (ht *handlerTest) rawResponse(req string) string { reqb := reqBytes(req) var output bytes.Buffer conn := &rwTestConn{ @@ -150,7 +151,11 @@ func (ht handlerTest) rawResponse(req string) string { closec: make(chan bool, 1), } ln := &oneConnListener{conn: conn} - go Serve(ln, ht.handler) + srv := &Server{ + ErrorLog: log.New(&ht.logbuf, "", 0), + Handler: ht.handler, + } + go srv.Serve(ln) <-conn.closec return output.String() } @@ -379,6 +384,18 @@ func TestServeMuxHandler(t *testing.T) { } } +// Issue 24297 +func TestServeMuxHandleFuncWithNilHandler(t *testing.T) { + setParallel(t) + defer func() { + if err := recover(); err == nil { + t.Error("expected call to mux.HandleFunc to panic") + } + }() + mux := NewServeMux() + mux.HandleFunc("/", nil) +} + var serveMuxTests2 = []struct { method string host string @@ -581,8 +598,19 @@ func TestServeWithSlashRedirectForHostPatterns(t *testing.T) { } } -func BenchmarkServeMux(b *testing.B) { +func TestShouldRedirectConcurrency(t *testing.T) { + setParallel(t) + defer afterTest(t) + + mux := NewServeMux() + ts := httptest.NewServer(mux) + defer ts.Close() + mux.HandleFunc("/", func(w ResponseWriter, r *Request) {}) +} +func BenchmarkServeMux(b *testing.B) { benchmarkServeMux(b, true) } +func BenchmarkServeMux_SkipServe(b *testing.B) { benchmarkServeMux(b, false) } +func benchmarkServeMux(b *testing.B, runHandler bool) { type test struct { path string code int @@ -614,9 +642,11 @@ func BenchmarkServeMux(b *testing.B) { for _, tt := range tests { *rw = httptest.ResponseRecorder{} h, pattern := mux.Handler(tt.req) - h.ServeHTTP(rw, tt.req) - if pattern != tt.path || rw.Code != tt.code { - b.Fatalf("got %d, %q, want %d, %q", rw.Code, pattern, tt.code, tt.path) + if runHandler { + h.ServeHTTP(rw, tt.req) + if pattern != tt.path || rw.Code != tt.code { + b.Fatalf("got %d, %q, want %d, %q", rw.Code, pattern, tt.code, tt.path) + } } } } @@ -931,7 +961,7 @@ func TestOnlyWriteTimeout(t *testing.T) { if err == nil { t.Errorf("expected an error from Get request") } - case <-time.After(5 * time.Second): + case <-time.After(10 * time.Second): t.Fatal("timeout waiting for Get error") } if err := <-afterTimeoutErrc; err == nil { @@ -2294,6 +2324,9 @@ func testTimeoutHandler(t *testing.T, h2 bool) { if !strings.Contains(string(body), "<title>Timeout</title>") { t.Errorf("expected timeout body; got %q", string(body)) } + if g, w := res.Header.Get("Content-Type"), "text/html; charset=utf-8"; g != w { + t.Errorf("response content-type = %q; want %q", g, w) + } // Now make the previously-timed out handler speak again, // which verifies the panic is handled: @@ -2554,31 +2587,49 @@ func TestRedirect(t *testing.T) { for _, tt := range tests { rec := httptest.NewRecorder() Redirect(rec, req, tt.in, 302) + if got, want := rec.Code, 302; got != want { + t.Errorf("Redirect(%q) generated status code %v; want %v", tt.in, got, want) + } if got := rec.Header().Get("Location"); got != tt.want { t.Errorf("Redirect(%q) generated Location header %q; want %q", tt.in, got, tt.want) } } } -// Test that Content-Type header is set for GET and HEAD requests. -func TestRedirectContentTypeAndBody(t *testing.T) { +// Test that Redirect sets Content-Type header for GET and HEAD requests +// and writes a short HTML body, unless the request already has a Content-Type header. +func TestRedirect_contentTypeAndBody(t *testing.T) { + type ctHeader struct { + Values []string + } + var tests = []struct { method string + ct *ctHeader // Optional Content-Type header to set. wantCT string wantBody string }{ - {MethodGet, "text/html; charset=utf-8", "<a href=\"/foo\">Found</a>.\n\n"}, - {MethodHead, "text/html; charset=utf-8", ""}, - {MethodPost, "", ""}, - {MethodDelete, "", ""}, - {"foo", "", ""}, + {MethodGet, nil, "text/html; charset=utf-8", "<a href=\"/foo\">Found</a>.\n\n"}, + {MethodHead, nil, "text/html; charset=utf-8", ""}, + {MethodPost, nil, "", ""}, + {MethodDelete, nil, "", ""}, + {"foo", nil, "", ""}, + {MethodGet, &ctHeader{[]string{"application/test"}}, "application/test", ""}, + {MethodGet, &ctHeader{[]string{}}, "", ""}, + {MethodGet, &ctHeader{nil}, "", ""}, } for _, tt := range tests { req := httptest.NewRequest(tt.method, "http://example.com/qux/", nil) rec := httptest.NewRecorder() + if tt.ct != nil { + rec.Header()["Content-Type"] = tt.ct.Values + } Redirect(rec, req, "/foo", 302) + if got, want := rec.Code, 302; got != want { + t.Errorf("Redirect(%q, %#v) generated status code %v; want %v", tt.method, tt.ct, got, want) + } if got, want := rec.Header().Get("Content-Type"), tt.wantCT; got != want { - t.Errorf("Redirect(%q) generated Content-Type header %q; want %q", tt.method, got, want) + t.Errorf("Redirect(%q, %#v) generated Content-Type header %q; want %q", tt.method, tt.ct, got, want) } resp := rec.Result() body, err := ioutil.ReadAll(resp.Body) @@ -2586,7 +2637,7 @@ func TestRedirectContentTypeAndBody(t *testing.T) { t.Fatal(err) } if got, want := string(body), tt.wantBody; got != want { - t.Errorf("Redirect(%q) generated Body %q; want %q", tt.method, got, want) + t.Errorf("Redirect(%q, %#v) generated Body %q; want %q", tt.method, tt.ct, got, want) } } } @@ -3127,25 +3178,32 @@ For: ts.Close() } -// Tests that a pipelined request causes the first request's Handler's CloseNotify -// channel to fire. Previously it deadlocked. +// Tests that a pipelined request does not cause the first request's +// Handler's CloseNotify channel to fire. // -// Issue 13165 +// Issue 13165 (where it used to deadlock), but behavior changed in Issue 23921. func TestCloseNotifierPipelined(t *testing.T) { + setParallel(t) defer afterTest(t) gotReq := make(chan bool, 2) sawClose := make(chan bool, 2) ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { gotReq <- true cc := rw.(CloseNotifier).CloseNotify() - <-cc + select { + case <-cc: + t.Error("unexpected CloseNotify") + case <-time.After(100 * time.Millisecond): + } sawClose <- true })) + defer ts.Close() conn, err := net.Dial("tcp", ts.Listener.Addr().String()) if err != nil { t.Fatalf("error dialing: %v", err) } diec := make(chan bool, 1) + defer close(diec) go func() { const req = "GET / HTTP/1.1\r\nConnection: keep-alive\r\nHost: foo\r\n\r\n" _, err = io.WriteString(conn, req+req) // two requests @@ -3158,27 +3216,23 @@ func TestCloseNotifierPipelined(t *testing.T) { }() reqs := 0 closes := 0 -For: for { select { case <-gotReq: reqs++ if reqs > 2 { t.Fatal("too many requests") - } else if reqs > 1 { - diec <- true } case <-sawClose: closes++ if closes > 1 { - break For + return } case <-time.After(5 * time.Second): ts.CloseClientConnections() t.Fatal("timeout") } } - ts.Close() } func TestCloseNotifierChanLeak(t *testing.T) { @@ -3377,14 +3431,14 @@ func TestHeaderToWire(t *testing.T) { tests := []struct { name string handler func(ResponseWriter, *Request) - check func(output string) error + check func(got, logs string) error }{ { name: "write without Header", handler: func(rw ResponseWriter, r *Request) { rw.Write([]byte("hello world")) }, - check: func(got string) error { + check: func(got, logs string) error { if !strings.Contains(got, "Content-Length:") { return errors.New("no content-length") } @@ -3402,7 +3456,7 @@ func TestHeaderToWire(t *testing.T) { rw.Write([]byte("hello world")) h.Set("Too-Late", "bogus") }, - check: func(got string) error { + check: func(got, logs string) error { if !strings.Contains(got, "Content-Length:") { return errors.New("no content-length") } @@ -3421,7 +3475,7 @@ func TestHeaderToWire(t *testing.T) { rw.Write([]byte("hello world")) rw.Header().Set("Too-Late", "Write already wrote headers") }, - check: func(got string) error { + check: func(got, logs string) error { if strings.Contains(got, "Too-Late") { return errors.New("header appeared from after WriteHeader") } @@ -3435,7 +3489,7 @@ func TestHeaderToWire(t *testing.T) { rw.Write([]byte("post-flush")) rw.Header().Set("Too-Late", "Write already wrote headers") }, - check: func(got string) error { + check: func(got, logs string) error { if !strings.Contains(got, "Transfer-Encoding: chunked") { return errors.New("not chunked") } @@ -3453,7 +3507,7 @@ func TestHeaderToWire(t *testing.T) { rw.Write([]byte("post-flush")) rw.Header().Set("Too-Late", "Write already wrote headers") }, - check: func(got string) error { + check: func(got, logs string) error { if !strings.Contains(got, "Transfer-Encoding: chunked") { return errors.New("not chunked") } @@ -3472,7 +3526,7 @@ func TestHeaderToWire(t *testing.T) { rw.Write([]byte("<html><head></head><body>some html</body></html>")) rw.Header().Set("Content-Type", "x/wrong") }, - check: func(got string) error { + check: func(got, logs string) error { if !strings.Contains(got, "Content-Type: text/html") { return errors.New("wrong content-type; want html") } @@ -3485,7 +3539,7 @@ func TestHeaderToWire(t *testing.T) { rw.Header().Set("Content-Type", "some/type") rw.Write([]byte("<html><head></head><body>some html</body></html>")) }, - check: func(got string) error { + check: func(got, logs string) error { if !strings.Contains(got, "Content-Type: some/type") { return errors.New("wrong content-type; want html") } @@ -3496,7 +3550,7 @@ func TestHeaderToWire(t *testing.T) { name: "empty handler", handler: func(rw ResponseWriter, r *Request) { }, - check: func(got string) error { + check: func(got, logs string) error { if !strings.Contains(got, "Content-Length: 0") { return errors.New("want 0 content-length") } @@ -3508,7 +3562,7 @@ func TestHeaderToWire(t *testing.T) { handler: func(rw ResponseWriter, r *Request) { rw.Header().Set("Some-Header", "some-value") }, - check: func(got string) error { + check: func(got, logs string) error { if !strings.Contains(got, "Some-Header") { return errors.New("didn't get header") } @@ -3521,7 +3575,7 @@ func TestHeaderToWire(t *testing.T) { rw.WriteHeader(404) rw.Header().Set("Too-Late", "some-value") }, - check: func(got string) error { + check: func(got, logs string) error { if !strings.Contains(got, "404") { return errors.New("wrong status") } @@ -3535,8 +3589,9 @@ func TestHeaderToWire(t *testing.T) { for _, tc := range tests { ht := newHandlerTest(HandlerFunc(tc.handler)) got := ht.rawResponse("GET / HTTP/1.1\nHost: golang.org") - if err := tc.check(got); err != nil { - t.Errorf("%s: %v\nGot response:\n%s", tc.name, err, got) + logs := ht.logbuf.String() + if err := tc.check(got, logs); err != nil { + t.Errorf("%s: %v\nGot response:\n%s\n\n%s", tc.name, err, got, logs) } } } @@ -5493,6 +5548,76 @@ func testServerShutdown(t *testing.T, h2 bool) { } } +func TestServerShutdownStateNew(t *testing.T) { + if testing.Short() { + t.Skip("test takes 5-6 seconds; skipping in short mode") + } + setParallel(t) + defer afterTest(t) + + ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) { + // nothing. + })) + var connAccepted sync.WaitGroup + ts.Config.ConnState = func(conn net.Conn, state ConnState) { + if state == StateNew { + connAccepted.Done() + } + } + ts.Start() + defer ts.Close() + + // Start a connection but never write to it. + connAccepted.Add(1) + c, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + // Wait for the connection to be accepted by the server. Otherwise, if + // Shutdown happens to run first, the server will be closed when + // encountering the connection, in which case it will be rejected + // immediately. + connAccepted.Wait() + + shutdownRes := make(chan error, 1) + go func() { + shutdownRes <- ts.Config.Shutdown(context.Background()) + }() + readRes := make(chan error, 1) + go func() { + _, err := c.Read([]byte{0}) + readRes <- err + }() + + const expectTimeout = 5 * time.Second + t0 := time.Now() + select { + case got := <-shutdownRes: + d := time.Since(t0) + if got != nil { + t.Fatalf("shutdown error after %v: %v", d, err) + } + if d < expectTimeout/2 { + t.Errorf("shutdown too soon after %v", d) + } + case <-time.After(expectTimeout * 3 / 2): + t.Fatalf("timeout waiting for shutdown") + } + + // Wait for c.Read to unblock; should be already done at this point, + // or within a few milliseconds. + select { + case err := <-readRes: + if err == nil { + t.Error("expected error from Read") + } + case <-time.After(2 * time.Second): + t.Errorf("timeout waiting for Read to unblock") + } +} + // Issue 17878: tests that we can call Close twice. func TestServerCloseDeadlock(t *testing.T) { var s Server @@ -5590,6 +5715,10 @@ func runTimeSensitiveTest(t *testing.T, durations []time.Duration, test func(t * // Issue 18535: test that the Server doesn't try to do a background // read if it's already done one. func TestServerDuplicateBackgroundRead(t *testing.T) { + if runtime.GOOS == "netbsd" && runtime.GOARCH == "arm" { + testenv.SkipFlaky(t, 24826) + } + setParallel(t) defer afterTest(t) @@ -5653,31 +5782,23 @@ func TestServerHijackGetsBackgroundByte(t *testing.T) { // Tell the client to send more data after the GET request. inHandler <- true - // Wait until the HTTP server sees the extra data - // after the GET request. The HTTP server fires the - // close notifier here, assuming it's a pipelined - // request, as documented. - select { - case <-w.(CloseNotifier).CloseNotify(): - case <-time.After(5 * time.Second): - t.Error("timeout") - return - } - conn, buf, err := w.(Hijacker).Hijack() if err != nil { t.Error(err) return } defer conn.Close() - n := buf.Reader.Buffered() - if n != 1 { - t.Errorf("buffered data = %d; want 1", n) - } + peek, err := buf.Reader.Peek(3) if string(peek) != "foo" || err != nil { t.Errorf("Peek = %q, %v; want foo, nil", peek, err) } + + select { + case <-r.Context().Done(): + t.Error("context unexpectedly canceled") + default: + } })) defer ts.Close() @@ -5718,17 +5839,6 @@ func TestServerHijackGetsBackgroundByte_big(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { defer close(done) - // Wait until the HTTP server sees the extra data - // after the GET request. The HTTP server fires the - // close notifier here, assuming it's a pipelined - // request, as documented. - select { - case <-w.(CloseNotifier).CloseNotify(): - case <-time.After(5 * time.Second): - t.Error("timeout") - return - } - conn, buf, err := w.(Hijacker).Hijack() if err != nil { t.Error(err) @@ -5800,6 +5910,94 @@ func TestServerValidatesMethod(t *testing.T) { } } +// Listener for TestServerListenNotComparableListener. +type eofListenerNotComparable []int + +func (eofListenerNotComparable) Accept() (net.Conn, error) { return nil, io.EOF } +func (eofListenerNotComparable) Addr() net.Addr { return nil } +func (eofListenerNotComparable) Close() error { return nil } + +// Issue 24812: don't crash on non-comparable Listener +func TestServerListenNotComparableListener(t *testing.T) { + var s Server + s.Serve(make(eofListenerNotComparable, 1)) // used to panic +} + +// countCloseListener is a Listener wrapper that counts the number of Close calls. +type countCloseListener struct { + net.Listener + closes int32 // atomic +} + +func (p *countCloseListener) Close() error { + atomic.AddInt32(&p.closes, 1) + return nil +} + +// Issue 24803: don't call Listener.Close on Server.Shutdown. +func TestServerCloseListenerOnce(t *testing.T) { + setParallel(t) + defer afterTest(t) + + ln := newLocalListener(t) + defer ln.Close() + + cl := &countCloseListener{Listener: ln} + server := &Server{} + sdone := make(chan bool, 1) + + go func() { + server.Serve(cl) + sdone <- true + }() + time.Sleep(10 * time.Millisecond) + server.Shutdown(context.Background()) + ln.Close() + <-sdone + + nclose := atomic.LoadInt32(&cl.closes) + if nclose != 1 { + t.Errorf("Close calls = %v; want 1", nclose) + } +} + +// Issue 20239: don't block in Serve if Shutdown is called first. +func TestServerShutdownThenServe(t *testing.T) { + var srv Server + cl := &countCloseListener{Listener: nil} + srv.Shutdown(context.Background()) + got := srv.Serve(cl) + if got != ErrServerClosed { + t.Errorf("Serve err = %v; want ErrServerClosed", got) + } + nclose := atomic.LoadInt32(&cl.closes) + if nclose != 1 { + t.Errorf("Close calls = %v; want 1", nclose) + } +} + +// Issue 23351: document and test behavior of ServeMux with ports +func TestStripPortFromHost(t *testing.T) { + mux := NewServeMux() + + mux.HandleFunc("example.com/", func(w ResponseWriter, r *Request) { + fmt.Fprintf(w, "OK") + }) + mux.HandleFunc("example.com:9000/", func(w ResponseWriter, r *Request) { + fmt.Fprintf(w, "uh-oh!") + }) + + req := httptest.NewRequest("GET", "http://example.com:9000/", nil) + rw := httptest.NewRecorder() + + mux.ServeHTTP(rw, req) + + response := rw.Body.String() + if response != "OK" { + t.Errorf("Response gotten was %q", response) + } +} + func BenchmarkResponseStatusLine(b *testing.B) { b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go index 57e1b5d..c24ad75 100644 --- a/libgo/go/net/http/server.go +++ b/libgo/go/net/http/server.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. -// HTTP server. See RFC 2616. +// HTTP server. See RFC 7230 through 7235. package http @@ -28,7 +28,7 @@ import ( "sync/atomic" "time" - "golang_org/x/net/lex/httplex" + "golang_org/x/net/http/httpguts" ) // Errors used by the HTTP server. @@ -51,7 +51,9 @@ var ( // declared. ErrContentLength = errors.New("http: wrote more than the declared Content-Length") - // Deprecated: ErrWriteAfterFlush is no longer used. + // Deprecated: ErrWriteAfterFlush is no longer returned by + // anything in the net/http package. Callers should not + // compare errors against this variable. ErrWriteAfterFlush = errors.New("unused") ) @@ -107,7 +109,7 @@ type ResponseWriter interface { // is to prefix the Header map keys with the TrailerPrefix // constant value. See TrailerPrefix. // - // To suppress implicit response headers (such as "Date"), set + // To suppress automatic response headers (such as "Date"), set // their value to nil. Header() Header @@ -117,7 +119,9 @@ type ResponseWriter interface { // WriteHeader(http.StatusOK) before writing the data. If the Header // does not contain a Content-Type line, Write adds a Content-Type set // to the result of passing the initial 512 bytes of written data to - // DetectContentType. + // DetectContentType. Additionally, if the total size of all written + // data is under a few KB and there are no Flush calls, the + // Content-Length header is added automatically. // // Depending on the HTTP protocol version and the client, calling // Write or WriteHeader may prevent future reads on the @@ -187,8 +191,10 @@ type Hijacker interface { // The returned bufio.Reader may contain unprocessed buffered // data from the client. // - // After a call to Hijack, the original Request.Body must - // not be used. + // After a call to Hijack, the original Request.Body must not + // be used. The original Request's Context remains valid and + // is not canceled until the Request's ServeHTTP method + // returns. Hijack() (net.Conn, *bufio.ReadWriter, error) } @@ -197,6 +203,9 @@ type Hijacker interface { // // This mechanism can be used to cancel long operations on the server // if the client has disconnected before the response is ready. +// +// Deprecated: the CloseNotifier interface predates Go's context package. +// New code should use Request.Context instead. type CloseNotifier interface { // CloseNotify returns a channel that receives at most a // single value (true) when the client connection has gone @@ -227,8 +236,8 @@ var ( ServerContextKey = &contextKey{"http-server"} // LocalAddrContextKey is a context key. It can be used in - // HTTP handlers with context.WithValue to access the address - // the local address the connection arrived on. + // HTTP handlers with context.WithValue to access the local + // address the connection arrived on. // The associated value will be of type net.Addr. LocalAddrContextKey = &contextKey{"local-addr"} ) @@ -279,7 +288,7 @@ type conn struct { curReq atomic.Value // of *response (which has a Request in it) - curState atomic.Value // of ConnState + curState struct{ atomic uint64 } // packed (unixtime<<8|uint8(ConnState)) // mu guards hijackedv mu sync.Mutex @@ -334,7 +343,7 @@ type chunkWriter struct { res *response // header is either nil or a deep clone of res.handlerHeader - // at the time of res.WriteHeader, if res.WriteHeader is + // at the time of res.writeHeader, if res.writeHeader is // called and extra buffering is being done to calculate // Content-Type and/or Content-Length. header Header @@ -510,9 +519,8 @@ func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } // written in the trailers at the end of the response. func (w *response) declareTrailer(k string) { k = CanonicalHeaderKey(k) - switch k { - case "Transfer-Encoding", "Content-Length", "Trailer": - // Forbidden by RFC 2616 14.40. + if !httpguts.ValidTrailerHeader(k) { + // Forbidden by RFC 7230, section 4.1.2 return } w.trailers = append(w.trailers, k) @@ -669,10 +677,28 @@ func (cr *connReader) backgroundRead() { cr.lock() if n == 1 { cr.hasByte = true - // We were at EOF already (since we wouldn't be in a - // background read otherwise), so this is a pipelined - // HTTP request. - cr.closeNotifyFromPipelinedRequest() + // We were past the end of the previous request's body already + // (since we wouldn't be in a background read otherwise), so + // this is a pipelined HTTP request. Prior to Go 1.11 we used to + // send on the CloseNotify channel and cancel the context here, + // but the behavior was documented as only "may", and we only + // did that because that's how CloseNotify accidentally behaved + // in very early Go releases prior to context support. Once we + // added context support, people used a Handler's + // Request.Context() and passed it along. Having that context + // cancel on pipelined HTTP requests caused problems. + // Fortunately, almost nothing uses HTTP/1.x pipelining. + // Unfortunately, apt-get does, or sometimes does. + // New Go 1.11 behavior: don't fire CloseNotify or cancel + // contexts on pipelined requests. Shouldn't affect people, but + // fixes cases like Issue 23921. This does mean that a client + // closing their TCP connection after sending a pipelined + // request won't cancel the context, but we'll catch that on any + // write failure (in checkConnErrorWriter.Write). + // If the server never writes, yes, there are still contrived + // server & client behaviors where this fails to ever cancel the + // context, but that's kinda why HTTP/1.x pipelining died + // anyway. } if ne, ok := err.(net.Error); ok && cr.aborted && ne.Timeout() { // Ignore this error. It's the expected error from @@ -704,22 +730,18 @@ func (cr *connReader) setReadLimit(remain int64) { cr.remain = remain } func (cr *connReader) setInfiniteReadLimit() { cr.remain = maxInt64 } func (cr *connReader) hitReadLimit() bool { return cr.remain <= 0 } -// may be called from multiple goroutines. -func (cr *connReader) handleReadError(err error) { - cr.conn.cancelCtx() - cr.closeNotify() -} - -// closeNotifyFromPipelinedRequest simply calls closeNotify. +// handleReadError is called whenever a Read from the client returns a +// non-nil error. // -// This method wrapper is here for documentation. The callers are the -// cases where we send on the closenotify channel because of a -// pipelined HTTP request, per the previous Go behavior and -// documentation (that this "MAY" happen). +// The provided non-nil err is almost always io.EOF or a "use of +// closed network connection". In any case, the error is not +// particularly interesting, except perhaps for debugging during +// development. Any error means the connection is dead and we should +// down its context. // -// TODO: consider changing this behavior and making context -// cancelation and closenotify work the same. -func (cr *connReader) closeNotifyFromPipelinedRequest() { +// It may be called from multiple goroutines. +func (cr *connReader) handleReadError(_ error) { + cr.conn.cancelCtx() cr.closeNotify() } @@ -937,7 +959,7 @@ func (c *conn) readRequest(ctx context.Context) (w *response, err error) { c.r.setReadLimit(c.server.initialReadLimitSize()) if c.lastMethod == "POST" { - // RFC 2616 section 4.1 tolerance for old buggy clients. + // RFC 7230 section 3 tolerance for old buggy clients. peek, _ := c.bufr.Peek(4) // ReadRequest will get err below c.bufr.Discard(numLeadingCRorLF(peek)) } @@ -964,15 +986,15 @@ func (c *conn) readRequest(ctx context.Context) (w *response, err error) { if len(hosts) > 1 { return nil, badRequestError("too many Host headers") } - if len(hosts) == 1 && !httplex.ValidHostHeader(hosts[0]) { + if len(hosts) == 1 && !httpguts.ValidHostHeader(hosts[0]) { return nil, badRequestError("malformed Host header") } for k, vv := range req.Header { - if !httplex.ValidHeaderFieldName(k) { + if !httpguts.ValidHeaderFieldName(k) { return nil, badRequestError("invalid header name") } for _, v := range vv { - if !httplex.ValidHeaderFieldValue(v) { + if !httpguts.ValidHeaderFieldValue(v) { return nil, badRequestError("invalid header value") } } @@ -1058,7 +1080,7 @@ func checkWriteHeaderCode(code int) { // Issue 22880: require valid WriteHeader status codes. // For now we only enforce that it's three digits. // In the future we might block things over 599 (600 and above aren't defined - // at http://httpwg.org/specs/rfc7231.html#status.codes) + // at https://httpwg.org/specs/rfc7231.html#status.codes) // and we might block under 200 (once we have more mature 1xx support). // But for now any three digits. // @@ -1414,7 +1436,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { } // foreachHeaderElement splits v according to the "#rule" construction -// in RFC 2616 section 2.1 and calls fn for each non-empty element. +// in RFC 7230 section 7 and calls fn for each non-empty element. func foreachHeaderElement(v string, fn func(string)) { v = textproto.TrimString(v) if v == "" { @@ -1431,7 +1453,7 @@ func foreachHeaderElement(v string, fn func(string)) { } } -// writeStatusLine writes an HTTP/1.x Status-Line (RFC 2616 Section 6.1) +// writeStatusLine writes an HTTP/1.x Status-Line (RFC 7230 Section 3.1.2) // to bw. is11 is whether the HTTP request is HTTP/1.1. false means HTTP/1.0. // code is the response status code. // scratch is an optional scratch buffer. If it has at least capacity 3, it's used. @@ -1668,21 +1690,19 @@ func (c *conn) setState(nc net.Conn, state ConnState) { case StateHijacked, StateClosed: srv.trackConn(c, false) } - c.curState.Store(connStateInterface[state]) + if state > 0xff || state < 0 { + panic("internal error") + } + packedState := uint64(time.Now().Unix()<<8) | uint64(state) + atomic.StoreUint64(&c.curState.atomic, packedState) if hook := srv.ConnState; hook != nil { hook(nc, state) } } -// connStateInterface is an array of the interface{} versions of -// ConnState values, so we can use them in atomic.Values later without -// paying the cost of shoving their integers in an interface{}. -var connStateInterface = [...]interface{}{ - StateNew: StateNew, - StateActive: StateActive, - StateIdle: StateIdle, - StateHijacked: StateHijacked, - StateClosed: StateClosed, +func (c *conn) getState() (state ConnState, unixSec int64) { + packedState := atomic.LoadUint64(&c.curState.atomic) + return ConnState(packedState & 0xff), int64(packedState >> 8) } // badRequestError is a literal string (used by in the server in HTML, @@ -1814,9 +1834,6 @@ func (c *conn) serve(ctx context.Context) { if requestBodyRemains(req.Body) { registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead) } else { - if w.conn.bufr.Buffered() > 0 { - w.conn.r.closeNotifyFromPipelinedRequest() - } w.conn.r.startBackgroundRead() } @@ -1868,11 +1885,11 @@ func (w *response) sendExpectationFailed() { // make the ResponseWriter an optional // "ExpectReplier" interface or something. // - // For now we'll just obey RFC 2616 14.20 which says - // "If a server receives a request containing an - // Expect field that includes an expectation- - // extension that it does not support, it MUST - // respond with a 417 (Expectation Failed) status." + // For now we'll just obey RFC 7231 5.1.1 which says + // "A server that receives an Expect field-value other + // than 100-continue MAY respond with a 417 (Expectation + // Failed) status code to indicate that the unexpected + // expectation cannot be met." w.Header().Set("Connection", "close") w.WriteHeader(StatusExpectationFailed) w.finishRequest() @@ -1995,25 +2012,19 @@ func StripPrefix(prefix string, h Handler) Handler { // // The provided code should be in the 3xx range and is usually // StatusMovedPermanently, StatusFound or StatusSeeOther. +// +// If the Content-Type header has not been set, Redirect sets it +// to "text/html; charset=utf-8" and writes a small HTML body. +// Setting the Content-Type header to any value, including nil, +// disables that behavior. func Redirect(w ResponseWriter, r *Request, url string, code int) { // parseURL is just url.Parse (url is shadowed for godoc). if u, err := parseURL(url); err == nil { - // If url was relative, make absolute by + // If url was relative, make its path absolute by // combining with request path. - // The browser would probably do this for us, + // The client would probably do this for us, // but doing it ourselves is more reliable. - - // NOTE(rsc): RFC 2616 says that the Location - // line must be an absolute URI, like - // "http://www.google.com/redirect/", - // not a path like "/redirect/". - // Unfortunately, we don't know what to - // put in the host name section to get the - // client to connect to us again, so we can't - // know the right absolute URI to send back. - // Because of this problem, no one pays attention - // to the RFC; they all send back just a new path. - // So do we. + // See RFC 7231, section 7.1.2 if u.Scheme == "" && u.Host == "" { oldpath := r.URL.Path if oldpath == "" { // should not happen, but avoid a crash if it does @@ -2042,18 +2053,23 @@ func Redirect(w ResponseWriter, r *Request, url string, code int) { } } - w.Header().Set("Location", hexEscapeNonASCII(url)) - if r.Method == "GET" || r.Method == "HEAD" { - w.Header().Set("Content-Type", "text/html; charset=utf-8") + h := w.Header() + + // RFC 7231 notes that a short HTML body is usually included in + // the response because older user agents may not understand 301/307. + // Do it only if the request didn't already have a Content-Type header. + _, hadCT := h["Content-Type"] + + h.Set("Location", hexEscapeNonASCII(url)) + if !hadCT && (r.Method == "GET" || r.Method == "HEAD") { + h.Set("Content-Type", "text/html; charset=utf-8") } w.WriteHeader(code) - // RFC 2616 recommends that a short note "SHOULD" be included in the - // response because older user agents may not understand 301/307. - // Shouldn't send the response for POST or HEAD; that leaves GET. - if r.Method == "GET" { - note := "<a href=\"" + htmlEscape(url) + "\">" + statusText[code] + "</a>.\n" - fmt.Fprintln(w, note) + // Shouldn't send the body for POST or HEAD; that leaves GET. + if !hadCT && r.Method == "GET" { + body := "<a href=\"" + htmlEscape(url) + "\">" + statusText[code] + "</a>.\n" + fmt.Fprintln(w, body) } } @@ -2127,9 +2143,9 @@ func RedirectHandler(url string, code int) Handler { // "/codesearch" and "codesearch.google.com/" without also taking over // requests for "http://www.google.com/". // -// ServeMux also takes care of sanitizing the URL request path, -// redirecting any request containing . or .. elements or repeated slashes -// to an equivalent, cleaner URL. +// ServeMux also takes care of sanitizing the URL request path and the Host +// header, stripping the port number and redirecting any request containing . or +// .. elements or repeated slashes to an equivalent, cleaner URL. type ServeMux struct { mu sync.RWMutex m map[string]muxEntry @@ -2162,7 +2178,7 @@ func pathMatch(pattern, path string) bool { return len(path) >= n && path[0:n] == pattern } -// Return the canonical path for p, eliminating . and .. elements. +// cleanPath returns the canonical path for p, eliminating . and .. elements. func cleanPath(p string) string { if p == "" { return "/" @@ -2174,7 +2190,12 @@ func cleanPath(p string) string { // path.Clean removes trailing slash except for root; // put the trailing slash back if necessary. if p[len(p)-1] == '/' && np != "/" { - np += "/" + // Fast path for common case of p being the string we want: + if len(p) == len(np)+1 && strings.HasPrefix(p, np) { + np = p + } else { + np += "/" + } } return np } @@ -2221,7 +2242,10 @@ func (mux *ServeMux) match(path string) (h Handler, pattern string) { // not for path itself. If the path needs appending to, it creates a new // URL, setting the path to u.Path + "/" and returning true to indicate so. func (mux *ServeMux) redirectToPathSlash(host, path string, u *url.URL) (*url.URL, bool) { - if !mux.shouldRedirect(host, path) { + mux.mu.RLock() + shouldRedirect := mux.shouldRedirectRLocked(host, path) + mux.mu.RUnlock() + if !shouldRedirect { return u, false } path = path + "/" @@ -2229,10 +2253,10 @@ func (mux *ServeMux) redirectToPathSlash(host, path string, u *url.URL) (*url.UR return u, true } -// shouldRedirect reports whether the given path and host should be redirected to +// shouldRedirectRLocked reports whether the given path and host should be redirected to // path+"/". This should happen if a handler is registered for path+"/" but // not path -- see comments at ServeMux. -func (mux *ServeMux) shouldRedirect(host, path string) bool { +func (mux *ServeMux) shouldRedirectRLocked(host, path string) bool { p := []string{path, host + path} for _, c := range p { @@ -2365,6 +2389,9 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) { // HandleFunc registers the handler function for the given pattern. func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { + if handler == nil { + panic("http: nil handler") + } mux.Handle(pattern, HandlerFunc(handler)) } @@ -2383,7 +2410,14 @@ func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { // Serve accepts incoming HTTP connections on the listener l, // creating a new service goroutine for each. The service goroutines // read requests and then call handler to reply to them. -// Handler is typically nil, in which case the DefaultServeMux is used. +// +// The handler is typically nil, in which case the DefaultServeMux is used. +// +// HTTP/2 support is only enabled if the Listener returns *tls.Conn +// connections and they were configured with "h2" in the TLS +// Config.NextProtos. +// +// Serve always returns a non-nil error. func Serve(l net.Listener, handler Handler) error { srv := &Server{Handler: handler} return srv.Serve(l) @@ -2393,12 +2427,14 @@ func Serve(l net.Listener, handler Handler) error { // creating a new service goroutine for each. The service goroutines // read requests and then call handler to reply to them. // -// Handler is typically nil, in which case the DefaultServeMux is used. +// The handler is typically nil, in which case the DefaultServeMux is used. // // Additionally, files containing a certificate and matching private key // for the server must be provided. If the certificate is signed by a // certificate authority, the certFile should be the concatenation // of the server's certificate, any intermediates, and the CA's certificate. +// +// ServeTLS always returns a non-nil error. func ServeTLS(l net.Listener, handler Handler, certFile, keyFile string) error { srv := &Server{Handler: handler} return srv.ServeTLS(l, certFile, keyFile) @@ -2481,7 +2517,7 @@ type Server struct { nextProtoErr error // result of http2.ConfigureServer if used mu sync.Mutex - listeners map[net.Listener]struct{} + listeners map[*net.Listener]struct{} activeConn map[*conn]struct{} doneChan chan struct{} onShutdown []func() @@ -2522,6 +2558,7 @@ func (s *Server) closeDoneChanLocked() { // Close returns any error returned from closing the Server's // underlying Listener(s). func (srv *Server) Close() error { + atomic.StoreInt32(&srv.inShutdown, 1) srv.mu.Lock() defer srv.mu.Unlock() srv.closeDoneChanLocked() @@ -2559,9 +2596,11 @@ var shutdownPollInterval = 500 * time.Millisecond // separately notify such long-lived connections of shutdown and wait // for them to close, if desired. See RegisterOnShutdown for a way to // register shutdown notification functions. +// +// Once Shutdown has been called on a server, it may not be reused; +// future calls to methods such as Serve will return ErrServerClosed. func (srv *Server) Shutdown(ctx context.Context) error { - atomic.AddInt32(&srv.inShutdown, 1) - defer atomic.AddInt32(&srv.inShutdown, -1) + atomic.StoreInt32(&srv.inShutdown, 1) srv.mu.Lock() lnerr := srv.closeListenersLocked() @@ -2603,8 +2642,16 @@ func (s *Server) closeIdleConns() bool { defer s.mu.Unlock() quiescent := true for c := range s.activeConn { - st, ok := c.curState.Load().(ConnState) - if !ok || st != StateIdle { + st, unixSec := c.getState() + // Issue 22682: treat StateNew connections as if + // they're idle if we haven't read the first request's + // header in over 5 seconds. + if st == StateNew && unixSec < time.Now().Unix()-5 { + st = StateIdle + } + if st != StateIdle || unixSec == 0 { + // Assume unixSec == 0 means it's a very new + // connection, without state set yet. quiescent = false continue } @@ -2617,7 +2664,7 @@ func (s *Server) closeIdleConns() bool { func (s *Server) closeListenersLocked() error { var err error for ln := range s.listeners { - if cerr := ln.Close(); cerr != nil && err == nil { + if cerr := (*ln).Close(); cerr != nil && err == nil { err = cerr } delete(s.listeners, ln) @@ -2697,9 +2744,15 @@ func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { // ListenAndServe listens on the TCP network address srv.Addr and then // calls Serve to handle requests on incoming connections. // Accepted connections are configured to enable TCP keep-alives. +// // If srv.Addr is blank, ":http" is used. -// ListenAndServe always returns a non-nil error. +// +// ListenAndServe always returns a non-nil error. After Shutdown or Close, +// the returned error is ErrServerClosed. func (srv *Server) ListenAndServe() error { + if srv.shuttingDown() { + return ErrServerClosed + } addr := srv.Addr if addr == "" { addr = ":http" @@ -2743,27 +2796,30 @@ var ErrServerClosed = errors.New("http: Server closed") // new service goroutine for each. The service goroutines read requests and // then call srv.Handler to reply to them. // -// For HTTP/2 support, srv.TLSConfig should be initialized to the -// provided listener's TLS Config before calling Serve. If -// srv.TLSConfig is non-nil and doesn't include the string "h2" in -// Config.NextProtos, HTTP/2 support is not enabled. +// HTTP/2 support is only enabled if the Listener returns *tls.Conn +// connections and they were configured with "h2" in the TLS +// Config.NextProtos. // -// Serve always returns a non-nil error. After Shutdown or Close, the -// returned error is ErrServerClosed. +// Serve always returns a non-nil error and closes l. +// After Shutdown or Close, the returned error is ErrServerClosed. func (srv *Server) Serve(l net.Listener) error { - defer l.Close() if fn := testHookServerServe; fn != nil { - fn(srv, l) + fn(srv, l) // call hook with unwrapped listener } - var tempDelay time.Duration // how long to sleep on accept failure + + l = &onceCloseListener{Listener: l} + defer l.Close() if err := srv.setupHTTP2_Serve(); err != nil { return err } - srv.trackListener(l, true) - defer srv.trackListener(l, false) + if !srv.trackListener(&l, true) { + return ErrServerClosed + } + defer srv.trackListener(&l, false) + var tempDelay time.Duration // how long to sleep on accept failure baseCtx := context.Background() // base is always background, per Issue 16220 ctx := context.WithValue(baseCtx, ServerContextKey, srv) for { @@ -2797,19 +2853,15 @@ func (srv *Server) Serve(l net.Listener) error { } // ServeTLS accepts incoming connections on the Listener l, creating a -// new service goroutine for each. The service goroutines read requests and -// then call srv.Handler to reply to them. +// new service goroutine for each. The service goroutines perform TLS +// setup and then read requests, calling srv.Handler to reply to them. // -// Additionally, files containing a certificate and matching private key for -// the server must be provided if neither the Server's TLSConfig.Certificates -// nor TLSConfig.GetCertificate are populated.. If the certificate is signed by -// a certificate authority, the certFile should be the concatenation of the -// server's certificate, any intermediates, and the CA's certificate. -// -// For HTTP/2 support, srv.TLSConfig should be initialized to the -// provided listener's TLS Config before calling ServeTLS. If -// srv.TLSConfig is non-nil and doesn't include the string "h2" in -// Config.NextProtos, HTTP/2 support is not enabled. +// Files containing a certificate and matching private key for the +// server must be provided if neither the Server's +// TLSConfig.Certificates nor TLSConfig.GetCertificate are populated. +// If the certificate is signed by a certificate authority, the +// certFile should be the concatenation of the server's certificate, +// any intermediates, and the CA's certificate. // // ServeTLS always returns a non-nil error. After Shutdown or Close, the // returned error is ErrServerClosed. @@ -2839,22 +2891,31 @@ func (srv *Server) ServeTLS(l net.Listener, certFile, keyFile string) error { return srv.Serve(tlsListener) } -func (s *Server) trackListener(ln net.Listener, add bool) { +// trackListener adds or removes a net.Listener to the set of tracked +// listeners. +// +// We store a pointer to interface in the map set, in case the +// net.Listener is not comparable. This is safe because we only call +// trackListener via Serve and can track+defer untrack the same +// pointer to local variable there. We never need to compare a +// Listener from another caller. +// +// It reports whether the server is still up (not Shutdown or Closed). +func (s *Server) trackListener(ln *net.Listener, add bool) bool { s.mu.Lock() defer s.mu.Unlock() if s.listeners == nil { - s.listeners = make(map[net.Listener]struct{}) + s.listeners = make(map[*net.Listener]struct{}) } if add { - // If the *Server is being reused after a previous - // Close or Shutdown, reset its doneChan: - if len(s.listeners) == 0 && len(s.activeConn) == 0 { - s.doneChan = nil + if s.shuttingDown() { + return false } s.listeners[ln] = struct{}{} } else { delete(s.listeners, ln) } + return true } func (s *Server) trackConn(c *conn, add bool) { @@ -2889,6 +2950,8 @@ func (s *Server) doKeepAlives() bool { } func (s *Server) shuttingDown() bool { + // TODO: replace inShutdown with the existing atomicBool type; + // see https://github.com/golang/go/issues/20239#issuecomment-381434582 return atomic.LoadInt32(&s.inShutdown) != 0 } @@ -2906,14 +2969,7 @@ func (srv *Server) SetKeepAlivesEnabled(v bool) { // Close idle HTTP/1 conns: srv.closeIdleConns() - // Close HTTP/2 conns, as soon as they become idle, but reset - // the chan so future conns (if the listener is still active) - // still work and don't get a GOAWAY immediately, before their - // first request: - srv.mu.Lock() - defer srv.mu.Unlock() - srv.closeDoneChanLocked() // closes http2 conns - srv.doneChan = nil + // TODO: Issue 26303: close HTTP/2 conns as soon as they become idle. } func (s *Server) logf(format string, args ...interface{}) { @@ -2936,32 +2992,11 @@ func logf(r *Request, format string, args ...interface{}) { } } -// ListenAndServe listens on the TCP network address addr -// and then calls Serve with handler to handle requests -// on incoming connections. +// ListenAndServe listens on the TCP network address addr and then calls +// Serve with handler to handle requests on incoming connections. // Accepted connections are configured to enable TCP keep-alives. -// Handler is typically nil, in which case the DefaultServeMux is -// used. -// -// A trivial example server is: -// -// package main // -// import ( -// "io" -// "net/http" -// "log" -// ) -// -// // hello world, the web server -// func HelloServer(w http.ResponseWriter, req *http.Request) { -// io.WriteString(w, "hello, world!\n") -// } -// -// func main() { -// http.HandleFunc("/hello", HelloServer) -// log.Fatal(http.ListenAndServe(":12345", nil)) -// } +// The handler is typically nil, in which case the DefaultServeMux is used. // // ListenAndServe always returns a non-nil error. func ListenAndServe(addr string, handler Handler) error { @@ -2974,36 +3009,13 @@ func ListenAndServe(addr string, handler Handler) error { // matching private key for the server must be provided. If the certificate // is signed by a certificate authority, the certFile should be the concatenation // of the server's certificate, any intermediates, and the CA's certificate. -// -// A trivial example server is: -// -// import ( -// "log" -// "net/http" -// ) -// -// func handler(w http.ResponseWriter, req *http.Request) { -// w.Header().Set("Content-Type", "text/plain") -// w.Write([]byte("This is an example server.\n")) -// } -// -// func main() { -// http.HandleFunc("/", handler) -// log.Printf("About to listen on 10443. Go to https://127.0.0.1:10443/") -// err := http.ListenAndServeTLS(":10443", "cert.pem", "key.pem", nil) -// log.Fatal(err) -// } -// -// One can use generate_cert.go in crypto/tls to generate cert.pem and key.pem. -// -// ListenAndServeTLS always returns a non-nil error. func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServeTLS(certFile, keyFile) } // ListenAndServeTLS listens on the TCP network address srv.Addr and -// then calls Serve to handle requests on incoming TLS connections. +// then calls ServeTLS to handle requests on incoming TLS connections. // Accepted connections are configured to enable TCP keep-alives. // // Filenames containing a certificate and matching private key for the @@ -3015,8 +3027,12 @@ func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error { // // If srv.Addr is blank, ":https" is used. // -// ListenAndServeTLS always returns a non-nil error. +// ListenAndServeTLS always returns a non-nil error. After Shutdown or +// Close, the returned error is ErrServerClosed. func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error { + if srv.shuttingDown() { + return ErrServerClosed + } addr := srv.Addr if addr == "" { addr = ":https" @@ -3042,8 +3058,8 @@ func (srv *Server) setupHTTP2_ServeTLS() error { // setupHTTP2_Serve is called from (*Server).Serve and conditionally // configures HTTP/2 on srv using a more conservative policy than -// setupHTTP2_ServeTLS because Serve may be called -// concurrently. +// setupHTTP2_ServeTLS because Serve is called after tls.Listen, +// and may be called concurrently. See shouldConfigureHTTP2ForServe. // // The tests named TestTransportAutomaticHTTP2* and // TestConcurrentServerServe in server_test.go demonstrate some @@ -3222,6 +3238,21 @@ func (ln tcpKeepAliveListener) Accept() (net.Conn, error) { return tc, nil } +// onceCloseListener wraps a net.Listener, protecting it from +// multiple Close calls. +type onceCloseListener struct { + net.Listener + once sync.Once + closeErr error +} + +func (oc *onceCloseListener) Close() error { + oc.once.Do(oc.close) + return oc.closeErr +} + +func (oc *onceCloseListener) close() { oc.closeErr = oc.Listener.Close() } + // globalOptionsHandler responds to "OPTIONS *" requests. type globalOptionsHandler struct{} diff --git a/libgo/go/net/http/sniff.go b/libgo/go/net/http/sniff.go index 365a36c..c1494ab 100644 --- a/libgo/go/net/http/sniff.go +++ b/libgo/go/net/http/sniff.go @@ -13,7 +13,7 @@ import ( const sniffLen = 512 // DetectContentType implements the algorithm described -// at http://mimesniff.spec.whatwg.org/ to determine the +// at https://mimesniff.spec.whatwg.org/ to determine the // Content-Type of the given data. It considers at most the // first 512 bytes of data. DetectContentType always returns // a valid MIME type: if it cannot determine a more specific one, it @@ -136,16 +136,19 @@ var sniffSignatures = []sniffSig{ mask: []byte("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF"), ct: "application/vnd.ms-fontobject", }, - &exactSig{[]byte("\x00\x01\x00\x00"), "application/font-ttf"}, - &exactSig{[]byte("OTTO"), "application/font-off"}, - &exactSig{[]byte("ttcf"), "application/font-cff"}, - &exactSig{[]byte("wOFF"), "application/font-woff"}, + &exactSig{[]byte("\x00\x01\x00\x00"), "font/ttf"}, + &exactSig{[]byte("OTTO"), "font/otf"}, + &exactSig{[]byte("ttcf"), "font/collection"}, + &exactSig{[]byte("wOFF"), "font/woff"}, + &exactSig{[]byte("wOF2"), "font/woff2"}, &exactSig{[]byte("\x1A\x45\xDF\xA3"), "video/webm"}, &exactSig{[]byte("\x52\x61\x72\x20\x1A\x07\x00"), "application/x-rar-compressed"}, &exactSig{[]byte("\x50\x4B\x03\x04"), "application/zip"}, &exactSig{[]byte("\x1F\x8B\x08"), "application/x-gzip"}, + &exactSig{[]byte("\x00\x61\x73\x6D"), "application/wasm"}, + mp4Sig{}, textSig{}, // should be last diff --git a/libgo/go/net/http/sniff_test.go b/libgo/go/net/http/sniff_test.go index bf1f6be..b4d3c9f 100644 --- a/libgo/go/net/http/sniff_test.go +++ b/libgo/go/net/http/sniff_test.go @@ -58,14 +58,14 @@ var sniffTests = []struct { // Font types. // {"MS.FontObject", []byte("\x00\x00")}, - {"TTF sample I", []byte("\x00\x01\x00\x00\x00\x17\x01\x00\x00\x04\x01\x60\x4f"), "application/font-ttf"}, - {"TTF sample II", []byte("\x00\x01\x00\x00\x00\x0e\x00\x80\x00\x03\x00\x60\x46"), "application/font-ttf"}, + {"TTF sample I", []byte("\x00\x01\x00\x00\x00\x17\x01\x00\x00\x04\x01\x60\x4f"), "font/ttf"}, + {"TTF sample II", []byte("\x00\x01\x00\x00\x00\x0e\x00\x80\x00\x03\x00\x60\x46"), "font/ttf"}, - {"OTTO sample I", []byte("\x4f\x54\x54\x4f\x00\x0e\x00\x80\x00\x03\x00\x60\x42\x41\x53\x45"), "application/font-off"}, + {"OTTO sample I", []byte("\x4f\x54\x54\x4f\x00\x0e\x00\x80\x00\x03\x00\x60\x42\x41\x53\x45"), "font/otf"}, - {"woff sample I", []byte("\x77\x4f\x46\x46\x00\x01\x00\x00\x00\x00\x30\x54\x00\x0d\x00\x00"), "application/font-woff"}, - // Woff2 is not yet recognized, change this test once mime-sniff working group adds woff2 - {"woff2 not recognized", []byte("\x77\x4f\x46\x32\x00\x01\x00\x00\x00"), "application/octet-stream"}, + {"woff sample I", []byte("\x77\x4f\x46\x46\x00\x01\x00\x00\x00\x00\x30\x54\x00\x0d\x00\x00"), "font/woff"}, + {"woff2 sample", []byte("\x77\x4f\x46\x32\x00\x01\x00\x00\x00"), "font/woff2"}, + {"wasm sample", []byte("\x00\x61\x73\x6d\x01\x00"), "application/wasm"}, } func TestDetectContentType(t *testing.T) { diff --git a/libgo/go/net/http/socks_bundle.go b/libgo/go/net/http/socks_bundle.go new file mode 100644 index 0000000..e4314b4 --- /dev/null +++ b/libgo/go/net/http/socks_bundle.go @@ -0,0 +1,472 @@ +// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT. +//go:generate bundle -o socks_bundle.go -dst net/http -prefix socks -underscore golang.org/x/net/internal/socks + +// Package socks provides a SOCKS version 5 client implementation. +// +// SOCKS protocol version 5 is defined in RFC 1928. +// Username/Password authentication for SOCKS version 5 is defined in +// RFC 1929. +// + +package http + +import ( + "context" + "errors" + "io" + "net" + "strconv" + "time" +) + +var ( + socksnoDeadline = time.Time{} + socksaLongTimeAgo = time.Unix(1, 0) +) + +func (d *socksDialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) { + host, port, err := sockssplitHostPort(address) + if err != nil { + return nil, err + } + if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() { + c.SetDeadline(deadline) + defer c.SetDeadline(socksnoDeadline) + } + if ctx != context.Background() { + errCh := make(chan error, 1) + done := make(chan struct{}) + defer func() { + close(done) + if ctxErr == nil { + ctxErr = <-errCh + } + }() + go func() { + select { + case <-ctx.Done(): + c.SetDeadline(socksaLongTimeAgo) + errCh <- ctx.Err() + case <-done: + errCh <- nil + } + }() + } + + b := make([]byte, 0, 6+len(host)) // the size here is just an estimate + b = append(b, socksVersion5) + if len(d.AuthMethods) == 0 || d.Authenticate == nil { + b = append(b, 1, byte(socksAuthMethodNotRequired)) + } else { + ams := d.AuthMethods + if len(ams) > 255 { + return nil, errors.New("too many authentication methods") + } + b = append(b, byte(len(ams))) + for _, am := range ams { + b = append(b, byte(am)) + } + } + if _, ctxErr = c.Write(b); ctxErr != nil { + return + } + + if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil { + return + } + if b[0] != socksVersion5 { + return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0]))) + } + am := socksAuthMethod(b[1]) + if am == socksAuthMethodNoAcceptableMethods { + return nil, errors.New("no acceptable authentication methods") + } + if d.Authenticate != nil { + if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil { + return + } + } + + b = b[:0] + b = append(b, socksVersion5, byte(d.cmd), 0) + if ip := net.ParseIP(host); ip != nil { + if ip4 := ip.To4(); ip4 != nil { + b = append(b, socksAddrTypeIPv4) + b = append(b, ip4...) + } else if ip6 := ip.To16(); ip6 != nil { + b = append(b, socksAddrTypeIPv6) + b = append(b, ip6...) + } else { + return nil, errors.New("unknown address type") + } + } else { + if len(host) > 255 { + return nil, errors.New("FQDN too long") + } + b = append(b, socksAddrTypeFQDN) + b = append(b, byte(len(host))) + b = append(b, host...) + } + b = append(b, byte(port>>8), byte(port)) + if _, ctxErr = c.Write(b); ctxErr != nil { + return + } + + if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil { + return + } + if b[0] != socksVersion5 { + return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0]))) + } + if cmdErr := socksReply(b[1]); cmdErr != socksStatusSucceeded { + return nil, errors.New("unknown error " + cmdErr.String()) + } + if b[2] != 0 { + return nil, errors.New("non-zero reserved field") + } + l := 2 + var a socksAddr + switch b[3] { + case socksAddrTypeIPv4: + l += net.IPv4len + a.IP = make(net.IP, net.IPv4len) + case socksAddrTypeIPv6: + l += net.IPv6len + a.IP = make(net.IP, net.IPv6len) + case socksAddrTypeFQDN: + if _, err := io.ReadFull(c, b[:1]); err != nil { + return nil, err + } + l += int(b[0]) + default: + return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3]))) + } + if cap(b) < l { + b = make([]byte, l) + } else { + b = b[:l] + } + if _, ctxErr = io.ReadFull(c, b); ctxErr != nil { + return + } + if a.IP != nil { + copy(a.IP, b) + } else { + a.Name = string(b[:len(b)-2]) + } + a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1]) + return &a, nil +} + +func sockssplitHostPort(address string) (string, int, error) { + host, port, err := net.SplitHostPort(address) + if err != nil { + return "", 0, err + } + portnum, err := strconv.Atoi(port) + if err != nil { + return "", 0, err + } + if 1 > portnum || portnum > 0xffff { + return "", 0, errors.New("port number out of range " + port) + } + return host, portnum, nil +} + +// A Command represents a SOCKS command. +type socksCommand int + +func (cmd socksCommand) String() string { + switch cmd { + case socksCmdConnect: + return "socks connect" + case sockscmdBind: + return "socks bind" + default: + return "socks " + strconv.Itoa(int(cmd)) + } +} + +// An AuthMethod represents a SOCKS authentication method. +type socksAuthMethod int + +// A Reply represents a SOCKS command reply code. +type socksReply int + +func (code socksReply) String() string { + switch code { + case socksStatusSucceeded: + return "succeeded" + case 0x01: + return "general SOCKS server failure" + case 0x02: + return "connection not allowed by ruleset" + case 0x03: + return "network unreachable" + case 0x04: + return "host unreachable" + case 0x05: + return "connection refused" + case 0x06: + return "TTL expired" + case 0x07: + return "command not supported" + case 0x08: + return "address type not supported" + default: + return "unknown code: " + strconv.Itoa(int(code)) + } +} + +// Wire protocol constants. +const ( + socksVersion5 = 0x05 + + socksAddrTypeIPv4 = 0x01 + socksAddrTypeFQDN = 0x03 + socksAddrTypeIPv6 = 0x04 + + socksCmdConnect socksCommand = 0x01 // establishes an active-open forward proxy connection + sockscmdBind socksCommand = 0x02 // establishes a passive-open forward proxy connection + + socksAuthMethodNotRequired socksAuthMethod = 0x00 // no authentication required + socksAuthMethodUsernamePassword socksAuthMethod = 0x02 // use username/password + socksAuthMethodNoAcceptableMethods socksAuthMethod = 0xff // no acceptable authentication methods + + socksStatusSucceeded socksReply = 0x00 +) + +// An Addr represents a SOCKS-specific address. +// Either Name or IP is used exclusively. +type socksAddr struct { + Name string // fully-qualified domain name + IP net.IP + Port int +} + +func (a *socksAddr) Network() string { return "socks" } + +func (a *socksAddr) String() string { + if a == nil { + return "<nil>" + } + port := strconv.Itoa(a.Port) + if a.IP == nil { + return net.JoinHostPort(a.Name, port) + } + return net.JoinHostPort(a.IP.String(), port) +} + +// A Conn represents a forward proxy connection. +type socksConn struct { + net.Conn + + boundAddr net.Addr +} + +// BoundAddr returns the address assigned by the proxy server for +// connecting to the command target address from the proxy server. +func (c *socksConn) BoundAddr() net.Addr { + if c == nil { + return nil + } + return c.boundAddr +} + +// A Dialer holds SOCKS-specific options. +type socksDialer struct { + cmd socksCommand // either CmdConnect or cmdBind + proxyNetwork string // network between a proxy server and a client + proxyAddress string // proxy server address + + // ProxyDial specifies the optional dial function for + // establishing the transport connection. + ProxyDial func(context.Context, string, string) (net.Conn, error) + + // AuthMethods specifies the list of request authention + // methods. + // If empty, SOCKS client requests only AuthMethodNotRequired. + AuthMethods []socksAuthMethod + + // Authenticate specifies the optional authentication + // function. It must be non-nil when AuthMethods is not empty. + // It must return an error when the authentication is failed. + Authenticate func(context.Context, io.ReadWriter, socksAuthMethod) error +} + +// DialContext connects to the provided address on the provided +// network. +// +// The returned error value may be a net.OpError. When the Op field of +// net.OpError contains "socks", the Source field contains a proxy +// server address and the Addr field contains a command target +// address. +// +// See func Dial of the net package of standard library for a +// description of the network and address parameters. +func (d *socksDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { + if err := d.validateTarget(network, address); err != nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} + } + if ctx == nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")} + } + var err error + var c net.Conn + if d.ProxyDial != nil { + c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress) + } else { + var dd net.Dialer + c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress) + } + if err != nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} + } + a, err := d.connect(ctx, c, address) + if err != nil { + c.Close() + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} + } + return &socksConn{Conn: c, boundAddr: a}, nil +} + +// DialWithConn initiates a connection from SOCKS server to the target +// network and address using the connection c that is already +// connected to the SOCKS server. +// +// It returns the connection's local address assigned by the SOCKS +// server. +func (d *socksDialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) { + if err := d.validateTarget(network, address); err != nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} + } + if ctx == nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")} + } + a, err := d.connect(ctx, c, address) + if err != nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} + } + return a, nil +} + +// Dial connects to the provided address on the provided network. +// +// Unlike DialContext, it returns a raw transport connection instead +// of a forward proxy connection. +// +// Deprecated: Use DialContext or DialWithConn instead. +func (d *socksDialer) Dial(network, address string) (net.Conn, error) { + if err := d.validateTarget(network, address); err != nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} + } + var err error + var c net.Conn + if d.ProxyDial != nil { + c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress) + } else { + c, err = net.Dial(d.proxyNetwork, d.proxyAddress) + } + if err != nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} + } + if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil { + return nil, err + } + return c, nil +} + +func (d *socksDialer) validateTarget(network, address string) error { + switch network { + case "tcp", "tcp6", "tcp4": + default: + return errors.New("network not implemented") + } + switch d.cmd { + case socksCmdConnect, sockscmdBind: + default: + return errors.New("command not implemented") + } + return nil +} + +func (d *socksDialer) pathAddrs(address string) (proxy, dst net.Addr, err error) { + for i, s := range []string{d.proxyAddress, address} { + host, port, err := sockssplitHostPort(s) + if err != nil { + return nil, nil, err + } + a := &socksAddr{Port: port} + a.IP = net.ParseIP(host) + if a.IP == nil { + a.Name = host + } + if i == 0 { + proxy = a + } else { + dst = a + } + } + return +} + +// NewDialer returns a new Dialer that dials through the provided +// proxy server's network and address. +func socksNewDialer(network, address string) *socksDialer { + return &socksDialer{proxyNetwork: network, proxyAddress: address, cmd: socksCmdConnect} +} + +const ( + socksauthUsernamePasswordVersion = 0x01 + socksauthStatusSucceeded = 0x00 +) + +// UsernamePassword are the credentials for the username/password +// authentication method. +type socksUsernamePassword struct { + Username string + Password string +} + +// Authenticate authenticates a pair of username and password with the +// proxy server. +func (up *socksUsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth socksAuthMethod) error { + switch auth { + case socksAuthMethodNotRequired: + return nil + case socksAuthMethodUsernamePassword: + if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) == 0 || len(up.Password) > 255 { + return errors.New("invalid username/password") + } + b := []byte{socksauthUsernamePasswordVersion} + b = append(b, byte(len(up.Username))) + b = append(b, up.Username...) + b = append(b, byte(len(up.Password))) + b = append(b, up.Password...) + // TODO(mikio): handle IO deadlines and cancelation if + // necessary + if _, err := rw.Write(b); err != nil { + return err + } + if _, err := io.ReadFull(rw, b[:2]); err != nil { + return err + } + if b[0] != socksauthUsernamePasswordVersion { + return errors.New("invalid username/password version") + } + if b[1] != socksauthStatusSucceeded { + return errors.New("username/password authentication failed") + } + return nil + } + return errors.New("unsupported authentication method " + strconv.Itoa(int(auth))) +} diff --git a/libgo/go/net/http/status.go b/libgo/go/net/http/status.go index 98645b7..dd72d67 100644 --- a/libgo/go/net/http/status.go +++ b/libgo/go/net/http/status.go @@ -5,7 +5,7 @@ package http // HTTP status codes as registered with IANA. -// See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml +// See: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml const ( StatusContinue = 100 // RFC 7231, 6.2.1 StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2 @@ -51,6 +51,7 @@ const ( StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4 StatusExpectationFailed = 417 // RFC 7231, 6.5.14 StatusTeapot = 418 // RFC 7168, 2.3.3 + StatusMisdirectedRequest = 421 // RFC 7540, 9.1.2 StatusUnprocessableEntity = 422 // RFC 4918, 11.2 StatusLocked = 423 // RFC 4918, 11.3 StatusFailedDependency = 424 // RFC 4918, 11.4 @@ -117,6 +118,7 @@ var statusText = map[int]string{ StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable", StatusExpectationFailed: "Expectation Failed", StatusTeapot: "I'm a teapot", + StatusMisdirectedRequest: "Misdirected Request", StatusUnprocessableEntity: "Unprocessable Entity", StatusLocked: "Locked", StatusFailedDependency: "Failed Dependency", diff --git a/libgo/go/net/http/transfer.go b/libgo/go/net/http/transfer.go index a400a6a..2c6ba32 100644 --- a/libgo/go/net/http/transfer.go +++ b/libgo/go/net/http/transfer.go @@ -11,15 +11,17 @@ import ( "fmt" "io" "io/ioutil" + "net/http/httptrace" "net/http/internal" "net/textproto" + "reflect" "sort" "strconv" "strings" "sync" "time" - "golang_org/x/net/lex/httplex" + "golang_org/x/net/http/httpguts" ) // ErrLineTooLong is returned when reading request or response bodies @@ -105,6 +107,17 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) { if t.ContentLength < 0 && len(t.TransferEncoding) == 0 && t.shouldSendChunkedRequestBody() { t.TransferEncoding = []string{"chunked"} } + // If there's a body, conservatively flush the headers + // to any bufio.Writer we're writing to, just in case + // the server needs the headers early, before we copy + // the body and possibly block. We make an exception + // for the common standard library in-memory types, + // though, to avoid unnecessary TCP packets on the + // wire. (Issue 22088.) + if t.ContentLength != 0 && !isKnownInMemoryReader(t.Body) { + t.FlushHeaders = true + } + atLeastHTTP11 = true // Transport requests are always 1.1 or 2.0 case *Response: t.IsResponse = true @@ -268,11 +281,14 @@ func (t *transferWriter) shouldSendContentLength() bool { return false } -func (t *transferWriter) WriteHeader(w io.Writer) error { +func (t *transferWriter) writeHeader(w io.Writer, trace *httptrace.ClientTrace) error { if t.Close && !hasToken(t.Header.get("Connection"), "close") { if _, err := io.WriteString(w, "Connection: close\r\n"); err != nil { return err } + if trace != nil && trace.WroteHeaderField != nil { + trace.WroteHeaderField("Connection", []string{"close"}) + } } // Write Content-Length and/or Transfer-Encoding whose values are a @@ -285,10 +301,16 @@ func (t *transferWriter) WriteHeader(w io.Writer) error { if _, err := io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n"); err != nil { return err } + if trace != nil && trace.WroteHeaderField != nil { + trace.WroteHeaderField("Content-Length", []string{strconv.FormatInt(t.ContentLength, 10)}) + } } else if chunked(t.TransferEncoding) { if _, err := io.WriteString(w, "Transfer-Encoding: chunked\r\n"); err != nil { return err } + if trace != nil && trace.WroteHeaderField != nil { + trace.WroteHeaderField("Transfer-Encoding", []string{"chunked"}) + } } // Write Trailer header @@ -309,13 +331,16 @@ func (t *transferWriter) WriteHeader(w io.Writer) error { if _, err := io.WriteString(w, "Trailer: "+strings.Join(keys, ",")+"\r\n"); err != nil { return err } + if trace != nil && trace.WroteHeaderField != nil { + trace.WroteHeaderField("Trailer", keys) + } } } return nil } -func (t *transferWriter) WriteBody(w io.Writer) error { +func (t *transferWriter) writeBody(w io.Writer) error { var err error var ncopy int64 @@ -390,7 +415,7 @@ func (t *transferReader) protoAtLeast(m, n int) bool { } // bodyAllowedForStatus reports whether a given response status code -// permits a body. See RFC 2616, section 4.4. +// permits a body. See RFC 7230, section 3.3. func bodyAllowedForStatus(status int) bool { switch { case status >= 100 && status <= 199: @@ -411,7 +436,7 @@ var ( func suppressedHeaders(status int) []string { switch { case status == 304: - // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers" + // RFC 7232 section 4.1 return suppressedHeaders304 case !bodyAllowedForStatus(status): return suppressedHeadersNoBody @@ -482,7 +507,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) { // If there is no Content-Length or chunked Transfer-Encoding on a *Response // and the status is not 1xx, 204 or 304, then the body is unbounded. - // See RFC 2616, section 4.4. + // See RFC 7230, section 3.3. switch msg.(type) { case *Response: if realLength == -1 && @@ -601,7 +626,7 @@ func (t *transferReader) fixTransferEncoding() error { return nil } -// Determine the expected body length, using RFC 2616 Section 4.4. This +// Determine the expected body length, using RFC 7230 Section 3.3. This // function is not a method, because ultimately it should be shared by // ReadResponse and ReadRequest. func fixLength(isResponse bool, status int, requestMethod string, header Header, te []string) (int64, error) { @@ -667,7 +692,7 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header, header.Del("Content-Length") if isRequest { - // RFC 2616 neither explicitly permits nor forbids an + // RFC 7230 neither explicitly permits nor forbids an // entity-body on a GET request so we permit one if // declared, but we default to 0 here (not -1 below) // if there's no mention of a body. @@ -690,9 +715,9 @@ func shouldClose(major, minor int, header Header, removeCloseHeader bool) bool { } conv := header["Connection"] - hasClose := httplex.HeaderValuesContainsToken(conv, "close") + hasClose := httpguts.HeaderValuesContainsToken(conv, "close") if major == 1 && minor == 0 { - return hasClose || !httplex.HeaderValuesContainsToken(conv, "keep-alive") + return hasClose || !httpguts.HeaderValuesContainsToken(conv, "keep-alive") } if hasClose && removeCloseHeader { @@ -1009,3 +1034,19 @@ func (fr finishAsyncByteRead) Read(p []byte) (n int, err error) { } return } + +var nopCloserType = reflect.TypeOf(ioutil.NopCloser(nil)) + +// isKnownInMemoryReader reports whether r is a type known to not +// block on Read. Its caller uses this as an optional optimization to +// send fewer TCP packets. +func isKnownInMemoryReader(r io.Reader) bool { + switch r.(type) { + case *bytes.Reader, *bytes.Buffer, *strings.Reader: + return true + } + if reflect.TypeOf(r) == nopCloserType { + return isKnownInMemoryReader(reflect.ValueOf(r).Field(0).Interface().(io.Reader)) + } + return false +} diff --git a/libgo/go/net/http/transfer_test.go b/libgo/go/net/http/transfer_test.go index 48cd540..993ea4e 100644 --- a/libgo/go/net/http/transfer_test.go +++ b/libgo/go/net/http/transfer_test.go @@ -6,7 +6,9 @@ package http import ( "bufio" + "bytes" "io" + "io/ioutil" "strings" "testing" ) @@ -62,3 +64,29 @@ func TestFinalChunkedBodyReadEOF(t *testing.T) { t.Errorf("buf = %q; want %q", buf, want) } } + +func TestDetectInMemoryReaders(t *testing.T) { + pr, _ := io.Pipe() + tests := []struct { + r io.Reader + want bool + }{ + {pr, false}, + + {bytes.NewReader(nil), true}, + {bytes.NewBuffer(nil), true}, + {strings.NewReader(""), true}, + + {ioutil.NopCloser(pr), false}, + + {ioutil.NopCloser(bytes.NewReader(nil)), true}, + {ioutil.NopCloser(bytes.NewBuffer(nil)), true}, + {ioutil.NopCloser(strings.NewReader("")), true}, + } + for i, tt := range tests { + got := isKnownInMemoryReader(tt.r) + if got != tt.want { + t.Errorf("%d: got = %v; want %v", i, got, tt.want) + } + } +} diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go index 7ef8f01..40947ba 100644 --- a/libgo/go/net/http/transport.go +++ b/libgo/go/net/http/transport.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. -// HTTP client implementation. See RFC 2616. +// HTTP client implementation. See RFC 7230 through 7235. // // This is the low-level Transport implementation of RoundTripper. // The high-level interface is in client.go. @@ -21,15 +21,17 @@ import ( "log" "net" "net/http/httptrace" + "net/textproto" "net/url" "os" + "reflect" "strings" "sync" "sync/atomic" "time" - "golang_org/x/net/lex/httplex" - "golang_org/x/net/proxy" + "golang_org/x/net/http/httpguts" + "golang_org/x/net/http/httpproxy" ) // DefaultTransport is the default implementation of Transport and is @@ -54,6 +56,15 @@ var DefaultTransport RoundTripper = &Transport{ // MaxIdleConnsPerHost. const DefaultMaxIdleConnsPerHost = 2 +// connsPerHostClosedCh is a closed channel used by MaxConnsPerHost +// for the property that receives from a closed channel return the +// zero value. +var connsPerHostClosedCh = make(chan struct{}) + +func init() { + close(connsPerHostClosedCh) +} + // Transport is an implementation of RoundTripper that supports HTTP, // HTTPS, and HTTP proxies (for either HTTP or HTTPS with CONNECT). // @@ -82,6 +93,13 @@ const DefaultMaxIdleConnsPerHost = 2 // being written while the response body is streamed. Go's HTTP/2 // implementation does support full duplex, but many CONNECT proxies speak // HTTP/1.x. +// +// Responses with status codes in the 1xx range are either handled +// automatically (100 expect-continue) or ignored. The one +// exception is HTTP status code 101 (Switching Protocols), which is +// considered a terminal status and returned by RoundTrip. To see the +// ignored 1xx responses, use the httptrace trace package's +// ClientTrace.Got1xxResponse. type Transport struct { idleMu sync.Mutex wantIdle bool // user has requested to close all idle conns @@ -95,12 +113,16 @@ type Transport struct { altMu sync.Mutex // guards changing altProto only altProto atomic.Value // of nil or map[string]RoundTripper, key is URI scheme + connCountMu sync.Mutex + connPerHostCount map[connectMethodKey]int + connPerHostAvailable map[connectMethodKey]chan struct{} + // Proxy specifies a function to return a proxy for a given // Request. If the function returns a non-nil error, the // request is aborted with the provided error. // - // The proxy type is determined by the URL scheme. "http" - // and "socks5" are supported. If the scheme is empty, + // The proxy type is determined by the URL scheme. "http", + // "https", and "socks5" are supported. If the scheme is empty, // "http" is assumed. // // If Proxy is nil or returns a nil *URL, no proxy is used. @@ -109,10 +131,20 @@ type Transport struct { // DialContext specifies the dial function for creating unencrypted TCP connections. // If DialContext is nil (and the deprecated Dial below is also nil), // then the transport dials using package net. + // + // DialContext runs concurrently with calls to RoundTrip. + // A RoundTrip call that initiates a dial may end up using + // an connection dialed previously when the earlier connection + // becomes idle before the later DialContext completes. DialContext func(ctx context.Context, network, addr string) (net.Conn, error) // Dial specifies the dial function for creating unencrypted TCP connections. // + // Dial runs concurrently with calls to RoundTrip. + // A RoundTrip call that initiates a dial may end up using + // an connection dialed previously when the earlier connection + // becomes idle before the later Dial completes. + // // Deprecated: Use DialContext instead, which allows the transport // to cancel dials as soon as they are no longer needed. // If both are set, DialContext takes priority. @@ -139,8 +171,11 @@ type Transport struct { // wait for a TLS handshake. Zero means no timeout. TLSHandshakeTimeout time.Duration - // DisableKeepAlives, if true, prevents re-use of TCP connections - // between different HTTP requests. + // DisableKeepAlives, if true, disables HTTP keep-alives and + // will only use the connection to the server for a single + // HTTP request. + // + // This is unrelated to the similarly named TCP keep-alives. DisableKeepAlives bool // DisableCompression, if true, prevents the Transport from @@ -162,6 +197,18 @@ type Transport struct { // DefaultMaxIdleConnsPerHost is used. MaxIdleConnsPerHost int + // MaxConnsPerHost optionally limits the total number of + // connections per host, including connections in the dialing, + // active, and idle states. On limit violation, dials will block. + // + // Zero means no limit. + // + // For HTTP/2, this currently only controls the number of new + // connections being created at a time, instead of the total + // number. In practice, hosts using HTTP/2 only have about one + // idle connection, though. + MaxConnsPerHost int + // IdleConnTimeout is the maximum amount of time an idle // (keep-alive) connection will remain idle before closing // itself. @@ -209,9 +256,17 @@ type Transport struct { // nextProtoOnce guards initialization of TLSNextProto and // h2transport (via onceSetNextProtoDefaults) nextProtoOnce sync.Once - h2transport *http2Transport // non-nil if http2 wired up + h2transport h2Transport // non-nil if http2 wired up +} - // TODO: tunable on max per-host TCP dials in flight (Issue 13957) +// h2Transport is the interface we expect to be able to call from +// net/http against an *http2.Transport that's either bundled into +// h2_bundle.go or supplied by the user via x/net/http2. +// +// We name it with the "h2" prefix to stay out of the "http2" prefix +// namespace used by x/tools/cmd/bundle for h2_bundle.go. +type h2Transport interface { + CloseIdleConnections() } // onceSetNextProtoDefaults initializes TLSNextProto. @@ -220,6 +275,21 @@ func (t *Transport) onceSetNextProtoDefaults() { if strings.Contains(os.Getenv("GODEBUG"), "http2client=0") { return } + + // If they've already configured http2 with + // golang.org/x/net/http2 instead of the bundled copy, try to + // get at its http2.Transport value (via the the "https" + // altproto map) so we can call CloseIdleConnections on it if + // requested. (Issue 22891) + altProto, _ := t.altProto.Load().(map[string]RoundTripper) + if rv := reflect.ValueOf(altProto["https"]); rv.IsValid() && rv.Type().Kind() == reflect.Struct && rv.Type().NumField() == 1 { + if v := rv.Field(0); v.CanInterface() { + if h2i, ok := v.Interface().(h2Transport); ok { + t.h2transport = h2i + } + } + } + if t.TLSNextProto != nil { // This is the documented way to disable http2 on a // Transport. @@ -273,39 +343,7 @@ func (t *Transport) onceSetNextProtoDefaults() { // As a special case, if req.URL.Host is "localhost" (with or without // a port number), then a nil URL and nil error will be returned. func ProxyFromEnvironment(req *Request) (*url.URL, error) { - var proxy string - if req.URL.Scheme == "https" { - proxy = httpsProxyEnv.Get() - } - if proxy == "" { - proxy = httpProxyEnv.Get() - if proxy != "" && os.Getenv("REQUEST_METHOD") != "" { - return nil, errors.New("net/http: refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy") - } - } - if proxy == "" { - return nil, nil - } - if !useProxy(canonicalAddr(req.URL)) { - return nil, nil - } - proxyURL, err := url.Parse(proxy) - if err != nil || - (proxyURL.Scheme != "http" && - proxyURL.Scheme != "https" && - proxyURL.Scheme != "socks5") { - // proxy was bogus. Try prepending "http://" to it and - // see if that parses correctly. If not, we fall - // through and complain about the original one. - if proxyURL, err := url.Parse("http://" + proxy); err == nil { - return proxyURL, nil - } - - } - if err != nil { - return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err) - } - return proxyURL, nil + return envProxyFunc()(req.URL) } // ProxyURL returns a proxy function (for use in a Transport) @@ -343,11 +381,8 @@ func (tr *transportRequest) setError(err error) { tr.mu.Unlock() } -// RoundTrip implements the RoundTripper interface. -// -// For higher-level HTTP client support (such as handling of cookies -// and redirects), see Get, Post, and the Client type. -func (t *Transport) RoundTrip(req *Request) (*Response, error) { +// roundTrip implements a RoundTripper over HTTP. +func (t *Transport) roundTrip(req *Request) (*Response, error) { t.nextProtoOnce.Do(t.onceSetNextProtoDefaults) ctx := req.Context() trace := httptrace.ContextClientTrace(ctx) @@ -364,11 +399,11 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { isHTTP := scheme == "http" || scheme == "https" if isHTTP { for k, vv := range req.Header { - if !httplex.ValidHeaderFieldName(k) { + if !httpguts.ValidHeaderFieldName(k) { return nil, fmt.Errorf("net/http: invalid header field name %q", k) } for _, v := range vv { - if !httplex.ValidHeaderFieldValue(v) { + if !httpguts.ValidHeaderFieldValue(v) { return nil, fmt.Errorf("net/http: invalid header field value %q for key %v", v, k) } } @@ -394,6 +429,13 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { } for { + select { + case <-ctx.Done(): + req.closeBody() + return nil, ctx.Err() + default: + } + // treq gets modified by roundTrip, so we need to recreate for each retry. treq := &transportRequest{Request: req, trace: trace} cm, err := t.connectMethodForRequest(treq) @@ -416,7 +458,8 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { var resp *Response if pconn.alt != nil { // HTTP/2 path. - t.setReqCanceler(req, nil) // not cancelable with CancelRequest + t.decHostConnCount(cm.key()) // don't count cached http2 conns toward conns per host + t.setReqCanceler(req, nil) // not cancelable with CancelRequest resp, err = pconn.alt.RoundTrip(req) } else { resp, err = pconn.roundTrip(treq) @@ -575,44 +618,25 @@ func (t *Transport) cancelRequest(req *Request, err error) { // var ( - httpProxyEnv = &envOnce{ - names: []string{"HTTP_PROXY", "http_proxy"}, - } - httpsProxyEnv = &envOnce{ - names: []string{"HTTPS_PROXY", "https_proxy"}, - } - noProxyEnv = &envOnce{ - names: []string{"NO_PROXY", "no_proxy"}, - } + // proxyConfigOnce guards proxyConfig + envProxyOnce sync.Once + envProxyFuncValue func(*url.URL) (*url.URL, error) ) -// envOnce looks up an environment variable (optionally by multiple -// names) once. It mitigates expensive lookups on some platforms -// (e.g. Windows). -type envOnce struct { - names []string - once sync.Once - val string +// defaultProxyConfig returns a ProxyConfig value looked up +// from the environment. This mitigates expensive lookups +// on some platforms (e.g. Windows). +func envProxyFunc() func(*url.URL) (*url.URL, error) { + envProxyOnce.Do(func() { + envProxyFuncValue = httpproxy.FromEnvironment().ProxyFunc() + }) + return envProxyFuncValue } -func (e *envOnce) Get() string { - e.once.Do(e.init) - return e.val -} - -func (e *envOnce) init() { - for _, n := range e.names { - e.val = os.Getenv(n) - if e.val != "" { - return - } - } -} - -// reset is used by tests -func (e *envOnce) reset() { - e.once = sync.Once{} - e.val = "" +// resetProxyConfig is used by tests. +func resetProxyConfig() { + envProxyOnce = sync.Once{} + envProxyFuncValue = nil } func (t *Transport) connectMethodForRequest(treq *transportRequest) (cm connectMethod, err error) { @@ -813,12 +837,6 @@ func (t *Transport) getIdleConn(cm connectMethod) (pconn *persistConn, idleSince // carry on. continue } - if pconn.idleTimer != nil && !pconn.idleTimer.Stop() { - // We picked this conn at the ~same time it - // was expiring and it's trying to close - // itself in another goroutine. Don't use it. - continue - } return pconn, pconn.idleAt } } @@ -934,6 +952,7 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (*persistC err error } dialc := make(chan dialRes) + cmKey := cm.key() // Copy these hooks so we don't race on the postPendingDial in // the goroutine we launch. Issue 11136. @@ -945,6 +964,8 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (*persistC go func() { if v := <-dialc; v.err == nil { t.putOrCloseIdleConn(v.pc) + } else { + t.decHostConnCount(cmKey) } testHookPostPendingDial() }() @@ -953,6 +974,27 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (*persistC cancelc := make(chan error, 1) t.setReqCanceler(req, func(err error) { cancelc <- err }) + if t.MaxConnsPerHost > 0 { + select { + case <-t.incHostConnCount(cmKey): + // count below conn per host limit; proceed + case pc := <-t.getIdleConnCh(cm): + if trace != nil && trace.GotConn != nil { + trace.GotConn(httptrace.GotConnInfo{Conn: pc.conn, Reused: pc.isReused()}) + } + return pc, nil + case <-req.Cancel: + return nil, errRequestCanceledConn + case <-req.Context().Done(): + return nil, req.Context().Err() + case err := <-cancelc: + if err == errRequestCanceled { + err = errRequestCanceledConn + } + return nil, err + } + } + go func() { pc, err := t.dialConn(ctx, cm) dialc <- dialRes{pc, err} @@ -970,6 +1012,7 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (*persistC } // Our dial failed. See why to return a nicer error // value. + t.decHostConnCount(cmKey) select { case <-req.Cancel: // It was an error due to cancelation, so prioritize that @@ -1013,21 +1056,81 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (*persistC } } -type oneConnDialer <-chan net.Conn - -func newOneConnDialer(c net.Conn) proxy.Dialer { - ch := make(chan net.Conn, 1) - ch <- c - return oneConnDialer(ch) +// incHostConnCount increments the count of connections for a +// given host. It returns an already-closed channel if the count +// is not at its limit; otherwise it returns a channel which is +// notified when the count is below the limit. +func (t *Transport) incHostConnCount(cmKey connectMethodKey) <-chan struct{} { + if t.MaxConnsPerHost <= 0 { + return connsPerHostClosedCh + } + t.connCountMu.Lock() + defer t.connCountMu.Unlock() + if t.connPerHostCount[cmKey] == t.MaxConnsPerHost { + if t.connPerHostAvailable == nil { + t.connPerHostAvailable = make(map[connectMethodKey]chan struct{}) + } + ch, ok := t.connPerHostAvailable[cmKey] + if !ok { + ch = make(chan struct{}) + t.connPerHostAvailable[cmKey] = ch + } + return ch + } + if t.connPerHostCount == nil { + t.connPerHostCount = make(map[connectMethodKey]int) + } + t.connPerHostCount[cmKey]++ + // return a closed channel to avoid race: if decHostConnCount is called + // after incHostConnCount and during the nil check, decHostConnCount + // will delete the channel since it's not being listened on yet. + return connsPerHostClosedCh } -func (d oneConnDialer) Dial(network, addr string) (net.Conn, error) { +// decHostConnCount decrements the count of connections +// for a given host. +// See Transport.MaxConnsPerHost. +func (t *Transport) decHostConnCount(cmKey connectMethodKey) { + if t.MaxConnsPerHost <= 0 { + return + } + t.connCountMu.Lock() + defer t.connCountMu.Unlock() + t.connPerHostCount[cmKey]-- select { - case c := <-d: - return c, nil + case t.connPerHostAvailable[cmKey] <- struct{}{}: default: - return nil, io.EOF + // close channel before deleting avoids getConn waiting forever in + // case getConn has reference to channel but hasn't started waiting. + // This could lead to more than MaxConnsPerHost in the unlikely case + // that > 1 go routine has fetched the channel but none started waiting. + if t.connPerHostAvailable[cmKey] != nil { + close(t.connPerHostAvailable[cmKey]) + } + delete(t.connPerHostAvailable, cmKey) + } + if t.connPerHostCount[cmKey] == 0 { + delete(t.connPerHostCount, cmKey) + } +} + +// connCloseListener wraps a connection, the transport that dialed it +// and the connected-to host key so the host connection count can be +// transparently decremented by whatever closes the embedded connection. +type connCloseListener struct { + net.Conn + t *Transport + cmKey connectMethodKey + didClose int32 +} + +func (c *connCloseListener) Close() error { + if atomic.AddInt32(&c.didClose, 1) != 1 { + return nil } + err := c.Conn.Close() + c.t.decHostConnCount(c.cmKey) + return err } // The connect method and the transport can both specify a TLS @@ -1078,12 +1181,6 @@ func (pconn *persistConn) addTLS(name string, trace *httptrace.ClientTrace) erro } return err } - if !cfg.InsecureSkipVerify { - if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { - plainConn.Close() - return err - } - } cs := tlsConn.ConnectionState() if trace != nil && trace.TLSHandshakeDone != nil { trace.TLSHandshakeDone(cs, nil) @@ -1162,18 +1259,19 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistCon // Do nothing. Not using a proxy. case cm.proxyURL.Scheme == "socks5": conn := pconn.conn - var auth *proxy.Auth + d := socksNewDialer("tcp", conn.RemoteAddr().String()) if u := cm.proxyURL.User; u != nil { - auth = &proxy.Auth{} - auth.User = u.Username() + auth := &socksUsernamePassword{ + Username: u.Username(), + } auth.Password, _ = u.Password() + d.AuthMethods = []socksAuthMethod{ + socksAuthMethodNotRequired, + socksAuthMethodUsernamePassword, + } + d.Authenticate = auth.Authenticate } - p, err := proxy.SOCKS5("", cm.addr(), auth, newOneConnDialer(conn)) - if err != nil { - conn.Close() - return nil, err - } - if _, err := p.Dial("tcp", cm.targetAddr); err != nil { + if _, err := d.DialWithConn(ctx, conn, "tcp", cm.targetAddr); err != nil { conn.Close() return nil, err } @@ -1232,6 +1330,9 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistCon } } + if t.MaxConnsPerHost > 0 { + pconn.conn = &connCloseListener{Conn: pconn.conn, t: t, cmKey: pconn.cacheKey} + } pconn.br = bufio.NewReader(pconn) pconn.bw = bufio.NewWriter(persistConnWriter{pconn}) go pconn.readLoop() @@ -1255,63 +1356,6 @@ func (w persistConnWriter) Write(p []byte) (n int, err error) { return } -// useProxy reports whether requests to addr should use a proxy, -// according to the NO_PROXY or no_proxy environment variable. -// addr is always a canonicalAddr with a host and port. -func useProxy(addr string) bool { - if len(addr) == 0 { - return true - } - host, _, err := net.SplitHostPort(addr) - if err != nil { - return false - } - if host == "localhost" { - return false - } - if ip := net.ParseIP(host); ip != nil { - if ip.IsLoopback() { - return false - } - } - - noProxy := noProxyEnv.Get() - if noProxy == "*" { - return false - } - - addr = strings.ToLower(strings.TrimSpace(addr)) - if hasPort(addr) { - addr = addr[:strings.LastIndex(addr, ":")] - } - - for _, p := range strings.Split(noProxy, ",") { - p = strings.ToLower(strings.TrimSpace(p)) - if len(p) == 0 { - continue - } - if hasPort(p) { - p = p[:strings.LastIndex(p, ":")] - } - if addr == p { - return false - } - if len(p) == 0 { - // There is no host part, likely the entry is malformed; ignore. - continue - } - if p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:]) { - // no_proxy ".foo.com" matches "bar.foo.com" or "foo.com" - return false - } - if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' { - // no_proxy "foo.com" matches "bar.foo.com" - return false - } - } - return true -} - // connectMethod is the map key (in its String form) for keeping persistent // TCP connections alive for subsequent HTTP requests. // @@ -1764,26 +1808,45 @@ func (pc *persistConn) readResponse(rc requestAndChan, trace *httptrace.ClientTr trace.GotFirstResponseByte() } } - resp, err = ReadResponse(pc.br, rc.req) - if err != nil { - return - } - if rc.continueCh != nil { - if resp.StatusCode == 100 { - if trace != nil && trace.Got100Continue != nil { - trace.Got100Continue() - } - rc.continueCh <- struct{}{} - } else { - close(rc.continueCh) - } - } - if resp.StatusCode == 100 { - pc.readLimit = pc.maxHeaderResponseSize() // reset the limit + num1xx := 0 // number of informational 1xx headers received + const max1xxResponses = 5 // arbitrary bound on number of informational responses + + continueCh := rc.continueCh + for { resp, err = ReadResponse(pc.br, rc.req) if err != nil { return } + resCode := resp.StatusCode + if continueCh != nil { + if resCode == 100 { + if trace != nil && trace.Got100Continue != nil { + trace.Got100Continue() + } + continueCh <- struct{}{} + continueCh = nil + } else if resCode >= 200 { + close(continueCh) + continueCh = nil + } + } + is1xx := 100 <= resCode && resCode <= 199 + // treat 101 as a terminal status, see issue 26161 + is1xxNonTerminal := is1xx && resCode != StatusSwitchingProtocols + if is1xxNonTerminal { + num1xx++ + if num1xx > max1xxResponses { + return nil, errors.New("net/http: too many 1xx informational responses") + } + pc.readLimit = pc.maxHeaderResponseSize() // reset the limit + if trace != nil && trace.Got1xxResponse != nil { + if err := trace.Got1xxResponse(resCode, textproto.MIMEHeader(resp.Header)); err != nil { + return nil, err + } + } + continue + } + break } resp.TLS = pc.tlsState return @@ -1855,6 +1918,11 @@ func (pc *persistConn) writeLoop() { } } +// maxWriteWaitBeforeConnReuse is how long the a Transport RoundTrip +// will wait to see the Request's Body.Write result after getting a +// response from the server. See comments in (*persistConn).wroteRequest. +const maxWriteWaitBeforeConnReuse = 50 * time.Millisecond + // wroteRequest is a check before recycling a connection that the previous write // (from writeLoop above) happened and was successful. func (pc *persistConn) wroteRequest() bool { @@ -1877,7 +1945,7 @@ func (pc *persistConn) wroteRequest() bool { select { case err := <-pc.writeErrCh: return err == nil - case <-time.After(50 * time.Millisecond): + case <-time.After(maxWriteWaitBeforeConnReuse): return false } } @@ -1979,7 +2047,7 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err // // Note that we don't request this for HEAD requests, // due to a bug in nginx: - // http://trac.nginx.org/nginx/ticket/358 + // https://trac.nginx.org/nginx/ticket/358 // https://golang.org/issue/5522 // // We don't request gzip if the request is for a range, since diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go index 5588077..aa8beb9 100644 --- a/libgo/go/net/http/transport_test.go +++ b/libgo/go/net/http/transport_test.go @@ -16,6 +16,7 @@ import ( "context" "crypto/rand" "crypto/tls" + "crypto/x509" "encoding/binary" "errors" "fmt" @@ -30,6 +31,7 @@ import ( "net/http/httptrace" "net/http/httputil" "net/http/internal" + "net/textproto" "net/url" "os" "reflect" @@ -444,27 +446,95 @@ func TestTransportMaxPerHostIdleConns(t *testing.T) { if e, g := 1, len(keys); e != g { t.Fatalf("after first response, expected %d idle conn cache keys; got %d", e, g) } - cacheKey := "|http|" + ts.Listener.Addr().String() + addr := ts.Listener.Addr().String() + cacheKey := "|http|" + addr if keys[0] != cacheKey { t.Fatalf("Expected idle cache key %q; got %q", cacheKey, keys[0]) } - if e, g := 1, tr.IdleConnCountForTesting(cacheKey); e != g { + if e, g := 1, tr.IdleConnCountForTesting("http", addr); e != g { t.Errorf("after first response, expected %d idle conns; got %d", e, g) } resch <- "res2" <-donech - if g, w := tr.IdleConnCountForTesting(cacheKey), 2; g != w { + if g, w := tr.IdleConnCountForTesting("http", addr), 2; g != w { t.Errorf("after second response, idle conns = %d; want %d", g, w) } resch <- "res3" <-donech - if g, w := tr.IdleConnCountForTesting(cacheKey), maxIdleConnsPerHost; g != w { + if g, w := tr.IdleConnCountForTesting("http", addr), maxIdleConnsPerHost; g != w { t.Errorf("after third response, idle conns = %d; want %d", g, w) } } +func TestTransportMaxConnsPerHostIncludeDialInProgress(t *testing.T) { + defer afterTest(t) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + _, err := w.Write([]byte("foo")) + if err != nil { + t.Fatalf("Write: %v", err) + } + })) + defer ts.Close() + c := ts.Client() + tr := c.Transport.(*Transport) + dialStarted := make(chan struct{}) + stallDial := make(chan struct{}) + tr.Dial = func(network, addr string) (net.Conn, error) { + dialStarted <- struct{}{} + <-stallDial + return net.Dial(network, addr) + } + + tr.DisableKeepAlives = true + tr.MaxConnsPerHost = 1 + + preDial := make(chan struct{}) + reqComplete := make(chan struct{}) + doReq := func(reqId string) { + req, _ := NewRequest("GET", ts.URL, nil) + trace := &httptrace.ClientTrace{ + GetConn: func(hostPort string) { + preDial <- struct{}{} + }, + } + req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) + resp, err := tr.RoundTrip(req) + if err != nil { + t.Errorf("unexpected error for request %s: %v", reqId, err) + } + _, err = ioutil.ReadAll(resp.Body) + if err != nil { + t.Errorf("unexpected error for request %s: %v", reqId, err) + } + reqComplete <- struct{}{} + } + // get req1 to dial-in-progress + go doReq("req1") + <-preDial + <-dialStarted + + // get req2 to waiting on conns per host to go down below max + go doReq("req2") + <-preDial + select { + case <-dialStarted: + t.Error("req2 dial started while req1 dial in progress") + return + default: + } + + // let req1 complete + stallDial <- struct{}{} + <-reqComplete + + // let req2 complete + <-dialStarted + stallDial <- struct{}{} + <-reqComplete +} + func TestTransportRemovesDeadIdleConnections(t *testing.T) { setParallel(t) defer afterTest(t) @@ -958,7 +1028,7 @@ func TestTransportExpect100Continue(t *testing.T) { } } -func TestSocks5Proxy(t *testing.T) { +func TestSOCKS5Proxy(t *testing.T) { defer afterTest(t) ch := make(chan string, 1) l := newLocalListener(t) @@ -995,9 +1065,9 @@ func TestSocks5Proxy(t *testing.T) { var ipLen int switch buf[3] { case 1: - ipLen = 4 + ipLen = net.IPv4len case 4: - ipLen = 16 + ipLen = net.IPv6len default: t.Errorf("socks5 proxy second read: unexpected address type %v", buf[4]) return @@ -2286,6 +2356,7 @@ Content-Length: %d c := &Client{Transport: tr} testResponse := func(req *Request, name string, wantCode int) { + t.Helper() res, err := c.Do(req) if err != nil { t.Fatalf("%s: Do: %v", name, err) @@ -2308,13 +2379,90 @@ Content-Length: %d req.Header.Set("Request-Id", reqID(i)) testResponse(req, fmt.Sprintf("100, %d/%d", i, numReqs), 200) } +} - // And some other informational 1xx but non-100 responses, to test - // we return them but don't re-use the connection. - for i := 1; i <= numReqs; i++ { - req, _ := NewRequest("POST", "http://other.tld/", strings.NewReader(reqBody(i))) - req.Header.Set("X-Want-Response-Code", "123 Sesame Street") - testResponse(req, fmt.Sprintf("123, %d/%d", i, numReqs), 123) +// Issue 17739: the HTTP client must ignore any unknown 1xx +// informational responses before the actual response. +func TestTransportIgnore1xxResponses(t *testing.T) { + setParallel(t) + defer afterTest(t) + cst := newClientServerTest(t, h1Mode, HandlerFunc(func(w ResponseWriter, r *Request) { + conn, buf, _ := w.(Hijacker).Hijack() + buf.Write([]byte("HTTP/1.1 123 OneTwoThree\r\nFoo: bar\r\n\r\nHTTP/1.1 200 OK\r\nBar: baz\r\nContent-Length: 5\r\n\r\nHello")) + buf.Flush() + conn.Close() + })) + defer cst.close() + cst.tr.DisableKeepAlives = true // prevent log spam; our test server is hanging up anyway + + var got bytes.Buffer + + req, _ := NewRequest("GET", cst.ts.URL, nil) + req = req.WithContext(httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{ + Got1xxResponse: func(code int, header textproto.MIMEHeader) error { + fmt.Fprintf(&got, "1xx: code=%v, header=%v\n", code, header) + return nil + }, + })) + res, err := cst.c.Do(req) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + + res.Write(&got) + want := "1xx: code=123, header=map[Foo:[bar]]\nHTTP/1.1 200 OK\r\nContent-Length: 5\r\nBar: baz\r\n\r\nHello" + if got.String() != want { + t.Errorf(" got: %q\nwant: %q\n", got.Bytes(), want) + } +} + +func TestTransportLimits1xxResponses(t *testing.T) { + setParallel(t) + defer afterTest(t) + cst := newClientServerTest(t, h1Mode, HandlerFunc(func(w ResponseWriter, r *Request) { + conn, buf, _ := w.(Hijacker).Hijack() + for i := 0; i < 10; i++ { + buf.Write([]byte("HTTP/1.1 123 OneTwoThree\r\n\r\n")) + } + buf.Write([]byte("HTTP/1.1 204 No Content\r\n\r\n")) + buf.Flush() + conn.Close() + })) + defer cst.close() + cst.tr.DisableKeepAlives = true // prevent log spam; our test server is hanging up anyway + + res, err := cst.c.Get(cst.ts.URL) + if res != nil { + defer res.Body.Close() + } + got := fmt.Sprint(err) + wantSub := "too many 1xx informational responses" + if !strings.Contains(got, wantSub) { + t.Errorf("Get error = %v; want substring %q", err, wantSub) + } +} + +// Issue 26161: the HTTP client must treat 101 responses +// as the final response. +func TestTransportTreat101Terminal(t *testing.T) { + setParallel(t) + defer afterTest(t) + cst := newClientServerTest(t, h1Mode, HandlerFunc(func(w ResponseWriter, r *Request) { + conn, buf, _ := w.(Hijacker).Hijack() + buf.Write([]byte("HTTP/1.1 101 Switching Protocols\r\n\r\n")) + buf.Write([]byte("HTTP/1.1 204 No Content\r\n\r\n")) + buf.Flush() + conn.Close() + })) + defer cst.close() + res, err := cst.c.Get(cst.ts.URL) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + if res.StatusCode != StatusSwitchingProtocols { + t.Errorf("StatusCode = %v; want 101 Switching Protocols", res.StatusCode) } } @@ -2380,39 +2528,61 @@ var proxyFromEnvTests = []proxyFromEnvTest{ // where HTTP_PROXY can be attacker-controlled. {env: "http://10.1.2.3:8080", reqmeth: "POST", want: "<nil>", - wanterr: errors.New("net/http: refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy")}, + wanterr: errors.New("refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy")}, {want: "<nil>"}, {noenv: "example.com", req: "http://example.com/", env: "proxy", want: "<nil>"}, - {noenv: ".example.com", req: "http://example.com/", env: "proxy", want: "<nil>"}, + {noenv: ".example.com", req: "http://example.com/", env: "proxy", want: "http://proxy"}, {noenv: "ample.com", req: "http://example.com/", env: "proxy", want: "http://proxy"}, {noenv: "example.com", req: "http://foo.example.com/", env: "proxy", want: "<nil>"}, {noenv: ".foo.com", req: "http://example.com/", env: "proxy", want: "http://proxy"}, } +func testProxyForRequest(t *testing.T, tt proxyFromEnvTest, proxyForRequest func(req *Request) (*url.URL, error)) { + t.Helper() + reqURL := tt.req + if reqURL == "" { + reqURL = "http://example.com" + } + req, _ := NewRequest("GET", reqURL, nil) + url, err := proxyForRequest(req) + if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e { + t.Errorf("%v: got error = %q, want %q", tt, g, e) + return + } + if got := fmt.Sprintf("%s", url); got != tt.want { + t.Errorf("%v: got URL = %q, want %q", tt, url, tt.want) + } +} + func TestProxyFromEnvironment(t *testing.T) { ResetProxyEnv() defer ResetProxyEnv() for _, tt := range proxyFromEnvTests { - os.Setenv("HTTP_PROXY", tt.env) - os.Setenv("HTTPS_PROXY", tt.httpsenv) - os.Setenv("NO_PROXY", tt.noenv) - os.Setenv("REQUEST_METHOD", tt.reqmeth) - ResetCachedEnvironment() - reqURL := tt.req - if reqURL == "" { - reqURL = "http://example.com" - } - req, _ := NewRequest("GET", reqURL, nil) - url, err := ProxyFromEnvironment(req) - if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e { - t.Errorf("%v: got error = %q, want %q", tt, g, e) - continue - } - if got := fmt.Sprintf("%s", url); got != tt.want { - t.Errorf("%v: got URL = %q, want %q", tt, url, tt.want) - } + testProxyForRequest(t, tt, func(req *Request) (*url.URL, error) { + os.Setenv("HTTP_PROXY", tt.env) + os.Setenv("HTTPS_PROXY", tt.httpsenv) + os.Setenv("NO_PROXY", tt.noenv) + os.Setenv("REQUEST_METHOD", tt.reqmeth) + ResetCachedEnvironment() + return ProxyFromEnvironment(req) + }) + } +} + +func TestProxyFromEnvironmentLowerCase(t *testing.T) { + ResetProxyEnv() + defer ResetProxyEnv() + for _, tt := range proxyFromEnvTests { + testProxyForRequest(t, tt, func(req *Request) (*url.URL, error) { + os.Setenv("http_proxy", tt.env) + os.Setenv("https_proxy", tt.httpsenv) + os.Setenv("no_proxy", tt.noenv) + os.Setenv("REQUEST_METHOD", tt.reqmeth) + ResetCachedEnvironment() + return ProxyFromEnvironment(req) + }) } } @@ -2880,9 +3050,16 @@ func TestRetryRequestsOnError(t *testing.T) { defer SetRoundTripRetried(nil) for i := 0; i < 3; i++ { + t0 := time.Now() res, err := c.Do(tc.req()) if err != nil { - t.Fatalf("i=%d: Do = %v", i, err) + if time.Since(t0) < MaxWriteWaitBeforeConnReuse/2 { + mu.Lock() + got := logbuf.String() + mu.Unlock() + t.Fatalf("i=%d: Do = %v; log:\n%s", i, err, got) + } + t.Skipf("connection likely wasn't recycled within %d, interfering with actual test; skipping", MaxWriteWaitBeforeConnReuse) } res.Body.Close() } @@ -3016,7 +3193,7 @@ func TestRoundTripReturnsProxyError(t *testing.T) { func TestTransportCloseIdleConnsThenReturn(t *testing.T) { tr := &Transport{} wantIdle := func(when string, n int) bool { - got := tr.IdleConnCountForTesting("|http|example.com") // key used by PutIdleTestConn + got := tr.IdleConnCountForTesting("http", "example.com") // key used by PutIdleTestConn if got == n { return true } @@ -3024,10 +3201,10 @@ func TestTransportCloseIdleConnsThenReturn(t *testing.T) { return false } wantIdle("start", 0) - if !tr.PutIdleTestConn() { + if !tr.PutIdleTestConn("http", "example.com") { t.Fatal("put failed") } - if !tr.PutIdleTestConn() { + if !tr.PutIdleTestConn("http", "example.com") { t.Fatal("second put failed") } wantIdle("after put", 2) @@ -3036,7 +3213,7 @@ func TestTransportCloseIdleConnsThenReturn(t *testing.T) { t.Error("should be idle after CloseIdleConnections") } wantIdle("after close idle", 0) - if tr.PutIdleTestConn() { + if tr.PutIdleTestConn("http", "example.com") { t.Fatal("put didn't fail") } wantIdle("after second put", 0) @@ -3045,7 +3222,7 @@ func TestTransportCloseIdleConnsThenReturn(t *testing.T) { if tr.IsIdleForTesting() { t.Error("shouldn't be idle after RequestIdleConnChForTesting") } - if !tr.PutIdleTestConn() { + if !tr.PutIdleTestConn("http", "example.com") { t.Fatal("after re-activation") } wantIdle("after final put", 1) @@ -3263,8 +3440,8 @@ func TestTransportFlushesBodyChunks(t *testing.T) { defer res.Body.Close() want := []string{ - "POST / HTTP/1.1\r\nHost: localhost:8080\r\nUser-Agent: x\r\nTransfer-Encoding: chunked\r\nAccept-Encoding: gzip\r\n\r\n" + - "5\r\nnum0\n\r\n", + "POST / HTTP/1.1\r\nHost: localhost:8080\r\nUser-Agent: x\r\nTransfer-Encoding: chunked\r\nAccept-Encoding: gzip\r\n\r\n", + "5\r\nnum0\n\r\n", "5\r\nnum1\n\r\n", "5\r\nnum2\n\r\n", "0\r\n\r\n", @@ -3274,6 +3451,40 @@ func TestTransportFlushesBodyChunks(t *testing.T) { } } +// Issue 22088: flush Transport request headers if we're not sure the body won't block on read. +func TestTransportFlushesRequestHeader(t *testing.T) { + defer afterTest(t) + gotReq := make(chan struct{}) + cst := newClientServerTest(t, h1Mode, HandlerFunc(func(w ResponseWriter, r *Request) { + close(gotReq) + })) + defer cst.close() + + pr, pw := io.Pipe() + req, err := NewRequest("POST", cst.ts.URL, pr) + if err != nil { + t.Fatal(err) + } + gotRes := make(chan struct{}) + go func() { + defer close(gotRes) + res, err := cst.tr.RoundTrip(req) + if err != nil { + t.Error(err) + return + } + res.Body.Close() + }() + + select { + case <-gotReq: + pw.Close() + case <-time.After(5 * time.Second): + t.Fatal("timeout waiting for handler to get request") + } + <-gotRes +} + // Issue 11745. func TestTransportPrefersResponseOverWriteError(t *testing.T) { if testing.Short() { @@ -3578,8 +3789,12 @@ func TestTransportEventTrace_NoHooks_h2(t *testing.T) { testTransportEventTrace( func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) { defer afterTest(t) const resBody = "some body" - gotWroteReqEvent := make(chan struct{}) + gotWroteReqEvent := make(chan struct{}, 500) cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { + if r.Method == "GET" { + // Do nothing for the second request. + return + } if _, err := ioutil.ReadAll(r.Body); err != nil { t.Error(err) } @@ -3620,7 +3835,9 @@ func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) { return []net.IPAddr{{IP: net.ParseIP(ip)}}, nil }) - req, _ := NewRequest("POST", cst.scheme()+"://dns-is-faked.golang:"+port, strings.NewReader("some body")) + body := "some body" + req, _ := NewRequest("POST", cst.scheme()+"://dns-is-faked.golang:"+port, strings.NewReader(body)) + req.Header["X-Foo-Multiple-Vals"] = []string{"bar", "baz"} trace := &httptrace.ClientTrace{ GetConn: func(hostPort string) { logf("Getting conn for %v ...", hostPort) }, GotConn: func(ci httptrace.GotConnInfo) { logf("got conn: %+v", ci) }, @@ -3635,11 +3852,17 @@ func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) { } logf("ConnectDone: connected to %s %s = %v", network, addr, err) }, + WroteHeaderField: func(key string, value []string) { + logf("WroteHeaderField: %s: %v", key, value) + }, + WroteHeaders: func() { + logf("WroteHeaders") + }, Wait100Continue: func() { logf("Wait100Continue") }, Got100Continue: func() { logf("Got100Continue") }, WroteRequest: func(e httptrace.WroteRequestInfo) { logf("WroteRequest: %+v", e) - close(gotWroteReqEvent) + gotWroteReqEvent <- struct{}{} }, } if h2 { @@ -3704,7 +3927,15 @@ func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) { wantOnce("tls handshake done") } else { wantOnce("PutIdleConn = <nil>") - } + wantOnce("WroteHeaderField: User-Agent: [Go-http-client/1.1]") + // TODO(meirf): issue 19761. Make these agnostic to h1/h2. (These are not h1 specific, but the + // WroteHeaderField hook is not yet implemented in h2.) + wantOnce(fmt.Sprintf("WroteHeaderField: Host: [dns-is-faked.golang:%s]", port)) + wantOnce(fmt.Sprintf("WroteHeaderField: Content-Length: [%d]", len(body))) + wantOnce("WroteHeaderField: X-Foo-Multiple-Vals: [bar baz]") + wantOnce("WroteHeaderField: Accept-Encoding: [gzip]") + } + wantOnce("WroteHeaders") wantOnce("Wait100Continue") wantOnce("Got100Continue") wantOnce("WroteRequest: {Err:<nil>}") @@ -3714,6 +3945,90 @@ func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) { if t.Failed() { t.Errorf("Output:\n%s", got) } + + // And do a second request: + req, _ = NewRequest("GET", cst.scheme()+"://dns-is-faked.golang:"+port, nil) + req = req.WithContext(httptrace.WithClientTrace(ctx, trace)) + res, err = cst.c.Do(req) + if err != nil { + t.Fatal(err) + } + if res.StatusCode != 200 { + t.Fatal(res.Status) + } + res.Body.Close() + + mu.Lock() + got = buf.String() + mu.Unlock() + + sub := "Getting conn for dns-is-faked.golang:" + if gotn, want := strings.Count(got, sub), 2; gotn != want { + t.Errorf("substring %q appeared %d times; want %d. Log:\n%s", sub, gotn, want, got) + } + +} + +func TestTransportEventTraceTLSVerify(t *testing.T) { + var mu sync.Mutex + var buf bytes.Buffer + logf := func(format string, args ...interface{}) { + mu.Lock() + defer mu.Unlock() + fmt.Fprintf(&buf, format, args...) + buf.WriteByte('\n') + } + + ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) { + t.Error("Unexpected request") + })) + defer ts.Close() + ts.Config.ErrorLog = log.New(funcWriter(func(p []byte) (int, error) { + logf("%s", p) + return len(p), nil + }), "", 0) + + certpool := x509.NewCertPool() + certpool.AddCert(ts.Certificate()) + + c := &Client{Transport: &Transport{ + TLSClientConfig: &tls.Config{ + ServerName: "dns-is-faked.golang", + RootCAs: certpool, + }, + }} + + trace := &httptrace.ClientTrace{ + TLSHandshakeStart: func() { logf("TLSHandshakeStart") }, + TLSHandshakeDone: func(s tls.ConnectionState, err error) { + logf("TLSHandshakeDone: ConnectionState = %v \n err = %v", s, err) + }, + } + + req, _ := NewRequest("GET", ts.URL, nil) + req = req.WithContext(httptrace.WithClientTrace(context.Background(), trace)) + _, err := c.Do(req) + if err == nil { + t.Error("Expected request to fail TLS verification") + } + + mu.Lock() + got := buf.String() + mu.Unlock() + + wantOnce := func(sub string) { + if strings.Count(got, sub) != 1 { + t.Errorf("expected substring %q exactly once in output.", sub) + } + } + + wantOnce("TLSHandshakeStart") + wantOnce("TLSHandshakeDone") + wantOnce("err = x509: certificate is valid for example.com") + + if t.Failed() { + t.Errorf("Output:\n%s", got) + } } var ( @@ -4365,3 +4680,161 @@ func TestNoBodyOnChunked304Response(t *testing.T) { t.Errorf("Unexpected body on 304 response") } } + +type funcWriter func([]byte) (int, error) + +func (f funcWriter) Write(p []byte) (int, error) { return f(p) } + +type doneContext struct { + context.Context + err error +} + +func (doneContext) Done() <-chan struct{} { + c := make(chan struct{}) + close(c) + return c +} + +func (d doneContext) Err() error { return d.err } + +// Issue 25852: Transport should check whether Context is done early. +func TestTransportCheckContextDoneEarly(t *testing.T) { + tr := &Transport{} + req, _ := NewRequest("GET", "http://fake.example/", nil) + wantErr := errors.New("some error") + req = req.WithContext(doneContext{context.Background(), wantErr}) + _, err := tr.RoundTrip(req) + if err != wantErr { + t.Errorf("error = %v; want %v", err, wantErr) + } +} + +// Issue 23399: verify that if a client request times out, the Transport's +// conn is closed so that it's not reused. +// +// This is the test variant that times out before the server replies with +// any response headers. +func TestClientTimeoutKillsConn_BeforeHeaders(t *testing.T) { + setParallel(t) + defer afterTest(t) + inHandler := make(chan net.Conn, 1) + handlerReadReturned := make(chan bool, 1) + cst := newClientServerTest(t, h1Mode, HandlerFunc(func(w ResponseWriter, r *Request) { + conn, _, err := w.(Hijacker).Hijack() + if err != nil { + t.Error(err) + return + } + inHandler <- conn + n, err := conn.Read([]byte{0}) + if n != 0 || err != io.EOF { + t.Errorf("unexpected Read result: %v, %v", n, err) + } + handlerReadReturned <- true + })) + defer cst.close() + + const timeout = 50 * time.Millisecond + cst.c.Timeout = timeout + + _, err := cst.c.Get(cst.ts.URL) + if err == nil { + t.Fatal("unexpected Get succeess") + } + + select { + case c := <-inHandler: + select { + case <-handlerReadReturned: + // Success. + return + case <-time.After(5 * time.Second): + t.Error("Handler's conn.Read seems to be stuck in Read") + c.Close() // close it to unblock Handler + } + case <-time.After(timeout * 10): + // If we didn't get into the Handler in 50ms, that probably means + // the builder was just slow and the the Get failed in that time + // but never made it to the server. That's fine. We'll usually + // test the part above on faster machines. + t.Skip("skipping test on slow builder") + } +} + +// Issue 23399: verify that if a client request times out, the Transport's +// conn is closed so that it's not reused. +// +// This is the test variant that has the server send response headers +// first, and time out during the the write of the response body. +func TestClientTimeoutKillsConn_AfterHeaders(t *testing.T) { + setParallel(t) + defer afterTest(t) + inHandler := make(chan net.Conn, 1) + handlerResult := make(chan error, 1) + cst := newClientServerTest(t, h1Mode, HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("Content-Length", "100") + w.(Flusher).Flush() + conn, _, err := w.(Hijacker).Hijack() + if err != nil { + t.Error(err) + return + } + conn.Write([]byte("foo")) + inHandler <- conn + n, err := conn.Read([]byte{0}) + // The error should be io.EOF or "read tcp + // 127.0.0.1:35827->127.0.0.1:40290: read: connection + // reset by peer" depending on timing. Really we just + // care that it returns at all. But if it returns with + // data, that's weird. + if n != 0 || err == nil { + handlerResult <- fmt.Errorf("unexpected Read result: %v, %v", n, err) + return + } + handlerResult <- nil + })) + defer cst.close() + + // Set Timeout to something very long but non-zero to exercise + // the codepaths that check for it. But rather than wait for it to fire + // (which would make the test slow), we send on the req.Cancel channel instead, + // which happens to exercise the same code paths. + cst.c.Timeout = time.Minute // just to be non-zero, not to hit it. + req, _ := NewRequest("GET", cst.ts.URL, nil) + cancel := make(chan struct{}) + req.Cancel = cancel + + res, err := cst.c.Do(req) + if err != nil { + select { + case <-inHandler: + t.Fatalf("Get error: %v", err) + default: + // Failed before entering handler. Ignore result. + t.Skip("skipping test on slow builder") + } + } + + close(cancel) + got, err := ioutil.ReadAll(res.Body) + if err == nil { + t.Fatalf("unexpected success; read %q, nil", got) + } + + select { + case c := <-inHandler: + select { + case err := <-handlerResult: + if err != nil { + t.Errorf("handler: %v", err) + } + return + case <-time.After(5 * time.Second): + t.Error("Handler's conn.Read seems to be stuck in Read") + c.Close() // close it to unblock Handler + } + case <-time.After(5 * time.Second): + t.Fatal("timeout") + } +} diff --git a/libgo/go/net/http/triv.go b/libgo/go/net/http/triv.go index cfbc577..23e65d5 100644 --- a/libgo/go/net/http/triv.go +++ b/libgo/go/net/http/triv.go @@ -107,7 +107,7 @@ func DateServer(rw http.ResponseWriter, req *http.Request) { date, err := exec.Command("/bin/date").Output() if err != nil { - http.Error(rw, err.Error(), 500) + http.Error(rw, err.Error(), http.StatusInternalServerError) return } rw.Write(date) @@ -115,7 +115,7 @@ func DateServer(rw http.ResponseWriter, req *http.Request) { func Logger(w http.ResponseWriter, req *http.Request) { log.Print(req.URL) - http.Error(w, "oops", 404) + http.Error(w, "oops", http.StatusNotFound) } var webroot = flag.String("root", os.Getenv("HOME"), "web root directory") diff --git a/libgo/go/net/interface.go b/libgo/go/net/interface.go index 4036a7f..375a456 100644 --- a/libgo/go/net/interface.go +++ b/libgo/go/net/interface.go @@ -10,7 +10,7 @@ import ( "time" ) -// BUG(mikio): On NaCl, methods and functions related to +// BUG(mikio): On JS and NaCl, methods and functions related to // Interface are not implemented. // BUG(mikio): On DragonFly BSD, NetBSD, OpenBSD, Plan 9 and Solaris, diff --git a/libgo/go/net/interface_stub.go b/libgo/go/net/interface_stub.go index 6d7147e..3b3b3c0 100644 --- a/libgo/go/net/interface_stub.go +++ b/libgo/go/net/interface_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix nacl +// +build aix nacl js,wasm package net diff --git a/libgo/go/net/interface_test.go b/libgo/go/net/interface_test.go index 534137a..5d183c5 100644 --- a/libgo/go/net/interface_test.go +++ b/libgo/go/net/interface_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( @@ -202,7 +204,7 @@ func validateInterfaceUnicastAddrs(ifat []Addr) (*routeStats, error) { if 0 >= prefixLen || prefixLen > 8*IPv4len || maxPrefixLen != 8*IPv4len { return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa) } - if ifa.IP.IsLoopback() && (prefixLen != 8 && prefixLen != 8*IPv4len) { // see RFC 1122 + if ifa.IP.IsLoopback() && prefixLen < 8 { // see RFC 1122 return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa) } stats.ipv4++ diff --git a/libgo/go/net/interface_windows.go b/libgo/go/net/interface_windows.go index b08d158..28b0a65 100644 --- a/libgo/go/net/interface_windows.go +++ b/libgo/go/net/interface_windows.go @@ -11,22 +11,6 @@ import ( "unsafe" ) -// supportsVistaIP reports whether the platform implements new IP -// stack and ABIs supported on Windows Vista and above. -var supportsVistaIP bool - -func init() { - supportsVistaIP = probeWindowsIPStack() -} - -func probeWindowsIPStack() (supportsVistaIP bool) { - v, err := syscall.GetVersion() - if err != nil { - return true // Windows 10 and above will deprecate this API - } - return byte(v) >= 6 // major version of Windows Vista is 6 -} - // adapterAddresses returns a list of IP adapter and address // structures. The structure contains an IP adapter and flattened // multiple IP addresses including unicast, anycast and multicast @@ -81,9 +65,8 @@ func interfaceTable(ifindex int) ([]Interface, error) { } // For now we need to infer link-layer service // capabilities from media types. - // We will be able to use - // MIB_IF_ROW2.AccessType once we drop support - // for Windows XP. + // TODO: use MIB_IF_ROW2.AccessType now that we no longer support + // Windows XP. switch aa.IfType { case windows.IF_TYPE_ETHERNET_CSMACD, windows.IF_TYPE_ISO88025_TOKENRING, windows.IF_TYPE_IEEE80211, windows.IF_TYPE_IEEE1394: ifi.Flags |= FlagBroadcast | FlagMulticast @@ -126,35 +109,17 @@ func interfaceAddrTable(ifi *Interface) ([]Addr, error) { if index == 0 { // ipv6IfIndex is a substitute for ifIndex index = aa.Ipv6IfIndex } - var pfx4, pfx6 []IPNet - if !supportsVistaIP { - pfx4, pfx6, err = addrPrefixTable(aa) - if err != nil { - return nil, err - } - } if ifi == nil || ifi.Index == int(index) { for puni := aa.FirstUnicastAddress; puni != nil; puni = puni.Next { sa, err := puni.Address.Sockaddr.Sockaddr() if err != nil { return nil, os.NewSyscallError("sockaddr", err) } - var l int switch sa := sa.(type) { case *syscall.SockaddrInet4: - if supportsVistaIP { - l = int(puni.OnLinkPrefixLength) - } else { - l = addrPrefixLen(pfx4, IP(sa.Addr[:])) - } - ifat = append(ifat, &IPNet{IP: IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]), Mask: CIDRMask(l, 8*IPv4len)}) + ifat = append(ifat, &IPNet{IP: IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]), Mask: CIDRMask(int(puni.OnLinkPrefixLength), 8*IPv4len)}) case *syscall.SockaddrInet6: - if supportsVistaIP { - l = int(puni.OnLinkPrefixLength) - } else { - l = addrPrefixLen(pfx6, IP(sa.Addr[:])) - } - ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(l, 8*IPv6len)} + ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(puni.OnLinkPrefixLength), 8*IPv6len)} copy(ifa.IP, sa.Addr[:]) ifat = append(ifat, ifa) } @@ -178,59 +143,6 @@ func interfaceAddrTable(ifi *Interface) ([]Addr, error) { return ifat, nil } -func addrPrefixTable(aa *windows.IpAdapterAddresses) (pfx4, pfx6 []IPNet, err error) { - for p := aa.FirstPrefix; p != nil; p = p.Next { - sa, err := p.Address.Sockaddr.Sockaddr() - if err != nil { - return nil, nil, os.NewSyscallError("sockaddr", err) - } - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - pfx := IPNet{IP: IP(sa.Addr[:]), Mask: CIDRMask(int(p.PrefixLength), 8*IPv4len)} - pfx4 = append(pfx4, pfx) - case *syscall.SockaddrInet6: - pfx := IPNet{IP: IP(sa.Addr[:]), Mask: CIDRMask(int(p.PrefixLength), 8*IPv6len)} - pfx6 = append(pfx6, pfx) - } - } - return -} - -// addrPrefixLen returns an appropriate prefix length in bits for ip -// from pfxs. It returns 32 or 128 when no appropriate on-link address -// prefix found. -// -// NOTE: This is pretty naive implementation that contains many -// allocations and non-effective linear search, and should not be used -// freely. -func addrPrefixLen(pfxs []IPNet, ip IP) int { - var l int - var cand *IPNet - for i := range pfxs { - if !pfxs[i].Contains(ip) { - continue - } - if cand == nil { - l, _ = pfxs[i].Mask.Size() - cand = &pfxs[i] - continue - } - m, _ := pfxs[i].Mask.Size() - if m > l { - l = m - cand = &pfxs[i] - continue - } - } - if l > 0 { - return l - } - if ip.To4() != nil { - return 8 * IPv4len - } - return 8 * IPv6len -} - // interfaceMulticastAddrTable returns addresses for a specific // interface. func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { diff --git a/libgo/go/net/interface_windows_test.go b/libgo/go/net/interface_windows_test.go deleted file mode 100644 index 03f9168..0000000 --- a/libgo/go/net/interface_windows_test.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2015 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 ( - "bytes" - "internal/syscall/windows" - "sort" - "testing" -) - -func TestWindowsInterfaces(t *testing.T) { - aas, err := adapterAddresses() - if err != nil { - t.Fatal(err) - } - ift, err := Interfaces() - if err != nil { - t.Fatal(err) - } - for i, ifi := range ift { - aa := aas[i] - if len(ifi.HardwareAddr) != int(aa.PhysicalAddressLength) { - t.Errorf("got %d; want %d", len(ifi.HardwareAddr), aa.PhysicalAddressLength) - } - if ifi.MTU > 0x7fffffff { - t.Errorf("%s: got %d; want less than or equal to 1<<31 - 1", ifi.Name, ifi.MTU) - } - if ifi.Flags&FlagUp != 0 && aa.OperStatus != windows.IfOperStatusUp { - t.Errorf("%s: got %v; should not include FlagUp", ifi.Name, ifi.Flags) - } - if ifi.Flags&FlagLoopback != 0 && aa.IfType != windows.IF_TYPE_SOFTWARE_LOOPBACK { - t.Errorf("%s: got %v; should not include FlagLoopback", ifi.Name, ifi.Flags) - } - if _, _, err := addrPrefixTable(aa); err != nil { - t.Errorf("%s: %v", ifi.Name, err) - } - } -} - -type byAddrLen []IPNet - -func (ps byAddrLen) Len() int { return len(ps) } - -func (ps byAddrLen) Less(i, j int) bool { - if n := bytes.Compare(ps[i].IP, ps[j].IP); n != 0 { - return n < 0 - } - if n := bytes.Compare(ps[i].Mask, ps[j].Mask); n != 0 { - return n < 0 - } - return false -} - -func (ps byAddrLen) Swap(i, j int) { ps[i], ps[j] = ps[j], ps[i] } - -var windowsAddrPrefixLenTests = []struct { - pfxs []IPNet - ip IP - out int -}{ - { - []IPNet{ - {IP: IPv4(172, 16, 0, 0), Mask: IPv4Mask(255, 255, 0, 0)}, - {IP: IPv4(192, 168, 0, 0), Mask: IPv4Mask(255, 255, 255, 0)}, - {IP: IPv4(192, 168, 0, 0), Mask: IPv4Mask(255, 255, 255, 128)}, - {IP: IPv4(192, 168, 0, 0), Mask: IPv4Mask(255, 255, 255, 192)}, - }, - IPv4(192, 168, 0, 1), - 26, - }, - { - []IPNet{ - {IP: ParseIP("2001:db8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0"))}, - {IP: ParseIP("2001:db8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff8"))}, - {IP: ParseIP("2001:db8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc"))}, - }, - ParseIP("2001:db8::1"), - 126, - }, - - // Fallback cases. It may happen on Windows XP or 2003 server. - { - []IPNet{ - {IP: IPv4(127, 0, 0, 0).To4(), Mask: IPv4Mask(255, 0, 0, 0)}, - {IP: IPv4(10, 0, 0, 0).To4(), Mask: IPv4Mask(255, 0, 0, 0)}, - {IP: IPv4(172, 16, 0, 0).To4(), Mask: IPv4Mask(255, 255, 0, 0)}, - {IP: IPv4(192, 168, 255, 0), Mask: IPv4Mask(255, 255, 255, 0)}, - {IP: IPv4zero, Mask: IPv4Mask(0, 0, 0, 0)}, - }, - IPv4(192, 168, 0, 1), - 8 * IPv4len, - }, - { - nil, - IPv4(192, 168, 0, 1), - 8 * IPv4len, - }, - { - []IPNet{ - {IP: IPv6loopback, Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))}, - {IP: ParseIP("2001:db8:1::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0"))}, - {IP: ParseIP("2001:db8:2::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff8"))}, - {IP: ParseIP("2001:db8:3::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc"))}, - {IP: IPv6unspecified, Mask: IPMask(ParseIP("::"))}, - }, - ParseIP("2001:db8::1"), - 8 * IPv6len, - }, - { - nil, - ParseIP("2001:db8::1"), - 8 * IPv6len, - }, -} - -func TestWindowsAddrPrefixLen(t *testing.T) { - for i, tt := range windowsAddrPrefixLenTests { - sort.Sort(byAddrLen(tt.pfxs)) - l := addrPrefixLen(tt.pfxs, tt.ip) - if l != tt.out { - t.Errorf("#%d: got %d; want %d", i, l, tt.out) - } - sort.Sort(sort.Reverse(byAddrLen(tt.pfxs))) - l = addrPrefixLen(tt.pfxs, tt.ip) - if l != tt.out { - t.Errorf("#%d: got %d; want %d", i, l, tt.out) - } - } -} diff --git a/libgo/go/net/internal/socktest/main_test.go b/libgo/go/net/internal/socktest/main_test.go index 60e581f..3b0a48a 100644 --- a/libgo/go/net/internal/socktest/main_test.go +++ b/libgo/go/net/internal/socktest/main_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !plan9 +// +build !js,!plan9 package socktest_test diff --git a/libgo/go/net/internal/socktest/main_unix_test.go b/libgo/go/net/internal/socktest/main_unix_test.go index b8eebc2..4d9d414 100644 --- a/libgo/go/net/internal/socktest/main_unix_test.go +++ b/libgo/go/net/internal/socktest/main_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !plan9,!windows +// +build !js,!plan9,!windows package socktest_test diff --git a/libgo/go/net/internal/socktest/switch_unix.go b/libgo/go/net/internal/socktest/switch_unix.go index 8fb15f3..0626aa0 100644 --- a/libgo/go/net/internal/socktest/switch_unix.go +++ b/libgo/go/net/internal/socktest/switch_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package socktest diff --git a/libgo/go/net/internal/socktest/sys_cloexec.go b/libgo/go/net/internal/socktest/sys_cloexec.go index d1b8f4f..986d894 100644 --- a/libgo/go/net/internal/socktest/sys_cloexec.go +++ b/libgo/go/net/internal/socktest/sys_cloexec.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build dragonfly freebsd linux +// +build dragonfly freebsd linux netbsd openbsd package socktest diff --git a/libgo/go/net/internal/socktest/sys_unix.go b/libgo/go/net/internal/socktest/sys_unix.go index 397c524..b96075b 100644 --- a/libgo/go/net/internal/socktest/sys_unix.go +++ b/libgo/go/net/internal/socktest/sys_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package socktest diff --git a/libgo/go/net/ip.go b/libgo/go/net/ip.go index 6b7ba4c..da8dca5 100644 --- a/libgo/go/net/ip.go +++ b/libgo/go/net/ip.go @@ -260,6 +260,25 @@ func (ip IP) Mask(mask IPMask) IP { return out } +// ubtoa encodes the string form of the integer v to dst[start:] and +// returns the number of bytes written to dst. The caller must ensure +// that dst has sufficient length. +func ubtoa(dst []byte, start int, v byte) int { + if v < 10 { + dst[start] = v + '0' + return 1 + } else if v < 100 { + dst[start+1] = v%10 + '0' + dst[start] = v/10 + '0' + return 2 + } + + dst[start+2] = v%10 + '0' + dst[start+1] = (v/10)%10 + '0' + dst[start] = v/100 + '0' + return 3 +} + // String returns the string form of the IP address ip. // It returns one of 4 forms: // - "<nil>", if ip has length 0 @@ -275,10 +294,23 @@ func (ip IP) String() string { // If IPv4, use dotted notation. if p4 := p.To4(); len(p4) == IPv4len { - return uitoa(uint(p4[0])) + "." + - uitoa(uint(p4[1])) + "." + - uitoa(uint(p4[2])) + "." + - uitoa(uint(p4[3])) + const maxIPv4StringLen = len("255.255.255.255") + b := make([]byte, maxIPv4StringLen) + + n := ubtoa(b, 0, p4[0]) + b[n] = '.' + n++ + + n += ubtoa(b, n, p4[1]) + b[n] = '.' + n++ + + n += ubtoa(b, n, p4[2]) + b[n] = '.' + n++ + + n += ubtoa(b, n, p4[3]) + return string(b[:n]) } if len(p) != IPv6len { return "?" + hexString(ip) @@ -530,25 +562,26 @@ func parseIPv4(s string) IP { return IPv4(p[0], p[1], p[2], p[3]) } -// parseIPv6 parses s as a literal IPv6 address described in RFC 4291 -// and RFC 5952. It can also parse a literal scoped IPv6 address with -// zone identifier which is described in RFC 4007 when zoneAllowed is -// true. -func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) { +// parseIPv6Zone parses s as a literal IPv6 address and its associated zone +// identifier which is described in RFC 4007. +func parseIPv6Zone(s string) (IP, string) { + s, zone := splitHostZone(s) + return parseIPv6(s), zone +} + +// parseIPv6Zone parses s as a literal IPv6 address described in RFC 4291 +// and RFC 5952. +func parseIPv6(s string) (ip IP) { ip = make(IP, IPv6len) ellipsis := -1 // position of ellipsis in ip - if zoneAllowed { - s, zone = splitHostZone(s) - } - // Might have leading ellipsis if len(s) >= 2 && s[0] == ':' && s[1] == ':' { ellipsis = 0 s = s[2:] // Might be only ellipsis if len(s) == 0 { - return ip, zone + return ip } } @@ -558,22 +591,22 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) { // Hex number. n, c, ok := xtoi(s) if !ok || n > 0xFFFF { - return nil, zone + return nil } // If followed by dot, might be in trailing IPv4. if c < len(s) && s[c] == '.' { if ellipsis < 0 && i != IPv6len-IPv4len { // Not the right place. - return nil, zone + return nil } if i+IPv4len > IPv6len { // Not enough room. - return nil, zone + return nil } ip4 := parseIPv4(s) if ip4 == nil { - return nil, zone + return nil } ip[i] = ip4[12] ip[i+1] = ip4[13] @@ -597,14 +630,14 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) { // Otherwise must be followed by colon and more. if s[0] != ':' || len(s) == 1 { - return nil, zone + return nil } s = s[1:] // Look for ellipsis. if s[0] == ':' { if ellipsis >= 0 { // already have one - return nil, zone + return nil } ellipsis = i s = s[1:] @@ -616,13 +649,13 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) { // Must have used entire string. if len(s) != 0 { - return nil, zone + return nil } // If didn't parse enough, expand ellipsis. if i < IPv6len { if ellipsis < 0 { - return nil, zone + return nil } n := IPv6len - i for j := i - 1; j >= ellipsis; j-- { @@ -633,9 +666,9 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) { } } else if ellipsis >= 0 { // Ellipsis must represent at least one 0 group. - return nil, zone + return nil } - return ip, zone + return ip } // ParseIP parses s as an IP address, returning the result. @@ -649,13 +682,26 @@ func ParseIP(s string) IP { case '.': return parseIPv4(s) case ':': - ip, _ := parseIPv6(s, false) - return ip + return parseIPv6(s) } } return nil } +// parseIPZone parses s as an IP address, return it and its associated zone +// identifier (IPv6 only). +func parseIPZone(s string) (IP, string) { + for i := 0; i < len(s); i++ { + switch s[i] { + case '.': + return parseIPv4(s), "" + case ':': + return parseIPv6Zone(s) + } + } + return nil, "" +} + // ParseCIDR parses s as a CIDR notation IP address and prefix length, // like "192.0.2.0/24" or "2001:db8::/32", as defined in // RFC 4632 and RFC 4291. @@ -674,7 +720,7 @@ func ParseCIDR(s string) (IP, *IPNet, error) { ip := parseIPv4(addr) if ip == nil { iplen = IPv6len - ip, _ = parseIPv6(addr, false) + ip = parseIPv6(addr) } n, i, ok := dtoi(mask) if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen { diff --git a/libgo/go/net/ip_test.go b/libgo/go/net/ip_test.go index ad13388..a5fc5e6 100644 --- a/libgo/go/net/ip_test.go +++ b/libgo/go/net/ip_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( @@ -129,7 +131,7 @@ func TestMarshalEmptyIP(t *testing.T) { } } -var ipStringTests = []struct { +var ipStringTests = []*struct { in IP // see RFC 791 and RFC 4291 str string // see RFC 791, RFC 4291 and RFC 5952 byt []byte @@ -252,9 +254,21 @@ var sink string func BenchmarkIPString(b *testing.B) { testHookUninstaller.Do(uninstallTestHooks) + b.Run("IPv4", func(b *testing.B) { + benchmarkIPString(b, IPv4len) + }) + + b.Run("IPv6", func(b *testing.B) { + benchmarkIPString(b, IPv6len) + }) +} + +func benchmarkIPString(b *testing.B, size int) { + b.ReportAllocs() + b.ResetTimer() for i := 0; i < b.N; i++ { for _, tt := range ipStringTests { - if tt.in != nil { + if tt.in != nil && len(tt.in) == size { sink = tt.in.String() } } diff --git a/libgo/go/net/iprawsock.go b/libgo/go/net/iprawsock.go index 72cbc39..8a9c265 100644 --- a/libgo/go/net/iprawsock.go +++ b/libgo/go/net/iprawsock.go @@ -21,8 +21,8 @@ import ( // change the behavior of these methods; use Read or ReadMsgIP // instead. -// BUG(mikio): On NaCl and Plan 9, the ReadMsgIP and -// WriteMsgIP methods of IPConn are not implemented. +// BUG(mikio): On JS, NaCl and Plan 9, methods and functions related +// to IPConn are not implemented. // BUG(mikio): On Windows, the File method of IPConn is not // implemented. @@ -209,7 +209,11 @@ func newIPConn(fd *netFD) *IPConn { return &IPConn{conn{fd}} } // If the IP field of raddr is nil or an unspecified IP address, the // local system is assumed. func DialIP(network string, laddr, raddr *IPAddr) (*IPConn, error) { - c, err := dialIP(context.Background(), network, laddr, raddr) + if raddr == nil { + return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress} + } + sd := &sysDialer{network: network, address: raddr.String()} + c, err := sd.dialIP(context.Background(), laddr, raddr) if err != nil { return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err} } @@ -224,7 +228,11 @@ func DialIP(network string, laddr, raddr *IPAddr) (*IPConn, error) { // ListenIP listens on all available IP addresses of the local system // except multicast IP addresses. func ListenIP(network string, laddr *IPAddr) (*IPConn, error) { - c, err := listenIP(context.Background(), network, laddr) + if laddr == nil { + laddr = &IPAddr{} + } + sl := &sysListener{network: network, address: laddr.String()} + c, err := sl.listenIP(context.Background(), laddr) if err != nil { return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err} } diff --git a/libgo/go/net/iprawsock_plan9.go b/libgo/go/net/iprawsock_plan9.go index 6aebea1..ebe5808 100644 --- a/libgo/go/net/iprawsock_plan9.go +++ b/libgo/go/net/iprawsock_plan9.go @@ -25,10 +25,10 @@ func (c *IPConn) writeMsg(b, oob []byte, addr *IPAddr) (n, oobn int, err error) return 0, 0, syscall.EPLAN9 } -func dialIP(ctx context.Context, netProto string, laddr, raddr *IPAddr) (*IPConn, error) { +func (sd *sysDialer) dialIP(ctx context.Context, laddr, raddr *IPAddr) (*IPConn, error) { return nil, syscall.EPLAN9 } -func listenIP(ctx context.Context, netProto string, laddr *IPAddr) (*IPConn, error) { +func (sl *sysListener) listenIP(ctx context.Context, laddr *IPAddr) (*IPConn, error) { return nil, syscall.EPLAN9 } diff --git a/libgo/go/net/iprawsock_posix.go b/libgo/go/net/iprawsock_posix.go index d613e6f..2a5d49f 100644 --- a/libgo/go/net/iprawsock_posix.go +++ b/libgo/go/net/iprawsock_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package net @@ -112,37 +112,34 @@ func (c *IPConn) writeMsg(b, oob []byte, addr *IPAddr) (n, oobn int, err error) return c.fd.writeMsg(b, oob, sa) } -func dialIP(ctx context.Context, netProto string, laddr, raddr *IPAddr) (*IPConn, error) { - network, proto, err := parseNetwork(ctx, netProto, true) +func (sd *sysDialer) dialIP(ctx context.Context, laddr, raddr *IPAddr) (*IPConn, error) { + network, proto, err := parseNetwork(ctx, sd.network, true) if err != nil { return nil, err } switch network { case "ip", "ip4", "ip6": default: - return nil, UnknownNetworkError(netProto) + return nil, UnknownNetworkError(sd.network) } - if raddr == nil { - return nil, errMissingAddress - } - fd, err := internetSocket(ctx, network, laddr, raddr, syscall.SOCK_RAW, proto, "dial") + fd, err := internetSocket(ctx, network, laddr, raddr, syscall.SOCK_RAW, proto, "dial", sd.Dialer.Control) if err != nil { return nil, err } return newIPConn(fd), nil } -func listenIP(ctx context.Context, netProto string, laddr *IPAddr) (*IPConn, error) { - network, proto, err := parseNetwork(ctx, netProto, true) +func (sl *sysListener) listenIP(ctx context.Context, laddr *IPAddr) (*IPConn, error) { + network, proto, err := parseNetwork(ctx, sl.network, true) if err != nil { return nil, err } switch network { case "ip", "ip4", "ip6": default: - return nil, UnknownNetworkError(netProto) + return nil, UnknownNetworkError(sl.network) } - fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_RAW, proto, "listen") + fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_RAW, proto, "listen", sl.ListenConfig.Control) if err != nil { return nil, err } diff --git a/libgo/go/net/iprawsock_test.go b/libgo/go/net/iprawsock_test.go index 8972051..8e3543d 100644 --- a/libgo/go/net/iprawsock_test.go +++ b/libgo/go/net/iprawsock_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( diff --git a/libgo/go/net/ipsock.go b/libgo/go/net/ipsock.go index 947bdf3..f4ff82b 100644 --- a/libgo/go/net/ipsock.go +++ b/libgo/go/net/ipsock.go @@ -276,24 +276,16 @@ func (r *Resolver) internetAddrList(ctx context.Context, net, addr string) (addr } // Try as a literal IP address, then as a DNS name. - var ips []IPAddr - if ip := parseIPv4(host); ip != nil { - ips = []IPAddr{{IP: ip}} - } else if ip, zone := parseIPv6(host, true); ip != nil { - ips = []IPAddr{{IP: ip, Zone: zone}} - // Issue 18806: if the machine has halfway configured - // IPv6 such that it can bind on "::" (IPv6unspecified) - // but not connect back to that same address, fall - // back to dialing 0.0.0.0. - if ip.Equal(IPv6unspecified) { - ips = append(ips, IPAddr{IP: IPv4zero}) - } - } else { - // Try as a DNS name. - ips, err = r.LookupIPAddr(ctx, host) - if err != nil { - return nil, err - } + ips, err := r.LookupIPAddr(ctx, host) + if err != nil { + return nil, err + } + // Issue 18806: if the machine has halfway configured + // IPv6 such that it can bind on "::" (IPv6unspecified) + // but not connect back to that same address, fall + // back to dialing 0.0.0.0. + if len(ips) == 1 && ips[0].IP.Equal(IPv6unspecified) { + ips = append(ips, IPAddr{IP: IPv4zero}) } var filter func(IPAddr) bool diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go index 9cff960..08804ca 100644 --- a/libgo/go/net/ipsock_posix.go +++ b/libgo/go/net/ipsock_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package net @@ -133,12 +133,12 @@ func favoriteAddrFamily(network string, laddr, raddr sockaddr, mode string) (fam return syscall.AF_INET6, false } -func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string) (fd *netFD, err error) { +func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string, ctrlFn func(string, string, syscall.RawConn) error) (fd *netFD, err error) { if (runtime.GOOS == "windows" || runtime.GOOS == "openbsd" || runtime.GOOS == "nacl") && mode == "dial" && raddr.isWildcard() { raddr = raddr.toLocal(net) } family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode) - return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr) + return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr, ctrlFn) } func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) { diff --git a/libgo/go/net/listen_test.go b/libgo/go/net/listen_test.go index 96624f9..ffce8e2 100644 --- a/libgo/go/net/listen_test.go +++ b/libgo/go/net/listen_test.go @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !plan9 +// +build !js,!plan9 package net import ( + "context" "fmt" "internal/testenv" "os" @@ -729,3 +730,56 @@ func TestClosingListener(t *testing.T) { } ln2.Close() } + +func TestListenConfigControl(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9": + t.Skipf("not supported on %s", runtime.GOOS) + } + + t.Run("StreamListen", func(t *testing.T) { + for _, network := range []string{"tcp", "tcp4", "tcp6", "unix", "unixpacket"} { + if !testableNetwork(network) { + continue + } + ln, err := newLocalListener(network) + if err != nil { + t.Error(err) + continue + } + address := ln.Addr().String() + ln.Close() + lc := ListenConfig{Control: controlOnConnSetup} + ln, err = lc.Listen(context.Background(), network, address) + if err != nil { + t.Error(err) + continue + } + ln.Close() + } + }) + t.Run("PacketListen", func(t *testing.T) { + for _, network := range []string{"udp", "udp4", "udp6", "unixgram"} { + if !testableNetwork(network) { + continue + } + c, err := newLocalPacketListener(network) + if err != nil { + t.Error(err) + continue + } + address := c.LocalAddr().String() + c.Close() + if network == "unixgram" { + os.Remove(address) + } + lc := ListenConfig{Control: controlOnConnSetup} + c, err = lc.ListenPacket(context.Background(), network, address) + if err != nil { + t.Error(err) + continue + } + c.Close() + } + }) +} diff --git a/libgo/go/net/lookup.go b/libgo/go/net/lookup.go index a65b735..e0f21fa 100644 --- a/libgo/go/net/lookup.go +++ b/libgo/go/net/lookup.go @@ -15,7 +15,7 @@ import ( // names and numbers for platforms that don't have a complete list of // protocol numbers. // -// See http://www.iana.org/assignments/protocol-numbers +// See https://www.iana.org/assignments/protocol-numbers // // On Unix, this map is augmented by readProtocols via lookupProtocol. var protocols = map[string]int{ @@ -133,10 +133,25 @@ type Resolver struct { // If nil, the default dialer is used. Dial func(ctx context.Context, network, address string) (Conn, error) + // lookupGroup merges LookupIPAddr calls together for lookups for the same + // host. The lookupGroup key is the LookupIPAddr.host argument. + // The return values are ([]IPAddr, error). + lookupGroup singleflight.Group + // TODO(bradfitz): optional interface impl override hook // TODO(bradfitz): Timeout time.Duration? } +func (r *Resolver) preferGo() bool { return r != nil && r.PreferGo } +func (r *Resolver) strictErrors() bool { return r != nil && r.StrictErrors } + +func (r *Resolver) getLookupGroup() *singleflight.Group { + if r == nil { + return &DefaultResolver.lookupGroup + } + return &r.lookupGroup +} + // LookupHost looks up the given host using the local resolver. // It returns a slice of that host's addresses. func LookupHost(host string) (addrs []string, err error) { @@ -147,11 +162,11 @@ func LookupHost(host string) (addrs []string, err error) { // It returns a slice of that host's addresses. func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) { // Make sure that no matter what we do later, host=="" is rejected. - // ParseIP, for example, does accept empty strings. + // parseIP, for example, does accept empty strings. if host == "" { return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host} } - if ip := ParseIP(host); ip != nil { + if ip, _ := parseIPZone(host); ip != nil { return []string{host}, nil } return r.lookupHost(ctx, host) @@ -175,12 +190,12 @@ func LookupIP(host string) ([]IP, error) { // It returns a slice of that host's IPv4 and IPv6 addresses. func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) { // Make sure that no matter what we do later, host=="" is rejected. - // ParseIP, for example, does accept empty strings. + // parseIP, for example, does accept empty strings. if host == "" { return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host} } - if ip := ParseIP(host); ip != nil { - return []IPAddr{{IP: ip}}, nil + if ip, zone := parseIPZone(host); ip != nil { + return []IPAddr{{IP: ip, Zone: zone}}, nil } trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace) if trace != nil && trace.DNSStart != nil { @@ -201,7 +216,7 @@ func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, err lookupGroupCtx, lookupGroupCancel := context.WithCancel(context.Background()) dnsWaitGroup.Add(1) - ch, called := lookupGroup.DoChan(host, func() (interface{}, error) { + ch, called := r.getLookupGroup().DoChan(host, func() (interface{}, error) { defer dnsWaitGroup.Done() return testHookLookupIP(lookupGroupCtx, resolverFunc, host) }) @@ -218,7 +233,7 @@ func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, err // let the lookup continue uncanceled, and let later // lookups with the same key share the result. // See issues 8602, 20703, 22724. - if lookupGroup.ForgetUnshared(host) { + if r.getLookupGroup().ForgetUnshared(host) { lookupGroupCancel() } else { go func() { @@ -241,12 +256,6 @@ func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, err } } -// lookupGroup merges LookupIPAddr calls together for lookups -// for the same host. The lookupGroup key is is the LookupIPAddr.host -// argument. -// The return values are ([]IPAddr, error). -var lookupGroup singleflight.Group - // lookupIPReturn turns the return values from singleflight.Do into // the return values from LookupIP. func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error) { diff --git a/libgo/go/net/lookup_nacl.go b/libgo/go/net/lookup_fake.go index 43cebad..d3d1dbc 100644 --- a/libgo/go/net/lookup_nacl.go +++ b/libgo/go/net/lookup_fake.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build nacl +// +build nacl js,wasm package net @@ -50,3 +50,9 @@ func (*Resolver) lookupTXT(ctx context.Context, name string) (txts []string, err func (*Resolver) lookupAddr(ctx context.Context, addr string) (ptrs []string, err error) { return nil, syscall.ENOPROTOOPT } + +// concurrentThreadsLimit returns the number of threads we permit to +// run concurrently doing DNS lookups. +func concurrentThreadsLimit() int { + return 500 +} diff --git a/libgo/go/net/lookup_plan9.go b/libgo/go/net/lookup_plan9.go index 1037b81..5547f0b 100644 --- a/libgo/go/net/lookup_plan9.go +++ b/libgo/go/net/lookup_plan9.go @@ -11,34 +11,58 @@ import ( "os" ) -func query(ctx context.Context, filename, query string, bufSize int) (res []string, err error) { - file, err := os.OpenFile(filename, os.O_RDWR, 0) - if err != nil { - return - } - defer file.Close() +func query(ctx context.Context, filename, query string, bufSize int) (addrs []string, err error) { + queryAddrs := func() (addrs []string, err error) { + file, err := os.OpenFile(filename, os.O_RDWR, 0) + if err != nil { + return nil, err + } + defer file.Close() - _, err = file.Seek(0, io.SeekStart) - if err != nil { - return - } - _, err = file.WriteString(query) - if err != nil { - return + _, err = file.Seek(0, io.SeekStart) + if err != nil { + return nil, err + } + _, err = file.WriteString(query) + if err != nil { + return nil, err + } + _, err = file.Seek(0, io.SeekStart) + if err != nil { + return nil, err + } + buf := make([]byte, bufSize) + for { + n, _ := file.Read(buf) + if n <= 0 { + break + } + addrs = append(addrs, string(buf[:n])) + } + return addrs, nil } - _, err = file.Seek(0, io.SeekStart) - if err != nil { - return + + type ret struct { + addrs []string + err error } - buf := make([]byte, bufSize) - for { - n, _ := file.Read(buf) - if n <= 0 { - break + + ch := make(chan ret, 1) + go func() { + addrs, err := queryAddrs() + ch <- ret{addrs: addrs, err: err} + }() + + select { + case r := <-ch: + return r.addrs, r.err + case <-ctx.Done(): + return nil, &DNSError{ + Name: query, + Err: ctx.Err().Error(), + IsTimeout: ctx.Err() == context.DeadlineExceeded, } - res = append(res, string(buf[:n])) } - return } func queryCS(ctx context.Context, net, host, service string) (res []string, err error) { @@ -305,3 +329,9 @@ func (*Resolver) lookupAddr(ctx context.Context, addr string) (name []string, er } return } + +// concurrentThreadsLimit returns the number of threads we permit to +// run concurrently doing DNS lookups. +func concurrentThreadsLimit() int { + return 500 +} diff --git a/libgo/go/net/lookup_test.go b/libgo/go/net/lookup_test.go index 24787cc..5c66dfa 100644 --- a/libgo/go/net/lookup_test.go +++ b/libgo/go/net/lookup_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( @@ -13,6 +15,7 @@ import ( "runtime" "sort" "strings" + "sync" "testing" "time" ) @@ -60,19 +63,34 @@ var lookupGoogleSRVTests = []struct { }, } +var backoffDuration = [...]time.Duration{time.Second, 5 * time.Second, 30 * time.Second} + func TestLookupGoogleSRV(t *testing.T) { - if testenv.Builder() == "" { - testenv.MustHaveExternalNetwork(t) + t.Parallel() + mustHaveExternalNetwork(t) + + if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { + t.Skip("no resolv.conf on iOS") } if !supportsIPv4() || !*testIPv4 { t.Skip("IPv4 is required") } - for _, tt := range lookupGoogleSRVTests { + attempts := 0 + for i := 0; i < len(lookupGoogleSRVTests); i++ { + tt := lookupGoogleSRVTests[i] cname, srvs, err := LookupSRV(tt.service, tt.proto, tt.name) if err != nil { testenv.SkipFlakyNet(t) + if attempts < len(backoffDuration) { + dur := backoffDuration[attempts] + t.Logf("backoff %v after failure %v\n", dur, err) + time.Sleep(dur) + attempts++ + i-- + continue + } t.Fatal(err) } if len(srvs) == 0 { @@ -97,19 +115,31 @@ var lookupGmailMXTests = []struct { } func TestLookupGmailMX(t *testing.T) { - if testenv.Builder() == "" { - testenv.MustHaveExternalNetwork(t) + t.Parallel() + mustHaveExternalNetwork(t) + + if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { + t.Skip("no resolv.conf on iOS") } if !supportsIPv4() || !*testIPv4 { t.Skip("IPv4 is required") } - defer dnsWaitGroup.Wait() - - for _, tt := range lookupGmailMXTests { + attempts := 0 + for i := 0; i < len(lookupGmailMXTests); i++ { + tt := lookupGmailMXTests[i] mxs, err := LookupMX(tt.name) if err != nil { + testenv.SkipFlakyNet(t) + if attempts < len(backoffDuration) { + dur := backoffDuration[attempts] + t.Logf("backoff %v after failure %v\n", dur, err) + time.Sleep(dur) + attempts++ + i-- + continue + } t.Fatal(err) } if len(mxs) == 0 { @@ -131,20 +161,31 @@ var lookupGmailNSTests = []struct { } func TestLookupGmailNS(t *testing.T) { - if testenv.Builder() == "" { - testenv.MustHaveExternalNetwork(t) + t.Parallel() + mustHaveExternalNetwork(t) + + if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { + t.Skip("no resolv.conf on iOS") } if !supportsIPv4() || !*testIPv4 { t.Skip("IPv4 is required") } - defer dnsWaitGroup.Wait() - - for _, tt := range lookupGmailNSTests { + attempts := 0 + for i := 0; i < len(lookupGmailNSTests); i++ { + tt := lookupGmailNSTests[i] nss, err := LookupNS(tt.name) if err != nil { testenv.SkipFlakyNet(t) + if attempts < len(backoffDuration) { + dur := backoffDuration[attempts] + t.Logf("backoff %v after failure %v\n", dur, err) + time.Sleep(dur) + attempts++ + i-- + continue + } t.Fatal(err) } if len(nss) == 0 { @@ -166,19 +207,31 @@ var lookupGmailTXTTests = []struct { } func TestLookupGmailTXT(t *testing.T) { - if testenv.Builder() == "" { - testenv.MustHaveExternalNetwork(t) + t.Parallel() + mustHaveExternalNetwork(t) + + if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { + t.Skip("no resolv.conf on iOS") } if !supportsIPv4() || !*testIPv4 { t.Skip("IPv4 is required") } - defer dnsWaitGroup.Wait() - - for _, tt := range lookupGmailTXTTests { + attempts := 0 + for i := 0; i < len(lookupGmailTXTTests); i++ { + tt := lookupGmailTXTTests[i] txts, err := LookupTXT(tt.name) if err != nil { + testenv.SkipFlakyNet(t) + if attempts < len(backoffDuration) { + dur := backoffDuration[attempts] + t.Logf("backoff %v after failure %v\n", dur, err) + time.Sleep(dur) + attempts++ + i-- + continue + } t.Fatal(err) } if len(txts) == 0 { @@ -203,9 +256,7 @@ var lookupGooglePublicDNSAddrTests = []struct { } func TestLookupGooglePublicDNSAddr(t *testing.T) { - if testenv.Builder() == "" { - testenv.MustHaveExternalNetwork(t) - } + mustHaveExternalNetwork(t) if !supportsIPv4() || !supportsIPv6() || !*testIPv4 || !*testIPv6 { t.Skip("both IPv4 and IPv6 are required") @@ -255,6 +306,32 @@ func TestLookupIPv6LinkLocalAddr(t *testing.T) { } } +func TestLookupIPv6LinkLocalAddrWithZone(t *testing.T) { + if !supportsIPv6() || !*testIPv6 { + t.Skip("IPv6 is required") + } + + ipaddrs, err := DefaultResolver.LookupIPAddr(context.Background(), "fe80::1%lo0") + if err != nil { + t.Error(err) + } + for _, addr := range ipaddrs { + if e, a := "lo0", addr.Zone; e != a { + t.Errorf("wrong zone: want %q, got %q", e, a) + } + } + + addrs, err := DefaultResolver.LookupHost(context.Background(), "fe80::1%lo0") + if err != nil { + t.Error(err) + } + for _, addr := range addrs { + if e, a := "fe80::1%lo0", addr; e != a { + t.Errorf("wrong host: want %q got %q", e, a) + } + } +} + var lookupCNAMETests = []struct { name, cname string }{ @@ -264,9 +341,7 @@ var lookupCNAMETests = []struct { } func TestLookupCNAME(t *testing.T) { - if testenv.Builder() == "" { - testenv.MustHaveExternalNetwork(t) - } + mustHaveExternalNetwork(t) if !supportsIPv4() || !*testIPv4 { t.Skip("IPv4 is required") @@ -274,9 +349,20 @@ func TestLookupCNAME(t *testing.T) { defer dnsWaitGroup.Wait() - for _, tt := range lookupCNAMETests { + attempts := 0 + for i := 0; i < len(lookupCNAMETests); i++ { + tt := lookupCNAMETests[i] cname, err := LookupCNAME(tt.name) if err != nil { + testenv.SkipFlakyNet(t) + if attempts < len(backoffDuration) { + dur := backoffDuration[attempts] + t.Logf("backoff %v after failure %v\n", dur, err) + time.Sleep(dur) + attempts++ + i-- + continue + } t.Fatal(err) } if !strings.HasSuffix(cname, tt.cname) { @@ -293,9 +379,7 @@ var lookupGoogleHostTests = []struct { } func TestLookupGoogleHost(t *testing.T) { - if testenv.Builder() == "" { - testenv.MustHaveExternalNetwork(t) - } + mustHaveExternalNetwork(t) if !supportsIPv4() || !*testIPv4 { t.Skip("IPv4 is required") @@ -320,12 +404,8 @@ func TestLookupGoogleHost(t *testing.T) { } func TestLookupLongTXT(t *testing.T) { - if runtime.GOOS == "plan9" { - t.Skip("skipping on plan9; see https://golang.org/issue/22857") - } - if testenv.Builder() == "" { - testenv.MustHaveExternalNetwork(t) - } + testenv.SkipFlaky(t, 22857) + mustHaveExternalNetwork(t) defer dnsWaitGroup.Wait() @@ -351,9 +431,7 @@ var lookupGoogleIPTests = []struct { } func TestLookupGoogleIP(t *testing.T) { - if testenv.Builder() == "" { - testenv.MustHaveExternalNetwork(t) - } + mustHaveExternalNetwork(t) if !supportsIPv4() || !*testIPv4 { t.Skip("IPv4 is required") @@ -499,9 +577,7 @@ func TestLookupDotsWithLocalSource(t *testing.T) { t.Skip("IPv4 is required") } - if testenv.Builder() == "" { - testenv.MustHaveExternalNetwork(t) - } + mustHaveExternalNetwork(t) defer dnsWaitGroup.Wait() @@ -542,14 +618,16 @@ func TestLookupDotsWithLocalSource(t *testing.T) { } func TestLookupDotsWithRemoteSource(t *testing.T) { - if testenv.Builder() == "" { - testenv.MustHaveExternalNetwork(t) - } + mustHaveExternalNetwork(t) if !supportsIPv4() || !*testIPv4 { t.Skip("IPv4 is required") } + if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { + t.Skip("no resolv.conf on iOS") + } + defer dnsWaitGroup.Wait() if fixup := forceGoDNS(); fixup != nil { @@ -664,10 +742,10 @@ func srvString(srvs []*SRV) string { } func TestLookupPort(t *testing.T) { - // See http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml + // See https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml // // Please be careful about adding new test cases. - // There are platforms having incomplete mappings for + // There are platforms which have incomplete mappings for // restricted resource access and security reasons. type test struct { network string @@ -793,9 +871,7 @@ func TestLookupNonLDH(t *testing.T) { } func TestLookupContextCancel(t *testing.T) { - if testenv.Builder() == "" { - testenv.MustHaveExternalNetwork(t) - } + mustHaveExternalNetwork(t) if runtime.GOOS == "nacl" { t.Skip("skip on nacl") } @@ -816,3 +892,119 @@ func TestLookupContextCancel(t *testing.T) { t.Fatal(err) } } + +// Issue 24330: treat the nil *Resolver like a zero value. Verify nothing +// crashes if nil is used. +func TestNilResolverLookup(t *testing.T) { + mustHaveExternalNetwork(t) + if runtime.GOOS == "nacl" { + t.Skip("skip on nacl") + } + var r *Resolver = nil + ctx := context.Background() + + // Don't care about the results, just that nothing panics: + r.LookupAddr(ctx, "8.8.8.8") + r.LookupCNAME(ctx, "google.com") + r.LookupHost(ctx, "google.com") + r.LookupIPAddr(ctx, "google.com") + r.LookupMX(ctx, "gmail.com") + r.LookupNS(ctx, "google.com") + r.LookupPort(ctx, "tcp", "smtp") + r.LookupSRV(ctx, "service", "proto", "name") + r.LookupTXT(ctx, "gmail.com") +} + +// TestLookupHostCancel verifies that lookup works even after many +// canceled lookups (see golang.org/issue/24178 for details). +func TestLookupHostCancel(t *testing.T) { + mustHaveExternalNetwork(t) + if runtime.GOOS == "nacl" { + t.Skip("skip on nacl") + } + + const ( + google = "www.google.com" + invalidDomain = "nonexistentdomain.golang.org" + n = 600 // this needs to be larger than threadLimit size + ) + + _, err := LookupHost(google) + if err != nil { + t.Fatal(err) + } + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + for i := 0; i < n; i++ { + addr, err := DefaultResolver.LookupHost(ctx, invalidDomain) + if err == nil { + t.Fatalf("LookupHost(%q): returns %v, but should fail", invalidDomain, addr) + } + if !strings.Contains(err.Error(), "canceled") { + t.Fatalf("LookupHost(%q): failed with unexpected error: %v", invalidDomain, err) + } + time.Sleep(time.Millisecond * 1) + } + + _, err = LookupHost(google) + if err != nil { + t.Fatal(err) + } +} + +type lookupCustomResolver struct { + *Resolver + mu sync.RWMutex + dialed bool +} + +func (lcr *lookupCustomResolver) dial() func(ctx context.Context, network, address string) (Conn, error) { + return func(ctx context.Context, network, address string) (Conn, error) { + lcr.mu.Lock() + lcr.dialed = true + lcr.mu.Unlock() + return Dial(network, address) + } +} + +// TestConcurrentPreferGoResolversDial tests that multiple resolvers with the +// PreferGo option used concurrently are all dialed properly. +func TestConcurrentPreferGoResolversDial(t *testing.T) { + // The windows implementation of the resolver does not use the Dial + // function. + if runtime.GOOS == "windows" { + t.Skip("skip on windows") + } + + testenv.MustHaveExternalNetwork(t) + testenv.SkipFlakyNet(t) + + defer dnsWaitGroup.Wait() + + resolvers := make([]*lookupCustomResolver, 2) + for i := range resolvers { + cs := lookupCustomResolver{Resolver: &Resolver{PreferGo: true}} + cs.Dial = cs.dial() + resolvers[i] = &cs + } + + var wg sync.WaitGroup + wg.Add(len(resolvers)) + for i, resolver := range resolvers { + go func(r *Resolver, index int) { + defer wg.Done() + _, err := r.LookupIPAddr(context.Background(), "google.com") + if err != nil { + t.Fatalf("lookup failed for resolver %d: %q", index, err) + } + }(resolver.Resolver, i) + } + wg.Wait() + + for i, resolver := range resolvers { + if !resolver.dialed { + t.Errorf("custom resolver %d not dialed during lookup", i) + } + } +} diff --git a/libgo/go/net/lookup_unix.go b/libgo/go/net/lookup_unix.go index 2813f14..76d6ae3 100644 --- a/libgo/go/net/lookup_unix.go +++ b/libgo/go/net/lookup_unix.go @@ -9,6 +9,9 @@ package net import ( "context" "sync" + "syscall" + + "golang_org/x/net/dns/dnsmessage" ) var onceReadProtocols sync.Once @@ -51,7 +54,7 @@ func lookupProtocol(_ context.Context, name string) (int, error) { return lookupProtocolMap(name) } -func (r *Resolver) dial(ctx context.Context, network, server string) (dnsConn, error) { +func (r *Resolver) dial(ctx context.Context, network, server string) (Conn, error) { // Calling Dial here is scary -- we have to be sure not to // dial a name that will require a DNS lookup, or Dial will // call back here to translate it. The DNS config parser has @@ -59,7 +62,7 @@ func (r *Resolver) dial(ctx context.Context, network, server string) (dnsConn, e // addresses, which Dial will use without a DNS lookup. var c Conn var err error - if r.Dial != nil { + if r != nil && r.Dial != nil { c, err = r.Dial(ctx, network, server) } else { var d Dialer @@ -68,15 +71,12 @@ func (r *Resolver) dial(ctx context.Context, network, server string) (dnsConn, e if err != nil { return nil, mapErr(err) } - if _, ok := c.(PacketConn); ok { - return &dnsPacketConn{c}, nil - } - return &dnsStreamConn{c}, nil + return c, nil } func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) { - order := systemConf().hostLookupOrder(host) - if !r.PreferGo && order == hostLookupCgo { + order := systemConf().hostLookupOrder(r, host) + if !r.preferGo() && order == hostLookupCgo { if addrs, err, ok := cgoLookupHost(ctx, host); ok { return addrs, err } @@ -87,10 +87,10 @@ func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string, } func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) { - if r.PreferGo { + if r.preferGo() { return r.goLookupIP(ctx, host) } - order := systemConf().hostLookupOrder(host) + order := systemConf().hostLookupOrder(r, host) if order == hostLookupCgo { if addrs, err, ok := cgoLookupIP(ctx, host); ok { return addrs, err @@ -98,12 +98,12 @@ func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, e // cgo not available (or netgo); fall back to Go's DNS resolver order = hostLookupFilesDNS } - addrs, _, err = r.goLookupIPCNAMEOrder(ctx, host, order) - return + ips, _, err := r.goLookupIPCNAMEOrder(ctx, host, order) + return ips, err } func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) { - if !r.PreferGo && systemConf().canUseCgo() { + if !r.preferGo() && systemConf().canUseCgo() { if port, err, ok := cgoLookupPort(ctx, network, service); ok { if err != nil { // Issue 18213: if cgo fails, first check to see whether we @@ -119,7 +119,7 @@ func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int } func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) { - if !r.PreferGo && systemConf().canUseCgo() { + if !r.preferGo() && systemConf().canUseCgo() { if cname, err, ok := cgoLookupCNAME(ctx, name); ok { return cname, err } @@ -134,62 +134,209 @@ func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) ( } else { target = "_" + service + "._" + proto + "." + name } - cname, rrs, err := r.lookup(ctx, target, dnsTypeSRV) + p, server, err := r.lookup(ctx, target, dnsmessage.TypeSRV) if err != nil { return "", nil, err } - srvs := make([]*SRV, len(rrs)) - for i, rr := range rrs { - rr := rr.(*dnsRR_SRV) - srvs[i] = &SRV{Target: rr.Target, Port: rr.Port, Priority: rr.Priority, Weight: rr.Weight} + var srvs []*SRV + var cname dnsmessage.Name + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return "", nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeSRV { + if err := p.SkipAnswer(); err != nil { + return "", nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + if cname.Length == 0 && h.Name.Length != 0 { + cname = h.Name + } + srv, err := p.SRVResource() + if err != nil { + return "", nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + srvs = append(srvs, &SRV{Target: srv.Target.String(), Port: srv.Port, Priority: srv.Priority, Weight: srv.Weight}) } byPriorityWeight(srvs).sort() - return cname, srvs, nil + return cname.String(), srvs, nil } func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) { - _, rrs, err := r.lookup(ctx, name, dnsTypeMX) + p, server, err := r.lookup(ctx, name, dnsmessage.TypeMX) if err != nil { return nil, err } - mxs := make([]*MX, len(rrs)) - for i, rr := range rrs { - rr := rr.(*dnsRR_MX) - mxs[i] = &MX{Host: rr.Mx, Pref: rr.Pref} + var mxs []*MX + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeMX { + if err := p.SkipAnswer(); err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + mx, err := p.MXResource() + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + mxs = append(mxs, &MX{Host: mx.MX.String(), Pref: mx.Pref}) + } byPref(mxs).sort() return mxs, nil } func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) { - _, rrs, err := r.lookup(ctx, name, dnsTypeNS) + p, server, err := r.lookup(ctx, name, dnsmessage.TypeNS) if err != nil { return nil, err } - nss := make([]*NS, len(rrs)) - for i, rr := range rrs { - nss[i] = &NS{Host: rr.(*dnsRR_NS).Ns} + var nss []*NS + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeNS { + if err := p.SkipAnswer(); err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + ns, err := p.NSResource() + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + nss = append(nss, &NS{Host: ns.NS.String()}) } return nss, nil } func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) { - _, rrs, err := r.lookup(ctx, name, dnsTypeTXT) + p, server, err := r.lookup(ctx, name, dnsmessage.TypeTXT) if err != nil { return nil, err } - txts := make([]string, len(rrs)) - for i, rr := range rrs { - txts[i] = rr.(*dnsRR_TXT).Txt + var txts []string + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeTXT { + if err := p.SkipAnswer(); err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + txt, err := p.TXTResource() + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if len(txts) == 0 { + txts = txt.TXT + } else { + txts = append(txts, txt.TXT...) + } } return txts, nil } func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) { - if !r.PreferGo && systemConf().canUseCgo() { + if !r.preferGo() && systemConf().canUseCgo() { if ptrs, err, ok := cgoLookupPTR(ctx, addr); ok { return ptrs, err } } return r.goLookupPTR(ctx, addr) } + +// concurrentThreadsLimit returns the number of threads we permit to +// run concurrently doing DNS lookups via cgo. A DNS lookup may use a +// file descriptor so we limit this to less than the number of +// permitted open files. On some systems, notably Darwin, if +// getaddrinfo is unable to open a file descriptor it simply returns +// EAI_NONAME rather than a useful error. Limiting the number of +// concurrent getaddrinfo calls to less than the permitted number of +// file descriptors makes that error less likely. We don't bother to +// apply the same limit to DNS lookups run directly from Go, because +// there we will return a meaningful "too many open files" error. +func concurrentThreadsLimit() int { + var rlim syscall.Rlimit + if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlim); err != nil { + return 500 + } + r := int(rlim.Cur) + if r > 500 { + r = 500 + } else if r > 30 { + r -= 30 + } + return r +} diff --git a/libgo/go/net/lookup_windows.go b/libgo/go/net/lookup_windows.go index ac1f9b4..f76e0af 100644 --- a/libgo/go/net/lookup_windows.go +++ b/libgo/go/net/lookup_windows.go @@ -79,12 +79,7 @@ func (r *Resolver) lookupHost(ctx context.Context, name string) ([]string, error func (r *Resolver) lookupIP(ctx context.Context, name string) ([]IPAddr, error) { // TODO(bradfitz,brainman): use ctx more. See TODO below. - type ret struct { - addrs []IPAddr - err error - } - ch := make(chan ret, 1) - go func() { + getaddr := func() ([]IPAddr, error) { acquireThread() defer releaseThread() hints := syscall.AddrinfoW{ @@ -95,7 +90,7 @@ func (r *Resolver) lookupIP(ctx context.Context, name string) ([]IPAddr, error) var result *syscall.AddrinfoW e := syscall.GetAddrInfoW(syscall.StringToUTF16Ptr(name), nil, &hints, &result) if e != nil { - ch <- ret{err: &DNSError{Err: winError("getaddrinfow", e).Error(), Name: name}} + return nil, &DNSError{Err: winError("getaddrinfow", e).Error(), Name: name} } defer syscall.FreeAddrInfoW(result) addrs := make([]IPAddr, 0, 5) @@ -110,11 +105,23 @@ func (r *Resolver) lookupIP(ctx context.Context, name string) ([]IPAddr, error) zone := zoneCache.name(int((*syscall.RawSockaddrInet6)(addr).Scope_id)) addrs = append(addrs, IPAddr{IP: IP{a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]}, Zone: zone}) default: - ch <- ret{err: &DNSError{Err: syscall.EWINDOWS.Error(), Name: name}} + return nil, &DNSError{Err: syscall.EWINDOWS.Error(), Name: name} } } - ch <- ret{addrs: addrs} + return addrs, nil + } + + type ret struct { + addrs []IPAddr + err error + } + + ch := make(chan ret, 1) + go func() { + addr, err := getaddr() + ch <- ret{addrs: addr, err: err} }() + select { case r := <-ch: return r.addrs, r.err @@ -136,7 +143,7 @@ func (r *Resolver) lookupIP(ctx context.Context, name string) ([]IPAddr, error) } func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) { - if r.PreferGo { + if r.preferGo() { return lookupPortMap(network, service) } @@ -357,3 +364,9 @@ Cname: } return name } + +// concurrentThreadsLimit returns the number of threads we permit to +// run concurrently doing DNS lookups. +func concurrentThreadsLimit() int { + return 500 +} diff --git a/libgo/go/net/mail/message.go b/libgo/go/net/mail/message.go index 4f3184f..5912b90 100644 --- a/libgo/go/net/mail/message.go +++ b/libgo/go/net/mail/message.go @@ -19,7 +19,6 @@ package mail import ( "bufio" - "bytes" "errors" "fmt" "io" @@ -735,7 +734,7 @@ func isQtext(r rune) bool { // quoteString renders a string as an RFC 5322 quoted-string. func quoteString(s string) string { - var buf bytes.Buffer + var buf strings.Builder buf.WriteByte('"') for _, r := range s { if isQtext(r) || isWSP(r) { diff --git a/libgo/go/net/main_cloexec_test.go b/libgo/go/net/main_cloexec_test.go index fa1ed02..5398f9e 100644 --- a/libgo/go/net/main_cloexec_test.go +++ b/libgo/go/net/main_cloexec_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build dragonfly freebsd linux +// +build dragonfly freebsd linux netbsd openbsd package net diff --git a/libgo/go/net/main_conf_test.go b/libgo/go/net/main_conf_test.go index 9875cea..b535046 100644 --- a/libgo/go/net/main_conf_test.go +++ b/libgo/go/net/main_conf_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !nacl,!plan9,!windows +// +build !js,!nacl,!plan9,!windows package net diff --git a/libgo/go/net/main_noconf_test.go b/libgo/go/net/main_noconf_test.go index 489477b..55e3770 100644 --- a/libgo/go/net/main_noconf_test.go +++ b/libgo/go/net/main_noconf_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build nacl plan9 windows +// +build js,wasm nacl plan9 windows package net diff --git a/libgo/go/net/main_posix_test.go b/libgo/go/net/main_posix_test.go index ead311c..f2484f3 100644 --- a/libgo/go/net/main_posix_test.go +++ b/libgo/go/net/main_posix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !plan9 +// +build !js,!plan9 package net diff --git a/libgo/go/net/main_test.go b/libgo/go/net/main_test.go index 3e7a85a..85a269d 100644 --- a/libgo/go/net/main_test.go +++ b/libgo/go/net/main_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( diff --git a/libgo/go/net/mockserver_test.go b/libgo/go/net/mockserver_test.go index 44581d9..5302935 100644 --- a/libgo/go/net/mockserver_test.go +++ b/libgo/go/net/mockserver_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( diff --git a/libgo/go/net/net.go b/libgo/go/net/net.go index 3ad9103..c909986 100644 --- a/libgo/go/net/net.go +++ b/libgo/go/net/net.go @@ -84,6 +84,7 @@ import ( "internal/poll" "io" "os" + "sync" "syscall" "time" ) @@ -229,7 +230,7 @@ func (c *conn) SetDeadline(t time.Time) error { if !c.ok() { return syscall.EINVAL } - if err := c.fd.pfd.SetDeadline(t); err != nil { + if err := c.fd.SetDeadline(t); err != nil { return &OpError{Op: "set", Net: c.fd.net, Source: nil, Addr: c.fd.laddr, Err: err} } return nil @@ -240,7 +241,7 @@ func (c *conn) SetReadDeadline(t time.Time) error { if !c.ok() { return syscall.EINVAL } - if err := c.fd.pfd.SetReadDeadline(t); err != nil { + if err := c.fd.SetReadDeadline(t); err != nil { return &OpError{Op: "set", Net: c.fd.net, Source: nil, Addr: c.fd.laddr, Err: err} } return nil @@ -251,7 +252,7 @@ func (c *conn) SetWriteDeadline(t time.Time) error { if !c.ok() { return syscall.EINVAL } - if err := c.fd.pfd.SetWriteDeadline(t); err != nil { + if err := c.fd.SetWriteDeadline(t); err != nil { return &OpError{Op: "set", Net: c.fd.net, Source: nil, Addr: c.fd.laddr, Err: err} } return nil @@ -281,15 +282,13 @@ func (c *conn) SetWriteBuffer(bytes int) error { return nil } -// File sets the underlying os.File to blocking mode and returns a copy. +// File returns a copy of the underlying os.File // It is the caller's responsibility to close f when finished. // Closing c does not affect f, and closing f does not affect c. // // The returned os.File's file descriptor is different from the connection's. // Attempting to change properties of the original using this duplicate // may or may not have the desired effect. -// -// On Unix systems this will cause the SetDeadline methods to stop working. func (c *conn) File() (f *os.File, err error) { f, err = c.fd.dup() if err != nil { @@ -303,20 +302,23 @@ func (c *conn) File() (f *os.File, err error) { // Multiple goroutines may invoke methods on a PacketConn simultaneously. type PacketConn interface { // ReadFrom reads a packet from the connection, - // copying the payload into b. It returns the number of - // bytes copied into b and the return address that + // copying the payload into p. It returns the number of + // bytes copied into p and the return address that // was on the packet. + // It returns the number of bytes read (0 <= n <= len(p)) + // and any error encountered. Callers should always process + // the n > 0 bytes returned before considering the error err. // ReadFrom can be made to time out and return // an Error with Timeout() == true after a fixed time limit; // see SetDeadline and SetReadDeadline. - ReadFrom(b []byte) (n int, addr Addr, err error) + ReadFrom(p []byte) (n int, addr Addr, err error) - // WriteTo writes a packet with payload b to addr. + // WriteTo writes a packet with payload p to addr. // WriteTo can be made to time out and return // an Error with Timeout() == true after a fixed time limit; // see SetDeadline and SetWriteDeadline. // On packet-oriented connections, write timeouts are rare. - WriteTo(b []byte, addr Addr) (n int, err error) + WriteTo(p []byte, addr Addr) (n int, err error) // Close closes the connection. // Any blocked ReadFrom or WriteTo operations will be unblocked and return errors. @@ -489,6 +491,12 @@ type temporary interface { } func (e *OpError) Temporary() bool { + // Treat ECONNRESET and ECONNABORTED as temporary errors when + // they come from calling accept. See issue 6163. + if e.Op == "accept" && isConnError(e.Err) { + return true + } + if ne, ok := e.Err.(*os.SyscallError); ok { t, ok := ne.Err.(temporary) return ok && t.Temporary() @@ -603,9 +611,14 @@ func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) { // server is not responding. Then the many lookups each use a different // thread, and the system or the program runs out of threads. -var threadLimit = make(chan struct{}, 500) +var threadLimit chan struct{} + +var threadOnce sync.Once func acquireThread() { + threadOnce.Do(func() { + threadLimit = make(chan struct{}, concurrentThreadsLimit()) + }) threadLimit <- struct{}{} } diff --git a/libgo/go/net/net_fake.go b/libgo/go/net/net_fake.go new file mode 100644 index 0000000..0c48dd5 --- /dev/null +++ b/libgo/go/net/net_fake.go @@ -0,0 +1,284 @@ +// Copyright 2018 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. + +// Fake networking for js/wasm. It is intended to allow tests of other package to pass. + +// +build js,wasm + +package net + +import ( + "context" + "internal/poll" + "io" + "os" + "sync" + "syscall" + "time" +) + +var listenersMu sync.Mutex +var listeners = make(map[string]*netFD) + +var portCounterMu sync.Mutex +var portCounter = 0 + +func nextPort() int { + portCounterMu.Lock() + defer portCounterMu.Unlock() + portCounter++ + return portCounter +} + +// Network file descriptor. +type netFD struct { + r *bufferedPipe + w *bufferedPipe + incoming chan *netFD + + closedMu sync.Mutex + closed bool + + // immutable until Close + listener bool + family int + sotype int + net string + laddr Addr + raddr Addr + + // unused + pfd poll.FD + isConnected bool // handshake completed or use of association with peer +} + +// socket returns a network file descriptor that is ready for +// asynchronous I/O using the network poller. +func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr, ctrlFn func(string, string, syscall.RawConn) error) (*netFD, error) { + fd := &netFD{family: family, sotype: sotype, net: net} + + if laddr != nil && raddr == nil { // listener + l := laddr.(*TCPAddr) + fd.laddr = &TCPAddr{ + IP: l.IP, + Port: nextPort(), + Zone: l.Zone, + } + fd.listener = true + fd.incoming = make(chan *netFD, 1024) + listenersMu.Lock() + listeners[fd.laddr.(*TCPAddr).String()] = fd + listenersMu.Unlock() + return fd, nil + } + + fd.laddr = &TCPAddr{ + IP: IPv4(127, 0, 0, 1), + Port: nextPort(), + } + fd.raddr = raddr + fd.r = newBufferedPipe(65536) + fd.w = newBufferedPipe(65536) + + fd2 := &netFD{family: fd.family, sotype: sotype, net: net} + fd2.laddr = fd.raddr + fd2.raddr = fd.laddr + fd2.r = fd.w + fd2.w = fd.r + listenersMu.Lock() + l, ok := listeners[fd.raddr.(*TCPAddr).String()] + if !ok { + listenersMu.Unlock() + return nil, syscall.ECONNREFUSED + } + l.incoming <- fd2 + listenersMu.Unlock() + + return fd, nil +} + +func (fd *netFD) Read(p []byte) (n int, err error) { + return fd.r.Read(p) +} + +func (fd *netFD) Write(p []byte) (nn int, err error) { + return fd.w.Write(p) +} + +func (fd *netFD) Close() error { + fd.closedMu.Lock() + if fd.closed { + fd.closedMu.Unlock() + return nil + } + fd.closed = true + fd.closedMu.Unlock() + + if fd.listener { + listenersMu.Lock() + delete(listeners, fd.laddr.String()) + close(fd.incoming) + fd.listener = false + listenersMu.Unlock() + return nil + } + + fd.r.Close() + fd.w.Close() + return nil +} + +func (fd *netFD) closeRead() error { + fd.r.Close() + return nil +} + +func (fd *netFD) closeWrite() error { + fd.w.Close() + return nil +} + +func (fd *netFD) accept() (*netFD, error) { + c, ok := <-fd.incoming + if !ok { + return nil, syscall.EINVAL + } + return c, nil +} + +func (fd *netFD) SetDeadline(t time.Time) error { + fd.r.SetReadDeadline(t) + fd.w.SetWriteDeadline(t) + return nil +} + +func (fd *netFD) SetReadDeadline(t time.Time) error { + fd.r.SetReadDeadline(t) + return nil +} + +func (fd *netFD) SetWriteDeadline(t time.Time) error { + fd.w.SetWriteDeadline(t) + return nil +} + +func newBufferedPipe(softLimit int) *bufferedPipe { + p := &bufferedPipe{softLimit: softLimit} + p.rCond.L = &p.mu + p.wCond.L = &p.mu + return p +} + +type bufferedPipe struct { + softLimit int + mu sync.Mutex + buf []byte + closed bool + rCond sync.Cond + wCond sync.Cond + rDeadline time.Time + wDeadline time.Time +} + +func (p *bufferedPipe) Read(b []byte) (int, error) { + p.mu.Lock() + defer p.mu.Unlock() + + for { + if p.closed && len(p.buf) == 0 { + return 0, io.EOF + } + if !p.rDeadline.IsZero() { + d := time.Until(p.rDeadline) + if d <= 0 { + return 0, syscall.EAGAIN + } + time.AfterFunc(d, p.rCond.Broadcast) + } + if len(p.buf) > 0 { + break + } + p.rCond.Wait() + } + + n := copy(b, p.buf) + p.buf = p.buf[n:] + p.wCond.Broadcast() + return n, nil +} + +func (p *bufferedPipe) Write(b []byte) (int, error) { + p.mu.Lock() + defer p.mu.Unlock() + + for { + if p.closed { + return 0, syscall.ENOTCONN + } + if !p.wDeadline.IsZero() { + d := time.Until(p.wDeadline) + if d <= 0 { + return 0, syscall.EAGAIN + } + time.AfterFunc(d, p.wCond.Broadcast) + } + if len(p.buf) <= p.softLimit { + break + } + p.wCond.Wait() + } + + p.buf = append(p.buf, b...) + p.rCond.Broadcast() + return len(b), nil +} + +func (p *bufferedPipe) Close() { + p.mu.Lock() + defer p.mu.Unlock() + + p.closed = true + p.rCond.Broadcast() + p.wCond.Broadcast() +} + +func (p *bufferedPipe) SetReadDeadline(t time.Time) { + p.mu.Lock() + defer p.mu.Unlock() + + p.rDeadline = t + p.rCond.Broadcast() +} + +func (p *bufferedPipe) SetWriteDeadline(t time.Time) { + p.mu.Lock() + defer p.mu.Unlock() + + p.wDeadline = t + p.wCond.Broadcast() +} + +func sysSocket(family, sotype, proto int) (int, error) { + return 0, syscall.ENOSYS +} + +func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { + return 0, nil, syscall.ENOSYS +} + +func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) { + return 0, 0, 0, nil, syscall.ENOSYS +} + +func (fd *netFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) { + return 0, syscall.ENOSYS +} + +func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) { + return 0, 0, syscall.ENOSYS +} + +func (fd *netFD) dup() (f *os.File, err error) { + return nil, syscall.ENOSYS +} diff --git a/libgo/go/net/net_test.go b/libgo/go/net/net_test.go index 024505e..692f269 100644 --- a/libgo/go/net/net_test.go +++ b/libgo/go/net/net_test.go @@ -2,11 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( "errors" "fmt" + "internal/testenv" "io" "net/internal/socktest" "os" @@ -516,3 +519,33 @@ func TestCloseUnblocksRead(t *testing.T) { } withTCPConnPair(t, client, server) } + +// Issue 24808: verify that ECONNRESET is not temporary for read. +func TestNotTemporaryRead(t *testing.T) { + if runtime.GOOS == "freebsd" { + testenv.SkipFlaky(t, 25289) + } + t.Parallel() + server := func(cs *TCPConn) error { + cs.SetLinger(0) + // Give the client time to get stuck in a Read. + time.Sleep(20 * time.Millisecond) + cs.Close() + return nil + } + client := func(ss *TCPConn) error { + _, err := ss.Read([]byte{0}) + if err == nil { + return errors.New("Read succeeded unexpectedly") + } else if err == io.EOF { + // This happens on NaCl and Plan 9. + return nil + } else if ne, ok := err.(Error); !ok { + return fmt.Errorf("unexpected error %v", err) + } else if ne.Temporary() { + return fmt.Errorf("unexpected temporary error %v", err) + } + return nil + } + withTCPConnPair(t, client, server) +} diff --git a/libgo/go/net/packetconn_test.go b/libgo/go/net/packetconn_test.go index 7d50489..a377d33 100644 --- a/libgo/go/net/packetconn_test.go +++ b/libgo/go/net/packetconn_test.go @@ -5,6 +5,8 @@ // This file implements API tests across platforms and will never have a build // tag. +// +build !js + package net import ( diff --git a/libgo/go/net/port_unix.go b/libgo/go/net/port_unix.go index 8dd1c32..ea3bb02 100644 --- a/libgo/go/net/port_unix.go +++ b/libgo/go/net/port_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris nacl +// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris nacl // Read system port mappings from /etc/services diff --git a/libgo/go/net/protoconn_test.go b/libgo/go/net/protoconn_test.go index def8d65..9f6772c 100644 --- a/libgo/go/net/protoconn_test.go +++ b/libgo/go/net/protoconn_test.go @@ -5,10 +5,11 @@ // This file implements API tests across platforms and will never have a build // tag. +// +build !js + package net import ( - "internal/testenv" "os" "runtime" "testing" @@ -139,15 +140,11 @@ func TestUDPConnSpecificMethods(t *testing.T) { if _, _, err := c.ReadFromUDP(rb); err != nil { t.Fatal(err) } - if testenv.IsWindowsXP() { - t.Log("skipping broken test on Windows XP (see golang.org/issue/23072)") - } else { - if _, _, err := c.WriteMsgUDP(wb, nil, c.LocalAddr().(*UDPAddr)); err != nil { - condFatalf(t, c.LocalAddr().Network(), "%v", err) - } - if _, _, _, _, err := c.ReadMsgUDP(rb, nil); err != nil { - condFatalf(t, c.LocalAddr().Network(), "%v", err) - } + if _, _, err := c.WriteMsgUDP(wb, nil, c.LocalAddr().(*UDPAddr)); err != nil { + condFatalf(t, c.LocalAddr().Network(), "%v", err) + } + if _, _, _, _, err := c.ReadMsgUDP(rb, nil); err != nil { + condFatalf(t, c.LocalAddr().Network(), "%v", err) } if f, err := c.File(); err != nil { diff --git a/libgo/go/net/rawconn.go b/libgo/go/net/rawconn.go index 2399c9f..c40ea4a 100644 --- a/libgo/go/net/rawconn.go +++ b/libgo/go/net/rawconn.go @@ -9,11 +9,14 @@ import ( "syscall" ) -// BUG(mikio): On Windows, the Read and Write methods of -// syscall.RawConn are not implemented. +// BUG(tmm1): On Windows, the Write method of syscall.RawConn +// does not integrate with the runtime's network poller. It cannot +// wait for the connection to become writeable, and does not respect +// deadlines. If the user-provided callback returns false, the Write +// method will fail immediately. -// BUG(mikio): On NaCl and Plan 9, the Control, Read and Write methods -// of syscall.RawConn are not implemented. +// BUG(mikio): On JS, NaCl and Plan 9, the Control, Read and Write +// methods of syscall.RawConn are not implemented. type rawConn struct { fd *netFD diff --git a/libgo/go/net/rawconn_stub_test.go b/libgo/go/net/rawconn_stub_test.go new file mode 100644 index 0000000..0a033c1 --- /dev/null +++ b/libgo/go/net/rawconn_stub_test.go @@ -0,0 +1,28 @@ +// Copyright 2018 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 js,wasm nacl plan9 + +package net + +import ( + "errors" + "syscall" +) + +func readRawConn(c syscall.RawConn, b []byte) (int, error) { + return 0, errors.New("not supported") +} + +func writeRawConn(c syscall.RawConn, b []byte) error { + return errors.New("not supported") +} + +func controlRawConn(c syscall.RawConn, addr Addr) error { + return errors.New("not supported") +} + +func controlOnConnSetup(network string, address string, c syscall.RawConn) error { + return nil +} diff --git a/libgo/go/net/rawconn_test.go b/libgo/go/net/rawconn_test.go new file mode 100644 index 0000000..11900df --- /dev/null +++ b/libgo/go/net/rawconn_test.go @@ -0,0 +1,220 @@ +// Copyright 2018 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 !js + +package net + +import ( + "bytes" + "runtime" + "testing" + "time" +) + +func TestRawConnReadWrite(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9": + t.Skipf("not supported on %s", runtime.GOOS) + } + + t.Run("TCP", func(t *testing.T) { + handler := func(ls *localServer, ln Listener) { + c, err := ln.Accept() + if err != nil { + t.Error(err) + return + } + defer c.Close() + + cc, err := ln.(*TCPListener).SyscallConn() + if err != nil { + t.Fatal(err) + } + called := false + op := func(uintptr) bool { + called = true + return true + } + err = cc.Write(op) + if err == nil { + t.Error("Write should return an error") + } + if called { + t.Error("Write shouldn't call op") + } + called = false + err = cc.Read(op) + if err == nil { + t.Error("Read should return an error") + } + if called { + t.Error("Read shouldn't call op") + } + + var b [32]byte + n, err := c.Read(b[:]) + if err != nil { + t.Error(err) + return + } + if _, err := c.Write(b[:n]); err != nil { + t.Error(err) + return + } + } + ls, err := newLocalServer("tcp") + if err != nil { + t.Fatal(err) + } + defer ls.teardown() + if err := ls.buildup(handler); err != nil { + t.Fatal(err) + } + + c, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + cc, err := c.(*TCPConn).SyscallConn() + if err != nil { + t.Fatal(err) + } + data := []byte("HELLO-R-U-THERE") + if err := writeRawConn(cc, data); err != nil { + t.Fatal(err) + } + var b [32]byte + n, err := readRawConn(cc, b[:]) + if err != nil { + t.Fatal(err) + } + if bytes.Compare(b[:n], data) != 0 { + t.Fatalf("got %q; want %q", b[:n], data) + } + }) + t.Run("Deadline", func(t *testing.T) { + switch runtime.GOOS { + case "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + + ln, err := newLocalListener("tcp") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + c, err := Dial(ln.Addr().Network(), ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + cc, err := c.(*TCPConn).SyscallConn() + if err != nil { + t.Fatal(err) + } + var b [1]byte + + c.SetDeadline(noDeadline) + if err := c.SetDeadline(time.Now().Add(-1)); err != nil { + t.Fatal(err) + } + if err = writeRawConn(cc, b[:]); err == nil { + t.Fatal("Write should fail") + } + if perr := parseWriteError(err); perr != nil { + t.Error(perr) + } + if nerr, ok := err.(Error); !ok || !nerr.Timeout() { + t.Errorf("got %v; want timeout", err) + } + if _, err = readRawConn(cc, b[:]); err == nil { + t.Fatal("Read should fail") + } + if perr := parseReadError(err); perr != nil { + t.Error(perr) + } + if nerr, ok := err.(Error); !ok || !nerr.Timeout() { + t.Errorf("got %v; want timeout", err) + } + + c.SetReadDeadline(noDeadline) + if err := c.SetReadDeadline(time.Now().Add(-1)); err != nil { + t.Fatal(err) + } + if _, err = readRawConn(cc, b[:]); err == nil { + t.Fatal("Read should fail") + } + if perr := parseReadError(err); perr != nil { + t.Error(perr) + } + if nerr, ok := err.(Error); !ok || !nerr.Timeout() { + t.Errorf("got %v; want timeout", err) + } + + c.SetWriteDeadline(noDeadline) + if err := c.SetWriteDeadline(time.Now().Add(-1)); err != nil { + t.Fatal(err) + } + if err = writeRawConn(cc, b[:]); err == nil { + t.Fatal("Write should fail") + } + if perr := parseWriteError(err); perr != nil { + t.Error(perr) + } + if nerr, ok := err.(Error); !ok || !nerr.Timeout() { + t.Errorf("got %v; want timeout", err) + } + }) +} + +func TestRawConnControl(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9": + t.Skipf("not supported on %s", runtime.GOOS) + } + + t.Run("TCP", func(t *testing.T) { + ln, err := newLocalListener("tcp") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + cc1, err := ln.(*TCPListener).SyscallConn() + if err != nil { + t.Fatal(err) + } + if err := controlRawConn(cc1, ln.Addr()); err != nil { + t.Fatal(err) + } + + c, err := Dial(ln.Addr().Network(), ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + cc2, err := c.(*TCPConn).SyscallConn() + if err != nil { + t.Fatal(err) + } + if err := controlRawConn(cc2, c.LocalAddr()); err != nil { + t.Fatal(err) + } + + ln.Close() + if err := controlRawConn(cc1, ln.Addr()); err == nil { + t.Fatal("Control after Close should fail") + } + c.Close() + if err := controlRawConn(cc2, c.LocalAddr()); err == nil { + t.Fatal("Control after Close should fail") + } + }) +} diff --git a/libgo/go/net/rawconn_unix_test.go b/libgo/go/net/rawconn_unix_test.go index 913ad86..a720a8a 100644 --- a/libgo/go/net/rawconn_unix_test.go +++ b/libgo/go/net/rawconn_unix_test.go @@ -7,138 +7,121 @@ package net import ( - "bytes" + "errors" "syscall" - "testing" ) -func TestRawConn(t *testing.T) { - handler := func(ls *localServer, ln Listener) { - c, err := ln.Accept() - if err != nil { - t.Error(err) - return - } - defer c.Close() - var b [32]byte - n, err := c.Read(b[:]) - if err != nil { - t.Error(err) - return - } - if _, err := c.Write(b[:n]); err != nil { - t.Error(err) - return - } - } - ls, err := newLocalServer("tcp") - if err != nil { - t.Fatal(err) - } - defer ls.teardown() - if err := ls.buildup(handler); err != nil { - t.Fatal(err) - } - - c, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String()) - if err != nil { - t.Fatal(err) - } - defer c.Close() - cc, err := c.(*TCPConn).SyscallConn() - if err != nil { - t.Fatal(err) - } - +func readRawConn(c syscall.RawConn, b []byte) (int, error) { var operr error - data := []byte("HELLO-R-U-THERE") - err = cc.Write(func(s uintptr) bool { - _, operr = syscall.Write(int(s), data) + var n int + err := c.Read(func(s uintptr) bool { + n, operr = syscall.Read(int(s), b) if operr == syscall.EAGAIN { return false } return true }) - if err != nil || operr != nil { - t.Fatal(err, operr) + if err != nil { + return n, err + } + if operr != nil { + return n, operr } + return n, nil +} - var nr int - var b [32]byte - err = cc.Read(func(s uintptr) bool { - nr, operr = syscall.Read(int(s), b[:]) +func writeRawConn(c syscall.RawConn, b []byte) error { + var operr error + err := c.Write(func(s uintptr) bool { + _, operr = syscall.Write(int(s), b) if operr == syscall.EAGAIN { return false } return true }) - if err != nil || operr != nil { - t.Fatal(err, operr) + if err != nil { + return err } - if bytes.Compare(b[:nr], data) != 0 { - t.Fatalf("got %#v; want %#v", b[:nr], data) + if operr != nil { + return operr } + return nil +} +func controlRawConn(c syscall.RawConn, addr Addr) error { + var operr error fn := func(s uintptr) { - operr = syscall.SetsockoptInt(int(s), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) + _, operr = syscall.GetsockoptInt(int(s), syscall.SOL_SOCKET, syscall.SO_REUSEADDR) + if operr != nil { + return + } + switch addr := addr.(type) { + case *TCPAddr: + // There's no guarantee that IP-level socket + // options work well with dual stack sockets. + // A simple solution would be to take a look + // at the bound address to the raw connection + // and to classify the address family of the + // underlying socket by the bound address: + // + // - When IP.To16() != nil and IP.To4() == nil, + // we can assume that the raw connection + // consists of an IPv6 socket using only + // IPv6 addresses. + // + // - When IP.To16() == nil and IP.To4() != nil, + // the raw connection consists of an IPv4 + // socket using only IPv4 addresses. + // + // - Otherwise, the raw connection is a dual + // stack socket, an IPv6 socket using IPv6 + // addresses including IPv4-mapped or + // IPv4-embedded IPv6 addresses. + if addr.IP.To16() != nil && addr.IP.To4() == nil { + operr = syscall.SetsockoptInt(int(s), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, 1) + } else if addr.IP.To16() == nil && addr.IP.To4() != nil { + operr = syscall.SetsockoptInt(int(s), syscall.IPPROTO_IP, syscall.IP_TTL, 1) + } + } } - err = cc.Control(fn) - if err != nil || operr != nil { - t.Fatal(err, operr) + if err := c.Control(fn); err != nil { + return err } - c.Close() - err = cc.Control(fn) - if err == nil { - t.Fatal("should fail") + if operr != nil { + return operr } + return nil } -func TestRawConnListener(t *testing.T) { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } - defer ln.Close() - - cc, err := ln.(*TCPListener).SyscallConn() - if err != nil { - t.Fatal(err) - } - - called := false - op := func(uintptr) bool { - called = true - return true - } - - err = cc.Write(op) - if err == nil { - t.Error("Write should return an error") - } - if called { - t.Error("Write shouldn't call op") - } - - called = false - err = cc.Read(op) - if err == nil { - t.Error("Read should return an error") - } - if called { - t.Error("Read shouldn't call op") - } - +func controlOnConnSetup(network string, address string, c syscall.RawConn) error { var operr error - fn := func(s uintptr) { - _, operr = syscall.GetsockoptInt(int(s), syscall.SOL_SOCKET, syscall.SO_REUSEADDR) + var fn func(uintptr) + switch network { + case "tcp", "udp", "ip": + return errors.New("ambiguous network: " + network) + case "unix", "unixpacket", "unixgram": + fn = func(s uintptr) { + _, operr = syscall.GetsockoptInt(int(s), syscall.SOL_SOCKET, syscall.SO_ERROR) + } + default: + switch network[len(network)-1] { + case '4': + fn = func(s uintptr) { + operr = syscall.SetsockoptInt(int(s), syscall.IPPROTO_IP, syscall.IP_TTL, 1) + } + case '6': + fn = func(s uintptr) { + operr = syscall.SetsockoptInt(int(s), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, 1) + } + default: + return errors.New("unknown network: " + network) + } } - err = cc.Control(fn) - if err != nil || operr != nil { - t.Fatal(err, operr) + if err := c.Control(fn); err != nil { + return err } - ln.Close() - err = cc.Control(fn) - if err == nil { - t.Fatal("Control after Close should fail") + if operr != nil { + return operr } + return nil } diff --git a/libgo/go/net/rawconn_windows_test.go b/libgo/go/net/rawconn_windows_test.go index 2ee12c3..2774c97 100644 --- a/libgo/go/net/rawconn_windows_test.go +++ b/libgo/go/net/rawconn_windows_test.go @@ -5,85 +5,124 @@ package net import ( + "errors" "syscall" - "testing" "unsafe" ) -func TestRawConn(t *testing.T) { - c, err := newLocalPacketListener("udp") - if err != nil { - t.Fatal(err) - } - defer c.Close() - cc, err := c.(*UDPConn).SyscallConn() - if err != nil { - t.Fatal(err) - } - +func readRawConn(c syscall.RawConn, b []byte) (int, error) { var operr error - fn := func(s uintptr) { - operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) - } - err = cc.Control(fn) - if err != nil || operr != nil { - t.Fatal(err, operr) - } - c.Close() - err = cc.Control(fn) - if err == nil { - t.Fatal("should fail") - } -} - -func TestRawConnListener(t *testing.T) { - ln, err := newLocalListener("tcp") + var n int + err := c.Read(func(s uintptr) bool { + var read uint32 + var flags uint32 + var buf syscall.WSABuf + buf.Buf = &b[0] + buf.Len = uint32(len(b)) + operr = syscall.WSARecv(syscall.Handle(s), &buf, 1, &read, &flags, nil, nil) + n = int(read) + return true + }) if err != nil { - t.Fatal(err) + return n, err } - defer ln.Close() - - cc, err := ln.(*TCPListener).SyscallConn() - if err != nil { - t.Fatal(err) + if operr != nil { + return n, operr } + return n, nil +} - called := false - op := func(uintptr) bool { - called = true +func writeRawConn(c syscall.RawConn, b []byte) error { + var operr error + err := c.Write(func(s uintptr) bool { + var written uint32 + var buf syscall.WSABuf + buf.Buf = &b[0] + buf.Len = uint32(len(b)) + operr = syscall.WSASend(syscall.Handle(s), &buf, 1, &written, 0, nil, nil) return true + }) + if err != nil { + return err } - - err = cc.Write(op) - if err == nil { - t.Error("Write should return an error") - } - if called { - t.Error("Write shouldn't call op") - } - - called = false - err = cc.Read(op) - if err == nil { - t.Error("Read should return an error") - } - if called { - t.Error("Read shouldn't call op") + if operr != nil { + return operr } + return nil +} +func controlRawConn(c syscall.RawConn, addr Addr) error { var operr error fn := func(s uintptr) { var v, l int32 l = int32(unsafe.Sizeof(v)) operr = syscall.Getsockopt(syscall.Handle(s), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, (*byte)(unsafe.Pointer(&v)), &l) + if operr != nil { + return + } + switch addr := addr.(type) { + case *TCPAddr: + // There's no guarantee that IP-level socket + // options work well with dual stack sockets. + // A simple solution would be to take a look + // at the bound address to the raw connection + // and to classify the address family of the + // underlying socket by the bound address: + // + // - When IP.To16() != nil and IP.To4() == nil, + // we can assume that the raw connection + // consists of an IPv6 socket using only + // IPv6 addresses. + // + // - When IP.To16() == nil and IP.To4() != nil, + // the raw connection consists of an IPv4 + // socket using only IPv4 addresses. + // + // - Otherwise, the raw connection is a dual + // stack socket, an IPv6 socket using IPv6 + // addresses including IPv4-mapped or + // IPv4-embedded IPv6 addresses. + if addr.IP.To16() != nil && addr.IP.To4() == nil { + operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, 1) + } else if addr.IP.To16() == nil && addr.IP.To4() != nil { + operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.IPPROTO_IP, syscall.IP_TTL, 1) + } + } + } + if err := c.Control(fn); err != nil { + return err + } + if operr != nil { + return operr + } + return nil +} + +func controlOnConnSetup(network string, address string, c syscall.RawConn) error { + var operr error + var fn func(uintptr) + switch network { + case "tcp", "udp", "ip": + return errors.New("ambiguous network: " + network) + default: + switch network[len(network)-1] { + case '4': + fn = func(s uintptr) { + operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.IPPROTO_IP, syscall.IP_TTL, 1) + } + case '6': + fn = func(s uintptr) { + operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, 1) + } + default: + return errors.New("unknown network: " + network) + } } - err = cc.Control(fn) - if err != nil || operr != nil { - t.Fatal(err, operr) + if err := c.Control(fn); err != nil { + return err } - ln.Close() - err = cc.Control(fn) - if err == nil { - t.Fatal("Control after Close should fail") + if operr != nil { + return operr } + return nil } diff --git a/libgo/go/net/rpc/client.go b/libgo/go/net/rpc/client.go index fce6a48..cad2d45 100644 --- a/libgo/go/net/rpc/client.go +++ b/libgo/go/net/rpc/client.go @@ -59,8 +59,8 @@ type Client struct { // connection. ReadResponseBody may be called with a nil // argument to force the body of the response to be read and then // discarded. +// See NewClient's comment for information about concurrent access. type ClientCodec interface { - // WriteRequest must be safe for concurrent use by multiple goroutines. WriteRequest(*Request, interface{}) error ReadResponseHeader(*Response) error ReadResponseBody(interface{}) error @@ -75,8 +75,8 @@ func (client *Client) send(call *Call) { // Register this call. client.mutex.Lock() if client.shutdown || client.closing { - call.Error = ErrShutdown client.mutex.Unlock() + call.Error = ErrShutdown call.done() return } @@ -185,6 +185,11 @@ func (call *Call) done() { // set of services at the other end of the connection. // It adds a buffer to the write side of the connection so // the header and payload are sent as a unit. +// +// The read and write halves of the connection are serialized independently, +// so no interlocking is required. However each half may be accessed +// concurrently so the implementation of conn should protect against +// concurrent reads or concurrent writes. func NewClient(conn io.ReadWriteCloser) *Client { encBuf := bufio.NewWriter(conn) client := &gobClientCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(encBuf), encBuf} diff --git a/libgo/go/net/rpc/server.go b/libgo/go/net/rpc/server.go index 96e6973..7bb6476 100644 --- a/libgo/go/net/rpc/server.go +++ b/libgo/go/net/rpc/server.go @@ -444,6 +444,7 @@ func (c *gobServerCodec) Close() error { // The caller typically invokes ServeConn in a go statement. // ServeConn uses the gob wire format (see package gob) on the // connection. To use an alternate codec, use ServeCodec. +// See NewClient's comment for information about concurrent access. func (server *Server) ServeConn(conn io.ReadWriteCloser) { buf := bufio.NewWriter(conn) srv := &gobServerCodec{ @@ -653,12 +654,13 @@ func RegisterName(name string, rcvr interface{}) error { // write a response back. The server calls Close when finished with the // connection. ReadRequestBody may be called with a nil // argument to force the body of the request to be read and discarded. +// See NewClient's comment for information about concurrent access. type ServerCodec interface { ReadRequestHeader(*Request) error ReadRequestBody(interface{}) error - // WriteResponse must be safe for concurrent use by multiple goroutines. WriteResponse(*Response, interface{}) error + // Close can be called multiple times and must be idempotent. Close() error } @@ -667,6 +669,7 @@ type ServerCodec interface { // The caller typically invokes ServeConn in a go statement. // ServeConn uses the gob wire format (see package gob) on the // connection. To use an alternate codec, use ServeCodec. +// See NewClient's comment for information about concurrent access. func ServeConn(conn io.ReadWriteCloser) { DefaultServer.ServeConn(conn) } diff --git a/libgo/go/net/sendfile_solaris.go b/libgo/go/net/sendfile_solaris.go deleted file mode 100644 index 63ca9d4..0000000 --- a/libgo/go/net/sendfile_solaris.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2015 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 ( - "internal/poll" - "io" - "os" -) - -// sendFile copies the contents of r to c using the sendfile -// system call to minimize copies. -// -// if handled == true, sendFile returns the number of bytes copied and any -// non-EOF error. -// -// if handled == false, sendFile performed no work. -func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { - // Solaris uses 0 as the "until EOF" value. If you pass in more bytes than the - // file contains, it will loop back to the beginning ad nauseam until it's sent - // exactly the number of bytes told to. As such, we need to know exactly how many - // bytes to send. - var remain int64 = 0 - - lr, ok := r.(*io.LimitedReader) - if ok { - remain, r = lr.N, lr.R - if remain <= 0 { - return 0, nil, true - } - } - f, ok := r.(*os.File) - if !ok { - return 0, nil, false - } - - if remain == 0 { - fi, err := f.Stat() - if err != nil { - return 0, err, false - } - - remain = fi.Size() - } - - // The other quirk with Solaris's sendfile implementation is that it doesn't - // use the current position of the file -- if you pass it offset 0, it starts - // from offset 0. There's no way to tell it "start from current position", so - // we have to manage that explicitly. - pos, err := f.Seek(0, io.SeekCurrent) - if err != nil { - return 0, err, false - } - - written, err = poll.SendFile(&c.pfd, int(f.Fd()), pos, remain) - - if lr != nil { - lr.N = remain - written - } - return written, wrapSyscallError("sendfile", err), written > 0 -} diff --git a/libgo/go/net/sendfile_stub.go b/libgo/go/net/sendfile_stub.go index f043062..6d338da 100644 --- a/libgo/go/net/sendfile_stub.go +++ b/libgo/go/net/sendfile_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin nacl netbsd openbsd +// +build aix darwin js,wasm nacl netbsd openbsd package net diff --git a/libgo/go/net/sendfile_test.go b/libgo/go/net/sendfile_test.go index 2255e7c..3b98277 100644 --- a/libgo/go/net/sendfile_test.go +++ b/libgo/go/net/sendfile_test.go @@ -2,9 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( + "bytes" "crypto/sha256" "encoding/hex" "fmt" @@ -88,3 +91,122 @@ func TestSendfile(t *testing.T) { t.Error(err) } } + +func TestSendfileParts(t *testing.T) { + ln, err := newLocalListener("tcp") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + errc := make(chan error, 1) + go func(ln Listener) { + // Wait for a connection. + conn, err := ln.Accept() + if err != nil { + errc <- err + close(errc) + return + } + + go func() { + defer close(errc) + defer conn.Close() + + f, err := os.Open(twain) + if err != nil { + errc <- err + return + } + defer f.Close() + + for i := 0; i < 3; i++ { + // Return file data using io.CopyN, which should use + // sendFile if available. + _, err = io.CopyN(conn, f, 3) + if err != nil { + errc <- err + return + } + } + }() + }(ln) + + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + buf := new(bytes.Buffer) + buf.ReadFrom(c) + + if want, have := "Produced ", buf.String(); have != want { + t.Errorf("unexpected server reply %q, want %q", have, want) + } + + for err := range errc { + t.Error(err) + } +} + +func TestSendfileSeeked(t *testing.T) { + ln, err := newLocalListener("tcp") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + const seekTo = 65 << 10 + const sendSize = 10 << 10 + + errc := make(chan error, 1) + go func(ln Listener) { + // Wait for a connection. + conn, err := ln.Accept() + if err != nil { + errc <- err + close(errc) + return + } + + go func() { + defer close(errc) + defer conn.Close() + + f, err := os.Open(twain) + if err != nil { + errc <- err + return + } + defer f.Close() + if _, err := f.Seek(seekTo, os.SEEK_SET); err != nil { + errc <- err + return + } + + _, err = io.CopyN(conn, f, sendSize) + if err != nil { + errc <- err + return + } + }() + }(ln) + + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + buf := new(bytes.Buffer) + buf.ReadFrom(c) + + if buf.Len() != sendSize { + t.Errorf("Got %d bytes; want %d", buf.Len(), sendSize) + } + + for err := range errc { + t.Error(err) + } +} diff --git a/libgo/go/net/sendfile_bsd.go b/libgo/go/net/sendfile_unix_alt.go index 7a2b48c..9b3ba4e 100644 --- a/libgo/go/net/sendfile_bsd.go +++ b/libgo/go/net/sendfile_unix_alt.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build dragonfly freebsd +// +build dragonfly freebsd solaris package net @@ -20,7 +20,7 @@ import ( // // if handled == false, sendFile performed no work. func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { - // FreeBSD and DragonFly use 0 as the "until EOF" value. + // FreeBSD, DragonFly and Solaris use 0 as the "until EOF" value. // If you pass in more bytes than the file contains, it will // loop back to the beginning ad nauseam until it's sent // exactly the number of bytes told to. As such, we need to @@ -48,7 +48,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { remain = fi.Size() } - // The other quirk with FreeBSD/DragonFly's sendfile + // The other quirk with FreeBSD/DragonFly/Solaris's sendfile // implementation is that it doesn't use the current position // of the file -- if you pass it offset 0, it starts from // offset 0. There's no way to tell it "start from current @@ -63,5 +63,11 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { if lr != nil { lr.N = remain - written } + + _, err1 := f.Seek(written, io.SeekCurrent) + if err1 != nil && err == nil { + return written, err1, written > 0 + } + return written, wrapSyscallError("sendfile", err), written > 0 } diff --git a/libgo/go/net/server_test.go b/libgo/go/net/server_test.go index 2e998e2..1608beb 100644 --- a/libgo/go/net/server_test.go +++ b/libgo/go/net/server_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( diff --git a/libgo/go/net/smtp/smtp.go b/libgo/go/net/smtp/smtp.go index cf699e6..e4e12ae 100644 --- a/libgo/go/net/smtp/smtp.go +++ b/libgo/go/net/smtp/smtp.go @@ -343,10 +343,11 @@ func SendMail(addr string, a Auth, from string, to []string, msg []byte) error { } } if a != nil && c.ext != nil { - if _, ok := c.ext["AUTH"]; ok { - if err = c.Auth(a); err != nil { - return err - } + if _, ok := c.ext["AUTH"]; !ok { + return errors.New("smtp: server doesn't support AUTH") + } + if err = c.Auth(a); err != nil { + return err } } if err = c.Mail(from); err != nil { diff --git a/libgo/go/net/smtp/smtp_test.go b/libgo/go/net/smtp/smtp_test.go index d489922..000cac4 100644 --- a/libgo/go/net/smtp/smtp_test.go +++ b/libgo/go/net/smtp/smtp_test.go @@ -15,6 +15,7 @@ import ( "net/textproto" "runtime" "strings" + "sync" "testing" "time" ) @@ -635,6 +636,50 @@ SendMail is working for me. QUIT ` +func TestSendMailWithAuth(t *testing.T) { + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("Unable to to create listener: %v", err) + } + defer l.Close() + wg := sync.WaitGroup{} + var done = make(chan struct{}) + go func() { + defer wg.Done() + conn, err := l.Accept() + if err != nil { + t.Errorf("Accept error: %v", err) + return + } + defer conn.Close() + + tc := textproto.NewConn(conn) + tc.PrintfLine("220 hello world") + msg, err := tc.ReadLine() + if msg == "EHLO localhost" { + tc.PrintfLine("250 mx.google.com at your service") + } + // for this test case, there should have no more traffic + <-done + }() + wg.Add(1) + + err = SendMail(l.Addr().String(), PlainAuth("", "user", "pass", "smtp.google.com"), "test@example.com", []string{"other@example.com"}, []byte(strings.Replace(`From: test@example.com +To: other@example.com +Subject: SendMail test + +SendMail is working for me. +`, "\n", "\r\n", -1))) + if err == nil { + t.Error("SendMail: Server doesn't support AUTH, expected to get an error, but got none ") + } + if err.Error() != "smtp: server doesn't support AUTH" { + t.Errorf("Expected: smtp: server doesn't support AUTH, got: %s", err) + } + close(done) + wg.Wait() +} + func TestAuthFailed(t *testing.T) { server := strings.Join(strings.Split(authFailedServer, "\n"), "\r\n") client := strings.Join(strings.Split(authFailedClient, "\n"), "\r\n") @@ -680,7 +725,7 @@ QUIT ` func TestTLSClient(t *testing.T) { - if runtime.GOOS == "freebsd" && runtime.GOARCH == "amd64" { + if (runtime.GOOS == "freebsd" && runtime.GOARCH == "amd64") || runtime.GOOS == "js" { testenv.SkipFlaky(t, 19229) } ln := newLocalListener(t) @@ -830,14 +875,9 @@ func init() { } func sendMail(hostPort string) error { - host, _, err := net.SplitHostPort(hostPort) - if err != nil { - return err - } - auth := PlainAuth("", "", "", host) from := "joe1@example.com" to := []string{"joe2@example.com"} - return SendMail(hostPort, auth, from, to, []byte("Subject: test\n\nhowdy!")) + return SendMail(hostPort, nil, from, to, []byte("Subject: test\n\nhowdy!")) } // (copied from net/http/httptest) diff --git a/libgo/go/net/sock_cloexec.go b/libgo/go/net/sock_cloexec.go index 06ff10d..0c883dc 100644 --- a/libgo/go/net/sock_cloexec.go +++ b/libgo/go/net/sock_cloexec.go @@ -5,7 +5,7 @@ // This file implements sysSocket and accept for platforms that // provide a fast path for setting SetNonblock and CloseOnExec. -// +build dragonfly freebsd linux +// +build dragonfly freebsd linux netbsd openbsd package net diff --git a/libgo/go/net/sock_posix.go b/libgo/go/net/sock_posix.go index 4733c42..fac3ac1 100644 --- a/libgo/go/net/sock_posix.go +++ b/libgo/go/net/sock_posix.go @@ -13,32 +13,9 @@ import ( "syscall" ) -// A sockaddr represents a TCP, UDP, IP or Unix network endpoint -// address that can be converted into a syscall.Sockaddr. -type sockaddr interface { - Addr - - // family returns the platform-dependent address family - // identifier. - family() int - - // isWildcard reports whether the address is a wildcard - // address. - isWildcard() bool - - // sockaddr returns the address converted into a syscall - // sockaddr type that implements syscall.Sockaddr - // interface. It returns a nil interface when the address is - // nil. - sockaddr(family int) (syscall.Sockaddr, error) - - // toLocal maps the zero address to a local system address (127.0.0.1 or ::1) - toLocal(net string) sockaddr -} - // socket returns a network file descriptor that is ready for // asynchronous I/O using the network poller. -func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr) (fd *netFD, err error) { +func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr, ctrlFn func(string, string, syscall.RawConn) error) (fd *netFD, err error) { s, err := sysSocket(family, sotype, proto) if err != nil { return nil, err @@ -77,26 +54,41 @@ func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only if laddr != nil && raddr == nil { switch sotype { case syscall.SOCK_STREAM, syscall.SOCK_SEQPACKET: - if err := fd.listenStream(laddr, listenerBacklog); err != nil { + if err := fd.listenStream(laddr, listenerBacklog, ctrlFn); err != nil { fd.Close() return nil, err } return fd, nil case syscall.SOCK_DGRAM: - if err := fd.listenDatagram(laddr); err != nil { + if err := fd.listenDatagram(laddr, ctrlFn); err != nil { fd.Close() return nil, err } return fd, nil } } - if err := fd.dial(ctx, laddr, raddr); err != nil { + if err := fd.dial(ctx, laddr, raddr, ctrlFn); err != nil { fd.Close() return nil, err } return fd, nil } +func (fd *netFD) ctrlNetwork() string { + switch fd.net { + case "unix", "unixgram", "unixpacket": + return fd.net + } + switch fd.net[len(fd.net)-1] { + case '4', '6': + return fd.net + } + if fd.family == syscall.AF_INET { + return fd.net + "4" + } + return fd.net + "6" +} + func (fd *netFD) addrFunc() func(syscall.Sockaddr) Addr { switch fd.family { case syscall.AF_INET, syscall.AF_INET6: @@ -121,14 +113,29 @@ func (fd *netFD) addrFunc() func(syscall.Sockaddr) Addr { return func(syscall.Sockaddr) Addr { return nil } } -func (fd *netFD) dial(ctx context.Context, laddr, raddr sockaddr) error { +func (fd *netFD) dial(ctx context.Context, laddr, raddr sockaddr, ctrlFn func(string, string, syscall.RawConn) error) error { + if ctrlFn != nil { + c, err := newRawConn(fd) + if err != nil { + return err + } + var ctrlAddr string + if raddr != nil { + ctrlAddr = raddr.String() + } else if laddr != nil { + ctrlAddr = laddr.String() + } + if err := ctrlFn(fd.ctrlNetwork(), ctrlAddr, c); err != nil { + return err + } + } var err error var lsa syscall.Sockaddr if laddr != nil { if lsa, err = laddr.sockaddr(fd.family); err != nil { return err } else if lsa != nil { - if err := syscall.Bind(fd.pfd.Sysfd, lsa); err != nil { + if err = syscall.Bind(fd.pfd.Sysfd, lsa); err != nil { return os.NewSyscallError("bind", err) } } @@ -165,24 +172,34 @@ func (fd *netFD) dial(ctx context.Context, laddr, raddr sockaddr) error { return nil } -func (fd *netFD) listenStream(laddr sockaddr, backlog int) error { - if err := setDefaultListenerSockopts(fd.pfd.Sysfd); err != nil { +func (fd *netFD) listenStream(laddr sockaddr, backlog int, ctrlFn func(string, string, syscall.RawConn) error) error { + var err error + if err = setDefaultListenerSockopts(fd.pfd.Sysfd); err != nil { return err } - if lsa, err := laddr.sockaddr(fd.family); err != nil { + var lsa syscall.Sockaddr + if lsa, err = laddr.sockaddr(fd.family); err != nil { return err - } else if lsa != nil { - if err := syscall.Bind(fd.pfd.Sysfd, lsa); err != nil { - return os.NewSyscallError("bind", err) + } + if ctrlFn != nil { + c, err := newRawConn(fd) + if err != nil { + return err + } + if err := ctrlFn(fd.ctrlNetwork(), laddr.String(), c); err != nil { + return err } } - if err := listenFunc(fd.pfd.Sysfd, backlog); err != nil { + if err = syscall.Bind(fd.pfd.Sysfd, lsa); err != nil { + return os.NewSyscallError("bind", err) + } + if err = listenFunc(fd.pfd.Sysfd, backlog); err != nil { return os.NewSyscallError("listen", err) } - if err := fd.init(); err != nil { + if err = fd.init(); err != nil { return err } - lsa, err := syscall.Getsockname(fd.pfd.Sysfd) + lsa, err = syscall.Getsockname(fd.pfd.Sysfd) if err != nil { return os.NewSyscallError("getsockname", err) } @@ -190,7 +207,7 @@ func (fd *netFD) listenStream(laddr sockaddr, backlog int) error { return nil } -func (fd *netFD) listenDatagram(laddr sockaddr) error { +func (fd *netFD) listenDatagram(laddr sockaddr, ctrlFn func(string, string, syscall.RawConn) error) error { switch addr := laddr.(type) { case *UDPAddr: // We provide a socket that listens to a wildcard @@ -214,17 +231,27 @@ func (fd *netFD) listenDatagram(laddr sockaddr) error { laddr = &addr } } - if lsa, err := laddr.sockaddr(fd.family); err != nil { + var err error + var lsa syscall.Sockaddr + if lsa, err = laddr.sockaddr(fd.family); err != nil { return err - } else if lsa != nil { - if err := syscall.Bind(fd.pfd.Sysfd, lsa); err != nil { - return os.NewSyscallError("bind", err) + } + if ctrlFn != nil { + c, err := newRawConn(fd) + if err != nil { + return err } + if err := ctrlFn(fd.ctrlNetwork(), laddr.String(), c); err != nil { + return err + } + } + if err = syscall.Bind(fd.pfd.Sysfd, lsa); err != nil { + return os.NewSyscallError("bind", err) } - if err := fd.init(); err != nil { + if err = fd.init(); err != nil { return err } - lsa, err := syscall.Getsockname(fd.pfd.Sysfd) + lsa, err = syscall.Getsockname(fd.pfd.Sysfd) if err != nil { return os.NewSyscallError("getsockname", err) } diff --git a/libgo/go/net/sock_stub.go b/libgo/go/net/sock_stub.go index d1ec029..bbce61b 100644 --- a/libgo/go/net/sock_stub.go +++ b/libgo/go/net/sock_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix nacl solaris +// +build aix nacl js,wasm solaris package net diff --git a/libgo/go/net/sockaddr_posix.go b/libgo/go/net/sockaddr_posix.go new file mode 100644 index 0000000..4b8699d --- /dev/null +++ b/libgo/go/net/sockaddr_posix.go @@ -0,0 +1,34 @@ +// Copyright 2018 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 dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows + +package net + +import ( + "syscall" +) + +// A sockaddr represents a TCP, UDP, IP or Unix network endpoint +// address that can be converted into a syscall.Sockaddr. +type sockaddr interface { + Addr + + // family returns the platform-dependent address family + // identifier. + family() int + + // isWildcard reports whether the address is a wildcard + // address. + isWildcard() bool + + // sockaddr returns the address converted into a syscall + // sockaddr type that implements syscall.Sockaddr + // interface. It returns a nil interface when the address is + // nil. + sockaddr(family int) (syscall.Sockaddr, error) + + // toLocal maps the zero address to a local system address (127.0.0.1 or ::1) + toLocal(net string) sockaddr +} diff --git a/libgo/go/net/sockopt_stub.go b/libgo/go/net/sockopt_stub.go index 7e9e560..bc06675 100644 --- a/libgo/go/net/sockopt_stub.go +++ b/libgo/go/net/sockopt_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build nacl +// +build nacl js,wasm package net diff --git a/libgo/go/net/sockoptip_stub.go b/libgo/go/net/sockoptip_stub.go index fc20a9f..3297969 100644 --- a/libgo/go/net/sockoptip_stub.go +++ b/libgo/go/net/sockoptip_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build nacl +// +build nacl js,wasm package net diff --git a/libgo/go/net/splice_linux.go b/libgo/go/net/splice_linux.go new file mode 100644 index 0000000..b055f93 --- /dev/null +++ b/libgo/go/net/splice_linux.go @@ -0,0 +1,35 @@ +// Copyright 2018 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 ( + "internal/poll" + "io" +) + +// splice transfers data from r to c using the splice system call to minimize +// copies from and to userspace. c must be a TCP connection. Currently, splice +// is only enabled if r is also a TCP connection. +// +// If splice returns handled == false, it has performed no work. +func splice(c *netFD, r io.Reader) (written int64, err error, handled bool) { + var remain int64 = 1 << 62 // by default, copy until EOF + lr, ok := r.(*io.LimitedReader) + if ok { + remain, r = lr.N, lr.R + if remain <= 0 { + return 0, nil, true + } + } + s, ok := r.(*TCPConn) + if !ok { + return 0, nil, false + } + written, handled, sc, err := poll.Splice(&c.pfd, &s.fd.pfd, remain) + if lr != nil { + lr.N -= written + } + return written, wrapSyscallError(sc, err), handled +} diff --git a/libgo/go/net/splice_stub.go b/libgo/go/net/splice_stub.go new file mode 100644 index 0000000..9106cb2 --- /dev/null +++ b/libgo/go/net/splice_stub.go @@ -0,0 +1,13 @@ +// Copyright 2018 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 !linux + +package net + +import "io" + +func splice(c *netFD, r io.Reader) (int64, error, bool) { + return 0, nil, false +} diff --git a/libgo/go/net/splice_test.go b/libgo/go/net/splice_test.go new file mode 100644 index 0000000..44a5c00 --- /dev/null +++ b/libgo/go/net/splice_test.go @@ -0,0 +1,489 @@ +// Copyright 2018 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 linux + +package net + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "sync" + "testing" +) + +func TestSplice(t *testing.T) { + t.Run("simple", testSpliceSimple) + t.Run("multipleWrite", testSpliceMultipleWrite) + t.Run("big", testSpliceBig) + t.Run("honorsLimitedReader", testSpliceHonorsLimitedReader) + t.Run("readerAtEOF", testSpliceReaderAtEOF) + t.Run("issue25985", testSpliceIssue25985) +} + +func testSpliceSimple(t *testing.T) { + srv, err := newSpliceTestServer() + if err != nil { + t.Fatal(err) + } + defer srv.Close() + copyDone := srv.Copy() + msg := []byte("splice test") + if _, err := srv.Write(msg); err != nil { + t.Fatal(err) + } + got := make([]byte, len(msg)) + if _, err := io.ReadFull(srv, got); err != nil { + t.Fatal(err) + } + if !bytes.Equal(got, msg) { + t.Errorf("got %q, wrote %q", got, msg) + } + srv.CloseWrite() + srv.CloseRead() + if err := <-copyDone; err != nil { + t.Errorf("splice: %v", err) + } +} + +func testSpliceMultipleWrite(t *testing.T) { + srv, err := newSpliceTestServer() + if err != nil { + t.Fatal(err) + } + defer srv.Close() + copyDone := srv.Copy() + msg1 := []byte("splice test part 1 ") + msg2 := []byte(" splice test part 2") + if _, err := srv.Write(msg1); err != nil { + t.Fatalf("Write: %v", err) + } + if _, err := srv.Write(msg2); err != nil { + t.Fatal(err) + } + got := make([]byte, len(msg1)+len(msg2)) + if _, err := io.ReadFull(srv, got); err != nil { + t.Fatal(err) + } + want := append(msg1, msg2...) + if !bytes.Equal(got, want) { + t.Errorf("got %q, wrote %q", got, want) + } + srv.CloseWrite() + srv.CloseRead() + if err := <-copyDone; err != nil { + t.Errorf("splice: %v", err) + } +} + +func testSpliceBig(t *testing.T) { + // The maximum amount of data that internal/poll.Splice will use in a + // splice(2) call is 4 << 20. Use a bigger size here so that we test an + // amount that doesn't fit in a single call. + size := 5 << 20 + srv, err := newSpliceTestServer() + if err != nil { + t.Fatal(err) + } + defer srv.Close() + big := make([]byte, size) + copyDone := srv.Copy() + type readResult struct { + b []byte + err error + } + readDone := make(chan readResult) + go func() { + got := make([]byte, len(big)) + _, err := io.ReadFull(srv, got) + readDone <- readResult{got, err} + }() + if _, err := srv.Write(big); err != nil { + t.Fatal(err) + } + res := <-readDone + if res.err != nil { + t.Fatal(res.err) + } + got := res.b + if !bytes.Equal(got, big) { + t.Errorf("input and output differ") + } + srv.CloseWrite() + srv.CloseRead() + if err := <-copyDone; err != nil { + t.Errorf("splice: %v", err) + } +} + +func testSpliceHonorsLimitedReader(t *testing.T) { + t.Run("stopsAfterN", testSpliceStopsAfterN) + t.Run("updatesN", testSpliceUpdatesN) +} + +func testSpliceStopsAfterN(t *testing.T) { + clientUp, serverUp, err := spliceTestSocketPair("tcp") + if err != nil { + t.Fatal(err) + } + defer clientUp.Close() + defer serverUp.Close() + clientDown, serverDown, err := spliceTestSocketPair("tcp") + if err != nil { + t.Fatal(err) + } + defer clientDown.Close() + defer serverDown.Close() + count := 128 + copyDone := make(chan error) + lr := &io.LimitedReader{ + N: int64(count), + R: serverUp, + } + go func() { + _, err := io.Copy(serverDown, lr) + serverDown.Close() + copyDone <- err + }() + msg := make([]byte, 2*count) + if _, err := clientUp.Write(msg); err != nil { + t.Fatal(err) + } + clientUp.Close() + var buf bytes.Buffer + if _, err := io.Copy(&buf, clientDown); err != nil { + t.Fatal(err) + } + if buf.Len() != count { + t.Errorf("splice transferred %d bytes, want to stop after %d", buf.Len(), count) + } + clientDown.Close() + if err := <-copyDone; err != nil { + t.Errorf("splice: %v", err) + } +} + +func testSpliceUpdatesN(t *testing.T) { + clientUp, serverUp, err := spliceTestSocketPair("tcp") + if err != nil { + t.Fatal(err) + } + defer clientUp.Close() + defer serverUp.Close() + clientDown, serverDown, err := spliceTestSocketPair("tcp") + if err != nil { + t.Fatal(err) + } + defer clientDown.Close() + defer serverDown.Close() + count := 128 + copyDone := make(chan error) + lr := &io.LimitedReader{ + N: int64(100 + count), + R: serverUp, + } + go func() { + _, err := io.Copy(serverDown, lr) + copyDone <- err + }() + msg := make([]byte, count) + if _, err := clientUp.Write(msg); err != nil { + t.Fatal(err) + } + clientUp.Close() + got := make([]byte, count) + if _, err := io.ReadFull(clientDown, got); err != nil { + t.Fatal(err) + } + clientDown.Close() + if err := <-copyDone; err != nil { + t.Errorf("splice: %v", err) + } + wantN := int64(100) + if lr.N != wantN { + t.Errorf("lr.N = %d, want %d", lr.N, wantN) + } +} + +func testSpliceReaderAtEOF(t *testing.T) { + clientUp, serverUp, err := spliceTestSocketPair("tcp") + if err != nil { + t.Fatal(err) + } + defer clientUp.Close() + defer serverUp.Close() + clientDown, serverDown, err := spliceTestSocketPair("tcp") + if err != nil { + t.Fatal(err) + } + defer clientDown.Close() + defer serverDown.Close() + + serverUp.Close() + _, err, handled := splice(serverDown.(*TCPConn).fd, serverUp) + if !handled { + t.Errorf("closed connection: got err = %v, handled = %t, want handled = true", err, handled) + } + lr := &io.LimitedReader{ + N: 0, + R: serverUp, + } + _, err, handled = splice(serverDown.(*TCPConn).fd, lr) + if !handled { + t.Errorf("exhausted LimitedReader: got err = %v, handled = %t, want handled = true", err, handled) + } +} + +func testSpliceIssue25985(t *testing.T) { + front, err := newLocalListener("tcp") + if err != nil { + t.Fatal(err) + } + defer front.Close() + back, err := newLocalListener("tcp") + if err != nil { + t.Fatal(err) + } + defer back.Close() + + var wg sync.WaitGroup + wg.Add(2) + + proxy := func() { + src, err := front.Accept() + if err != nil { + return + } + dst, err := Dial("tcp", back.Addr().String()) + if err != nil { + return + } + defer dst.Close() + defer src.Close() + go func() { + io.Copy(src, dst) + wg.Done() + }() + go func() { + io.Copy(dst, src) + wg.Done() + }() + } + + go proxy() + + toFront, err := Dial("tcp", front.Addr().String()) + if err != nil { + t.Fatal(err) + } + + io.WriteString(toFront, "foo") + toFront.Close() + + fromProxy, err := back.Accept() + if err != nil { + t.Fatal(err) + } + defer fromProxy.Close() + + _, err = ioutil.ReadAll(fromProxy) + if err != nil { + t.Fatal(err) + } + + wg.Wait() +} + +func BenchmarkTCPReadFrom(b *testing.B) { + testHookUninstaller.Do(uninstallTestHooks) + + var chunkSizes []int + for i := uint(10); i <= 20; i++ { + chunkSizes = append(chunkSizes, 1<<i) + } + // To benchmark the genericReadFrom code path, set this to false. + useSplice := true + for _, chunkSize := range chunkSizes { + b.Run(fmt.Sprint(chunkSize), func(b *testing.B) { + benchmarkSplice(b, chunkSize, useSplice) + }) + } +} + +func benchmarkSplice(b *testing.B, chunkSize int, useSplice bool) { + srv, err := newSpliceTestServer() + if err != nil { + b.Fatal(err) + } + defer srv.Close() + var copyDone <-chan error + if useSplice { + copyDone = srv.Copy() + } else { + copyDone = srv.CopyNoSplice() + } + chunk := make([]byte, chunkSize) + discardDone := make(chan struct{}) + go func() { + for { + buf := make([]byte, chunkSize) + _, err := srv.Read(buf) + if err != nil { + break + } + } + discardDone <- struct{}{} + }() + b.SetBytes(int64(chunkSize)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + srv.Write(chunk) + } + srv.CloseWrite() + <-copyDone + srv.CloseRead() + <-discardDone +} + +type spliceTestServer struct { + clientUp io.WriteCloser + clientDown io.ReadCloser + serverUp io.ReadCloser + serverDown io.WriteCloser +} + +func newSpliceTestServer() (*spliceTestServer, error) { + // For now, both networks are hard-coded to TCP. + // If splice is enabled for non-tcp upstream connections, + // newSpliceTestServer will need to take a network parameter. + clientUp, serverUp, err := spliceTestSocketPair("tcp") + if err != nil { + return nil, err + } + clientDown, serverDown, err := spliceTestSocketPair("tcp") + if err != nil { + clientUp.Close() + serverUp.Close() + return nil, err + } + return &spliceTestServer{clientUp, clientDown, serverUp, serverDown}, nil +} + +// Read reads from the downstream connection. +func (srv *spliceTestServer) Read(b []byte) (int, error) { + return srv.clientDown.Read(b) +} + +// Write writes to the upstream connection. +func (srv *spliceTestServer) Write(b []byte) (int, error) { + return srv.clientUp.Write(b) +} + +// Close closes the server. +func (srv *spliceTestServer) Close() error { + err := srv.closeUp() + err1 := srv.closeDown() + if err == nil { + return err1 + } + return err +} + +// CloseWrite closes the client side of the upstream connection. +func (srv *spliceTestServer) CloseWrite() error { + return srv.clientUp.Close() +} + +// CloseRead closes the client side of the downstream connection. +func (srv *spliceTestServer) CloseRead() error { + return srv.clientDown.Close() +} + +// Copy copies from the server side of the upstream connection +// to the server side of the downstream connection, in a separate +// goroutine. Copy is done when the first send on the returned +// channel succeeds. +func (srv *spliceTestServer) Copy() <-chan error { + ch := make(chan error) + go func() { + _, err := io.Copy(srv.serverDown, srv.serverUp) + ch <- err + close(ch) + }() + return ch +} + +// CopyNoSplice is like Copy, but ensures that the splice code path +// is not reached. +func (srv *spliceTestServer) CopyNoSplice() <-chan error { + type onlyReader struct { + io.Reader + } + ch := make(chan error) + go func() { + _, err := io.Copy(srv.serverDown, onlyReader{srv.serverUp}) + ch <- err + close(ch) + }() + return ch +} + +func (srv *spliceTestServer) closeUp() error { + var err, err1 error + if srv.serverUp != nil { + err = srv.serverUp.Close() + } + if srv.clientUp != nil { + err1 = srv.clientUp.Close() + } + if err == nil { + return err1 + } + return err +} + +func (srv *spliceTestServer) closeDown() error { + var err, err1 error + if srv.serverDown != nil { + err = srv.serverDown.Close() + } + if srv.clientDown != nil { + err1 = srv.clientDown.Close() + } + if err == nil { + return err1 + } + return err +} + +func spliceTestSocketPair(net string) (client, server Conn, err error) { + ln, err := newLocalListener(net) + if err != nil { + return nil, nil, err + } + defer ln.Close() + var cerr, serr error + acceptDone := make(chan struct{}) + go func() { + server, serr = ln.Accept() + acceptDone <- struct{}{} + }() + client, cerr = Dial(ln.Addr().Network(), ln.Addr().String()) + <-acceptDone + if cerr != nil { + if server != nil { + server.Close() + } + return nil, nil, cerr + } + if serr != nil { + if client != nil { + client.Close() + } + return nil, nil, serr + } + return client, server, nil +} diff --git a/libgo/go/net/sys_cloexec.go b/libgo/go/net/sys_cloexec.go index def05cb..e97fb21 100644 --- a/libgo/go/net/sys_cloexec.go +++ b/libgo/go/net/sys_cloexec.go @@ -5,7 +5,7 @@ // This file implements sysSocket and accept for platforms that do not // provide a fast path for setting SetNonblock and CloseOnExec. -// +build aix darwin nacl netbsd openbsd solaris +// +build aix darwin nacl solaris package net diff --git a/libgo/go/net/tcpsock.go b/libgo/go/net/tcpsock.go index 9528140..db5d1f8 100644 --- a/libgo/go/net/tcpsock.go +++ b/libgo/go/net/tcpsock.go @@ -12,8 +12,8 @@ import ( "time" ) -// BUG(mikio): On Windows, the File method of TCPListener is not -// implemented. +// BUG(mikio): On JS, NaCl and Windows, the File method of TCPConn and +// TCPListener is not implemented. // TCPAddr represents the address of a TCP end point. type TCPAddr struct { @@ -212,7 +212,8 @@ func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error) { if raddr == nil { return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress} } - c, err := dialTCP(context.Background(), network, laddr, raddr) + sd := &sysDialer{network: network, address: raddr.String()} + c, err := sd.dialTCP(context.Background(), laddr, raddr) if err != nil { return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err} } @@ -292,8 +293,8 @@ func (l *TCPListener) SetDeadline(t time.Time) error { return nil } -// File returns a copy of the underlying os.File, set to blocking -// mode. It is the caller's responsibility to close f when finished. +// File returns a copy of the underlying os.File. +// It is the caller's responsibility to close f when finished. // Closing l does not affect f, and closing f does not affect l. // // The returned os.File's file descriptor is different from the @@ -328,7 +329,8 @@ func ListenTCP(network string, laddr *TCPAddr) (*TCPListener, error) { if laddr == nil { laddr = &TCPAddr{} } - ln, err := listenTCP(context.Background(), network, laddr) + sl := &sysListener{network: network, address: laddr.String()} + ln, err := sl.listenTCP(context.Background(), laddr) if err != nil { return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err} } diff --git a/libgo/go/net/tcpsock_plan9.go b/libgo/go/net/tcpsock_plan9.go index e37f065..f70ef6f 100644 --- a/libgo/go/net/tcpsock_plan9.go +++ b/libgo/go/net/tcpsock_plan9.go @@ -14,23 +14,23 @@ func (c *TCPConn) readFrom(r io.Reader) (int64, error) { return genericReadFrom(c, r) } -func dialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) { +func (sd *sysDialer) dialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) { if testHookDialTCP != nil { - return testHookDialTCP(ctx, net, laddr, raddr) + return testHookDialTCP(ctx, sd.network, laddr, raddr) } - return doDialTCP(ctx, net, laddr, raddr) + return sd.doDialTCP(ctx, laddr, raddr) } -func doDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) { - switch net { +func (sd *sysDialer) doDialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) { + switch sd.network { case "tcp", "tcp4", "tcp6": default: - return nil, UnknownNetworkError(net) + return nil, UnknownNetworkError(sd.network) } if raddr == nil { return nil, errMissingAddress } - fd, err := dialPlan9(ctx, net, laddr, raddr) + fd, err := dialPlan9(ctx, sd.network, laddr, raddr) if err != nil { return nil, err } @@ -69,8 +69,8 @@ func (ln *TCPListener) file() (*os.File, error) { return f, nil } -func listenTCP(ctx context.Context, network string, laddr *TCPAddr) (*TCPListener, error) { - fd, err := listenPlan9(ctx, network, laddr) +func (sl *sysListener) listenTCP(ctx context.Context, laddr *TCPAddr) (*TCPListener, error) { + fd, err := listenPlan9(ctx, sl.network, laddr) if err != nil { return nil, err } diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go index 9ba199d..64e71bf 100644 --- a/libgo/go/net/tcpsock_posix.go +++ b/libgo/go/net/tcpsock_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package net @@ -45,21 +45,24 @@ func (a *TCPAddr) toLocal(net string) sockaddr { } func (c *TCPConn) readFrom(r io.Reader) (int64, error) { + if n, err, handled := splice(c.fd, r); handled { + return n, err + } if n, err, handled := sendFile(c.fd, r); handled { return n, err } return genericReadFrom(c, r) } -func dialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) { +func (sd *sysDialer) dialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) { if testHookDialTCP != nil { - return testHookDialTCP(ctx, net, laddr, raddr) + return testHookDialTCP(ctx, sd.network, laddr, raddr) } - return doDialTCP(ctx, net, laddr, raddr) + return sd.doDialTCP(ctx, laddr, raddr) } -func doDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) { - fd, err := internetSocket(ctx, net, laddr, raddr, syscall.SOCK_STREAM, 0, "dial") +func (sd *sysDialer) doDialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) { + fd, err := internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", sd.Dialer.Control) // TCP has a rarely used mechanism called a 'simultaneous connection' in // which Dial("tcp", addr1, addr2) run on the machine at addr1 can @@ -77,7 +80,7 @@ func doDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn // close the fd and try again. If it happens twice more, we relent and // use the result. See also: // https://golang.org/issue/2690 - // http://stackoverflow.com/questions/4949858/ + // https://stackoverflow.com/questions/4949858/ // // The opposite can also happen: if we ask the kernel to pick an appropriate // originating local address, sometimes it picks one that is already in use. @@ -89,7 +92,7 @@ func doDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn if err == nil { fd.Close() } - fd, err = internetSocket(ctx, net, laddr, raddr, syscall.SOCK_STREAM, 0, "dial") + fd, err = internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", sd.Dialer.Control) } if err != nil { @@ -152,8 +155,8 @@ func (ln *TCPListener) file() (*os.File, error) { return f, nil } -func listenTCP(ctx context.Context, network string, laddr *TCPAddr) (*TCPListener, error) { - fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_STREAM, 0, "listen") +func (sl *sysListener) listenTCP(ctx context.Context, laddr *TCPAddr) (*TCPListener, error) { + fd, err := internetSocket(ctx, sl.network, laddr, nil, syscall.SOCK_STREAM, 0, "listen", sl.ListenConfig.Control) if err != nil { return nil, err } diff --git a/libgo/go/net/tcpsock_test.go b/libgo/go/net/tcpsock_test.go index 04b38b6..f8a775f 100644 --- a/libgo/go/net/tcpsock_test.go +++ b/libgo/go/net/tcpsock_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( diff --git a/libgo/go/net/tcpsock_unix_test.go b/libgo/go/net/tcpsock_unix_test.go index 95c02d2..2bd591b 100644 --- a/libgo/go/net/tcpsock_unix_test.go +++ b/libgo/go/net/tcpsock_unix_test.go @@ -2,13 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !plan9,!windows +// +build !js,!plan9,!windows package net import ( "context" - "internal/testenv" "math/rand" "runtime" "sync" @@ -84,9 +83,8 @@ func TestTCPSpuriousConnSetupCompletion(t *testing.T) { // Issue 19289. // Test that a canceled Dial does not cause a subsequent Dial to succeed. func TestTCPSpuriousConnSetupCompletionWithCancel(t *testing.T) { - if testenv.Builder() == "" { - testenv.MustHaveExternalNetwork(t) - } + mustHaveExternalNetwork(t) + defer dnsWaitGroup.Wait() t.Parallel() const tries = 10000 diff --git a/libgo/go/net/tcpsockopt_darwin.go b/libgo/go/net/tcpsockopt_darwin.go index 7415c76..5b738d2 100644 --- a/libgo/go/net/tcpsockopt_darwin.go +++ b/libgo/go/net/tcpsockopt_darwin.go @@ -16,9 +16,7 @@ func setKeepAlivePeriod(fd *netFD, d time.Duration) error { // The kernel expects seconds so round to next highest second. d += (time.Second - time.Nanosecond) secs := int(d.Seconds()) - switch err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, sysTCP_KEEPINTVL, secs); err { - case nil, syscall.ENOPROTOOPT: // OS X 10.7 and earlier don't support this option - default: + if err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, sysTCP_KEEPINTVL, secs); err != nil { return wrapSyscallError("setsockopt", err) } err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs) diff --git a/libgo/go/net/tcpsockopt_stub.go b/libgo/go/net/tcpsockopt_stub.go index 19c83e6..fd7f579 100644 --- a/libgo/go/net/tcpsockopt_stub.go +++ b/libgo/go/net/tcpsockopt_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build nacl +// +build nacl js,wasm package net diff --git a/libgo/go/net/textproto/reader.go b/libgo/go/net/textproto/reader.go index 8c3a052..feb464b 100644 --- a/libgo/go/net/textproto/reader.go +++ b/libgo/go/net/textproto/reader.go @@ -236,7 +236,7 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err err // with the same code followed by a space. Each line in message is // separated by a newline (\n). // -// See page 36 of RFC 959 (http://www.ietf.org/rfc/rfc959.txt) for +// See page 36 of RFC 959 (https://www.ietf.org/rfc/rfc959.txt) for // details of another form of response accepted: // // code-message line 1 diff --git a/libgo/go/net/textproto/reader_test.go b/libgo/go/net/textproto/reader_test.go index c6a6ced..4f37903 100644 --- a/libgo/go/net/textproto/reader_test.go +++ b/libgo/go/net/textproto/reader_test.go @@ -290,7 +290,7 @@ var readResponseTests = []readResponseTest{ }, } -// See http://www.ietf.org/rfc/rfc959.txt page 36. +// See https://www.ietf.org/rfc/rfc959.txt page 36. func TestRFC959Lines(t *testing.T) { for i, tt := range readResponseTests { r := reader(tt.in + "\nFOLLOWING DATA") diff --git a/libgo/go/net/timeout_test.go b/libgo/go/net/timeout_test.go index 9de7801..7c7d0c8 100644 --- a/libgo/go/net/timeout_test.go +++ b/libgo/go/net/timeout_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( @@ -482,7 +484,7 @@ func TestReadFromTimeout(t *testing.T) { time.Sleep(tt.timeout / 3) continue } - if n != 0 { + if nerr, ok := err.(Error); ok && nerr.Timeout() && n != 0 { t.Fatalf("#%d/%d: read %d; want 0", i, j, n) } break diff --git a/libgo/go/net/udpsock.go b/libgo/go/net/udpsock.go index 158265f..b234ed8 100644 --- a/libgo/go/net/udpsock.go +++ b/libgo/go/net/udpsock.go @@ -18,6 +18,9 @@ import ( // BUG(mikio): On NaCl, the ListenMulticastUDP function is not // implemented. +// BUG(mikio): On JS, methods and functions related to UDPConn are not +// implemented. + // UDPAddr represents the address of a UDP end point. type UDPAddr struct { IP IP @@ -208,7 +211,8 @@ func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error) { if raddr == nil { return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress} } - c, err := dialUDP(context.Background(), network, laddr, raddr) + sd := &sysDialer{network: network, address: raddr.String()} + c, err := sd.dialUDP(context.Background(), laddr, raddr) if err != nil { return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err} } @@ -233,7 +237,8 @@ func ListenUDP(network string, laddr *UDPAddr) (*UDPConn, error) { if laddr == nil { laddr = &UDPAddr{} } - c, err := listenUDP(context.Background(), network, laddr) + sl := &sysListener{network: network, address: laddr.String()} + c, err := sl.listenUDP(context.Background(), laddr) if err != nil { return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err} } @@ -266,7 +271,8 @@ func ListenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPCon if gaddr == nil || gaddr.IP == nil { return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr.opAddr(), Err: errMissingAddress} } - c, err := listenMulticastUDP(context.Background(), network, ifi, gaddr) + sl := &sysListener{network: network, address: gaddr.String()} + c, err := sl.listenMulticastUDP(context.Background(), ifi, gaddr) if err != nil { return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr.opAddr(), Err: err} } diff --git a/libgo/go/net/udpsock_plan9.go b/libgo/go/net/udpsock_plan9.go index 1ce7f88..563d943 100644 --- a/libgo/go/net/udpsock_plan9.go +++ b/libgo/go/net/udpsock_plan9.go @@ -55,8 +55,8 @@ func (c *UDPConn) writeMsg(b, oob []byte, addr *UDPAddr) (n, oobn int, err error return 0, 0, syscall.EPLAN9 } -func dialUDP(ctx context.Context, net string, laddr, raddr *UDPAddr) (*UDPConn, error) { - fd, err := dialPlan9(ctx, net, laddr, raddr) +func (sd *sysDialer) dialUDP(ctx context.Context, laddr, raddr *UDPAddr) (*UDPConn, error) { + fd, err := dialPlan9(ctx, sd.network, laddr, raddr) if err != nil { return nil, err } @@ -91,8 +91,8 @@ func unmarshalUDPHeader(b []byte) (*udpHeader, []byte) { return h, b } -func listenUDP(ctx context.Context, network string, laddr *UDPAddr) (*UDPConn, error) { - l, err := listenPlan9(ctx, network, laddr) +func (sl *sysListener) listenUDP(ctx context.Context, laddr *UDPAddr) (*UDPConn, error) { + l, err := listenPlan9(ctx, sl.network, laddr) if err != nil { return nil, err } @@ -108,8 +108,8 @@ func listenUDP(ctx context.Context, network string, laddr *UDPAddr) (*UDPConn, e return newUDPConn(fd), err } -func listenMulticastUDP(ctx context.Context, network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) { - l, err := listenPlan9(ctx, network, gaddr) +func (sl *sysListener) listenMulticastUDP(ctx context.Context, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) { + l, err := listenPlan9(ctx, sl.network, gaddr) if err != nil { return nil, err } diff --git a/libgo/go/net/udpsock_posix.go b/libgo/go/net/udpsock_posix.go index fe552ba..611fe51 100644 --- a/libgo/go/net/udpsock_posix.go +++ b/libgo/go/net/udpsock_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package net @@ -94,24 +94,24 @@ func (c *UDPConn) writeMsg(b, oob []byte, addr *UDPAddr) (n, oobn int, err error return c.fd.writeMsg(b, oob, sa) } -func dialUDP(ctx context.Context, net string, laddr, raddr *UDPAddr) (*UDPConn, error) { - fd, err := internetSocket(ctx, net, laddr, raddr, syscall.SOCK_DGRAM, 0, "dial") +func (sd *sysDialer) dialUDP(ctx context.Context, laddr, raddr *UDPAddr) (*UDPConn, error) { + fd, err := internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_DGRAM, 0, "dial", sd.Dialer.Control) if err != nil { return nil, err } return newUDPConn(fd), nil } -func listenUDP(ctx context.Context, network string, laddr *UDPAddr) (*UDPConn, error) { - fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_DGRAM, 0, "listen") +func (sl *sysListener) listenUDP(ctx context.Context, laddr *UDPAddr) (*UDPConn, error) { + fd, err := internetSocket(ctx, sl.network, laddr, nil, syscall.SOCK_DGRAM, 0, "listen", sl.ListenConfig.Control) if err != nil { return nil, err } return newUDPConn(fd), nil } -func listenMulticastUDP(ctx context.Context, network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) { - fd, err := internetSocket(ctx, network, gaddr, nil, syscall.SOCK_DGRAM, 0, "listen") +func (sl *sysListener) listenMulticastUDP(ctx context.Context, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) { + fd, err := internetSocket(ctx, sl.network, gaddr, nil, syscall.SOCK_DGRAM, 0, "listen", sl.ListenConfig.Control) if err != nil { return nil, err } diff --git a/libgo/go/net/udpsock_test.go b/libgo/go/net/udpsock_test.go index 769576c..4940644 100644 --- a/libgo/go/net/udpsock_test.go +++ b/libgo/go/net/udpsock_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( @@ -163,11 +165,6 @@ func testWriteToConn(t *testing.T, raddr string) { switch runtime.GOOS { case "nacl": // see golang.org/issue/9252 t.Skipf("not implemented yet on %s", runtime.GOOS) - case "windows": - if testenv.IsWindowsXP() { - t.Log("skipping broken test on Windows XP (see golang.org/issue/23072)") - return - } default: if err != nil { t.Fatal(err) @@ -211,11 +208,6 @@ func testWriteToPacketConn(t *testing.T, raddr string) { switch runtime.GOOS { case "nacl": // see golang.org/issue/9252 t.Skipf("not implemented yet on %s", runtime.GOOS) - case "windows": - if testenv.IsWindowsXP() { - t.Log("skipping broken test on Windows XP (see golang.org/issue/23072)") - return - } default: if err != nil { t.Fatal(err) @@ -408,9 +400,56 @@ func TestUDPZeroByteBuffer(t *testing.T) { switch err { case nil: // ReadFrom succeeds default: // Read may timeout, it depends on the platform - if nerr, ok := err.(Error); (!ok || !nerr.Timeout()) && runtime.GOOS != "windows" { // Windows returns WSAEMSGSIZ + if nerr, ok := err.(Error); (!ok || !nerr.Timeout()) && runtime.GOOS != "windows" { // Windows returns WSAEMSGSIZE + t.Fatal(err) + } + } + } +} + +func TestUDPReadSizeError(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9": + t.Skipf("not supported on %s", runtime.GOOS) + } + + c1, err := newLocalPacketListener("udp") + if err != nil { + t.Fatal(err) + } + defer c1.Close() + + c2, err := Dial("udp", c1.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + defer c2.Close() + + b1 := []byte("READ SIZE ERROR TEST") + for _, genericRead := range []bool{false, true} { + n, err := c2.Write(b1) + if err != nil { + t.Fatal(err) + } + if n != len(b1) { + t.Errorf("got %d; want %d", n, len(b1)) + } + c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) + b2 := make([]byte, len(b1)-1) + if genericRead { + n, err = c1.(Conn).Read(b2) + } else { + n, _, err = c1.ReadFrom(b2) + } + switch err { + case nil: // ReadFrom succeeds + default: // Read may timeout, it depends on the platform + if nerr, ok := err.(Error); (!ok || !nerr.Timeout()) && runtime.GOOS != "windows" { // Windows returns WSAEMSGSIZE t.Fatal(err) } } + if n != len(b1)-1 { + t.Fatalf("got %d; want %d", n, len(b1)-1) + } } } diff --git a/libgo/go/net/unixsock.go b/libgo/go/net/unixsock.go index 20326da..3ae62f6 100644 --- a/libgo/go/net/unixsock.go +++ b/libgo/go/net/unixsock.go @@ -12,6 +12,9 @@ import ( "time" ) +// BUG(mikio): On JS, NaCl, Plan 9 and Windows, methods and functions +// related to UnixConn and UnixListener are not implemented. + // UnixAddr represents the address of a Unix domain socket end point. type UnixAddr struct { Name string @@ -200,7 +203,8 @@ func DialUnix(network string, laddr, raddr *UnixAddr) (*UnixConn, error) { default: return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: UnknownNetworkError(network)} } - c, err := dialUnix(context.Background(), network, laddr, raddr) + sd := &sysDialer{network: network, address: raddr.String()} + c, err := sd.dialUnix(context.Background(), laddr, raddr) if err != nil { return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err} } @@ -286,8 +290,8 @@ func (l *UnixListener) SetDeadline(t time.Time) error { return nil } -// File returns a copy of the underlying os.File, set to blocking -// mode. It is the caller's responsibility to close f when finished. +// File returns a copy of the underlying os.File. +// It is the caller's responsibility to close f when finished. // Closing l does not affect f, and closing f does not affect l. // // The returned os.File's file descriptor is different from the @@ -316,7 +320,8 @@ func ListenUnix(network string, laddr *UnixAddr) (*UnixListener, error) { if laddr == nil { return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: errMissingAddress} } - ln, err := listenUnix(context.Background(), network, laddr) + sl := &sysListener{network: network, address: laddr.String()} + ln, err := sl.listenUnix(context.Background(), laddr) if err != nil { return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err} } @@ -335,7 +340,8 @@ func ListenUnixgram(network string, laddr *UnixAddr) (*UnixConn, error) { if laddr == nil { return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: errMissingAddress} } - c, err := listenUnixgram(context.Background(), network, laddr) + sl := &sysListener{network: network, address: laddr.String()} + c, err := sl.listenUnixgram(context.Background(), laddr) if err != nil { return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err} } diff --git a/libgo/go/net/unixsock_plan9.go b/libgo/go/net/unixsock_plan9.go index e70eb21..6ebd4d7 100644 --- a/libgo/go/net/unixsock_plan9.go +++ b/libgo/go/net/unixsock_plan9.go @@ -26,7 +26,7 @@ func (c *UnixConn) writeMsg(b, oob []byte, addr *UnixAddr) (n, oobn int, err err return 0, 0, syscall.EPLAN9 } -func dialUnix(ctx context.Context, network string, laddr, raddr *UnixAddr) (*UnixConn, error) { +func (sd *sysDialer) dialUnix(ctx context.Context, laddr, raddr *UnixAddr) (*UnixConn, error) { return nil, syscall.EPLAN9 } @@ -42,10 +42,10 @@ func (ln *UnixListener) file() (*os.File, error) { return nil, syscall.EPLAN9 } -func listenUnix(ctx context.Context, network string, laddr *UnixAddr) (*UnixListener, error) { +func (sl *sysListener) listenUnix(ctx context.Context, laddr *UnixAddr) (*UnixListener, error) { return nil, syscall.EPLAN9 } -func listenUnixgram(ctx context.Context, network string, laddr *UnixAddr) (*UnixConn, error) { +func (sl *sysListener) listenUnixgram(ctx context.Context, laddr *UnixAddr) (*UnixConn, error) { return nil, syscall.EPLAN9 } diff --git a/libgo/go/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go index 945aa03..74f5cc2 100644 --- a/libgo/go/net/unixsock_posix.go +++ b/libgo/go/net/unixsock_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package net @@ -13,7 +13,7 @@ import ( "syscall" ) -func unixSocket(ctx context.Context, net string, laddr, raddr sockaddr, mode string) (*netFD, error) { +func unixSocket(ctx context.Context, net string, laddr, raddr sockaddr, mode string, ctrlFn func(string, string, syscall.RawConn) error) (*netFD, error) { var sotype int switch net { case "unix": @@ -42,7 +42,7 @@ func unixSocket(ctx context.Context, net string, laddr, raddr sockaddr, mode str return nil, errors.New("unknown mode: " + mode) } - fd, err := socket(ctx, net, syscall.AF_UNIX, sotype, 0, false, laddr, raddr) + fd, err := socket(ctx, net, syscall.AF_UNIX, sotype, 0, false, laddr, raddr, ctrlFn) if err != nil { return nil, err } @@ -150,8 +150,8 @@ func (c *UnixConn) writeMsg(b, oob []byte, addr *UnixAddr) (n, oobn int, err err return c.fd.writeMsg(b, oob, sa) } -func dialUnix(ctx context.Context, net string, laddr, raddr *UnixAddr) (*UnixConn, error) { - fd, err := unixSocket(ctx, net, laddr, raddr, "dial") +func (sd *sysDialer) dialUnix(ctx context.Context, laddr, raddr *UnixAddr) (*UnixConn, error) { + fd, err := unixSocket(ctx, sd.network, laddr, raddr, "dial", sd.Dialer.Control) if err != nil { return nil, err } @@ -206,16 +206,16 @@ func (l *UnixListener) SetUnlinkOnClose(unlink bool) { l.unlink = unlink } -func listenUnix(ctx context.Context, network string, laddr *UnixAddr) (*UnixListener, error) { - fd, err := unixSocket(ctx, network, laddr, nil, "listen") +func (sl *sysListener) listenUnix(ctx context.Context, laddr *UnixAddr) (*UnixListener, error) { + fd, err := unixSocket(ctx, sl.network, laddr, nil, "listen", sl.ListenConfig.Control) if err != nil { return nil, err } return &UnixListener{fd: fd, path: fd.laddr.String(), unlink: true}, nil } -func listenUnixgram(ctx context.Context, network string, laddr *UnixAddr) (*UnixConn, error) { - fd, err := unixSocket(ctx, network, laddr, nil, "listen") +func (sl *sysListener) listenUnixgram(ctx context.Context, laddr *UnixAddr) (*UnixConn, error) { + fd, err := unixSocket(ctx, sl.network, laddr, nil, "listen", sl.ListenConfig.Control) if err != nil { return nil, err } diff --git a/libgo/go/net/unixsock_test.go b/libgo/go/net/unixsock_test.go index 3e5c8bc..4828990 100644 --- a/libgo/go/net/unixsock_test.go +++ b/libgo/go/net/unixsock_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !nacl,!plan9,!windows +// +build !js,!nacl,!plan9,!windows package net diff --git a/libgo/go/net/url/url.go b/libgo/go/net/url/url.go index 3e12179..80eb7a8 100644 --- a/libgo/go/net/url/url.go +++ b/libgo/go/net/url/url.go @@ -11,7 +11,6 @@ package url // contain references to issue numbers with details. import ( - "bytes" "errors" "fmt" "sort" @@ -159,13 +158,26 @@ func shouldEscape(c byte, mode encoding) bool { } } + if mode == encodeFragment { + // RFC 3986 §2.2 allows not escaping sub-delims. A subset of sub-delims are + // included in reserved from RFC 2396 §2.2. The remaining sub-delims do not + // need to be escaped. To minimize potential breakage, we apply two restrictions: + // (1) we always escape sub-delims outside of the fragment, and (2) we always + // escape single quote to avoid breaking callers that had previously assumed that + // single quotes would be escaped. See issue #19917. + switch c { + case '!', '(', ')', '*': + return false + } + } + // Everything else must be escaped. return true } // QueryUnescape does the inverse transformation of QueryEscape, // converting each 3-byte encoded substring of the form "%AB" into the -// hex-decoded byte 0xAB. It also converts '+' into ' ' (space). +// hex-decoded byte 0xAB. // It returns an error if any % is not followed by two hexadecimal // digits. func QueryUnescape(s string) (string, error) { @@ -174,9 +186,8 @@ func QueryUnescape(s string) (string, error) { // PathUnescape does the inverse transformation of PathEscape, // converting each 3-byte encoded substring of the form "%AB" into the -// hex-decoded byte 0xAB. It also converts '+' into ' ' (space). -// It returns an error if any % is not followed by two hexadecimal -// digits. +// hex-decoded byte 0xAB. It returns an error if any % is not followed +// by two hexadecimal digits. // // PathUnescape is identical to QueryUnescape except that it does not // unescape '+' to ' ' (space). @@ -738,7 +749,7 @@ func validOptionalPort(port string) bool { // - if u.RawQuery is empty, ?query is omitted. // - if u.Fragment is empty, #fragment is omitted. func (u *URL) String() string { - var buf bytes.Buffer + var buf strings.Builder if u.Scheme != "" { buf.WriteString(u.Scheme) buf.WriteByte(':') @@ -879,7 +890,7 @@ func (v Values) Encode() string { if v == nil { return "" } - var buf bytes.Buffer + var buf strings.Builder keys := make([]string, 0, len(v)) for k := range v { keys = append(keys, k) @@ -887,12 +898,13 @@ func (v Values) Encode() string { sort.Strings(keys) for _, k := range keys { vs := v[k] - prefix := QueryEscape(k) + "=" + keyEscaped := QueryEscape(k) for _, v := range vs { if buf.Len() > 0 { buf.WriteByte('&') } - buf.WriteString(prefix) + buf.WriteString(keyEscaped) + buf.WriteByte('=') buf.WriteString(QueryEscape(v)) } } @@ -953,7 +965,7 @@ func (u *URL) Parse(ref string) (*URL, error) { } // ResolveReference resolves a URI reference to an absolute URI from -// an absolute base URI, per RFC 3986 Section 5.2. The URI reference +// an absolute base URI u, per RFC 3986 Section 5.2. The URI reference // may be relative or absolute. ResolveReference always returns a new // URL instance, even if the returned URL is identical to either the // base or reference. If ref is an absolute URL, then ResolveReference diff --git a/libgo/go/net/url/url_test.go b/libgo/go/net/url/url_test.go index f2d311a..9043a84 100644 --- a/libgo/go/net/url/url_test.go +++ b/libgo/go/net/url/url_test.go @@ -1075,6 +1075,7 @@ var resolveReferenceTests = []struct { // Fragment {"http://foo.com/bar", ".#frag", "http://foo.com/#frag"}, + {"http://example.org/", "#!$&%27()*+,;=", "http://example.org/#!$&%27()*+,;="}, // Paths with escaping (issue 16947). {"http://foo.com/foo%2fbar/", "../baz", "http://foo.com/baz"}, @@ -1433,8 +1434,8 @@ func TestParseErrors(t *testing.T) { {"mysql://x@y(1.2.3.4:123)/foo", false}, {"http://[]%20%48%54%54%50%2f%31%2e%31%0a%4d%79%48%65%61%64%65%72%3a%20%31%32%33%0a%0a/", true}, // golang.org/issue/11208 - {"http://a b.com/", true}, // no space in host name please - {"cache_object://foo", true}, // scheme cannot have _, relative path cannot have : in first segment + {"http://a b.com/", true}, // no space in host name please + {"cache_object://foo", true}, // scheme cannot have _, relative path cannot have : in first segment {"cache_object:foo", true}, {"cache_object:foo/bar", true}, {"cache_object/:foo/bar", false}, diff --git a/libgo/go/net/writev_test.go b/libgo/go/net/writev_test.go index 4c05be4..c43be84 100644 --- a/libgo/go/net/writev_test.go +++ b/libgo/go/net/writev_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !js + package net import ( diff --git a/libgo/go/os/dir_unix.go b/libgo/go/os/dir_unix.go index 2dc6a89..edfc9ea 100644 --- a/libgo/go/os/dir_unix.go +++ b/libgo/go/os/dir_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package os diff --git a/libgo/go/os/env.go b/libgo/go/os/env.go index 4e0171f..330297b 100644 --- a/libgo/go/os/env.go +++ b/libgo/go/os/env.go @@ -14,18 +14,33 @@ import ( // Expand replaces ${var} or $var in the string based on the mapping function. // For example, os.ExpandEnv(s) is equivalent to os.Expand(s, os.Getenv). func Expand(s string, mapping func(string) string) string { - buf := make([]byte, 0, 2*len(s)) + var buf []byte // ${} is all ASCII, so bytes are fine for this operation. i := 0 for j := 0; j < len(s); j++ { if s[j] == '$' && j+1 < len(s) { + if buf == nil { + buf = make([]byte, 0, 2*len(s)) + } buf = append(buf, s[i:j]...) name, w := getShellName(s[j+1:]) - buf = append(buf, mapping(name)...) + if name == "" && w > 0 { + // Encountered invalid syntax; eat the + // characters. + } else if name == "" { + // Valid syntax, but $ was not followed by a + // name. Leave the dollar character untouched. + buf = append(buf, s[j]) + } else { + buf = append(buf, mapping(name)...) + } j += w i = j + 1 } } + if buf == nil { + return s + } return string(buf) + s[i:] } @@ -63,10 +78,13 @@ func getShellName(s string) (string, int) { // Scan to closing brace for i := 1; i < len(s); i++ { if s[i] == '}' { + if i == 1 { + return "", 2 // Bad syntax; eat "${}" + } return s[1:i], i + 1 } } - return "", 1 // Bad syntax; just eat the brace. + return "", 1 // Bad syntax; eat "${" case isShellSpecialVar(s[0]): return s[0:1], 1 } diff --git a/libgo/go/os/env_test.go b/libgo/go/os/env_test.go index 16f1945..4b86015 100644 --- a/libgo/go/os/env_test.go +++ b/libgo/go/os/env_test.go @@ -49,6 +49,12 @@ var expandTests = []struct { {"${HOME}", "/usr/gopher"}, {"${H}OME", "(Value of H)OME"}, {"A$$$#$1$H$home_1*B", "APIDNARGSARGUMENT1(Value of H)/usr/foo*B"}, + {"start$+middle$^end$", "start$+middle$^end$"}, + {"mixed$|bag$$$", "mixed$|bagPID$"}, + {"$", "$"}, + {"$}", "$}"}, + {"${", ""}, // invalid syntax; eat up the characters + {"${}", ""}, // invalid syntax; eat up the characters } func TestExpand(t *testing.T) { @@ -60,6 +66,27 @@ func TestExpand(t *testing.T) { } } +var global interface{} + +func BenchmarkExpand(b *testing.B) { + b.Run("noop", func(b *testing.B) { + var s string + b.ReportAllocs() + for i := 0; i < b.N; i++ { + s = Expand("tick tick tick tick", func(string) string { return "" }) + } + global = s + }) + b.Run("multiple", func(b *testing.B) { + var s string + b.ReportAllocs() + for i := 0; i < b.N; i++ { + s = Expand("$a $a $a $a", func(string) string { return "boom" }) + } + global = s + }) +} + func TestConsistentEnviron(t *testing.T) { e0 := Environ() for i := 0; i < 10; i++ { @@ -103,7 +130,7 @@ func TestClearenv(t *testing.T) { defer func(origEnv []string) { for _, pair := range origEnv { // Environment variables on Windows can begin with = - // http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx + // https://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx i := strings.Index(pair[1:], "=") + 1 if err := Setenv(pair[:i], pair[i+1:]); err != nil { t.Errorf("Setenv(%q, %q) failed during reset: %v", pair[:i], pair[i+1:], err) diff --git a/libgo/go/os/error_posix.go b/libgo/go/os/error_posix.go index 2049e44..3c81b41 100644 --- a/libgo/go/os/error_posix.go +++ b/libgo/go/os/error_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package os diff --git a/libgo/go/os/error_unix.go b/libgo/go/os/error_unix.go index 2349851..bb6bbcc 100644 --- a/libgo/go/os/error_unix.go +++ b/libgo/go/os/error_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package os diff --git a/libgo/go/os/error_unix_test.go b/libgo/go/os/error_unix_test.go index 76fe015..8db9867 100644 --- a/libgo/go/os/error_unix_test.go +++ b/libgo/go/os/error_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package os_test diff --git a/libgo/go/os/example_test.go b/libgo/go/os/example_test.go index 5749194..e21415a 100644 --- a/libgo/go/os/example_test.go +++ b/libgo/go/os/example_test.go @@ -82,6 +82,24 @@ func init() { os.Unsetenv("GOPATH") } +func ExampleExpand() { + mapper := func(placeholderName string) string { + switch placeholderName { + case "DAY_PART": + return "morning" + case "USER": + return "Gopher" + } + + return "" + } + + fmt.Println(os.Expand("Good ${DAY_PART}, $USER!", mapper)) + + // Output: + // Good morning, Gopher! +} + func ExampleExpandEnv() { fmt.Println(os.ExpandEnv("$USER lives in ${HOME}.")) diff --git a/libgo/go/os/exec.go b/libgo/go/os/exec.go index a7f8710..cab6a73 100644 --- a/libgo/go/os/exec.go +++ b/libgo/go/os/exec.go @@ -109,7 +109,9 @@ func (p *Process) Release() error { return p.release() } -// Kill causes the Process to exit immediately. +// Kill causes the Process to exit immediately. Kill does not wait until +// the Process has actually exited. This only kills the Process itself, +// not any other processes it may have started. func (p *Process) Kill() error { return p.kill() } diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go index 5ef9540..88b0a91 100644 --- a/libgo/go/os/exec/exec.go +++ b/libgo/go/os/exec/exec.go @@ -34,11 +34,13 @@ import ( "syscall" ) -// Error records the name of a binary that failed to be executed -// and the reason it failed. +// Error is returned by LookPath when it fails to classify a file as an +// executable. type Error struct { + // Name is the file name for which the error occurred. Name string - Err error + // Err is the underlying error. + Err error } func (e *Error) Error() string { @@ -111,6 +113,8 @@ type Cmd struct { // ExtraFiles specifies additional open files to be inherited by the // new process. It does not include standard input, standard output, or // standard error. If non-nil, entry i becomes file descriptor 3+i. + // + // ExtraFiles is not supported on Windows. ExtraFiles []*os.File // SysProcAttr holds optional, operating system-specific attributes. diff --git a/libgo/go/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go index aa33570f..1e38285 100644 --- a/libgo/go/os/exec/exec_test.go +++ b/libgo/go/os/exec/exec_test.go @@ -146,11 +146,11 @@ func TestCatGoodAndBadFile(t *testing.T) { } } -func TestNoExistBinary(t *testing.T) { - // Can't run a non-existent binary - err := exec.Command("/no-exist-binary").Run() +func TestNoExistExecutable(t *testing.T) { + // Can't run a non-existent executable + err := exec.Command("/no-exist-executable").Run() if err == nil { - t.Error("expected error from /no-exist-binary") + t.Error("expected error from /no-exist-executable") } } @@ -338,7 +338,7 @@ func TestPipeLookPathLeak(t *testing.T) { } for i := 0; i < 6; i++ { - cmd := exec.Command("something-that-does-not-exist-binary") + cmd := exec.Command("something-that-does-not-exist-executable") cmd.StdoutPipe() cmd.StderrPipe() cmd.StdinPipe() @@ -408,6 +408,12 @@ var testedAlreadyLeaked = false // stdin, stdout, stderr, epoll/kqueue, maybe testlog func basefds() uintptr { n := os.Stderr.Fd() + 1 + // The poll (epoll/kqueue) descriptor can be numerically + // either between stderr and the testlog-fd, or after + // testlog-fd. + if poll.PollDescriptor() == n { + n++ + } for _, arg := range os.Args { if strings.HasPrefix(arg, "-test.testlogfile=") { n++ @@ -1009,9 +1015,6 @@ func TestContext(t *testing.T) { } func TestContextCancel(t *testing.T) { - if testenv.Builder() == "windows-386-xp" { - t.Skipf("known to fail on Windows XP. Issue 17245") - } ctx, cancel := context.WithCancel(context.Background()) defer cancel() c := helperCommandContext(t, ctx, "cat") diff --git a/libgo/go/os/exec/lp_js.go b/libgo/go/os/exec/lp_js.go new file mode 100644 index 0000000..6750fb9 --- /dev/null +++ b/libgo/go/os/exec/lp_js.go @@ -0,0 +1,23 @@ +// Copyright 2018 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 js,wasm + +package exec + +import ( + "errors" +) + +// ErrNotFound is the error resulting if a path search failed to find an executable file. +var ErrNotFound = errors.New("executable file not found in $PATH") + +// LookPath searches for an executable named file in the +// directories named by the PATH environment variable. +// If file contains a slash, it is tried directly and the PATH is not consulted. +// The result may be an absolute path or a path relative to the current directory. +func LookPath(file string) (string, error) { + // Wasm can not execute processes, so act as if there are no executables at all. + return "", &Error{file, ErrNotFound} +} diff --git a/libgo/go/os/exec/lp_plan9.go b/libgo/go/os/exec/lp_plan9.go index 142f87e..5860cbc 100644 --- a/libgo/go/os/exec/lp_plan9.go +++ b/libgo/go/os/exec/lp_plan9.go @@ -25,8 +25,8 @@ func findExecutable(file string) error { return os.ErrPermission } -// LookPath searches for an executable binary named file -// in the directories named by the path environment variable. +// LookPath searches for an executable named file in the +// directories named by the path environment variable. // If file begins with "/", "#", "./", or "../", it is tried // directly and the path is not consulted. // The result may be an absolute path or a path relative to the current directory. diff --git a/libgo/go/os/exec/lp_unix.go b/libgo/go/os/exec/lp_unix.go index 20ce7a4..799e0b4 100644 --- a/libgo/go/os/exec/lp_unix.go +++ b/libgo/go/os/exec/lp_unix.go @@ -27,8 +27,8 @@ func findExecutable(file string) error { return os.ErrPermission } -// LookPath searches for an executable binary named file -// in the directories named by the PATH environment variable. +// LookPath searches for an executable named file in the +// directories named by the PATH environment variable. // If file contains a slash, it is tried directly and the PATH is not consulted. // The result may be an absolute path or a path relative to the current directory. func LookPath(file string) (string, error) { diff --git a/libgo/go/os/exec/lp_windows.go b/libgo/go/os/exec/lp_windows.go index 793d4d9..9ea3d76 100644 --- a/libgo/go/os/exec/lp_windows.go +++ b/libgo/go/os/exec/lp_windows.go @@ -50,8 +50,8 @@ func findExecutable(file string, exts []string) (string, error) { return "", os.ErrNotExist } -// LookPath searches for an executable binary named file -// in the directories named by the PATH environment variable. +// LookPath searches for an executable named file in the +// directories named by the PATH environment variable. // If file contains a slash, it is tried directly and the PATH is not consulted. // LookPath also uses PATHEXT environment variable to match // a suitable candidate. diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go index 056f139..dbbb5df 100644 --- a/libgo/go/os/exec_posix.go +++ b/libgo/go/os/exec_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package os diff --git a/libgo/go/os/exec_unix.go b/libgo/go/os/exec_unix.go index d6433bf..abae5a2 100644 --- a/libgo/go/os/exec_unix.go +++ b/libgo/go/os/exec_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package os diff --git a/libgo/go/os/executable_darwin.go b/libgo/go/os/executable_darwin.go index ce5b814..dae9f4e 100644 --- a/libgo/go/os/executable_darwin.go +++ b/libgo/go/os/executable_darwin.go @@ -4,12 +4,17 @@ package os +import "errors" + var executablePath string // set by ../runtime/os_darwin.go var initCwd, initCwdErr = Getwd() func executable() (string, error) { ep := executablePath + if len(ep) == 0 { + return ep, errors.New("cannot find executable path") + } if ep[0] != '/' { if initCwdErr != nil { return ep, initCwdErr diff --git a/libgo/go/os/executable_procfs.go b/libgo/go/os/executable_procfs.go index b5fae59..5bb63b9 100644 --- a/libgo/go/os/executable_procfs.go +++ b/libgo/go/os/executable_procfs.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build linux netbsd dragonfly nacl +// +build linux netbsd dragonfly nacl js,wasm package os diff --git a/libgo/go/os/executable_solaris.go b/libgo/go/os/executable_solaris.go index 80f9372..b145980 100644 --- a/libgo/go/os/executable_solaris.go +++ b/libgo/go/os/executable_solaris.go @@ -6,12 +6,17 @@ package os import "syscall" +var executablePath string // set by sysauxv in ../runtime/os3_solaris.go + var initCwd, initCwdErr = Getwd() func executable() (string, error) { - path, err := syscall.Getexecname() - if err != nil { - return path, err + path := executablePath + if len(path) == 0 { + path, err := syscall.Getexecname() + if err != nil { + return path, err + } } if len(path) > 0 && path[0] != '/' { if initCwdErr != nil { diff --git a/libgo/go/os/fifo_test.go b/libgo/go/os/fifo_test.go new file mode 100644 index 0000000..3041dcf --- /dev/null +++ b/libgo/go/os/fifo_test.go @@ -0,0 +1,112 @@ +// Copyright 2015 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 dragonfly freebsd linux netbsd openbsd + +package os_test + +import ( + "bufio" + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "sync" + "syscall" + "testing" + "time" +) + +// Issue 24164. +func TestFifoEOF(t *testing.T) { + switch runtime.GOOS { + case "android": + t.Skip("skipping on Android; mkfifo syscall not available") + case "openbsd": + // On OpenBSD 6.2 this test just hangs for some reason. + t.Skip("skipping on OpenBSD; issue 25877") + } + + dir, err := ioutil.TempDir("", "TestFifoEOF") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + fifoName := filepath.Join(dir, "fifo") + if err := syscall.Mkfifo(fifoName, 0600); err != nil { + t.Fatal(err) + } + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + + w, err := os.OpenFile(fifoName, os.O_WRONLY, 0) + if err != nil { + t.Error(err) + return + } + + defer func() { + if err := w.Close(); err != nil { + t.Errorf("error closing writer: %v", err) + } + }() + + for i := 0; i < 3; i++ { + time.Sleep(10 * time.Millisecond) + _, err := fmt.Fprintf(w, "line %d\n", i) + if err != nil { + t.Errorf("error writing to fifo: %v", err) + return + } + } + time.Sleep(10 * time.Millisecond) + }() + + defer wg.Wait() + + r, err := os.Open(fifoName) + if err != nil { + t.Fatal(err) + } + + done := make(chan bool) + go func() { + defer close(done) + + defer func() { + if err := r.Close(); err != nil { + t.Errorf("error closing reader: %v", err) + } + }() + + rbuf := bufio.NewReader(r) + for { + b, err := rbuf.ReadBytes('\n') + if err == io.EOF { + break + } + if err != nil { + t.Error(err) + return + } + t.Logf("%s\n", bytes.TrimSpace(b)) + } + }() + + select { + case <-done: + // Test succeeded. + case <-time.After(time.Second): + t.Error("timed out waiting for read") + // Close the reader to force the read to complete. + r.Close() + } +} diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go index c667421..cba70d7 100644 --- a/libgo/go/os/file.go +++ b/libgo/go/os/file.go @@ -41,6 +41,7 @@ import ( "internal/poll" "internal/testlog" "io" + "runtime" "syscall" "time" ) @@ -220,12 +221,26 @@ func Mkdir(name string, perm FileMode) error { // mkdir(2) itself won't handle the sticky bit on *BSD and Solaris if !supportsCreateWithStickyBit && perm&ModeSticky != 0 { - Chmod(name, perm) + e = setStickyBit(name) + + if e != nil { + Remove(name) + return e + } } return nil } +// setStickyBit adds ModeSticky to the permision bits of path, non atomic. +func setStickyBit(name string) error { + fi, err := Stat(name) + if err != nil { + return err + } + return Chmod(name, fi.Mode()|ModeSticky) +} + // Chdir changes the current working directory to the named directory. // If there is an error, it will be of type *PathError. func Chdir(dir string) error { @@ -315,6 +330,57 @@ func TempDir() string { return tempDir() } +// UserCacheDir returns the default root directory to use for user-specific +// cached data. Users should create their own application-specific subdirectory +// within this one and use that. +// +// On Unix systems, it returns $XDG_CACHE_HOME as specified by +// https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html if +// non-empty, else $HOME/.cache. +// On Darwin, it returns $HOME/Library/Caches. +// On Windows, it returns %LocalAppData%. +// On Plan 9, it returns $home/lib/cache. +// +// If the location cannot be determined (for example, $HOME is not defined), +// then it will return an error. +func UserCacheDir() (string, error) { + var dir string + + switch runtime.GOOS { + case "windows": + dir = Getenv("LocalAppData") + if dir == "" { + return "", errors.New("%LocalAppData% is not defined") + } + + case "darwin": + dir = Getenv("HOME") + if dir == "" { + return "", errors.New("$HOME is not defined") + } + dir += "/Library/Caches" + + case "plan9": + dir = Getenv("home") + if dir == "" { + return "", errors.New("$home is not defined") + } + dir += "/lib/cache" + + default: // Unix + dir = Getenv("XDG_CACHE_HOME") + if dir == "" { + dir = Getenv("HOME") + if dir == "" { + return "", errors.New("neither $XDG_CACHE_HOME nor $HOME are defined") + } + dir += "/.cache" + } + } + + return dir, nil +} + // 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. // If there is an error, it will be of type *PathError. diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go index 7e28178..2c74403 100644 --- a/libgo/go/os/file_plan9.go +++ b/libgo/go/os/file_plan9.go @@ -133,7 +133,8 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) { } // Close closes the File, rendering it unusable for I/O. -// It returns an error, if any. +// On files that support SetDeadline, any pending I/O operations will +// be canceled and return immediately with an error. func (f *File) Close() error { if err := f.checkValid("close"); err != nil { return err @@ -451,7 +452,11 @@ func Readlink(name string) (string, 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. +// A uid or gid of -1 means to not change that value. // If there is an error, it will be of type *PathError. +// +// On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or +// EPLAN9 error, wrapped in *PathError. func Chown(name string, uid, gid int) error { return &PathError{"chown", name, syscall.EPLAN9} } @@ -473,7 +478,12 @@ func (f *File) Chown(uid, gid int) error { } func tempDir() string { - return "/tmp" + dir := Getenv("TMPDIR") + if dir == "" { + dir = "/tmp" + } + return dir + } // Chdir changes the current working directory to the file, diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go index 67da384..9bd1cc7 100644 --- a/libgo/go/os/file_posix.go +++ b/libgo/go/os/file_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package os @@ -69,10 +69,11 @@ func (f *File) chmod(mode FileMode) 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. +// A uid or gid of -1 means to not change that value. // If there is an error, it will be of type *PathError. // -// On Windows, it always returns the syscall.EWINDOWS error, wrapped -// in *PathError. +// On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or +// EPLAN9 error, wrapped in *PathError. func Chown(name string, uid, gid int) error { if e := syscall.Chown(name, uid, gid); e != nil { return &PathError{"chown", name, e} diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go index 67d2ee1..b2aea33 100644 --- a/libgo/go/os/file_unix.go +++ b/libgo/go/os/file_unix.go @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package os import ( "internal/poll" + "internal/syscall/unix" "runtime" "syscall" ) @@ -74,9 +75,15 @@ func (f *File) Fd() uintptr { // NewFile returns a new File with the given file descriptor and // name. The returned value will be nil if fd is not a valid file -// descriptor. +// descriptor. On Unix systems, if the file descriptor is in +// non-blocking mode, NewFile will attempt to return a pollable File +// (one for which the SetDeadline methods work). func NewFile(fd uintptr, name string) *File { - return newFile(fd, name, kindNewFile) + kind := kindNewFile + if nb, err := unix.IsNonblock(int(fd)); err == nil && nb { + kind = kindNonBlock + } + return newFile(fd, name, kind) } // newFileKind describes the kind of file to newFile. @@ -86,6 +93,7 @@ const ( kindNewFile newFileKind = iota kindOpenFile kindPipe + kindNonBlock ) // newFile is like NewFile, but if called from OpenFile or Pipe @@ -106,14 +114,28 @@ func newFile(fd uintptr, name string, kind newFileKind) *File { stdoutOrErr: fdi == 1 || fdi == 2, }} + pollable := kind == kindOpenFile || kind == kindPipe || kind == kindNonBlock + // Don't try to use kqueue with regular files on FreeBSD. // It crashes the system unpredictably while running all.bash. // Issue 19093. + // If the caller passed a non-blocking filedes (kindNonBlock), + // we assume they know what they are doing so we allow it to be + // used with kqueue. if runtime.GOOS == "freebsd" && kind == kindOpenFile { - kind = kindNewFile + pollable = false + } + + // On Darwin, kqueue does not work properly with fifos: + // closing the last writer does not cause a kqueue event + // for any readers. See issue #24164. + if runtime.GOOS == "darwin" && kind == kindOpenFile { + var st syscall.Stat_t + if err := syscall.Fstat(fdi, &st); err == nil && st.Mode&syscall.S_IFMT == syscall.S_IFIFO { + pollable = false + } } - pollable := kind == kindOpenFile || kind == kindPipe if err := f.pfd.Init("file", pollable); err != nil { // An error here indicates a failure to register // with the netpoll system. That can happen for @@ -154,10 +176,10 @@ const DevNull = "/dev/null" // openFileNolog is the Unix implementation of OpenFile. func openFileNolog(name string, flag int, perm FileMode) (*File, error) { - chmod := false + setSticky := false if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 { if _, err := Stat(name); IsNotExist(err) { - chmod = true + setSticky = true } } @@ -171,7 +193,7 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) { // On OS X, sigaction(2) doesn't guarantee that SA_RESTART will cause // open(2) to be restarted for regular files. This is easy to reproduce on - // fuse file systems (see http://golang.org/issue/11180). + // fuse file systems (see https://golang.org/issue/11180). if runtime.GOOS == "darwin" && e == syscall.EINTR { continue } @@ -180,8 +202,8 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) { } // open(2) itself won't handle the sticky bit on *BSD and Solaris - if chmod { - Chmod(name, perm) + if setSticky { + setStickyBit(name) } // There's a race here with fork/exec, which we are @@ -194,7 +216,8 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) { } // Close closes the File, rendering it unusable for I/O. -// It returns an error, if any. +// On files that support SetDeadline, any pending I/O operations will +// be canceled and return immediately with an error. func (f *File) Close() error { if f == nil { return ErrInvalid @@ -283,7 +306,7 @@ func Truncate(name string, size int64) error { return nil } -// Remove removes the named file or directory. +// Remove removes the named file or (empty) directory. // If there is an error, it will be of type *PathError. func Remove(name string) error { // System call interface forces us to know diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go index 8ed9252..5a77d66 100644 --- a/libgo/go/os/os_test.go +++ b/libgo/go/os/os_test.go @@ -1388,7 +1388,7 @@ func TestSeek(t *testing.T) { func TestSeekError(t *testing.T) { switch runtime.GOOS { - case "plan9", "nacl": + case "js", "nacl", "plan9": t.Skipf("skipping test on %v", runtime.GOOS) } @@ -1521,11 +1521,7 @@ func runBinHostname(t *testing.T, argv []string) string { return output } -func testWindowsHostname(t *testing.T) { - hostname, err := Hostname() - if err != nil { - t.Fatal(err) - } +func testWindowsHostname(t *testing.T, hostname string) { cmd := osexec.Command("hostname") out, err := cmd.CombinedOutput() if err != nil { @@ -1533,18 +1529,30 @@ func testWindowsHostname(t *testing.T) { } want := strings.Trim(string(out), "\r\n") if hostname != want { - t.Fatalf("Hostname() = %q, want %q", hostname, want) + t.Fatalf("Hostname() = %q != system hostname of %q", hostname, want) } } func TestHostname(t *testing.T) { + hostname, err := Hostname() + if err != nil { + t.Fatal(err) + } + if hostname == "" { + t.Fatal("Hostname returned empty string and no error") + } + if strings.Contains(hostname, "\x00") { + t.Fatalf("unexpected zero byte in hostname: %q", hostname) + } + // There is no other way to fetch hostname on windows, but via winapi. // On Plan 9 it can be taken from #c/sysname as Hostname() does. switch runtime.GOOS { case "android", "plan9": - t.Skipf("%s doesn't have /bin/hostname", runtime.GOOS) + // No /bin/hostname to verify against. + return case "windows": - testWindowsHostname(t) + testWindowsHostname(t, hostname) return } @@ -1553,11 +1561,6 @@ func TestHostname(t *testing.T) { // 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 - hostname, err := Hostname() - if err != nil { - t.Fatalf("%v", err) - } - var want string if runtime.GOOS == "aix" { want = runBinHostname(t, []string{"hostname", "-s"}) @@ -1795,23 +1798,54 @@ func TestSameFile(t *testing.T) { } } -func TestDevNullFile(t *testing.T) { - f, err := Open(DevNull) +func testDevNullFileInfo(t *testing.T, statname, devNullName string, fi FileInfo, ignoreCase bool) { + pre := fmt.Sprintf("%s(%q): ", statname, devNullName) + name := filepath.Base(devNullName) + if ignoreCase { + if strings.ToUpper(fi.Name()) != strings.ToUpper(name) { + t.Errorf(pre+"wrong file name have %v want %v", fi.Name(), name) + } + } else { + if fi.Name() != name { + t.Errorf(pre+"wrong file name have %v want %v", fi.Name(), name) + } + } + if fi.Size() != 0 { + t.Errorf(pre+"wrong file size have %d want 0", fi.Size()) + } + if fi.Mode()&ModeDevice == 0 { + t.Errorf(pre+"wrong file mode %q: ModeDevice is not set", fi.Mode()) + } + if fi.Mode()&ModeCharDevice == 0 { + t.Errorf(pre+"wrong file mode %q: ModeCharDevice is not set", fi.Mode()) + } + if fi.Mode().IsRegular() { + t.Errorf(pre+"wrong file mode %q: IsRegular returns true", fi.Mode()) + } +} + +func testDevNullFile(t *testing.T, devNullName string, ignoreCase bool) { + f, err := Open(devNullName) if err != nil { - t.Fatalf("Open(%s): %v", DevNull, err) + t.Fatalf("Open(%s): %v", devNullName, err) } defer f.Close() + fi, err := f.Stat() if err != nil { - t.Fatalf("Stat(%s): %v", DevNull, err) - } - name := filepath.Base(DevNull) - if fi.Name() != name { - t.Fatalf("wrong file name have %v want %v", fi.Name(), name) + t.Fatalf("Stat(%s): %v", devNullName, err) } - if fi.Size() != 0 { - t.Fatalf("wrong file size have %d want 0", fi.Size()) + testDevNullFileInfo(t, "f.Stat", devNullName, fi, ignoreCase) + + fi, err = Stat(devNullName) + if err != nil { + t.Fatalf("Stat(%s): %v", devNullName, err) } + testDevNullFileInfo(t, "Stat", devNullName, fi, ignoreCase) +} + +func TestDevNullFile(t *testing.T) { + testDevNullFile(t, DevNull, false) } var testLargeWrite = flag.Bool("large_write", false, "run TestLargeWriteToConsole test that floods console with output") @@ -1885,7 +1919,7 @@ func TestStatStdin(t *testing.T) { t.Fatal(err) } switch mode := fi.Mode(); { - case mode&ModeCharDevice != 0: + case mode&ModeCharDevice != 0 && mode&ModeDevice != 0: case mode&ModeNamedPipe != 0: default: t.Fatalf("unexpected Stdin mode (%v), want ModeCharDevice or ModeNamedPipe", mode) @@ -2221,6 +2255,8 @@ func TestPipeThreads(t *testing.T) { t.Skip("skipping on Windows; issue 19098") case "plan9": t.Skip("skipping on Plan 9; does not support runtime poller") + case "js": + t.Skip("skipping on js; no support for os.Pipe") } threads := 100 diff --git a/libgo/go/os/os_unix_test.go b/libgo/go/os/os_unix_test.go index 56c885c..54f121e 100644 --- a/libgo/go/os/os_unix_test.go +++ b/libgo/go/os/os_unix_test.go @@ -15,6 +15,7 @@ import ( "strings" "syscall" "testing" + "time" ) func init() { @@ -204,3 +205,76 @@ func TestReaddirRemoveRace(t *testing.T) { t.FailNow() } } + +// Issue 23120: respect umask when doing Mkdir with the sticky bit +func TestMkdirStickyUmask(t *testing.T) { + const umask = 0077 + dir := newDir("TestMkdirStickyUmask", t) + defer RemoveAll(dir) + oldUmask := syscall.Umask(umask) + defer syscall.Umask(oldUmask) + p := filepath.Join(dir, "dir1") + if err := Mkdir(p, ModeSticky|0755); err != nil { + t.Fatal(err) + } + fi, err := Stat(p) + if err != nil { + t.Fatal(err) + } + if mode := fi.Mode(); (mode&umask) != 0 || (mode&^ModePerm) != (ModeDir|ModeSticky) { + t.Errorf("unexpected mode %s", mode) + } +} + +// See also issues: 22939, 24331 +func newFileTest(t *testing.T, blocking bool) { + p := make([]int, 2) + if err := syscall.Pipe(p); err != nil { + t.Fatalf("pipe: %v", err) + } + defer syscall.Close(p[1]) + + // Set the the read-side to non-blocking. + if !blocking { + if err := syscall.SetNonblock(p[0], true); err != nil { + syscall.Close(p[0]) + t.Fatalf("SetNonblock: %v", err) + } + } + // Convert it to a file. + file := NewFile(uintptr(p[0]), "notapipe") + if file == nil { + syscall.Close(p[0]) + t.Fatalf("failed to convert fd to file!") + } + defer file.Close() + + // Try to read with deadline (but don't block forever). + b := make([]byte, 1) + // Send something after 100ms. + timer := time.AfterFunc(100*time.Millisecond, func() { syscall.Write(p[1], []byte("a")) }) + defer timer.Stop() + file.SetReadDeadline(time.Now().Add(10 * time.Millisecond)) + _, err := file.Read(b) + if !blocking { + // We want it to fail with a timeout. + if !IsTimeout(err) { + t.Fatalf("No timeout reading from file: %v", err) + } + } else { + // We want it to succeed after 100ms + if err != nil { + t.Fatalf("Error reading from file: %v", err) + } + } +} + +func TestNewFileBlock(t *testing.T) { + t.Parallel() + newFileTest(t, true) +} + +func TestNewFileNonBlock(t *testing.T) { + t.Parallel() + newFileTest(t, false) +} diff --git a/libgo/go/os/path.go b/libgo/go/os/path.go index eb996e5..cdfbc18 100644 --- a/libgo/go/os/path.go +++ b/libgo/go/os/path.go @@ -6,7 +6,6 @@ package os import ( "io" - "runtime" "syscall" ) @@ -39,8 +38,8 @@ func MkdirAll(path string, perm FileMode) error { } if j > 1 { - // Create parent - err = MkdirAll(path[0:j-1], perm) + // Create parent. + err = MkdirAll(fixRootDirectory(path[:j-1]), perm) if err != nil { return err } @@ -84,32 +83,35 @@ func RemoveAll(path string) error { return err } - // Directory. - fd, err := Open(path) - if err != nil { - if IsNotExist(err) { - // Race. It was deleted between the Lstat and Open. - // Return nil per RemoveAll's docs. - return nil - } - return err - } - // Remove contents & return first error. err = nil for { - if err == nil && (runtime.GOOS == "plan9" || runtime.GOOS == "nacl") { - // Reset read offset after removing directory entries. - // See golang.org/issue/22572. - fd.Seek(0, 0) + fd, err := Open(path) + if err != nil { + if IsNotExist(err) { + // Already deleted by someone else. + return nil + } + return err } - names, err1 := fd.Readdirnames(100) + + const request = 1024 + names, err1 := fd.Readdirnames(request) + + // Removing files from the directory may have caused + // the OS to reshuffle it. Simply calling Readdirnames + // again may skip some entries. The only reliable way + // to avoid this is to close and re-open the + // directory. See issue 20841. + fd.Close() + for _, name := range names { err1 := RemoveAll(path + string(PathSeparator) + name) if err == nil { err = err1 } } + if err1 == io.EOF { break } @@ -120,10 +122,29 @@ func RemoveAll(path string) error { if len(names) == 0 { break } - } - // Close directory, because windows won't remove opened directory. - fd.Close() + // We don't want to re-open unnecessarily, so if we + // got fewer than request names from Readdirnames, try + // simply removing the directory now. If that + // succeeds, we are done. + if len(names) < request { + err1 := Remove(path) + if err1 == nil || IsNotExist(err1) { + return nil + } + + if err != nil { + // We got some error removing the + // directory contents, and since we + // read fewer names than we requested + // there probably aren't more files to + // remove. Don't loop around to read + // the directory again. We'll probably + // just get the same error. + return err + } + } + } // Remove directory. err1 := Remove(path) diff --git a/libgo/go/os/path_plan9.go b/libgo/go/os/path_plan9.go index b09b53a..a54b4b9 100644 --- a/libgo/go/os/path_plan9.go +++ b/libgo/go/os/path_plan9.go @@ -13,3 +13,7 @@ const ( func IsPathSeparator(c uint8) bool { return PathSeparator == c } + +func fixRootDirectory(p string) string { + return p +} diff --git a/libgo/go/os/path_unix.go b/libgo/go/os/path_unix.go index bc0f239..3cb0e3a 100644 --- a/libgo/go/os/path_unix.go +++ b/libgo/go/os/path_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package os @@ -33,3 +33,7 @@ func basename(name string) string { return name } + +func fixRootDirectory(p string) string { + return p +} diff --git a/libgo/go/os/path_windows.go b/libgo/go/os/path_windows.go index 101b026..87b1cac 100644 --- a/libgo/go/os/path_windows.go +++ b/libgo/go/os/path_windows.go @@ -207,3 +207,14 @@ func fixLongPath(path string) string { } return string(pathbuf[:w]) } + +// fixRootDirectory fixes a reference to a drive's root directory to +// have the required trailing slash. +func fixRootDirectory(p string) string { + if len(p) == len(`\\?\c:`) { + if IsPathSeparator(p[0]) && IsPathSeparator(p[1]) && p[2] == '?' && IsPathSeparator(p[3]) && p[5] == ':' { + return p + `\` + } + } + return p +} diff --git a/libgo/go/os/path_windows_test.go b/libgo/go/os/path_windows_test.go index cce0bdd..00a3e63 100644 --- a/libgo/go/os/path_windows_test.go +++ b/libgo/go/os/path_windows_test.go @@ -5,8 +5,10 @@ package os_test import ( + "io/ioutil" "os" "strings" + "syscall" "testing" ) @@ -44,3 +46,31 @@ func TestFixLongPath(t *testing.T) { } } } + +func TestMkdirAllExtendedLength(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "TestMkdirAllExtendedLength") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + const prefix = `\\?\` + if len(tmpDir) < 4 || tmpDir[:4] != prefix { + fullPath, err := syscall.FullPath(tmpDir) + if err != nil { + t.Fatalf("FullPath(%q) fails: %v", tmpDir, err) + } + tmpDir = prefix + fullPath + } + path := tmpDir + `\dir\` + err = os.MkdirAll(path, 0777) + if err != nil { + t.Fatalf("MkdirAll(%q) failed: %v", path, err) + } + + path = path + `.\dir2` + err = os.MkdirAll(path, 0777) + if err == nil { + t.Fatalf("MkdirAll(%q) should have failed, but did not", path) + } +} diff --git a/libgo/go/os/pipe_freebsd.go b/libgo/go/os/pipe2_bsd.go index 93bd869..0ef894b 100644 --- a/libgo/go/os/pipe_freebsd.go +++ b/libgo/go/os/pipe2_bsd.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build freebsd netbsd openbsd + package os import "syscall" diff --git a/libgo/go/os/pipe_bsd.go b/libgo/go/os/pipe_bsd.go index 8398a24..dc4c951 100644 --- a/libgo/go/os/pipe_bsd.go +++ b/libgo/go/os/pipe_bsd.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly nacl netbsd openbsd solaris +// +build aix darwin dragonfly js,wasm nacl solaris package os diff --git a/libgo/go/os/pipe_test.go b/libgo/go/os/pipe_test.go index aad6c27..59d31e5 100644 --- a/libgo/go/os/pipe_test.go +++ b/libgo/go/os/pipe_test.go @@ -3,11 +3,13 @@ // license that can be found in the LICENSE file. // Test broken pipes on Unix systems. -// +build !windows,!plan9,!nacl +// +build !windows,!plan9,!nacl,!js package os_test import ( + "bufio" + "bytes" "fmt" "internal/testenv" "io" @@ -305,3 +307,133 @@ func testCloseWithBlockingRead(t *testing.T, r, w *os.File) { wg.Wait() } + +// Issue 24164, for pipes. +func TestPipeEOF(t *testing.T) { + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + + defer func() { + if err := w.Close(); err != nil { + t.Errorf("error closing writer: %v", err) + } + }() + + for i := 0; i < 3; i++ { + time.Sleep(10 * time.Millisecond) + _, err := fmt.Fprintf(w, "line %d\n", i) + if err != nil { + t.Errorf("error writing to fifo: %v", err) + return + } + } + time.Sleep(10 * time.Millisecond) + }() + + defer wg.Wait() + + done := make(chan bool) + go func() { + defer close(done) + + defer func() { + if err := r.Close(); err != nil { + t.Errorf("error closing reader: %v", err) + } + }() + + rbuf := bufio.NewReader(r) + for { + b, err := rbuf.ReadBytes('\n') + if err == io.EOF { + break + } + if err != nil { + t.Error(err) + return + } + t.Logf("%s\n", bytes.TrimSpace(b)) + } + }() + + select { + case <-done: + // Test succeeded. + case <-time.After(time.Second): + t.Error("timed out waiting for read") + // Close the reader to force the read to complete. + r.Close() + } +} + +// Issue 24481. +func TestFdRace(t *testing.T) { + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + defer r.Close() + defer w.Close() + + var wg sync.WaitGroup + call := func() { + defer wg.Done() + w.Fd() + } + + const tries = 100 + for i := 0; i < tries; i++ { + wg.Add(1) + go call() + } + wg.Wait() +} + +func TestFdReadRace(t *testing.T) { + t.Parallel() + + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + defer r.Close() + defer w.Close() + + c := make(chan bool) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + var buf [10]byte + r.SetReadDeadline(time.Now().Add(time.Second)) + c <- true + if _, err := r.Read(buf[:]); os.IsTimeout(err) { + t.Error("read timed out") + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + <-c + // Give the other goroutine a chance to enter the Read. + // It doesn't matter if this occasionally fails, the test + // will still pass, it just won't test anything. + time.Sleep(10 * time.Millisecond) + r.Fd() + + // The bug was that Fd would hang until Read timed out. + // If the bug is fixed, then closing r here will cause + // the Read to exit before the timeout expires. + r.Close() + }() + + wg.Wait() +} diff --git a/libgo/go/os/signal/signal.go b/libgo/go/os/signal/signal.go index e5a21e8..a0eba0d 100644 --- a/libgo/go/os/signal/signal.go +++ b/libgo/go/os/signal/signal.go @@ -86,6 +86,12 @@ func Ignore(sig ...os.Signal) { cancel(sig, ignoreSignal) } +// Ignored reports whether sig is currently ignored. +func Ignored(sig os.Signal) bool { + sn := signum(sig) + return sn >= 0 && signalIgnored(sn) +} + // Notify causes package signal to relay incoming signals to c. // If no signals are provided, all incoming signals will be relayed to c. // Otherwise, just the provided signals will. diff --git a/libgo/go/os/signal/signal_plan9.go b/libgo/go/os/signal/signal_plan9.go index b065ae5..a1eb688 100644 --- a/libgo/go/os/signal/signal_plan9.go +++ b/libgo/go/os/signal/signal_plan9.go @@ -15,6 +15,7 @@ var sigtab = make(map[os.Signal]int) func signal_disable(uint32) func signal_enable(uint32) func signal_ignore(uint32) +func signal_ignored(uint32) bool func signal_recv() string func init() { @@ -58,3 +59,7 @@ func disableSignal(sig int) { func ignoreSignal(sig int) { signal_ignore(uint32(sig)) } + +func signalIgnored(sig int) bool { + return signal_ignored(uint32(sig)) +} diff --git a/libgo/go/os/signal/signal_test.go b/libgo/go/os/signal/signal_test.go index d27ff0b..ecb05fd 100644 --- a/libgo/go/os/signal/signal_test.go +++ b/libgo/go/os/signal/signal_test.go @@ -192,6 +192,65 @@ func TestIgnore(t *testing.T) { testCancel(t, true) } +// Test that Ignored correctly detects changes to the ignored status of a signal. +func TestIgnored(t *testing.T) { + // Ask to be notified on SIGWINCH. + c := make(chan os.Signal, 1) + Notify(c, syscall.SIGWINCH) + + // If we're being notified, then the signal should not be ignored. + if Ignored(syscall.SIGWINCH) { + t.Errorf("expected SIGWINCH to not be ignored.") + } + Stop(c) + Ignore(syscall.SIGWINCH) + + // We're no longer paying attention to this signal. + if !Ignored(syscall.SIGWINCH) { + t.Errorf("expected SIGWINCH to be ignored when explicitly ignoring it.") + } + + Reset() +} + +var checkSighupIgnored = flag.Bool("check_sighup_ignored", false, "if true, TestDetectNohup will fail if SIGHUP is not ignored.") + +// Test that Ignored(SIGHUP) correctly detects whether it is being run under nohup. +func TestDetectNohup(t *testing.T) { + if *checkSighupIgnored { + if !Ignored(syscall.SIGHUP) { + t.Fatal("SIGHUP is not ignored.") + } else { + t.Log("SIGHUP is ignored.") + } + } else { + defer Reset() + // Ugly: ask for SIGHUP so that child will not have no-hup set + // even if test is running under nohup environment. + // We have no intention of reading from c. + c := make(chan os.Signal, 1) + Notify(c, syscall.SIGHUP) + if out, err := exec.Command(os.Args[0], "-test.run=TestDetectNohup", "-check_sighup_ignored").CombinedOutput(); err == nil { + t.Errorf("ran test with -check_sighup_ignored and it succeeded: expected failure.\nOutput:\n%s", out) + } + Stop(c) + // Again, this time with nohup, assuming we can find it. + _, err := os.Stat("/usr/bin/nohup") + if err != nil { + t.Skip("cannot find nohup; skipping second half of test") + } + Ignore(syscall.SIGHUP) + os.Remove("nohup.out") + out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestDetectNohup", "-check_sighup_ignored").CombinedOutput() + + data, _ := ioutil.ReadFile("nohup.out") + os.Remove("nohup.out") + if err != nil { + t.Errorf("ran test with -check_sighup_ignored under nohup and it failed: expected success.\nError: %v\nOutput:\n%s%s", err, out, data) + } + } +} + var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop") // Test that Stop cancels the channel's registrations. diff --git a/libgo/go/os/signal/signal_unix.go b/libgo/go/os/signal/signal_unix.go index 5ec7e97..7fa634f 100644 --- a/libgo/go/os/signal/signal_unix.go +++ b/libgo/go/os/signal/signal_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package signal @@ -15,6 +15,7 @@ import ( func signal_disable(uint32) func signal_enable(uint32) func signal_ignore(uint32) +func signal_ignored(uint32) bool func signal_recv() uint32 func loop() { @@ -56,3 +57,7 @@ func disableSignal(sig int) { func ignoreSignal(sig int) { signal_ignore(uint32(sig)) } + +func signalIgnored(sig int) bool { + return signal_ignored(uint32(sig)) +} diff --git a/libgo/go/os/stat_nacl.go b/libgo/go/os/stat_nacljs.go index 0c53f2f..f14add8 100644 --- a/libgo/go/os/stat_nacl.go +++ b/libgo/go/os/stat_nacljs.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build js,wasm nacl + package os import ( diff --git a/libgo/go/os/stat_plan9.go b/libgo/go/os/stat_plan9.go index 8057fd4..b43339a 100644 --- a/libgo/go/os/stat_plan9.go +++ b/libgo/go/os/stat_plan9.go @@ -9,7 +9,7 @@ import ( "time" ) -const _BIT16SZ = 2 +const bitSize16 = 2 func fileInfoFromStat(d *syscall.Dir) FileInfo { fs := &fileStat{ @@ -35,6 +35,10 @@ func fileInfoFromStat(d *syscall.Dir) FileInfo { if d.Type != 'M' { fs.mode |= ModeDevice } + // Consider all files served by #c as character device files. + if d.Type == 'c' { + fs.mode |= ModeCharDevice + } return fs } @@ -46,7 +50,7 @@ func dirstat(arg interface{}) (*syscall.Dir, error) { size := syscall.STATFIXLEN + 16*4 for i := 0; i < 2; i++ { - buf := make([]byte, _BIT16SZ+size) + buf := make([]byte, bitSize16+size) var n int switch a := arg.(type) { @@ -60,7 +64,7 @@ func dirstat(arg interface{}) (*syscall.Dir, error) { panic("phase error in dirstat") } - if n < _BIT16SZ { + if n < bitSize16 { return nil, &PathError{"stat", name, err} } diff --git a/libgo/go/os/stat_unix.go b/libgo/go/os/stat_unix.go index bc5d06c..4f85dce 100644 --- a/libgo/go/os/stat_unix.go +++ b/libgo/go/os/stat_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package os diff --git a/libgo/go/os/sys_bsd.go b/libgo/go/os/sys_bsd.go index 8ad5e21..d820be2 100644 --- a/libgo/go/os/sys_bsd.go +++ b/libgo/go/os/sys_bsd.go @@ -2,10 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd nacl netbsd openbsd - -// os code shared between *BSD systems including OS X (Darwin) -// and FreeBSD. +// +build darwin dragonfly freebsd js,wasm nacl netbsd openbsd package os diff --git a/libgo/go/os/sys_darwin.go b/libgo/go/os/sys_darwin.go deleted file mode 100644 index 11d678e..0000000 --- a/libgo/go/os/sys_darwin.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2014 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 - -// supportsCloseOnExec reports whether the platform supports the -// O_CLOEXEC flag. -// The O_CLOEXEC flag was introduced in OS X 10.7 (Darwin 11.0.0). -// See http://support.apple.com/kb/HT1633. -const supportsCloseOnExec = true diff --git a/libgo/go/os/sys_freebsd.go b/libgo/go/os/sys_js.go index 3ec49fa..e860654 100644 --- a/libgo/go/os/sys_freebsd.go +++ b/libgo/go/os/sys_js.go @@ -1,10 +1,11 @@ -// Copyright 2014 The Go Authors. All rights reserved. +// Copyright 2018 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 js,wasm + package os // supportsCloseOnExec reports whether the platform supports the // O_CLOEXEC flag. -// The O_CLOEXEC flag was introduced in FreeBSD 8.3. -const supportsCloseOnExec bool = true +const supportsCloseOnExec = false diff --git a/libgo/go/os/sys_linux.go b/libgo/go/os/sys_linux.go index 76cdf50..36a8a24 100644 --- a/libgo/go/os/sys_linux.go +++ b/libgo/go/os/sys_linux.go @@ -2,19 +2,46 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Linux-specific - package os +import ( + "runtime" + "syscall" +) + func hostname() (name string, err error) { + // Try uname first, as it's only one system call and reading + // from /proc is not allowed on Android. + var un syscall.Utsname + err = syscall.Uname(&un) + + var buf [512]byte // Enough for a DNS name. + for i, b := range un.Nodename[:] { + buf[i] = uint8(b) + if b == 0 { + name = string(buf[:i]) + break + } + } + // If we got a name and it's not potentially truncated + // (Nodename is 65 bytes), return it. + if err == nil && len(name) > 0 && len(name) < 64 { + return name, nil + } + if runtime.GOOS == "android" { + if name != "" { + return name, nil + } + return "localhost", nil + } + f, err := Open("/proc/sys/kernel/hostname") if err != nil { return "", err } defer f.Close() - var buf [512]byte // Enough for a DNS name. - n, err := f.Read(buf[0:]) + n, err := f.Read(buf[:]) if err != nil { return "", err } @@ -22,5 +49,5 @@ func hostname() (name string, err error) { if n > 0 && buf[n-1] == '\n' { n-- } - return string(buf[0:n]), nil + return string(buf[:n]), nil } diff --git a/libgo/go/os/sys_plan9.go b/libgo/go/os/sys_plan9.go index 07a7905..40374eb 100644 --- a/libgo/go/os/sys_plan9.go +++ b/libgo/go/os/sys_plan9.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Plan 9-specific - package os func hostname() (name string, err error) { diff --git a/libgo/go/os/sys_unix.go b/libgo/go/os/sys_unix.go index 4caf8bd..8491bad 100644 --- a/libgo/go/os/sys_unix.go +++ b/libgo/go/os/sys_unix.go @@ -2,10 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix dragonfly linux netbsd openbsd solaris +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package os // supportsCloseOnExec reports whether the platform supports the // O_CLOEXEC flag. +// On Darwin, the O_CLOEXEC flag was introduced in OS X 10.7 (Darwin 11.0.0). +// See https://support.apple.com/kb/HT1633. +// On FreeBSD, the O_CLOEXEC flag was introduced in version 8.3. const supportsCloseOnExec = true diff --git a/libgo/go/os/timeout_test.go b/libgo/go/os/timeout_test.go index 6f47ed0..4720738 100644 --- a/libgo/go/os/timeout_test.go +++ b/libgo/go/os/timeout_test.go @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. // +build !nacl +// +build !js // +build !plan9 // +build !windows @@ -15,8 +16,10 @@ import ( "io/ioutil" "math/rand" "os" + "os/signal" "runtime" "sync" + "syscall" "testing" "time" ) @@ -587,3 +590,40 @@ func TestRacyWrite(t *testing.T) { }() } } + +// Closing a TTY while reading from it should not hang. Issue 23943. +func TestTTYClose(t *testing.T) { + // Ignore SIGTTIN in case we are running in the background. + signal.Ignore(syscall.SIGTTIN) + defer signal.Reset(syscall.SIGTTIN) + + f, err := os.Open("/dev/tty") + if err != nil { + t.Skipf("skipping because opening /dev/tty failed: %v", err) + } + + go func() { + var buf [1]byte + f.Read(buf[:]) + }() + + // Give the goroutine a chance to enter the read. + // It doesn't matter much if it occasionally fails to do so, + // we won't be testing what we want to test but the test will pass. + time.Sleep(time.Millisecond) + + c := make(chan bool) + go func() { + defer close(c) + f.Close() + }() + + select { + case <-c: + case <-time.After(time.Second): + t.Error("timed out waiting for close") + } + + // On some systems the goroutines may now be hanging. + // There's not much we can do about that. +} diff --git a/libgo/go/os/types.go b/libgo/go/os/types.go index db78487..b0b7d8d 100644 --- a/libgo/go/os/types.go +++ b/libgo/go/os/types.go @@ -54,15 +54,16 @@ const ( ModeSetgid // g: setgid ModeCharDevice // c: Unix character device, when ModeDevice is set ModeSticky // t: sticky + ModeIrregular // ?: non-regular file; nothing else is known about this file // Mask for the type bits. For regular files, none will be set. - ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice + ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeIrregular ModePerm FileMode = 0777 // Unix permission bits ) func (m FileMode) String() string { - const str = "dalTLDpSugct" + const str = "dalTLDpSugct?" var buf [32]byte // Mode is uint32. w := 0 for i, c := range str { diff --git a/libgo/go/os/types_windows.go b/libgo/go/os/types_windows.go index 01d6b62..f3297c0 100644 --- a/libgo/go/os/types_windows.go +++ b/libgo/go/os/types_windows.go @@ -5,16 +5,30 @@ package os import ( + "internal/syscall/windows" "sync" "syscall" "time" + "unsafe" ) // A fileStat is the implementation of FileInfo returned by Stat and Lstat. type fileStat struct { - name string - sys syscall.Win32FileAttributeData - filetype uint32 // what syscall.GetFileType returns + name string + + // from ByHandleFileInformation, Win32FileAttributeData and Win32finddata + FileAttributes uint32 + CreationTime syscall.Filetime + LastAccessTime syscall.Filetime + LastWriteTime syscall.Filetime + FileSizeHigh uint32 + FileSizeLow uint32 + + // from Win32finddata + Reserved0 uint32 + + // what syscall.GetFileType returns + filetype uint32 // used to implement SameFile sync.Mutex @@ -25,40 +39,160 @@ type fileStat struct { appendNameToPath bool } +// newFileStatFromGetFileInformationByHandle calls GetFileInformationByHandle +// to gather all required information about the file handle h. +func newFileStatFromGetFileInformationByHandle(path string, h syscall.Handle) (fs *fileStat, err error) { + var d syscall.ByHandleFileInformation + err = syscall.GetFileInformationByHandle(h, &d) + if err != nil { + return nil, &PathError{"GetFileInformationByHandle", path, err} + } + return &fileStat{ + name: basename(path), + FileAttributes: d.FileAttributes, + CreationTime: d.CreationTime, + LastAccessTime: d.LastAccessTime, + LastWriteTime: d.LastWriteTime, + FileSizeHigh: d.FileSizeHigh, + FileSizeLow: d.FileSizeLow, + vol: d.VolumeSerialNumber, + idxhi: d.FileIndexHigh, + idxlo: d.FileIndexLow, + // fileStat.path is used by os.SameFile to decide if it needs + // to fetch vol, idxhi and idxlo. But these are already set, + // so set fileStat.path to "" to prevent os.SameFile doing it again. + }, nil +} + +// newFileStatFromWin32finddata copies all required information +// from syscall.Win32finddata d into the newly created fileStat. +func newFileStatFromWin32finddata(d *syscall.Win32finddata) *fileStat { + return &fileStat{ + FileAttributes: d.FileAttributes, + CreationTime: d.CreationTime, + LastAccessTime: d.LastAccessTime, + LastWriteTime: d.LastWriteTime, + FileSizeHigh: d.FileSizeHigh, + FileSizeLow: d.FileSizeLow, + Reserved0: d.Reserved0, + } +} + +// newFileStatFromGetFileAttributesExOrFindFirstFile calls GetFileAttributesEx +// and FindFirstFile to gather all required information about the provided file path pathp. +func newFileStatFromGetFileAttributesExOrFindFirstFile(path string, pathp *uint16) (*fileStat, error) { + // As suggested by Microsoft, use GetFileAttributes() to acquire the file information, + // and if it's a reparse point use FindFirstFile() to get the tag: + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363940(v=vs.85).aspx + // Notice that always calling FindFirstFile can create performance problems + // (https://golang.org/issues/19922#issuecomment-300031421) + var fa syscall.Win32FileAttributeData + err := syscall.GetFileAttributesEx(pathp, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa))) + if err == nil && fa.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { + // Not a symlink. + return &fileStat{ + FileAttributes: fa.FileAttributes, + CreationTime: fa.CreationTime, + LastAccessTime: fa.LastAccessTime, + LastWriteTime: fa.LastWriteTime, + FileSizeHigh: fa.FileSizeHigh, + FileSizeLow: fa.FileSizeLow, + }, nil + } + // GetFileAttributesEx returns ERROR_INVALID_NAME if called + // for invalid file name like "*.txt". Do not attempt to call + // FindFirstFile with "*.txt", because FindFirstFile will + // succeed. So just return ERROR_INVALID_NAME instead. + // see https://golang.org/issue/24999 for details. + if errno, _ := err.(syscall.Errno); errno == windows.ERROR_INVALID_NAME { + return nil, &PathError{"GetFileAttributesEx", path, err} + } + // We might have symlink here. But some directories also have + // FileAttributes FILE_ATTRIBUTE_REPARSE_POINT bit set. + // For example, OneDrive directory is like that + // (see golang.org/issue/22579 for details). + // So use FindFirstFile instead to distinguish directories like + // OneDrive from real symlinks (see instructions described at + // https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/ + // and in particular bits about using both FileAttributes and + // Reserved0 fields). + var fd syscall.Win32finddata + sh, err := syscall.FindFirstFile(pathp, &fd) + if err != nil { + return nil, &PathError{"FindFirstFile", path, err} + } + syscall.FindClose(sh) + + return newFileStatFromWin32finddata(&fd), nil +} + +func (fs *fileStat) updatePathAndName(name string) error { + fs.path = name + if !isAbs(fs.path) { + var err error + fs.path, err = syscall.FullPath(fs.path) + if err != nil { + return &PathError{"FullPath", name, err} + } + } + fs.name = basename(name) + return nil +} + +func (fs *fileStat) isSymlink() bool { + // Use instructions described at + // https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/ + // to recognize whether it's a symlink. + if fs.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { + return false + } + return fs.Reserved0 == syscall.IO_REPARSE_TAG_SYMLINK || + fs.Reserved0 == windows.IO_REPARSE_TAG_MOUNT_POINT +} + func (fs *fileStat) Size() int64 { - return int64(fs.sys.FileSizeHigh)<<32 + int64(fs.sys.FileSizeLow) + return int64(fs.FileSizeHigh)<<32 + int64(fs.FileSizeLow) } func (fs *fileStat) Mode() (m FileMode) { if fs == &devNullStat { return ModeDevice | ModeCharDevice | 0666 } - if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 { + if fs.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 { m |= 0444 } else { m |= 0666 } - if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 { + if fs.isSymlink() { return m | ModeSymlink } - if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { + if fs.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { m |= ModeDir | 0111 } switch fs.filetype { case syscall.FILE_TYPE_PIPE: m |= ModeNamedPipe case syscall.FILE_TYPE_CHAR: - m |= ModeCharDevice + m |= ModeDevice | ModeCharDevice } return m } func (fs *fileStat) ModTime() time.Time { - return time.Unix(0, fs.sys.LastWriteTime.Nanoseconds()) + return time.Unix(0, fs.LastWriteTime.Nanoseconds()) } // Sys returns syscall.Win32FileAttributeData for file fs. -func (fs *fileStat) Sys() interface{} { return &fs.sys } +func (fs *fileStat) Sys() interface{} { + return &syscall.Win32FileAttributeData{ + FileAttributes: fs.FileAttributes, + CreationTime: fs.CreationTime, + LastAccessTime: fs.LastAccessTime, + LastWriteTime: fs.LastWriteTime, + FileSizeHigh: fs.FileSizeHigh, + FileSizeLow: fs.FileSizeLow, + } +} func (fs *fileStat) loadFileId() error { fs.Lock() diff --git a/libgo/go/os/user/cgo_lookup_unix.go b/libgo/go/os/user/cgo_lookup_unix.go index 6c815b4..722e95b 100644 --- a/libgo/go/os/user/cgo_lookup_unix.go +++ b/libgo/go/os/user/cgo_lookup_unix.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris -// +build cgo +// +build cgo,!osusergo package user diff --git a/libgo/go/os/user/cgo_unix_test.go b/libgo/go/os/user/cgo_unix_test.go index 6741118..1d341aa 100644 --- a/libgo/go/os/user/cgo_unix_test.go +++ b/libgo/go/os/user/cgo_unix_test.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris -// +build cgo +// +build cgo,!osusergo package user diff --git a/libgo/go/os/user/listgroups_solaris.go b/libgo/go/os/user/listgroups_solaris.go index 28a8a78..f3cbf6c 100644 --- a/libgo/go/os/user/listgroups_solaris.go +++ b/libgo/go/os/user/listgroups_solaris.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build cgo +// +build cgo,!osusergo // Even though this file requires no C, it is used to provide a // listGroup stub because all the other Solaris calls work. Otherwise, diff --git a/libgo/go/os/user/listgroups_unix.go b/libgo/go/os/user/listgroups_unix.go index b142e2b..63f73e6 100644 --- a/libgo/go/os/user/listgroups_unix.go +++ b/libgo/go/os/user/listgroups_unix.go @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. // +build dragonfly darwin freebsd !android,linux netbsd openbsd +// +build cgo,!osusergo package user diff --git a/libgo/go/os/user/lookup_stubs.go b/libgo/go/os/user/lookup_stubs.go index d23870f..f7d138f 100644 --- a/libgo/go/os/user/lookup_stubs.go +++ b/libgo/go/os/user/lookup_stubs.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !cgo,!windows,!plan9 android +// +build !cgo,!windows,!plan9 android osusergo,!windows,!plan9 package user diff --git a/libgo/go/os/user/lookup_unix.go b/libgo/go/os/user/lookup_unix.go index 5f34ba8..c4e9ba1 100644 --- a/libgo/go/os/user/lookup_unix.go +++ b/libgo/go/os/user/lookup_unix.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd !android,linux nacl netbsd openbsd solaris -// +build !cgo +// +build darwin dragonfly freebsd js,wasm !android,linux nacl netbsd openbsd solaris +// +build !cgo osusergo package user diff --git a/libgo/go/os/user/lookup_windows.go b/libgo/go/os/user/lookup_windows.go index 4e36a5c..7499f6a 100644 --- a/libgo/go/os/user/lookup_windows.go +++ b/libgo/go/os/user/lookup_windows.go @@ -5,16 +5,13 @@ package user import ( - "errors" "fmt" + "internal/syscall/windows" + "internal/syscall/windows/registry" "syscall" "unsafe" ) -func init() { - groupImplemented = false -} - func isDomainJoined() (bool, error) { var domain *uint16 var status uint32 @@ -72,19 +69,120 @@ func lookupFullName(domain, username, domainAndUser string) (string, error) { return username, nil } -func newUser(usid *syscall.SID, gid, dir string) (*User, error) { +// getProfilesDirectory retrieves the path to the root directory +// where user profiles are stored. +func getProfilesDirectory() (string, error) { + n := uint32(100) + for { + b := make([]uint16, n) + e := windows.GetProfilesDirectory(&b[0], &n) + if e == nil { + return syscall.UTF16ToString(b), nil + } + if e != syscall.ERROR_INSUFFICIENT_BUFFER { + return "", e + } + if n <= uint32(len(b)) { + return "", e + } + } +} + +// lookupUsernameAndDomain obtains the username and domain for usid. +func lookupUsernameAndDomain(usid *syscall.SID) (username, domain string, e error) { username, domain, t, e := usid.LookupAccount("") if e != nil { - return nil, e + return "", "", e } if t != syscall.SidTypeUser { - return nil, fmt.Errorf("user: should be user account type, not %d", t) + return "", "", fmt.Errorf("user: should be user account type, not %d", t) } - domainAndUser := domain + `\` + username - uid, e := usid.String() + return username, domain, nil +} + +// findHomeDirInRegistry finds the user home path based on the uid. +func findHomeDirInRegistry(uid string) (dir string, e error) { + k, e := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\`+uid, registry.QUERY_VALUE) if e != nil { - return nil, e + return "", e + } + defer k.Close() + dir, _, e = k.GetStringValue("ProfileImagePath") + if e != nil { + return "", e + } + return dir, nil +} + +// lookupGroupName accepts the name of a group and retrieves the group SID. +func lookupGroupName(groupname string) (string, error) { + sid, _, t, e := syscall.LookupSID("", groupname) + if e != nil { + return "", e + } + // https://msdn.microsoft.com/en-us/library/cc245478.aspx#gt_0387e636-5654-4910-9519-1f8326cf5ec0 + // SidTypeAlias should also be treated as a group type next to SidTypeGroup + // and SidTypeWellKnownGroup: + // "alias object -> resource group: A group object..." + // + // Tests show that "Administrators" can be considered of type SidTypeAlias. + if t != syscall.SidTypeGroup && t != syscall.SidTypeWellKnownGroup && t != syscall.SidTypeAlias { + return "", fmt.Errorf("lookupGroupName: should be group account type, not %d", t) } + return sid.String() +} + +// listGroupsForUsernameAndDomain accepts username and domain and retrieves +// a SID list of the local groups where this user is a member. +func listGroupsForUsernameAndDomain(username, domain string) ([]string, error) { + // Check if both the domain name and user should be used. + var query string + joined, err := isDomainJoined() + if err == nil && joined && len(domain) != 0 { + query = domain + `\` + username + } else { + query = username + } + q, err := syscall.UTF16PtrFromString(query) + if err != nil { + return nil, err + } + var p0 *byte + var entriesRead, totalEntries uint32 + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa370655(v=vs.85).aspx + // NetUserGetLocalGroups() would return a list of LocalGroupUserInfo0 + // elements which hold the names of local groups where the user participates. + // The list does not follow any sorting order. + // + // If no groups can be found for this user, NetUserGetLocalGroups() should + // always return the SID of a single group called "None", which + // also happens to be the primary group for the local user. + err = windows.NetUserGetLocalGroups(nil, q, 0, windows.LG_INCLUDE_INDIRECT, &p0, windows.MAX_PREFERRED_LENGTH, &entriesRead, &totalEntries) + if err != nil { + return nil, err + } + defer syscall.NetApiBufferFree(p0) + if entriesRead == 0 { + return nil, fmt.Errorf("listGroupsForUsernameAndDomain: NetUserGetLocalGroups() returned an empty list for domain: %s, username: %s", domain, username) + } + entries := (*[1024]windows.LocalGroupUserInfo0)(unsafe.Pointer(p0))[:entriesRead] + var sids []string + for _, entry := range entries { + if entry.Name == nil { + continue + } + name := syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(entry.Name))[:]) + sid, err := lookupGroupName(name) + if err != nil { + return nil, err + } + sids = append(sids, sid) + } + return sids, nil +} + +func newUser(uid, gid, dir, username, domain string) (*User, error) { + domainAndUser := domain + `\` + username name, e := lookupFullName(domain, username, domainAndUser) if e != nil { return nil, e @@ -113,6 +211,10 @@ func current() (*User, error) { if e != nil { return nil, e } + uid, e := u.User.Sid.String() + if e != nil { + return nil, e + } gid, e := pg.PrimaryGroup.String() if e != nil { return nil, e @@ -121,17 +223,105 @@ func current() (*User, error) { if e != nil { return nil, e } - return newUser(u.User.Sid, gid, dir) + username, domain, e := lookupUsernameAndDomain(u.User.Sid) + if e != nil { + return nil, e + } + return newUser(uid, gid, dir, username, domain) } -// BUG(brainman): Lookup and LookupId functions do not set -// Gid and HomeDir fields in the User struct returned on windows. +// lookupUserPrimaryGroup obtains the primary group SID for a user using this method: +// https://support.microsoft.com/en-us/help/297951/how-to-use-the-primarygroupid-attribute-to-find-the-primary-group-for +// The method follows this formula: domainRID + "-" + primaryGroupRID +func lookupUserPrimaryGroup(username, domain string) (string, error) { + // get the domain RID + sid, _, t, e := syscall.LookupSID("", domain) + if e != nil { + return "", e + } + if t != syscall.SidTypeDomain { + return "", fmt.Errorf("lookupUserPrimaryGroup: should be domain account type, not %d", t) + } + domainRID, e := sid.String() + if e != nil { + return "", e + } + // If the user has joined a domain use the RID of the default primary group + // called "Domain Users": + // https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems + // SID: S-1-5-21domain-513 + // + // The correct way to obtain the primary group of a domain user is + // probing the user primaryGroupID attribute in the server Active Directory: + // https://msdn.microsoft.com/en-us/library/ms679375(v=vs.85).aspx + // + // Note that the primary group of domain users should not be modified + // on Windows for performance reasons, even if it's possible to do that. + // The .NET Developer's Guide to Directory Services Programming - Page 409 + // https://books.google.bg/books?id=kGApqjobEfsC&lpg=PA410&ots=p7oo-eOQL7&dq=primary%20group%20RID&hl=bg&pg=PA409#v=onepage&q&f=false + joined, err := isDomainJoined() + if err == nil && joined { + return domainRID + "-513", nil + } + // For non-domain users call NetUserGetInfo() with level 4, which + // in this case would not have any network overhead. + // The primary group should not change from RID 513 here either + // but the group will be called "None" instead: + // https://www.adampalmer.me/iodigitalsec/2013/08/10/windows-null-session-enumeration/ + // "Group 'None' (RID: 513)" + u, e := syscall.UTF16PtrFromString(username) + if e != nil { + return "", e + } + d, e := syscall.UTF16PtrFromString(domain) + if e != nil { + return "", e + } + var p *byte + e = syscall.NetUserGetInfo(d, u, 4, &p) + if e != nil { + return "", e + } + defer syscall.NetApiBufferFree(p) + i := (*windows.UserInfo4)(unsafe.Pointer(p)) + return fmt.Sprintf("%s-%d", domainRID, i.PrimaryGroupID), nil +} func newUserFromSid(usid *syscall.SID) (*User, error) { - // TODO(brainman): do not know where to get gid and dir fields - gid := "unknown" - dir := "Unknown directory" - return newUser(usid, gid, dir) + username, domain, e := lookupUsernameAndDomain(usid) + if e != nil { + return nil, e + } + gid, e := lookupUserPrimaryGroup(username, domain) + if e != nil { + return nil, e + } + uid, e := usid.String() + if e != nil { + return nil, e + } + // If this user has logged in at least once their home path should be stored + // in the registry under the specified SID. References: + // https://social.technet.microsoft.com/wiki/contents/articles/13895.how-to-remove-a-corrupted-user-profile-from-the-registry.aspx + // https://support.asperasoft.com/hc/en-us/articles/216127438-How-to-delete-Windows-user-profiles + // + // The registry is the most reliable way to find the home path as the user + // might have decided to move it outside of the default location, + // (e.g. C:\users). Reference: + // https://answers.microsoft.com/en-us/windows/forum/windows_7-security/how-do-i-set-a-home-directory-outside-cusers-for-a/aed68262-1bf4-4a4d-93dc-7495193a440f + dir, e := findHomeDirInRegistry(uid) + if e != nil { + // If the home path does not exist in the registry, the user might + // have not logged in yet; fall back to using getProfilesDirectory(). + // Find the username based on a SID and append that to the result of + // getProfilesDirectory(). The domain is not relevant here. + dir, e = getProfilesDirectory() + if e != nil { + return nil, e + } + dir += `\` + username + } + return newUser(uid, gid, dir, username, domain) } func lookupUser(username string) (*User, error) { @@ -154,13 +344,47 @@ func lookupUserId(uid string) (*User, error) { } func lookupGroup(groupname string) (*Group, error) { - return nil, errors.New("user: LookupGroup not implemented on windows") + sid, err := lookupGroupName(groupname) + if err != nil { + return nil, err + } + return &Group{Name: groupname, Gid: sid}, nil } -func lookupGroupId(string) (*Group, error) { - return nil, errors.New("user: LookupGroupId not implemented on windows") +func lookupGroupId(gid string) (*Group, error) { + sid, err := syscall.StringToSid(gid) + if err != nil { + return nil, err + } + groupname, _, t, err := sid.LookupAccount("") + if err != nil { + return nil, err + } + if t != syscall.SidTypeGroup && t != syscall.SidTypeWellKnownGroup && t != syscall.SidTypeAlias { + return nil, fmt.Errorf("lookupGroupId: should be group account type, not %d", t) + } + return &Group{Name: groupname, Gid: gid}, nil } -func listGroups(*User) ([]string, error) { - return nil, errors.New("user: GroupIds not implemented on windows") +func listGroups(user *User) ([]string, error) { + sid, err := syscall.StringToSid(user.Uid) + if err != nil { + return nil, err + } + username, domain, err := lookupUsernameAndDomain(sid) + if err != nil { + return nil, err + } + sids, err := listGroupsForUsernameAndDomain(username, domain) + if err != nil { + return nil, err + } + // Add the primary group of the user to the list if it is not already there. + // This is done only to comply with the POSIX concept of a primary group. + for _, sid := range sids { + if sid == user.Gid { + return sids, nil + } + } + return append(sids, user.Gid), nil } diff --git a/libgo/go/os/user/user.go b/libgo/go/os/user/user.go index ad61992..1f733b8 100644 --- a/libgo/go/os/user/user.go +++ b/libgo/go/os/user/user.go @@ -2,7 +2,18 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package user allows user account lookups by name or id. +/* +Package user allows user account lookups by name or id. + +For most Unix systems, this package has two internal implementations of +resolving user and group ids to names. One is written in pure Go and +parses /etc/passwd and /etc/group. The other is cgo-based and relies on +the standard C library (libc) routines such as getpwuid_r and getgrnam_r. + +When cgo is available, cgo-based (libc-backed) code is used by default. +This can be overriden by using osusergo build tag, which enforces +the pure Go implementation. +*/ package user import ( diff --git a/libgo/go/os/user/user_test.go b/libgo/go/os/user/user_test.go index b3aeed8..8fd760e 100644 --- a/libgo/go/os/user/user_test.go +++ b/libgo/go/os/user/user_test.go @@ -5,6 +5,8 @@ package user import ( + "internal/testenv" + "os" "runtime" "testing" ) @@ -16,6 +18,20 @@ func checkUser(t *testing.T) { } func TestCurrent(t *testing.T) { + // The Go builders (in particular the ones using containers) + // often have minimal environments without $HOME or $USER set, + // which breaks Current which relies on those working as a + // fallback. + // TODO: we should fix that (Issue 24884) and remove these + // workarounds. + if testenv.Builder() != "" && runtime.GOOS != "windows" && runtime.GOOS != "plan9" { + if os.Getenv("HOME") == "" { + os.Setenv("HOME", "/tmp") + } + if os.Getenv("USER") == "" { + os.Setenv("USER", "gobuilder") + } + } u, err := Current() if err != nil { t.Fatalf("Current: %v (got %#v)", err, u) @@ -44,16 +60,12 @@ func compare(t *testing.T, want, got *User) { if want.Name != got.Name { t.Errorf("got Name=%q; want %q", got.Name, want.Name) } - // TODO(brainman): fix it once we know how. - if runtime.GOOS == "windows" { - t.Skip("skipping Gid and HomeDir comparisons") + if want.HomeDir != got.HomeDir { + t.Errorf("got HomeDir=%q; want %q", got.HomeDir, want.HomeDir) } if want.Gid != got.Gid { t.Errorf("got Gid=%q; want %q", got.Gid, want.Gid) } - if want.HomeDir != got.HomeDir { - t.Errorf("got HomeDir=%q; want %q", got.HomeDir, want.HomeDir) - } } func TestLookup(t *testing.T) { diff --git a/libgo/go/os/wait_unimp.go b/libgo/go/os/wait_unimp.go index 98243b5..d070604 100644 --- a/libgo/go/os/wait_unimp.go +++ b/libgo/go/os/wait_unimp.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly nacl netbsd openbsd solaris +// +build aix darwin dragonfly js,wasm nacl netbsd openbsd solaris package os diff --git a/libgo/go/path/example_test.go b/libgo/go/path/example_test.go index 5cac36c9..77fbfa9 100644 --- a/libgo/go/path/example_test.go +++ b/libgo/go/path/example_test.go @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package path +// +build gccgo_examples -/* Commented out until gccgo has example support. +package path import ( "fmt" @@ -93,6 +93,16 @@ func ExampleJoin() { // a } +func ExampleMatch() { + fmt.Println(path.Match("abc", "abc")) + fmt.Println(path.Match("a*", "abc")) + fmt.Println(path.Match("a*/b", "a/c/b")) + // Output: + // true <nil> + // true <nil> + // false <nil> +} + func ExampleSplit() { fmt.Println(path.Split("static/myfile.css")) fmt.Println(path.Split("myfile.css")) @@ -102,5 +112,3 @@ func ExampleSplit() { // myfile.css // } - -*/ diff --git a/libgo/go/path/filepath/example_unix_test.go b/libgo/go/path/filepath/example_unix_test.go index 40bc547..cd8233c 100644 --- a/libgo/go/path/filepath/example_unix_test.go +++ b/libgo/go/path/filepath/example_unix_test.go @@ -8,7 +8,6 @@ package filepath_test import ( "fmt" - "os" "path/filepath" ) @@ -80,24 +79,3 @@ func ExampleJoin() { // a/b/c // a/b/c } -func ExampleWalk() { - dir := "dir/to/walk" - subDirToSkip := "skip" // dir/to/walk/skip - - err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if err != nil { - fmt.Printf("prevent panic by handling failure accessing a path %q: %v\n", dir, err) - return err - } - if info.IsDir() && info.Name() == subDirToSkip { - fmt.Printf("skipping a dir without errors: %+v \n", info.Name()) - return filepath.SkipDir - } - fmt.Printf("visited file: %q\n", path) - return nil - }) - - if err != nil { - fmt.Printf("error walking the path %q: %v\n", dir, err) - } -} diff --git a/libgo/go/path/filepath/example_unix_walk_test.go b/libgo/go/path/filepath/example_unix_walk_test.go new file mode 100644 index 0000000..fa8b8e3 --- /dev/null +++ b/libgo/go/path/filepath/example_unix_walk_test.go @@ -0,0 +1,66 @@ +// Copyright 2018 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 !windows,!plan9 + +package filepath_test + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" +) + +func prepareTestDirTree(tree string) (string, error) { + tmpDir, err := ioutil.TempDir("", "") + if err != nil { + return "", fmt.Errorf("error creating temp directory: %v\n", err) + } + + err = os.MkdirAll(filepath.Join(tmpDir, tree), 0755) + if err != nil { + os.RemoveAll(tmpDir) + return "", err + } + + return tmpDir, nil +} + +func ExampleWalk() { + tmpDir, err := prepareTestDirTree("dir/to/walk/skip") + if err != nil { + fmt.Printf("unable to create test dir tree: %v\n", err) + return + } + defer os.RemoveAll(tmpDir) + os.Chdir(tmpDir) + + subDirToSkip := "skip" + + fmt.Println("On Unix:") + err = filepath.Walk(".", func(path string, info os.FileInfo, err error) error { + if err != nil { + fmt.Printf("prevent panic by handling failure accessing a path %q: %v\n", path, err) + return err + } + if info.IsDir() && info.Name() == subDirToSkip { + fmt.Printf("skipping a dir without errors: %+v \n", info.Name()) + return filepath.SkipDir + } + fmt.Printf("visited file or dir: %q\n", path) + return nil + }) + if err != nil { + fmt.Printf("error walking the path %q: %v\n", tmpDir, err) + return + } + // Output: + // On Unix: + // visited file or dir: "." + // visited file or dir: "dir" + // visited file or dir: "dir/to" + // visited file or dir: "dir/to/walk" + // skipping a dir without errors: skip +} diff --git a/libgo/go/path/filepath/match.go b/libgo/go/path/filepath/match.go index 5168e03..46badb5 100644 --- a/libgo/go/path/filepath/match.go +++ b/libgo/go/path/filepath/match.go @@ -13,7 +13,7 @@ import ( "unicode/utf8" ) -// ErrBadPattern indicates a globbing pattern was malformed. +// ErrBadPattern indicates a pattern was malformed. var ErrBadPattern = errors.New("syntax error in pattern") // Match reports whether name matches the shell file name pattern. @@ -339,6 +339,9 @@ func glob(dir, pattern string, matches []string) (m []string, e error) { // hasMeta reports whether path contains any of the magic characters // recognized by Match. func hasMeta(path string) bool { - // TODO(niemeyer): Should other magic characters be added here? - return strings.ContainsAny(path, "*?[") + magicChars := `*?[` + if runtime.GOOS != "windows" { + magicChars = `*?[\` + } + return strings.ContainsAny(path, magicChars) } diff --git a/libgo/go/path/filepath/match_test.go b/libgo/go/path/filepath/match_test.go index 12d922f..b73d6d2 100644 --- a/libgo/go/path/filepath/match_test.go +++ b/libgo/go/path/filepath/match_test.go @@ -10,6 +10,7 @@ import ( "io/ioutil" "os" . "path/filepath" + "reflect" "runtime" "sort" "strings" @@ -372,3 +373,18 @@ func TestWindowsGlob(t *testing.T) { } } } + +func TestNonWindowsGlobEscape(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skipf("skipping non-windows specific test") + } + pattern := `\match.go` + want := []string{"match.go"} + matches, err := Glob(pattern) + if err != nil { + t.Fatalf("Glob error for %q: %s", pattern, err) + } + if !reflect.DeepEqual(matches, want) { + t.Fatalf("Glob(%#q) = %v want %v", pattern, matches, want) + } +} diff --git a/libgo/go/path/filepath/path.go b/libgo/go/path/filepath/path.go index 87f8faf..1508137 100644 --- a/libgo/go/path/filepath/path.go +++ b/libgo/go/path/filepath/path.go @@ -341,12 +341,13 @@ var SkipDir = errors.New("skip this directory") // // If there was a problem walking to the file or directory named by path, the // incoming error will describe the problem and the function can decide how -// to handle that error (and Walk will not descend into that directory). If -// an error is returned, processing stops. The sole exception is when the function -// returns the special value SkipDir. If the function returns SkipDir when invoked -// on a directory, Walk skips the directory's contents entirely. -// If the function returns SkipDir when invoked on a non-directory file, -// Walk skips the remaining files in the containing directory. +// to handle that error (and Walk will not descend into that directory). In the +// case of an error, the info argument will be nil. If an error is returned, +// processing stops. The sole exception is when the function returns the special +// value SkipDir. If the function returns SkipDir when invoked on a directory, +// Walk skips the directory's contents entirely. If the function returns SkipDir +// when invoked on a non-directory file, Walk skips the remaining files in the +// containing directory. type WalkFunc func(path string, info os.FileInfo, err error) error var lstat = os.Lstat // for testing diff --git a/libgo/go/path/filepath/path_test.go b/libgo/go/path/filepath/path_test.go index ccffdba..5983a94 100644 --- a/libgo/go/path/filepath/path_test.go +++ b/libgo/go/path/filepath/path_test.go @@ -436,6 +436,22 @@ func TestWalk(t *testing.T) { defer restore() } } + + tmpDir, err := ioutil.TempDir("", "TestWalk") + if err != nil { + t.Fatal("creating temp dir:", err) + } + defer os.RemoveAll(tmpDir) + + origDir, err := os.Getwd() + if err != nil { + t.Fatal("finding working dir:", err) + } + if err = os.Chdir(tmpDir); err != nil { + t.Fatal("entering temp dir:", err) + } + defer os.Chdir(origDir) + makeTree(t) errors := make([]error, 0, 10) clear := true @@ -443,7 +459,7 @@ func TestWalk(t *testing.T) { return mark(info, err, &errors, clear) } // Expect no errors. - err := filepath.Walk(tree.name, markFn) + err = filepath.Walk(tree.name, markFn) if err != nil { t.Fatalf("no error expected, found: %s", err) } @@ -502,11 +518,6 @@ func TestWalk(t *testing.T) { os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770) os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770) } - - // cleanup - if err := os.RemoveAll(tree.name); err != nil { - t.Errorf("removeTree: %v", err) - } } func touch(t *testing.T, name string) { @@ -1061,6 +1072,47 @@ func TestAbs(t *testing.T) { } } +// Empty path needs to be special-cased on Windows. See golang.org/issue/24441. +// We test it separately from all other absTests because the empty string is not +// a valid path, so it can't be used with os.Stat. +func TestAbsEmptyString(t *testing.T) { + root, err := ioutil.TempDir("", "TestAbsEmptyString") + if err != nil { + t.Fatal("TempDir failed: ", err) + } + defer os.RemoveAll(root) + + wd, err := os.Getwd() + if err != nil { + t.Fatal("getwd failed: ", err) + } + err = os.Chdir(root) + if err != nil { + t.Fatal("chdir failed: ", err) + } + defer os.Chdir(wd) + + info, err := os.Stat(root) + if err != nil { + t.Fatalf("%s: %s", root, err) + } + + abspath, err := filepath.Abs("") + if err != nil { + t.Fatalf(`Abs("") error: %v`, err) + } + absinfo, err := os.Stat(abspath) + if err != nil || !os.SameFile(absinfo, info) { + t.Errorf(`Abs("")=%q, not the same file`, abspath) + } + if !filepath.IsAbs(abspath) { + t.Errorf(`Abs("")=%q, not an absolute path`, abspath) + } + if filepath.IsAbs(abspath) && abspath != filepath.Clean(abspath) { + t.Errorf(`Abs("")=%q, isn't clean`, abspath) + } +} + type RelTests struct { root, path, want string } diff --git a/libgo/go/path/filepath/path_unix.go b/libgo/go/path/filepath/path_unix.go index 2d407a8..c10b328 100644 --- a/libgo/go/path/filepath/path_unix.go +++ b/libgo/go/path/filepath/path_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package filepath diff --git a/libgo/go/path/filepath/path_windows.go b/libgo/go/path/filepath/path_windows.go index 0354255..409e8d6 100644 --- a/libgo/go/path/filepath/path_windows.go +++ b/libgo/go/path/filepath/path_windows.go @@ -107,6 +107,12 @@ func splitList(path string) []string { } func abs(path string) (string, error) { + if path == "" { + // syscall.FullPath returns an error on empty path, because it's not a valid path. + // To implement Abs behavior of returning working directory on empty string input, + // special-case empty path by changing it to "." path. See golang.org/issue/24441. + path = "." + } fullPath, err := syscall.FullPath(path) if err != nil { return "", err diff --git a/libgo/go/path/match.go b/libgo/go/path/match.go index 8d9aa51..d39d244 100644 --- a/libgo/go/path/match.go +++ b/libgo/go/path/match.go @@ -10,10 +10,10 @@ import ( "unicode/utf8" ) -// ErrBadPattern indicates a globbing pattern was malformed. +// ErrBadPattern indicates a pattern was malformed. var ErrBadPattern = errors.New("syntax error in pattern") -// Match reports whether name matches the shell file name pattern. +// Match reports whether name matches the shell pattern. // The pattern syntax is: // // pattern: diff --git a/libgo/go/path/match_test.go b/libgo/go/path/match_test.go index 6b0676f..127180e 100644 --- a/libgo/go/path/match_test.go +++ b/libgo/go/path/match_test.go @@ -73,7 +73,7 @@ func TestMatch(t *testing.T) { for _, tt := range matchTests { ok, err := Match(tt.pattern, tt.s) if ok != tt.match || err != tt.err { - t.Errorf("Match(%#q, %#q) = %v, %v want %v, nil", tt.pattern, tt.s, ok, err, tt.match) + t.Errorf("Match(%#q, %#q) = %v, %v want %v, %v", tt.pattern, tt.s, ok, err, tt.match, tt.err) } } } diff --git a/libgo/go/plugin/plugin_stubs.go b/libgo/go/plugin/plugin_stubs.go index f0bcb4a..40a4164 100644 --- a/libgo/go/plugin/plugin_stubs.go +++ b/libgo/go/plugin/plugin_stubs.go @@ -8,7 +8,7 @@ package plugin import "errors" -func lookup(p *Plugin, symName string) (interface{}, error) { +func lookup(p *Plugin, symName string) (Symbol, error) { return nil, errors.New("plugin: not implemented") } diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go index 96b57ef..eb893e9 100644 --- a/libgo/go/reflect/all_test.go +++ b/libgo/go/reflect/all_test.go @@ -2536,9 +2536,9 @@ func TestFieldPkgPath(t *testing.T) { }{}) type pkgpathTest struct { - index []int - pkgPath string - anonymous bool + index []int + pkgPath string + embedded bool } checkPkgPath := func(name string, s []pkgpathTest) { @@ -2547,7 +2547,7 @@ func TestFieldPkgPath(t *testing.T) { if got, want := f.PkgPath, test.pkgPath; got != want { t.Errorf("%s: Field(%d).PkgPath = %q, want %q", name, test.index, got, want) } - if got, want := f.Anonymous, test.anonymous; got != want { + if got, want := f.Anonymous, test.embedded; got != want { t.Errorf("%s: Field(%d).Anonymous = %v, want %v", name, test.index, got, want) } } @@ -4833,7 +4833,20 @@ func (i StructI) Get() int { return int(i) } type StructIPtr int -func (i *StructIPtr) Get() int { return int(*i) } +func (i *StructIPtr) Get() int { return int(*i) } +func (i *StructIPtr) Set(v int) { *(*int)(i) = v } + +type SettableStruct struct { + SettableField int +} + +func (p *SettableStruct) Set(v int) { p.SettableField = v } + +type SettablePointer struct { + SettableField *int +} + +func (p *SettablePointer) Set(v int) { *p.SettableField = v } /* gccgo does not yet support StructOf with methods. @@ -4843,6 +4856,9 @@ func TestStructOfWithInterface(t *testing.T) { type Iface interface { Get() int } + type IfaceSet interface { + Set(int) + } tests := []struct { name string typ Type @@ -4904,7 +4920,7 @@ func TestStructOfWithInterface(t *testing.T) { }) // We currently do not correctly implement methods - // for anonymous fields other than the first. + // for embedded fields other than the first. // Therefore, for now, we expect those methods // to not exist. See issues 15924 and 20824. // When those issues are fixed, this test of panic @@ -4950,6 +4966,76 @@ func TestStructOfWithInterface(t *testing.T) { } } } + + // Test an embedded nil pointer with pointer methods. + fields := []StructField{{ + Name: "StructIPtr", + Anonymous: true, + Type: PtrTo(TypeOf(StructIPtr(want))), + }} + rt := StructOf(fields) + rv := New(rt).Elem() + // This should panic since the pointer is nil. + shouldPanic(func() { + rv.Interface().(IfaceSet).Set(want) + }) + + // Test an embedded nil pointer to a struct with pointer methods. + + fields = []StructField{{ + Name: "SettableStruct", + Anonymous: true, + Type: PtrTo(TypeOf(SettableStruct{})), + }} + rt = StructOf(fields) + rv = New(rt).Elem() + // This should panic since the pointer is nil. + shouldPanic(func() { + rv.Interface().(IfaceSet).Set(want) + }) + + // The behavior is different if there is a second field, + // since now an interface value holds a pointer to the struct + // rather than just holding a copy of the struct. + fields = []StructField{ + { + Name: "SettableStruct", + Anonymous: true, + Type: PtrTo(TypeOf(SettableStruct{})), + }, + { + Name: "EmptyStruct", + Anonymous: true, + Type: StructOf(nil), + }, + } + // With the current implementation this is expected to panic. + // Ideally it should work and we should be able to see a panic + // if we call the Set method. + shouldPanic(func() { + StructOf(fields) + }) + + // Embed a field that can be stored directly in an interface, + // with a second field. + fields = []StructField{ + { + Name: "SettablePointer", + Anonymous: true, + Type: TypeOf(SettablePointer{}), + }, + { + Name: "EmptyStruct", + Anonymous: true, + Type: StructOf(nil), + }, + } + // With the current implementation this is expected to panic. + // Ideally it should work and we should be able to call the + // Set and Get methods. + shouldPanic(func() { + StructOf(fields) + }) } */ diff --git a/libgo/go/reflect/deepequal.go b/libgo/go/reflect/deepequal.go index 2fdd6a3..5b6694d 100644 --- a/libgo/go/reflect/deepequal.go +++ b/libgo/go/reflect/deepequal.go @@ -116,7 +116,7 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool { for _, k := range v1.MapKeys() { val1 := v1.MapIndex(k) val2 := v2.MapIndex(k) - if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) { + if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(val1, val2, visited, depth+1) { return false } } diff --git a/libgo/go/reflect/set_test.go b/libgo/go/reflect/set_test.go index 7c39623..a633e6e 100644 --- a/libgo/go/reflect/set_test.go +++ b/libgo/go/reflect/set_test.go @@ -14,8 +14,6 @@ import ( "unsafe" ) -type MyBuffer bytes.Buffer - func TestImplicitMapConversion(t *testing.T) { // Test implicit conversions in MapIndex and SetMapIndex. { @@ -102,10 +100,7 @@ func TestImplicitMapConversion(t *testing.T) { } { // convert identical underlying types - // TODO(rsc): Should be able to define MyBuffer here. - // 6l prints very strange messages about .this.Bytes etc - // when we do that though, so MyBuffer is defined - // at top level. + type MyBuffer bytes.Buffer m := make(map[*MyBuffer]*bytes.Buffer) mv := ValueOf(m) b1 := new(MyBuffer) diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index bbbef91..da7796f 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -68,14 +68,15 @@ type Type interface { // NumMethod returns the number of exported methods in the type's method set. NumMethod() int - // Name returns the type's name within its package. - // It returns an empty string for unnamed types. + // Name returns the type's name within its package for a defined type. + // For other (non-defined) types it returns the empty string. Name() string - // PkgPath returns a named type's package path, that is, the import path + // PkgPath returns a defined type's package path, that is, the import path // that uniquely identifies the package, such as "encoding/base64". - // If the type was predeclared (string, error) or unnamed (*T, struct{}, []int), - // the package path will be the empty string. + // If the type was predeclared (string, error) or not defined (*T, struct{}, + // []int, or A where A is an alias for a non-defined type), the package path + // will be the empty string. PkgPath() string // Size returns the number of bytes needed to store @@ -166,13 +167,13 @@ type Type interface { // the field was found. // // FieldByNameFunc considers the fields in the struct itself - // and then the fields in any anonymous structs, in breadth first order, + // and then the fields in any embedded structs, in breadth first order, // stopping at the shallowest nesting depth containing one or more // fields satisfying the match function. If multiple fields at that depth // satisfy the match function, they cancel each other // and FieldByNameFunc returns no match. // This behavior mirrors Go's handling of name lookup in - // structs containing anonymous fields. + // structs containing embedded fields. FieldByNameFunc(match func(string) bool) (StructField, bool) // In returns the type of a function type's i'th input parameter. @@ -258,9 +259,7 @@ const ( ) // rtype is the common implementation of most values. -// It is embedded in other, public struct types, but always -// with a unique tag like `reflect:"array"` or `reflect:"ptr"` -// so that code cannot convert from, say, *arrayType to *ptrType. +// It is embedded in other struct types. // // rtype must be kept in sync with ../runtime/type.go:/^type._type. type rtype struct { @@ -290,10 +289,10 @@ type method struct { tfn unsafe.Pointer // fn used for normal method call } -// uncommonType is present only for types with names or methods -// (if T is a named type, the uncommonTypes for T and *T have methods). +// uncommonType is present only for defined types or types with methods +// (if T is a defined type, the uncommonTypes for T and *T have methods). // Using a pointer to this struct reduces the overall size required -// to describe an unnamed type with no methods. +// to describe a non-defined type with no methods. type uncommonType struct { name *string // name of type pkgPath *string // import path; nil for built-in types like int, string @@ -311,7 +310,7 @@ const ( // arrayType represents a fixed array type. type arrayType struct { - rtype `reflect:"array"` + rtype elem *rtype // array element type slice *rtype // slice type len uintptr @@ -319,14 +318,14 @@ type arrayType struct { // chanType represents a channel type. type chanType struct { - rtype `reflect:"chan"` - elem *rtype // channel element type - dir uintptr // channel direction (ChanDir) + rtype + elem *rtype // channel element type + dir uintptr // channel direction (ChanDir) } // funcType represents a function type. type funcType struct { - rtype `reflect:"func"` + rtype dotdotdot bool // last input parameter is ... in []*rtype // input parameter types out []*rtype // output parameter types @@ -341,13 +340,13 @@ type imethod struct { // interfaceType represents an interface type. type interfaceType struct { - rtype `reflect:"interface"` + rtype methods []imethod // sorted by hash } // mapType represents a map type. type mapType struct { - rtype `reflect:"map"` + rtype key *rtype // map key type elem *rtype // map element (value) type bucket *rtype // internal bucket structure @@ -362,36 +361,36 @@ type mapType struct { // ptrType represents a pointer type. type ptrType struct { - rtype `reflect:"ptr"` - elem *rtype // pointer element (pointed at) type + rtype + elem *rtype // pointer element (pointed at) type } // sliceType represents a slice type. type sliceType struct { - rtype `reflect:"slice"` - elem *rtype // slice element type + rtype + elem *rtype // slice element type } // Struct field type structField struct { - name *string // name is always non-empty - pkgPath *string // nil for exported Names; otherwise import path - typ *rtype // type of field - tag *string // nil if no tag - offsetAnon uintptr // byte offset of field<<1 | isAnonymous + name *string // name is always non-empty + pkgPath *string // nil for exported Names; otherwise import path + typ *rtype // type of field + tag *string // nil if no tag + offsetEmbed uintptr // byte offset of field<<1 | isAnonymous } func (f *structField) offset() uintptr { - return f.offsetAnon >> 1 + return f.offsetEmbed >> 1 } -func (f *structField) anon() bool { - return f.offsetAnon&1 != 0 +func (f *structField) embedded() bool { + return f.offsetEmbed&1 != 0 } // structType represents a struct type. type structType struct { - rtype `reflect:"struct"` + rtype fields []structField // sorted by offset } @@ -478,6 +477,39 @@ func (t *uncommonType) Name() string { return *t.name } +var methodCache sync.Map // map[*uncommonType][]method + +func (t *uncommonType) exportedMethods() []method { + methodsi, found := methodCache.Load(t) + if found { + return methodsi.([]method) + } + + allm := t.methods + allExported := true + for _, m := range allm { + if m.pkgPath != nil { + allExported = false + break + } + } + var methods []method + if allExported { + methods = allm + } else { + methods = make([]method, 0, len(allm)) + for _, m := range allm { + if m.pkgPath == nil { + methods = append(methods, m) + } + } + methods = methods[:len(methods):len(methods)] + } + + methodsi, _ = methodCache.LoadOrStore(t, methods) + return methodsi.([]method) +} + func (t *rtype) rawString() string { return *t.string } func (t *rtype) String() string { @@ -520,41 +552,12 @@ func (t *rtype) pointers() bool { return t.kind&kindNoPointers == 0 } func (t *rtype) common() *rtype { return t } -var methodCache sync.Map // map[*rtype][]method - func (t *rtype) exportedMethods() []method { - methodsi, found := methodCache.Load(t) - if found { - return methodsi.([]method) - } - ut := t.uncommon() if ut == nil { return nil } - allm := ut.methods - allExported := true - for _, m := range allm { - if m.pkgPath != nil { - allExported = false - break - } - } - var methods []method - if allExported { - methods = allm - } else { - methods = make([]method, 0, len(allm)) - for _, m := range allm { - if m.pkgPath == nil { - methods = append(methods, m) - } - } - methods = methods[:len(methods):len(methods)] - } - - methodsi, _ = methodCache.LoadOrStore(t, methods) - return methodsi.([]method) + return ut.exportedMethods() } func (t *rtype) NumMethod() int { @@ -562,9 +565,6 @@ func (t *rtype) NumMethod() int { tt := (*interfaceType)(unsafe.Pointer(t)) return tt.NumMethod() } - if t.uncommonType == nil { - return 0 // avoid methodCache synchronization - } return len(t.exportedMethods()) } @@ -907,7 +907,7 @@ func (t *structType) Field(i int) (f StructField) { p := &t.fields[i] f.Type = toType(p.typ) f.Name = *p.name - f.Anonymous = p.anon() + f.Anonymous = p.embedded() if p.pkgPath != nil { f.PkgPath = *p.pkgPath } @@ -1001,11 +1001,11 @@ func (t *structType) FieldByNameFunc(match func(string) bool) (result StructFiel visited[t] = true for i := range t.fields { f := &t.fields[i] - // Find name and (for anonymous field) type for field f. + // Find name and (for embedded field) type for field f. fname := *f.name var ntyp *rtype - if f.anon() { - // Anonymous field of type T or *T. + if f.embedded() { + // Embedded field of type T or *T. ntyp = f.typ if ntyp.Kind() == Ptr { ntyp = ntyp.Elem().common() @@ -1062,20 +1062,20 @@ func (t *structType) FieldByNameFunc(match func(string) bool) (result StructFiel // FieldByName returns the struct field with the given name // and a boolean to indicate if the field was found. func (t *structType) FieldByName(name string) (f StructField, present bool) { - // Quick check for top-level name, or struct without anonymous fields. - hasAnon := false + // Quick check for top-level name, or struct without embedded fields. + hasEmbeds := false if name != "" { for i := range t.fields { tf := &t.fields[i] if *tf.name == name { return t.Field(i), true } - if tf.anon() { - hasAnon = true + if tf.embedded() { + hasEmbeds = true } } } - if !hasAnon { + if !hasEmbeds { return } return t.FieldByNameFunc(func(s string) bool { return s == name }) @@ -1274,7 +1274,7 @@ func directlyAssignable(T, V *rtype) bool { return true } - // Otherwise at least one of T and V must be unnamed + // Otherwise at least one of T and V must not be defined // and they must have the same kind. if T.Name() != "" && V.Name() != "" || T.Kind() != V.Kind() { return false @@ -1383,7 +1383,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { if cmpTags && tf.tag != vf.tag && (tf.tag == nil || vf.tag == nil || *tf.tag != *vf.tag) { return false } - if tf.offsetAnon != vf.offsetAnon { + if tf.offsetEmbed != vf.offsetEmbed { return false } } @@ -1715,7 +1715,7 @@ func needKeyUpdate(t *rtype) bool { } } -// Make sure these routines stay in sync with ../../runtime/hashmap.go! +// Make sure these routines stay in sync with ../../runtime/map.go! // These types exist only for GC, so we only fill out GC relevant info. // Currently, that's just size and the GC program. We also fill in string // for possible debugging use. @@ -1726,7 +1726,7 @@ const ( ) func bucketOf(ktyp, etyp *rtype) *rtype { - // See comment on hmap.overflow in ../runtime/hashmap.go. + // See comment on hmap.overflow in ../runtime/map.go. var kind uint8 if ktyp.kind&kindNoPointers != 0 && etyp.kind&kindNoPointers != 0 && ktyp.size <= maxKeySize && etyp.size <= maxValSize { @@ -1907,8 +1907,9 @@ func isValidFieldName(fieldName string) bool { // The Offset and Index fields are ignored and computed as they would be // by the compiler. // -// StructOf currently does not generate wrapper methods for embedded fields. -// This limitation may be lifted in a future version. +// StructOf currently does not generate wrapper methods for embedded +// fields and panics if passed unexported StructFields. +// These limitations may be lifted in a future version. func StructOf(fields []StructField) Type { var ( hash = uint32(12) @@ -1949,7 +1950,7 @@ func StructOf(fields []StructField) Type { // Update string and hash name := *f.name hash = (hash << 1) + ft.hash - if !f.anon() { + if !f.embedded() { repr = append(repr, (" " + name)...) } else { // Embedded field @@ -1958,7 +1959,7 @@ func StructOf(fields []StructField) Type { // Embedded ** and *interface{} are illegal elem := ft.Elem() if k := elem.Kind(); k == Ptr || k == Interface { - panic("reflect.StructOf: illegal anonymous field type " + ft.String()) + panic("reflect.StructOf: illegal embedded field type " + ft.String()) } name = elem.String() } else { @@ -2012,7 +2013,7 @@ func StructOf(fields []StructField) Type { typalign = int8(ft.fieldAlign) } size = offset + ft.size - f.offsetAnon |= offset << 1 + f.offsetEmbed |= offset << 1 if ft.size == 0 { lastzero = size @@ -2197,9 +2198,9 @@ func runtimeStructField(field StructField) structField { panic("reflect.StructOf: field \"" + field.Name + "\" is unexported but missing PkgPath") } - offsetAnon := uintptr(0) + offsetEmbed := uintptr(0) if field.Anonymous { - offsetAnon |= 1 + offsetEmbed |= 1 } s := field.Name @@ -2212,11 +2213,11 @@ func runtimeStructField(field StructField) structField { } return structField{ - name: name, - pkgPath: nil, - typ: field.Type.common(), - tag: tag, - offsetAnon: offsetAnon, + name: name, + pkgPath: nil, + typ: field.Type.common(), + tag: tag, + offsetEmbed: offsetEmbed, } } diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go index 3682b39..5ddd30d 100644 --- a/libgo/go/reflect/value.go +++ b/libgo/go/reflect/value.go @@ -632,7 +632,7 @@ func (v Value) Field(i int) Value { fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind()) // Using an unexported field forces flagRO. if field.pkgPath != nil { - if field.anon() { + if field.embedded() { fl |= flagEmbedRO } else { fl |= flagStickyRO @@ -1078,7 +1078,7 @@ func overflowFloat32(x float64) bool { } // OverflowInt reports whether the int64 x cannot be represented by v's type. -// It panics if v's Kind is not Int, Int8, int16, Int32, or Int64. +// It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64. func (v Value) OverflowInt(x int64) bool { k := v.kind() switch k { @@ -1942,7 +1942,7 @@ func MakeSlice(typ Type, len, cap int) Value { } s := sliceHeader{unsafe_NewArray(typ.Elem().(*rtype), cap), len, cap} - return Value{typ.common(), unsafe.Pointer(&s), flagIndir | flag(Slice)} + return Value{typ.(*rtype), unsafe.Pointer(&s), flagIndir | flag(Slice)} } // MakeChan creates a new channel with the specified type and buffer size. @@ -1956,8 +1956,9 @@ func MakeChan(typ Type, buffer int) Value { if typ.ChanDir() != BothDir { panic("reflect.MakeChan: unidirectional channel type") } - ch := makechan(typ.(*rtype), buffer) - return Value{typ.common(), unsafe.Pointer(&ch), flag(Chan) | flagIndir} + t := typ.(*rtype) + ch := makechan(t, buffer) + return Value{t, unsafe.Pointer(&ch), flag(Chan) | flagIndir} } // MakeMap creates a new map with the specified type. @@ -1971,8 +1972,9 @@ func MakeMapWithSize(typ Type, n int) Value { if typ.Kind() != Map { panic("reflect.MakeMapWithSize of non-map type") } - m := makemap(typ.(*rtype), n) - return Value{typ.common(), unsafe.Pointer(&m), flag(Map) | flagIndir} + t := typ.(*rtype) + m := makemap(t, n) + return Value{t, unsafe.Pointer(&m), flag(Map) | flagIndir} } // Indirect returns the value that v points to. @@ -2010,10 +2012,10 @@ func Zero(typ Type) Value { if typ == nil { panic("reflect: Zero(nil)") } - t := typ.common() + t := typ.(*rtype) fl := flag(t.Kind()) if ifaceIndir(t) { - return Value{t, unsafe_New(typ.(*rtype)), fl | flagIndir} + return Value{t, unsafe_New(t), fl | flagIndir} } return Value{t, nil, fl} } @@ -2024,16 +2026,18 @@ func New(typ Type) Value { if typ == nil { panic("reflect: New(nil)") } - ptr := unsafe_New(typ.(*rtype)) + t := typ.(*rtype) + ptr := unsafe_New(t) fl := flag(Ptr) - return Value{typ.common().ptrTo(), ptr, fl} + return Value{t.ptrTo(), ptr, fl} } // NewAt returns a Value representing a pointer to a value of the // specified type, using p as that pointer. func NewAt(typ Type, p unsafe.Pointer) Value { fl := flag(Ptr) - return Value{typ.common().ptrTo(), p, fl} + t := typ.(*rtype) + return Value{t.ptrTo(), p, fl} } // assignTo returns a value v that can be assigned directly to typ. @@ -2155,7 +2159,7 @@ func convertOp(dst, src *rtype) func(Value, Type) Value { return cvtDirect } - // dst and src are unnamed pointer types with same underlying base type. + // dst and src are non-defined pointer types with same underlying base type. if dst.Kind() == Ptr && dst.Name() == "" && src.Kind() == Ptr && src.Name() == "" && haveIdenticalUnderlyingType(dst.Elem().common(), src.Elem().common(), false) { diff --git a/libgo/go/regexp/all_test.go b/libgo/go/regexp/all_test.go index 28fe20c..0fabeae 100644 --- a/libgo/go/regexp/all_test.go +++ b/libgo/go/regexp/all_test.go @@ -581,6 +581,19 @@ func BenchmarkFind(b *testing.B) { } } +func BenchmarkFindAllNoMatches(b *testing.B) { + re := MustCompile("a+b+") + s := []byte("acddee") + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + all := re.FindAll(s, -1) + if all != nil { + b.Fatalf("FindAll(%q) = %q; want nil", s, all) + } + } +} + func BenchmarkFindString(b *testing.B) { b.StopTimer() re := MustCompile("a+b+") diff --git a/libgo/go/regexp/exec.go b/libgo/go/regexp/exec.go index 84cb3e6..1c7b02d 100644 --- a/libgo/go/regexp/exec.go +++ b/libgo/go/regexp/exec.go @@ -10,7 +10,7 @@ import ( ) // A queue is a 'sparse array' holding pending threads of execution. -// See http://research.swtch.com/2008/03/using-uninitialized-memory-for-fun-and.html +// See https://research.swtch.com/2008/03/using-uninitialized-memory-for-fun-and.html type queue struct { sparse []uint32 dense []entry @@ -27,7 +27,7 @@ type entry struct { // A thread is the state of a single path through the machine: // an instruction and a corresponding capture array. -// See http://swtch.com/~rsc/regexp/regexp2.html +// See https://swtch.com/~rsc/regexp/regexp2.html type thread struct { inst *syntax.Inst cap []int diff --git a/libgo/go/regexp/onepass.go b/libgo/go/regexp/onepass.go index 3ceb461..125be59 100644 --- a/libgo/go/regexp/onepass.go +++ b/libgo/go/regexp/onepass.go @@ -5,9 +5,9 @@ package regexp import ( - "bytes" "regexp/syntax" "sort" + "strings" "unicode" ) @@ -54,7 +54,7 @@ func onePassPrefix(p *syntax.Prog) (prefix string, complete bool, pc uint32) { } // Have prefix; gather characters. - var buf bytes.Buffer + var buf strings.Builder for iop(i) == syntax.InstRune && len(i.Rune) == 1 && syntax.Flags(i.Arg)&syntax.FoldCase == 0 { buf.WriteRune(i.Rune[0]) pc, i = i.Out, &p.Inst[i.Out] diff --git a/libgo/go/regexp/regexp.go b/libgo/go/regexp/regexp.go index b1af23e..61ed9c5 100644 --- a/libgo/go/regexp/regexp.go +++ b/libgo/go/regexp/regexp.go @@ -16,7 +16,7 @@ // (This is a property not guaranteed by most open source // implementations of regular expressions.) For more information // about this property, see -// http://swtch.com/~rsc/regexp/regexp1.html +// https://swtch.com/~rsc/regexp/regexp1.html // or any book about automata theory. // // All characters are UTF-8-encoded code points. @@ -30,8 +30,8 @@ // matches of the entire expression. Empty matches abutting a preceding // match are ignored. The return value is a slice containing the successive // return values of the corresponding non-'All' routine. These routines take -// an extra integer argument, n; if n >= 0, the function returns at most n -// matches/submatches. +// an extra integer argument, n. If n >= 0, the function returns at most n +// matches/submatches; otherwise, it returns all of them. // // If 'String' is present, the argument is a string; otherwise it is a slice // of bytes; return values are adjusted as appropriate. @@ -151,7 +151,7 @@ func Compile(expr string) (*Regexp, error) { // specifies that the match be chosen to maximize the length of the first // subexpression, then the second, and so on from left to right. // The POSIX rule is computationally prohibitive and not even well-defined. -// See http://swtch.com/~rsc/regexp/regexp2.html#posix for details. +// See https://swtch.com/~rsc/regexp/regexp2.html#posix for details. func CompilePOSIX(expr string) (*Regexp, error) { return compile(expr, syntax.POSIX, true) } @@ -226,6 +226,11 @@ func (re *Regexp) get() *machine { // grow to the maximum number of simultaneous matches // run using re. (The cache empties when re gets garbage collected.) func (re *Regexp) put(z *machine) { + // Remove references to input data that we no longer need. + z.inputBytes.str = nil + z.inputString.str = "" + z.inputReader.r = nil + re.mu.Lock() re.machine = append(re.machine, z) re.mu.Unlock() @@ -235,9 +240,9 @@ func (re *Regexp) put(z *machine) { // It simplifies safe initialization of global variables holding compiled regular // expressions. func MustCompile(str string) *Regexp { - regexp, error := Compile(str) - if error != nil { - panic(`regexp: Compile(` + quote(str) + `): ` + error.Error()) + regexp, err := Compile(str) + if err != nil { + panic(`regexp: Compile(` + quote(str) + `): ` + err.Error()) } return regexp } @@ -246,9 +251,9 @@ func MustCompile(str string) *Regexp { // It simplifies safe initialization of global variables holding compiled regular // expressions. func MustCompilePOSIX(str string) *Regexp { - regexp, error := CompilePOSIX(str) - if error != nil { - panic(`regexp: CompilePOSIX(` + quote(str) + `): ` + error.Error()) + regexp, err := CompilePOSIX(str) + if err != nil { + panic(`regexp: CompilePOSIX(` + quote(str) + `): ` + err.Error()) } return regexp } @@ -424,25 +429,27 @@ func (re *Regexp) LiteralPrefix() (prefix string, complete bool) { return re.prefix, re.prefixComplete } -// MatchReader reports whether the Regexp matches the text read by the -// RuneReader. +// MatchReader reports whether the text returned by the RuneReader +// contains any match of the regular expression re. func (re *Regexp) MatchReader(r io.RuneReader) bool { return re.doMatch(r, nil, "") } -// MatchString reports whether the Regexp matches the string s. +// MatchString reports whether the string s +// contains any match of the regular expression re. func (re *Regexp) MatchString(s string) bool { return re.doMatch(nil, nil, s) } -// Match reports whether the Regexp matches the byte slice b. +// Match reports whether the byte slice b +// contains any match of the regular expression re. func (re *Regexp) Match(b []byte) bool { return re.doMatch(nil, b, "") } -// MatchReader checks whether a textual regular expression matches the text -// read by the RuneReader. More complicated queries need to use Compile and -// the full Regexp interface. +// MatchReader reports whether the text returned by the RuneReader +// contains any match of the regular expression pattern. +// More complicated queries need to use Compile and the full Regexp interface. func MatchReader(pattern string, r io.RuneReader) (matched bool, err error) { re, err := Compile(pattern) if err != nil { @@ -451,9 +458,9 @@ func MatchReader(pattern string, r io.RuneReader) (matched bool, err error) { return re.MatchReader(r), nil } -// MatchString checks whether a textual regular expression -// matches a string. More complicated queries need -// to use Compile and the full Regexp interface. +// MatchString reports whether the string s +// contains any match of the regular expression pattern. +// More complicated queries need to use Compile and the full Regexp interface. func MatchString(pattern string, s string) (matched bool, err error) { re, err := Compile(pattern) if err != nil { @@ -462,9 +469,9 @@ func MatchString(pattern string, s string) (matched bool, err error) { return re.MatchString(s), nil } -// Match checks whether a textual regular expression -// matches a byte slice. More complicated queries need -// to use Compile and the full Regexp interface. +// MatchString reports whether the byte slice b +// contains any match of the regular expression pattern. +// More complicated queries need to use Compile and the full Regexp interface. func Match(pattern string, b []byte) (matched bool, err error) { re, err := Compile(pattern) if err != nil { @@ -623,9 +630,9 @@ func init() { } } -// QuoteMeta returns a string that quotes all regular expression metacharacters +// QuoteMeta returns a string that escapes all regular expression metacharacters // inside the argument text; the returned string is a regular expression matching -// the literal text. For example, QuoteMeta(`[foo]`) returns `\[foo\]`. +// the literal text. func QuoteMeta(s string) string { // A byte loop is correct because all metacharacters are ASCII. var i int @@ -670,7 +677,9 @@ func (re *Regexp) pad(a []int) []int { return a } -// Find matches in slice b if b is non-nil, otherwise find matches in string s. +// allMatches calls deliver at most n times +// with the location of successive matches in the input text. +// The input text is b if non-nil, otherwise s. func (re *Regexp) allMatches(s string, b []byte, n int, deliver func([]int)) { var end int if b == nil { @@ -984,13 +993,13 @@ func (re *Regexp) FindAll(b []byte, n int) [][]byte { if n < 0 { n = len(b) + 1 } - result := make([][]byte, 0, startSize) + var result [][]byte re.allMatches("", b, n, func(match []int) { + if result == nil { + result = make([][]byte, 0, startSize) + } result = append(result, b[match[0]:match[1]]) }) - if len(result) == 0 { - return nil - } return result } @@ -1002,13 +1011,13 @@ func (re *Regexp) FindAllIndex(b []byte, n int) [][]int { if n < 0 { n = len(b) + 1 } - result := make([][]int, 0, startSize) + var result [][]int re.allMatches("", b, n, func(match []int) { + if result == nil { + result = make([][]int, 0, startSize) + } result = append(result, match[0:2]) }) - if len(result) == 0 { - return nil - } return result } @@ -1020,13 +1029,13 @@ func (re *Regexp) FindAllString(s string, n int) []string { if n < 0 { n = len(s) + 1 } - result := make([]string, 0, startSize) + var result []string re.allMatches(s, nil, n, func(match []int) { + if result == nil { + result = make([]string, 0, startSize) + } result = append(result, s[match[0]:match[1]]) }) - if len(result) == 0 { - return nil - } return result } @@ -1038,13 +1047,13 @@ func (re *Regexp) FindAllStringIndex(s string, n int) [][]int { if n < 0 { n = len(s) + 1 } - result := make([][]int, 0, startSize) + var result [][]int re.allMatches(s, nil, n, func(match []int) { + if result == nil { + result = make([][]int, 0, startSize) + } result = append(result, match[0:2]) }) - if len(result) == 0 { - return nil - } return result } @@ -1056,8 +1065,11 @@ func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte { if n < 0 { n = len(b) + 1 } - result := make([][][]byte, 0, startSize) + var result [][][]byte re.allMatches("", b, n, func(match []int) { + if result == nil { + result = make([][][]byte, 0, startSize) + } slice := make([][]byte, len(match)/2) for j := range slice { if match[2*j] >= 0 { @@ -1066,9 +1078,6 @@ func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte { } result = append(result, slice) }) - if len(result) == 0 { - return nil - } return result } @@ -1080,13 +1089,13 @@ func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int { if n < 0 { n = len(b) + 1 } - result := make([][]int, 0, startSize) + var result [][]int re.allMatches("", b, n, func(match []int) { + if result == nil { + result = make([][]int, 0, startSize) + } result = append(result, match) }) - if len(result) == 0 { - return nil - } return result } @@ -1098,8 +1107,11 @@ func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string { if n < 0 { n = len(s) + 1 } - result := make([][]string, 0, startSize) + var result [][]string re.allMatches(s, nil, n, func(match []int) { + if result == nil { + result = make([][]string, 0, startSize) + } slice := make([]string, len(match)/2) for j := range slice { if match[2*j] >= 0 { @@ -1108,9 +1120,6 @@ func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string { } result = append(result, slice) }) - if len(result) == 0 { - return nil - } return result } @@ -1123,13 +1132,13 @@ func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int { if n < 0 { n = len(s) + 1 } - result := make([][]int, 0, startSize) + var result [][]int re.allMatches(s, nil, n, func(match []int) { + if result == nil { + result = make([][]int, 0, startSize) + } result = append(result, match) }) - if len(result) == 0 { - return nil - } return result } diff --git a/libgo/go/regexp/syntax/compile.go b/libgo/go/regexp/syntax/compile.go index 83e53ba..1d8ab87 100644 --- a/libgo/go/regexp/syntax/compile.go +++ b/libgo/go/regexp/syntax/compile.go @@ -9,7 +9,7 @@ import "unicode" // A patchList is a list of instruction pointers that need to be filled in (patched). // Because the pointers haven't been filled in yet, we can reuse their storage // to hold the list. It's kind of sleazy, but works well in practice. -// See http://swtch.com/~rsc/regexp/regexp1.html for inspiration. +// See https://swtch.com/~rsc/regexp/regexp1.html for inspiration. // // These aren't really pointers: they're integers, so we can reinterpret them // this way without using package unsafe. A value l denotes diff --git a/libgo/go/regexp/syntax/op_string.go b/libgo/go/regexp/syntax/op_string.go new file mode 100644 index 0000000..3952b2bd --- /dev/null +++ b/libgo/go/regexp/syntax/op_string.go @@ -0,0 +1,26 @@ +// Code generated by "stringer -type Op -trimprefix Op"; DO NOT EDIT. + +package syntax + +import "strconv" + +const ( + _Op_name_0 = "NoMatchEmptyMatchLiteralCharClassAnyCharNotNLAnyCharBeginLineEndLineBeginTextEndTextWordBoundaryNoWordBoundaryCaptureStarPlusQuestRepeatConcatAlternate" + _Op_name_1 = "opPseudo" +) + +var ( + _Op_index_0 = [...]uint8{0, 7, 17, 24, 33, 45, 52, 61, 68, 77, 84, 96, 110, 117, 121, 125, 130, 136, 142, 151} +) + +func (i Op) String() string { + switch { + case 1 <= i && i <= 19: + i -= 1 + return _Op_name_0[_Op_index_0[i]:_Op_index_0[i+1]] + case i == 128: + return _Op_name_1 + default: + return "Op(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/libgo/go/regexp/syntax/parse_test.go b/libgo/go/regexp/syntax/parse_test.go index dd6529f..fe3d251 100644 --- a/libgo/go/regexp/syntax/parse_test.go +++ b/libgo/go/regexp/syntax/parse_test.go @@ -5,8 +5,8 @@ package syntax import ( - "bytes" "fmt" + "strings" "testing" "unicode" ) @@ -282,7 +282,7 @@ func testParseDump(t *testing.T, tests []parseTest, flags Flags) { // dump prints a string representation of the regexp showing // the structure explicitly. func dump(re *Regexp) string { - var b bytes.Buffer + var b strings.Builder dumpRegexp(&b, re) return b.String() } @@ -312,7 +312,7 @@ var opNames = []string{ // dumpRegexp writes an encoding of the syntax tree for the regexp re to b. // It is used during testing to distinguish between parses that might print // the same using re's String method. -func dumpRegexp(b *bytes.Buffer, re *Regexp) { +func dumpRegexp(b *strings.Builder, re *Regexp) { if int(re.Op) >= len(opNames) || opNames[re.Op] == "" { fmt.Fprintf(b, "op%d", re.Op) } else { diff --git a/libgo/go/regexp/syntax/prog.go b/libgo/go/regexp/syntax/prog.go index 6c56371..49a06bb 100644 --- a/libgo/go/regexp/syntax/prog.go +++ b/libgo/go/regexp/syntax/prog.go @@ -5,8 +5,8 @@ package syntax import ( - "bytes" "strconv" + "strings" "unicode" ) @@ -117,20 +117,18 @@ type Inst struct { } func (p *Prog) String() string { - var b bytes.Buffer + var b strings.Builder dumpProg(&b, p) return b.String() } -// skipNop follows any no-op or capturing instructions -// and returns the resulting pc. -func (p *Prog) skipNop(pc uint32) (*Inst, uint32) { +// skipNop follows any no-op or capturing instructions. +func (p *Prog) skipNop(pc uint32) *Inst { i := &p.Inst[pc] for i.Op == InstNop || i.Op == InstCapture { - pc = i.Out - i = &p.Inst[pc] + i = &p.Inst[i.Out] } - return i, pc + return i } // op returns i.Op but merges all the Rune special cases into InstRune @@ -147,7 +145,7 @@ func (i *Inst) op() InstOp { // regexp must start with. Complete is true if the prefix // is the entire match. func (p *Prog) Prefix() (prefix string, complete bool) { - i, _ := p.skipNop(uint32(p.Start)) + i := p.skipNop(uint32(p.Start)) // Avoid allocation of buffer if prefix is empty. if i.op() != InstRune || len(i.Rune) != 1 { @@ -155,10 +153,10 @@ func (p *Prog) Prefix() (prefix string, complete bool) { } // Have prefix; gather characters. - var buf bytes.Buffer + var buf strings.Builder for i.op() == InstRune && len(i.Rune) == 1 && Flags(i.Arg)&FoldCase == 0 { buf.WriteRune(i.Rune[0]) - i, _ = p.skipNop(i.Out) + i = p.skipNop(i.Out) } return buf.String(), i.Op == InstMatch } @@ -269,18 +267,18 @@ func (i *Inst) MatchEmptyWidth(before rune, after rune) bool { } func (i *Inst) String() string { - var b bytes.Buffer + var b strings.Builder dumpInst(&b, i) return b.String() } -func bw(b *bytes.Buffer, args ...string) { +func bw(b *strings.Builder, args ...string) { for _, s := range args { b.WriteString(s) } } -func dumpProg(b *bytes.Buffer, p *Prog) { +func dumpProg(b *strings.Builder, p *Prog) { for j := range p.Inst { i := &p.Inst[j] pc := strconv.Itoa(j) @@ -300,7 +298,7 @@ func u32(i uint32) string { return strconv.FormatUint(uint64(i), 10) } -func dumpInst(b *bytes.Buffer, i *Inst) { +func dumpInst(b *strings.Builder, i *Inst) { switch i.Op { case InstAlt: bw(b, "alt -> ", u32(i.Out), ", ", u32(i.Arg)) diff --git a/libgo/go/regexp/syntax/regexp.go b/libgo/go/regexp/syntax/regexp.go index 0fe9269..a3f56f8 100644 --- a/libgo/go/regexp/syntax/regexp.go +++ b/libgo/go/regexp/syntax/regexp.go @@ -8,7 +8,6 @@ package syntax // In this package, re is always a *Regexp and r is always a rune. import ( - "bytes" "strconv" "strings" "unicode" @@ -27,6 +26,8 @@ type Regexp struct { Name string // capturing name, for OpCapture } +//go:generate stringer -type Op -trimprefix Op + // An Op is a single regular expression operator. type Op uint8 @@ -112,7 +113,7 @@ func (x *Regexp) Equal(y *Regexp) bool { } // writeRegexp writes the Perl syntax for the regular expression re to b. -func writeRegexp(b *bytes.Buffer, re *Regexp) { +func writeRegexp(b *strings.Builder, re *Regexp) { switch re.Op { default: b.WriteString("<invalid op" + strconv.Itoa(int(re.Op)) + ">") @@ -243,14 +244,14 @@ func writeRegexp(b *bytes.Buffer, re *Regexp) { } func (re *Regexp) String() string { - var b bytes.Buffer + var b strings.Builder writeRegexp(&b, re) return b.String() } const meta = `\.+*?()|[]{}^$` -func escape(b *bytes.Buffer, r rune, force bool) { +func escape(b *strings.Builder, r rune, force bool) { if unicode.IsPrint(r) { if strings.ContainsRune(meta, r) || force { b.WriteRune('\\') diff --git a/libgo/go/runtime/alg.go b/libgo/go/runtime/alg.go index 7c98f1b..c6bc6b6 100644 --- a/libgo/go/runtime/alg.go +++ b/libgo/go/runtime/alg.go @@ -5,6 +5,7 @@ package runtime import ( + "internal/cpu" "runtime/internal/sys" "unsafe" ) @@ -137,7 +138,7 @@ func interhash(p unsafe.Pointer, h uintptr) uintptr { t := *(**_type)(tab) fn := t.hashfn if fn == nil { - panic(errorString("hash of unhashable type " + *t.string)) + panic(errorString("hash of unhashable type " + t.string())) } if isDirectIface(t) { return c1 * fn(unsafe.Pointer(&a.data), h^c0) @@ -154,7 +155,7 @@ func nilinterhash(p unsafe.Pointer, h uintptr) uintptr { } fn := t.hashfn if fn == nil { - panic(errorString("hash of unhashable type " + *t.string)) + panic(errorString("hash of unhashable type " + t.string())) } if isDirectIface(t) { return c1 * fn(unsafe.Pointer(&a.data), h^c0) @@ -212,7 +213,7 @@ func efaceeq(x, y eface) bool { } eq := t.equalfn if eq == nil { - panic(errorString("comparing uncomparable type " + *t.string)) + panic(errorString("comparing uncomparable type " + t.string())) } if isDirectIface(t) { return x.data == y.data @@ -233,7 +234,7 @@ func ifaceeq(x, y iface) bool { } eq := t.equalfn if eq == nil { - panic(errorString("comparing uncomparable type " + *t.string)) + panic(errorString("comparing uncomparable type " + t.string())) } if isDirectIface(t) { return x.data == y.data @@ -251,7 +252,7 @@ func ifacevaleq(x iface, t *_type, p unsafe.Pointer) bool { } eq := t.equalfn if eq == nil { - panic(errorString("comparing uncomparable type " + *t.string)) + panic(errorString("comparing uncomparable type " + t.string())) } if isDirectIface(t) { return x.data == p @@ -272,7 +273,7 @@ func ifaceefaceeq(x iface, y eface) bool { } eq := xt.equalfn if eq == nil { - panic(errorString("comparing uncomparable type " + *xt.string)) + panic(errorString("comparing uncomparable type " + xt.string())) } if isDirectIface(xt) { return x.data == y.data @@ -289,7 +290,7 @@ func efacevaleq(x eface, t *_type, p unsafe.Pointer) bool { } eq := t.equalfn if eq == nil { - panic(errorString("comparing uncomparable type " + *t.string)) + panic(errorString("comparing uncomparable type " + t.string())) } if isDirectIface(t) { return x.data == p @@ -388,23 +389,25 @@ func ifaceHash(i interface { const hashRandomBytes = sys.PtrSize / 4 * 64 -// used in asm_{386,amd64}.s to seed the hash function +// used in asm_{386,amd64,arm64}.s to seed the hash function var aeskeysched [hashRandomBytes]byte // used in hash{32,64}.go to seed the hash function var hashkey [4]uintptr func alginit() { - // Install aes hash algorithm if we have the instructions we need + // Install AES hash algorithms if the instructions needed are present. if (GOARCH == "386" || GOARCH == "amd64") && GOOS != "nacl" && support_aes && - cpuid_ecx&(1<<25) != 0 && // aes (aesenc) - cpuid_ecx&(1<<9) != 0 && // sse3 (pshufb) - cpuid_ecx&(1<<19) != 0 { // sse4.1 (pinsr{d,q}) - useAeshash = true - // Initialize with random data so hash collisions will be hard to engineer. - getRandomData(aeskeysched[:]) + cpu.X86.HasAES && // AESENC + cpu.X86.HasSSSE3 && // PSHUFB + cpu.X86.HasSSE41 { // PINSR{D,Q} + initAlgAES() + return + } + if GOARCH == "arm64" && cpu.ARM64.HasAES { + initAlgAES() return } getRandomData((*[len(hashkey) * sys.PtrSize]byte)(unsafe.Pointer(&hashkey))[:]) @@ -413,3 +416,9 @@ func alginit() { hashkey[2] |= 1 hashkey[3] |= 1 } + +func initAlgAES() { + useAeshash = true + // Initialize with random data so hash collisions will be hard to engineer. + getRandomData(aeskeysched[:]) +} diff --git a/libgo/go/runtime/atomic_pointer.go b/libgo/go/runtime/atomic_pointer.go index b66ef58..2d023d3 100644 --- a/libgo/go/runtime/atomic_pointer.go +++ b/libgo/go/runtime/atomic_pointer.go @@ -16,11 +16,24 @@ import ( // Instead, these are wrappers around the actual atomics (casp1 and so on) // that use noescape to convey which arguments do not escape. +// atomicwb performs a write barrier before an atomic pointer write. +// The caller should guard the call with "if writeBarrier.enabled". +// +//go:nosplit +func atomicwb(ptr *unsafe.Pointer, new unsafe.Pointer) { + slot := (*uintptr)(unsafe.Pointer(ptr)) + if !getg().m.p.ptr().wbBuf.putFast(*slot, uintptr(new)) { + wbBufFlush(slot, uintptr(new)) + } +} + // atomicstorep performs *ptr = new atomically and invokes a write barrier. // //go:nosplit func atomicstorep(ptr unsafe.Pointer, new unsafe.Pointer) { - writebarrierptr_prewrite((*uintptr)(ptr), uintptr(new)) + if writeBarrier.enabled { + atomicwb((*unsafe.Pointer)(ptr), new) + } atomic.StorepNoWB(noescape(ptr), new) } @@ -29,7 +42,9 @@ func casp(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool { // The write barrier is only necessary if the CAS succeeds, // but since it needs to happen before the write becomes // public, we have to do it conservatively all the time. - writebarrierptr_prewrite((*uintptr)(unsafe.Pointer(ptr)), uintptr(new)) + if writeBarrier.enabled { + atomicwb(ptr, new) + } return atomic.Casp1((*unsafe.Pointer)(noescape(unsafe.Pointer(ptr))), noescape(old), new) } @@ -43,7 +58,9 @@ func sync_atomic_StoreUintptr(ptr *uintptr, new uintptr) //go:linkname sync_atomic_StorePointer sync_atomic.StorePointer //go:nosplit func sync_atomic_StorePointer(ptr *unsafe.Pointer, new unsafe.Pointer) { - writebarrierptr_prewrite((*uintptr)(unsafe.Pointer(ptr)), uintptr(new)) + if writeBarrier.enabled { + atomicwb(ptr, new) + } sync_atomic_StoreUintptr((*uintptr)(unsafe.Pointer(ptr)), uintptr(new)) } @@ -53,7 +70,9 @@ func sync_atomic_SwapUintptr(ptr *uintptr, new uintptr) uintptr //go:linkname sync_atomic_SwapPointer sync_atomic.SwapPointer //go:nosplit func sync_atomic_SwapPointer(ptr *unsafe.Pointer, new unsafe.Pointer) unsafe.Pointer { - writebarrierptr_prewrite((*uintptr)(unsafe.Pointer(ptr)), uintptr(new)) + if writeBarrier.enabled { + atomicwb(ptr, new) + } old := unsafe.Pointer(sync_atomic_SwapUintptr((*uintptr)(noescape(unsafe.Pointer(ptr))), uintptr(new))) return old } @@ -64,6 +83,8 @@ func sync_atomic_CompareAndSwapUintptr(ptr *uintptr, old, new uintptr) bool //go:linkname sync_atomic_CompareAndSwapPointer sync_atomic.CompareAndSwapPointer //go:nosplit func sync_atomic_CompareAndSwapPointer(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool { - writebarrierptr_prewrite((*uintptr)(unsafe.Pointer(ptr)), uintptr(new)) + if writeBarrier.enabled { + atomicwb(ptr, new) + } return sync_atomic_CompareAndSwapUintptr((*uintptr)(noescape(unsafe.Pointer(ptr))), uintptr(old), uintptr(new)) } diff --git a/libgo/go/runtime/vdso_none.go b/libgo/go/runtime/auxv_none.go index fc21240..3ca617b 100644 --- a/libgo/go/runtime/vdso_none.go +++ b/libgo/go/runtime/auxv_none.go @@ -4,6 +4,10 @@ // +build !linux // +build !darwin +// +build !dragonfly +// +build !freebsd +// +build !netbsd +// +build !solaris package runtime diff --git a/libgo/go/runtime/cgocall.go b/libgo/go/runtime/cgocall.go index 67b2bce..24bf749 100644 --- a/libgo/go/runtime/cgocall.go +++ b/libgo/go/runtime/cgocall.go @@ -212,22 +212,13 @@ func cgoCheckArg(t *_type, p unsafe.Pointer, indir, top bool, msg string) { // pointer into Go memory. If it does, we panic. // The return values are unused but useful to see in panic tracebacks. func cgoCheckUnknownPointer(p unsafe.Pointer, msg string) (base, i uintptr) { - if cgoInRange(p, mheap_.arena_start, mheap_.arena_used) { - if !inheap(uintptr(p)) { - // On 32-bit systems it is possible for C's allocated memory - // to have addresses between arena_start and arena_used. - // Either this pointer is a stack or an unused span or it's - // a C allocation. Escape analysis should prevent the first, - // garbage collection should prevent the second, - // and the third is completely OK. - return - } - - b, hbits, span, _ := heapBitsForObject(uintptr(p), 0, 0, false) + if inheap(uintptr(p)) { + b, span, _ := findObject(uintptr(p), 0, 0, false) base = b if base == 0 { return } + hbits := heapBitsForAddr(base) n := span.elemsize for i = uintptr(0); i < n; i += sys.PtrSize { if i != 1*sys.PtrSize && !hbits.morePointers() { diff --git a/libgo/go/runtime/cgocheck.go b/libgo/go/runtime/cgocheck.go index b85b519..d896fb7 100644 --- a/libgo/go/runtime/cgocheck.go +++ b/libgo/go/runtime/cgocheck.go @@ -126,9 +126,7 @@ func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) { roots = roots.next } - aoff := uintptr(src) - mheap_.arena_start - idx := aoff >> _PageShift - s := mheap_.spans[idx] + s := spanOfUnchecked(uintptr(src)) if s.state == _MSpanManual { // There are no heap bits for value stored on the stack. // For a channel receive src might be on the stack of some @@ -151,9 +149,7 @@ func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) { if i >= off && bits&bitPointer != 0 { v := *(*unsafe.Pointer)(add(src, i)) if cgoIsGoPointer(v) { - systemstack(func() { - throw(cgoWriteBarrierFail) - }) + throw(cgoWriteBarrierFail) } } hbits = hbits.next() @@ -186,9 +182,7 @@ func cgoCheckBits(src unsafe.Pointer, gcbits *byte, off, size uintptr) { if bits&1 != 0 { v := *(*unsafe.Pointer)(add(src, i)) if cgoIsGoPointer(v) { - systemstack(func() { - throw(cgoWriteBarrierFail) - }) + throw(cgoWriteBarrierFail) } } } diff --git a/libgo/go/runtime/chan.go b/libgo/go/runtime/chan.go index 87f7879..88a8944 100644 --- a/libgo/go/runtime/chan.go +++ b/libgo/go/runtime/chan.go @@ -88,7 +88,7 @@ func makechan(t *chantype, size int) *hchan { throw("makechan: bad alignment") } - if size < 0 || uintptr(size) > maxSliceCap(elem.size) || uintptr(size)*elem.size > _MaxMem-hchanSize { + if size < 0 || uintptr(size) > maxSliceCap(elem.size) || uintptr(size)*elem.size > maxAlloc-hchanSize { panic(plainError("makechan: size out of range")) } @@ -157,7 +157,7 @@ func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool { if !block { return false } - gopark(nil, nil, "chan send (nil chan)", traceEvGoStop, 2) + gopark(nil, nil, waitReasonChanSendNilChan, traceEvGoStop, 2) throw("unreachable") } @@ -246,7 +246,7 @@ func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool { gp.waiting = mysg gp.param = nil c.sendq.enqueue(mysg) - goparkunlock(&c.lock, "chan send", traceEvGoBlockSend, 3) + goparkunlock(&c.lock, waitReasonChanSend, traceEvGoBlockSend, 3) // someone woke us up. if mysg != gp.waiting { @@ -325,6 +325,8 @@ func sendDirect(t *_type, sg *sudog, src unsafe.Pointer) { // So make sure that no preemption points can happen between read & use. dst := sg.elem typeBitsBulkBarrier(t, uintptr(dst), uintptr(src), t.size) + // No need for cgo write barrier checks because dst is always + // Go memory. memmove(dst, src, t.size) } @@ -444,7 +446,7 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) if !block { return } - gopark(nil, nil, "chan receive (nil chan)", traceEvGoStop, 2) + gopark(nil, nil, waitReasonChanReceiveNilChan, traceEvGoStop, 2) throw("unreachable") } @@ -535,7 +537,7 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) mysg.c = c gp.param = nil c.recvq.enqueue(mysg) - goparkunlock(&c.lock, "chan receive", traceEvGoBlockRecv, 3) + goparkunlock(&c.lock, waitReasonChanReceive, traceEvGoBlockRecv, 3) // someone woke us up if mysg != gp.waiting { diff --git a/libgo/go/runtime/chanbarrier_test.go b/libgo/go/runtime/chanbarrier_test.go index b6029fb..d479574 100644 --- a/libgo/go/runtime/chanbarrier_test.go +++ b/libgo/go/runtime/chanbarrier_test.go @@ -57,7 +57,7 @@ func testChanSendBarrier(useSelect bool) { var globalMu sync.Mutex outer := 100 inner := 100000 - if testing.Short() { + if testing.Short() || runtime.GOARCH == "wasm" { outer = 10 inner = 1000 } diff --git a/libgo/go/runtime/cputicks.go b/libgo/go/runtime/cputicks.go index 7e62dc1..c41a58b 100644 --- a/libgo/go/runtime/cputicks.go +++ b/libgo/go/runtime/cputicks.go @@ -2,6 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// // +build !arm +// // +build !arm64 +// // +build !mips64 +// // +build !mips64le +// // +build !mips +// // +build !mipsle +// // +build !wasm + package runtime // careful: cputicks is not guaranteed to be monotonic! In particular, we have diff --git a/libgo/go/runtime/crash_cgo_test.go b/libgo/go/runtime/crash_cgo_test.go index 6688b3c..770f85e 100644 --- a/libgo/go/runtime/crash_cgo_test.go +++ b/libgo/go/runtime/crash_cgo_test.go @@ -89,19 +89,6 @@ func TestCgoExternalThreadSIGPROF(t *testing.T) { switch runtime.GOOS { case "plan9", "windows": t.Skipf("no pthreads on %s", runtime.GOOS) - case "darwin": - if runtime.GOARCH != "arm" && runtime.GOARCH != "arm64" { - // static constructor needs external linking, but we don't support - // external linking on OS X 10.6. - out, err := exec.Command("uname", "-r").Output() - if err != nil { - t.Fatalf("uname -r failed: %v", err) - } - // OS X 10.6 == Darwin 10.x - if strings.HasPrefix(string(out), "10.") { - t.Skipf("no external linking on OS X 10.6") - } - } } if runtime.GOARCH == "ppc64" { // TODO(austin) External linking not implemented on @@ -252,8 +239,12 @@ func TestCgoCCodeSIGPROF(t *testing.T) { func TestCgoCrashTraceback(t *testing.T) { t.Parallel() - if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le") { - t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH) + switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform { + case "darwin/amd64": + case "linux/amd64": + case "linux/ppc64le": + default: + t.Skipf("not yet supported on %s", platform) } if runtime.Compiler == "gccgo" { t.Skip("gccgo does not have SetCgoTraceback") @@ -352,7 +343,7 @@ func TestCgoPprofThreadNoTraceback(t *testing.T) { } func TestRaceProf(t *testing.T) { - if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + if (runtime.GOOS != "linux" && runtime.GOOS != "freebsd") || runtime.GOARCH != "amd64" { t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH) } if runtime.Compiler == "gccgo" { @@ -384,7 +375,7 @@ func TestRaceProf(t *testing.T) { func TestRaceSignal(t *testing.T) { t.Parallel() - if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + if (runtime.GOOS != "linux" && runtime.GOOS != "freebsd") || runtime.GOARCH != "amd64" { t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH) } @@ -514,3 +505,19 @@ func TestCgoTracebackSigpanic(t *testing.T) { t.Fatalf("failure incorrectly contains %q. output:\n%s\n", nowant, got) } } + +// Test that C code called via cgo can use large Windows thread stacks +// and call back in to Go without crashing. See issue #20975. +// +// See also TestBigStackCallbackSyscall. +func TestBigStackCallbackCgo(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skip("skipping windows specific test") + } + t.Parallel() + got := runTestProg(t, "testprogcgo", "BigStack") + want := "OK\n" + if got != want { + t.Errorf("expected %q got %v", want, got) + } +} diff --git a/libgo/go/runtime/crash_gccgo_test.go b/libgo/go/runtime/crash_gccgo_test.go index c216e54..d4a826e 100644 --- a/libgo/go/runtime/crash_gccgo_test.go +++ b/libgo/go/runtime/crash_gccgo_test.go @@ -38,8 +38,8 @@ func TestGccgoCrashTracebackNodebug(t *testing.T) { } cc := strings.Fields(os.Getenv("CC")) - cc = append(cc, "-x", "c++", "-") - out, _ := exec.Command(cc[0], cc[1:]...).CombinedOutput() + cc = append(cc, "-o", os.DevNull, "-x", "c++", "-") + out, _ := testenv.CleanCmdEnv(exec.Command(cc[0], cc[1:]...)).CombinedOutput() if bytes.Contains(out, []byte("error trying to exec 'cc1plus'")) { t.Skip("no C++ compiler") } diff --git a/libgo/go/runtime/crash_nonunix_test.go b/libgo/go/runtime/crash_nonunix_test.go index 2ce995c..bf349a5 100644 --- a/libgo/go/runtime/crash_nonunix_test.go +++ b/libgo/go/runtime/crash_nonunix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build windows plan9 nacl +// +build windows plan9 nacl js,wasm package runtime_test diff --git a/libgo/go/runtime/crash_test.go b/libgo/go/runtime/crash_test.go index 602630d..91a5c16 100644 --- a/libgo/go/runtime/crash_test.go +++ b/libgo/go/runtime/crash_test.go @@ -652,3 +652,112 @@ func TestBadTraceback(t *testing.T) { } } } + +func TestTimePprof(t *testing.T) { + if runtime.Compiler == "gccgo" { + t.Skip("gccgo may not have the pprof tool") + } + fn := runTestProg(t, "testprog", "TimeProf") + fn = strings.TrimSpace(fn) + defer os.Remove(fn) + + cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1", fn)) + cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir()) + top, err := cmd.CombinedOutput() + t.Logf("%s", top) + if err != nil { + t.Error(err) + } else if bytes.Contains(top, []byte("ExternalCode")) { + t.Error("profiler refers to ExternalCode") + } +} + +// Test that runtime.abort does so. +func TestAbort(t *testing.T) { + // Pass GOTRACEBACK to ensure we get runtime frames. + output := runTestProg(t, "testprog", "Abort", "GOTRACEBACK=system") + if want := "runtime.abort"; !strings.Contains(output, want) { + t.Errorf("output does not contain %q:\n%s", want, output) + } + if strings.Contains(output, "BAD") { + t.Errorf("output contains BAD:\n%s", output) + } + // Check that it's a signal traceback. + want := "PC=" + // For systems that use a breakpoint, check specifically for that. + if runtime.Compiler == "gc" { + switch runtime.GOARCH { + case "386", "amd64": + switch runtime.GOOS { + case "plan9": + want = "sys: breakpoint" + case "windows": + want = "Exception 0x80000003" + default: + want = "SIGTRAP" + } + } + } + if !strings.Contains(output, want) { + t.Errorf("output does not contain %q:\n%s", want, output) + } +} + +// For TestRuntimePanic: test a panic in the runtime package without +// involving the testing harness. +func init() { + if os.Getenv("GO_TEST_RUNTIME_PANIC") == "1" { + defer func() { + if r := recover(); r != nil { + // We expect to crash, so exit 0 + // to indicate failure. + os.Exit(0) + } + }() + runtime.PanicForTesting(nil, 1) + // We expect to crash, so exit 0 to indicate failure. + os.Exit(0) + } +} + +func TestRuntimePanic(t *testing.T) { + testenv.MustHaveExec(t) + cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestRuntimePanic")) + cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_PANIC=1") + out, err := cmd.CombinedOutput() + t.Logf("%s", out) + if err == nil { + t.Error("child process did not fail") + } else if want := "runtime.unexportedPanicForTesting"; !bytes.Contains(out, []byte(want)) { + t.Errorf("output did not contain expected string %q", want) + } +} + +// Test that g0 stack overflows are handled gracefully. +func TestG0StackOverflow(t *testing.T) { + testenv.MustHaveExec(t) + + switch runtime.GOOS { + case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "android": + t.Skipf("g0 stack is wrong on pthread platforms (see golang.org/issue/26061)") + } + + if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" { + cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestG0StackOverflow", "-test.v")) + cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1") + out, err := cmd.CombinedOutput() + // Don't check err since it's expected to crash. + if n := strings.Count(string(out), "morestack on g0\n"); n != 1 { + t.Fatalf("%s\n(exit status %v)", out, err) + } + // Check that it's a signal-style traceback. + if runtime.GOOS != "windows" { + if want := "PC="; !strings.Contains(string(out), want) { + t.Errorf("output does not contain %q:\n%s", want, out) + } + } + return + } + + runtime.G0StackOverflow() +} diff --git a/libgo/go/runtime/debug.go b/libgo/go/runtime/debug.go index 7cddd29..5be2ec4 100644 --- a/libgo/go/runtime/debug.go +++ b/libgo/go/runtime/debug.go @@ -15,6 +15,10 @@ import ( // The number of logical CPUs on the local machine can be queried with NumCPU. // This call will go away when the scheduler improves. func GOMAXPROCS(n int) int { + if GOARCH == "wasm" && n > 1 { + n = 1 // WebAssembly has no threads yet, so only one CPU is possible. + } + lock(&sched.lock) ret := int(gomaxprocs) unlock(&sched.lock) diff --git a/libgo/go/runtime/debug/heapdump_test.go b/libgo/go/runtime/debug/heapdump_test.go index 7d5b950..c986efc 100644 --- a/libgo/go/runtime/debug/heapdump_test.go +++ b/libgo/go/runtime/debug/heapdump_test.go @@ -13,8 +13,8 @@ import ( ) func TestWriteHeapDumpNonempty(t *testing.T) { - if runtime.GOOS == "nacl" { - t.Skip("WriteHeapDump is not available on NaCl.") + if runtime.GOOS == "nacl" || runtime.GOOS == "js" { + t.Skipf("WriteHeapDump is not available on %s.", runtime.GOOS) } f, err := ioutil.TempFile("", "heapdumptest") if err != nil { @@ -42,8 +42,8 @@ func objfin(x *Obj) { } func TestWriteHeapDumpFinalizers(t *testing.T) { - if runtime.GOOS == "nacl" { - t.Skip("WriteHeapDump is not available on NaCl.") + if runtime.GOOS == "nacl" || runtime.GOOS == "js" { + t.Skipf("WriteHeapDump is not available on %s.", runtime.GOOS) } f, err := ioutil.TempFile("", "heapdumptest") if err != nil { diff --git a/libgo/go/runtime/debug_test.go b/libgo/go/runtime/debug_test.go new file mode 100644 index 0000000..38c764f --- /dev/null +++ b/libgo/go/runtime/debug_test.go @@ -0,0 +1,207 @@ +// Copyright 2018 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. + +// TODO: This test could be implemented on all (most?) UNIXes if we +// added syscall.Tgkill more widely. + +// We skip all of these tests under race mode because our test thread +// spends all of its time in the race runtime, which isn't a safe +// point. + +// +build ignore_for_gccgo +// +build amd64 +// +build linux +// +build !race + +package runtime_test + +import ( + "fmt" + "runtime" + "runtime/debug" + "sync/atomic" + "syscall" + "testing" +) + +func startDebugCallWorker(t *testing.T) (g *runtime.G, after func()) { + // This can deadlock if there aren't enough threads or if a GC + // tries to interrupt an atomic loop (see issue #10958). + ogomaxprocs := runtime.GOMAXPROCS(2) + ogcpercent := debug.SetGCPercent(-1) + + ready := make(chan *runtime.G) + var stop uint32 + done := make(chan error) + go debugCallWorker(ready, &stop, done) + g = <-ready + return g, func() { + atomic.StoreUint32(&stop, 1) + err := <-done + if err != nil { + t.Fatal(err) + } + runtime.GOMAXPROCS(ogomaxprocs) + debug.SetGCPercent(ogcpercent) + } +} + +func debugCallWorker(ready chan<- *runtime.G, stop *uint32, done chan<- error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ready <- runtime.Getg() + + x := 2 + debugCallWorker2(stop, &x) + if x != 1 { + done <- fmt.Errorf("want x = 2, got %d; register pointer not adjusted?", x) + } + close(done) +} + +func debugCallWorker2(stop *uint32, x *int) { + for atomic.LoadUint32(stop) == 0 { + // Strongly encourage x to live in a register so we + // can test pointer register adjustment. + *x++ + } + *x = 1 +} + +func debugCallTKill(tid int) error { + return syscall.Tgkill(syscall.Getpid(), tid, syscall.SIGTRAP) +} + +func TestDebugCall(t *testing.T) { + g, after := startDebugCallWorker(t) + defer after() + + // Inject a call into the debugCallWorker goroutine and test + // basic argument and result passing. + var args struct { + x int + yRet int + } + fn := func(x int) (yRet int) { + return x + 1 + } + args.x = 42 + if _, err := runtime.InjectDebugCall(g, fn, &args, debugCallTKill); err != nil { + t.Fatal(err) + } + if args.yRet != 43 { + t.Fatalf("want 43, got %d", args.yRet) + } +} + +func TestDebugCallLarge(t *testing.T) { + g, after := startDebugCallWorker(t) + defer after() + + // Inject a call with a large call frame. + const N = 128 + var args struct { + in [N]int + out [N]int + } + fn := func(in [N]int) (out [N]int) { + for i := range in { + out[i] = in[i] + 1 + } + return + } + var want [N]int + for i := range args.in { + args.in[i] = i + want[i] = i + 1 + } + if _, err := runtime.InjectDebugCall(g, fn, &args, debugCallTKill); err != nil { + t.Fatal(err) + } + if want != args.out { + t.Fatalf("want %v, got %v", want, args.out) + } +} + +func TestDebugCallGC(t *testing.T) { + g, after := startDebugCallWorker(t) + defer after() + + // Inject a call that performs a GC. + if _, err := runtime.InjectDebugCall(g, runtime.GC, nil, debugCallTKill); err != nil { + t.Fatal(err) + } +} + +func TestDebugCallGrowStack(t *testing.T) { + g, after := startDebugCallWorker(t) + defer after() + + // Inject a call that grows the stack. debugCallWorker checks + // for stack pointer breakage. + if _, err := runtime.InjectDebugCall(g, func() { growStack(nil) }, nil, debugCallTKill); err != nil { + t.Fatal(err) + } +} + +//go:nosplit +func debugCallUnsafePointWorker(gpp **runtime.G, ready, stop *uint32) { + // The nosplit causes this function to not contain safe-points + // except at calls. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + *gpp = runtime.Getg() + + for atomic.LoadUint32(stop) == 0 { + atomic.StoreUint32(ready, 1) + } +} + +func TestDebugCallUnsafePoint(t *testing.T) { + // This can deadlock if there aren't enough threads or if a GC + // tries to interrupt an atomic loop (see issue #10958). + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) + defer debug.SetGCPercent(debug.SetGCPercent(-1)) + + // Test that the runtime refuses call injection at unsafe points. + var g *runtime.G + var ready, stop uint32 + defer atomic.StoreUint32(&stop, 1) + go debugCallUnsafePointWorker(&g, &ready, &stop) + for atomic.LoadUint32(&ready) == 0 { + runtime.Gosched() + } + + _, err := runtime.InjectDebugCall(g, func() {}, nil, debugCallTKill) + if msg := "call not at safe point"; err == nil || err.Error() != msg { + t.Fatalf("want %q, got %s", msg, err) + } +} + +func TestDebugCallPanic(t *testing.T) { + // This can deadlock if there aren't enough threads. + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) + + ready := make(chan *runtime.G) + var stop uint32 + defer atomic.StoreUint32(&stop, 1) + go func() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ready <- runtime.Getg() + for atomic.LoadUint32(&stop) == 0 { + } + }() + g := <-ready + + p, err := runtime.InjectDebugCall(g, func() { panic("test") }, nil, debugCallTKill) + if err != nil { + t.Fatal(err) + } + if ps, ok := p.(string); !ok || ps != "test" { + t.Fatalf("wanted panic %v, got %v", "test", p) + } +} diff --git a/libgo/go/runtime/env_posix.go b/libgo/go/runtime/env_posix.go index ddf3c02..399e88f 100644 --- a/libgo/go/runtime/env_posix.go +++ b/libgo/go/runtime/env_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package runtime diff --git a/libgo/go/runtime/error.go b/libgo/go/runtime/error.go index 1a038cf..b1a3f68 100644 --- a/libgo/go/runtime/error.go +++ b/libgo/go/runtime/error.go @@ -19,55 +19,40 @@ type Error interface { // A TypeAssertionError explains a failed type assertion. type TypeAssertionError struct { - interfaceString string - concreteString string - assertedString string - missingMethod string // one method needed by Interface, missing from Concrete + _interface *_type + concrete *_type + asserted *_type + missingMethod string // one method needed by Interface, missing from Concrete } func (*TypeAssertionError) RuntimeError() {} func (e *TypeAssertionError) Error() string { - inter := e.interfaceString - if inter == "" { - inter = "interface" + inter := "interface" + if e._interface != nil { + inter = e._interface.string() } - if e.concreteString == "" { - return "interface conversion: " + inter + " is nil, not " + e.assertedString + as := e.asserted.string() + if e.concrete == nil { + return "interface conversion: " + inter + " is nil, not " + as } + cs := e.concrete.string() if e.missingMethod == "" { - return "interface conversion: " + inter + " is " + e.concreteString + - ", not " + e.assertedString + msg := "interface conversion: " + inter + " is " + cs + ", not " + as + if cs == as { + // provide slightly clearer error message + if e.concrete.pkgpath() != e.asserted.pkgpath() { + msg += " (types from different packages)" + } else { + msg += " (types from different scopes)" + } + } + return msg } - return "interface conversion: " + e.concreteString + " is not " + e.assertedString + + return "interface conversion: " + cs + " is not " + as + ": missing method " + e.missingMethod } -// For calling from C. -func NewTypeAssertionError(ps1, ps2, ps3 *string, pmeth *string, ret *interface{}) { - var s1, s2, s3, meth string - - if ps1 != nil { - s1 = *ps1 - } - if ps2 != nil { - s2 = *ps2 - } - if ps3 != nil { - s3 = *ps3 - } - if pmeth != nil { - meth = *pmeth - } - - // For gccgo, strip out quoted strings. - s1 = unquote(s1) - s2 = unquote(s2) - s3 = unquote(s3) - - *ret = &TypeAssertionError{s1, s2, s3, meth} -} - // Remove quoted strings from gccgo reflection strings. func unquote(s string) string { ls := len(s) @@ -135,7 +120,7 @@ type stringer interface { func typestring(x interface{}) string { e := efaceOf(&x) - return *e._type.string + return e._type.string() } // printany prints an argument passed to panic. diff --git a/libgo/go/runtime/export_debug_test.go b/libgo/go/runtime/export_debug_test.go new file mode 100644 index 0000000..2d2d535 --- /dev/null +++ b/libgo/go/runtime/export_debug_test.go @@ -0,0 +1,169 @@ +// Copyright 2018 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 ignore_for_gccgo +// +build amd64 +// +build linux + +package runtime + +import ( + "runtime/internal/sys" + "unsafe" +) + +// InjectDebugCall injects a debugger call to fn into g. args must be +// a pointer to a valid call frame (including arguments and return +// space) for fn, or nil. tkill must be a function that will send +// SIGTRAP to thread ID tid. gp must be locked to its OS thread and +// running. +// +// On success, InjectDebugCall returns the panic value of fn or nil. +// If fn did not panic, its results will be available in args. +func InjectDebugCall(gp *g, fn, args interface{}, tkill func(tid int) error) (interface{}, error) { + if gp.lockedm == 0 { + return nil, plainError("goroutine not locked to thread") + } + + tid := int(gp.lockedm.ptr().procid) + if tid == 0 { + return nil, plainError("missing tid") + } + + f := efaceOf(&fn) + if f._type == nil || f._type.kind&kindMask != kindFunc { + return nil, plainError("fn must be a function") + } + fv := (*funcval)(f.data) + + a := efaceOf(&args) + if a._type != nil && a._type.kind&kindMask != kindPtr { + return nil, plainError("args must be a pointer or nil") + } + argp := a.data + var argSize uintptr + if argp != nil { + argSize = (*ptrtype)(unsafe.Pointer(a._type)).elem.size + } + + h := new(debugCallHandler) + h.gp = gp + h.fv, h.argp, h.argSize = fv, argp, argSize + h.handleF = h.handle // Avoid allocating closure during signal + noteclear(&h.done) + + defer func() { testSigtrap = nil }() + testSigtrap = h.inject + if err := tkill(tid); err != nil { + return nil, err + } + // Wait for completion. + notetsleepg(&h.done, -1) + if len(h.err) != 0 { + return nil, h.err + } + return h.panic, nil +} + +type debugCallHandler struct { + gp *g + fv *funcval + argp unsafe.Pointer + argSize uintptr + panic interface{} + + handleF func(info *siginfo, ctxt *sigctxt, gp2 *g) bool + + err plainError + done note + savedRegs sigcontext + savedFP fpstate1 +} + +func (h *debugCallHandler) inject(info *siginfo, ctxt *sigctxt, gp2 *g) bool { + switch h.gp.atomicstatus { + case _Grunning: + if getg().m != h.gp.m { + println("trap on wrong M", getg().m, h.gp.m) + return false + } + // Push current PC on the stack. + rsp := ctxt.rsp() - sys.PtrSize + *(*uint64)(unsafe.Pointer(uintptr(rsp))) = ctxt.rip() + ctxt.set_rsp(rsp) + // Write the argument frame size. + *(*uintptr)(unsafe.Pointer(uintptr(rsp - 16))) = h.argSize + // Save current registers. + h.savedRegs = *ctxt.regs() + h.savedFP = *h.savedRegs.fpstate + h.savedRegs.fpstate = nil + // Set PC to debugCallV1. + ctxt.set_rip(uint64(funcPC(debugCallV1))) + default: + h.err = plainError("goroutine in unexpected state at call inject") + return true + } + // Switch to the debugCall protocol and resume execution. + testSigtrap = h.handleF + return true +} + +func (h *debugCallHandler) handle(info *siginfo, ctxt *sigctxt, gp2 *g) bool { + // Sanity check. + if getg().m != h.gp.m { + println("trap on wrong M", getg().m, h.gp.m) + return false + } + f := findfunc(uintptr(ctxt.rip())) + if !(hasprefix(funcname(f), "runtime.debugCall") || hasprefix(funcname(f), "debugCall")) { + println("trap in unknown function", funcname(f)) + return false + } + if *(*byte)(unsafe.Pointer(uintptr(ctxt.rip() - 1))) != 0xcc { + println("trap at non-INT3 instruction pc =", hex(ctxt.rip())) + return false + } + + switch status := ctxt.rax(); status { + case 0: + // Frame is ready. Copy the arguments to the frame. + sp := ctxt.rsp() + memmove(unsafe.Pointer(uintptr(sp)), h.argp, h.argSize) + // Push return PC. + sp -= sys.PtrSize + ctxt.set_rsp(sp) + *(*uint64)(unsafe.Pointer(uintptr(sp))) = ctxt.rip() + // Set PC to call and context register. + ctxt.set_rip(uint64(h.fv.fn)) + ctxt.regs().rcx = uint64(uintptr(unsafe.Pointer(h.fv))) + case 1: + // Function returned. Copy frame back out. + sp := ctxt.rsp() + memmove(h.argp, unsafe.Pointer(uintptr(sp)), h.argSize) + case 2: + // Function panicked. Copy panic out. + sp := ctxt.rsp() + memmove(unsafe.Pointer(&h.panic), unsafe.Pointer(uintptr(sp)), 2*sys.PtrSize) + case 8: + // Call isn't safe. Get the reason. + sp := ctxt.rsp() + reason := *(*string)(unsafe.Pointer(uintptr(sp))) + h.err = plainError(reason) + case 16: + // Restore all registers except RIP and RSP. + rip, rsp := ctxt.rip(), ctxt.rsp() + fp := ctxt.regs().fpstate + *ctxt.regs() = h.savedRegs + ctxt.regs().fpstate = fp + *fp = h.savedFP + ctxt.set_rip(rip) + ctxt.set_rsp(rsp) + // Done + notewakeup(&h.done) + default: + h.err = plainError("unexpected debugCallV1 status") + } + // Resume execution. + return true +} diff --git a/libgo/go/runtime/export_linux_test.go b/libgo/go/runtime/export_linux_test.go index 183a6ee..96ff1c7 100644 --- a/libgo/go/runtime/export_linux_test.go +++ b/libgo/go/runtime/export_linux_test.go @@ -6,5 +6,11 @@ package runtime -//var NewOSProc0 = newosproc0 -//var Mincore = mincore +import "unsafe" + +// var NewOSProc0 = newosproc0 +// var Mincore = mincore + +func Epollctl(epfd, op, fd int32, ev unsafe.Pointer) int32 { + return epollctl(epfd, op, fd, (*epollevent)(ev)) +} diff --git a/libgo/go/runtime/export_test.go b/libgo/go/runtime/export_test.go index 5e798e3..7f4811c 100644 --- a/libgo/go/runtime/export_test.go +++ b/libgo/go/runtime/export_test.go @@ -21,7 +21,6 @@ import ( //var Fcmp64 = fcmp64 //var Fintto64 = fintto64 //var F64toint = f64toint -//var Sqrt = sqrt var Entersyscall = entersyscall var Exitsyscall = exitsyscall @@ -372,6 +371,8 @@ func (rw *RWMutex) Unlock() { rw.rw.unlock() } +const RuntimeHmapSize = unsafe.Sizeof(hmap{}) + func MapBucketsCount(m map[int]int) int { h := *(**hmap)(unsafe.Pointer(&m)) return 1 << h.B @@ -395,3 +396,61 @@ func LockOSCounts() (external, internal uint32) { } return g.m.lockedExt, g.m.lockedInt } + +func KeepNArenaHints(n int) { + hint := mheap_.arenaHints + for i := 1; i < n; i++ { + hint = hint.next + if hint == nil { + return + } + } + hint.next = nil +} + +// MapNextArenaHint reserves a page at the next arena growth hint, +// preventing the arena from growing there, and returns the range of +// addresses that are no longer viable. +func MapNextArenaHint() (start, end uintptr) { + hint := mheap_.arenaHints + addr := hint.addr + if hint.down { + start, end = addr-heapArenaBytes, addr + addr -= physPageSize + } else { + start, end = addr, addr+heapArenaBytes + } + sysReserve(unsafe.Pointer(addr), physPageSize) + return +} + +func GetNextArenaHint() uintptr { + return mheap_.arenaHints.addr +} + +type G = g + +func Getg() *G { + return getg() +} + +//go:noinline +func PanicForTesting(b []byte, i int) byte { + return unexportedPanicForTesting(b, i) +} + +//go:noinline +func unexportedPanicForTesting(b []byte, i int) byte { + return b[i] +} + +func G0StackOverflow() { + systemstack(func() { + stackOverflow(nil) + }) +} + +func stackOverflow(x *byte) { + var buf [256]byte + stackOverflow(&buf[0]) +} diff --git a/libgo/go/runtime/extern.go b/libgo/go/runtime/extern.go index b3afd10..c9d10f1 100644 --- a/libgo/go/runtime/extern.go +++ b/libgo/go/runtime/extern.go @@ -116,6 +116,12 @@ It is a comma-separated list of name=val pairs setting these named variables: schedtrace: setting schedtrace=X causes the scheduler to emit a single line to standard error every X milliseconds, summarizing the scheduler state. + tracebackancestors: setting tracebackancestors=N extends tracebacks with the stacks at + which goroutines were created, where N limits the number of ancestor goroutines to + report. This also extends the information returned by runtime.Stack. Ancestor's goroutine + IDs will refer to the ID of the goroutine at the time of creation; it's possible for this + ID to be reused for another goroutine. Setting N to 0 will report no ancestry information. + The net and net/http packages also refer to debugging variables in GODEBUG. See the documentation for those packages for details. diff --git a/libgo/go/runtime/gc_test.go b/libgo/go/runtime/gc_test.go index a8c52d2..180919b 100644 --- a/libgo/go/runtime/gc_test.go +++ b/libgo/go/runtime/gc_test.go @@ -10,6 +10,7 @@ import ( "reflect" "runtime" "runtime/debug" + "sync" "sync/atomic" "testing" "time" @@ -44,7 +45,7 @@ func TestGcDeepNesting(t *testing.T) { } } -func TestGcHashmapIndirection(t *testing.T) { +func TestGcMapIndirection(t *testing.T) { defer debug.SetGCPercent(debug.SetGCPercent(1)) runtime.GC() type T struct { @@ -157,6 +158,10 @@ func TestHugeGCInfo(t *testing.T) { /* func TestPeriodicGC(t *testing.T) { + if runtime.GOARCH == "wasm" { + t.Skip("no sysmon on wasm yet") + } + // Make sure we're not in the middle of a GC. runtime.GC() @@ -642,3 +647,34 @@ func BenchmarkBulkWriteBarrier(b *testing.B) { runtime.KeepAlive(ptrs) } + +func BenchmarkScanStackNoLocals(b *testing.B) { + var ready sync.WaitGroup + teardown := make(chan bool) + for j := 0; j < 10; j++ { + ready.Add(1) + go func() { + x := 100000 + countpwg(&x, &ready, teardown) + }() + } + ready.Wait() + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StartTimer() + runtime.GC() + runtime.GC() + b.StopTimer() + } + close(teardown) +} + +func countpwg(n *int, ready *sync.WaitGroup, teardown chan bool) { + if *n == 0 { + ready.Done() + <-teardown + return + } + *n-- + countpwg(n, ready, teardown) +} diff --git a/libgo/go/runtime/gcinfo_test.go b/libgo/go/runtime/gcinfo_test.go index 4ac67dc..ca012bb 100644 --- a/libgo/go/runtime/gcinfo_test.go +++ b/libgo/go/runtime/gcinfo_test.go @@ -133,7 +133,7 @@ func infoBigStruct() []byte { typeScalar, typeScalar, typeScalar, typeScalar, // t int; y uint16; u uint64 typePointer, typeScalar, // i string } - case "arm64", "amd64", "mips64", "mips64le", "ppc64", "ppc64le", "s390x": + case "arm64", "amd64", "mips64", "mips64le", "ppc64", "ppc64le", "s390x", "wasm": return []byte{ typePointer, // q *int typeScalar, typeScalar, typeScalar, // w byte; e [17]byte @@ -186,6 +186,6 @@ var ( infoString = []byte{typePointer, typeScalar} infoSlice = []byte{typePointer, typeScalar, typeScalar} - infoEface = []byte{typePointer, typePointer} - infoIface = []byte{typePointer, typePointer} + infoEface = []byte{typeScalar, typePointer} + infoIface = []byte{typeScalar, typePointer} ) diff --git a/libgo/go/runtime/hash64.go b/libgo/go/runtime/hash64.go index 74775a8..7c6513e 100644 --- a/libgo/go/runtime/hash64.go +++ b/libgo/go/runtime/hash64.go @@ -6,7 +6,7 @@ // xxhash: https://code.google.com/p/xxhash/ // cityhash: https://code.google.com/p/cityhash/ -// +build amd64 amd64p32 arm64 mips64 mips64le ppc64 ppc64le s390x alpha arm64be ia64 mips64p32 mips64p32le sparc64 riscv64 +// +build amd64 amd64p32 arm64 mips64 mips64le ppc64 ppc64le s390x wasm alpha arm64be ia64 mips64p32 mips64p32le sparc64 riscv64 package runtime @@ -26,7 +26,8 @@ const ( ) func memhash(p unsafe.Pointer, seed, s uintptr) uintptr { - if GOARCH == "amd64" && GOOS != "nacl" && useAeshash { + if (GOARCH == "amd64" || GOARCH == "arm64") && + GOOS != "nacl" && useAeshash { return aeshash(p, seed, s) } h := uint64(seed + s*hashkey[0]) diff --git a/libgo/go/runtime/hash_test.go b/libgo/go/runtime/hash_test.go index 54c9160..070edb6 100644 --- a/libgo/go/runtime/hash_test.go +++ b/libgo/go/runtime/hash_test.go @@ -161,6 +161,9 @@ func TestSmhasherZeros(t *testing.T) { // Strings with up to two nonzero bytes all have distinct hashes. func TestSmhasherTwoNonzero(t *testing.T) { + if GOARCH == "wasm" { + t.Skip("Too slow on wasm") + } if testing.Short() { t.Skip("Skipping in short mode") } @@ -229,6 +232,9 @@ func TestSmhasherCyclic(t *testing.T) { // Test strings with only a few bits set func TestSmhasherSparse(t *testing.T) { + if GOARCH == "wasm" { + t.Skip("Too slow on wasm") + } if testing.Short() { t.Skip("Skipping in short mode") } @@ -264,6 +270,9 @@ func setbits(h *HashSet, b []byte, i int, k int) { // Test all possible combinations of n blocks from the set s. // "permutation" is a bad name here, but it is what Smhasher uses. func TestSmhasherPermutation(t *testing.T) { + if GOARCH == "wasm" { + t.Skip("Too slow on wasm") + } if testing.Short() { t.Skip("Skipping in short mode") } @@ -433,6 +442,9 @@ func (k *IfaceKey) name() string { // Flipping a single bit of a key should flip each output bit with 50% probability. func TestSmhasherAvalanche(t *testing.T) { + if GOARCH == "wasm" { + t.Skip("Too slow on wasm") + } if testing.Short() { t.Skip("Skipping in short mode") } @@ -508,6 +520,9 @@ func TestSmhasherWindowed(t *testing.T) { windowed(t, &BytesKey{make([]byte, 128)}) } func windowed(t *testing.T, k Key) { + if GOARCH == "wasm" { + t.Skip("Too slow on wasm") + } if testing.Short() { t.Skip("Skipping in short mode") } diff --git a/libgo/go/runtime/hashmap_fast.go b/libgo/go/runtime/hashmap_fast.go deleted file mode 100644 index e0fc981..0000000 --- a/libgo/go/runtime/hashmap_fast.go +++ /dev/null @@ -1,1237 +0,0 @@ -// Copyright 2014 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 runtime - -import ( - "runtime/internal/sys" - "unsafe" -) - -func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer { - if raceenabled && h != nil { - callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_fast32)) - } - if h == nil || h.count == 0 { - return unsafe.Pointer(&zeroVal[0]) - } - if h.flags&hashWriting != 0 { - throw("concurrent map read and map write") - } - var b *bmap - if h.B == 0 { - // One-bucket table. No need to hash. - b = (*bmap)(h.buckets) - } else { - hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) - m := bucketMask(h.B) - b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) - if c := h.oldbuckets; c != nil { - if !h.sameSizeGrow() { - // There used to be half as many buckets; mask down one more power of two. - m >>= 1 - } - oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) - if !evacuated(oldb) { - b = oldb - } - } - } - for ; b != nil; b = b.overflow(t) { - for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 4) { - if *(*uint32)(k) == key && b.tophash[i] != empty { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize)) - } - } - } - return unsafe.Pointer(&zeroVal[0]) -} - -func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) { - if raceenabled && h != nil { - callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_fast32)) - } - if h == nil || h.count == 0 { - return unsafe.Pointer(&zeroVal[0]), false - } - if h.flags&hashWriting != 0 { - throw("concurrent map read and map write") - } - var b *bmap - if h.B == 0 { - // One-bucket table. No need to hash. - b = (*bmap)(h.buckets) - } else { - hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) - m := bucketMask(h.B) - b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) - if c := h.oldbuckets; c != nil { - if !h.sameSizeGrow() { - // There used to be half as many buckets; mask down one more power of two. - m >>= 1 - } - oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) - if !evacuated(oldb) { - b = oldb - } - } - } - for ; b != nil; b = b.overflow(t) { - for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 4) { - if *(*uint32)(k) == key && b.tophash[i] != empty { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize)), true - } - } - } - return unsafe.Pointer(&zeroVal[0]), false -} - -func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer { - if raceenabled && h != nil { - callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_fast64)) - } - if h == nil || h.count == 0 { - return unsafe.Pointer(&zeroVal[0]) - } - if h.flags&hashWriting != 0 { - throw("concurrent map read and map write") - } - var b *bmap - if h.B == 0 { - // One-bucket table. No need to hash. - b = (*bmap)(h.buckets) - } else { - hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) - m := bucketMask(h.B) - b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) - if c := h.oldbuckets; c != nil { - if !h.sameSizeGrow() { - // There used to be half as many buckets; mask down one more power of two. - m >>= 1 - } - oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) - if !evacuated(oldb) { - b = oldb - } - } - } - for ; b != nil; b = b.overflow(t) { - for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 8) { - if *(*uint64)(k) == key && b.tophash[i] != empty { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize)) - } - } - } - return unsafe.Pointer(&zeroVal[0]) -} - -func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) { - if raceenabled && h != nil { - callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_fast64)) - } - if h == nil || h.count == 0 { - return unsafe.Pointer(&zeroVal[0]), false - } - if h.flags&hashWriting != 0 { - throw("concurrent map read and map write") - } - var b *bmap - if h.B == 0 { - // One-bucket table. No need to hash. - b = (*bmap)(h.buckets) - } else { - hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) - m := bucketMask(h.B) - b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) - if c := h.oldbuckets; c != nil { - if !h.sameSizeGrow() { - // There used to be half as many buckets; mask down one more power of two. - m >>= 1 - } - oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) - if !evacuated(oldb) { - b = oldb - } - } - } - for ; b != nil; b = b.overflow(t) { - for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 8) { - if *(*uint64)(k) == key && b.tophash[i] != empty { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize)), true - } - } - } - return unsafe.Pointer(&zeroVal[0]), false -} - -func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer { - if raceenabled && h != nil { - callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_faststr)) - } - if h == nil || h.count == 0 { - return unsafe.Pointer(&zeroVal[0]) - } - if h.flags&hashWriting != 0 { - throw("concurrent map read and map write") - } - key := stringStructOf(&ky) - if h.B == 0 { - // One-bucket table. - b := (*bmap)(h.buckets) - if key.len < 32 { - // short key, doing lots of comparisons is ok - for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { - k := (*stringStruct)(kptr) - if k.len != key.len || b.tophash[i] == empty { - continue - } - if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)) - } - } - return unsafe.Pointer(&zeroVal[0]) - } - // long key, try not to do more comparisons than necessary - keymaybe := uintptr(bucketCnt) - for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { - k := (*stringStruct)(kptr) - if k.len != key.len || b.tophash[i] == empty { - continue - } - if k.str == key.str { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)) - } - // check first 4 bytes - if *((*[4]byte)(key.str)) != *((*[4]byte)(k.str)) { - continue - } - // check last 4 bytes - if *((*[4]byte)(add(key.str, uintptr(key.len)-4))) != *((*[4]byte)(add(k.str, uintptr(key.len)-4))) { - continue - } - if keymaybe != bucketCnt { - // Two keys are potential matches. Use hash to distinguish them. - goto dohash - } - keymaybe = i - } - if keymaybe != bucketCnt { - k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*sys.PtrSize)) - if memequal(k.str, key.str, uintptr(key.len)) { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+keymaybe*uintptr(t.valuesize)) - } - } - return unsafe.Pointer(&zeroVal[0]) - } -dohash: - hash := t.key.hashfn(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0)) - m := bucketMask(h.B) - b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) - if c := h.oldbuckets; c != nil { - if !h.sameSizeGrow() { - // There used to be half as many buckets; mask down one more power of two. - m >>= 1 - } - oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) - if !evacuated(oldb) { - b = oldb - } - } - top := tophash(hash) - for ; b != nil; b = b.overflow(t) { - for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { - k := (*stringStruct)(kptr) - if k.len != key.len || b.tophash[i] != top { - continue - } - if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)) - } - } - } - return unsafe.Pointer(&zeroVal[0]) -} - -func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) { - if raceenabled && h != nil { - callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr)) - } - if h == nil || h.count == 0 { - return unsafe.Pointer(&zeroVal[0]), false - } - if h.flags&hashWriting != 0 { - throw("concurrent map read and map write") - } - key := stringStructOf(&ky) - if h.B == 0 { - // One-bucket table. - b := (*bmap)(h.buckets) - if key.len < 32 { - // short key, doing lots of comparisons is ok - for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { - k := (*stringStruct)(kptr) - if k.len != key.len || b.tophash[i] == empty { - continue - } - if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)), true - } - } - return unsafe.Pointer(&zeroVal[0]), false - } - // long key, try not to do more comparisons than necessary - keymaybe := uintptr(bucketCnt) - for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { - k := (*stringStruct)(kptr) - if k.len != key.len || b.tophash[i] == empty { - continue - } - if k.str == key.str { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)), true - } - // check first 4 bytes - if *((*[4]byte)(key.str)) != *((*[4]byte)(k.str)) { - continue - } - // check last 4 bytes - if *((*[4]byte)(add(key.str, uintptr(key.len)-4))) != *((*[4]byte)(add(k.str, uintptr(key.len)-4))) { - continue - } - if keymaybe != bucketCnt { - // Two keys are potential matches. Use hash to distinguish them. - goto dohash - } - keymaybe = i - } - if keymaybe != bucketCnt { - k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*sys.PtrSize)) - if memequal(k.str, key.str, uintptr(key.len)) { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+keymaybe*uintptr(t.valuesize)), true - } - } - return unsafe.Pointer(&zeroVal[0]), false - } -dohash: - hash := t.key.hashfn(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0)) - m := bucketMask(h.B) - b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) - if c := h.oldbuckets; c != nil { - if !h.sameSizeGrow() { - // There used to be half as many buckets; mask down one more power of two. - m >>= 1 - } - oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) - if !evacuated(oldb) { - b = oldb - } - } - top := tophash(hash) - for ; b != nil; b = b.overflow(t) { - for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { - k := (*stringStruct)(kptr) - if k.len != key.len || b.tophash[i] != top { - continue - } - if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)), true - } - } - } - return unsafe.Pointer(&zeroVal[0]), false -} - -func mapassign_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer { - if h == nil { - panic(plainError("assignment to entry in nil map")) - } - if raceenabled { - callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast32)) - } - if h.flags&hashWriting != 0 { - throw("concurrent map writes") - } - hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) - - // Set hashWriting after calling alg.hash for consistency with mapassign. - h.flags |= hashWriting - - if h.buckets == nil { - h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) - } - -again: - bucket := hash & bucketMask(h.B) - if h.growing() { - growWork_fast32(t, h, bucket) - } - b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) - - var insertb *bmap - var inserti uintptr - var insertk unsafe.Pointer - - for { - for i := uintptr(0); i < bucketCnt; i++ { - if b.tophash[i] == empty { - if insertb == nil { - inserti = i - insertb = b - } - continue - } - k := *((*uint32)(add(unsafe.Pointer(b), dataOffset+i*4))) - if k != key { - continue - } - inserti = i - insertb = b - goto done - } - ovf := b.overflow(t) - if ovf == nil { - break - } - b = ovf - } - - // Did not find mapping for key. Allocate new cell & add entry. - - // If we hit the max load factor or we have too many overflow buckets, - // and we're not already in the middle of growing, start growing. - if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { - hashGrow(t, h) - goto again // Growing the table invalidates everything, so try again - } - - if insertb == nil { - // all current buckets are full, allocate a new one. - insertb = h.newoverflow(t, b) - inserti = 0 // not necessary, but avoids needlessly spilling inserti - } - insertb.tophash[inserti&(bucketCnt-1)] = tophash(hash) // mask inserti to avoid bounds checks - - insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*4) - // store new key at insert position - *(*uint32)(insertk) = key - - h.count++ - -done: - val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*4+inserti*uintptr(t.valuesize)) - if h.flags&hashWriting == 0 { - throw("concurrent map writes") - } - h.flags &^= hashWriting - return val -} - -func mapassign_fast32ptr(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { - if h == nil { - panic(plainError("assignment to entry in nil map")) - } - if raceenabled { - callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast32)) - } - if h.flags&hashWriting != 0 { - throw("concurrent map writes") - } - hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) - - // Set hashWriting after calling alg.hash for consistency with mapassign. - h.flags |= hashWriting - - if h.buckets == nil { - h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) - } - -again: - bucket := hash & bucketMask(h.B) - if h.growing() { - growWork_fast32(t, h, bucket) - } - b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) - - var insertb *bmap - var inserti uintptr - var insertk unsafe.Pointer - - for { - for i := uintptr(0); i < bucketCnt; i++ { - if b.tophash[i] == empty { - if insertb == nil { - inserti = i - insertb = b - } - continue - } - k := *((*unsafe.Pointer)(add(unsafe.Pointer(b), dataOffset+i*4))) - if k != key { - continue - } - inserti = i - insertb = b - goto done - } - ovf := b.overflow(t) - if ovf == nil { - break - } - b = ovf - } - - // Did not find mapping for key. Allocate new cell & add entry. - - // If we hit the max load factor or we have too many overflow buckets, - // and we're not already in the middle of growing, start growing. - if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { - hashGrow(t, h) - goto again // Growing the table invalidates everything, so try again - } - - if insertb == nil { - // all current buckets are full, allocate a new one. - insertb = h.newoverflow(t, b) - inserti = 0 // not necessary, but avoids needlessly spilling inserti - } - insertb.tophash[inserti&(bucketCnt-1)] = tophash(hash) // mask inserti to avoid bounds checks - - insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*4) - // store new key at insert position - *(*unsafe.Pointer)(insertk) = key - - h.count++ - -done: - val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*4+inserti*uintptr(t.valuesize)) - if h.flags&hashWriting == 0 { - throw("concurrent map writes") - } - h.flags &^= hashWriting - return val -} - -func mapassign_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer { - if h == nil { - panic(plainError("assignment to entry in nil map")) - } - if raceenabled { - callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast64)) - } - if h.flags&hashWriting != 0 { - throw("concurrent map writes") - } - hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) - - // Set hashWriting after calling alg.hash for consistency with mapassign. - h.flags |= hashWriting - - if h.buckets == nil { - h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) - } - -again: - bucket := hash & bucketMask(h.B) - if h.growing() { - growWork_fast64(t, h, bucket) - } - b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) - - var insertb *bmap - var inserti uintptr - var insertk unsafe.Pointer - - for { - for i := uintptr(0); i < bucketCnt; i++ { - if b.tophash[i] == empty { - if insertb == nil { - insertb = b - inserti = i - } - continue - } - k := *((*uint64)(add(unsafe.Pointer(b), dataOffset+i*8))) - if k != key { - continue - } - insertb = b - inserti = i - goto done - } - ovf := b.overflow(t) - if ovf == nil { - break - } - b = ovf - } - - // Did not find mapping for key. Allocate new cell & add entry. - - // If we hit the max load factor or we have too many overflow buckets, - // and we're not already in the middle of growing, start growing. - if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { - hashGrow(t, h) - goto again // Growing the table invalidates everything, so try again - } - - if insertb == nil { - // all current buckets are full, allocate a new one. - insertb = h.newoverflow(t, b) - inserti = 0 // not necessary, but avoids needlessly spilling inserti - } - insertb.tophash[inserti&(bucketCnt-1)] = tophash(hash) // mask inserti to avoid bounds checks - - insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*8) - // store new key at insert position - *(*uint64)(insertk) = key - - h.count++ - -done: - val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*8+inserti*uintptr(t.valuesize)) - if h.flags&hashWriting == 0 { - throw("concurrent map writes") - } - h.flags &^= hashWriting - return val -} - -func mapassign_fast64ptr(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { - if h == nil { - panic(plainError("assignment to entry in nil map")) - } - if raceenabled { - callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast64)) - } - if h.flags&hashWriting != 0 { - throw("concurrent map writes") - } - hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) - - // Set hashWriting after calling alg.hash for consistency with mapassign. - h.flags |= hashWriting - - if h.buckets == nil { - h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) - } - -again: - bucket := hash & bucketMask(h.B) - if h.growing() { - growWork_fast64(t, h, bucket) - } - b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) - - var insertb *bmap - var inserti uintptr - var insertk unsafe.Pointer - - for { - for i := uintptr(0); i < bucketCnt; i++ { - if b.tophash[i] == empty { - if insertb == nil { - insertb = b - inserti = i - } - continue - } - k := *((*unsafe.Pointer)(add(unsafe.Pointer(b), dataOffset+i*8))) - if k != key { - continue - } - insertb = b - inserti = i - goto done - } - ovf := b.overflow(t) - if ovf == nil { - break - } - b = ovf - } - - // Did not find mapping for key. Allocate new cell & add entry. - - // If we hit the max load factor or we have too many overflow buckets, - // and we're not already in the middle of growing, start growing. - if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { - hashGrow(t, h) - goto again // Growing the table invalidates everything, so try again - } - - if insertb == nil { - // all current buckets are full, allocate a new one. - insertb = h.newoverflow(t, b) - inserti = 0 // not necessary, but avoids needlessly spilling inserti - } - insertb.tophash[inserti&(bucketCnt-1)] = tophash(hash) // mask inserti to avoid bounds checks - - insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*8) - // store new key at insert position - *(*unsafe.Pointer)(insertk) = key - - h.count++ - -done: - val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*8+inserti*uintptr(t.valuesize)) - if h.flags&hashWriting == 0 { - throw("concurrent map writes") - } - h.flags &^= hashWriting - return val -} - -func mapassign_faststr(t *maptype, h *hmap, s string) unsafe.Pointer { - if h == nil { - panic(plainError("assignment to entry in nil map")) - } - if raceenabled { - callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_faststr)) - } - if h.flags&hashWriting != 0 { - throw("concurrent map writes") - } - key := stringStructOf(&s) - hash := t.key.hashfn(noescape(unsafe.Pointer(&s)), uintptr(h.hash0)) - - // Set hashWriting after calling alg.hash for consistency with mapassign. - h.flags |= hashWriting - - if h.buckets == nil { - h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) - } - -again: - bucket := hash & bucketMask(h.B) - if h.growing() { - growWork_faststr(t, h, bucket) - } - b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) - top := tophash(hash) - - var insertb *bmap - var inserti uintptr - var insertk unsafe.Pointer - - for { - for i := uintptr(0); i < bucketCnt; i++ { - if b.tophash[i] != top { - if b.tophash[i] == empty && insertb == nil { - insertb = b - inserti = i - } - continue - } - k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*sys.PtrSize)) - if k.len != key.len { - continue - } - if k.str != key.str && !memequal(k.str, key.str, uintptr(key.len)) { - continue - } - // already have a mapping for key. Update it. - inserti = i - insertb = b - goto done - } - ovf := b.overflow(t) - if ovf == nil { - break - } - b = ovf - } - - // Did not find mapping for key. Allocate new cell & add entry. - - // If we hit the max load factor or we have too many overflow buckets, - // and we're not already in the middle of growing, start growing. - if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { - hashGrow(t, h) - goto again // Growing the table invalidates everything, so try again - } - - if insertb == nil { - // all current buckets are full, allocate a new one. - insertb = h.newoverflow(t, b) - inserti = 0 // not necessary, but avoids needlessly spilling inserti - } - insertb.tophash[inserti&(bucketCnt-1)] = top // mask inserti to avoid bounds checks - - insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*2*sys.PtrSize) - // store new key at insert position - *((*stringStruct)(insertk)) = *key - h.count++ - -done: - val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*2*sys.PtrSize+inserti*uintptr(t.valuesize)) - if h.flags&hashWriting == 0 { - throw("concurrent map writes") - } - h.flags &^= hashWriting - return val -} - -func mapdelete_fast32(t *maptype, h *hmap, key uint32) { - if raceenabled && h != nil { - callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapdelete_fast32)) - } - if h == nil || h.count == 0 { - return - } - if h.flags&hashWriting != 0 { - throw("concurrent map writes") - } - - hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) - - // Set hashWriting after calling alg.hash for consistency with mapdelete - h.flags |= hashWriting - - bucket := hash & bucketMask(h.B) - if h.growing() { - growWork_fast32(t, h, bucket) - } - b := (*bmap)(add(h.buckets, bucket*uintptr(t.bucketsize))) -search: - for ; b != nil; b = b.overflow(t) { - for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 4) { - if key != *(*uint32)(k) || b.tophash[i] == empty { - continue - } - // Only clear key if there are pointers in it. - if t.key.kind&kindNoPointers == 0 { - memclrHasPointers(k, t.key.size) - } - // Only clear value if there are pointers in it. - if t.elem.kind&kindNoPointers == 0 { - v := add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize)) - memclrHasPointers(v, t.elem.size) - } - b.tophash[i] = empty - h.count-- - break search - } - } - - if h.flags&hashWriting == 0 { - throw("concurrent map writes") - } - h.flags &^= hashWriting -} - -func mapdelete_fast64(t *maptype, h *hmap, key uint64) { - if raceenabled && h != nil { - callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapdelete_fast64)) - } - if h == nil || h.count == 0 { - return - } - if h.flags&hashWriting != 0 { - throw("concurrent map writes") - } - - hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) - - // Set hashWriting after calling alg.hash for consistency with mapdelete - h.flags |= hashWriting - - bucket := hash & bucketMask(h.B) - if h.growing() { - growWork_fast64(t, h, bucket) - } - b := (*bmap)(add(h.buckets, bucket*uintptr(t.bucketsize))) -search: - for ; b != nil; b = b.overflow(t) { - for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 8) { - if key != *(*uint64)(k) || b.tophash[i] == empty { - continue - } - // Only clear key if there are pointers in it. - if t.key.kind&kindNoPointers == 0 { - memclrHasPointers(k, t.key.size) - } - // Only clear value if there are pointers in it. - if t.elem.kind&kindNoPointers == 0 { - v := add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize)) - memclrHasPointers(v, t.elem.size) - } - b.tophash[i] = empty - h.count-- - break search - } - } - - if h.flags&hashWriting == 0 { - throw("concurrent map writes") - } - h.flags &^= hashWriting -} - -func mapdelete_faststr(t *maptype, h *hmap, ky string) { - if raceenabled && h != nil { - callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapdelete_faststr)) - } - if h == nil || h.count == 0 { - return - } - if h.flags&hashWriting != 0 { - throw("concurrent map writes") - } - - key := stringStructOf(&ky) - hash := t.key.hashfn(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0)) - - // Set hashWriting after calling alg.hash for consistency with mapdelete - h.flags |= hashWriting - - bucket := hash & bucketMask(h.B) - if h.growing() { - growWork_faststr(t, h, bucket) - } - b := (*bmap)(add(h.buckets, bucket*uintptr(t.bucketsize))) - top := tophash(hash) -search: - for ; b != nil; b = b.overflow(t) { - for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { - k := (*stringStruct)(kptr) - if k.len != key.len || b.tophash[i] != top { - continue - } - if k.str != key.str && !memequal(k.str, key.str, uintptr(key.len)) { - continue - } - // Clear key's pointer. - k.str = nil - // Only clear value if there are pointers in it. - if t.elem.kind&kindNoPointers == 0 { - v := add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)) - memclrHasPointers(v, t.elem.size) - } - b.tophash[i] = empty - h.count-- - break search - } - } - - if h.flags&hashWriting == 0 { - throw("concurrent map writes") - } - h.flags &^= hashWriting -} - -func growWork_fast32(t *maptype, h *hmap, bucket uintptr) { - // make sure we evacuate the oldbucket corresponding - // to the bucket we're about to use - evacuate_fast32(t, h, bucket&h.oldbucketmask()) - - // evacuate one more oldbucket to make progress on growing - if h.growing() { - evacuate_fast32(t, h, h.nevacuate) - } -} - -func evacuate_fast32(t *maptype, h *hmap, oldbucket uintptr) { - b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize))) - newbit := h.noldbuckets() - if !evacuated(b) { - // TODO: reuse overflow buckets instead of using new ones, if there - // is no iterator using the old buckets. (If !oldIterator.) - - // xy contains the x and y (low and high) evacuation destinations. - var xy [2]evacDst - x := &xy[0] - x.b = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize))) - x.k = add(unsafe.Pointer(x.b), dataOffset) - x.v = add(x.k, bucketCnt*4) - - if !h.sameSizeGrow() { - // Only calculate y pointers if we're growing bigger. - // Otherwise GC can see bad pointers. - y := &xy[1] - y.b = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize))) - y.k = add(unsafe.Pointer(y.b), dataOffset) - y.v = add(y.k, bucketCnt*4) - } - - for ; b != nil; b = b.overflow(t) { - k := add(unsafe.Pointer(b), dataOffset) - v := add(k, bucketCnt*4) - for i := 0; i < bucketCnt; i, k, v = i+1, add(k, 4), add(v, uintptr(t.valuesize)) { - top := b.tophash[i] - if top == empty { - b.tophash[i] = evacuatedEmpty - continue - } - if top < minTopHash { - throw("bad map state") - } - var useY uint8 - if !h.sameSizeGrow() { - // Compute hash to make our evacuation decision (whether we need - // to send this key/value to bucket x or bucket y). - hash := t.key.hashfn(k, uintptr(h.hash0)) - if hash&newbit != 0 { - useY = 1 - } - } - - b.tophash[i] = evacuatedX + useY // evacuatedX + 1 == evacuatedY, enforced in makemap - dst := &xy[useY] // evacuation destination - - if dst.i == bucketCnt { - dst.b = h.newoverflow(t, dst.b) - dst.i = 0 - dst.k = add(unsafe.Pointer(dst.b), dataOffset) - dst.v = add(dst.k, bucketCnt*4) - } - dst.b.tophash[dst.i&(bucketCnt-1)] = top // mask dst.i as an optimization, to avoid a bounds check - - // Copy key. - if sys.PtrSize == 4 && t.key.kind&kindNoPointers == 0 && writeBarrier.enabled { - writebarrierptr((*uintptr)(dst.k), *(*uintptr)(k)) - } else { - *(*uint32)(dst.k) = *(*uint32)(k) - } - - typedmemmove(t.elem, dst.v, v) - dst.i++ - // These updates might push these pointers past the end of the - // key or value arrays. That's ok, as we have the overflow pointer - // at the end of the bucket to protect against pointing past the - // end of the bucket. - dst.k = add(dst.k, 4) - dst.v = add(dst.v, uintptr(t.valuesize)) - } - } - // Unlink the overflow buckets & clear key/value to help GC. - if h.flags&oldIterator == 0 && t.bucket.kind&kindNoPointers == 0 { - b := add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)) - // Preserve b.tophash because the evacuation - // state is maintained there. - ptr := add(b, dataOffset) - n := uintptr(t.bucketsize) - dataOffset - memclrHasPointers(ptr, n) - } - } - - if oldbucket == h.nevacuate { - advanceEvacuationMark(h, t, newbit) - } -} - -func growWork_fast64(t *maptype, h *hmap, bucket uintptr) { - // make sure we evacuate the oldbucket corresponding - // to the bucket we're about to use - evacuate_fast64(t, h, bucket&h.oldbucketmask()) - - // evacuate one more oldbucket to make progress on growing - if h.growing() { - evacuate_fast64(t, h, h.nevacuate) - } -} - -func evacuate_fast64(t *maptype, h *hmap, oldbucket uintptr) { - b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize))) - newbit := h.noldbuckets() - if !evacuated(b) { - // TODO: reuse overflow buckets instead of using new ones, if there - // is no iterator using the old buckets. (If !oldIterator.) - - // xy contains the x and y (low and high) evacuation destinations. - var xy [2]evacDst - x := &xy[0] - x.b = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize))) - x.k = add(unsafe.Pointer(x.b), dataOffset) - x.v = add(x.k, bucketCnt*8) - - if !h.sameSizeGrow() { - // Only calculate y pointers if we're growing bigger. - // Otherwise GC can see bad pointers. - y := &xy[1] - y.b = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize))) - y.k = add(unsafe.Pointer(y.b), dataOffset) - y.v = add(y.k, bucketCnt*8) - } - - for ; b != nil; b = b.overflow(t) { - k := add(unsafe.Pointer(b), dataOffset) - v := add(k, bucketCnt*8) - for i := 0; i < bucketCnt; i, k, v = i+1, add(k, 8), add(v, uintptr(t.valuesize)) { - top := b.tophash[i] - if top == empty { - b.tophash[i] = evacuatedEmpty - continue - } - if top < minTopHash { - throw("bad map state") - } - var useY uint8 - if !h.sameSizeGrow() { - // Compute hash to make our evacuation decision (whether we need - // to send this key/value to bucket x or bucket y). - hash := t.key.hashfn(k, uintptr(h.hash0)) - if hash&newbit != 0 { - useY = 1 - } - } - - b.tophash[i] = evacuatedX + useY // evacuatedX + 1 == evacuatedY, enforced in makemap - dst := &xy[useY] // evacuation destination - - if dst.i == bucketCnt { - dst.b = h.newoverflow(t, dst.b) - dst.i = 0 - dst.k = add(unsafe.Pointer(dst.b), dataOffset) - dst.v = add(dst.k, bucketCnt*8) - } - dst.b.tophash[dst.i&(bucketCnt-1)] = top // mask dst.i as an optimization, to avoid a bounds check - - // Copy key. - if t.key.kind&kindNoPointers == 0 && writeBarrier.enabled { - if sys.PtrSize == 8 { - writebarrierptr((*uintptr)(dst.k), *(*uintptr)(k)) - } else { - // There are three ways to squeeze at least one 32 bit pointer into 64 bits. - // Give up and call typedmemmove. - typedmemmove(t.key, dst.k, k) - } - } else { - *(*uint64)(dst.k) = *(*uint64)(k) - } - - typedmemmove(t.elem, dst.v, v) - dst.i++ - // These updates might push these pointers past the end of the - // key or value arrays. That's ok, as we have the overflow pointer - // at the end of the bucket to protect against pointing past the - // end of the bucket. - dst.k = add(dst.k, 8) - dst.v = add(dst.v, uintptr(t.valuesize)) - } - } - // Unlink the overflow buckets & clear key/value to help GC. - if h.flags&oldIterator == 0 && t.bucket.kind&kindNoPointers == 0 { - b := add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)) - // Preserve b.tophash because the evacuation - // state is maintained there. - ptr := add(b, dataOffset) - n := uintptr(t.bucketsize) - dataOffset - memclrHasPointers(ptr, n) - } - } - - if oldbucket == h.nevacuate { - advanceEvacuationMark(h, t, newbit) - } -} - -func growWork_faststr(t *maptype, h *hmap, bucket uintptr) { - // make sure we evacuate the oldbucket corresponding - // to the bucket we're about to use - evacuate_faststr(t, h, bucket&h.oldbucketmask()) - - // evacuate one more oldbucket to make progress on growing - if h.growing() { - evacuate_faststr(t, h, h.nevacuate) - } -} - -func evacuate_faststr(t *maptype, h *hmap, oldbucket uintptr) { - b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize))) - newbit := h.noldbuckets() - if !evacuated(b) { - // TODO: reuse overflow buckets instead of using new ones, if there - // is no iterator using the old buckets. (If !oldIterator.) - - // xy contains the x and y (low and high) evacuation destinations. - var xy [2]evacDst - x := &xy[0] - x.b = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize))) - x.k = add(unsafe.Pointer(x.b), dataOffset) - x.v = add(x.k, bucketCnt*2*sys.PtrSize) - - if !h.sameSizeGrow() { - // Only calculate y pointers if we're growing bigger. - // Otherwise GC can see bad pointers. - y := &xy[1] - y.b = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize))) - y.k = add(unsafe.Pointer(y.b), dataOffset) - y.v = add(y.k, bucketCnt*2*sys.PtrSize) - } - - for ; b != nil; b = b.overflow(t) { - k := add(unsafe.Pointer(b), dataOffset) - v := add(k, bucketCnt*2*sys.PtrSize) - for i := 0; i < bucketCnt; i, k, v = i+1, add(k, 2*sys.PtrSize), add(v, uintptr(t.valuesize)) { - top := b.tophash[i] - if top == empty { - b.tophash[i] = evacuatedEmpty - continue - } - if top < minTopHash { - throw("bad map state") - } - var useY uint8 - if !h.sameSizeGrow() { - // Compute hash to make our evacuation decision (whether we need - // to send this key/value to bucket x or bucket y). - hash := t.key.hashfn(k, uintptr(h.hash0)) - if hash&newbit != 0 { - useY = 1 - } - } - - b.tophash[i] = evacuatedX + useY // evacuatedX + 1 == evacuatedY, enforced in makemap - dst := &xy[useY] // evacuation destination - - if dst.i == bucketCnt { - dst.b = h.newoverflow(t, dst.b) - dst.i = 0 - dst.k = add(unsafe.Pointer(dst.b), dataOffset) - dst.v = add(dst.k, bucketCnt*2*sys.PtrSize) - } - dst.b.tophash[dst.i&(bucketCnt-1)] = top // mask dst.i as an optimization, to avoid a bounds check - - // Copy key. - *(*string)(dst.k) = *(*string)(k) - - typedmemmove(t.elem, dst.v, v) - dst.i++ - // These updates might push these pointers past the end of the - // key or value arrays. That's ok, as we have the overflow pointer - // at the end of the bucket to protect against pointing past the - // end of the bucket. - dst.k = add(dst.k, 2*sys.PtrSize) - dst.v = add(dst.v, uintptr(t.valuesize)) - } - } - // Unlink the overflow buckets & clear key/value to help GC. - // Unlink the overflow buckets & clear key/value to help GC. - if h.flags&oldIterator == 0 && t.bucket.kind&kindNoPointers == 0 { - b := add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)) - // Preserve b.tophash because the evacuation - // state is maintained there. - ptr := add(b, dataOffset) - n := uintptr(t.bucketsize) - dataOffset - memclrHasPointers(ptr, n) - } - } - - if oldbucket == h.nevacuate { - advanceEvacuationMark(h, t, newbit) - } -} diff --git a/libgo/go/runtime/heapdump.go b/libgo/go/runtime/heapdump.go index a4b168d..e92ea39 100644 --- a/libgo/go/runtime/heapdump.go +++ b/libgo/go/runtime/heapdump.go @@ -184,7 +184,7 @@ func dumptype(t *_type) { dumpint(uint64(uintptr(unsafe.Pointer(t)))) dumpint(uint64(t.size)) if x := t.uncommontype; x == nil || t.pkgPath == nil || *t.pkgPath == "" { - dumpstr(*t.string) + dumpstr(t.string()) } else { pkgpathstr := *t.pkgPath pkgpath := stringStructOf(&pkgpathstr) @@ -233,9 +233,8 @@ type childInfo struct { // dump kinds & offsets of interesting fields in bv func dumpbv(cbv *bitvector, offset uintptr) { - bv := gobv(*cbv) - for i := uintptr(0); i < bv.n; i++ { - if bv.bytedata[i/8]>>(i%8)&1 == 1 { + for i := uintptr(0); i < uintptr(cbv.n); i++ { + if cbv.ptrbit(i) == 1 { dumpint(fieldKindPtr) dumpint(uint64(offset + i*sys.PtrSize)) } @@ -254,7 +253,7 @@ func dumpgoroutine(gp *g) { dumpbool(isSystemGoroutine(gp)) dumpbool(false) // isbackground dumpint(uint64(gp.waitsince)) - dumpstr(gp.waitreason) + dumpstr(gp.waitreason.String()) dumpint(0) dumpint(uint64(uintptr(unsafe.Pointer(gp.m)))) dumpint(uint64(uintptr(unsafe.Pointer(gp._defer)))) @@ -372,8 +371,26 @@ func dumpparams() { dumpbool(true) // big-endian ptrs } dumpint(sys.PtrSize) - dumpint(uint64(mheap_.arena_start)) - dumpint(uint64(mheap_.arena_used)) + var arenaStart, arenaEnd uintptr + for i1 := range mheap_.arenas { + if mheap_.arenas[i1] == nil { + continue + } + for i, ha := range mheap_.arenas[i1] { + if ha == nil { + continue + } + base := arenaBase(arenaIdx(i1)<<arenaL1Shift | arenaIdx(i)) + if arenaStart == 0 || base < arenaStart { + arenaStart = base + } + if base+heapArenaBytes > arenaEnd { + arenaEnd = base + heapArenaBytes + } + } + } + dumpint(uint64(arenaStart)) + dumpint(uint64(arenaEnd)) dumpstr(sys.GOARCH) dumpstr(sys.Goexperiment) dumpint(uint64(ncpu)) @@ -509,7 +526,7 @@ func mdump() { func writeheapdump_m(fd uintptr) { _g_ := getg() casgstatus(_g_.m.curg, _Grunning, _Gwaiting) - _g_.waitreason = "dumping heap" + _g_.waitreason = waitReasonDumpingHeap // Update stats so we can dump them. // As a side effect, flushes all the MCaches so the MSpan.freelist diff --git a/libgo/go/runtime/iface.go b/libgo/go/runtime/iface.go index 62d47ce..8ed67c1 100644 --- a/libgo/go/runtime/iface.go +++ b/libgo/go/runtime/iface.go @@ -94,7 +94,7 @@ func getitab(lhs, rhs *_type, canfail bool) unsafe.Pointer { if canfail { return nil } - panic(&TypeAssertionError{"", *rhs.string, *lhs.string, *lhsi.methods[0].name}) + panic(&TypeAssertionError{nil, rhs, lhs, *lhsi.methods[0].name}) } methods := make([]unsafe.Pointer, len(lhsi.methods)+1) @@ -110,7 +110,7 @@ func getitab(lhs, rhs *_type, canfail bool) unsafe.Pointer { if canfail { return nil } - panic(&TypeAssertionError{"", *rhs.string, *lhs.string, *lhsMethod.name}) + panic(&TypeAssertionError{nil, rhs, lhs, *lhsMethod.name}) } rhsMethod = &rhs.methods[ri] @@ -126,7 +126,7 @@ func getitab(lhs, rhs *_type, canfail bool) unsafe.Pointer { if canfail { return nil } - panic(&TypeAssertionError{"", *rhs.string, *lhs.string, *lhsMethod.name}) + panic(&TypeAssertionError{nil, rhs, lhs, *lhsMethod.name}) } methods[li+1] = unsafe.Pointer(rhsMethod.tfn) @@ -147,7 +147,7 @@ func requireitab(lhs, rhs *_type) unsafe.Pointer { // impossible or if the rhs type is nil. func assertitab(lhs, rhs *_type) unsafe.Pointer { if rhs == nil { - panic(&TypeAssertionError{"", "", *lhs.string, ""}) + panic(&TypeAssertionError{nil, nil, lhs, ""}) } if lhs.kind&kindMask != kindInterface { @@ -167,10 +167,10 @@ func assertitab(lhs, rhs *_type) unsafe.Pointer { // type, panicing if not. func assertI2T(lhs, rhs, inter *_type) { if rhs == nil { - panic(&TypeAssertionError{"", "", *lhs.string, ""}) + panic(&TypeAssertionError{nil, nil, lhs, ""}) } if !eqtype(lhs, rhs) { - panic(&TypeAssertionError{*inter.string, *rhs.string, *lhs.string, ""}) + panic(&TypeAssertionError{inter, rhs, lhs, ""}) } } @@ -327,8 +327,44 @@ func ifaceT2Ip(to, from *_type) bool { func reflect_ifaceE2I(inter *interfacetype, e eface, dst *iface) { t := e._type if t == nil { - panic(TypeAssertionError{"", "", *inter.typ.string, ""}) + panic(TypeAssertionError{nil, nil, &inter.typ, ""}) } dst.tab = requireitab((*_type)(unsafe.Pointer(inter)), t) dst.data = e.data } + +// staticbytes is used to avoid convT2E for byte-sized values. +var staticbytes = [...]byte{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +} diff --git a/libgo/go/runtime/internal/atomic/atomic_test.go b/libgo/go/runtime/internal/atomic/atomic_test.go index b697aa8..25ece43 100644 --- a/libgo/go/runtime/internal/atomic/atomic_test.go +++ b/libgo/go/runtime/internal/atomic/atomic_test.go @@ -93,8 +93,10 @@ func TestUnaligned64(t *testing.T) { } x := make([]uint32, 4) - up64 := (*uint64)(unsafe.Pointer(&x[1])) // misaligned - p64 := (*int64)(unsafe.Pointer(&x[1])) // misaligned + u := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) | 4) // force alignment to 4 + + up64 := (*uint64)(u) // misaligned + p64 := (*int64)(u) // misaligned shouldPanic(t, "Load64", func() { atomic.Load64(up64) }) shouldPanic(t, "Loadint64", func() { atomic.Loadint64(p64) }) diff --git a/libgo/go/runtime/internal/atomic/bench_test.go b/libgo/go/runtime/internal/atomic/bench_test.go index 47010e3..083a75c 100644 --- a/libgo/go/runtime/internal/atomic/bench_test.go +++ b/libgo/go/runtime/internal/atomic/bench_test.go @@ -26,3 +26,39 @@ func BenchmarkAtomicStore64(b *testing.B) { atomic.Store64(&x, 0) } } + +func BenchmarkAtomicLoad(b *testing.B) { + var x uint32 + sink = &x + for i := 0; i < b.N; i++ { + _ = atomic.Load(&x) + } +} + +func BenchmarkAtomicStore(b *testing.B) { + var x uint32 + sink = &x + for i := 0; i < b.N; i++ { + atomic.Store(&x, 0) + } +} + +func BenchmarkXadd(b *testing.B) { + var x uint32 + ptr := &x + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + atomic.Xadd(ptr, 1) + } + }) +} + +func BenchmarkXadd64(b *testing.B) { + var x uint64 + ptr := &x + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + atomic.Xadd64(ptr, 1) + } + }) +} diff --git a/libgo/go/runtime/internal/atomic/stubs.go b/libgo/go/runtime/internal/atomic/stubs.go index 497b980..62e30d1 100644 --- a/libgo/go/runtime/internal/atomic/stubs.go +++ b/libgo/go/runtime/internal/atomic/stubs.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !wasm + package atomic import "unsafe" diff --git a/libgo/go/runtime/internal/sys/intrinsics.go b/libgo/go/runtime/internal/sys/intrinsics.go index 2928280..6906938 100644 --- a/libgo/go/runtime/internal/sys/intrinsics.go +++ b/libgo/go/runtime/internal/sys/intrinsics.go @@ -32,6 +32,30 @@ func Ctz32(x uint32) int { return int(builtinCtz32(x)) } +// Ctz8 returns the number of trailing zero bits in x; the result is 8 for x == 0. +func Ctz8(x uint8) int { + return int(ntz8tab[x]) +} + +var ntz8tab = [256]uint8{ + 0x08, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, +} + //extern __builtin_bswap64 func bswap64(uint64) uint64 diff --git a/libgo/go/runtime/lfstack.go b/libgo/go/runtime/lfstack.go index 4787c5b..406561a 100644 --- a/libgo/go/runtime/lfstack.go +++ b/libgo/go/runtime/lfstack.go @@ -55,3 +55,13 @@ func (head *lfstack) pop() unsafe.Pointer { func (head *lfstack) empty() bool { return atomic.Load64((*uint64)(head)) == 0 } + +// lfnodeValidate panics if node is not a valid address for use with +// lfstack.push. This only needs to be called when node is allocated. +func lfnodeValidate(node *lfnode) { + if lfstackUnpack(lfstackPack(node, ^uintptr(0))) != node { + printlock() + println("runtime: bad lfnode address", hex(uintptr(unsafe.Pointer(node)))) + throw("bad lfnode address") + } +} diff --git a/libgo/go/runtime/lfstack_64bit.go b/libgo/go/runtime/lfstack_64bit.go index dca1718..401f83d 100644 --- a/libgo/go/runtime/lfstack_64bit.go +++ b/libgo/go/runtime/lfstack_64bit.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build amd64 arm64 mips64 mips64le ppc64 ppc64le s390x arm64be alpha sparc64 ia64 riscv64 +// +build amd64 arm64 mips64 mips64le ppc64 ppc64le s390x wasm arm64be alpha sparc64 ia64 riscv64 package runtime @@ -11,21 +11,17 @@ import "unsafe" const ( // addrBits is the number of bits needed to represent a virtual address. // - // In Linux the user address space for each architecture is limited as - // follows (taken from the processor.h file for the architecture): + // See heapAddrBits for a table of address space sizes on + // various architectures. 48 bits is enough for all + // architectures except s390x. // - // Architecture Name Maximum Value (exclusive) - // --------------------------------------------------------------------- - // arm64 TASK_SIZE_64 Depends on configuration. - // ppc64{,le} TASK_SIZE_USER64 0x400000000000UL (46 bit addresses) - // mips64{,le} TASK_SIZE64 0x010000000000UL (40 bit addresses) - // s390x TASK_SIZE 0x020000000000UL (41 bit addresses) - // - // These values may increase over time. - // - // On AMD64, virtual addresses are 48-bit numbers sign extended to 64. + // On AMD64, virtual addresses are 48-bit (or 57-bit) numbers sign extended to 64. // We shift the address left 16 to eliminate the sign extended part and make // room in the bottom for the count. + // + // On s390x, virtual addresses are 64-bit. There's not much we + // can do about this, so we just hope that the kernel doesn't + // get to really high addresses and panic if it does. addrBits = 48 // In addition to the 16 bits taken from the top, we can take 3 from the diff --git a/libgo/go/runtime/lock_futex.go b/libgo/go/runtime/lock_futex.go index b2c9ccb..f7ca1f0 100644 --- a/libgo/go/runtime/lock_futex.go +++ b/libgo/go/runtime/lock_futex.go @@ -241,3 +241,9 @@ func notetsleepg(n *note, ns int64) bool { exitsyscall() return ok } + +func pauseSchedulerUntilCallback() bool { + return false +} + +func checkTimeouts() {} diff --git a/libgo/go/runtime/lock_js.go b/libgo/go/runtime/lock_js.go new file mode 100644 index 0000000..df321e5 --- /dev/null +++ b/libgo/go/runtime/lock_js.go @@ -0,0 +1,172 @@ +// Copyright 2018 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 js,wasm + +package runtime + +import ( + _ "unsafe" +) + +// js/wasm has no support for threads yet. There is no preemption. +// Waiting for a mutex is implemented by allowing other goroutines +// to run until the mutex gets unlocked. + +const ( + mutex_unlocked = 0 + mutex_locked = 1 + + note_cleared = 0 + note_woken = 1 + note_timeout = 2 + + active_spin = 4 + active_spin_cnt = 30 + passive_spin = 1 +) + +func lock(l *mutex) { + for l.key == mutex_locked { + mcall(gosched_m) + } + l.key = mutex_locked +} + +func unlock(l *mutex) { + if l.key == mutex_unlocked { + throw("unlock of unlocked lock") + } + l.key = mutex_unlocked +} + +// One-time notifications. + +type noteWithTimeout struct { + gp *g + deadline int64 +} + +var ( + notes = make(map[*note]*g) + notesWithTimeout = make(map[*note]noteWithTimeout) +) + +func noteclear(n *note) { + n.key = note_cleared +} + +func notewakeup(n *note) { + // gp := getg() + if n.key == note_woken { + throw("notewakeup - double wakeup") + } + cleared := n.key == note_cleared + n.key = note_woken + if cleared { + goready(notes[n], 1) + } +} + +func notesleep(n *note) { + throw("notesleep not supported by js") +} + +func notetsleep(n *note, ns int64) bool { + throw("notetsleep not supported by js") + return false +} + +// same as runtime·notetsleep, but called on user g (not g0) +func notetsleepg(n *note, ns int64) bool { + gp := getg() + if gp == gp.m.g0 { + throw("notetsleepg on g0") + } + + if ns >= 0 { + deadline := nanotime() + ns + delay := ns/1000000 + 1 // round up + if delay > 1<<31-1 { + delay = 1<<31 - 1 // cap to max int32 + } + + id := scheduleCallback(delay) + mp := acquirem() + notes[n] = gp + notesWithTimeout[n] = noteWithTimeout{gp: gp, deadline: deadline} + releasem(mp) + + gopark(nil, nil, waitReasonSleep, traceEvNone, 1) + + clearScheduledCallback(id) // note might have woken early, clear timeout + mp = acquirem() + delete(notes, n) + delete(notesWithTimeout, n) + releasem(mp) + + return n.key == note_woken + } + + for n.key != note_woken { + mp := acquirem() + notes[n] = gp + releasem(mp) + + gopark(nil, nil, waitReasonZero, traceEvNone, 1) + + mp = acquirem() + delete(notes, n) + releasem(mp) + } + return true +} + +// checkTimeouts resumes goroutines that are waiting on a note which has reached its deadline. +func checkTimeouts() { + now := nanotime() + for n, nt := range notesWithTimeout { + if n.key == note_cleared && now > nt.deadline { + n.key = note_timeout + goready(nt.gp, 1) + } + } +} + +var waitingForCallback *g + +// sleepUntilCallback puts the current goroutine to sleep until a callback is triggered. +// It is currently only used by the callback routine of the syscall/js package. +//go:linkname sleepUntilCallback syscall/js.sleepUntilCallback +func sleepUntilCallback() { + waitingForCallback = getg() + gopark(nil, nil, waitReasonZero, traceEvNone, 1) + waitingForCallback = nil +} + +// pauseSchedulerUntilCallback gets called from the scheduler and pauses the execution +// of Go's WebAssembly code until a callback is triggered. Then it checks for note timeouts +// and resumes goroutines that are waiting for a callback. +func pauseSchedulerUntilCallback() bool { + if waitingForCallback == nil && len(notesWithTimeout) == 0 { + return false + } + + pause() + checkTimeouts() + if waitingForCallback != nil { + goready(waitingForCallback, 1) + } + return true +} + +// pause pauses the execution of Go's WebAssembly code until a callback is triggered. +func pause() + +// scheduleCallback tells the WebAssembly environment to trigger a callback after ms milliseconds. +// It returns a timer id that can be used with clearScheduledCallback. +func scheduleCallback(ms int64) int32 + +// clearScheduledCallback clears a callback scheduled by scheduleCallback. +func clearScheduledCallback(id int32) diff --git a/libgo/go/runtime/lock_sema.go b/libgo/go/runtime/lock_sema.go index b5cce6a..237513c 100644 --- a/libgo/go/runtime/lock_sema.go +++ b/libgo/go/runtime/lock_sema.go @@ -294,3 +294,9 @@ func notetsleepg(n *note, ns int64) bool { exitsyscall() return ok } + +func pauseSchedulerUntilCallback() bool { + return false +} + +func checkTimeouts() {} diff --git a/libgo/go/runtime/malloc.go b/libgo/go/runtime/malloc.go index 523989e..ac4759f 100644 --- a/libgo/go/runtime/malloc.go +++ b/libgo/go/runtime/malloc.go @@ -78,9 +78,34 @@ // // 3. We don't zero pages that never get reused. +// Virtual memory layout +// +// The heap consists of a set of arenas, which are 64MB on 64-bit and +// 4MB on 32-bit (heapArenaBytes). Each arena's start address is also +// aligned to the arena size. +// +// Each arena has an associated heapArena object that stores the +// metadata for that arena: the heap bitmap for all words in the arena +// and the span map for all pages in the arena. heapArena objects are +// themselves allocated off-heap. +// +// Since arenas are aligned, the address space can be viewed as a +// series of arena frames. The arena map (mheap_.arenas) maps from +// arena frame number to *heapArena, or nil for parts of the address +// space not backed by the Go heap. The arena map is structured as a +// two-level array consisting of a "L1" arena map and many "L2" arena +// maps; however, since arenas are large, on many architectures, the +// arena map consists of a single, large L2 map. +// +// The arena map covers the entire possible address space, allowing +// the Go heap to use any part of the address space. The allocator +// attempts to keep arenas contiguous so that large spans (and hence +// large objects) can cross arenas. + package runtime import ( + "runtime/internal/atomic" "runtime/internal/sys" "unsafe" ) @@ -124,9 +149,8 @@ const ( _TinySize = 16 _TinySizeClass = int8(2) - _FixAllocChunk = 16 << 10 // Chunk size for FixAlloc - _MaxMHeapList = 1 << (20 - _PageShift) // Maximum page length for fixed-size list in MHeap. - _HeapAllocChunk = 1 << 20 // Chunk size for heap growth + _FixAllocChunk = 16 << 10 // Chunk size for FixAlloc + _MaxMHeapList = 1 << (20 - _PageShift) // Maximum page length for fixed-size list in MHeap. // Per-P, per order stack segment cache size. _StackCacheSize = 32 * 1024 @@ -145,25 +169,144 @@ const ( // plan9 | 4KB | 3 _NumStackOrders = 4 - sys.PtrSize/4*sys.GoosWindows - 1*sys.GoosPlan9 - // Number of bits in page to span calculations (4k pages). - // On Windows 64-bit we limit the arena to 32GB or 35 bits. - // Windows counts memory used by page table into committed memory - // of the process, so we can't reserve too much memory. - // See https://golang.org/issue/5402 and https://golang.org/issue/5236. - // On other 64-bit platforms, we limit the arena to 512GB, or 39 bits. - // On 32-bit, we don't bother limiting anything, so we use the full 32-bit address. - // The only exception is mips32 which only has access to low 2GB of virtual memory. - // On Darwin/arm64, we cannot reserve more than ~5GB of virtual memory, - // but as most devices have less than 4GB of physical memory anyway, we - // try to be conservative here, and only ask for a 2GB heap. - _MHeapMap_TotalBits = (_64bit*sys.GoosWindows)*35 + (_64bit*(1-sys.GoosWindows)*(1-sys.GoosDarwin*sys.GoarchArm64))*39 + sys.GoosDarwin*sys.GoarchArm64*31 + (1-_64bit)*(32-(sys.GoarchMips+sys.GoarchMipsle)) - _MHeapMap_Bits = _MHeapMap_TotalBits - _PageShift - - // _MaxMem is the maximum heap arena size minus 1. + // heapAddrBits is the number of bits in a heap address. On + // amd64, addresses are sign-extended beyond heapAddrBits. On + // other arches, they are zero-extended. + // + // On 64-bit platforms, we limit this to 48 bits based on a + // combination of hardware and OS limitations. + // + // amd64 hardware limits addresses to 48 bits, sign-extended + // to 64 bits. Addresses where the top 16 bits are not either + // all 0 or all 1 are "non-canonical" and invalid. Because of + // these "negative" addresses, we offset addresses by 1<<47 + // (arenaBaseOffset) on amd64 before computing indexes into + // the heap arenas index. In 2017, amd64 hardware added + // support for 57 bit addresses; however, currently only Linux + // supports this extension and the kernel will never choose an + // address above 1<<47 unless mmap is called with a hint + // address above 1<<47 (which we never do). + // + // arm64 hardware (as of ARMv8) limits user addresses to 48 + // bits, in the range [0, 1<<48). + // + // ppc64, mips64, and s390x support arbitrary 64 bit addresses + // in hardware. However, since Go only supports Linux on + // these, we lean on OS limits. Based on Linux's processor.h, + // the user address space is limited as follows on 64-bit + // architectures: + // + // Architecture Name Maximum Value (exclusive) + // --------------------------------------------------------------------- + // amd64 TASK_SIZE_MAX 0x007ffffffff000 (47 bit addresses) + // arm64 TASK_SIZE_64 0x01000000000000 (48 bit addresses) + // ppc64{,le} TASK_SIZE_USER64 0x00400000000000 (46 bit addresses) + // mips64{,le} TASK_SIZE64 0x00010000000000 (40 bit addresses) + // s390x TASK_SIZE 1<<64 (64 bit addresses) + // + // These limits may increase over time, but are currently at + // most 48 bits except on s390x. On all architectures, Linux + // starts placing mmap'd regions at addresses that are + // significantly below 48 bits, so even if it's possible to + // exceed Go's 48 bit limit, it's extremely unlikely in + // practice. + // + // On 32-bit platforms, we accept the full 32-bit address + // space because doing so is cheap. + // mips32 only has access to the low 2GB of virtual memory, so + // we further limit it to 31 bits. + // + // WebAssembly currently has a limit of 4GB linear memory. + heapAddrBits = (_64bit*(1-sys.GoarchWasm))*48 + (1-_64bit+sys.GoarchWasm)*(32-(sys.GoarchMips+sys.GoarchMipsle)) + + // maxAlloc is the maximum size of an allocation. On 64-bit, + // it's theoretically possible to allocate 1<<heapAddrBits bytes. On + // 32-bit, however, this is one less than 1<<32 because the + // number of bytes in the address space doesn't actually fit + // in a uintptr. + maxAlloc = (1 << heapAddrBits) - (1-_64bit)*1 + + // The number of bits in a heap address, the size of heap + // arenas, and the L1 and L2 arena map sizes are related by // - // On 32-bit, this is also the maximum heap pointer value, - // since the arena starts at address 0. - _MaxMem = 1<<_MHeapMap_TotalBits - 1 + // (1 << addrBits) = arenaBytes * L1entries * L2entries + // + // Currently, we balance these as follows: + // + // Platform Addr bits Arena size L1 entries L2 size + // -------------- --------- ---------- ---------- ------- + // */64-bit 48 64MB 1 32MB + // windows/64-bit 48 4MB 64 8MB + // */32-bit 32 4MB 1 4KB + // */mips(le) 31 4MB 1 2KB + + // heapArenaBytes is the size of a heap arena. The heap + // consists of mappings of size heapArenaBytes, aligned to + // heapArenaBytes. The initial heap mapping is one arena. + // + // This is currently 64MB on 64-bit non-Windows and 4MB on + // 32-bit and on Windows. We use smaller arenas on Windows + // because all committed memory is charged to the process, + // even if it's not touched. Hence, for processes with small + // heaps, the mapped arena space needs to be commensurate. + // This is particularly important with the race detector, + // since it significantly amplifies the cost of committed + // memory. + heapArenaBytes = 1 << logHeapArenaBytes + + // logHeapArenaBytes is log_2 of heapArenaBytes. For clarity, + // prefer using heapArenaBytes where possible (we need the + // constant to compute some other constants). + logHeapArenaBytes = (6+20)*(_64bit*(1-sys.GoosWindows)) + (2+20)*(_64bit*sys.GoosWindows) + (2+20)*(1-_64bit) + + // heapArenaBitmapBytes is the size of each heap arena's bitmap. + heapArenaBitmapBytes = heapArenaBytes / (sys.PtrSize * 8 / 2) + + pagesPerArena = heapArenaBytes / pageSize + + // arenaL1Bits is the number of bits of the arena number + // covered by the first level arena map. + // + // This number should be small, since the first level arena + // map requires PtrSize*(1<<arenaL1Bits) of space in the + // binary's BSS. It can be zero, in which case the first level + // index is effectively unused. There is a performance benefit + // to this, since the generated code can be more efficient, + // but comes at the cost of having a large L2 mapping. + // + // We use the L1 map on 64-bit Windows because the arena size + // is small, but the address space is still 48 bits, and + // there's a high cost to having a large L2. + arenaL1Bits = 6 * (_64bit * sys.GoosWindows) + + // arenaL2Bits is the number of bits of the arena number + // covered by the second level arena index. + // + // The size of each arena map allocation is proportional to + // 1<<arenaL2Bits, so it's important that this not be too + // large. 48 bits leads to 32MB arena index allocations, which + // is about the practical threshold. + arenaL2Bits = heapAddrBits - logHeapArenaBytes - arenaL1Bits + + // arenaL1Shift is the number of bits to shift an arena frame + // number by to compute an index into the first level arena map. + arenaL1Shift = arenaL2Bits + + // arenaBits is the total bits in a combined arena map index. + // This is split between the index into the L1 arena map and + // the L2 arena map. + arenaBits = arenaL1Bits + arenaL2Bits + + // arenaBaseOffset is the pointer value that corresponds to + // index 0 in the heap arena map. + // + // On amd64, the address space is 48 bits, sign extended to 64 + // bits. This offset lets us handle "negative" addresses (or + // high addresses if viewed as unsigned). + // + // On other platforms, the user address space is contiguous + // and starts at 0, so no offset is necessary. + arenaBaseOffset uintptr = sys.GoarchAmd64 * (1 << 47) // Max number of threads to run garbage collection. // 2, 3, and 4 are all plausible maximums depending @@ -209,18 +352,12 @@ var physPageSize uintptr // SysReserve reserves address space without allocating memory. // If the pointer passed to it is non-nil, the caller wants the // reservation there, but SysReserve can still choose another -// location if that one is unavailable. On some systems and in some -// cases SysReserve will simply check that the address space is -// available and not actually reserve it. If SysReserve returns -// non-nil, it sets *reserved to true if the address space is -// reserved, false if it has merely been checked. +// location if that one is unavailable. // NOTE: SysReserve returns OS-aligned memory, but the heap allocator // may use larger alignment, so the caller must be careful to realign the // memory obtained by sysAlloc. // // SysMap maps previously reserved address space for use. -// The reserved argument is true if the address space was really -// reserved, not merely checked. // // SysFault marks a (already sysAlloc'd) region to fault // if accessed. Used only for debugging the runtime. @@ -233,6 +370,12 @@ func mallocinit() { // Not used for gccgo. // testdefersizes() + if heapArenaBitmapBytes&(heapArenaBitmapBytes-1) != 0 { + // heapBits expects modular arithmetic on bitmap + // addresses to work. + throw("heapArenaBitmapBytes not a power of 2") + } + // Copy class sizes out for statistics table. for i := range class_to_size { memstats.by_size[i].size = uint32(class_to_size[i]) @@ -252,55 +395,47 @@ func mallocinit() { throw("bad system page size") } - // The auxiliary regions start at p and are laid out in the - // following order: spans, bitmap, arena. - var p, pSize uintptr - var reserved bool - - // The spans array holds one *mspan per _PageSize of arena. - var spansSize uintptr = (_MaxMem + 1) / _PageSize * sys.PtrSize - spansSize = round(spansSize, _PageSize) - // The bitmap holds 2 bits per word of arena. - var bitmapSize uintptr = (_MaxMem + 1) / (sys.PtrSize * 8 / 2) - bitmapSize = round(bitmapSize, _PageSize) - - // Set up the allocation arena, a contiguous area of memory where - // allocated data will be found. - if sys.PtrSize == 8 { - // On a 64-bit machine, allocate from a single contiguous reservation. - // 512 GB (MaxMem) should be big enough for now. + // Initialize the heap. + mheap_.init() + _g_ := getg() + _g_.m.mcache = allocmcache() + + // Create initial arena growth hints. + if sys.PtrSize == 8 && GOARCH != "wasm" { + // On a 64-bit machine, we pick the following hints + // because: + // + // 1. Starting from the middle of the address space + // makes it easier to grow out a contiguous range + // without running in to some other mapping. // - // The code will work with the reservation at any address, but ask - // SysReserve to use 0x0000XXc000000000 if possible (XX=00...7f). - // Allocating a 512 GB region takes away 39 bits, and the amd64 - // doesn't let us choose the top 17 bits, so that leaves the 9 bits - // in the middle of 0x00c0 for us to choose. Choosing 0x00c0 means - // that the valid memory addresses will begin 0x00c0, 0x00c1, ..., 0x00df. - // In little-endian, that's c0 00, c1 00, ..., df 00. None of those are valid + // 2. This makes Go heap addresses more easily + // recognizable when debugging. + // + // 3. Stack scanning in gccgo is still conservative, + // so it's important that addresses be distinguishable + // from other data. + // + // Starting at 0x00c0 means that the valid memory addresses + // will begin 0x00c0, 0x00c1, ... + // In little-endian, that's c0 00, c1 00, ... None of those are valid // UTF-8 sequences, and they are otherwise as far away from // ff (likely a common byte) as possible. If that fails, we try other 0xXXc0 // addresses. An earlier attempt to use 0x11f8 caused out of memory errors // on OS X during thread allocations. 0x00c0 causes conflicts with // AddressSanitizer which reserves all memory up to 0x0100. - // These choices are both for debuggability and to reduce the - // odds of a conservative garbage collector (as is still used in gccgo) + // These choices reduce the odds of a conservative garbage collector // not collecting memory because some non-pointer block of memory // had a bit pattern that matched a memory address. // - // Actually we reserve 544 GB (because the bitmap ends up being 32 GB) - // but it hardly matters: e0 00 is not valid UTF-8 either. - // - // If this fails we fall back to the 32 bit memory mechanism - // // However, on arm64, we ignore all this advice above and slam the // allocation at 0x40 << 32 because when using 4k pages with 3-level // translation buffers, the user address space is limited to 39 bits // On darwin/arm64, the address space is even smaller. // On AIX, mmap adresses range starts at 0x0700000000000000 for 64-bit // processes. The new address space allocator starts at 0x0A00000000000000. - arenaSize := round(_MaxMem, _PageSize) - pSize = bitmapSize + spansSize + arenaSize + _PageSize - for i := 0; i <= 0x7f; i++ { + for i := 0x7f; i >= 0; i-- { + var p uintptr switch { case GOARCH == "arm64" && GOOS == "darwin": p = uintptr(i)<<40 | uintptrMask&(0x0013<<28) @@ -312,225 +447,283 @@ func mallocinit() { } else { p = uintptr(i)<<42 | uintptrMask&(0x70<<52) } + case raceenabled: + // The TSAN runtime requires the heap + // to be in the range [0x00c000000000, + // 0x00e000000000). + p = uintptr(i)<<32 | uintptrMask&(0x00c0<<32) + if p >= uintptrMask&0x00e000000000 { + continue + } default: p = uintptr(i)<<40 | uintptrMask&(0x00c0<<32) } - p = uintptr(sysReserve(unsafe.Pointer(p), pSize, &reserved)) - if p != 0 { - break - } + hint := (*arenaHint)(mheap_.arenaHintAlloc.alloc()) + hint.addr = p + hint.next, mheap_.arenaHints = mheap_.arenaHints, hint } - } + } else { + // On a 32-bit machine, we're much more concerned + // about keeping the usable heap contiguous. + // Hence: + // + // 1. We reserve space for all heapArenas up front so + // they don't get interleaved with the heap. They're + // ~258MB, so this isn't too bad. (We could reserve a + // smaller amount of space up front if this is a + // problem.) + // + // 2. We hint the heap to start right above the end of + // the binary so we have the best chance of keeping it + // contiguous. + // + // 3. We try to stake out a reasonably large initial + // heap reservation. - if p == 0 { - // On a 32-bit machine, we can't typically get away - // with a giant virtual address space reservation. - // Instead we map the memory information bitmap - // immediately after the data segment, large enough - // to handle the entire 4GB address space (256 MB), - // along with a reservation for an initial arena. - // When that gets used up, we'll start asking the kernel - // for any memory anywhere. + const arenaMetaSize = unsafe.Sizeof([1 << arenaBits]heapArena{}) + meta := uintptr(sysReserve(nil, arenaMetaSize)) + if meta != 0 { + mheap_.heapArenaAlloc.init(meta, arenaMetaSize) + } // We want to start the arena low, but if we're linked // against C code, it's possible global constructors // have called malloc and adjusted the process' brk. // Query the brk so we can avoid trying to map the - // arena over it (which will cause the kernel to put - // the arena somewhere else, likely at a high + // region over it (which will cause the kernel to put + // the region somewhere else, likely at a high // address). procBrk := sbrk0() - // If we fail to allocate, try again with a smaller arena. - // This is necessary on Android L where we share a process - // with ART, which reserves virtual memory aggressively. - // In the worst case, fall back to a 0-sized initial arena, - // in the hope that subsequent reservations will succeed. + // If we ask for the end of the data segment but the + // operating system requires a little more space + // before we can start allocating, it will give out a + // slightly higher pointer. Except QEMU, which is + // buggy, as usual: it won't adjust the pointer + // upward. So adjust it upward a little bit ourselves: + // 1/4 MB to get away from the running binary image. + p := getEnd() + if p < procBrk { + p = procBrk + } + if mheap_.heapArenaAlloc.next <= p && p < mheap_.heapArenaAlloc.end { + p = mheap_.heapArenaAlloc.end + } + p = round(p+(256<<10), heapArenaBytes) + // Because we're worried about fragmentation on + // 32-bit, we try to make a large initial reservation. arenaSizes := [...]uintptr{ 512 << 20, 256 << 20, 128 << 20, - 0, } - for _, arenaSize := range &arenaSizes { - // SysReserve treats the address we ask for, end, as a hint, - // not as an absolute requirement. If we ask for the end - // of the data segment but the operating system requires - // a little more space before we can start allocating, it will - // give out a slightly higher pointer. Except QEMU, which - // is buggy, as usual: it won't adjust the pointer upward. - // So adjust it upward a little bit ourselves: 1/4 MB to get - // away from the running binary image and then round up - // to a MB boundary. - p = round(getEnd()+(1<<18), 1<<20) - pSize = bitmapSize + spansSize + arenaSize + _PageSize - if p <= procBrk && procBrk < p+pSize { - // Move the start above the brk, - // leaving some room for future brk - // expansion. - p = round(procBrk+(1<<20), 1<<20) - } - p = uintptr(sysReserve(unsafe.Pointer(p), pSize, &reserved)) - if p != 0 { + a, size := sysReserveAligned(unsafe.Pointer(p), arenaSize, heapArenaBytes) + if a != nil { + mheap_.arena.init(uintptr(a), size) + p = uintptr(a) + size // For hint below break } } - if p == 0 { - throw("runtime: cannot reserve arena virtual address space") - } - } - - // PageSize can be larger than OS definition of page size, - // so SysReserve can give us a PageSize-unaligned pointer. - // To overcome this we ask for PageSize more and round up the pointer. - p1 := round(p, _PageSize) - pSize -= p1 - p - - spansStart := p1 - p1 += spansSize - mheap_.bitmap = p1 + bitmapSize - p1 += bitmapSize - if sys.PtrSize == 4 { - // Set arena_start such that we can accept memory - // reservations located anywhere in the 4GB virtual space. - mheap_.arena_start = 0 - } else { - mheap_.arena_start = p1 + hint := (*arenaHint)(mheap_.arenaHintAlloc.alloc()) + hint.addr = p + hint.next, mheap_.arenaHints = mheap_.arenaHints, hint } - mheap_.arena_end = p + pSize - mheap_.arena_used = p1 - mheap_.arena_alloc = p1 - mheap_.arena_reserved = reserved - - if mheap_.arena_start&(_PageSize-1) != 0 { - println("bad pagesize", hex(p), hex(p1), hex(spansSize), hex(bitmapSize), hex(_PageSize), "start", hex(mheap_.arena_start)) - throw("misrounded allocation in mallocinit") - } - - // Initialize the rest of the allocator. - mheap_.init(spansStart, spansSize) - _g_ := getg() - _g_.m.mcache = allocmcache() } -// sysAlloc allocates the next n bytes from the heap arena. The -// returned pointer is always _PageSize aligned and between -// h.arena_start and h.arena_end. sysAlloc returns nil on failure. +// sysAlloc allocates heap arena space for at least n bytes. The +// returned pointer is always heapArenaBytes-aligned and backed by +// h.arenas metadata. The returned size is always a multiple of +// heapArenaBytes. sysAlloc returns nil on failure. // There is no corresponding free function. -func (h *mheap) sysAlloc(n uintptr) unsafe.Pointer { - // strandLimit is the maximum number of bytes to strand from - // the current arena block. If we would need to strand more - // than this, we fall back to sysAlloc'ing just enough for - // this allocation. - const strandLimit = 16 << 20 - - if n > h.arena_end-h.arena_alloc { - // If we haven't grown the arena to _MaxMem yet, try - // to reserve some more address space. - p_size := round(n+_PageSize, 256<<20) - new_end := h.arena_end + p_size // Careful: can overflow - if h.arena_end <= new_end && new_end-h.arena_start-1 <= _MaxMem { - // TODO: It would be bad if part of the arena - // is reserved and part is not. - var reserved bool - p := uintptr(sysReserve(unsafe.Pointer(h.arena_end), p_size, &reserved)) - if p == 0 { - // TODO: Try smaller reservation - // growths in case we're in a crowded - // 32-bit address space. - goto reservationFailed - } - // p can be just about anywhere in the address - // space, including before arena_end. - if p == h.arena_end { - // The new block is contiguous with - // the current block. Extend the - // current arena block. - h.arena_end = new_end - h.arena_reserved = reserved - } else if h.arena_start <= p && p+p_size-h.arena_start-1 <= _MaxMem && h.arena_end-h.arena_alloc < strandLimit { - // We were able to reserve more memory - // within the arena space, but it's - // not contiguous with our previous - // reservation. It could be before or - // after our current arena_used. - // - // Keep everything page-aligned. - // Our pages are bigger than hardware pages. - h.arena_end = p + p_size - p = round(p, _PageSize) - h.arena_alloc = p - h.arena_reserved = reserved - } else { - // We got a mapping, but either - // - // 1) It's not in the arena, so we - // can't use it. (This should never - // happen on 32-bit.) - // - // 2) We would need to discard too - // much of our current arena block to - // use it. - // - // We haven't added this allocation to - // the stats, so subtract it from a - // fake stat (but avoid underflow). - // - // We'll fall back to a small sysAlloc. - stat := uint64(p_size) - sysFree(unsafe.Pointer(p), p_size, &stat) +// +// h must be locked. +func (h *mheap) sysAlloc(n uintptr) (v unsafe.Pointer, size uintptr) { + n = round(n, heapArenaBytes) + + // First, try the arena pre-reservation. + v = h.arena.alloc(n, heapArenaBytes, &memstats.heap_sys) + if v != nil { + size = n + goto mapped + } + + // Try to grow the heap at a hint address. + for h.arenaHints != nil { + hint := h.arenaHints + p := hint.addr + if hint.down { + p -= n + } + if p+n < p { + // We can't use this, so don't ask. + v = nil + } else if arenaIndex(p+n-1) >= 1<<arenaBits { + // Outside addressable heap. Can't use. + v = nil + } else { + v = sysReserve(unsafe.Pointer(p), n) + } + if p == uintptr(v) { + // Success. Update the hint. + if !hint.down { + p += n } + hint.addr = p + size = n + break } + // Failed. Discard this hint and try the next. + // + // TODO: This would be cleaner if sysReserve could be + // told to only return the requested address. In + // particular, this is already how Windows behaves, so + // it would simply things there. + if v != nil { + sysFree(v, n, nil) + } + h.arenaHints = hint.next + h.arenaHintAlloc.free(unsafe.Pointer(hint)) } - if n <= h.arena_end-h.arena_alloc { - // Keep taking from our reservation. - p := h.arena_alloc - sysMap(unsafe.Pointer(p), n, h.arena_reserved, &memstats.heap_sys) - h.arena_alloc += n - if h.arena_alloc > h.arena_used { - h.setArenaUsed(h.arena_alloc, true) + if size == 0 { + if raceenabled { + // The race detector assumes the heap lives in + // [0x00c000000000, 0x00e000000000), but we + // just ran out of hints in this region. Give + // a nice failure. + throw("too many address space collisions for -race mode") } - if p&(_PageSize-1) != 0 { - throw("misrounded allocation in MHeap_SysAlloc") + // All of the hints failed, so we'll take any + // (sufficiently aligned) address the kernel will give + // us. + v, size = sysReserveAligned(nil, n, heapArenaBytes) + if v == nil { + return nil, 0 } - return unsafe.Pointer(p) + + // Create new hints for extending this region. + hint := (*arenaHint)(h.arenaHintAlloc.alloc()) + hint.addr, hint.down = uintptr(v), true + hint.next, mheap_.arenaHints = mheap_.arenaHints, hint + hint = (*arenaHint)(h.arenaHintAlloc.alloc()) + hint.addr = uintptr(v) + size + hint.next, mheap_.arenaHints = mheap_.arenaHints, hint } -reservationFailed: - // If using 64-bit, our reservation is all we have. - if sys.PtrSize != 4 { - return nil + // Check for bad pointers or pointers we can't use. + { + var bad string + p := uintptr(v) + if p+size < p { + bad = "region exceeds uintptr range" + } else if arenaIndex(p) >= 1<<arenaBits { + bad = "base outside usable address space" + } else if arenaIndex(p+size-1) >= 1<<arenaBits { + bad = "end outside usable address space" + } + if bad != "" { + // This should be impossible on most architectures, + // but it would be really confusing to debug. + print("runtime: memory allocated by OS [", hex(p), ", ", hex(p+size), ") not in usable address space: ", bad, "\n") + throw("memory reservation exceeds address space limit") + } } - // On 32-bit, once the reservation is gone we can - // try to get memory at a location chosen by the OS. - p_size := round(n, _PageSize) + _PageSize - p := uintptr(sysAlloc(p_size, &memstats.heap_sys)) - if p == 0 { - return nil + if uintptr(v)&(heapArenaBytes-1) != 0 { + throw("misrounded allocation in sysAlloc") } - if p < h.arena_start || p+p_size-h.arena_start > _MaxMem { - // This shouldn't be possible because _MaxMem is the - // whole address space on 32-bit. - top := uint64(h.arena_start) + _MaxMem - print("runtime: memory allocated by OS (", hex(p), ") not in usable range [", hex(h.arena_start), ",", hex(top), ")\n") - sysFree(unsafe.Pointer(p), p_size, &memstats.heap_sys) - return nil + // Back the reservation. + sysMap(v, size, &memstats.heap_sys) + +mapped: + // Create arena metadata. + for ri := arenaIndex(uintptr(v)); ri <= arenaIndex(uintptr(v)+size-1); ri++ { + l2 := h.arenas[ri.l1()] + if l2 == nil { + // Allocate an L2 arena map. + l2 = (*[1 << arenaL2Bits]*heapArena)(persistentalloc(unsafe.Sizeof(*l2), sys.PtrSize, nil)) + if l2 == nil { + throw("out of memory allocating heap arena map") + } + atomic.StorepNoWB(unsafe.Pointer(&h.arenas[ri.l1()]), unsafe.Pointer(l2)) + } + + if l2[ri.l2()] != nil { + throw("arena already initialized") + } + var r *heapArena + r = (*heapArena)(h.heapArenaAlloc.alloc(unsafe.Sizeof(*r), sys.PtrSize, &memstats.gc_sys)) + if r == nil { + r = (*heapArena)(persistentalloc(unsafe.Sizeof(*r), sys.PtrSize, &memstats.gc_sys)) + if r == nil { + throw("out of memory allocating heap arena metadata") + } + } + + // Store atomically just in case an object from the + // new heap arena becomes visible before the heap lock + // is released (which shouldn't happen, but there's + // little downside to this). + atomic.StorepNoWB(unsafe.Pointer(&l2[ri.l2()]), unsafe.Pointer(r)) } - p += -p & (_PageSize - 1) - if p+n > h.arena_used { - h.setArenaUsed(p+n, true) + // Tell the race detector about the new heap memory. + if raceenabled { + racemapshadow(v, size) } - if p&(_PageSize-1) != 0 { - throw("misrounded allocation in MHeap_SysAlloc") + return +} + +// sysReserveAligned is like sysReserve, but the returned pointer is +// aligned to align bytes. It may reserve either n or n+align bytes, +// so it returns the size that was reserved. +func sysReserveAligned(v unsafe.Pointer, size, align uintptr) (unsafe.Pointer, uintptr) { + // Since the alignment is rather large in uses of this + // function, we're not likely to get it by chance, so we ask + // for a larger region and remove the parts we don't need. + retries := 0 +retry: + p := uintptr(sysReserve(v, size+align)) + switch { + case p == 0: + return nil, 0 + case p&(align-1) == 0: + // We got lucky and got an aligned region, so we can + // use the whole thing. + return unsafe.Pointer(p), size + align + case GOOS == "windows": + // On Windows we can't release pieces of a + // reservation, so we release the whole thing and + // re-reserve the aligned sub-region. This may race, + // so we may have to try again. + sysFree(unsafe.Pointer(p), size+align, nil) + p = round(p, align) + p2 := sysReserve(unsafe.Pointer(p), size) + if p != uintptr(p2) { + // Must have raced. Try again. + sysFree(p2, size, nil) + if retries++; retries == 100 { + throw("failed to allocate aligned heap memory; too many retries") + } + goto retry + } + // Success. + return p2, size + default: + // Trim off the unaligned parts. + pAligned := round(p, align) + sysFree(unsafe.Pointer(p), pAligned-p, nil) + end := pAligned + size + endLen := (p + size + align) - end + if endLen > 0 { + sysFree(unsafe.Pointer(end), endLen, nil) + } + return unsafe.Pointer(pAligned), size } - return unsafe.Pointer(p) } // base address for all 0-byte allocations @@ -862,7 +1055,7 @@ func largeAlloc(size uintptr, needzero bool, noscan bool) *mspan { throw("out of memory") } s.limit = s.base() + size - heapBitsForSpan(s.base()).initSpan(s) + heapBitsForAddr(s.base()).initSpan(s) return s } @@ -875,7 +1068,7 @@ func newobject(typ *_type) unsafe.Pointer { //go:linkname reflect_unsafe_New reflect.unsafe_New func reflect_unsafe_New(typ *_type) unsafe.Pointer { - return newobject(typ) + return mallocgc(typ.size, typ, true) } // newarray allocates an array of n elements of type typ. @@ -1046,6 +1239,34 @@ func persistentalloc1(size, align uintptr, sysStat *uint64) *notInHeap { return p } +// linearAlloc is a simple linear allocator that pre-reserves a region +// of memory and then maps that region as needed. The caller is +// responsible for locking. +type linearAlloc struct { + next uintptr // next free byte + mapped uintptr // one byte past end of mapped space + end uintptr // end of reserved space +} + +func (l *linearAlloc) init(base, size uintptr) { + l.next, l.mapped = base, base + l.end = base + size +} + +func (l *linearAlloc) alloc(size, align uintptr, sysStat *uint64) unsafe.Pointer { + p := round(l.next, align) + if p+size > l.end { + return nil + } + l.next = p + size + if pEnd := round(l.next-1, physPageSize); pEnd > l.mapped { + // We need to map more of the reserved space. + sysMap(unsafe.Pointer(l.mapped), pEnd-l.mapped, sysStat) + l.mapped = pEnd + } + return unsafe.Pointer(p) +} + // notInHeap is off-heap memory allocated by a lower-level allocator // like sysAlloc or persistentAlloc. // diff --git a/libgo/go/runtime/malloc_test.go b/libgo/go/runtime/malloc_test.go index ab580f8..30a7d84 100644 --- a/libgo/go/runtime/malloc_test.go +++ b/libgo/go/runtime/malloc_test.go @@ -7,16 +7,25 @@ package runtime_test import ( "flag" "fmt" + "internal/race" + "internal/testenv" + "os" + "os/exec" "reflect" . "runtime" + "strings" "testing" "time" "unsafe" ) +var testMemStatsCount int + func TestMemStats(t *testing.T) { t.Skip("skipping test with gccgo") + testMemStatsCount++ + // Make sure there's at least one forced GC. GC() @@ -32,6 +41,13 @@ func TestMemStats(t *testing.T) { } le := func(thresh float64) func(interface{}) error { return func(x interface{}) error { + // These sanity tests aren't necessarily valid + // with high -test.count values, so only run + // them once. + if testMemStatsCount > 1 { + return nil + } + if reflect.ValueOf(x).Convert(reflect.TypeOf(thresh)).Float() < thresh { return nil } @@ -50,7 +66,7 @@ func TestMemStats(t *testing.T) { // PauseTotalNs can be 0 if timer resolution is poor. fields := map[string][]func(interface{}) error{ "Alloc": {nz, le(1e10)}, "TotalAlloc": {nz, le(1e11)}, "Sys": {nz, le(1e10)}, - "Lookups": {nz, le(1e10)}, "Mallocs": {nz, le(1e10)}, "Frees": {nz, le(1e10)}, + "Lookups": {eq(uint64(0))}, "Mallocs": {nz, le(1e10)}, "Frees": {nz, le(1e10)}, "HeapAlloc": {nz, le(1e10)}, "HeapSys": {nz, le(1e10)}, "HeapIdle": {le(1e10)}, "HeapInuse": {nz, le(1e10)}, "HeapReleased": {le(1e10)}, "HeapObjects": {nz, le(1e10)}, "StackInuse": {nz, le(1e10)}, "StackSys": {nz, le(1e10)}, @@ -154,6 +170,64 @@ func TestTinyAlloc(t *testing.T) { } } +type acLink struct { + x [1 << 20]byte +} + +var arenaCollisionSink []*acLink + +func TestArenaCollision(t *testing.T) { + testenv.MustHaveExec(t) + + // Test that mheap.sysAlloc handles collisions with other + // memory mappings. + if os.Getenv("TEST_ARENA_COLLISION") != "1" { + cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestArenaCollision", "-test.v")) + cmd.Env = append(cmd.Env, "TEST_ARENA_COLLISION=1") + out, err := cmd.CombinedOutput() + if race.Enabled { + // This test runs the runtime out of hint + // addresses, so it will start mapping the + // heap wherever it can. The race detector + // doesn't support this, so look for the + // expected failure. + if want := "too many address space collisions"; !strings.Contains(string(out), want) { + t.Fatalf("want %q, got:\n%s", want, string(out)) + } + } else if !strings.Contains(string(out), "PASS\n") || err != nil { + t.Fatalf("%s\n(exit status %v)", string(out), err) + } + return + } + disallowed := [][2]uintptr{} + // Drop all but the next 3 hints. 64-bit has a lot of hints, + // so it would take a lot of memory to go through all of them. + KeepNArenaHints(3) + // Consume these 3 hints and force the runtime to find some + // fallback hints. + for i := 0; i < 5; i++ { + // Reserve memory at the next hint so it can't be used + // for the heap. + start, end := MapNextArenaHint() + disallowed = append(disallowed, [2]uintptr{start, end}) + // Allocate until the runtime tries to use the hint we + // just mapped over. + hint := GetNextArenaHint() + for GetNextArenaHint() == hint { + ac := new(acLink) + arenaCollisionSink = append(arenaCollisionSink, ac) + // The allocation must not have fallen into + // one of the reserved regions. + p := uintptr(unsafe.Pointer(ac)) + for _, d := range disallowed { + if d[0] <= p && p < d[1] { + t.Fatalf("allocation %#x in reserved region [%#x, %#x)", p, d[0], d[1]) + } + } + } + } +} + var mallocSink uintptr func BenchmarkMalloc8(b *testing.B) { diff --git a/libgo/go/runtime/hashmap.go b/libgo/go/runtime/map.go index 53b05b1..8e97bc5 100644 --- a/libgo/go/runtime/hashmap.go +++ b/libgo/go/runtime/map.go @@ -87,7 +87,7 @@ const ( // Maximum key or value size to keep inline (instead of mallocing per element). // Must fit in a uint8. // Fast versions cannot handle big values - the cutoff size for - // fast versions in ../../cmd/internal/gc/walk.go must be at most this value. + // fast versions in cmd/compile/internal/gc/walk.go must be at most this value. maxKeySize = 128 maxValueSize = 128 @@ -121,8 +121,8 @@ const ( // A header for a Go map. type hmap struct { - // Note: the format of the Hmap is encoded in ../../cmd/internal/gc/reflect.go and - // ../reflect/type.go. Don't change this structure without also changing that code! + // Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go. + // Make sure this stays in sync with the compiler's definition. count int // # live cells == size of map. Must be first (used by len() builtin) flags uint8 B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items) @@ -141,7 +141,7 @@ type mapextra struct { // If both key and value do not contain pointers and are inline, then we mark bucket // type as containing no pointers. This avoids scanning such maps. // However, bmap.overflow is a pointer. In order to keep overflow buckets - // alive, we store pointers to all overflow buckets in hmap.overflow and h.map.oldoverflow. + // alive, we store pointers to all overflow buckets in hmap.extra.overflow and hmap.extra.oldoverflow. // overflow and oldoverflow are only used if key and value do not contain pointers. // overflow contains overflow buckets for hmap.buckets. // oldoverflow contains overflow buckets for hmap.oldbuckets. @@ -167,7 +167,7 @@ type bmap struct { } // A hash iteration structure. -// If you modify hiter, also change cmd/internal/gc/reflect.go to indicate +// If you modify hiter, also change cmd/compile/internal/gc/reflect.go to indicate // the layout of this structure. type hiter struct { key unsafe.Pointer // Must be in first position. Write nil to indicate iteration end (see cmd/internal/gc/range.go). @@ -333,7 +333,7 @@ func makemap(t *maptype, hint int, h *hmap) *hmap { // If hint is large zeroing this memory could take a while. if h.B != 0 { var nextOverflow *bmap - h.buckets, nextOverflow = makeBucketArray(t, h.B) + h.buckets, nextOverflow = makeBucketArray(t, h.B, nil) if nextOverflow != nil { h.extra = new(mapextra) h.extra.nextOverflow = nextOverflow @@ -343,6 +343,57 @@ func makemap(t *maptype, hint int, h *hmap) *hmap { return h } +// makeBucketArray initializes a backing array for map buckets. +// 1<<b is the minimum number of buckets to allocate. +// dirtyalloc should either be nil or a bucket array previously +// allocated by makeBucketArray with the same t and b parameters. +// If dirtyalloc is nil a new backing array will be alloced and +// otherwise dirtyalloc will be cleared and reused as backing array. +func makeBucketArray(t *maptype, b uint8, dirtyalloc unsafe.Pointer) (buckets unsafe.Pointer, nextOverflow *bmap) { + base := bucketShift(b) + nbuckets := base + // For small b, overflow buckets are unlikely. + // Avoid the overhead of the calculation. + if b >= 4 { + // Add on the estimated number of overflow buckets + // required to insert the median number of elements + // used with this value of b. + nbuckets += bucketShift(b - 4) + sz := t.bucket.size * nbuckets + up := roundupsize(sz) + if up != sz { + nbuckets = up / t.bucket.size + } + } + + if dirtyalloc == nil { + buckets = newarray(t.bucket, int(nbuckets)) + } else { + // dirtyalloc was previously generated by + // the above newarray(t.bucket, int(nbuckets)) + // but may not be empty. + buckets = dirtyalloc + size := t.bucket.size * nbuckets + if t.bucket.kind&kindNoPointers == 0 { + memclrHasPointers(buckets, size) + } else { + memclrNoHeapPointers(buckets, size) + } + } + + if base != nbuckets { + // We preallocated some overflow buckets. + // To keep the overhead of tracking these overflow buckets to a minimum, + // we use the convention that if a preallocated overflow bucket's overflow + // pointer is nil, then there are more available by bumping the pointer. + // We need a safe non-nil pointer for the last overflow bucket; just use buckets. + nextOverflow = (*bmap)(add(buckets, base*uintptr(t.bucketsize))) + last := (*bmap)(add(buckets, (nbuckets-1)*uintptr(t.bucketsize))) + last.setoverflow(t, (*bmap)(buckets)) + } + return buckets, nextOverflow +} + // mapaccess1 returns a pointer to h[key]. Never returns nil, instead // it will return a reference to the zero object for the value type if // the key is not in the map. @@ -696,14 +747,13 @@ search: } else if t.key.kind&kindNoPointers == 0 { memclrHasPointers(k, t.key.size) } - // Only clear value if there are pointers in it. - if t.indirectvalue || t.elem.kind&kindNoPointers == 0 { - v := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize)) - if t.indirectvalue { - *(*unsafe.Pointer)(v) = nil - } else { - memclrHasPointers(v, t.elem.size) - } + v := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize)) + if t.indirectvalue { + *(*unsafe.Pointer)(v) = nil + } else if t.elem.kind&kindNoPointers == 0 { + memclrHasPointers(v, t.elem.size) + } else { + memclrNoHeapPointers(v, t.elem.size) } b.tophash[i] = empty h.count-- @@ -746,7 +796,7 @@ func mapiterinit(t *maptype, h *hmap, it *hiter) { } if unsafe.Sizeof(hiter{})/sys.PtrSize != 12 { - throw("hash_iter size incorrect") // see ../../cmd/internal/gc/reflect.go + throw("hash_iter size incorrect") // see cmd/compile/internal/gc/reflect.go } it.t = t it.h = h @@ -915,34 +965,49 @@ next: goto next } -func makeBucketArray(t *maptype, b uint8) (buckets unsafe.Pointer, nextOverflow *bmap) { - base := bucketShift(b) - nbuckets := base - // For small b, overflow buckets are unlikely. - // Avoid the overhead of the calculation. - if b >= 4 { - // Add on the estimated number of overflow buckets - // required to insert the median number of elements - // used with this value of b. - nbuckets += bucketShift(b - 4) - sz := t.bucket.size * nbuckets - up := roundupsize(sz) - if up != sz { - nbuckets = up / t.bucket.size - } +// mapclear deletes all keys from a map. +func mapclear(t *maptype, h *hmap) { + if raceenabled && h != nil { + callerpc := getcallerpc() + pc := funcPC(mapclear) + racewritepc(unsafe.Pointer(h), callerpc, pc) } - buckets = newarray(t.bucket, int(nbuckets)) - if base != nbuckets { - // We preallocated some overflow buckets. - // To keep the overhead of tracking these overflow buckets to a minimum, - // we use the convention that if a preallocated overflow bucket's overflow - // pointer is nil, then there are more available by bumping the pointer. - // We need a safe non-nil pointer for the last overflow bucket; just use buckets. - nextOverflow = (*bmap)(add(buckets, base*uintptr(t.bucketsize))) - last := (*bmap)(add(buckets, (nbuckets-1)*uintptr(t.bucketsize))) - last.setoverflow(t, (*bmap)(buckets)) + + if h == nil || h.count == 0 { + return } - return buckets, nextOverflow + + if h.flags&hashWriting != 0 { + throw("concurrent map writes") + } + + h.flags |= hashWriting + + h.flags &^= sameSizeGrow + h.oldbuckets = nil + h.nevacuate = 0 + h.noverflow = 0 + h.count = 0 + + // Keep the mapextra allocation but clear any extra information. + if h.extra != nil { + *h.extra = mapextra{} + } + + // makeBucketArray clears the memory pointed to by h.buckets + // and recovers any overflow buckets by generating them + // as if h.buckets was newly alloced. + _, nextOverflow := makeBucketArray(t, h.B, h.buckets) + if nextOverflow != nil { + // If overflow buckets are created then h.extra + // will have been allocated during initial bucket creation. + h.extra.nextOverflow = nextOverflow + } + + if h.flags&hashWriting == 0 { + throw("concurrent map writes") + } + h.flags &^= hashWriting } func hashGrow(t *maptype, h *hmap) { @@ -955,7 +1020,7 @@ func hashGrow(t *maptype, h *hmap) { h.flags |= sameSizeGrow } oldbuckets := h.buckets - newbuckets, nextOverflow := makeBucketArray(t, h.B+bigger) + newbuckets, nextOverflow := makeBucketArray(t, h.B+bigger, nil) flags := h.flags &^ (iterator | oldIterator) if h.flags&iterator != 0 { @@ -1059,7 +1124,6 @@ type evacDst struct { func evacuate(t *maptype, h *hmap, oldbucket uintptr) { b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize))) newbit := h.noldbuckets() - hashfn := t.key.hashfn if !evacuated(b) { // TODO: reuse overflow buckets instead of using new ones, if there // is no iterator using the old buckets. (If !oldIterator.) @@ -1100,7 +1164,7 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) { if !h.sameSizeGrow() { // Compute hash to make our evacuation decision (whether we need // to send this key/value to bucket x or bucket y). - hash := hashfn(k2, uintptr(h.hash0)) + hash := t.key.hashfn(k2, uintptr(h.hash0)) if h.flags&iterator != 0 && !t.reflexivekey && !t.key.equalfn(k2, k2) { // If key != key (NaNs), then the hash could be (and probably // will be) entirely different from the old hash. Moreover, @@ -1203,6 +1267,7 @@ func ismapkey(t *_type) bool { //go:linkname reflect_makemap reflect.makemap func reflect_makemap(t *maptype, cap int) *hmap { + // Check invariants and reflects math. if !ismapkey(t.key) { throw("runtime.reflect_makemap: unsupported map key type") } @@ -1294,5 +1359,5 @@ func reflect_ismapkey(t *_type) bool { return ismapkey(t) } -const maxZero = 1024 // must match value in ../cmd/compile/internal/gc/walk.go +const maxZero = 1024 // must match value in cmd/compile/internal/gc/walk.go var zeroVal [maxZero]byte diff --git a/libgo/go/runtime/mapspeed_test.go b/libgo/go/runtime/map_benchmark_test.go index aec0c51..025c039 100644 --- a/libgo/go/runtime/mapspeed_test.go +++ b/libgo/go/runtime/map_benchmark_test.go @@ -341,3 +341,32 @@ func BenchmarkComplexAlgMap(b *testing.B) { _ = m[k] } } + +func BenchmarkGoMapClear(b *testing.B) { + b.Run("Reflexive", func(b *testing.B) { + for size := 1; size < 100000; size *= 10 { + b.Run(strconv.Itoa(size), func(b *testing.B) { + m := make(map[int]int, size) + for i := 0; i < b.N; i++ { + m[0] = size // Add one element so len(m) != 0 avoiding fast paths. + for k := range m { + delete(m, k) + } + } + }) + } + }) + b.Run("NonReflexive", func(b *testing.B) { + for size := 1; size < 100000; size *= 10 { + b.Run(strconv.Itoa(size), func(b *testing.B) { + m := make(map[float64]int, size) + for i := 0; i < b.N; i++ { + m[1.0] = size // Add one element so len(m) != 0 avoiding fast paths. + for k := range m { + delete(m, k) + } + } + }) + } + }) +} diff --git a/libgo/go/runtime/map_fast32.go b/libgo/go/runtime/map_fast32.go new file mode 100644 index 0000000..a9a06a8 --- /dev/null +++ b/libgo/go/runtime/map_fast32.go @@ -0,0 +1,413 @@ +// Copyright 2018 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 runtime + +import ( + "runtime/internal/sys" + "unsafe" +) + +func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer { + if raceenabled && h != nil { + callerpc := getcallerpc() + racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_fast32)) + } + if h == nil || h.count == 0 { + return unsafe.Pointer(&zeroVal[0]) + } + if h.flags&hashWriting != 0 { + throw("concurrent map read and map write") + } + var b *bmap + if h.B == 0 { + // One-bucket table. No need to hash. + b = (*bmap)(h.buckets) + } else { + hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) + m := bucketMask(h.B) + b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) + if c := h.oldbuckets; c != nil { + if !h.sameSizeGrow() { + // There used to be half as many buckets; mask down one more power of two. + m >>= 1 + } + oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) + if !evacuated(oldb) { + b = oldb + } + } + } + for ; b != nil; b = b.overflow(t) { + for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 4) { + if *(*uint32)(k) == key && b.tophash[i] != empty { + return add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize)) + } + } + } + return unsafe.Pointer(&zeroVal[0]) +} + +func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) { + if raceenabled && h != nil { + callerpc := getcallerpc() + racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_fast32)) + } + if h == nil || h.count == 0 { + return unsafe.Pointer(&zeroVal[0]), false + } + if h.flags&hashWriting != 0 { + throw("concurrent map read and map write") + } + var b *bmap + if h.B == 0 { + // One-bucket table. No need to hash. + b = (*bmap)(h.buckets) + } else { + hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) + m := bucketMask(h.B) + b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) + if c := h.oldbuckets; c != nil { + if !h.sameSizeGrow() { + // There used to be half as many buckets; mask down one more power of two. + m >>= 1 + } + oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) + if !evacuated(oldb) { + b = oldb + } + } + } + for ; b != nil; b = b.overflow(t) { + for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 4) { + if *(*uint32)(k) == key && b.tophash[i] != empty { + return add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize)), true + } + } + } + return unsafe.Pointer(&zeroVal[0]), false +} + +func mapassign_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer { + if h == nil { + panic(plainError("assignment to entry in nil map")) + } + if raceenabled { + callerpc := getcallerpc() + racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast32)) + } + if h.flags&hashWriting != 0 { + throw("concurrent map writes") + } + hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) + + // Set hashWriting after calling alg.hash for consistency with mapassign. + h.flags |= hashWriting + + if h.buckets == nil { + h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) + } + +again: + bucket := hash & bucketMask(h.B) + if h.growing() { + growWork_fast32(t, h, bucket) + } + b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) + + var insertb *bmap + var inserti uintptr + var insertk unsafe.Pointer + + for { + for i := uintptr(0); i < bucketCnt; i++ { + if b.tophash[i] == empty { + if insertb == nil { + inserti = i + insertb = b + } + continue + } + k := *((*uint32)(add(unsafe.Pointer(b), dataOffset+i*4))) + if k != key { + continue + } + inserti = i + insertb = b + goto done + } + ovf := b.overflow(t) + if ovf == nil { + break + } + b = ovf + } + + // Did not find mapping for key. Allocate new cell & add entry. + + // If we hit the max load factor or we have too many overflow buckets, + // and we're not already in the middle of growing, start growing. + if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { + hashGrow(t, h) + goto again // Growing the table invalidates everything, so try again + } + + if insertb == nil { + // all current buckets are full, allocate a new one. + insertb = h.newoverflow(t, b) + inserti = 0 // not necessary, but avoids needlessly spilling inserti + } + insertb.tophash[inserti&(bucketCnt-1)] = tophash(hash) // mask inserti to avoid bounds checks + + insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*4) + // store new key at insert position + *(*uint32)(insertk) = key + + h.count++ + +done: + val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*4+inserti*uintptr(t.valuesize)) + if h.flags&hashWriting == 0 { + throw("concurrent map writes") + } + h.flags &^= hashWriting + return val +} + +func mapassign_fast32ptr(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { + if h == nil { + panic(plainError("assignment to entry in nil map")) + } + if raceenabled { + callerpc := getcallerpc() + racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast32)) + } + if h.flags&hashWriting != 0 { + throw("concurrent map writes") + } + hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) + + // Set hashWriting after calling alg.hash for consistency with mapassign. + h.flags |= hashWriting + + if h.buckets == nil { + h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) + } + +again: + bucket := hash & bucketMask(h.B) + if h.growing() { + growWork_fast32(t, h, bucket) + } + b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) + + var insertb *bmap + var inserti uintptr + var insertk unsafe.Pointer + + for { + for i := uintptr(0); i < bucketCnt; i++ { + if b.tophash[i] == empty { + if insertb == nil { + inserti = i + insertb = b + } + continue + } + k := *((*unsafe.Pointer)(add(unsafe.Pointer(b), dataOffset+i*4))) + if k != key { + continue + } + inserti = i + insertb = b + goto done + } + ovf := b.overflow(t) + if ovf == nil { + break + } + b = ovf + } + + // Did not find mapping for key. Allocate new cell & add entry. + + // If we hit the max load factor or we have too many overflow buckets, + // and we're not already in the middle of growing, start growing. + if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { + hashGrow(t, h) + goto again // Growing the table invalidates everything, so try again + } + + if insertb == nil { + // all current buckets are full, allocate a new one. + insertb = h.newoverflow(t, b) + inserti = 0 // not necessary, but avoids needlessly spilling inserti + } + insertb.tophash[inserti&(bucketCnt-1)] = tophash(hash) // mask inserti to avoid bounds checks + + insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*4) + // store new key at insert position + *(*unsafe.Pointer)(insertk) = key + + h.count++ + +done: + val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*4+inserti*uintptr(t.valuesize)) + if h.flags&hashWriting == 0 { + throw("concurrent map writes") + } + h.flags &^= hashWriting + return val +} + +func mapdelete_fast32(t *maptype, h *hmap, key uint32) { + if raceenabled && h != nil { + callerpc := getcallerpc() + racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapdelete_fast32)) + } + if h == nil || h.count == 0 { + return + } + if h.flags&hashWriting != 0 { + throw("concurrent map writes") + } + + hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) + + // Set hashWriting after calling alg.hash for consistency with mapdelete + h.flags |= hashWriting + + bucket := hash & bucketMask(h.B) + if h.growing() { + growWork_fast32(t, h, bucket) + } + b := (*bmap)(add(h.buckets, bucket*uintptr(t.bucketsize))) +search: + for ; b != nil; b = b.overflow(t) { + for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 4) { + if key != *(*uint32)(k) || b.tophash[i] == empty { + continue + } + // Only clear key if there are pointers in it. + if t.key.kind&kindNoPointers == 0 { + memclrHasPointers(k, t.key.size) + } + v := add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize)) + if t.elem.kind&kindNoPointers == 0 { + memclrHasPointers(v, t.elem.size) + } else { + memclrNoHeapPointers(v, t.elem.size) + } + b.tophash[i] = empty + h.count-- + break search + } + } + + if h.flags&hashWriting == 0 { + throw("concurrent map writes") + } + h.flags &^= hashWriting +} + +func growWork_fast32(t *maptype, h *hmap, bucket uintptr) { + // make sure we evacuate the oldbucket corresponding + // to the bucket we're about to use + evacuate_fast32(t, h, bucket&h.oldbucketmask()) + + // evacuate one more oldbucket to make progress on growing + if h.growing() { + evacuate_fast32(t, h, h.nevacuate) + } +} + +func evacuate_fast32(t *maptype, h *hmap, oldbucket uintptr) { + b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize))) + newbit := h.noldbuckets() + if !evacuated(b) { + // TODO: reuse overflow buckets instead of using new ones, if there + // is no iterator using the old buckets. (If !oldIterator.) + + // xy contains the x and y (low and high) evacuation destinations. + var xy [2]evacDst + x := &xy[0] + x.b = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize))) + x.k = add(unsafe.Pointer(x.b), dataOffset) + x.v = add(x.k, bucketCnt*4) + + if !h.sameSizeGrow() { + // Only calculate y pointers if we're growing bigger. + // Otherwise GC can see bad pointers. + y := &xy[1] + y.b = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize))) + y.k = add(unsafe.Pointer(y.b), dataOffset) + y.v = add(y.k, bucketCnt*4) + } + + for ; b != nil; b = b.overflow(t) { + k := add(unsafe.Pointer(b), dataOffset) + v := add(k, bucketCnt*4) + for i := 0; i < bucketCnt; i, k, v = i+1, add(k, 4), add(v, uintptr(t.valuesize)) { + top := b.tophash[i] + if top == empty { + b.tophash[i] = evacuatedEmpty + continue + } + if top < minTopHash { + throw("bad map state") + } + var useY uint8 + if !h.sameSizeGrow() { + // Compute hash to make our evacuation decision (whether we need + // to send this key/value to bucket x or bucket y). + hash := t.key.hashfn(k, uintptr(h.hash0)) + if hash&newbit != 0 { + useY = 1 + } + } + + b.tophash[i] = evacuatedX + useY // evacuatedX + 1 == evacuatedY, enforced in makemap + dst := &xy[useY] // evacuation destination + + if dst.i == bucketCnt { + dst.b = h.newoverflow(t, dst.b) + dst.i = 0 + dst.k = add(unsafe.Pointer(dst.b), dataOffset) + dst.v = add(dst.k, bucketCnt*4) + } + dst.b.tophash[dst.i&(bucketCnt-1)] = top // mask dst.i as an optimization, to avoid a bounds check + + // Copy key. + if sys.PtrSize == 4 && t.key.kind&kindNoPointers == 0 && writeBarrier.enabled { + // Write with a write barrier. + *(*unsafe.Pointer)(dst.k) = *(*unsafe.Pointer)(k) + } else { + *(*uint32)(dst.k) = *(*uint32)(k) + } + + typedmemmove(t.elem, dst.v, v) + dst.i++ + // These updates might push these pointers past the end of the + // key or value arrays. That's ok, as we have the overflow pointer + // at the end of the bucket to protect against pointing past the + // end of the bucket. + dst.k = add(dst.k, 4) + dst.v = add(dst.v, uintptr(t.valuesize)) + } + } + // Unlink the overflow buckets & clear key/value to help GC. + if h.flags&oldIterator == 0 && t.bucket.kind&kindNoPointers == 0 { + b := add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)) + // Preserve b.tophash because the evacuation + // state is maintained there. + ptr := add(b, dataOffset) + n := uintptr(t.bucketsize) - dataOffset + memclrHasPointers(ptr, n) + } + } + + if oldbucket == h.nevacuate { + advanceEvacuationMark(h, t, newbit) + } +} diff --git a/libgo/go/runtime/map_fast64.go b/libgo/go/runtime/map_fast64.go new file mode 100644 index 0000000..a2a51fc --- /dev/null +++ b/libgo/go/runtime/map_fast64.go @@ -0,0 +1,419 @@ +// Copyright 2018 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 runtime + +import ( + "runtime/internal/sys" + "unsafe" +) + +func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer { + if raceenabled && h != nil { + callerpc := getcallerpc() + racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_fast64)) + } + if h == nil || h.count == 0 { + return unsafe.Pointer(&zeroVal[0]) + } + if h.flags&hashWriting != 0 { + throw("concurrent map read and map write") + } + var b *bmap + if h.B == 0 { + // One-bucket table. No need to hash. + b = (*bmap)(h.buckets) + } else { + hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) + m := bucketMask(h.B) + b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) + if c := h.oldbuckets; c != nil { + if !h.sameSizeGrow() { + // There used to be half as many buckets; mask down one more power of two. + m >>= 1 + } + oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) + if !evacuated(oldb) { + b = oldb + } + } + } + for ; b != nil; b = b.overflow(t) { + for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 8) { + if *(*uint64)(k) == key && b.tophash[i] != empty { + return add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize)) + } + } + } + return unsafe.Pointer(&zeroVal[0]) +} + +func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) { + if raceenabled && h != nil { + callerpc := getcallerpc() + racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_fast64)) + } + if h == nil || h.count == 0 { + return unsafe.Pointer(&zeroVal[0]), false + } + if h.flags&hashWriting != 0 { + throw("concurrent map read and map write") + } + var b *bmap + if h.B == 0 { + // One-bucket table. No need to hash. + b = (*bmap)(h.buckets) + } else { + hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) + m := bucketMask(h.B) + b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) + if c := h.oldbuckets; c != nil { + if !h.sameSizeGrow() { + // There used to be half as many buckets; mask down one more power of two. + m >>= 1 + } + oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) + if !evacuated(oldb) { + b = oldb + } + } + } + for ; b != nil; b = b.overflow(t) { + for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 8) { + if *(*uint64)(k) == key && b.tophash[i] != empty { + return add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize)), true + } + } + } + return unsafe.Pointer(&zeroVal[0]), false +} + +func mapassign_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer { + if h == nil { + panic(plainError("assignment to entry in nil map")) + } + if raceenabled { + callerpc := getcallerpc() + racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast64)) + } + if h.flags&hashWriting != 0 { + throw("concurrent map writes") + } + hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) + + // Set hashWriting after calling alg.hash for consistency with mapassign. + h.flags |= hashWriting + + if h.buckets == nil { + h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) + } + +again: + bucket := hash & bucketMask(h.B) + if h.growing() { + growWork_fast64(t, h, bucket) + } + b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) + + var insertb *bmap + var inserti uintptr + var insertk unsafe.Pointer + + for { + for i := uintptr(0); i < bucketCnt; i++ { + if b.tophash[i] == empty { + if insertb == nil { + insertb = b + inserti = i + } + continue + } + k := *((*uint64)(add(unsafe.Pointer(b), dataOffset+i*8))) + if k != key { + continue + } + insertb = b + inserti = i + goto done + } + ovf := b.overflow(t) + if ovf == nil { + break + } + b = ovf + } + + // Did not find mapping for key. Allocate new cell & add entry. + + // If we hit the max load factor or we have too many overflow buckets, + // and we're not already in the middle of growing, start growing. + if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { + hashGrow(t, h) + goto again // Growing the table invalidates everything, so try again + } + + if insertb == nil { + // all current buckets are full, allocate a new one. + insertb = h.newoverflow(t, b) + inserti = 0 // not necessary, but avoids needlessly spilling inserti + } + insertb.tophash[inserti&(bucketCnt-1)] = tophash(hash) // mask inserti to avoid bounds checks + + insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*8) + // store new key at insert position + *(*uint64)(insertk) = key + + h.count++ + +done: + val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*8+inserti*uintptr(t.valuesize)) + if h.flags&hashWriting == 0 { + throw("concurrent map writes") + } + h.flags &^= hashWriting + return val +} + +func mapassign_fast64ptr(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { + if h == nil { + panic(plainError("assignment to entry in nil map")) + } + if raceenabled { + callerpc := getcallerpc() + racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast64)) + } + if h.flags&hashWriting != 0 { + throw("concurrent map writes") + } + hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) + + // Set hashWriting after calling alg.hash for consistency with mapassign. + h.flags |= hashWriting + + if h.buckets == nil { + h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) + } + +again: + bucket := hash & bucketMask(h.B) + if h.growing() { + growWork_fast64(t, h, bucket) + } + b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) + + var insertb *bmap + var inserti uintptr + var insertk unsafe.Pointer + + for { + for i := uintptr(0); i < bucketCnt; i++ { + if b.tophash[i] == empty { + if insertb == nil { + insertb = b + inserti = i + } + continue + } + k := *((*unsafe.Pointer)(add(unsafe.Pointer(b), dataOffset+i*8))) + if k != key { + continue + } + insertb = b + inserti = i + goto done + } + ovf := b.overflow(t) + if ovf == nil { + break + } + b = ovf + } + + // Did not find mapping for key. Allocate new cell & add entry. + + // If we hit the max load factor or we have too many overflow buckets, + // and we're not already in the middle of growing, start growing. + if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { + hashGrow(t, h) + goto again // Growing the table invalidates everything, so try again + } + + if insertb == nil { + // all current buckets are full, allocate a new one. + insertb = h.newoverflow(t, b) + inserti = 0 // not necessary, but avoids needlessly spilling inserti + } + insertb.tophash[inserti&(bucketCnt-1)] = tophash(hash) // mask inserti to avoid bounds checks + + insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*8) + // store new key at insert position + *(*unsafe.Pointer)(insertk) = key + + h.count++ + +done: + val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*8+inserti*uintptr(t.valuesize)) + if h.flags&hashWriting == 0 { + throw("concurrent map writes") + } + h.flags &^= hashWriting + return val +} + +func mapdelete_fast64(t *maptype, h *hmap, key uint64) { + if raceenabled && h != nil { + callerpc := getcallerpc() + racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapdelete_fast64)) + } + if h == nil || h.count == 0 { + return + } + if h.flags&hashWriting != 0 { + throw("concurrent map writes") + } + + hash := t.key.hashfn(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) + + // Set hashWriting after calling alg.hash for consistency with mapdelete + h.flags |= hashWriting + + bucket := hash & bucketMask(h.B) + if h.growing() { + growWork_fast64(t, h, bucket) + } + b := (*bmap)(add(h.buckets, bucket*uintptr(t.bucketsize))) +search: + for ; b != nil; b = b.overflow(t) { + for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 8) { + if key != *(*uint64)(k) || b.tophash[i] == empty { + continue + } + // Only clear key if there are pointers in it. + if t.key.kind&kindNoPointers == 0 { + memclrHasPointers(k, t.key.size) + } + v := add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize)) + if t.elem.kind&kindNoPointers == 0 { + memclrHasPointers(v, t.elem.size) + } else { + memclrNoHeapPointers(v, t.elem.size) + } + b.tophash[i] = empty + h.count-- + break search + } + } + + if h.flags&hashWriting == 0 { + throw("concurrent map writes") + } + h.flags &^= hashWriting +} + +func growWork_fast64(t *maptype, h *hmap, bucket uintptr) { + // make sure we evacuate the oldbucket corresponding + // to the bucket we're about to use + evacuate_fast64(t, h, bucket&h.oldbucketmask()) + + // evacuate one more oldbucket to make progress on growing + if h.growing() { + evacuate_fast64(t, h, h.nevacuate) + } +} + +func evacuate_fast64(t *maptype, h *hmap, oldbucket uintptr) { + b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize))) + newbit := h.noldbuckets() + if !evacuated(b) { + // TODO: reuse overflow buckets instead of using new ones, if there + // is no iterator using the old buckets. (If !oldIterator.) + + // xy contains the x and y (low and high) evacuation destinations. + var xy [2]evacDst + x := &xy[0] + x.b = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize))) + x.k = add(unsafe.Pointer(x.b), dataOffset) + x.v = add(x.k, bucketCnt*8) + + if !h.sameSizeGrow() { + // Only calculate y pointers if we're growing bigger. + // Otherwise GC can see bad pointers. + y := &xy[1] + y.b = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize))) + y.k = add(unsafe.Pointer(y.b), dataOffset) + y.v = add(y.k, bucketCnt*8) + } + + for ; b != nil; b = b.overflow(t) { + k := add(unsafe.Pointer(b), dataOffset) + v := add(k, bucketCnt*8) + for i := 0; i < bucketCnt; i, k, v = i+1, add(k, 8), add(v, uintptr(t.valuesize)) { + top := b.tophash[i] + if top == empty { + b.tophash[i] = evacuatedEmpty + continue + } + if top < minTopHash { + throw("bad map state") + } + var useY uint8 + if !h.sameSizeGrow() { + // Compute hash to make our evacuation decision (whether we need + // to send this key/value to bucket x or bucket y). + hash := t.key.hashfn(k, uintptr(h.hash0)) + if hash&newbit != 0 { + useY = 1 + } + } + + b.tophash[i] = evacuatedX + useY // evacuatedX + 1 == evacuatedY, enforced in makemap + dst := &xy[useY] // evacuation destination + + if dst.i == bucketCnt { + dst.b = h.newoverflow(t, dst.b) + dst.i = 0 + dst.k = add(unsafe.Pointer(dst.b), dataOffset) + dst.v = add(dst.k, bucketCnt*8) + } + dst.b.tophash[dst.i&(bucketCnt-1)] = top // mask dst.i as an optimization, to avoid a bounds check + + // Copy key. + if t.key.kind&kindNoPointers == 0 && writeBarrier.enabled { + if sys.PtrSize == 8 { + // Write with a write barrier. + *(*unsafe.Pointer)(dst.k) = *(*unsafe.Pointer)(k) + } else { + // There are three ways to squeeze at least one 32 bit pointer into 64 bits. + // Give up and call typedmemmove. + typedmemmove(t.key, dst.k, k) + } + } else { + *(*uint64)(dst.k) = *(*uint64)(k) + } + + typedmemmove(t.elem, dst.v, v) + dst.i++ + // These updates might push these pointers past the end of the + // key or value arrays. That's ok, as we have the overflow pointer + // at the end of the bucket to protect against pointing past the + // end of the bucket. + dst.k = add(dst.k, 8) + dst.v = add(dst.v, uintptr(t.valuesize)) + } + } + // Unlink the overflow buckets & clear key/value to help GC. + if h.flags&oldIterator == 0 && t.bucket.kind&kindNoPointers == 0 { + b := add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)) + // Preserve b.tophash because the evacuation + // state is maintained there. + ptr := add(b, dataOffset) + n := uintptr(t.bucketsize) - dataOffset + memclrHasPointers(ptr, n) + } + } + + if oldbucket == h.nevacuate { + advanceEvacuationMark(h, t, newbit) + } +} diff --git a/libgo/go/runtime/map_faststr.go b/libgo/go/runtime/map_faststr.go new file mode 100644 index 0000000..5812b3f --- /dev/null +++ b/libgo/go/runtime/map_faststr.go @@ -0,0 +1,430 @@ +// Copyright 2018 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 runtime + +import ( + "runtime/internal/sys" + "unsafe" +) + +func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer { + if raceenabled && h != nil { + callerpc := getcallerpc() + racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_faststr)) + } + if h == nil || h.count == 0 { + return unsafe.Pointer(&zeroVal[0]) + } + if h.flags&hashWriting != 0 { + throw("concurrent map read and map write") + } + key := stringStructOf(&ky) + if h.B == 0 { + // One-bucket table. + b := (*bmap)(h.buckets) + if key.len < 32 { + // short key, doing lots of comparisons is ok + for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { + k := (*stringStruct)(kptr) + if k.len != key.len || b.tophash[i] == empty { + continue + } + if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)) + } + } + return unsafe.Pointer(&zeroVal[0]) + } + // long key, try not to do more comparisons than necessary + keymaybe := uintptr(bucketCnt) + for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { + k := (*stringStruct)(kptr) + if k.len != key.len || b.tophash[i] == empty { + continue + } + if k.str == key.str { + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)) + } + // check first 4 bytes + if *((*[4]byte)(key.str)) != *((*[4]byte)(k.str)) { + continue + } + // check last 4 bytes + if *((*[4]byte)(add(key.str, uintptr(key.len)-4))) != *((*[4]byte)(add(k.str, uintptr(key.len)-4))) { + continue + } + if keymaybe != bucketCnt { + // Two keys are potential matches. Use hash to distinguish them. + goto dohash + } + keymaybe = i + } + if keymaybe != bucketCnt { + k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*sys.PtrSize)) + if memequal(k.str, key.str, uintptr(key.len)) { + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+keymaybe*uintptr(t.valuesize)) + } + } + return unsafe.Pointer(&zeroVal[0]) + } +dohash: + hash := t.key.hashfn(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0)) + m := bucketMask(h.B) + b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) + if c := h.oldbuckets; c != nil { + if !h.sameSizeGrow() { + // There used to be half as many buckets; mask down one more power of two. + m >>= 1 + } + oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) + if !evacuated(oldb) { + b = oldb + } + } + top := tophash(hash) + for ; b != nil; b = b.overflow(t) { + for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { + k := (*stringStruct)(kptr) + if k.len != key.len || b.tophash[i] != top { + continue + } + if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)) + } + } + } + return unsafe.Pointer(&zeroVal[0]) +} + +func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) { + if raceenabled && h != nil { + callerpc := getcallerpc() + racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr)) + } + if h == nil || h.count == 0 { + return unsafe.Pointer(&zeroVal[0]), false + } + if h.flags&hashWriting != 0 { + throw("concurrent map read and map write") + } + key := stringStructOf(&ky) + if h.B == 0 { + // One-bucket table. + b := (*bmap)(h.buckets) + if key.len < 32 { + // short key, doing lots of comparisons is ok + for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { + k := (*stringStruct)(kptr) + if k.len != key.len || b.tophash[i] == empty { + continue + } + if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)), true + } + } + return unsafe.Pointer(&zeroVal[0]), false + } + // long key, try not to do more comparisons than necessary + keymaybe := uintptr(bucketCnt) + for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { + k := (*stringStruct)(kptr) + if k.len != key.len || b.tophash[i] == empty { + continue + } + if k.str == key.str { + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)), true + } + // check first 4 bytes + if *((*[4]byte)(key.str)) != *((*[4]byte)(k.str)) { + continue + } + // check last 4 bytes + if *((*[4]byte)(add(key.str, uintptr(key.len)-4))) != *((*[4]byte)(add(k.str, uintptr(key.len)-4))) { + continue + } + if keymaybe != bucketCnt { + // Two keys are potential matches. Use hash to distinguish them. + goto dohash + } + keymaybe = i + } + if keymaybe != bucketCnt { + k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*sys.PtrSize)) + if memequal(k.str, key.str, uintptr(key.len)) { + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+keymaybe*uintptr(t.valuesize)), true + } + } + return unsafe.Pointer(&zeroVal[0]), false + } +dohash: + hash := t.key.hashfn(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0)) + m := bucketMask(h.B) + b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) + if c := h.oldbuckets; c != nil { + if !h.sameSizeGrow() { + // There used to be half as many buckets; mask down one more power of two. + m >>= 1 + } + oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) + if !evacuated(oldb) { + b = oldb + } + } + top := tophash(hash) + for ; b != nil; b = b.overflow(t) { + for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { + k := (*stringStruct)(kptr) + if k.len != key.len || b.tophash[i] != top { + continue + } + if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)), true + } + } + } + return unsafe.Pointer(&zeroVal[0]), false +} + +func mapassign_faststr(t *maptype, h *hmap, s string) unsafe.Pointer { + if h == nil { + panic(plainError("assignment to entry in nil map")) + } + if raceenabled { + callerpc := getcallerpc() + racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_faststr)) + } + if h.flags&hashWriting != 0 { + throw("concurrent map writes") + } + key := stringStructOf(&s) + hash := t.key.hashfn(noescape(unsafe.Pointer(&s)), uintptr(h.hash0)) + + // Set hashWriting after calling alg.hash for consistency with mapassign. + h.flags |= hashWriting + + if h.buckets == nil { + h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) + } + +again: + bucket := hash & bucketMask(h.B) + if h.growing() { + growWork_faststr(t, h, bucket) + } + b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize))) + top := tophash(hash) + + var insertb *bmap + var inserti uintptr + var insertk unsafe.Pointer + + for { + for i := uintptr(0); i < bucketCnt; i++ { + if b.tophash[i] != top { + if b.tophash[i] == empty && insertb == nil { + insertb = b + inserti = i + } + continue + } + k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*sys.PtrSize)) + if k.len != key.len { + continue + } + if k.str != key.str && !memequal(k.str, key.str, uintptr(key.len)) { + continue + } + // already have a mapping for key. Update it. + inserti = i + insertb = b + goto done + } + ovf := b.overflow(t) + if ovf == nil { + break + } + b = ovf + } + + // Did not find mapping for key. Allocate new cell & add entry. + + // If we hit the max load factor or we have too many overflow buckets, + // and we're not already in the middle of growing, start growing. + if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { + hashGrow(t, h) + goto again // Growing the table invalidates everything, so try again + } + + if insertb == nil { + // all current buckets are full, allocate a new one. + insertb = h.newoverflow(t, b) + inserti = 0 // not necessary, but avoids needlessly spilling inserti + } + insertb.tophash[inserti&(bucketCnt-1)] = top // mask inserti to avoid bounds checks + + insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*2*sys.PtrSize) + // store new key at insert position + *((*stringStruct)(insertk)) = *key + h.count++ + +done: + val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*2*sys.PtrSize+inserti*uintptr(t.valuesize)) + if h.flags&hashWriting == 0 { + throw("concurrent map writes") + } + h.flags &^= hashWriting + return val +} + +func mapdelete_faststr(t *maptype, h *hmap, ky string) { + if raceenabled && h != nil { + callerpc := getcallerpc() + racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapdelete_faststr)) + } + if h == nil || h.count == 0 { + return + } + if h.flags&hashWriting != 0 { + throw("concurrent map writes") + } + + key := stringStructOf(&ky) + hash := t.key.hashfn(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0)) + + // Set hashWriting after calling alg.hash for consistency with mapdelete + h.flags |= hashWriting + + bucket := hash & bucketMask(h.B) + if h.growing() { + growWork_faststr(t, h, bucket) + } + b := (*bmap)(add(h.buckets, bucket*uintptr(t.bucketsize))) + top := tophash(hash) +search: + for ; b != nil; b = b.overflow(t) { + for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { + k := (*stringStruct)(kptr) + if k.len != key.len || b.tophash[i] != top { + continue + } + if k.str != key.str && !memequal(k.str, key.str, uintptr(key.len)) { + continue + } + // Clear key's pointer. + k.str = nil + v := add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)) + if t.elem.kind&kindNoPointers == 0 { + memclrHasPointers(v, t.elem.size) + } else { + memclrNoHeapPointers(v, t.elem.size) + } + b.tophash[i] = empty + h.count-- + break search + } + } + + if h.flags&hashWriting == 0 { + throw("concurrent map writes") + } + h.flags &^= hashWriting +} + +func growWork_faststr(t *maptype, h *hmap, bucket uintptr) { + // make sure we evacuate the oldbucket corresponding + // to the bucket we're about to use + evacuate_faststr(t, h, bucket&h.oldbucketmask()) + + // evacuate one more oldbucket to make progress on growing + if h.growing() { + evacuate_faststr(t, h, h.nevacuate) + } +} + +func evacuate_faststr(t *maptype, h *hmap, oldbucket uintptr) { + b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize))) + newbit := h.noldbuckets() + if !evacuated(b) { + // TODO: reuse overflow buckets instead of using new ones, if there + // is no iterator using the old buckets. (If !oldIterator.) + + // xy contains the x and y (low and high) evacuation destinations. + var xy [2]evacDst + x := &xy[0] + x.b = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize))) + x.k = add(unsafe.Pointer(x.b), dataOffset) + x.v = add(x.k, bucketCnt*2*sys.PtrSize) + + if !h.sameSizeGrow() { + // Only calculate y pointers if we're growing bigger. + // Otherwise GC can see bad pointers. + y := &xy[1] + y.b = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize))) + y.k = add(unsafe.Pointer(y.b), dataOffset) + y.v = add(y.k, bucketCnt*2*sys.PtrSize) + } + + for ; b != nil; b = b.overflow(t) { + k := add(unsafe.Pointer(b), dataOffset) + v := add(k, bucketCnt*2*sys.PtrSize) + for i := 0; i < bucketCnt; i, k, v = i+1, add(k, 2*sys.PtrSize), add(v, uintptr(t.valuesize)) { + top := b.tophash[i] + if top == empty { + b.tophash[i] = evacuatedEmpty + continue + } + if top < minTopHash { + throw("bad map state") + } + var useY uint8 + if !h.sameSizeGrow() { + // Compute hash to make our evacuation decision (whether we need + // to send this key/value to bucket x or bucket y). + hash := t.key.hashfn(k, uintptr(h.hash0)) + if hash&newbit != 0 { + useY = 1 + } + } + + b.tophash[i] = evacuatedX + useY // evacuatedX + 1 == evacuatedY, enforced in makemap + dst := &xy[useY] // evacuation destination + + if dst.i == bucketCnt { + dst.b = h.newoverflow(t, dst.b) + dst.i = 0 + dst.k = add(unsafe.Pointer(dst.b), dataOffset) + dst.v = add(dst.k, bucketCnt*2*sys.PtrSize) + } + dst.b.tophash[dst.i&(bucketCnt-1)] = top // mask dst.i as an optimization, to avoid a bounds check + + // Copy key. + *(*string)(dst.k) = *(*string)(k) + + typedmemmove(t.elem, dst.v, v) + dst.i++ + // These updates might push these pointers past the end of the + // key or value arrays. That's ok, as we have the overflow pointer + // at the end of the bucket to protect against pointing past the + // end of the bucket. + dst.k = add(dst.k, 2*sys.PtrSize) + dst.v = add(dst.v, uintptr(t.valuesize)) + } + } + // Unlink the overflow buckets & clear key/value to help GC. + // Unlink the overflow buckets & clear key/value to help GC. + if h.flags&oldIterator == 0 && t.bucket.kind&kindNoPointers == 0 { + b := add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)) + // Preserve b.tophash because the evacuation + // state is maintained there. + ptr := add(b, dataOffset) + n := uintptr(t.bucketsize) - dataOffset + memclrHasPointers(ptr, n) + } + } + + if oldbucket == h.nevacuate { + advanceEvacuationMark(h, t, newbit) + } +} diff --git a/libgo/go/runtime/map_test.go b/libgo/go/runtime/map_test.go index 6d7097e..13f1d2e 100644 --- a/libgo/go/runtime/map_test.go +++ b/libgo/go/runtime/map_test.go @@ -9,6 +9,7 @@ import ( "math" "reflect" "runtime" + "runtime/internal/sys" "sort" "strconv" "strings" @@ -16,6 +17,17 @@ import ( "testing" ) +func TestHmapSize(t *testing.T) { + // The structure of hmap is defined in runtime/map.go + // and in cmd/compile/internal/gc/reflect.go and must be in sync. + // The size of hmap should be 48 bytes on 64 bit and 28 bytes on 32 bit platforms. + var hmapSize = uintptr(8 + 5*sys.PtrSize) + if runtime.RuntimeHmapSize != hmapSize { + t.Errorf("sizeof(runtime.hmap{})==%d, want %d", runtime.RuntimeHmapSize, hmapSize) + } + +} + // negative zero is a good test because: // 1) 0 and -0 are equal, yet have distinct representations. // 2) 0 is represented as all zeros, -0 isn't. @@ -52,14 +64,7 @@ func TestNegativeZero(t *testing.T) { } } -// nan is a good test because nan != nan, and nan has -// a randomized hash value. -func TestNan(t *testing.T) { - m := make(map[float64]int, 0) - nan := math.NaN() - m[nan] = 1 - m[nan] = 2 - m[nan] = 4 +func testMapNan(t *testing.T, m map[float64]int) { if len(m) != 3 { t.Error("length wrong") } @@ -78,6 +83,67 @@ func TestNan(t *testing.T) { } } +// nan is a good test because nan != nan, and nan has +// a randomized hash value. +func TestMapAssignmentNan(t *testing.T) { + m := make(map[float64]int, 0) + nan := math.NaN() + + // Test assignment. + m[nan] = 1 + m[nan] = 2 + m[nan] = 4 + testMapNan(t, m) +} + +// nan is a good test because nan != nan, and nan has +// a randomized hash value. +func TestMapOperatorAssignmentNan(t *testing.T) { + m := make(map[float64]int, 0) + nan := math.NaN() + + // Test assignment operations. + m[nan] += 1 + m[nan] += 2 + m[nan] += 4 + testMapNan(t, m) +} + +func TestMapOperatorAssignment(t *testing.T) { + m := make(map[int]int, 0) + + // "m[k] op= x" is rewritten into "m[k] = m[k] op x" + // differently when op is / or % than when it isn't. + // Simple test to make sure they all work as expected. + m[0] = 12345 + m[0] += 67890 + m[0] /= 123 + m[0] %= 456 + + const want = (12345 + 67890) / 123 % 456 + if got := m[0]; got != want { + t.Errorf("got %d, want %d", got, want) + } +} + +var sinkAppend bool + +func TestMapAppendAssignment(t *testing.T) { + m := make(map[int][]int, 0) + + m[0] = nil + m[0] = append(m[0], 12345) + m[0] = append(m[0], 67890) + sinkAppend, m[0] = !sinkAppend, append(m[0], 123, 456) + a := []int{7, 8, 9, 0} + m[0] = append(m[0], a...) + + want := []int{12345, 67890, 123, 456, 7, 8, 9, 0} + if got := m[0]; !reflect.DeepEqual(got, want) { + t.Errorf("got %v, want %v", got, want) + } +} + // Maps aren't actually copied on assignment. func TestAlias(t *testing.T) { m := make(map[int]int, 0) @@ -92,18 +158,25 @@ func TestAlias(t *testing.T) { func TestGrowWithNaN(t *testing.T) { m := make(map[float64]int, 4) nan := math.NaN() + + // Use both assignment and assignment operations as they may + // behave differently. m[nan] = 1 m[nan] = 2 - m[nan] = 4 + m[nan] += 4 + cnt := 0 s := 0 growflag := true for k, v := range m { if growflag { // force a hashtable resize - for i := 0; i < 100; i++ { + for i := 0; i < 50; i++ { m[float64(i)] = i } + for i := 50; i < 100; i++ { + m[float64(i)] += i + } growflag = false } if k != k { @@ -128,8 +201,8 @@ func TestGrowWithNegativeZero(t *testing.T) { negzero := math.Copysign(0.0, -1.0) m := make(map[FloatInt]int, 4) m[FloatInt{0.0, 0}] = 1 - m[FloatInt{0.0, 1}] = 2 - m[FloatInt{0.0, 2}] = 4 + m[FloatInt{0.0, 1}] += 2 + m[FloatInt{0.0, 2}] += 4 m[FloatInt{0.0, 3}] = 8 growflag := true s := 0 @@ -211,9 +284,12 @@ func TestIterGrowAndDelete(t *testing.T) { // an iterator is still using them. func TestIterGrowWithGC(t *testing.T) { m := make(map[int]int, 4) - for i := 0; i < 16; i++ { + for i := 0; i < 8; i++ { m[i] = i } + for i := 8; i < 16; i++ { + m[i] += i + } growflag := true bitmask := 0 for k := range m { @@ -364,11 +440,11 @@ func TestEmptyKeyAndValue(t *testing.T) { // ("quick keys") as well as long keys. func TestSingleBucketMapStringKeys_DupLen(t *testing.T) { testMapLookups(t, map[string]string{ - "x": "x1val", - "xx": "x2val", - "foo": "fooval", - "bar": "barval", // same key length as "foo" - "xxxx": "x4val", + "x": "x1val", + "xx": "x2val", + "foo": "fooval", + "bar": "barval", // same key length as "foo" + "xxxx": "x4val", strings.Repeat("x", 128): "longval1", strings.Repeat("y", 128): "longval2", }) @@ -627,7 +703,7 @@ func TestMapBuckets(t *testing.T) { // have a nil bucket pointer due to starting with preallocated buckets // on the stack. Escaping maps start with a non-nil bucket pointer if // hint size is above bucketCnt and thereby have more than one bucket. - // These tests depend on bucketCnt and loadFactor* in hashmap.go. + // These tests depend on bucketCnt and loadFactor* in map.go. t.Run("mapliteral", func(t *testing.T) { for _, tt := range mapBucketTests { localMap := map[int]int{} @@ -802,6 +878,23 @@ func benchmarkMapAssignInt32(b *testing.B, n int) { } } +func benchmarkMapOperatorAssignInt32(b *testing.B, n int) { + a := make(map[int32]int) + for i := 0; i < b.N; i++ { + a[int32(i&(n-1))] += i + } +} + +func benchmarkMapAppendAssignInt32(b *testing.B, n int) { + a := make(map[int32][]int) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + key := int32(i & (n - 1)) + a[key] = append(a[key], i) + } +} + func benchmarkMapDeleteInt32(b *testing.B, n int) { a := make(map[int32]int, n) b.ResetTimer() @@ -824,6 +917,23 @@ func benchmarkMapAssignInt64(b *testing.B, n int) { } } +func benchmarkMapOperatorAssignInt64(b *testing.B, n int) { + a := make(map[int64]int) + for i := 0; i < b.N; i++ { + a[int64(i&(n-1))] += i + } +} + +func benchmarkMapAppendAssignInt64(b *testing.B, n int) { + a := make(map[int64][]int) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + key := int64(i & (n - 1)) + a[key] = append(a[key], i) + } +} + func benchmarkMapDeleteInt64(b *testing.B, n int) { a := make(map[int64]int, n) b.ResetTimer() @@ -851,6 +961,33 @@ func benchmarkMapAssignStr(b *testing.B, n int) { } } +func benchmarkMapOperatorAssignStr(b *testing.B, n int) { + k := make([]string, n) + for i := 0; i < len(k); i++ { + k[i] = strconv.Itoa(i) + } + b.ResetTimer() + a := make(map[string]string) + for i := 0; i < b.N; i++ { + key := k[i&(n-1)] + a[key] += key + } +} + +func benchmarkMapAppendAssignStr(b *testing.B, n int) { + k := make([]string, n) + for i := 0; i < len(k); i++ { + k[i] = strconv.Itoa(i) + } + a := make(map[string][]string) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + key := k[i&(n-1)] + a[key] = append(a[key], key) + } +} + func benchmarkMapDeleteStr(b *testing.B, n int) { i2s := make([]string, n) for i := 0; i < n; i++ { @@ -886,8 +1023,127 @@ func BenchmarkMapAssign(b *testing.B) { b.Run("Str", runWith(benchmarkMapAssignStr, 1<<8, 1<<16)) } +func BenchmarkMapOperatorAssign(b *testing.B) { + b.Run("Int32", runWith(benchmarkMapOperatorAssignInt32, 1<<8, 1<<16)) + b.Run("Int64", runWith(benchmarkMapOperatorAssignInt64, 1<<8, 1<<16)) + b.Run("Str", runWith(benchmarkMapOperatorAssignStr, 1<<8, 1<<16)) +} + +func BenchmarkMapAppendAssign(b *testing.B) { + b.Run("Int32", runWith(benchmarkMapAppendAssignInt32, 1<<8, 1<<16)) + b.Run("Int64", runWith(benchmarkMapAppendAssignInt64, 1<<8, 1<<16)) + b.Run("Str", runWith(benchmarkMapAppendAssignStr, 1<<8, 1<<16)) +} + func BenchmarkMapDelete(b *testing.B) { b.Run("Int32", runWith(benchmarkMapDeleteInt32, 100, 1000, 10000)) b.Run("Int64", runWith(benchmarkMapDeleteInt64, 100, 1000, 10000)) b.Run("Str", runWith(benchmarkMapDeleteStr, 100, 1000, 10000)) } + +func TestDeferDeleteSlow(t *testing.T) { + ks := []complex128{0, 1, 2, 3} + + m := make(map[interface{}]int) + for i, k := range ks { + m[k] = i + } + if len(m) != len(ks) { + t.Errorf("want %d elements, got %d", len(ks), len(m)) + } + + func() { + for _, k := range ks { + defer delete(m, k) + } + }() + if len(m) != 0 { + t.Errorf("want 0 elements, got %d", len(m)) + } +} + +// TestIncrementAfterDeleteValueInt and other test Issue 25936. +// Value types int, int32, int64 are affected. Value type string +// works as expected. +func TestIncrementAfterDeleteValueInt(t *testing.T) { + const key1 = 12 + const key2 = 13 + + m := make(map[int]int) + m[key1] = 99 + delete(m, key1) + m[key2]++ + if n2 := m[key2]; n2 != 1 { + t.Errorf("incremented 0 to %d", n2) + } +} + +func TestIncrementAfterDeleteValueInt32(t *testing.T) { + const key1 = 12 + const key2 = 13 + + m := make(map[int]int32) + m[key1] = 99 + delete(m, key1) + m[key2]++ + if n2 := m[key2]; n2 != 1 { + t.Errorf("incremented 0 to %d", n2) + } +} + +func TestIncrementAfterDeleteValueInt64(t *testing.T) { + const key1 = 12 + const key2 = 13 + + m := make(map[int]int64) + m[key1] = 99 + delete(m, key1) + m[key2]++ + if n2 := m[key2]; n2 != 1 { + t.Errorf("incremented 0 to %d", n2) + } +} + +func TestIncrementAfterDeleteKeyStringValueInt(t *testing.T) { + const key1 = "" + const key2 = "x" + + m := make(map[string]int) + m[key1] = 99 + delete(m, key1) + m[key2] += 1 + if n2 := m[key2]; n2 != 1 { + t.Errorf("incremented 0 to %d", n2) + } +} + +func TestIncrementAfterDeleteKeyValueString(t *testing.T) { + const key1 = "" + const key2 = "x" + + m := make(map[string]string) + m[key1] = "99" + delete(m, key1) + m[key2] += "1" + if n2 := m[key2]; n2 != "1" { + t.Errorf("appended '1' to empty (nil) string, got %s", n2) + } +} + +// TestIncrementAfterBulkClearKeyStringValueInt tests that map bulk +// deletion (mapclear) still works as expected. Note that it was not +// affected by Issue 25936. +func TestIncrementAfterBulkClearKeyStringValueInt(t *testing.T) { + const key1 = "" + const key2 = "x" + + m := make(map[string]int) + m[key1] = 99 + for k := range m { + delete(m, k) + } + m[key2]++ + if n2 := m[key2]; n2 != 1 { + t.Errorf("incremented 0 to %d", n2) + } +} diff --git a/libgo/go/runtime/mbarrier.go b/libgo/go/runtime/mbarrier.go index 3b8f714..24e5865 100644 --- a/libgo/go/runtime/mbarrier.go +++ b/libgo/go/runtime/mbarrier.go @@ -6,10 +6,10 @@ // // For the concurrent garbage collector, the Go compiler implements // updates to pointer-valued fields that may be in heap objects by -// emitting calls to write barriers. This file contains the actual write barrier -// implementation, gcmarkwb_m, and the various wrappers called by the -// compiler to implement pointer assignment, slice assignment, -// typed memmove, and so on. +// emitting calls to write barriers. The main write barrier for +// individual pointer writes is gcWriteBarrier and is implemented in +// assembly. This file contains write barrier entry points for bulk +// operations. See also mwbbuf.go. package runtime @@ -21,14 +21,10 @@ import ( // For gccgo, use go:linkname to rename compiler-called functions to // themselves, so that the compiler will export them. // -//go:linkname writebarrierptr runtime.writebarrierptr //go:linkname typedmemmove runtime.typedmemmove //go:linkname typedslicecopy runtime.typedslicecopy -// gcmarkwb_m is the mark-phase write barrier, the only barrier we have. -// The rest of this file exists only to make calls to this function. -// -// This is a hybrid barrier that combines a Yuasa-style deletion +// Go uses a hybrid barrier that combines a Yuasa-style deletion // barrier—which shades the object whose reference is being // overwritten—with Dijkstra insertion barrier—which shades the object // whose reference is being written. The insertion part of the barrier @@ -144,105 +140,17 @@ import ( // reachable by some goroutine that currently cannot reach it. // // -//go:nowritebarrierrec -//go:systemstack -func gcmarkwb_m(slot *uintptr, ptr uintptr) { - if writeBarrier.needed { - // Note: This turns bad pointer writes into bad - // pointer reads, which could be confusing. We avoid - // reading from obviously bad pointers, which should - // take care of the vast majority of these. We could - // patch this up in the signal handler, or use XCHG to - // combine the read and the write. Checking inheap is - // insufficient since we need to track changes to - // roots outside the heap. - // - // Note: profbuf.go omits a barrier during signal handler - // profile logging; that's safe only because this deletion barrier exists. - // If we remove the deletion barrier, we'll have to work out - // a new way to handle the profile logging. - if slot1 := uintptr(unsafe.Pointer(slot)); slot1 >= minPhysPageSize { - if optr := *slot; optr != 0 { - shade(optr) - } - } - // TODO: Make this conditional on the caller's stack color. - if ptr != 0 && inheap(ptr) { - shade(ptr) - } - } -} - -// writebarrierptr_prewrite1 invokes a write barrier for *dst = src -// prior to the write happening. -// -// Write barrier calls must not happen during critical GC and scheduler -// related operations. In particular there are times when the GC assumes -// that the world is stopped but scheduler related code is still being -// executed, dealing with syscalls, dealing with putting gs on runnable -// queues and so forth. This code cannot execute write barriers because -// the GC might drop them on the floor. Stopping the world involves removing -// the p associated with an m. We use the fact that m.p == nil to indicate -// that we are in one these critical section and throw if the write is of -// a pointer to a heap object. -//go:nosplit -func writebarrierptr_prewrite1(dst *uintptr, src uintptr) { - mp := acquirem() - if mp.inwb || mp.dying > 0 { - // We explicitly allow write barriers in startpanic_m, - // since we're going down anyway. Ignore them here. - releasem(mp) - return - } - systemstack(func() { - if mp.p == 0 && memstats.enablegc && !mp.inwb && inheap(src) { - throw("writebarrierptr_prewrite1 called with mp.p == nil") - } - mp.inwb = true - gcmarkwb_m(dst, src) - }) - mp.inwb = false - releasem(mp) -} - -// NOTE: Really dst *unsafe.Pointer, src unsafe.Pointer, -// but if we do that, Go inserts a write barrier on *dst = src. -//go:nosplit -func writebarrierptr(dst *uintptr, src uintptr) { - if writeBarrier.cgo { - cgoCheckWriteBarrier(dst, src) - } - if !writeBarrier.needed { - *dst = src - return - } - if src != 0 && src < minPhysPageSize { - systemstack(func() { - print("runtime: writebarrierptr *", dst, " = ", hex(src), "\n") - throw("bad pointer in write barrier") - }) - } - writebarrierptr_prewrite1(dst, src) - *dst = src -} - -// writebarrierptr_prewrite is like writebarrierptr, but the store -// will be performed by the caller after this call. The caller must -// not allow preemption between this call and the write. +// Signal handler pointer writes: // -//go:nosplit -func writebarrierptr_prewrite(dst *uintptr, src uintptr) { - if writeBarrier.cgo { - cgoCheckWriteBarrier(dst, src) - } - if !writeBarrier.needed { - return - } - if src != 0 && src < minPhysPageSize { - systemstack(func() { throw("bad pointer in write barrier") }) - } - writebarrierptr_prewrite1(dst, src) -} +// In general, the signal handler cannot safely invoke the write +// barrier because it may run without a P or even during the write +// barrier. +// +// There is exactly one exception: profbuf.go omits a barrier during +// signal handler profile logging. That's safe only because of the +// deletion barrier. See profbuf.go for a detailed argument. If we +// remove the deletion barrier, we'll have to work out a new way to +// handle the profile logging. // typedmemmove copies a value of type t to dst from src. // Must be nosplit, see #16026. @@ -252,6 +160,9 @@ func writebarrierptr_prewrite(dst *uintptr, src uintptr) { // //go:nosplit func typedmemmove(typ *_type, dst, src unsafe.Pointer) { + if dst == src { + return + } if typ.kind&kindNoPointers == 0 { bulkBarrierPreWrite(uintptr(dst), uintptr(src), typ.size) } @@ -335,6 +246,10 @@ func typedslicecopy(typ *_type, dst, src slice) int { cgoCheckSliceCopy(typ, dst, src, n) } + if dstp == srcp { + return n + } + // Note: No point in checking typ.kind&kindNoPointers here: // compiler only emits calls to typedslicecopy for types with pointers, // and growslice and reflect_typedslicecopy check for pointers diff --git a/libgo/go/runtime/mbitmap.go b/libgo/go/runtime/mbitmap.go index c6c8e6a..42c2015 100644 --- a/libgo/go/runtime/mbitmap.go +++ b/libgo/go/runtime/mbitmap.go @@ -13,12 +13,11 @@ // // Heap bitmap // -// The allocated heap comes from a subset of the memory in the range [start, used), -// where start == mheap_.arena_start and used == mheap_.arena_used. -// The heap bitmap comprises 2 bits for each pointer-sized word in that range, -// stored in bytes indexed backward in memory from start. -// That is, the byte at address start-1 holds the 2-bit entries for the four words -// start through start+3*ptrSize, the byte at start-2 holds the entries for +// The heap bitmap comprises 2 bits for each pointer-sized word in the heap, +// stored in the heapArena metadata backing each heap arena. +// That is, if ha is the heapArena for the arena starting a start, +// then ha.bitmap[0] holds the 2-bit entries for the four words start +// through start+3*ptrSize, ha.bitmap[1] holds the entries for // start+4*ptrSize through start+7*ptrSize, and so on. // // In each 2-bit entry, the lower bit holds the same information as in the 1-bit @@ -85,8 +84,8 @@ const ( bitPointer = 1 << 0 bitScan = 1 << 4 - heapBitsShift = 1 // shift offset between successive bitPointer or bitScan entries - heapBitmapScale = sys.PtrSize * (8 / 2) // number of data bytes described by one heap bitmap byte + heapBitsShift = 1 // shift offset between successive bitPointer or bitScan entries + wordsPerBitmapByte = 8 / 2 // heap words described by one bitmap byte // all scan/pointer bits in a byte bitScanAll = bitScan | bitScan<<heapBitsShift | bitScan<<(2*heapBitsShift) | bitScan<<(3*heapBitsShift) @@ -104,8 +103,6 @@ func addb(p *byte, n uintptr) *byte { } // subtractb returns the byte pointer p-n. -// subtractb is typically used when traversing the pointer tables referred to by hbits -// which are arranged in reverse order. //go:nowritebarrier //go:nosplit func subtractb(p *byte, n uintptr) *byte { @@ -126,8 +123,6 @@ func add1(p *byte) *byte { } // subtract1 returns the byte pointer p-1. -// subtract1 is typically used when traversing the pointer tables referred to by hbits -// which are arranged in reverse order. //go:nowritebarrier // // nosplit because it is used during write barriers and must not be preempted. @@ -139,28 +134,6 @@ func subtract1(p *byte) *byte { return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) - 1)) } -// mapBits maps any additional bitmap memory needed for the new arena memory. -// -// Don't call this directly. Call mheap.setArenaUsed. -// -//go:nowritebarrier -func (h *mheap) mapBits(arena_used uintptr) { - // Caller has added extra mappings to the arena. - // Add extra mappings of bitmap words as needed. - // We allocate extra bitmap pieces in chunks of bitmapChunk. - const bitmapChunk = 8192 - - n := (arena_used - mheap_.arena_start) / heapBitmapScale - n = round(n, bitmapChunk) - n = round(n, physPageSize) - if h.bitmap_mapped >= n { - return - } - - sysMap(unsafe.Pointer(h.bitmap-n), n-h.bitmap_mapped, h.arena_reserved, &memstats.gc_sys) - h.bitmap_mapped = n -} - // heapBits provides access to the bitmap bits for a single heap word. // The methods on heapBits take value receivers so that the compiler // can more easily inline calls to those methods and registerize the @@ -168,8 +141,14 @@ func (h *mheap) mapBits(arena_used uintptr) { type heapBits struct { bitp *uint8 shift uint32 + arena uint32 // Index of heap arena containing bitp + last *uint8 // Last byte arena's bitmap } +// Make the compiler check that heapBits.arena is large enough to hold +// the maximum arena frame number. +var _ = heapBits{arena: (1<<heapAddrBits)/heapArenaBytes - 1} + // markBits provides access to the mark bit for an object in the heap. // bytep points to the byte holding the mark bit. // mask is a byte with a single bit set that can be &ed with *bytep @@ -191,7 +170,7 @@ func (s *mspan) allocBitsForIndex(allocBitIndex uintptr) markBits { return markBits{bytep, mask, allocBitIndex} } -// refillaCache takes 8 bytes s.allocBits starting at whichByte +// refillAllocCache takes 8 bytes s.allocBits starting at whichByte // and negates them so that ctz (count trailing zeros) instructions // can be used. It then places these 8 bytes into the cached 64 bit // s.allocCache. @@ -278,7 +257,7 @@ func (s *mspan) objIndex(p uintptr) uintptr { return 0 } if s.baseMask != 0 { - // s.baseMask is 0, elemsize is a power of two, so shift by s.divShift + // s.baseMask is non-0, elemsize is a power of two, so shift by s.divShift return byteOffset >> s.divShift } return uintptr(((uint64(byteOffset) >> s.divShift) * uint64(s.divMul)) >> s.divShift2) @@ -329,9 +308,6 @@ func (m markBits) clearMarked() { // markBitsForSpan returns the markBits for the span base address base. func markBitsForSpan(base uintptr) (mbits markBits) { - if base < mheap_.arena_start || base >= mheap_.arena_used { - throw("markBitsForSpan: base out of range") - } mbits = markBitsForAddr(base) if mbits.mask != 1 { throw("markBitsForSpan: unaligned start") @@ -351,31 +327,36 @@ func (m *markBits) advance() { } // heapBitsForAddr returns the heapBits for the address addr. -// The caller must have already checked that addr is in the range [mheap_.arena_start, mheap_.arena_used). +// The caller must ensure addr is in an allocated span. +// In particular, be careful not to point past the end of an object. // // nosplit because it is used during write barriers and must not be preempted. //go:nosplit -func heapBitsForAddr(addr uintptr) heapBits { - // 2 bits per work, 4 pairs per byte, and a mask is hard coded. - off := (addr - mheap_.arena_start) / sys.PtrSize - return heapBits{(*uint8)(unsafe.Pointer(mheap_.bitmap - off/4 - 1)), uint32(off & 3)} -} - -// heapBitsForSpan returns the heapBits for the span base address base. -func heapBitsForSpan(base uintptr) (hbits heapBits) { - if base < mheap_.arena_start || base >= mheap_.arena_used { - print("runtime: base ", hex(base), " not in range [", hex(mheap_.arena_start), ",", hex(mheap_.arena_used), ")\n") - throw("heapBitsForSpan: base out of range") +func heapBitsForAddr(addr uintptr) (h heapBits) { + // 2 bits per word, 4 pairs per byte, and a mask is hard coded. + arena := arenaIndex(addr) + ha := mheap_.arenas[arena.l1()][arena.l2()] + // The compiler uses a load for nil checking ha, but in this + // case we'll almost never hit that cache line again, so it + // makes more sense to do a value check. + if ha == nil { + // addr is not in the heap. Return nil heapBits, which + // we expect to crash in the caller. + return } - return heapBitsForAddr(base) + h.bitp = &ha.bitmap[(addr/(sys.PtrSize*4))%heapArenaBitmapBytes] + h.shift = uint32((addr / sys.PtrSize) & 3) + h.arena = uint32(arena) + h.last = &ha.bitmap[len(ha.bitmap)-1] + return } -// heapBitsForObject returns the base address for the heap object -// containing the address p, the heapBits for base, -// the object's span, and of the index of the object in s. -// If p does not point into a heap object, -// return base == 0 -// otherwise return the base of the object. +// findObject returns the base address for the heap object containing +// the address p, the object's span, and the index of the object in s. +// If p does not point into a heap object, it returns base == 0. +// +// If p points is an invalid heap pointer and debug.invalidptr != 0, +// findObject panics. // // For gccgo, the forStack parameter is true if the value came from the stack. // The stack is collected conservatively and may contain invalid pointers. @@ -383,16 +364,9 @@ func heapBitsForSpan(base uintptr) (hbits heapBits) { // refBase and refOff optionally give the base address of the object // in which the pointer p was found and the byte offset at which it // was found. These are used for error reporting. -func heapBitsForObject(p, refBase, refOff uintptr, forStack bool) (base uintptr, hbits heapBits, s *mspan, objIndex uintptr) { - arenaStart := mheap_.arena_start - if p < arenaStart || p >= mheap_.arena_used { - return - } - off := p - arenaStart - idx := off >> _PageShift - // p points into the heap, but possibly to the middle of an object. - // Consult the span table to find the block beginning. - s = mheap_.spans[idx] +func findObject(p, refBase, refOff uintptr, forStack bool) (base uintptr, s *mspan, objIndex uintptr) { + s = spanOf(p) + // If p is a bad pointer, it may not be in s's bounds. if s == nil || p < s.base() || p >= s.limit || s.state != mSpanInUse { if s == nil || s.state == _MSpanManual || forStack { // If s is nil, the virtual address has never been part of the heap. @@ -419,7 +393,7 @@ func heapBitsForObject(p, refBase, refOff uintptr, forStack bool) (base uintptr, } else { print(" to unused region of span") } - print(" idx=", hex(idx), " span.base()=", hex(s.base()), " span.limit=", hex(s.limit), " span.state=", s.state, "\n") + print(" span.base()=", hex(s.base()), " span.limit=", hex(s.limit), " span.state=", s.state, "\n") if refBase != 0 { print("runtime: found in object at *(", hex(refBase), "+", hex(refOff), ")\n") gcDumpObject("object", refBase, refOff) @@ -458,8 +432,6 @@ func heapBitsForObject(p, refBase, refOff uintptr, forStack bool) (base uintptr, base += objIndex * s.elemsize } } - // Now that we know the actual base, compute heapBits to return to caller. - hbits = heapBitsForAddr(base) return } @@ -471,9 +443,42 @@ func heapBitsForObject(p, refBase, refOff uintptr, forStack bool) (base uintptr, //go:nosplit func (h heapBits) next() heapBits { if h.shift < 3*heapBitsShift { - return heapBits{h.bitp, h.shift + heapBitsShift} + h.shift += heapBitsShift + } else if h.bitp != h.last { + h.bitp, h.shift = add1(h.bitp), 0 + } else { + // Move to the next arena. + return h.nextArena() } - return heapBits{subtract1(h.bitp), 0} + return h +} + +// nextArena advances h to the beginning of the next heap arena. +// +// This is a slow-path helper to next. gc's inliner knows that +// heapBits.next can be inlined even though it calls this. This is +// marked noinline so it doesn't get inlined into next and cause next +// to be too big to inline. +// +//go:nosplit +//go:noinline +func (h heapBits) nextArena() heapBits { + h.arena++ + ai := arenaIdx(h.arena) + l2 := mheap_.arenas[ai.l1()] + if l2 == nil { + // We just passed the end of the object, which + // was also the end of the heap. Poison h. It + // should never be dereferenced at this point. + return heapBits{} + } + ha := l2[ai.l2()] + if ha == nil { + return heapBits{} + } + h.bitp, h.shift = &ha.bitmap[0], 0 + h.last = &ha.bitmap[len(ha.bitmap)-1] + return h } // forward returns the heapBits describing n pointer-sized words ahead of h in memory. @@ -481,9 +486,39 @@ func (h heapBits) next() heapBits { // h.forward(1) is equivalent to h.next(), just slower. // Note that forward does not modify h. The caller must record the result. // bits returns the heap bits for the current word. +//go:nosplit func (h heapBits) forward(n uintptr) heapBits { n += uintptr(h.shift) / heapBitsShift - return heapBits{subtractb(h.bitp, n/4), uint32(n%4) * heapBitsShift} + nbitp := uintptr(unsafe.Pointer(h.bitp)) + n/4 + h.shift = uint32(n%4) * heapBitsShift + if nbitp <= uintptr(unsafe.Pointer(h.last)) { + h.bitp = (*uint8)(unsafe.Pointer(nbitp)) + return h + } + + // We're in a new heap arena. + past := nbitp - (uintptr(unsafe.Pointer(h.last)) + 1) + h.arena += 1 + uint32(past/heapArenaBitmapBytes) + ai := arenaIdx(h.arena) + if l2 := mheap_.arenas[ai.l1()]; l2 != nil && l2[ai.l2()] != nil { + a := l2[ai.l2()] + h.bitp = &a.bitmap[past%heapArenaBitmapBytes] + h.last = &a.bitmap[len(a.bitmap)-1] + } else { + h.bitp, h.last = nil, nil + } + return h +} + +// forwardOrBoundary is like forward, but stops at boundaries between +// contiguous sections of the bitmap. It returns the number of words +// advanced over, which will be <= n. +func (h heapBits) forwardOrBoundary(n uintptr) (heapBits, uintptr) { + maxn := 4 * ((uintptr(unsafe.Pointer(h.last)) + 1) - uintptr(unsafe.Pointer(h.bitp))) + if n > maxn { + n = maxn + } + return h.forward(n), n } // The caller can test morePointers and isPointer by &-ing with bitScan and bitPointer. @@ -564,6 +599,8 @@ func (h heapBits) setCheckmarked(size uintptr) { // make sure the underlying allocation contains pointers, usually // by checking typ.kind&kindNoPointers. // +// Callers must perform cgo checks if writeBarrier.cgo. +// //go:nosplit func bulkBarrierPreWrite(dst, src, size uintptr) { if (dst|src|size)&(sys.PtrSize-1) != 0 { @@ -572,7 +609,7 @@ func bulkBarrierPreWrite(dst, src, size uintptr) { if !writeBarrier.needed { return } - if !inheap(dst) { + if s := spanOf(dst); s == nil { // If dst is a global, use the data or BSS bitmaps to // execute write barriers. lo := 0 @@ -594,6 +631,14 @@ func bulkBarrierPreWrite(dst, src, size uintptr) { } } return + } else if s.state != _MSpanInUse || dst < s.base() || s.limit <= dst { + // dst was heap memory at some point, but isn't now. + // It can't be a global. It must be either our stack, + // or in the case of direct channel sends, it could be + // another stack. Either way, no need for barriers. + // This will also catch if dst is in a freed span, + // though that should never have. + return } buf := &getg().m.p.ptr().wbBuf @@ -663,7 +708,7 @@ func bulkBarrierBitmap(dst, src, size, maskOffset uintptr, bits *uint8) { } } -// typeBitsBulkBarrier executes writebarrierptr_prewrite for every +// typeBitsBulkBarrier executes a write barrier for every // pointer that would be copied from [src, src+size) to [dst, // dst+size) by a memmove using the type bitmap to locate those // pointer slots. @@ -677,23 +722,26 @@ func bulkBarrierBitmap(dst, src, size, maskOffset uintptr, bits *uint8) { // Must not be preempted because it typically runs right before memmove, // and the GC must observe them as an atomic action. // +// Callers must perform cgo checks if writeBarrier.cgo. +// //go:nosplit func typeBitsBulkBarrier(typ *_type, dst, src, size uintptr) { if typ == nil { throw("runtime: typeBitsBulkBarrier without type") } if typ.size != size { - println("runtime: typeBitsBulkBarrier with type ", *typ.string, " of size ", typ.size, " but memory size", size) + println("runtime: typeBitsBulkBarrier with type ", typ.string(), " of size ", typ.size, " but memory size", size) throw("runtime: invalid typeBitsBulkBarrier") } if typ.kind&kindGCProg != 0 { - println("runtime: typeBitsBulkBarrier with type ", *typ.string, " with GC prog") + println("runtime: typeBitsBulkBarrier with type ", typ.string(), " with GC prog") throw("runtime: invalid typeBitsBulkBarrier") } if !writeBarrier.needed { return } ptrmask := typ.gcdata + buf := &getg().m.p.ptr().wbBuf var bits uint32 for i := uintptr(0); i < typ.ptrdata; i += sys.PtrSize { if i&(sys.PtrSize*8-1) == 0 { @@ -705,7 +753,9 @@ func typeBitsBulkBarrier(typ *_type, dst, src, size uintptr) { if bits&1 != 0 { dstx := (*uintptr)(unsafe.Pointer(dst + i)) srcx := (*uintptr)(unsafe.Pointer(src + i)) - writebarrierptr_prewrite(dstx, *srcx) + if !buf.putFast(*dstx, *srcx) { + wbBufFlush(nil, 0) + } } } } @@ -736,23 +786,28 @@ func (h heapBits) initSpan(s *mspan) { s.allocBits = newAllocBits(s.nelems) // Clear bits corresponding to objects. - if total%heapBitmapScale != 0 { + nw := total / sys.PtrSize + if nw%wordsPerBitmapByte != 0 { throw("initSpan: unaligned length") } - nbyte := total / heapBitmapScale - if sys.PtrSize == 8 && size == sys.PtrSize { - end := h.bitp - bitp := subtractb(end, nbyte-1) - for { - *bitp = bitPointerAll | bitScanAll - if bitp == end { - break + if h.shift != 0 { + throw("initSpan: unaligned base") + } + for nw > 0 { + hNext, anw := h.forwardOrBoundary(nw) + nbyte := anw / wordsPerBitmapByte + if sys.PtrSize == 8 && size == sys.PtrSize { + bitp := h.bitp + for i := uintptr(0); i < nbyte; i++ { + *bitp = bitPointerAll | bitScanAll + bitp = add1(bitp) } - bitp = add1(bitp) + } else { + memclrNoHeapPointers(unsafe.Pointer(h.bitp), nbyte) } - return + h = hNext + nw -= anw } - memclrNoHeapPointers(unsafe.Pointer(subtractb(h.bitp, nbyte-1)), nbyte) } // initCheckmarkSpan initializes a span for being checkmarked. @@ -764,10 +819,9 @@ func (h heapBits) initCheckmarkSpan(size, n, total uintptr) { // Only possible on 64-bit system, since minimum size is 8. // Must clear type bit (checkmark bit) of every word. // The type bit is the lower of every two-bit pair. - bitp := h.bitp - for i := uintptr(0); i < n; i += 4 { - *bitp &^= bitPointerAll - bitp = subtract1(bitp) + for i := uintptr(0); i < n; i += wordsPerBitmapByte { + *h.bitp &^= bitPointerAll + h = h.forward(wordsPerBitmapByte) } return } @@ -788,10 +842,9 @@ func (h heapBits) clearCheckmarkSpan(size, n, total uintptr) { // Only possible on 64-bit system, since minimum size is 8. // Must clear type bit (checkmark bit) of every word. // The type bit is the lower of every two-bit pair. - bitp := h.bitp - for i := uintptr(0); i < n; i += 4 { - *bitp |= bitPointerAll - bitp = subtract1(bitp) + for i := uintptr(0); i < n; i += wordsPerBitmapByte { + *h.bitp |= bitPointerAll + h = h.forward(wordsPerBitmapByte) } } } @@ -958,6 +1011,19 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { // This is a lot of lines of code, but it compiles into relatively few // machine instructions. + outOfPlace := false + if arenaIndex(x+size-1) != arenaIdx(h.arena) || (doubleCheck && fastrand()%2 == 0) { + // This object spans heap arenas, so the bitmap may be + // discontiguous. Unroll it into the object instead + // and then copy it out. + // + // In doubleCheck mode, we randomly do this anyway to + // stress test the bitmap copying path. + outOfPlace = true + h.bitp = (*uint8)(unsafe.Pointer(x)) + h.last = nil + } + var ( // Ptrmask input. p *byte // last ptrmask byte read @@ -996,9 +1062,8 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { } ptrmask = debugPtrmask.data runGCProg(addb(typ.gcdata, 4), nil, ptrmask, 1) - goto Phase4 } - return + goto Phase4 } // Note about sizes: @@ -1106,7 +1171,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { } if nw == 0 { // No pointers! Caller was supposed to check. - println("runtime: invalid type ", *typ.string) + println("runtime: invalid type ", typ.string()) throw("heapBitsSetType: called with non-pointer type") return } @@ -1116,7 +1181,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { nw = 2 } - // Phase 1: Special case for leading byte (shift==0) or half-byte (shift==4). + // Phase 1: Special case for leading byte (shift==0) or half-byte (shift==2). // The leading byte is special because it contains the bits for word 1, // which does not have the scan bit set. // The leading half-byte is special because it's a half a byte, @@ -1146,7 +1211,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { goto Phase3 } *hbitp = uint8(hb) - hbitp = subtract1(hbitp) + hbitp = add1(hbitp) b >>= 4 nb -= 4 @@ -1167,7 +1232,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { // the checkmark. *hbitp &^= uint8((bitPointer | bitScan | (bitPointer << heapBitsShift)) << (2 * heapBitsShift)) *hbitp |= uint8(hb) - hbitp = subtract1(hbitp) + hbitp = add1(hbitp) if w += 2; w >= nw { // We know that there is more data, because we handled 2-word objects above. // This must be at least a 6-word object. If we're out of pointer words, @@ -1197,7 +1262,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { break } *hbitp = uint8(hb) - hbitp = subtract1(hbitp) + hbitp = add1(hbitp) b >>= 4 // Load more bits. b has nb right now. @@ -1245,7 +1310,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { break } *hbitp = uint8(hb) - hbitp = subtract1(hbitp) + hbitp = add1(hbitp) b >>= 4 } @@ -1266,11 +1331,11 @@ Phase3: // The first is hb, the rest are zero. if w <= nw { *hbitp = uint8(hb) - hbitp = subtract1(hbitp) + hbitp = add1(hbitp) hb = 0 // for possible final half-byte below for w += 4; w <= nw; w += 4 { *hbitp = 0 - hbitp = subtract1(hbitp) + hbitp = add1(hbitp) } } @@ -1287,11 +1352,91 @@ Phase3: } Phase4: - // Phase 4: all done, but perhaps double check. + // Phase 4: Copy unrolled bitmap to per-arena bitmaps, if necessary. + if outOfPlace { + // TODO: We could probably make this faster by + // handling [x+dataSize, x+size) specially. + h := heapBitsForAddr(x) + // cnw is the number of heap words, or bit pairs + // remaining (like nw above). + cnw := size / sys.PtrSize + src := (*uint8)(unsafe.Pointer(x)) + // We know the first and last byte of the bitmap are + // not the same, but it's still possible for small + // objects span arenas, so it may share bitmap bytes + // with neighboring objects. + // + // Handle the first byte specially if it's shared. See + // Phase 1 for why this is the only special case we need. + if doubleCheck { + if !(h.shift == 0 || (sys.PtrSize == 8 && h.shift == 2)) { + print("x=", x, " size=", size, " cnw=", h.shift, "\n") + throw("bad start shift") + } + } + if sys.PtrSize == 8 && h.shift == 2 { + *h.bitp = *h.bitp&^((bitPointer|bitScan|(bitPointer|bitScan)<<heapBitsShift)<<(2*heapBitsShift)) | *src + h = h.next().next() + cnw -= 2 + src = addb(src, 1) + } + // We're now byte aligned. Copy out to per-arena + // bitmaps until the last byte (which may again be + // partial). + for cnw >= 4 { + // This loop processes four words at a time, + // so round cnw down accordingly. + hNext, words := h.forwardOrBoundary(cnw / 4 * 4) + + // n is the number of bitmap bytes to copy. + n := words / 4 + memmove(unsafe.Pointer(h.bitp), unsafe.Pointer(src), n) + cnw -= words + h = hNext + src = addb(src, n) + } + if doubleCheck && h.shift != 0 { + print("cnw=", cnw, " h.shift=", h.shift, "\n") + throw("bad shift after block copy") + } + // Handle the last byte if it's shared. + if cnw == 2 { + *h.bitp = *h.bitp&^(bitPointer|bitScan|(bitPointer|bitScan)<<heapBitsShift) | *src + src = addb(src, 1) + h = h.next().next() + } + if doubleCheck { + if uintptr(unsafe.Pointer(src)) > x+size { + throw("copy exceeded object size") + } + if !(cnw == 0 || cnw == 2) { + print("x=", x, " size=", size, " cnw=", cnw, "\n") + throw("bad number of remaining words") + } + // Set up hbitp so doubleCheck code below can check it. + hbitp = h.bitp + } + // Zero the object where we wrote the bitmap. + memclrNoHeapPointers(unsafe.Pointer(x), uintptr(unsafe.Pointer(src))-x) + } + + // Double check the whole bitmap. if doubleCheck { - end := heapBitsForAddr(x + size) + // x+size may not point to the heap, so back up one + // word and then call next(). + end := heapBitsForAddr(x + size - sys.PtrSize).next() + endAI := arenaIdx(end.arena) + if !outOfPlace && (end.bitp == nil || (end.shift == 0 && end.bitp == &mheap_.arenas[endAI.l1()][endAI.l2()].bitmap[0])) { + // The unrolling code above walks hbitp just + // past the bitmap without moving to the next + // arena. Synthesize this for end.bitp. + end.arena-- + endAI = arenaIdx(end.arena) + end.bitp = addb(&mheap_.arenas[endAI.l1()][endAI.l2()].bitmap[0], heapArenaBitmapBytes) + end.last = nil + } if typ.kind&kindGCProg == 0 && (hbitp != end.bitp || (w == nw+2) != (end.shift == 2)) { - println("ended at wrong bitmap byte for", *typ.string, "x", dataSize/typ.size) + println("ended at wrong bitmap byte for", typ.string(), "x", dataSize/typ.size) print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n") print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n") h0 := heapBitsForAddr(x) @@ -1327,15 +1472,15 @@ Phase4: } } if have != want { - println("mismatch writing bits for", *typ.string, "x", dataSize/typ.size) + println("mismatch writing bits for", typ.string(), "x", dataSize/typ.size) print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n") - print("kindGCProg=", typ.kind&kindGCProg != 0, "\n") + print("kindGCProg=", typ.kind&kindGCProg != 0, " outOfPlace=", outOfPlace, "\n") print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n") h0 := heapBitsForAddr(x) print("initial bits h0.bitp=", h0.bitp, " h0.shift=", h0.shift, "\n") print("current bits h.bitp=", h.bitp, " h.shift=", h.shift, " *h.bitp=", hex(*h.bitp), "\n") print("ptrmask=", ptrmask, " p=", p, " endp=", endp, " endnb=", endnb, " pbits=", hex(pbits), " b=", hex(b), " nb=", nb, "\n") - println("at word", i, "offset", i*sys.PtrSize, "have", have, "want", want) + println("at word", i, "offset", i*sys.PtrSize, "have", hex(have), "want", hex(want)) if typ.kind&kindGCProg != 0 { println("GC program:") dumpGCProg(addb(typ.gcdata, 4)) @@ -1436,9 +1581,9 @@ func heapBitsSetTypeGCProg(h heapBits, progSize, elemSize, dataSize, allocSize u // so that scanobject can stop early in the final element. totalBits = (elemSize*(count-1) + progSize) / sys.PtrSize } - endProg := unsafe.Pointer(subtractb(h.bitp, (totalBits+3)/4)) - endAlloc := unsafe.Pointer(subtractb(h.bitp, allocSize/heapBitmapScale)) - memclrNoHeapPointers(add(endAlloc, 1), uintptr(endProg)-uintptr(endAlloc)) + endProg := unsafe.Pointer(addb(h.bitp, (totalBits+3)/4)) + endAlloc := unsafe.Pointer(addb(h.bitp, allocSize/sys.PtrSize/wordsPerBitmapByte)) + memclrNoHeapPointers(endProg, uintptr(endAlloc)-uintptr(endProg)) } // progToPointerMask returns the 1-bit pointer mask output by the GC program prog. @@ -1497,11 +1642,11 @@ Run: } else { v := bits&bitPointerAll | bitScanAll *dst = uint8(v) - dst = subtract1(dst) + dst = add1(dst) bits >>= 4 v = bits&bitPointerAll | bitScanAll *dst = uint8(v) - dst = subtract1(dst) + dst = add1(dst) bits >>= 4 } } @@ -1535,11 +1680,11 @@ Run: } else { v := bits&0xf | bitScanAll *dst = uint8(v) - dst = subtract1(dst) + dst = add1(dst) bits >>= 4 v = bits&0xf | bitScanAll *dst = uint8(v) - dst = subtract1(dst) + dst = add1(dst) bits >>= 4 } } @@ -1599,11 +1744,11 @@ Run: npattern += 8 } } else { - src = add1(src) + src = subtract1(src) for npattern < n { pattern <<= 4 pattern |= uintptr(*src) & 0xf - src = add1(src) + src = subtract1(src) npattern += 4 } } @@ -1665,7 +1810,7 @@ Run: } else { for nbits >= 4 { *dst = uint8(bits&0xf | bitScanAll) - dst = subtract1(dst) + dst = add1(dst) bits >>= 4 nbits -= 4 } @@ -1710,10 +1855,10 @@ Run: } } else { // Leading src fragment. - src = addb(src, (off+3)/4) + src = subtractb(src, (off+3)/4) if frag := off & 3; frag != 0 { bits |= (uintptr(*src) & 0xf) >> (4 - frag) << nbits - src = subtract1(src) + src = add1(src) nbits += frag c -= frag } @@ -1721,9 +1866,9 @@ Run: // The bits are rotating through the bit buffer. for i := c / 4; i > 0; i-- { bits |= (uintptr(*src) & 0xf) << nbits - src = subtract1(src) + src = add1(src) *dst = uint8(bits&0xf | bitScanAll) - dst = subtract1(dst) + dst = add1(dst) bits >>= 4 } // Final src fragment. @@ -1745,12 +1890,12 @@ Run: bits >>= 8 } } else { - totalBits = (uintptr(unsafe.Pointer(dstStart))-uintptr(unsafe.Pointer(dst)))*4 + nbits + totalBits = (uintptr(unsafe.Pointer(dst))-uintptr(unsafe.Pointer(dstStart)))*4 + nbits nbits += -nbits & 3 for ; nbits > 0; nbits -= 4 { v := bits&0xf | bitScanAll *dst = uint8(v) - dst = subtract1(dst) + dst = add1(dst) bits >>= 4 } } @@ -1839,12 +1984,11 @@ func getgcmask(ep interface{}) (mask []byte) { } // heap - var n uintptr - var base uintptr - if mlookup(uintptr(p), &base, &n, nil) != 0 { + if base, s, _ := findObject(uintptr(p), 0, 0, false); base != 0 { + hbits := heapBitsForAddr(base) + n := s.elemsize mask = make([]byte, n/sys.PtrSize) for i := uintptr(0); i < n; i += sys.PtrSize { - hbits := heapBitsForAddr(base + i) if hbits.isPointer() { mask[i/sys.PtrSize] = 1 } @@ -1852,6 +1996,7 @@ func getgcmask(ep interface{}) (mask []byte) { mask = mask[:i/sys.PtrSize] break } + hbits = hbits.next() } return } diff --git a/libgo/go/runtime/mcache.go b/libgo/go/runtime/mcache.go index 766cfd1..3dacf96 100644 --- a/libgo/go/runtime/mcache.go +++ b/libgo/go/runtime/mcache.go @@ -37,7 +37,6 @@ type mcache struct { alloc [numSpanClasses]*mspan // spans to allocate from, indexed by spanClass // Local allocator stats, flushed during GC. - local_nlookup uintptr // number of pointer lookups local_largefree uintptr // bytes freed for large objects (>maxsmallsize) local_nlargefree uintptr // number of frees for large objects (>maxsmallsize) local_nsmallfree [_NumSizeClasses]uintptr // number of frees for small objects (<=maxsmallsize) diff --git a/libgo/go/runtime/mcentral.go b/libgo/go/runtime/mcentral.go index 150f4fd..50a4791 100644 --- a/libgo/go/runtime/mcentral.go +++ b/libgo/go/runtime/mcentral.go @@ -246,6 +246,6 @@ func (c *mcentral) grow() *mspan { p := s.base() s.limit = p + size*n - heapBitsForSpan(s.base()).initSpan(s) + heapBitsForAddr(s.base()).initSpan(s) return s } diff --git a/libgo/go/runtime/mem_gccgo.go b/libgo/go/runtime/mem_gccgo.go index a087945..44f4648 100644 --- a/libgo/go/runtime/mem_gccgo.go +++ b/libgo/go/runtime/mem_gccgo.go @@ -21,9 +21,6 @@ func sysMmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uintptr) //extern munmap func munmap(addr unsafe.Pointer, length uintptr) int32 -//extern mincore -func mincore(addr unsafe.Pointer, n uintptr, dst *byte) int32 - //extern madvise func madvise(addr unsafe.Pointer, n uintptr, flags int32) int32 @@ -49,54 +46,6 @@ func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uintptr) (u return p, 0 } -// NOTE: vec must be just 1 byte long here. -// Mincore returns ENOMEM if any of the pages are unmapped, -// but we want to know that all of the pages are unmapped. -// To make these the same, we can only ask about one page -// at a time. See golang.org/issue/7476. -var addrspace_vec [1]byte - -func addrspace_free(v unsafe.Pointer, n uintptr) bool { - for off := uintptr(0); off < n; off += physPageSize { - // Use a length of 1 byte, which the kernel will round - // up to one physical page regardless of the true - // physical page size. - errval := 0 - if mincore(unsafe.Pointer(uintptr(v)+off), 1, &addrspace_vec[0]) < 0 { - errval = errno() - } - if errval == _ENOSYS { - // mincore is not available on this system. - // Assume the address is available. - return true - } - if errval == _EINVAL { - // Address is not a multiple of the physical - // page size. Shouldn't happen, but just ignore it. - continue - } - // ENOMEM means unmapped, which is what we want. - // Anything else we assume means the pages are mapped. - if errval != _ENOMEM { - return false - } - } - return true -} - -func mmap_fixed(v unsafe.Pointer, n uintptr, prot, flags, fd int32, offset uintptr) (unsafe.Pointer, int) { - p, err := mmap(v, n, prot, flags, fd, offset) - // On some systems, mmap ignores v without - // MAP_FIXED, so retry if the address space is free. - if p != v && addrspace_free(v, n) { - if err == 0 { - munmap(p, n) - } - p, err = mmap(v, n, prot, flags|_MAP_FIXED, fd, offset) - } - return p, err -} - // Don't split the stack as this method may be invoked without a valid G, which // prevents us from allocating more stack. //go:nosplit @@ -227,62 +176,17 @@ func sysFault(v unsafe.Pointer, n uintptr) { mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE|_MAP_FIXED, mmapFD, 0) } -func sysReserve(v unsafe.Pointer, n uintptr, reserved *bool) unsafe.Pointer { - // On 64-bit, people with ulimit -v set complain if we reserve too - // much address space. Instead, assume that the reservation is okay - // if we can reserve at least 64K and check the assumption in SysMap. - // Only user-mode Linux (UML) rejects these requests. - if sys.PtrSize == 8 && uint64(n) > 1<<32 { - p, err := mmap_fixed(v, 64<<10, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE, mmapFD, 0) - if p != v || err != 0 { - if err == 0 { - munmap(p, 64<<10) - } - return nil - } - munmap(p, 64<<10) - *reserved = false - return v - } - +func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { p, err := mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE, mmapFD, 0) if err != 0 { return nil } - *reserved = true return p } -func sysMap(v unsafe.Pointer, n uintptr, reserved bool, sysStat *uint64) { +func sysMap(v unsafe.Pointer, n uintptr, sysStat *uint64) { mSysStatInc(sysStat, n) - // On 64-bit, we don't actually have v reserved, so tread carefully. - if !reserved { - flags := int32(_MAP_ANON | _MAP_PRIVATE) - if GOOS == "dragonfly" { - // TODO(jsing): For some reason DragonFly seems to return - // memory at a different address than we requested, even when - // there should be no reason for it to do so. This can be - // avoided by using MAP_FIXED, but I'm not sure we should need - // to do this - we do not on other platforms. - flags |= _MAP_FIXED - } - p, err := mmap_fixed(v, n, _PROT_READ|_PROT_WRITE, flags, mmapFD, 0) - if err == _ENOMEM { - throw("runtime: out of memory") - } - if p != v || err != 0 { - print("runtime: address space conflict: map(", v, ") = ", p, " (err ", err, ")\n") - throw("runtime: address space conflict") - } - return - } - - if GOOS == "aix" { - // AIX does not allow mapping a range that is already mapped. - // So always unmap first even if it is already unmapped. - munmap(v, n) - } p, err := mmap(v, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, mmapFD, 0) if err == _ENOMEM { throw("runtime: out of memory") diff --git a/libgo/go/runtime/memmove_test.go b/libgo/go/runtime/memmove_test.go index 62de604..b490cd8 100644 --- a/libgo/go/runtime/memmove_test.go +++ b/libgo/go/runtime/memmove_test.go @@ -450,6 +450,13 @@ func BenchmarkCopyFat512(b *testing.B) { _ = y } } +func BenchmarkCopyFat520(b *testing.B) { + var x [520 / 4]uint32 + for i := 0; i < b.N; i++ { + y := x + _ = y + } +} func BenchmarkCopyFat1024(b *testing.B) { var x [1024 / 4]uint32 for i := 0; i < b.N; i++ { diff --git a/libgo/go/runtime/mfinal.go b/libgo/go/runtime/mfinal.go index 19573d8..1a7792c 100644 --- a/libgo/go/runtime/mfinal.go +++ b/libgo/go/runtime/mfinal.go @@ -142,7 +142,7 @@ func runfinq() { if fb == nil { fing = gp fingwait = true - goparkunlock(&finlock, "finalizer wait", traceEvGoBlock, 1) + goparkunlock(&finlock, waitReasonFinalizerWait, traceEvGoBlock, 1) continue } unlock(&finlock) @@ -233,8 +233,8 @@ func runfinq() { // is not guaranteed to run, because there is no ordering that // respects the dependencies. // -// The finalizer for obj is scheduled to run at some arbitrary time after -// obj becomes unreachable. +// The finalizer is scheduled to run at some arbitrary time after the +// program can no longer reach the object to which obj points. // There is no guarantee that finalizers will run before a program exits, // so typically they are useful only for releasing non-memory resources // associated with an object during a long-running program. @@ -284,7 +284,7 @@ func SetFinalizer(obj interface{}, finalizer interface{}) { throw("runtime.SetFinalizer: first argument is nil") } if etyp.kind&kindMask != kindPtr { - throw("runtime.SetFinalizer: first argument is " + *etyp.string + ", not pointer") + throw("runtime.SetFinalizer: first argument is " + etyp.string() + ", not pointer") } ot := (*ptrtype)(unsafe.Pointer(etyp)) if ot.elem == nil { @@ -292,9 +292,9 @@ func SetFinalizer(obj interface{}, finalizer interface{}) { } // find the containing object - _, base, _ := findObject(e.data) + base, _, _ := findObject(uintptr(e.data), 0, 0, false) - if base == nil { + if base == 0 { // 0-length objects are okay. if e.data == unsafe.Pointer(&zerobase) { return @@ -314,7 +314,7 @@ func SetFinalizer(obj interface{}, finalizer interface{}) { return } - if e.data != base { + if uintptr(e.data) != base { // As an implementation detail we allow to set finalizers for an inner byte // of an object if it could come from tiny alloc (see mallocgc for details). if ot.elem == nil || ot.elem.kind&kindNoPointers == 0 || ot.elem.size >= maxTinySize { @@ -333,14 +333,14 @@ func SetFinalizer(obj interface{}, finalizer interface{}) { } if ftyp.kind&kindMask != kindFunc { - throw("runtime.SetFinalizer: second argument is " + *ftyp.string + ", not a function") + throw("runtime.SetFinalizer: second argument is " + ftyp.string() + ", not a function") } ft := (*functype)(unsafe.Pointer(ftyp)) if ft.dotdotdot { - throw("runtime.SetFinalizer: cannot pass " + *etyp.string + " to finalizer " + *ftyp.string + " because dotdotdot") + throw("runtime.SetFinalizer: cannot pass " + etyp.string() + " to finalizer " + ftyp.string() + " because dotdotdot") } if len(ft.in) != 1 { - throw("runtime.SetFinalizer: cannot pass " + *etyp.string + " to finalizer " + *ftyp.string) + throw("runtime.SetFinalizer: cannot pass " + etyp.string() + " to finalizer " + ftyp.string()) } fint := ft.in[0] switch { @@ -363,7 +363,7 @@ func SetFinalizer(obj interface{}, finalizer interface{}) { goto okarg } } - throw("runtime.SetFinalizer: cannot pass " + *etyp.string + " to finalizer " + *ftyp.string) + throw("runtime.SetFinalizer: cannot pass " + etyp.string() + " to finalizer " + ftyp.string()) okarg: // make sure we have a finalizer goroutine createfing() @@ -379,46 +379,6 @@ okarg: }) } -// Look up pointer v in heap. Return the span containing the object, -// the start of the object, and the size of the object. If the object -// does not exist, return nil, nil, 0. -func findObject(v unsafe.Pointer) (s *mspan, x unsafe.Pointer, n uintptr) { - c := gomcache() - c.local_nlookup++ - if sys.PtrSize == 4 && c.local_nlookup >= 1<<30 { - // purge cache stats to prevent overflow - lock(&mheap_.lock) - purgecachedstats(c) - unlock(&mheap_.lock) - } - - // find span - arena_start := mheap_.arena_start - arena_used := mheap_.arena_used - if uintptr(v) < arena_start || uintptr(v) >= arena_used { - return - } - p := uintptr(v) >> pageShift - q := p - arena_start>>pageShift - s = mheap_.spans[q] - if s == nil { - return - } - x = unsafe.Pointer(s.base()) - - if uintptr(v) < uintptr(x) || uintptr(v) >= uintptr(unsafe.Pointer(s.limit)) || s.state != mSpanInUse { - s = nil - x = nil - return - } - - n = s.elemsize - if s.spanclass.sizeclass() != 0 { - x = add(x, (uintptr(v)-uintptr(x))/n*n) - } - return -} - // Mark KeepAlive as noinline so that it is easily detectable as an intrinsic. //go:noinline diff --git a/libgo/go/runtime/mfixalloc.go b/libgo/go/runtime/mfixalloc.go index 7496671..1febe78 100644 --- a/libgo/go/runtime/mfixalloc.go +++ b/libgo/go/runtime/mfixalloc.go @@ -11,7 +11,7 @@ package runtime import "unsafe" // FixAlloc is a simple free-list allocator for fixed size objects. -// Malloc uses a FixAlloc wrapped around sysAlloc to manages its +// Malloc uses a FixAlloc wrapped around sysAlloc to manage its // MCache and MSpan objects. // // Memory returned by fixalloc.alloc is zeroed by default, but the diff --git a/libgo/go/runtime/mgc.go b/libgo/go/runtime/mgc.go index 626f088..4ef982d 100644 --- a/libgo/go/runtime/mgc.go +++ b/libgo/go/runtime/mgc.go @@ -232,21 +232,10 @@ func setGCPercent(in int32) (out int32) { gcSetTriggerRatio(memstats.triggerRatio) unlock(&mheap_.lock) - // If we just disabled GC, wait for any concurrent GC to + // If we just disabled GC, wait for any concurrent GC mark to // finish so we always return with no GC running. if in < 0 { - // Disable phase transitions. - lock(&work.sweepWaiters.lock) - if gcphase == _GCmark { - // GC is active. Wait until we reach sweeping. - gp := getg() - gp.schedlink = work.sweepWaiters.head - work.sweepWaiters.head.set(gp) - goparkunlock(&work.sweepWaiters.lock, "wait for GC cycle", traceEvGoBlock, 1) - } else { - // GC isn't active. - unlock(&work.sweepWaiters.lock) - } + gcWaitOnMark(atomic.Load(&work.cycles)) } return out @@ -1091,21 +1080,10 @@ func GC() { // GC may move ahead on its own. For example, when we block // until mark termination N, we may wake up in cycle N+2. - gp := getg() - - // Prevent the GC phase or cycle count from changing. - lock(&work.sweepWaiters.lock) + // Wait until the current sweep termination, mark, and mark + // termination complete. n := atomic.Load(&work.cycles) - if gcphase == _GCmark { - // Wait until sweep termination, mark, and mark - // termination of cycle N complete. - gp.schedlink = work.sweepWaiters.head - work.sweepWaiters.head.set(gp) - goparkunlock(&work.sweepWaiters.lock, "wait for GC cycle", traceEvGoBlock, 1) - } else { - // We're in sweep N already. - unlock(&work.sweepWaiters.lock) - } + gcWaitOnMark(n) // We're now in sweep N or later. Trigger GC cycle N+1, which // will first finish sweep N if necessary and then enter sweep @@ -1113,14 +1091,7 @@ func GC() { gcStart(gcBackgroundMode, gcTrigger{kind: gcTriggerCycle, n: n + 1}) // Wait for mark termination N+1 to complete. - lock(&work.sweepWaiters.lock) - if gcphase == _GCmark && atomic.Load(&work.cycles) == n+1 { - gp.schedlink = work.sweepWaiters.head - work.sweepWaiters.head.set(gp) - goparkunlock(&work.sweepWaiters.lock, "wait for GC cycle", traceEvGoBlock, 1) - } else { - unlock(&work.sweepWaiters.lock) - } + gcWaitOnMark(n + 1) // Finish sweep N+1 before returning. We do this both to // complete the cycle and because runtime.GC() is often used @@ -1157,6 +1128,32 @@ func GC() { releasem(mp) } +// gcWaitOnMark blocks until GC finishes the Nth mark phase. If GC has +// already completed this mark phase, it returns immediately. +func gcWaitOnMark(n uint32) { + for { + // Disable phase transitions. + lock(&work.sweepWaiters.lock) + nMarks := atomic.Load(&work.cycles) + if gcphase != _GCmark { + // We've already completed this cycle's mark. + nMarks++ + } + if nMarks > n { + // We're done. + unlock(&work.sweepWaiters.lock) + return + } + + // Wait until sweep termination, mark, and mark + // termination of cycle N complete. + gp := getg() + gp.schedlink = work.sweepWaiters.head + work.sweepWaiters.head.set(gp) + goparkunlock(&work.sweepWaiters.lock, waitReasonWaitForGCCycle, traceEvGoBlock, 1) + } +} + // gcMode indicates how concurrent a GC cycle should be. type gcMode int @@ -1531,7 +1528,7 @@ func gcMarkTermination(nextTriggerRatio float64) { _g_.m.traceback = 2 gp := _g_.m.curg casgstatus(gp, _Grunning, _Gwaiting) - gp.waitreason = "garbage collection" + gp.waitreason = waitReasonGarbageCollection // Run gc on the g0 stack. We do this so that the g stack // we're currently running on will no longer change. Cuts @@ -1800,7 +1797,7 @@ func gcBgMarkWorker(_p_ *p) { } } return true - }, unsafe.Pointer(park), "GC worker (idle)", traceEvGoBlock, 0) + }, unsafe.Pointer(park), waitReasonGCWorkerIdle, traceEvGoBlock, 0) // Loop until the P dies and disassociates this // worker (the P may later be reused, in which case diff --git a/libgo/go/runtime/mgclarge.go b/libgo/go/runtime/mgclarge.go index fe437bf..e7fa831 100644 --- a/libgo/go/runtime/mgclarge.go +++ b/libgo/go/runtime/mgclarge.go @@ -9,7 +9,7 @@ // Large spans are the subject of this file. Spans consisting of less than // _MaxMHeapLists are held in lists of like sized spans. Larger spans // are held in a treap. See https://en.wikipedia.org/wiki/Treap or -// http://faculty.washington.edu/aragon/pubs/rst89.pdf for an overview. +// https://faculty.washington.edu/aragon/pubs/rst89.pdf for an overview. // sema.go also holds an implementation of a treap. // // Each treapNode holds a single span. The treap is sorted by page size @@ -43,7 +43,7 @@ type treapNode struct { parent *treapNode // direct parent of this node, nil if root npagesKey uintptr // number of pages in spanKey, used as primary sort key spanKey *mspan // span of size npagesKey, used as secondary sort key - priority uint32 // random number used by treap algorithm keep tree probablistically balanced + priority uint32 // random number used by treap algorithm to keep tree probabilistically balanced } func (t *treapNode) init() { @@ -137,7 +137,7 @@ func (root *mTreap) insert(span *mspan) { // npagesKeys, it is kept balanced on average by maintaining a heap ordering // on the priority: s.priority <= both s.right.priority and s.right.priority. // https://en.wikipedia.org/wiki/Treap - // http://faculty.washington.edu/aragon/pubs/rst89.pdf + // https://faculty.washington.edu/aragon/pubs/rst89.pdf t := (*treapNode)(mheap_.treapalloc.alloc()) t.init() diff --git a/libgo/go/runtime/mgcmark.go b/libgo/go/runtime/mgcmark.go index 7297fcb..88cae41 100644 --- a/libgo/go/runtime/mgcmark.go +++ b/libgo/go/runtime/mgcmark.go @@ -232,7 +232,7 @@ func markroot(gcw *gcWork, i uint32) { selfScan := gp == userG && readgstatus(userG) == _Grunning if selfScan { casgstatus(userG, _Grunning, _Gwaiting) - userG.waitreason = "garbage collection scan" + userG.waitreason = waitReasonGarbageCollectionScan } // TODO: scang blocks until gp's stack has @@ -467,7 +467,7 @@ func gcAssistAlloc1(gp *g, scanWork int64) { // store that clears it but an atomic check in every malloc // would be a performance hit. // Instead we recheck it here on the non-preemptable system - // stack to determine if we should preform an assist. + // stack to determine if we should perform an assist. // GC is done, so ignore any remaining debt. gp.gcAssistBytes = 0 @@ -486,7 +486,7 @@ func gcAssistAlloc1(gp *g, scanWork int64) { // gcDrainN requires the caller to be preemptible. casgstatus(gp, _Grunning, _Gwaiting) - gp.waitreason = "GC assist marking" + gp.waitreason = waitReasonGCAssistMarking // drain own cached work first in the hopes that it // will be more cache friendly. @@ -585,7 +585,7 @@ func gcParkAssist() bool { return false } // Park. - goparkunlock(&work.assistQueue.lock, "GC assist wait", traceEvGoBlockGC, 2) + goparkunlock(&work.assistQueue.lock, waitReasonGCAssistWait, traceEvGoBlockGC, 2) return true } @@ -934,9 +934,6 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork) { b := b0 n := n0 - arena_start := mheap_.arena_start - arena_used := mheap_.arena_used - for i := uintptr(0); i < n; { // Find bits for the next word. bits := uint32(*addb(ptrmask, i/(sys.PtrSize*8))) @@ -948,9 +945,9 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork) { if bits&1 != 0 { // Same work as in scanobject; see comments there. obj := *(*uintptr)(unsafe.Pointer(b + i)) - if obj != 0 && arena_start <= obj && obj < arena_used { - if obj, hbits, span, objIndex := heapBitsForObject(obj, b, i, false); obj != 0 { - greyobject(obj, b, i, hbits, span, gcw, objIndex, false) + if obj != 0 { + if obj, span, objIndex := findObject(obj, b, i, false); obj != 0 { + greyobject(obj, b, i, span, gcw, objIndex, false) } } } @@ -967,18 +964,6 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork) { // //go:nowritebarrier func scanobject(b uintptr, gcw *gcWork) { - // Note that arena_used may change concurrently during - // scanobject and hence scanobject may encounter a pointer to - // a newly allocated heap object that is *not* in - // [start,used). It will not mark this object; however, we - // know that it was just installed by a mutator, which means - // that mutator will execute a write barrier and take care of - // marking it. This is even more pronounced on relaxed memory - // architectures since we access arena_used without barriers - // or synchronization, but the same logic applies. - arena_start := mheap_.arena_start - arena_used := mheap_.arena_used - // Find the bits for b and the size of the object at b. // // b is either the beginning of an object, in which case this @@ -1052,11 +1037,19 @@ func scanobject(b uintptr, gcw *gcWork) { obj := *(*uintptr)(unsafe.Pointer(b + i)) // At this point we have extracted the next potential pointer. - // Check if it points into heap and not back at the current object. - if obj != 0 && arena_start <= obj && obj < arena_used && obj-b >= n { - // Mark the object. - if obj, hbits, span, objIndex := heapBitsForObject(obj, b, i, false); obj != 0 { - greyobject(obj, b, i, hbits, span, gcw, objIndex, false) + // Quickly filter out nil and pointers back to the current object. + if obj != 0 && obj-b >= n { + // Test if obj points into the Go heap and, if so, + // mark the object. + // + // Note that it's possible for findObject to + // fail if obj points to a just-allocated heap + // object because of a race with growing the + // heap. In this case, we know the object was + // just allocated and hence will be marked by + // allocation itself. + if obj, span, objIndex := findObject(obj, b, i, false); obj != 0 { + greyobject(obj, b, i, span, gcw, objIndex, false) } } } @@ -1071,16 +1064,11 @@ func scanobject(b uintptr, gcw *gcWork) { // scanblock, but we scan the stack conservatively, so there is no // bitmask of pointers. func scanstackblock(b, n uintptr, gcw *gcWork) { - arena_start := mheap_.arena_start - arena_used := mheap_.arena_used - for i := uintptr(0); i < n; i += sys.PtrSize { // Same work as in scanobject; see comments there. obj := *(*uintptr)(unsafe.Pointer(b + i)) - if obj != 0 && arena_start <= obj && obj < arena_used { - if obj, hbits, span, objIndex := heapBitsForObject(obj, b, i, true); obj != 0 { - greyobject(obj, b, i, hbits, span, gcw, objIndex, true) - } + if obj, span, objIndex := findObject(obj, b, i, true); obj != 0 { + greyobject(obj, b, i, span, gcw, objIndex, true) } } } @@ -1090,11 +1078,9 @@ func scanstackblock(b, n uintptr, gcw *gcWork) { // Preemption must be disabled. //go:nowritebarrier func shade(b uintptr) { - // shade can be called to shade a pointer found on the stack, - // so pass forStack as true to heapBitsForObject and greyobject. - if obj, hbits, span, objIndex := heapBitsForObject(b, 0, 0, true); obj != 0 { + if obj, span, objIndex := findObject(b, 0, 0, true); obj != 0 { gcw := &getg().m.p.ptr().gcw - greyobject(obj, 0, 0, hbits, span, gcw, objIndex, true) + greyobject(obj, 0, 0, span, gcw, objIndex, true) if gcphase == _GCmarktermination || gcBlackenPromptly { // Ps aren't allowed to cache work during mark // termination. @@ -1110,7 +1096,7 @@ func shade(b uintptr) { // See also wbBufFlush1, which partially duplicates this logic. // //go:nowritebarrierrec -func greyobject(obj, base, off uintptr, hbits heapBits, span *mspan, gcw *gcWork, objIndex uintptr, forStack bool) { +func greyobject(obj, base, off uintptr, span *mspan, gcw *gcWork, objIndex uintptr, forStack bool) { // obj should be start of allocation, and so must be at least pointer-aligned. if obj&(sys.PtrSize-1) != 0 { throw("greyobject: obj not pointer-aligned") @@ -1139,6 +1125,7 @@ func greyobject(obj, base, off uintptr, hbits heapBits, span *mspan, gcw *gcWork getg().m.traceback = 2 throw("checkmark found unmarked object") } + hbits := heapBitsForAddr(obj) if hbits.isCheckmarked(span.elemsize) { return } @@ -1190,15 +1177,8 @@ func greyobject(obj, base, off uintptr, hbits heapBits, span *mspan, gcw *gcWork // gcDumpObject dumps the contents of obj for debugging and marks the // field at byte offset off in obj. func gcDumpObject(label string, obj, off uintptr) { - if obj < mheap_.arena_start || obj >= mheap_.arena_used { - print(label, "=", hex(obj), " is not in the Go heap\n") - return - } - k := obj >> _PageShift - x := k - x -= mheap_.arena_start >> _PageShift - s := mheap_.spans[x] - print(label, "=", hex(obj), " k=", hex(k)) + s := spanOf(obj) + print(label, "=", hex(obj)) if s == nil { print(" s=nil\n") return @@ -1272,9 +1252,9 @@ func gcMarkTinyAllocs() { if c == nil || c.tiny == 0 { continue } - _, hbits, span, objIndex := heapBitsForObject(c.tiny, 0, 0, false) + _, span, objIndex := findObject(c.tiny, 0, 0, false) gcw := &p.gcw - greyobject(c.tiny, 0, 0, hbits, span, gcw, objIndex, false) + greyobject(c.tiny, 0, 0, span, gcw, objIndex, false) if gcBlackenPromptly { gcw.dispose() } @@ -1309,7 +1289,7 @@ func initCheckmarks() { useCheckmark = true for _, s := range mheap_.allspans { if s.state == _MSpanInUse { - heapBitsForSpan(s.base()).initCheckmarkSpan(s.layout()) + heapBitsForAddr(s.base()).initCheckmarkSpan(s.layout()) } } } @@ -1318,7 +1298,7 @@ func clearCheckmarks() { useCheckmark = false for _, s := range mheap_.allspans { if s.state == _MSpanInUse { - heapBitsForSpan(s.base()).clearCheckmarkSpan(s.layout()) + heapBitsForAddr(s.base()).clearCheckmarkSpan(s.layout()) } } } diff --git a/libgo/go/runtime/mgcsweep.go b/libgo/go/runtime/mgcsweep.go index d6be349..39dd54e 100644 --- a/libgo/go/runtime/mgcsweep.go +++ b/libgo/go/runtime/mgcsweep.go @@ -51,7 +51,7 @@ func bgsweep(c chan int) { lock(&sweep.lock) sweep.parked = true c <- 1 - goparkunlock(&sweep.lock, "GC sweep wait", traceEvGoBlock, 1) + goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1) for { for gosweepone() != ^uintptr(0) { @@ -70,7 +70,7 @@ func bgsweep(c chan int) { continue } sweep.parked = true - goparkunlock(&sweep.lock, "GC sweep wait", traceEvGoBlock, 1) + goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1) } } diff --git a/libgo/go/runtime/mgcwork.go b/libgo/go/runtime/mgcwork.go index c6634fc..99771e2 100644 --- a/libgo/go/runtime/mgcwork.go +++ b/libgo/go/runtime/mgcwork.go @@ -400,6 +400,7 @@ func getempty() *workbuf { for i := uintptr(0); i+_WorkbufSize <= workbufAlloc; i += _WorkbufSize { newb := (*workbuf)(unsafe.Pointer(s.base() + i)) newb.nobj = 0 + lfnodeValidate(&newb.node) if i == 0 { b = newb } else { diff --git a/libgo/go/runtime/mheap.go b/libgo/go/runtime/mheap.go index d971bfe..65622f4 100644 --- a/libgo/go/runtime/mheap.go +++ b/libgo/go/runtime/mheap.go @@ -50,23 +50,6 @@ type mheap struct { // access (since that may free the backing store). allspans []*mspan // all spans out there - // spans is a lookup table to map virtual address page IDs to *mspan. - // For allocated spans, their pages map to the span itself. - // For free spans, only the lowest and highest pages map to the span itself. - // Internal pages map to an arbitrary span. - // For pages that have never been allocated, spans entries are nil. - // - // Modifications are protected by mheap.lock. Reads can be - // performed without locking, but ONLY from indexes that are - // known to contain in-use or stack spans. This means there - // must not be a safe-point between establishing that an - // address is live and looking it up in the spans array. - // - // This is backed by a reserved region of the address space so - // it can grow without moving. The memory up to len(spans) is - // mapped. cap(spans) indicates the total reserved memory. - spans []*mspan - // sweepSpans contains two mspan stacks: one of swept in-use // spans, and one of unswept in-use spans. These two trade // roles on each GC cycle. Since the sweepgen increases by 2 @@ -78,7 +61,7 @@ type mheap struct { // on the swept stack. sweepSpans [2]gcSweepBuf - _ uint32 // align uint64 fields on 32-bit for atomics + //_ uint32 // align uint64 fields on 32-bit for atomics // Proportional sweep // @@ -113,36 +96,44 @@ type mheap struct { nlargefree uint64 // number of frees for large objects (>maxsmallsize) nsmallfree [_NumSizeClasses]uint64 // number of frees for small objects (<=maxsmallsize) - // range of addresses we might see in the heap - bitmap uintptr // Points to one byte past the end of the bitmap - bitmap_mapped uintptr - - // The arena_* fields indicate the addresses of the Go heap. + // arenas is the heap arena map. It points to the metadata for + // the heap for every arena frame of the entire usable virtual + // address space. + // + // Use arenaIndex to compute indexes into this array. // - // The maximum range of the Go heap is - // [arena_start, arena_start+_MaxMem+1). + // For regions of the address space that are not backed by the + // Go heap, the arena map contains nil. // - // The range of the current Go heap is - // [arena_start, arena_used). Parts of this range may not be - // mapped, but the metadata structures are always mapped for - // the full range. - arena_start uintptr - arena_used uintptr // Set with setArenaUsed. - - // The heap is grown using a linear allocator that allocates - // from the block [arena_alloc, arena_end). arena_alloc is - // often, but *not always* equal to arena_used. - arena_alloc uintptr - arena_end uintptr - - // arena_reserved indicates that the memory [arena_alloc, - // arena_end) is reserved (e.g., mapped PROT_NONE). If this is - // false, we have to be careful not to clobber existing - // mappings here. If this is true, then we own the mapping - // here and *must* clobber it to use it. - arena_reserved bool - - _ uint32 // ensure 64-bit alignment + // Modifications are protected by mheap_.lock. Reads can be + // performed without locking; however, a given entry can + // transition from nil to non-nil at any time when the lock + // isn't held. (Entries never transitions back to nil.) + // + // In general, this is a two-level mapping consisting of an L1 + // map and possibly many L2 maps. This saves space when there + // are a huge number of arena frames. However, on many + // platforms (even 64-bit), arenaL1Bits is 0, making this + // effectively a single-level map. In this case, arenas[0] + // will never be nil. + arenas [1 << arenaL1Bits]*[1 << arenaL2Bits]*heapArena + + // heapArenaAlloc is pre-reserved space for allocating heapArena + // objects. This is only used on 32-bit, where we pre-reserve + // this space to avoid interleaving it with the heap itself. + heapArenaAlloc linearAlloc + + // arenaHints is a list of addresses at which to attempt to + // add more heap arenas. This is initially populated with a + // set of general hint addresses, and grown with the bounds of + // actual heap arena ranges. + arenaHints *arenaHint + + // arena is a pre-reserved space for allocating heap arenas + // (the actual arenas). This is only used on 32-bit. + arena linearAlloc + + //_ uint32 // ensure 64-bit alignment of central // central free lists for small size classes. // the padding makes sure that the MCentrals are @@ -160,12 +151,51 @@ type mheap struct { specialfinalizeralloc fixalloc // allocator for specialfinalizer* specialprofilealloc fixalloc // allocator for specialprofile* speciallock mutex // lock for special record allocators. + arenaHintAlloc fixalloc // allocator for arenaHints unused *specialfinalizer // never set, just here to force the specialfinalizer type into DWARF } var mheap_ mheap +// A heapArena stores metadata for a heap arena. heapArenas are stored +// outside of the Go heap and accessed via the mheap_.arenas index. +// +// This gets allocated directly from the OS, so ideally it should be a +// multiple of the system page size. For example, avoid adding small +// fields. +// +//go:notinheap +type heapArena struct { + // bitmap stores the pointer/scalar bitmap for the words in + // this arena. See mbitmap.go for a description. Use the + // heapBits type to access this. + bitmap [heapArenaBitmapBytes]byte + + // spans maps from virtual address page ID within this arena to *mspan. + // For allocated spans, their pages map to the span itself. + // For free spans, only the lowest and highest pages map to the span itself. + // Internal pages map to an arbitrary span. + // For pages that have never been allocated, spans entries are nil. + // + // Modifications are protected by mheap.lock. Reads can be + // performed without locking, but ONLY from indexes that are + // known to contain in-use or stack spans. This means there + // must not be a safe-point between establishing that an + // address is live and looking it up in the spans array. + spans [pagesPerArena]*mspan +} + +// arenaHint is a hint for where to grow the heap arenas. See +// mheap_.arenaHints. +// +//go:notinheap +type arenaHint struct { + addr uintptr + down bool + next *arenaHint +} + // An MSpan is a run of pages. // // When a MSpan is in the heap free list, state == MSpanFree @@ -384,21 +414,55 @@ func (sc spanClass) noscan() bool { return sc&1 != 0 } +// arenaIndex returns the index into mheap_.arenas of the arena +// containing metadata for p. This index combines of an index into the +// L1 map and an index into the L2 map and should be used as +// mheap_.arenas[ai.l1()][ai.l2()]. +// +// If p is outside the range of valid heap addresses, either l1() or +// l2() will be out of bounds. +// +// It is nosplit because it's called by spanOf and several other +// nosplit functions. +// +//go:nosplit +func arenaIndex(p uintptr) arenaIdx { + return arenaIdx((p + arenaBaseOffset) / heapArenaBytes) +} + +// arenaBase returns the low address of the region covered by heap +// arena i. +func arenaBase(i arenaIdx) uintptr { + return uintptr(i)*heapArenaBytes - arenaBaseOffset +} + +type arenaIdx uint + +func (i arenaIdx) l1() uint { + if arenaL1Bits == 0 { + // Let the compiler optimize this away if there's no + // L1 map. + return 0 + } else { + return uint(i) >> arenaL1Shift + } +} + +func (i arenaIdx) l2() uint { + if arenaL1Bits == 0 { + return uint(i) + } else { + return uint(i) & (1<<arenaL2Bits - 1) + } +} + // inheap reports whether b is a pointer into a (potentially dead) heap object. // It returns false for pointers into _MSpanManual spans. // Non-preemptible because it is used by write barriers. //go:nowritebarrier //go:nosplit func inheap(b uintptr) bool { - if b == 0 || b < mheap_.arena_start || b >= mheap_.arena_used { - return false - } - // Not a beginning of a block, consult span table to find the block beginning. - s := mheap_.spans[(b-mheap_.arena_start)>>_PageShift] - if s == nil || b < s.base() || b >= s.limit || s.state != mSpanInUse { - return false - } - return true + return spanOfHeap(b) != nil } // inHeapOrStack is a variant of inheap that returns true for pointers @@ -407,11 +471,7 @@ func inheap(b uintptr) bool { //go:nowritebarrier //go:nosplit func inHeapOrStack(b uintptr) bool { - if b == 0 || b < mheap_.arena_start || b >= mheap_.arena_used { - return false - } - // Not a beginning of a block, consult span table to find the block beginning. - s := mheap_.spans[(b-mheap_.arena_start)>>_PageShift] + s := spanOf(b) if s == nil || b < s.base() { return false } @@ -423,81 +483,81 @@ func inHeapOrStack(b uintptr) bool { } } -// TODO: spanOf and spanOfUnchecked are open-coded in a lot of places. -// Use the functions instead. - -// spanOf returns the span of p. If p does not point into the heap or -// no span contains p, spanOf returns nil. +// spanOf returns the span of p. If p does not point into the heap +// arena or no span has ever contained p, spanOf returns nil. +// +// If p does not point to allocated memory, this may return a non-nil +// span that does *not* contain p. If this is a possibility, the +// caller should either call spanOfHeap or check the span bounds +// explicitly. +// +// Must be nosplit because it has callers that are nosplit. +// +//go:nosplit func spanOf(p uintptr) *mspan { - if p == 0 || p < mheap_.arena_start || p >= mheap_.arena_used { + // This function looks big, but we use a lot of constant + // folding around arenaL1Bits to get it under the inlining + // budget. Also, many of the checks here are safety checks + // that Go needs to do anyway, so the generated code is quite + // short. + ri := arenaIndex(p) + if arenaL1Bits == 0 { + // If there's no L1, then ri.l1() can't be out of bounds but ri.l2() can. + if ri.l2() >= uint(len(mheap_.arenas[0])) { + return nil + } + } else { + // If there's an L1, then ri.l1() can be out of bounds but ri.l2() can't. + if ri.l1() >= uint(len(mheap_.arenas)) { + return nil + } + } + l2 := mheap_.arenas[ri.l1()] + if arenaL1Bits != 0 && l2 == nil { // Should never happen if there's no L1. return nil } - return spanOfUnchecked(p) + ha := l2[ri.l2()] + if ha == nil { + return nil + } + return ha.spans[(p/pageSize)%pagesPerArena] } // spanOfUnchecked is equivalent to spanOf, but the caller must ensure -// that p points into the heap (that is, mheap_.arena_start <= p < -// mheap_.arena_used). +// that p points into an allocated heap arena. +// +// Must be nosplit because it has callers that are nosplit. +// +//go:nosplit func spanOfUnchecked(p uintptr) *mspan { - return mheap_.spans[(p-mheap_.arena_start)>>_PageShift] + ai := arenaIndex(p) + return mheap_.arenas[ai.l1()][ai.l2()].spans[(p/pageSize)%pagesPerArena] } -func mlookup(v uintptr, base *uintptr, size *uintptr, sp **mspan) int32 { - _g_ := getg() - - _g_.m.mcache.local_nlookup++ - if sys.PtrSize == 4 && _g_.m.mcache.local_nlookup >= 1<<30 { - // purge cache stats to prevent overflow - lock(&mheap_.lock) - purgecachedstats(_g_.m.mcache) - unlock(&mheap_.lock) - } - - s := mheap_.lookupMaybe(unsafe.Pointer(v)) - if sp != nil { - *sp = s - } - if s == nil { - if base != nil { - *base = 0 - } - if size != nil { - *size = 0 - } - return 0 - } - - p := s.base() - if s.spanclass.sizeclass() == 0 { - // Large object. - if base != nil { - *base = p - } - if size != nil { - *size = s.npages << _PageShift - } - return 1 - } - - n := s.elemsize - if base != nil { - i := (v - p) / n - *base = p + i*n - } - if size != nil { - *size = n +// spanOfHeap is like spanOf, but returns nil if p does not point to a +// heap object. +// +// Must be nosplit because it has callers that are nosplit. +// +//go:nosplit +func spanOfHeap(p uintptr) *mspan { + s := spanOf(p) + // If p is not allocated, it may point to a stale span, so we + // have to check the span's bounds and state. + if s == nil || p < s.base() || p >= s.limit || s.state != mSpanInUse { + return nil } - - return 1 + return s } // Initialize the heap. -func (h *mheap) init(spansStart, spansBytes uintptr) { +func (h *mheap) init() { h.treapalloc.init(unsafe.Sizeof(treapNode{}), nil, nil, &memstats.other_sys) h.spanalloc.init(unsafe.Sizeof(mspan{}), recordspan, unsafe.Pointer(h), &memstats.mspan_sys) h.cachealloc.init(unsafe.Sizeof(mcache{}), nil, nil, &memstats.mcache_sys) h.specialfinalizeralloc.init(unsafe.Sizeof(specialfinalizer{}), nil, nil, &memstats.other_sys) h.specialprofilealloc.init(unsafe.Sizeof(specialprofile{}), nil, nil, &memstats.other_sys) + h.arenaHintAlloc.init(unsafe.Sizeof(arenaHint{}), nil, nil, &memstats.other_sys) // Don't zero mspan allocations. Background sweeping can // inspect a span concurrently with allocating it, so it's @@ -518,60 +578,6 @@ func (h *mheap) init(spansStart, spansBytes uintptr) { for i := range h.central { h.central[i].mcentral.init(spanClass(i)) } - - sp := (*slice)(unsafe.Pointer(&h.spans)) - sp.array = unsafe.Pointer(spansStart) - sp.len = 0 - sp.cap = int(spansBytes / sys.PtrSize) - - // Map metadata structures. But don't map race detector memory - // since we're not actually growing the arena here (and TSAN - // gets mad if you map 0 bytes). - h.setArenaUsed(h.arena_used, false) -} - -// setArenaUsed extends the usable arena to address arena_used and -// maps auxiliary VM regions for any newly usable arena space. -// -// racemap indicates that this memory should be managed by the race -// detector. racemap should be true unless this is covering a VM hole. -func (h *mheap) setArenaUsed(arena_used uintptr, racemap bool) { - // Map auxiliary structures *before* h.arena_used is updated. - // Waiting to update arena_used until after the memory has been mapped - // avoids faults when other threads try access these regions immediately - // after observing the change to arena_used. - - // Map the bitmap. - h.mapBits(arena_used) - - // Map spans array. - h.mapSpans(arena_used) - - // Tell the race detector about the new heap memory. - if racemap && raceenabled { - racemapshadow(unsafe.Pointer(h.arena_used), arena_used-h.arena_used) - } - - h.arena_used = arena_used -} - -// mapSpans makes sure that the spans are mapped -// up to the new value of arena_used. -// -// Don't call this directly. Call mheap.setArenaUsed. -func (h *mheap) mapSpans(arena_used uintptr) { - // Map spans array, PageSize at a time. - n := arena_used - n -= h.arena_start - n = n / _PageSize * sys.PtrSize - n = round(n, physPageSize) - need := n / unsafe.Sizeof(h.spans[0]) - have := uintptr(len(h.spans)) - if have >= need { - return - } - h.spans = h.spans[:need] - sysMap(unsafe.Pointer(&h.spans[have]), (need-have)*unsafe.Sizeof(h.spans[0]), h.arena_reserved, &memstats.other_sys) } // Sweeps spans in list until reclaims at least npages into heap. @@ -598,7 +604,7 @@ retry: goto retry } if s.sweepgen == sg-1 { - // the span is being sweept by background sweeper, skip + // the span is being swept by background sweeper, skip continue } // already swept empty span, @@ -785,7 +791,7 @@ func (h *mheap) allocManual(npage uintptr, stat *uint64) *mspan { s.nelems = 0 s.elemsize = 0 s.limit = s.base() + s.npages<<_PageShift - // Manually manged memory doesn't count toward heap_sys. + // Manually managed memory doesn't count toward heap_sys. memstats.heap_sys -= uint64(s.npages << _PageShift) } @@ -795,6 +801,28 @@ func (h *mheap) allocManual(npage uintptr, stat *uint64) *mspan { return s } +// setSpan modifies the span map so spanOf(base) is s. +func (h *mheap) setSpan(base uintptr, s *mspan) { + ai := arenaIndex(base) + h.arenas[ai.l1()][ai.l2()].spans[(base/pageSize)%pagesPerArena] = s +} + +// setSpans modifies the span map so [spanOf(base), spanOf(base+npage*pageSize)) +// is s. +func (h *mheap) setSpans(base, npage uintptr, s *mspan) { + p := base / pageSize + ai := arenaIndex(base) + ha := h.arenas[ai.l1()][ai.l2()] + for n := uintptr(0); n < npage; n++ { + i := (p + n) % pagesPerArena + if i == 0 { + ai = arenaIndex(base + n*pageSize) + ha = h.arenas[ai.l1()][ai.l2()] + } + ha.spans[i] = s + } +} + // Allocates a span of the given size. h must be locked. // The returned span has been removed from the // free list, but its state is still MSpanFree. @@ -842,12 +870,9 @@ HaveSpan: t := (*mspan)(h.spanalloc.alloc()) t.init(s.base()+npage<<_PageShift, s.npages-npage) s.npages = npage - p := (t.base() - h.arena_start) >> _PageShift - if p > 0 { - h.spans[p-1] = s - } - h.spans[p] = t - h.spans[p+t.npages-1] = t + h.setSpan(t.base()-1, s) + h.setSpan(t.base(), t) + h.setSpan(t.base()+t.npages*pageSize-1, t) t.needzero = s.needzero s.state = _MSpanManual // prevent coalescing with s t.state = _MSpanManual @@ -856,10 +881,7 @@ HaveSpan: } s.unusedsince = 0 - p := (s.base() - h.arena_start) >> _PageShift - for n := uintptr(0); n < npage; n++ { - h.spans[p+n] = s - } + h.setSpans(s.base(), npage, s) *stat += uint64(npage << _PageShift) memstats.heap_idle -= uint64(npage << _PageShift) @@ -891,36 +913,18 @@ func (h *mheap) allocLarge(npage uintptr) *mspan { // // h must be locked. func (h *mheap) grow(npage uintptr) bool { - // Ask for a big chunk, to reduce the number of mappings - // the operating system needs to track; also amortizes - // the overhead of an operating system mapping. - // Allocate a multiple of 64kB. - npage = round(npage, (64<<10)/_PageSize) ask := npage << _PageShift - if ask < _HeapAllocChunk { - ask = _HeapAllocChunk - } - - v := h.sysAlloc(ask) + v, size := h.sysAlloc(ask) if v == nil { - if ask > npage<<_PageShift { - ask = npage << _PageShift - v = h.sysAlloc(ask) - } - if v == nil { - print("runtime: out of memory: cannot allocate ", ask, "-byte block (", memstats.heap_sys, " in use)\n") - return false - } + print("runtime: out of memory: cannot allocate ", ask, "-byte block (", memstats.heap_sys, " in use)\n") + return false } // Create a fake "in use" span and free it, so that the // right coalescing happens. s := (*mspan)(h.spanalloc.alloc()) - s.init(uintptr(v), ask>>_PageShift) - p := (s.base() - h.arena_start) >> _PageShift - for i := p; i < p+s.npages; i++ { - h.spans[i] = s - } + s.init(uintptr(v), size/pageSize) + h.setSpans(s.base(), s.npages, s) atomic.Store(&s.sweepgen, h.sweepgen) s.state = _MSpanInUse h.pagesInUse += uint64(s.npages) @@ -928,33 +932,6 @@ func (h *mheap) grow(npage uintptr) bool { return true } -// Look up the span at the given address. -// Address is guaranteed to be in map -// and is guaranteed to be start or end of span. -func (h *mheap) lookup(v unsafe.Pointer) *mspan { - p := uintptr(v) - p -= h.arena_start - return h.spans[p>>_PageShift] -} - -// Look up the span at the given address. -// Address is *not* guaranteed to be in map -// and may be anywhere in the span. -// Map entries for the middle of a span are only -// valid for allocated spans. Free spans may have -// other garbage in their middles, so we have to -// check for that. -func (h *mheap) lookupMaybe(v unsafe.Pointer) *mspan { - if uintptr(v) < h.arena_start || uintptr(v) >= h.arena_used { - return nil - } - s := h.spans[(uintptr(v)-h.arena_start)>>_PageShift] - if s == nil || uintptr(v) < s.base() || uintptr(v) >= uintptr(unsafe.Pointer(s.limit)) || s.state != _MSpanInUse { - return nil - } - return s -} - // Free the span back into the heap. func (h *mheap) freeSpan(s *mspan, acct int32) { systemstack(func() { @@ -1039,46 +1016,38 @@ func (h *mheap) freeSpanLocked(s *mspan, acctinuse, acctidle bool, unusedsince i s.npreleased = 0 // Coalesce with earlier, later spans. - p := (s.base() - h.arena_start) >> _PageShift - if p > 0 { - before := h.spans[p-1] - if before != nil && before.state == _MSpanFree { - // Now adjust s. - s.startAddr = before.startAddr - s.npages += before.npages - s.npreleased = before.npreleased // absorb released pages - s.needzero |= before.needzero - p -= before.npages - h.spans[p] = s - // The size is potentially changing so the treap needs to delete adjacent nodes and - // insert back as a combined node. - if h.isLargeSpan(before.npages) { - // We have a t, it is large so it has to be in the treap so we can remove it. - h.freelarge.removeSpan(before) - } else { - h.freeList(before.npages).remove(before) - } - before.state = _MSpanDead - h.spanalloc.free(unsafe.Pointer(before)) + if before := spanOf(s.base() - 1); before != nil && before.state == _MSpanFree { + // Now adjust s. + s.startAddr = before.startAddr + s.npages += before.npages + s.npreleased = before.npreleased // absorb released pages + s.needzero |= before.needzero + h.setSpan(before.base(), s) + // The size is potentially changing so the treap needs to delete adjacent nodes and + // insert back as a combined node. + if h.isLargeSpan(before.npages) { + // We have a t, it is large so it has to be in the treap so we can remove it. + h.freelarge.removeSpan(before) + } else { + h.freeList(before.npages).remove(before) } + before.state = _MSpanDead + h.spanalloc.free(unsafe.Pointer(before)) } // Now check to see if next (greater addresses) span is free and can be coalesced. - if (p + s.npages) < uintptr(len(h.spans)) { - after := h.spans[p+s.npages] - if after != nil && after.state == _MSpanFree { - s.npages += after.npages - s.npreleased += after.npreleased - s.needzero |= after.needzero - h.spans[p+s.npages-1] = s - if h.isLargeSpan(after.npages) { - h.freelarge.removeSpan(after) - } else { - h.freeList(after.npages).remove(after) - } - after.state = _MSpanDead - h.spanalloc.free(unsafe.Pointer(after)) + if after := spanOf(s.base() + s.npages*pageSize); after != nil && after.state == _MSpanFree { + s.npages += after.npages + s.npreleased += after.npreleased + s.needzero |= after.needzero + h.setSpan(s.base()+s.npages*pageSize-1, s) + if h.isLargeSpan(after.npages) { + h.freelarge.removeSpan(after) + } else { + h.freeList(after.npages).remove(after) } + after.state = _MSpanDead + h.spanalloc.free(unsafe.Pointer(after)) } // Insert s into appropriate list or treap. @@ -1343,7 +1312,7 @@ type special struct { // (The add will fail only if a record with the same p and s->kind // already exists.) func addspecial(p unsafe.Pointer, s *special) bool { - span := mheap_.lookupMaybe(p) + span := spanOfHeap(uintptr(p)) if span == nil { throw("addspecial on invalid pointer") } @@ -1391,7 +1360,7 @@ func addspecial(p unsafe.Pointer, s *special) bool { // Returns the record if the record existed, nil otherwise. // The caller must FixAlloc_Free the result. func removespecial(p unsafe.Pointer, kind uint8) *special { - span := mheap_.lookupMaybe(p) + span := spanOfHeap(uintptr(p)) if span == nil { throw("removespecial on invalid pointer") } @@ -1454,12 +1423,12 @@ func addfinalizer(p unsafe.Pointer, f *funcval, ft *functype, ot *ptrtype) bool // situation where it's possible that markrootSpans // has already run but mark termination hasn't yet. if gcphase != _GCoff { - _, base, _ := findObject(p) + base, _, _ := findObject(uintptr(p), 0, 0, false) mp := acquirem() gcw := &mp.p.ptr().gcw // Mark everything reachable from the object // so it's retained for the finalizer. - scanobject(uintptr(base), gcw) + scanobject(base, gcw) // Mark the finalizer itself, since the // special isn't part of the GC'd heap. scanblock(uintptr(unsafe.Pointer(&s.fn)), sys.PtrSize, &oneptrmask[0], gcw) @@ -1643,7 +1612,7 @@ func newMarkBits(nelems uintptr) *gcBits { // to be used for this span's alloc bits. // newAllocBits is used to provide newly initialized spans // allocation bits. For spans not being initialized the -// the mark bits are repurposed as allocation bits when +// mark bits are repurposed as allocation bits when // the span is swept. func newAllocBits(nelems uintptr) *gcBits { return newMarkBits(nelems) diff --git a/libgo/go/runtime/mprof.go b/libgo/go/runtime/mprof.go index f31c88c..2bbf37a 100644 --- a/libgo/go/runtime/mprof.go +++ b/libgo/go/runtime/mprof.go @@ -436,7 +436,7 @@ var mutexprofilerate uint64 // fraction sampled // reported. The previous rate is returned. // // To turn off profiling entirely, pass rate 0. -// To just read the current rate, pass rate -1. +// To just read the current rate, pass rate < 0. // (For n>1 the details of sampling may change.) func SetMutexProfileFraction(rate int) int { if rate < 0 { @@ -833,7 +833,7 @@ func tracealloc(p unsafe.Pointer, size uintptr, typ *_type) { if typ == nil { print("tracealloc(", p, ", ", hex(size), ")\n") } else { - print("tracealloc(", p, ", ", hex(size), ", ", *typ.string, ")\n") + print("tracealloc(", p, ", ", hex(size), ", ", typ.string(), ")\n") } if gp.m.curg == nil || gp == gp.m.curg { goroutineheader(gp) diff --git a/libgo/go/runtime/msan/msan.go b/libgo/go/runtime/msan/msan.go index b6ea3f0..c81577d 100644 --- a/libgo/go/runtime/msan/msan.go +++ b/libgo/go/runtime/msan/msan.go @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build msan,linux,amd64 +// +build msan,linux +// +build amd64 arm64 package msan diff --git a/libgo/go/runtime/mstats.go b/libgo/go/runtime/mstats.go index 095a0de..f54ce9d 100644 --- a/libgo/go/runtime/mstats.go +++ b/libgo/go/runtime/mstats.go @@ -26,7 +26,7 @@ type mstats struct { alloc uint64 // bytes allocated and not yet freed total_alloc uint64 // bytes allocated (even if freed) sys uint64 // bytes obtained from system (should be sum of xxx_sys below, no locking, approximate) - nlookup uint64 // number of pointer lookups + nlookup uint64 // number of pointer lookups (unused) nmalloc uint64 // number of mallocs nfree uint64 // number of frees @@ -637,8 +637,6 @@ func purgecachedstats(c *mcache) { c.local_scan = 0 memstats.tinyallocs += uint64(c.local_tinyallocs) c.local_tinyallocs = 0 - memstats.nlookup += uint64(c.local_nlookup) - c.local_nlookup = 0 h.largefree += uint64(c.local_largefree) c.local_largefree = 0 h.nlargefree += uint64(c.local_nlargefree) @@ -663,6 +661,9 @@ func purgecachedstats(c *mcache) { // overflow errors. //go:nosplit func mSysStatInc(sysStat *uint64, n uintptr) { + if sysStat == nil { + return + } if sys.BigEndian { atomic.Xadd64(sysStat, int64(n)) return @@ -677,6 +678,9 @@ func mSysStatInc(sysStat *uint64, n uintptr) { // mSysStatInc apply. //go:nosplit func mSysStatDec(sysStat *uint64, n uintptr) { + if sysStat == nil { + return + } if sys.BigEndian { atomic.Xadd64(sysStat, -int64(n)) return diff --git a/libgo/go/runtime/mwbbuf.go b/libgo/go/runtime/mwbbuf.go index 7e88463..39d1370 100644 --- a/libgo/go/runtime/mwbbuf.go +++ b/libgo/go/runtime/mwbbuf.go @@ -5,6 +5,9 @@ // This implements the write barrier buffer. The write barrier itself // is gcWriteBarrier and is implemented in assembly. // +// See mbarrier.go for algorithmic details on the write barrier. This +// file deals only with the buffer. +// // The write barrier has a fast path and a slow path. The fast path // simply enqueues to a per-P write barrier buffer. It's written in // assembly and doesn't clobber any general purpose registers, so it @@ -111,16 +114,21 @@ func (b *wbBuf) discard() { // if !buf.putFast(old, new) { // wbBufFlush(...) // } +// ... actual memory write ... // // The arguments to wbBufFlush depend on whether the caller is doing // its own cgo pointer checks. If it is, then this can be // wbBufFlush(nil, 0). Otherwise, it must pass the slot address and // new. // -// Since buf is a per-P resource, the caller must ensure there are no -// preemption points while buf is in use. +// The caller must ensure there are no preemption points during the +// above sequence. There must be no preemption points while buf is in +// use because it is a per-P resource. There must be no preemption +// points between the buffer put and the write to memory because this +// could allow a GC phase change, which could result in missed write +// barriers. // -// It must be nowritebarrierrec to because write barriers here would +// putFast must be nowritebarrierrec to because write barriers here would // corrupt the write barrier buffer. It (and everything it calls, if // it called anything) has to be nosplit to avoid scheduling on to a // different P and a different buffer. @@ -155,6 +163,13 @@ func wbBufFlush(dst *uintptr, src uintptr) { // Note: Every possible return from this function must reset // the buffer's next pointer to prevent buffer overflow. + // This *must not* modify its arguments because this + // function's argument slots do double duty in gcWriteBarrier + // as register spill slots. Currently, not modifying the + // arguments is sufficient to keep the spill slots unmodified + // (which seems unlikely to change since it costs little and + // helps with debugging). + if getg().m.dying > 0 { // We're going down. Not much point in write barriers // and this way we can allow write barriers in the @@ -214,11 +229,18 @@ func wbBufFlush1(_p_ *p) { // // TODO: Should scanobject/scanblock just stuff pointers into // the wbBuf? Then this would become the sole greying path. + // + // TODO: We could avoid shading any of the "new" pointers in + // the buffer if the stack has been shaded, or even avoid + // putting them in the buffer at all (which would double its + // capacity). This is slightly complicated with the buffer; we + // could track whether any un-shaded goroutine has used the + // buffer, or just track globally whether there are any + // un-shaded stacks and flush after each stack scan. gcw := &_p_.gcw pos := 0 - arenaStart := mheap_.arena_start for _, ptr := range ptrs { - if ptr < arenaStart { + if ptr < minLegalPointer { // nil pointers are very common, especially // for the "old" values. Filter out these and // other "obvious" non-heap pointers ASAP. @@ -227,11 +249,7 @@ func wbBufFlush1(_p_ *p) { // path to reduce the rate of flushes? continue } - // TODO: This doesn't use hbits, so calling - // heapBitsForObject seems a little silly. We could - // easily separate this out since heapBitsForObject - // just calls heapBitsForAddr(obj) to get hbits. - obj, _, span, objIndex := heapBitsForObject(ptr, 0, 0, false) + obj, span, objIndex := findObject(ptr, 0, 0, false) if obj == 0 { continue } diff --git a/libgo/go/runtime/netpoll.go b/libgo/go/runtime/netpoll.go index 3aeb1f6..ab3d14d 100644 --- a/libgo/go/runtime/netpoll.go +++ b/libgo/go/runtime/netpoll.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package runtime @@ -366,7 +366,7 @@ func netpollblock(pd *pollDesc, mode int32, waitio bool) bool { // this is necessary because runtime_pollUnblock/runtime_pollSetDeadline/deadlineimpl // do the opposite: store to closing/rd/wd, membarrier, load of rg/wg if waitio || netpollcheckerr(pd, mode) == 0 { - gopark(netpollblockcommit, unsafe.Pointer(gpp), "IO wait", traceEvGoBlockNet, 5) + gopark(netpollblockcommit, unsafe.Pointer(gpp), waitReasonIOWait, traceEvGoBlockNet, 5) } // be careful to not lose concurrent READY notification old := atomic.Xchguintptr(gpp, 0) diff --git a/libgo/go/runtime/netpoll_nacl.go b/libgo/go/runtime/netpoll_fake.go index dc5a55e..aab18dc 100644 --- a/libgo/go/runtime/netpoll_nacl.go +++ b/libgo/go/runtime/netpoll_fake.go @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Fake network poller for NaCl. -// Should never be used, because NaCl network connections do not honor "SetNonblock". +// Fake network poller for NaCl and wasm/js. +// Should never be used, because NaCl and wasm/js network connections do not honor "SetNonblock". + +// +build nacl js,wasm package runtime diff --git a/libgo/go/runtime/os_darwin.go b/libgo/go/runtime/os_darwin.go index ec92370..9597633 100644 --- a/libgo/go/runtime/os_darwin.go +++ b/libgo/go/runtime/os_darwin.go @@ -7,323 +7,61 @@ package runtime import "unsafe" type mOS struct { - machport uint32 // return address for mach ipc - waitsema uint32 // semaphore for parking on locks + initialized bool + mutex pthreadmutex + cond pthreadcond + count int } -//go:noescape -//extern mach_msg_trap -func mach_msg_trap(h unsafe.Pointer, op int32, send_size, rcv_size, rcv_name, timeout, notify uint32) int32 - -//extern mach_reply_port -func mach_reply_port() uint32 - -//extern mach_task_self -func mach_task_self() uint32 - func unimplemented(name string) { println(name, "not implemented") *(*int)(unsafe.Pointer(uintptr(1231))) = 1231 } //go:nosplit -func semawakeup(mp *m) { - mach_semrelease(mp.mos.waitsema) -} - -//go:nosplit func semacreate(mp *m) { - if mp.mos.waitsema != 0 { + if mp.initialized { return } - systemstack(func() { - mp.mos.waitsema = mach_semcreate() - }) -} - -// Mach IPC, to get at semaphores -// Definitions are in /usr/include/mach on a Mac. - -func macherror(r int32, fn string) { - print("mach error ", fn, ": ", r, "\n") - throw("mach error") -} - -const _DebugMach = false - -var zerondr machndr - -func mach_msgh_bits(a, b uint32) uint32 { - return a | b<<8 -} - -func mach_msg(h *machheader, op int32, send_size, rcv_size, rcv_name, timeout, notify uint32) int32 { - // TODO: Loop on interrupt. - return mach_msg_trap(unsafe.Pointer(h), op, send_size, rcv_size, rcv_name, timeout, notify) -} - -// Mach RPC (MIG) -const ( - _MinMachMsg = 48 - _MachReply = 100 -) - -type codemsg struct { - h machheader - ndr machndr - code int32 -} - -func machcall(h *machheader, maxsize int32, rxsize int32) int32 { - _g_ := getg() - port := _g_.m.mos.machport - if port == 0 { - port = mach_reply_port() - _g_.m.mos.machport = port - } - - h.msgh_bits |= mach_msgh_bits(_MACH_MSG_TYPE_COPY_SEND, _MACH_MSG_TYPE_MAKE_SEND_ONCE) - h.msgh_local_port = port - h.msgh_reserved = 0 - id := h.msgh_id - - if _DebugMach { - p := (*[10000]unsafe.Pointer)(unsafe.Pointer(h)) - print("send:\t") - var i uint32 - for i = 0; i < h.msgh_size/uint32(unsafe.Sizeof(p[0])); i++ { - print(" ", p[i]) - if i%8 == 7 { - print("\n\t") - } - } - if i%8 != 0 { - print("\n") - } - } - ret := mach_msg(h, _MACH_SEND_MSG|_MACH_RCV_MSG, h.msgh_size, uint32(maxsize), port, 0, 0) - if ret != 0 { - if _DebugMach { - print("mach_msg error ", ret, "\n") - } - return ret - } - if _DebugMach { - p := (*[10000]unsafe.Pointer)(unsafe.Pointer(h)) - var i uint32 - for i = 0; i < h.msgh_size/uint32(unsafe.Sizeof(p[0])); i++ { - print(" ", p[i]) - if i%8 == 7 { - print("\n\t") - } - } - if i%8 != 0 { - print("\n") - } - } - if h.msgh_id != id+_MachReply { - if _DebugMach { - print("mach_msg _MachReply id mismatch ", h.msgh_id, " != ", id+_MachReply, "\n") - } - return -303 // MIG_REPLY_MISMATCH - } - // Look for a response giving the return value. - // Any call can send this back with an error, - // and some calls only have return values so they - // send it back on success too. I don't quite see how - // you know it's one of these and not the full response - // format, so just look if the message is right. - c := (*codemsg)(unsafe.Pointer(h)) - if uintptr(h.msgh_size) == unsafe.Sizeof(*c) && h.msgh_bits&_MACH_MSGH_BITS_COMPLEX == 0 { - if _DebugMach { - print("mig result ", c.code, "\n") - } - return c.code - } - if h.msgh_size != uint32(rxsize) { - if _DebugMach { - print("mach_msg _MachReply size mismatch ", h.msgh_size, " != ", rxsize, "\n") - } - return -307 // MIG_ARRAY_TOO_LARGE - } - return 0 -} - -// Semaphores! - -const ( - tmach_semcreate = 3418 - rmach_semcreate = tmach_semcreate + _MachReply - - tmach_semdestroy = 3419 - rmach_semdestroy = tmach_semdestroy + _MachReply - - _KERN_ABORTED = 14 - _KERN_OPERATION_TIMED_OUT = 49 -) - -type tmach_semcreatemsg struct { - h machheader - ndr machndr - policy int32 - value int32 -} - -type rmach_semcreatemsg struct { - h machheader - body machbody - semaphore machport -} - -type tmach_semdestroymsg struct { - h machheader - body machbody - semaphore machport -} - -func mach_semcreate() uint32 { - var m [256]uint8 - tx := (*tmach_semcreatemsg)(unsafe.Pointer(&m)) - rx := (*rmach_semcreatemsg)(unsafe.Pointer(&m)) - - tx.h.msgh_bits = 0 - tx.h.msgh_size = uint32(unsafe.Sizeof(*tx)) - tx.h.msgh_remote_port = mach_task_self() - tx.h.msgh_id = tmach_semcreate - tx.ndr = zerondr - - tx.policy = 0 // 0 = SYNC_POLICY_FIFO - tx.value = 0 - - for { - r := machcall(&tx.h, int32(unsafe.Sizeof(m)), int32(unsafe.Sizeof(*rx))) - if r == 0 { - break - } - if r == _KERN_ABORTED { // interrupted - continue - } - macherror(r, "semaphore_create") - } - if rx.body.msgh_descriptor_count != 1 { - unimplemented("mach_semcreate desc count") - } - return rx.semaphore.name -} - -func mach_semdestroy(sem uint32) { - var m [256]uint8 - tx := (*tmach_semdestroymsg)(unsafe.Pointer(&m)) - - tx.h.msgh_bits = _MACH_MSGH_BITS_COMPLEX - tx.h.msgh_size = uint32(unsafe.Sizeof(*tx)) - tx.h.msgh_remote_port = mach_task_self() - tx.h.msgh_id = tmach_semdestroy - tx.body.msgh_descriptor_count = 1 - tx.semaphore.name = sem - tx.semaphore.disposition = _MACH_MSG_TYPE_MOVE_SEND - tx.semaphore._type = 0 - - for { - r := machcall(&tx.h, int32(unsafe.Sizeof(m)), 0) - if r == 0 { - break - } - if r == _KERN_ABORTED { // interrupted - continue - } - macherror(r, "semaphore_destroy") - } -} - -//extern semaphore_wait -func mach_semaphore_wait(sema uint32) int32 - -//extern semaphore_timedwait -func mach_semaphore_timedwait(sema, sec, nsec uint32) int32 - -//extern semaphore_signal -func mach_semaphore_signal(sema uint32) int32 - -//extern semaphore_signal_all -func mach_semaphore_signal_all(sema uint32) int32 - -func semasleep1(ns int64) int32 { - _g_ := getg() - - if ns >= 0 { - var nsecs int32 - secs := timediv(ns, 1000000000, &nsecs) - r := mach_semaphore_timedwait(_g_.m.mos.waitsema, uint32(secs), uint32(nsecs)) - if r == _KERN_ABORTED || r == _KERN_OPERATION_TIMED_OUT { - return -1 - } - if r != 0 { - macherror(r, "semaphore_wait") - } - return 0 + mp.initialized = true + if err := pthread_mutex_init(&mp.mutex, nil); err != 0 { + throw("pthread_mutex_init") } - - for { - r := mach_semaphore_wait(_g_.m.mos.waitsema) - if r == 0 { - break - } - // Note: We don't know how this call (with no timeout) can get _KERN_OPERATION_TIMED_OUT, - // but it does reliably, though at a very low rate, on OS X 10.8, 10.9, 10.10, and 10.11. - // See golang.org/issue/17161. - if r == _KERN_ABORTED || r == _KERN_OPERATION_TIMED_OUT { // interrupted - continue - } - macherror(r, "semaphore_wait") + if err := pthread_cond_init(&mp.cond, nil); err != 0 { + throw("pthread_cond_init") } - return 0 } //go:nosplit func semasleep(ns int64) int32 { - var r int32 - systemstack(func() { - r = semasleep1(ns) - }) - return r -} - -//go:nosplit -func mach_semrelease(sem uint32) { + mp := getg().m + pthread_mutex_lock(&mp.mutex) for { - r := mach_semaphore_signal(sem) - if r == 0 { - break - } - if r == _KERN_ABORTED { // interrupted - continue + if mp.count > 0 { + mp.count-- + pthread_mutex_unlock(&mp.mutex) + return 0 + } + if ns >= 0 { + var t timespec + t.set_nsec(ns) + err := pthread_cond_timedwait_relative_np(&mp.cond, &mp.mutex, &t) + if err == _ETIMEDOUT { + pthread_mutex_unlock(&mp.mutex) + return -1 + } + } else { + pthread_cond_wait(&mp.cond, &mp.mutex) } - - // mach_semrelease must be completely nosplit, - // because it is called from Go code. - // If we're going to die, start that process on the system stack - // to avoid a Go stack split. - systemstack(func() { macherror(r, "semaphore_signal") }) } } -type machheader struct { - msgh_bits uint32 - msgh_size uint32 - msgh_remote_port uint32 - msgh_local_port uint32 - msgh_reserved uint32 - msgh_id int32 -} - -type machndr struct { - mig_vers uint8 - if_vers uint8 - reserved1 uint8 - mig_encoding uint8 - int_rep uint8 - char_rep uint8 - float_rep uint8 - reserved2 uint8 +//go:nosplit +func semawakeup(mp *m) { + pthread_mutex_lock(&mp.mutex) + mp.count++ + if mp.count > 0 { + pthread_cond_signal(&mp.cond) + } + pthread_mutex_unlock(&mp.mutex) } diff --git a/libgo/go/runtime/os_dragonfly.go b/libgo/go/runtime/os_dragonfly.go index 6452984..abcad72 100644 --- a/libgo/go/runtime/os_dragonfly.go +++ b/libgo/go/runtime/os_dragonfly.go @@ -4,11 +4,12 @@ package runtime -import "unsafe" +import ( + "runtime/internal/sys" + "unsafe" +) -type mOS struct { - unused byte -} +type mOS struct{} //go:noescape //extern umtx_sleep diff --git a/libgo/go/runtime/os_freebsd.go b/libgo/go/runtime/os_freebsd.go index 8c3535b..34939c5 100644 --- a/libgo/go/runtime/os_freebsd.go +++ b/libgo/go/runtime/os_freebsd.go @@ -8,9 +8,7 @@ import ( "unsafe" ) -type mOS struct { - unused byte -} +type mOS struct{} //go:noescape //extern _umtx_op diff --git a/libgo/go/runtime/os_js.go b/libgo/go/runtime/os_js.go new file mode 100644 index 0000000..ad6db18 --- /dev/null +++ b/libgo/go/runtime/os_js.go @@ -0,0 +1,145 @@ +// Copyright 2018 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 js,wasm + +package runtime + +import ( + "unsafe" +) + +func exit(code int32) + +func write(fd uintptr, p unsafe.Pointer, n int32) int32 { + if fd > 2 { + throw("runtime.write to fd > 2 is unsupported") + } + wasmWrite(fd, p, n) + return n +} + +// Stubs so tests can link correctly. These should never be called. +func open(name *byte, mode, perm int32) int32 { panic("not implemented") } +func closefd(fd int32) int32 { panic("not implemented") } +func read(fd int32, p unsafe.Pointer, n int32) int32 { panic("not implemented") } + +//go:noescape +func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) + +func usleep(usec uint32) + +func exitThread(wait *uint32) + +type mOS struct{} + +func osyield() + +const _SIGSEGV = 0xb + +func sigpanic() { + g := getg() + if !canpanic(g) { + throw("unexpected signal during runtime execution") + } + + // js only invokes the exception handler for memory faults. + g.sig = _SIGSEGV + panicmem() +} + +type sigset struct{} + +// Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +func mpreinit(mp *m) { + mp.gsignal = malg(32 * 1024) + mp.gsignal.m = mp +} + +//go:nosplit +func msigsave(mp *m) { +} + +//go:nosplit +func msigrestore(sigmask sigset) { +} + +//go:nosplit +//go:nowritebarrierrec +func clearSignalHandlers() { +} + +//go:nosplit +func sigblock() { +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, cannot allocate memory. +func minit() { +} + +// Called from dropm to undo the effect of an minit. +func unminit() { +} + +func osinit() { + ncpu = 1 + getg().m.procid = 2 + physPageSize = 64 * 1024 +} + +// wasm has no signals +const _NSIG = 0 + +func signame(sig uint32) string { + return "" +} + +func crash() { + *(*int32)(nil) = 0 +} + +func getRandomData(r []byte) + +func goenvs() { + goenvs_unix() +} + +func initsig(preinit bool) { +} + +// May run with m.p==nil, so write barriers are not allowed. +//go:nowritebarrier +func newosproc(mp *m) { + panic("newosproc: not implemented") +} + +func setProcessCPUProfiler(hz int32) {} +func setThreadCPUProfiler(hz int32) {} +func sigdisable(uint32) {} +func sigenable(uint32) {} +func sigignore(uint32) {} + +//go:linkname os_sigpipe os.sigpipe +func os_sigpipe() { + throw("too many writes on closed pipe") +} + +//go:nosplit +func cputicks() int64 { + // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand(). + // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. + // TODO: need more entropy to better seed fastrand. + return nanotime() +} + +//go:linkname syscall_now syscall.now +func syscall_now() (sec int64, nsec int32) { + sec, nsec, _ = time_now() + return +} + +// gsignalStack is unused on js. +type gsignalStack struct{} diff --git a/libgo/go/runtime/os_linux.go b/libgo/go/runtime/os_linux.go index 816327e..04314bd 100644 --- a/libgo/go/runtime/os_linux.go +++ b/libgo/go/runtime/os_linux.go @@ -27,8 +27,9 @@ func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, // Futexsleep is allowed to wake up spuriously. const ( - _FUTEX_WAIT = 0 - _FUTEX_WAKE = 1 + _FUTEX_PRIVATE_FLAG = 128 + _FUTEX_WAIT_PRIVATE = 0 | _FUTEX_PRIVATE_FLAG + _FUTEX_WAKE_PRIVATE = 1 | _FUTEX_PRIVATE_FLAG ) // Atomically, @@ -45,7 +46,7 @@ func futexsleep(addr *uint32, val uint32, ns int64) { // here, and so can we: as it says a few lines up, // spurious wakeups are allowed. if ns < 0 { - futex(unsafe.Pointer(addr), _FUTEX_WAIT, val, nil, nil, 0) + futex(unsafe.Pointer(addr), _FUTEX_WAIT_PRIVATE, val, nil, nil, 0) return } @@ -62,13 +63,13 @@ func futexsleep(addr *uint32, val uint32, ns int64) { ts.tv_nsec = 0 ts.set_sec(int64(timediv(ns, 1000000000, (*int32)(unsafe.Pointer(&ts.tv_nsec))))) } - futex(unsafe.Pointer(addr), _FUTEX_WAIT, val, unsafe.Pointer(&ts), nil, 0) + futex(unsafe.Pointer(addr), _FUTEX_WAIT_PRIVATE, val, unsafe.Pointer(&ts), nil, 0) } // If any procs are sleeping on addr, wake up at most cnt. //go:nosplit func futexwakeup(addr *uint32, cnt uint32) { - ret := futex(unsafe.Pointer(addr), _FUTEX_WAKE, cnt, nil, nil, 0) + ret := futex(unsafe.Pointer(addr), _FUTEX_WAKE_PRIVATE, cnt, nil, nil, 0) if ret >= 0 { return } @@ -93,6 +94,11 @@ const ( var procAuxv = []byte("/proc/self/auxv\x00") +var addrspace_vec [1]byte + +//extern mincore +func mincore(addr unsafe.Pointer, n uintptr, dst *byte) int32 + func sysargs(argc int32, argv **byte) { n := argc + 1 @@ -158,12 +164,17 @@ func sysauxv(auxv []uintptr) int { // worth of random data. startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:] + setRandomNumber(uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | + uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24) + case _AT_PAGESZ: physPageSize = val } + archauxv(tag, val) + // Commented out for gccgo for now. - // archauxv(tag, val) + // vdsoauxv(tag, val) } return i / 2 } diff --git a/libgo/go/runtime/os_linux_arm.go b/libgo/go/runtime/os_linux_arm.go new file mode 100644 index 0000000..42c2839 --- /dev/null +++ b/libgo/go/runtime/os_linux_arm.go @@ -0,0 +1,60 @@ +// 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 runtime + +import "unsafe" + +const ( + _AT_PLATFORM = 15 // introduced in at least 2.6.11 + + _HWCAP_VFP = 1 << 6 // introduced in at least 2.6.11 + _HWCAP_VFPv3 = 1 << 13 // introduced in 2.6.30 + _HWCAP_IDIVA = 1 << 17 +) + +var randomNumber uint32 +var armArch uint8 = 6 // we default to ARMv6 +var hwcap uint32 // set by archauxv +var hardDiv bool // set if a hardware divider is available + +func checkgoarm() { + // On Android, /proc/self/auxv might be unreadable and hwcap won't + // reflect the CPU capabilities. Assume that every Android arm device + // has the necessary floating point hardware available. + if GOOS == "android" { + return + } + if goarm > 5 && hwcap&_HWCAP_VFP == 0 { + print("runtime: this CPU has no floating point hardware, so it cannot run\n") + print("this GOARM=", goarm, " binary. Recompile using GOARM=5.\n") + exit(1) + } + if goarm > 6 && hwcap&_HWCAP_VFPv3 == 0 { + print("runtime: this CPU has no VFPv3 floating point hardware, so it cannot run\n") + print("this GOARM=", goarm, " binary. Recompile using GOARM=5 or GOARM=6.\n") + exit(1) + } +} + +func archauxv(tag, val uintptr) { + switch tag { + case _AT_RANDOM: + // sysargs filled in startupRandomData, but that + // pointer may not be word aligned, so we must treat + // it as a byte array. + randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | + uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24 + + case _AT_PLATFORM: // v5l, v6l, v7l + t := *(*uint8)(unsafe.Pointer(val + 1)) + if '5' <= t && t <= '7' { + armArch = t - '0' + } + + case _AT_HWCAP: // CPU capability bit flags + hwcap = uint32(val) + hardDiv = (hwcap & _HWCAP_IDIVA) != 0 + } +} diff --git a/libgo/go/runtime/os_linux_arm64.go b/libgo/go/runtime/os_linux_arm64.go new file mode 100644 index 0000000..013e7ae --- /dev/null +++ b/libgo/go/runtime/os_linux_arm64.go @@ -0,0 +1,29 @@ +// Copyright 2015 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 arm64 + +package runtime + +import "internal/cpu" + +var randomNumber uint32 + +func archauxv(tag, val uintptr) { + switch tag { + case _AT_RANDOM: + // sysargs filled in startupRandomData, but that + // pointer may not be word aligned, so we must treat + // it as a byte array. + randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | + uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24 + + case _AT_HWCAP: + // arm64 doesn't have a 'cpuid' instruction equivalent and relies on + // HWCAP/HWCAP2 bits for hardware capabilities. + cpu.HWCap = uint(val) + case _AT_HWCAP2: + cpu.HWCap2 = uint(val) + } +} diff --git a/libgo/go/runtime/os_linux_mips64x.go b/libgo/go/runtime/os_linux_mips64x.go new file mode 100644 index 0000000..b7f737f --- /dev/null +++ b/libgo/go/runtime/os_linux_mips64x.go @@ -0,0 +1,21 @@ +// Copyright 2015 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 linux +// +build mips64 mips64le + +package runtime + +var randomNumber uint32 + +func archauxv(tag, val uintptr) { + switch tag { + case _AT_RANDOM: + // sysargs filled in startupRandomData, but that + // pointer may not be word aligned, so we must treat + // it as a byte array. + randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | + uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24 + } +} diff --git a/libgo/go/runtime/os_linux_mipsx.go b/libgo/go/runtime/os_linux_mipsx.go new file mode 100644 index 0000000..a2696de --- /dev/null +++ b/libgo/go/runtime/os_linux_mipsx.go @@ -0,0 +1,21 @@ +// Copyright 2016 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 linux +// +build mips mipsle + +package runtime + +var randomNumber uint32 + +func archauxv(tag, val uintptr) { + switch tag { + case _AT_RANDOM: + // sysargs filled in startupRandomData, but that + // pointer may not be word aligned, so we must treat + // it as a byte array. + randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | + uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24 + } +} diff --git a/libgo/go/runtime/os_linux_noauxv.go b/libgo/go/runtime/os_linux_noauxv.go new file mode 100644 index 0000000..895b4cd --- /dev/null +++ b/libgo/go/runtime/os_linux_noauxv.go @@ -0,0 +1,11 @@ +// Copyright 2014 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 linux +// +build !arm,!arm64,!mips,!mipsle,!mips64,!mips64le,!s390x,!ppc64,!ppc64le + +package runtime + +func archauxv(tag, val uintptr) { +} diff --git a/libgo/go/runtime/os_linux_ppc64x.go b/libgo/go/runtime/os_linux_ppc64x.go index d27902d..cc79cc4 100644 --- a/libgo/go/runtime/os_linux_ppc64x.go +++ b/libgo/go/runtime/os_linux_ppc64x.go @@ -2,27 +2,21 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build ignore_for_gccgo +// +build linux // +build ppc64 ppc64le package runtime -// For go:linkname -import _ "unsafe" - -// ppc64x doesn't have a 'cpuid' instruction equivalent and relies on -// HWCAP/HWCAP2 bits for hardware capabilities. - -//go:linkname cpu_hwcap internal/cpu.ppc64x_hwcap -//go:linkname cpu_hwcap2 internal/cpu.ppc64x_hwcap2 -var cpu_hwcap uint -var cpu_hwcap2 uint +import "internal/cpu" func archauxv(tag, val uintptr) { switch tag { case _AT_HWCAP: - cpu_hwcap = uint(val) + // ppc64x doesn't have a 'cpuid' instruction + // equivalent and relies on HWCAP/HWCAP2 bits for + // hardware capabilities. + cpu.HWCap = uint(val) case _AT_HWCAP2: - cpu_hwcap2 = uint(val) + cpu.HWCap2 = uint(val) } } diff --git a/libgo/go/runtime/os_linux_s390x.go b/libgo/go/runtime/os_linux_s390x.go new file mode 100644 index 0000000..55d35c7 --- /dev/null +++ b/libgo/go/runtime/os_linux_s390x.go @@ -0,0 +1,19 @@ +// Copyright 2016 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 runtime + +import "internal/cpu" + +const ( + // bit masks taken from bits/hwcap.h + _HWCAP_S390_VX = 2048 // vector facility +) + +func archauxv(tag, val uintptr) { + switch tag { + case _AT_HWCAP: // CPU capability bit flags + cpu.S390X.HasVX = val&_HWCAP_S390_VX != 0 + } +} diff --git a/libgo/go/runtime/os_netbsd.go b/libgo/go/runtime/os_netbsd.go index 81ebe76..ea47e5c 100644 --- a/libgo/go/runtime/os_netbsd.go +++ b/libgo/go/runtime/os_netbsd.go @@ -6,6 +6,7 @@ package runtime import ( "runtime/internal/atomic" + "runtime/internal/sys" "unsafe" ) diff --git a/libgo/go/runtime/os_openbsd.go b/libgo/go/runtime/os_openbsd.go index b64d3af..4f05665 100644 --- a/libgo/go/runtime/os_openbsd.go +++ b/libgo/go/runtime/os_openbsd.go @@ -6,6 +6,7 @@ package runtime import ( "runtime/internal/atomic" + "runtime/internal/sys" "unsafe" ) diff --git a/libgo/go/runtime/panic.go b/libgo/go/runtime/panic.go index 6b490b7..752bf71 100644 --- a/libgo/go/runtime/panic.go +++ b/libgo/go/runtime/panic.go @@ -39,7 +39,24 @@ func panicCheckMalloc(err error) { var indexError = error(errorString("index out of range")) +// The panicindex, panicslice, and panicdivide functions are called by +// code generated by the compiler for out of bounds index expressions, +// out of bounds slice expressions, and division by zero. The +// panicdivide (again), panicoverflow, panicfloat, and panicmem +// functions are called by the signal handler when a signal occurs +// indicating the respective problem. +// +// Since panicindex and panicslice are never called directly, and +// since the runtime package should never have an out of bounds slice +// or array reference, if we see those functions called from the +// runtime package we turn the panic into a throw. That will dump the +// entire runtime stack for easier debugging. + func panicindex() { + name, _, _ := funcfileline(getcallerpc(), -1) + if hasprefix(name, "runtime.") { + throw(string(indexError.(errorString))) + } panicCheckMalloc(indexError) panic(indexError) } @@ -47,6 +64,10 @@ func panicindex() { var sliceError = error(errorString("slice bounds out of range")) func panicslice() { + name, _, _ := funcfileline(getcallerpc(), -1) + if hasprefix(name, "runtime.") { + throw(string(sliceError.(errorString))) + } panicCheckMalloc(sliceError) panic(sliceError) } @@ -144,6 +165,12 @@ func newdefer() *_defer { // //go:nosplit func freedefer(d *_defer) { + if d._panic != nil { + freedeferpanic() + } + if d.pfn != 0 { + freedeferfn() + } pp := getg().m.p.ptr() if len(pp.deferpool) == cap(pp.deferpool) { // Transfer half of local cache to the central cache. @@ -176,15 +203,28 @@ func freedefer(d *_defer) { d.link = nil d.frame = nil d.panicStack = nil - d._panic = nil - d.pfn = 0 d.arg = nil d.retaddr = 0 d.makefunccanrecover = false + // d._panic and d.pfn must be nil already. + // If not, we would have called freedeferpanic or freedeferfn above, + // both of which throw. pp.deferpool = append(pp.deferpool, d) } +// Separate function so that it can split stack. +// Windows otherwise runs out of stack space. +func freedeferpanic() { + // _panic must be cleared before d is unlinked from gp. + throw("freedefer with d._panic != nil") +} + +func freedeferfn() { + // fn must be cleared before d is unlinked from gp. + throw("freedefer with d.fn != nil") +} + // deferreturn is called to undefer the stack. // The compiler inserts a call to this function as a finally clause // wrapped around the body of any function that calls defer. @@ -544,15 +584,9 @@ func gopanic(e interface{}) { // the world, we call preprintpanics to invoke all necessary Error // and String methods to prepare the panic strings before startpanic. preprintpanics(gp._panic) - startpanic() - - // startpanic set panicking, which will block main from exiting, - // so now OK to decrement runningPanicDefers. - atomic.Xadd(&runningPanicDefers, -1) - printpanics(gp._panic) - dopanic(0) // should not return - *(*int)(nil) = 0 // not reached + fatalpanic(gp._panic) // should not return + *(*int)(nil) = 0 // not reached } // currentDefer returns the top of the defer stack if it can be recovered. @@ -810,13 +844,16 @@ func sync_throw(s string) { //go:nosplit func throw(s string) { - print("fatal error: ", s, "\n") + // Everything throw does should be recursively nosplit so it + // can be called even when it's unsafe to grow the stack. + systemstack(func() { + print("fatal error: ", s, "\n") + }) gp := getg() if gp.m.throwing == 0 { gp.m.throwing = 1 } - startpanic() - dopanic(0) + fatalthrow() *(*int)(nil) = 0 // not reached } @@ -833,13 +870,76 @@ var panicking uint32 // so that two concurrent panics don't overlap their output. var paniclk mutex +// fatalthrow implements an unrecoverable runtime throw. It freezes the +// system, prints stack traces starting from its caller, and terminates the +// process. +// +//go:nosplit +func fatalthrow() { + pc := getcallerpc() + sp := getcallersp() + gp := getg() + + startpanic_m() + + if dopanic_m(gp, pc, sp) { + crash() + } + + exit(2) + + *(*int)(nil) = 0 // not reached +} + +// fatalpanic implements an unrecoverable panic. It is like fatalthrow, except +// that if msgs != nil, fatalpanic also prints panic messages and decrements +// runningPanicDefers once main is blocked from exiting. +// +//go:nosplit +func fatalpanic(msgs *_panic) { + pc := getcallerpc() + sp := getcallersp() + gp := getg() + var docrash bool + + if startpanic_m() && msgs != nil { + // There were panic messages and startpanic_m + // says it's okay to try to print them. + + // startpanic_m set panicking, which will + // block main from exiting, so now OK to + // decrement runningPanicDefers. + atomic.Xadd(&runningPanicDefers, -1) + + printpanics(msgs) + } + + docrash = dopanic_m(gp, pc, sp) + + if docrash { + // By crashing outside the above systemstack call, debuggers + // will not be confused when generating a backtrace. + // Function crash is marked nosplit to avoid stack growth. + crash() + } + + systemstack(func() { + exit(2) + }) + + *(*int)(nil) = 0 // not reached +} + // startpanic_m prepares for an unrecoverable panic. // +// It returns true if panic messages should be printed, or false if +// the runtime is in bad shape and should just print stacks. +// // It can have write barriers because the write barrier explicitly // ignores writes once dying > 0. // //go:yeswritebarrierrec -func startpanic() { +func startpanic_m() bool { _g_ := getg() if mheap_.cachealloc.size == 0 { // very early print("runtime: panic before malloc heap initialized\n") @@ -850,6 +950,12 @@ func startpanic() { // happen (even if we're not in one of these situations). _g_.m.mallocing++ + // If we're dying because of a bad lock count, set it to a + // good lock count so we don't recursively panic below. + if _g_.m.locks < 0 { + _g_.m.locks = 1 + } + switch _g_.m.dying { case 0: _g_.m.dying = 1 @@ -860,15 +966,13 @@ func startpanic() { schedtrace(true) } freezetheworld() - return + return true case 1: - // Something failed while panicking, probably the print of the - // argument to panic(). Just print a stack trace and exit. + // Something failed while panicking. + // Just print a stack trace and exit. _g_.m.dying = 2 print("panic during panic\n") - dopanic(0) - exit(3) - fallthrough + return false case 2: // This is a genuine bug in the runtime, we couldn't even // print the stack trace successfully. @@ -879,14 +983,14 @@ func startpanic() { default: // Can't even print! Just exit. exit(5) + return false // Need to return something. } } var didothers bool var deadlock mutex -func dopanic(unused int) { - gp := getg() +func dopanic_m(gp *g, pc, sp uintptr) bool { if gp.sig != 0 { signame := signame(gp.sig) if signame != "" { @@ -927,11 +1031,7 @@ func dopanic(unused int) { lock(&deadlock) } - if docrash { - crash() - } - - exit(2) + return docrash } // canpanic returns false if a signal should throw instead of @@ -951,7 +1051,7 @@ func canpanic(gp *g) bool { if gp == nil || gp != _m_.curg { return false } - if _m_.locks-_m_.softfloat != 0 || _m_.mallocing != 0 || _m_.throwing != 0 || _m_.preemptoff != "" || _m_.dying != 0 { + if _m_.locks != 0 || _m_.mallocing != 0 || _m_.throwing != 0 || _m_.preemptoff != "" || _m_.dying != 0 { return false } status := readgstatus(gp) @@ -960,3 +1060,14 @@ func canpanic(gp *g) bool { } return true } + +// isAbortPC returns true if pc is the program counter at which +// runtime.abort raises a signal. +// +// It is nosplit because it's part of the isgoexception +// implementation. +// +//go:nosplit +func isAbortPC(pc uintptr) bool { + return false +} diff --git a/libgo/go/runtime/pprof/internal/profile/encode.go b/libgo/go/runtime/pprof/internal/profile/encode.go index 6b879a8..af31933 100644 --- a/libgo/go/runtime/pprof/internal/profile/encode.go +++ b/libgo/go/runtime/pprof/internal/profile/encode.go @@ -197,6 +197,10 @@ var profileDecoder = []decoder{ }, // repeated int64 period = 12 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).Period) }, + // repeated int64 comment = 13 + func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Profile).commentX) }, + // int64 defaultSampleType = 14 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).defaultSampleTypeX) }, } // postDecode takes the unexported fields populated by decode (with @@ -278,6 +282,14 @@ func (p *Profile) postDecode() error { pt.Type, err = getString(p.stringTable, &pt.typeX, err) pt.Unit, err = getString(p.stringTable, &pt.unitX, err) } + for _, i := range p.commentX { + var c string + c, err = getString(p.stringTable, &i, err) + p.Comments = append(p.Comments, c) + } + + p.commentX = nil + p.DefaultSampleType, err = getString(p.stringTable, &p.defaultSampleTypeX, err) p.stringTable = nil return nil } diff --git a/libgo/go/runtime/pprof/internal/profile/profile.go b/libgo/go/runtime/pprof/internal/profile/profile.go index 9b6a6f9..64c3e3f 100644 --- a/libgo/go/runtime/pprof/internal/profile/profile.go +++ b/libgo/go/runtime/pprof/internal/profile/profile.go @@ -22,11 +22,13 @@ import ( // Profile is an in-memory representation of profile.proto. type Profile struct { - SampleType []*ValueType - Sample []*Sample - Mapping []*Mapping - Location []*Location - Function []*Function + SampleType []*ValueType + DefaultSampleType string + Sample []*Sample + Mapping []*Mapping + Location []*Location + Function []*Function + Comments []string DropFrames string KeepFrames string @@ -36,9 +38,11 @@ type Profile struct { PeriodType *ValueType Period int64 - dropFramesX int64 - keepFramesX int64 - stringTable []string + commentX []int64 + dropFramesX int64 + keepFramesX int64 + stringTable []string + defaultSampleTypeX int64 } // ValueType corresponds to Profile.ValueType diff --git a/libgo/go/runtime/pprof/pprof.go b/libgo/go/runtime/pprof/pprof.go index be4e869..5128c22 100644 --- a/libgo/go/runtime/pprof/pprof.go +++ b/libgo/go/runtime/pprof/pprof.go @@ -68,7 +68,7 @@ // all pprof commands. // // For more information about pprof, see -// https://github.com/google/pprof/blob/master/doc/pprof.md. +// https://github.com/google/pprof/blob/master/doc/README.md. package pprof import ( @@ -99,7 +99,8 @@ import ( // Each Profile has a unique name. A few profiles are predefined: // // goroutine - stack traces of all current goroutines -// heap - a sampling of all heap allocations +// heap - a sampling of memory allocations of live objects +// allocs - a sampling of all past memory allocations // threadcreate - stack traces that led to the creation of new OS threads // block - stack traces that led to blocking on synchronization primitives // mutex - stack traces of holders of contended mutexes @@ -114,6 +115,16 @@ import ( // all known allocations. This exception helps mainly in programs running // without garbage collection enabled, usually for debugging purposes. // +// The heap profile tracks both the allocation sites for all live objects in +// the application memory and for all objects allocated since the program start. +// Pprof's -inuse_space, -inuse_objects, -alloc_space, and -alloc_objects +// flags select which to display, defaulting to -inuse_space (live objects, +// scaled by size). +// +// The allocs profile is the same as the heap profile but changes the default +// pprof display to -alloc_space, the total number of bytes allocated since +// the program began (including garbage-collected bytes). +// // The CPU profile is not available as a Profile. It has a special API, // the StartCPUProfile and StopCPUProfile functions, because it streams // output to a writer during profiling. @@ -150,6 +161,12 @@ var heapProfile = &Profile{ write: writeHeap, } +var allocsProfile = &Profile{ + name: "allocs", + count: countHeap, // identical to heap profile + write: writeAlloc, +} + var blockProfile = &Profile{ name: "block", count: countBlock, @@ -170,6 +187,7 @@ func lockProfiles() { "goroutine": goroutineProfile, "threadcreate": threadcreateProfile, "heap": heapProfile, + "allocs": allocsProfile, "block": blockProfile, "mutex": mutexProfile, } @@ -525,6 +543,16 @@ func countHeap() int { // writeHeap writes the current runtime heap profile to w. func writeHeap(w io.Writer, debug int) error { + return writeHeapInternal(w, debug, "") +} + +// writeAlloc writes the current runtime heap profile to w +// with the total allocation space as the default sample type. +func writeAlloc(w io.Writer, debug int) error { + return writeHeapInternal(w, debug, "alloc_space") +} + +func writeHeapInternal(w io.Writer, debug int, defaultSampleType string) error { var memStats *runtime.MemStats if debug != 0 { // Read mem stats first, so that our other allocations @@ -555,7 +583,7 @@ func writeHeap(w io.Writer, debug int) error { } if debug == 0 { - return writeHeapProto(w, p, int64(runtime.MemProfileRate)) + return writeHeapProto(w, p, int64(runtime.MemProfileRate), defaultSampleType) } sort.Slice(p, func(i, j int) bool { return p[i].InUseBytes() > p[j].InUseBytes() }) diff --git a/libgo/go/runtime/pprof/pprof_test.go b/libgo/go/runtime/pprof/pprof_test.go index 02d99f5..74a7777 100644 --- a/libgo/go/runtime/pprof/pprof_test.go +++ b/libgo/go/runtime/pprof/pprof_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !nacl +// +build !nacl,!js package pprof @@ -732,7 +732,7 @@ func TestMutexProfile(t *testing.T) { return } // checking that the line is like "35258904 1 @ 0x48288d 0x47cd28 0x458931" - r2 := `^\d+ 1 @(?: 0x[[:xdigit:]]+)+` + r2 := `^\d+ \d+ @(?: 0x[[:xdigit:]]+)+` //r2 := "^[0-9]+ 1 @ 0x[0-9a-f x]+$" if ok, err := regexp.MatchString(r2, lines[3]); err != nil || !ok { t.Errorf("%q didn't match %q", lines[3], r2) @@ -862,16 +862,22 @@ func containsCounts(prof *profile.Profile, counts []int64) bool { return true } +var emptyCallStackTestRun int64 + // Issue 18836. func TestEmptyCallStack(t *testing.T) { + name := fmt.Sprintf("test18836_%d", emptyCallStackTestRun) + emptyCallStackTestRun++ + t.Parallel() var buf bytes.Buffer - p := NewProfile("test18836") + p := NewProfile(name) + p.Add("foo", 47674) p.WriteTo(&buf, 1) p.Remove("foo") got := buf.String() - prefix := "test18836 profile: total 1\n" + prefix := name + " profile: total 1\n" if !strings.HasPrefix(got, prefix) { t.Fatalf("got:\n\t%q\nwant prefix:\n\t%q\n", got, prefix) } diff --git a/libgo/go/runtime/pprof/proto.go b/libgo/go/runtime/pprof/proto.go index 793be44..d8456be 100644 --- a/libgo/go/runtime/pprof/proto.go +++ b/libgo/go/runtime/pprof/proto.go @@ -11,7 +11,6 @@ import ( "io" "io/ioutil" "runtime" - "sort" "strconv" "time" "unsafe" @@ -53,24 +52,43 @@ type profileBuilder struct { } type memMap struct { - start uintptr - end uintptr + // initialized as reading mapping + start uintptr + end uintptr + offset uint64 + file, buildID string + + funcs symbolizeFlag + fake bool // map entry was faked; /proc/self/maps wasn't available } +// symbolizeFlag keeps track of symbolization result. +// 0 : no symbol lookup was performed +// 1<<0 (lookupTried) : symbol lookup was performed +// 1<<1 (lookupFailed): symbol lookup was performed but failed +type symbolizeFlag uint8 + +const ( + lookupTried symbolizeFlag = 1 << iota + lookupFailed symbolizeFlag = 1 << iota +) + const ( // message Profile - tagProfile_SampleType = 1 // repeated ValueType - tagProfile_Sample = 2 // repeated Sample - tagProfile_Mapping = 3 // repeated Mapping - tagProfile_Location = 4 // repeated Location - tagProfile_Function = 5 // repeated Function - tagProfile_StringTable = 6 // repeated string - tagProfile_DropFrames = 7 // int64 (string table index) - tagProfile_KeepFrames = 8 // int64 (string table index) - tagProfile_TimeNanos = 9 // int64 - tagProfile_DurationNanos = 10 // int64 - tagProfile_PeriodType = 11 // ValueType (really optional string???) - tagProfile_Period = 12 // int64 + tagProfile_SampleType = 1 // repeated ValueType + tagProfile_Sample = 2 // repeated Sample + tagProfile_Mapping = 3 // repeated Mapping + tagProfile_Location = 4 // repeated Location + tagProfile_Function = 5 // repeated Function + tagProfile_StringTable = 6 // repeated string + tagProfile_DropFrames = 7 // int64 (string table index) + tagProfile_KeepFrames = 8 // int64 (string table index) + tagProfile_TimeNanos = 9 // int64 + tagProfile_DurationNanos = 10 // int64 + tagProfile_PeriodType = 11 // ValueType (really optional string???) + tagProfile_Period = 12 // int64 + tagProfile_Comment = 13 // repeated int64 + tagProfile_DefaultSampleType = 14 // int64 // message ValueType tagValueType_Type = 1 // int64 (string table index) @@ -174,7 +192,7 @@ func (b *profileBuilder) pbLine(tag int, funcID uint64, line int64) { } // pbMapping encodes a Mapping message to b.pb. -func (b *profileBuilder) pbMapping(tag int, id, base, limit, offset uint64, file, buildID string) { +func (b *profileBuilder) pbMapping(tag int, id, base, limit, offset uint64, file, buildID string, hasFuncs bool) { start := b.pb.startMessage() b.pb.uint64Opt(tagMapping_ID, id) b.pb.uint64Opt(tagMapping_Start, base) @@ -182,8 +200,15 @@ func (b *profileBuilder) pbMapping(tag int, id, base, limit, offset uint64, file b.pb.uint64Opt(tagMapping_Offset, offset) b.pb.int64Opt(tagMapping_Filename, b.stringIndex(file)) b.pb.int64Opt(tagMapping_BuildID, b.stringIndex(buildID)) - // TODO: Set any of HasInlineFrames, HasFunctions, HasFilenames, HasLineNumbers? - // It seems like they should all be true, but they've never been set. + // TODO: we set HasFunctions if all symbols from samples were symbolized (hasFuncs). + // Decide what to do about HasInlineFrames and HasLineNumbers. + // Also, another approach to handle the mapping entry with + // incomplete symbolization results is to dupliace the mapping + // entry (but with different Has* fields values) and use + // different entries for symbolized locations and unsymbolized locations. + if hasFuncs { + b.pb.bool(tagMapping_HasFunctions, true) + } b.pb.endMessage(tag, start) } @@ -208,6 +233,11 @@ func (b *profileBuilder) locForPC(addr uintptr) uint64 { return 0 } + symbolizeResult := lookupTried + if frame.PC == 0 || frame.Function == "" || frame.File == "" || frame.Line == 0 { + symbolizeResult |= lookupFailed + } + if frame.PC == 0 { // If we failed to resolve the frame, at least make up // a reasonable call PC. This mostly happens in tests. @@ -242,12 +272,14 @@ func (b *profileBuilder) locForPC(addr uintptr) uint64 { } frame, more = frames.Next() } - if len(b.mem) > 0 { - i := sort.Search(len(b.mem), func(i int) bool { - return b.mem[i].end > addr - }) - if i < len(b.mem) && b.mem[i].start <= addr && addr < b.mem[i].end { + for i := range b.mem { + if b.mem[i].start <= addr && addr < b.mem[i].end || b.mem[i].fake { b.pb.uint64Opt(tagLocation_MappingID, uint64(i+1)) + + m := b.mem[i] + m.funcs |= symbolizeResult + b.mem[i] = m + break } } b.pb.endMessage(tagProfile_Location, start) @@ -348,7 +380,7 @@ func (b *profileBuilder) addCPUData(data []uint64, tags []unsafe.Pointer) error } // build completes and returns the constructed profile. -func (b *profileBuilder) build() error { +func (b *profileBuilder) build() { b.end = time.Now() b.pb.int64Opt(tagProfile_TimeNanos, b.start.UnixNano()) @@ -395,13 +427,17 @@ func (b *profileBuilder) build() error { b.pbSample(values, locs, labels) } + for i, m := range b.mem { + hasFunctions := m.funcs == lookupTried // lookupTried but not lookupFailed + b.pbMapping(tagProfile_Mapping, uint64(i+1), uint64(m.start), uint64(m.end), m.offset, m.file, m.buildID, hasFunctions) + } + // TODO: Anything for tagProfile_DropFrames? // TODO: Anything for tagProfile_KeepFrames? b.pb.strings(tagProfile_StringTable, b.strings) b.zw.Write(b.pb.data) b.zw.Close() - return nil } // readMapping reads /proc/self/maps and writes mappings to b.pb. @@ -410,6 +446,12 @@ func (b *profileBuilder) build() error { func (b *profileBuilder) readMapping() { data, _ := ioutil.ReadFile("/proc/self/maps") parseProcSelfMaps(data, b.addMapping) + if len(b.mem) == 0 { // pprof expects a map entry, so fake one. + b.addMappingEntry(0, 0, 0, "", "", true) + // TODO(hyangah): make addMapping return *memMap or + // take a memMap struct, and get rid of addMappingEntry + // that takes a bunch of positional arguments. + } } func parseProcSelfMaps(data []byte, addMapping func(lo, hi, offset uint64, file, buildID string)) { @@ -510,6 +552,16 @@ func parseProcSelfMaps(data []byte, addMapping func(lo, hi, offset uint64, file, } func (b *profileBuilder) addMapping(lo, hi, offset uint64, file, buildID string) { - b.mem = append(b.mem, memMap{uintptr(lo), uintptr(hi)}) - b.pbMapping(tagProfile_Mapping, uint64(len(b.mem)), lo, hi, offset, file, buildID) + b.addMappingEntry(lo, hi, offset, file, buildID, false) +} + +func (b *profileBuilder) addMappingEntry(lo, hi, offset uint64, file, buildID string, fake bool) { + b.mem = append(b.mem, memMap{ + start: uintptr(lo), + end: uintptr(hi), + offset: offset, + file: file, + buildID: buildID, + fake: fake, + }) } diff --git a/libgo/go/runtime/pprof/proto_test.go b/libgo/go/runtime/pprof/proto_test.go index a268c3a..604628c 100644 --- a/libgo/go/runtime/pprof/proto_test.go +++ b/libgo/go/runtime/pprof/proto_test.go @@ -8,7 +8,10 @@ import ( "bytes" "encoding/json" "fmt" + "internal/testenv" "io/ioutil" + "os" + "os/exec" "reflect" "runtime" "runtime/pprof/internal/profile" @@ -63,7 +66,7 @@ func TestConvertCPUProfileEmpty(t *testing.T) { {Type: "cpu", Unit: "nanoseconds"}, } - checkProfile(t, p, 2000*1000, periodType, sampleType, nil) + checkProfile(t, p, 2000*1000, periodType, sampleType, nil, "") } // For gccgo make these functions different so that gccgo doesn't @@ -96,9 +99,16 @@ func testPCs(t *testing.T) (addr1, addr2 uint64, map1, map2 *profile.Mapping) { addr2 = mprof.Mapping[1].Start map2 = mprof.Mapping[1] map2.BuildID, _ = elfBuildID(map2.File) + case "js": + addr1 = uint64(funcPC(f1)) + addr2 = uint64(funcPC(f2)) default: addr1 = uint64(funcPC(f1)) addr2 = uint64(funcPC(f2)) + // Fake mapping - HasFunctions will be true because two PCs from Go + // will be fully symbolized. + fake := &profile.Mapping{ID: 1, HasFunctions: true} + map1, map2 = fake, fake } return } @@ -132,18 +142,23 @@ func TestConvertCPUProfile(t *testing.T) { {ID: 4, Mapping: map2, Address: addr2 + 1}, }}, } - checkProfile(t, p, period, periodType, sampleType, samples) + checkProfile(t, p, period, periodType, sampleType, samples, "") } -func checkProfile(t *testing.T, p *profile.Profile, period int64, periodType *profile.ValueType, sampleType []*profile.ValueType, samples []*profile.Sample) { +func checkProfile(t *testing.T, p *profile.Profile, period int64, periodType *profile.ValueType, sampleType []*profile.ValueType, samples []*profile.Sample, defaultSampleType string) { + t.Helper() + if p.Period != period { - t.Fatalf("p.Period = %d, want %d", p.Period, period) + t.Errorf("p.Period = %d, want %d", p.Period, period) } if !reflect.DeepEqual(p.PeriodType, periodType) { - t.Fatalf("p.PeriodType = %v\nwant = %v", fmtJSON(p.PeriodType), fmtJSON(periodType)) + t.Errorf("p.PeriodType = %v\nwant = %v", fmtJSON(p.PeriodType), fmtJSON(periodType)) } if !reflect.DeepEqual(p.SampleType, sampleType) { - t.Fatalf("p.SampleType = %v\nwant = %v", fmtJSON(p.SampleType), fmtJSON(sampleType)) + t.Errorf("p.SampleType = %v\nwant = %v", fmtJSON(p.SampleType), fmtJSON(sampleType)) + } + if defaultSampleType != p.DefaultSampleType { + t.Errorf("p.DefaultSampleType = %v\nwant = %v", p.DefaultSampleType, defaultSampleType) } // Clear line info since it is not in the expected samples. // If we used f1 and f2 above, then the samples will have line info. @@ -222,3 +237,114 @@ func TestProcSelfMaps(t *testing.T) { } } } + +// TestMapping checkes the mapping section of CPU profiles +// has the HasFunctions field set correctly. If all PCs included +// in the samples are successfully symbolized, the corresponding +// mapping entry (in this test case, only one entry) should have +// its HasFunctions field set true. +// The test generates a CPU profile that includes PCs from C side +// that the runtime can't symbolize. See ./testdata/mappingtest. +func TestMapping(t *testing.T) { + testenv.MustHaveGoRun(t) + testenv.MustHaveCGO(t) + + prog := "./testdata/mappingtest/main.go" + + // GoOnly includes only Go symbols that runtime will symbolize. + // Go+C includes C symbols that runtime will not symbolize. + for _, traceback := range []string{"GoOnly", "Go+C"} { + t.Run("traceback"+traceback, func(t *testing.T) { + cmd := exec.Command(testenv.GoToolPath(t), "run", prog) + if traceback != "GoOnly" { + cmd.Env = append(os.Environ(), "SETCGOTRACEBACK=1") + } + cmd.Stderr = new(bytes.Buffer) + + out, err := cmd.Output() + if err != nil { + t.Fatalf("failed to run the test program %q: %v\n%v", prog, err, cmd.Stderr) + } + + prof, err := profile.Parse(bytes.NewReader(out)) + if err != nil { + t.Fatalf("failed to parse the generated profile data: %v", err) + } + t.Logf("Profile: %s", prof) + + hit := make(map[*profile.Mapping]bool) + miss := make(map[*profile.Mapping]bool) + for _, loc := range prof.Location { + if symbolized(loc) { + hit[loc.Mapping] = true + } else { + miss[loc.Mapping] = true + } + } + if len(miss) == 0 { + t.Log("no location with missing symbol info was sampled") + } + + for _, m := range prof.Mapping { + if miss[m] && m.HasFunctions { + t.Errorf("mapping %+v has HasFunctions=true, but contains locations with failed symbolization", m) + continue + } + if !miss[m] && hit[m] && !m.HasFunctions { + t.Errorf("mapping %+v has HasFunctions=false, but all referenced locations from this lapping were symbolized successfully", m) + continue + } + } + }) + } +} + +func symbolized(loc *profile.Location) bool { + if len(loc.Line) == 0 { + return false + } + l := loc.Line[0] + f := l.Function + if l.Line == 0 || f == nil || f.Name == "" || f.Filename == "" { + return false + } + return true +} + +// TestFakeMapping tests if at least one mapping exists +// (including a fake mapping), and their HasFunctions bits +// are set correctly. +func TestFakeMapping(t *testing.T) { + var buf bytes.Buffer + if err := Lookup("heap").WriteTo(&buf, 0); err != nil { + t.Fatalf("failed to write heap profile: %v", err) + } + prof, err := profile.Parse(&buf) + if err != nil { + t.Fatalf("failed to parse the generated profile data: %v", err) + } + t.Logf("Profile: %s", prof) + if len(prof.Mapping) == 0 { + t.Fatal("want profile with at least one mapping entry, got 0 mapping") + } + + hit := make(map[*profile.Mapping]bool) + miss := make(map[*profile.Mapping]bool) + for _, loc := range prof.Location { + if symbolized(loc) { + hit[loc.Mapping] = true + } else { + miss[loc.Mapping] = true + } + } + for _, m := range prof.Mapping { + if miss[m] && m.HasFunctions { + t.Errorf("mapping %+v has HasFunctions=true, but contains locations with failed symbolization", m) + continue + } + if !miss[m] && hit[m] && !m.HasFunctions { + t.Errorf("mapping %+v has HasFunctions=false, but all referenced locations from this lapping were symbolized successfully", m) + continue + } + } +} diff --git a/libgo/go/runtime/pprof/protomem.go b/libgo/go/runtime/pprof/protomem.go index 2756cfd..82565d5 100644 --- a/libgo/go/runtime/pprof/protomem.go +++ b/libgo/go/runtime/pprof/protomem.go @@ -12,7 +12,7 @@ import ( ) // writeHeapProto writes the current heap profile in protobuf format to w. -func writeHeapProto(w io.Writer, p []runtime.MemProfileRecord, rate int64) error { +func writeHeapProto(w io.Writer, p []runtime.MemProfileRecord, rate int64, defaultSampleType string) error { b := newProfileBuilder(w) b.pbValueType(tagProfile_PeriodType, "space", "bytes") b.pb.int64Opt(tagProfile_Period, rate) @@ -20,6 +20,9 @@ func writeHeapProto(w io.Writer, p []runtime.MemProfileRecord, rate int64) error b.pbValueType(tagProfile_SampleType, "alloc_space", "bytes") b.pbValueType(tagProfile_SampleType, "inuse_objects", "count") b.pbValueType(tagProfile_SampleType, "inuse_space", "bytes") + if defaultSampleType != "" { + b.pb.int64Opt(tagProfile_DefaultSampleType, b.stringIndex(defaultSampleType)) + } values := []int64{0, 0, 0, 0} var locs []uint64 diff --git a/libgo/go/runtime/pprof/protomem_test.go b/libgo/go/runtime/pprof/protomem_test.go index 1e30ed9..315d5f0 100644 --- a/libgo/go/runtime/pprof/protomem_test.go +++ b/libgo/go/runtime/pprof/protomem_test.go @@ -14,7 +14,6 @@ import ( func TestConvertMemProfile(t *testing.T) { addr1, addr2, map1, map2 := testPCs(t) - var buf bytes.Buffer // MemProfileRecord stacks are return PCs, so add one to the // addresses recorded in the "profile". The proto profile // locations are call PCs, so conversion will subtract one @@ -27,15 +26,6 @@ func TestConvertMemProfile(t *testing.T) { {AllocBytes: 512 * 1024, FreeBytes: 512 * 1024, AllocObjects: 1, FreeObjects: 1, Stack0: [32]uintptr{a1 + 1, a1 + 2, a2 + 3}}, } - if err := writeHeapProto(&buf, rec, rate); err != nil { - t.Fatalf("writing profile: %v", err) - } - - p, err := profile.Parse(&buf) - if err != nil { - t.Fatalf("profile.Parse: %v", err) - } - periodType := &profile.ValueType{Type: "space", Unit: "bytes"} sampleType := []*profile.ValueType{ {Type: "alloc_objects", Unit: "count"}, @@ -70,5 +60,25 @@ func TestConvertMemProfile(t *testing.T) { NumLabel: map[string][]int64{"bytes": {829411}}, }, } - checkProfile(t, p, rate, periodType, sampleType, samples) + for _, tc := range []struct { + name string + defaultSampleType string + }{ + {"heap", ""}, + {"allocs", "alloc_space"}, + } { + t.Run(tc.name, func(t *testing.T) { + var buf bytes.Buffer + if err := writeHeapProto(&buf, rec, rate, tc.defaultSampleType); err != nil { + t.Fatalf("writing profile: %v", err) + } + + p, err := profile.Parse(&buf) + if err != nil { + t.Fatalf("profile.Parse: %v", err) + } + + checkProfile(t, p, rate, periodType, sampleType, samples, tc.defaultSampleType) + }) + } } diff --git a/libgo/go/runtime/pprof/testdata/mappingtest/main.go b/libgo/go/runtime/pprof/testdata/mappingtest/main.go new file mode 100644 index 0000000..7850faa --- /dev/null +++ b/libgo/go/runtime/pprof/testdata/mappingtest/main.go @@ -0,0 +1,105 @@ +// Copyright 2018 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 program outputs a CPU profile that includes +// both Go and Cgo stacks. This is used by the mapping info +// tests in runtime/pprof. +// +// If SETCGOTRACEBACK=1 is set, the CPU profile will includes +// PCs from C side but they will not be symbolized. +package main + +/* +#include <stdint.h> +#include <stdlib.h> + +int cpuHogCSalt1 = 0; +int cpuHogCSalt2 = 0; + +void CPUHogCFunction() { + int foo = cpuHogCSalt1; + int i; + for (i = 0; i < 100000; i++) { + if (foo > 0) { + foo *= foo; + } else { + foo *= foo + 1; + } + cpuHogCSalt2 = foo; + } +} + +struct CgoTracebackArg { + uintptr_t context; + uintptr_t sigContext; + uintptr_t *buf; + uintptr_t max; +}; + +void CollectCgoTraceback(void* parg) { + struct CgoTracebackArg* arg = (struct CgoTracebackArg*)(parg); + arg->buf[0] = (uintptr_t)(CPUHogCFunction); + arg->buf[1] = 0; +}; +*/ +import "C" + +import ( + "log" + "os" + "runtime" + "runtime/pprof" + "time" + "unsafe" +) + +func init() { + if v := os.Getenv("SETCGOTRACEBACK"); v == "1" { + // Collect some PCs from C-side, but don't symbolize. + runtime.SetCgoTraceback(0, unsafe.Pointer(C.CollectCgoTraceback), nil, nil) + } +} + +func main() { + go cpuHogGoFunction() + go cpuHogCFunction() + runtime.Gosched() + + if err := pprof.StartCPUProfile(os.Stdout); err != nil { + log.Fatal("can't start CPU profile: ", err) + } + time.Sleep(1 * time.Second) + pprof.StopCPUProfile() + + if err := os.Stdout.Close(); err != nil { + log.Fatal("can't write CPU profile: ", err) + } +} + +var salt1 int +var salt2 int + +func cpuHogGoFunction() { + // Generates CPU profile samples including a Go call path. + for { + foo := salt1 + for i := 0; i < 1e5; i++ { + if foo > 0 { + foo *= foo + } else { + foo *= foo + 1 + } + salt2 = foo + } + runtime.Gosched() + } +} + +func cpuHogCFunction() { + // Generates CPU profile samples including a Cgo call path. + for { + C.CPUHogCFunction() + runtime.Gosched() + } +} diff --git a/libgo/go/runtime/proc.go b/libgo/go/runtime/proc.go index 4fc45dd..77d379b 100644 --- a/libgo/go/runtime/proc.go +++ b/libgo/go/runtime/proc.go @@ -5,6 +5,7 @@ package runtime import ( + "internal/cpu" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -169,9 +170,11 @@ func main() { // Allow newproc to start new Ms. mainStarted = true - systemstack(func() { - newm(sysmon, nil) - }) + if GOARCH != "wasm" { // no threads on wasm yet, so no sysmon + systemstack(func() { + newm(sysmon, nil) + }) + } // Lock the main goroutine onto this, the main OS thread, // during initialization. Most programs won't care, but a few @@ -242,7 +245,7 @@ func main() { } } if atomic.Load(&panicking) != 0 { - gopark(nil, nil, "panicwait", traceEvGoStop, 1) + gopark(nil, nil, waitReasonPanicWait, traceEvGoStop, 1) } exit(0) @@ -276,7 +279,7 @@ func forcegchelper() { throw("forcegc: phase error") } atomic.Store(&forcegc.idle, 1) - goparkunlock(&forcegc.lock, "force gc (idle)", traceEvGoBlock, 1) + goparkunlock(&forcegc.lock, waitReasonForceGGIdle, traceEvGoBlock, 1) // this goroutine is explicitly resumed by sysmon if debug.gctrace > 0 { println("GC forced") @@ -291,6 +294,7 @@ func forcegchelper() { // Gosched yields the processor, allowing other goroutines to run. It does not // suspend the current goroutine, so execution resumes automatically. func Gosched() { + checkTimeouts() mcall(gosched_m) } @@ -305,7 +309,14 @@ func goschedguarded() { // If unlockf returns false, the goroutine is resumed. // unlockf must not access this G's stack, as it may be moved between // the call to gopark and the call to unlockf. -func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason string, traceEv byte, traceskip int) { +// Reason explains why the goroutine has been parked. +// It is displayed in stack traces and heap dumps. +// Reasons should be unique and descriptive. +// Do not re-use reasons, add new ones. +func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason waitReason, traceEv byte, traceskip int) { + if reason != waitReasonSleep { + checkTimeouts() // timeouts may expire while two goroutines keep the scheduler busy + } mp := acquirem() gp := mp.curg status := readgstatus(gp) @@ -324,7 +335,7 @@ func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason s // Puts the current goroutine into a waiting state and unlocks the lock. // The goroutine can be made runnable again by calling goready(gp). -func goparkunlock(lock *mutex, reason string, traceEv byte, traceskip int) { +func goparkunlock(lock *mutex, reason waitReason, traceEv byte, traceskip int) { gopark(parkunlock_c, unsafe.Pointer(lock), reason, traceEv, traceskip) } @@ -468,6 +479,37 @@ const ( _GoidCacheBatch = 16 ) +// cpuinit extracts the environment variable GODEBUGCPU from the environment on +// Linux and Darwin if the GOEXPERIMENT debugcpu was set and calls internal/cpu.Initialize. +func cpuinit() { + const prefix = "GODEBUGCPU=" + var env string + + if haveexperiment("debugcpu") && (GOOS == "linux" || GOOS == "darwin") { + cpu.DebugOptions = true + + // Similar to goenv_unix but extracts the environment value for + // GODEBUGCPU directly. + // TODO(moehrmann): remove when general goenvs() can be called before cpuinit() + n := int32(0) + for argv_index(argv, argc+1+n) != nil { + n++ + } + + for i := int32(0); i < n; i++ { + p := argv_index(argv, argc+1+i) + s := *(*string)(unsafe.Pointer(&stringStruct{unsafe.Pointer(p), findnull(p)})) + + if hasprefix(s, prefix) { + env = gostring(p)[len(prefix):] + break + } + } + } + + cpu.Initialize(env) +} + // The bootstrap sequence is: // // call osinit @@ -488,6 +530,7 @@ func schedinit() { mallocinit() mcommoninit(_g_.m) + cpuinit() // must run before alginit alginit() // maps must not be used before this call msigsave(_g_.m) @@ -778,7 +821,7 @@ func casgstatus(gp *g, oldval, newval uint32) { }) } - // See http://golang.org/cl/21503 for justification of the yield delay. + // See https://golang.org/cl/21503 for justification of the yield delay. const yieldDelay = 5 * 1000 var nextYield int64 @@ -786,9 +829,7 @@ func casgstatus(gp *g, oldval, newval uint32) { // GC time to finish and change the state to oldval. for i := 0; !atomic.Cas(&gp.atomicstatus, oldval, newval); i++ { if oldval == _Gwaiting && gp.atomicstatus == _Grunnable { - systemstack(func() { - throw("casgstatus: waiting for Gwaiting but is Grunnable") - }) + throw("casgstatus: waiting for Gwaiting but is Grunnable") } // Help GC if needed. // if gp.preemptscan && !gp.gcworkdone && (oldval == _Grunning || oldval == _Gsyscall) { @@ -826,7 +867,7 @@ func scang(gp *g, gcw *gcWork) { gp.gcscandone = false - // See http://golang.org/cl/21503 for justification of the yield delay. + // See https://golang.org/cl/21503 for justification of the yield delay. const yieldDelay = 10 * 1000 var nextYield int64 @@ -1212,7 +1253,9 @@ func mstart1() { //go:yeswritebarrierrec func mstartm0() { // Create an extra M for callbacks on threads not created by Go. - if iscgo && !cgoHasExtraM { + // An extra M is also needed on Windows for callbacks created by + // syscall.NewCallback. See issue #6751 for details. + if (iscgo || GOOS == "windows") && !cgoHasExtraM { cgoHasExtraM = true newextram() } @@ -1517,8 +1560,12 @@ func allocm(_p_ *p, fn func(), allocatestack bool) (mp *m, g0Stack unsafe.Pointe // put the m back on the list. //go:nosplit func needm(x byte) { - if iscgo && !cgoHasExtraM { + if (iscgo || GOOS == "windows") && !cgoHasExtraM { // Can happen if C/C++ code calls Go from a global ctor. + // Can also happen on Windows if a global ctor uses a + // callback created by syscall.NewCallback. See issue #6751 + // for details. + // // Can not throw, because scheduler is not initialized yet. write(2, unsafe.Pointer(&earlycgocallback[0]), int32(len(earlycgocallback))) exit(1) @@ -1814,13 +1861,16 @@ func newm1(mp *m) { // // The calling thread must itself be in a known-good state. func startTemplateThread() { + if GOARCH == "wasm" { // no threads on wasm yet + return + } if !atomic.Cas(&newmHandoff.haveTemplateThread, 0, 1) { return } newm(templateThread, nil) } -// tmeplateThread is a thread in a known-good state that exists solely +// templateThread is a thread in a known-good state that exists solely // to start new threads in known-good states when the calling thread // may not be a a good state. // @@ -2232,6 +2282,14 @@ stop: return gp, false } + // wasm only: + // Check if a goroutine is waiting for a callback from the WebAssembly host. + // If yes, pause the execution until a callback was triggered. + if pauseSchedulerUntilCallback() { + // A callback was triggered and caused at least one goroutine to wake up. + goto top + } + // Before we drop our P, make a snapshot of the allp slice, // which can change underfoot once we no longer block // safe-points. We don't need to snapshot the contents because @@ -2616,7 +2674,7 @@ func goexit0(gp *g) { gp._defer = nil // should be true already but just in case. gp._panic = nil // non-nil for Goexit during panic. points at stack-allocated data. gp.writebuf = nil - gp.waitreason = "" + gp.waitreason = 0 gp.param = nil gp.labels = nil gp.timer = nil @@ -2635,6 +2693,11 @@ func goexit0(gp *g) { gp.gcscanvalid = true dropg() + if GOARCH == "wasm" { // no threads yet on wasm + gfput(_g_.m.p.ptr(), gp) + schedule() // never returns + } + if _g_.m.lockedInt != 0 { print("invalid m->lockedInt = ", _g_.m.lockedInt, "\n") throw("internal lockOSThread error") @@ -2743,8 +2806,6 @@ func entersyscall_gcwait() { unlock(&sched.lock) } -// The same as reentersyscall(), but with a hint that the syscall is blocking. -//go:nosplit func reentersyscallblock(pc, sp uintptr) { _g_ := getg() @@ -2789,9 +2850,7 @@ func exitsyscall() { oldp := _g_.m.p.ptr() if exitsyscallfast() { if _g_.m.mcache == nil { - systemstack(func() { - throw("lost mcache") - }) + throw("lost mcache") } if trace.enabled { if oldp != _g_.m.p.ptr() || _g_.m.syscalltick != _g_.m.p.ptr().syscalltick { @@ -2836,9 +2895,7 @@ func exitsyscall() { mcall(exitsyscall0) if _g_.m.mcache == nil { - systemstack(func() { - throw("lost mcache") - }) + throw("lost mcache") } // Scheduler returned, so we're allowed to run now. @@ -3188,6 +3245,42 @@ func setSystemGoroutine() { atomic.Xadd(&expectedSystemGoroutines, -1) } +// saveAncestors copies previous ancestors of the given caller g and +// includes infor for the current caller into a new set of tracebacks for +// a g being created. +func saveAncestors(callergp *g) *[]ancestorInfo { + // Copy all prior info, except for the root goroutine (goid 0). + if debug.tracebackancestors <= 0 || callergp.goid == 0 { + return nil + } + var callerAncestors []ancestorInfo + if callergp.ancestors != nil { + callerAncestors = *callergp.ancestors + } + n := int32(len(callerAncestors)) + 1 + if n > debug.tracebackancestors { + n = debug.tracebackancestors + } + ancestors := make([]ancestorInfo, n) + copy(ancestors[1:], callerAncestors) + + var pcs [_TracebackMaxFrames]uintptr + // FIXME: This should get a traceback of callergp. + // npcs := gcallers(callergp, 0, pcs[:]) + npcs := 0 + ipcs := make([]uintptr, npcs) + copy(ipcs, pcs[:]) + ancestors[0] = ancestorInfo{ + pcs: ipcs, + goid: callergp.goid, + gopc: callergp.gopc, + } + + ancestorsp := new([]ancestorInfo) + *ancestorsp = ancestors + return ancestorsp +} + // Put on gfree list. // If local list is too long, transfer a batch to the global list. func gfput(_p_ *p, gp *g) { @@ -3265,6 +3358,9 @@ func Breakpoint() { // or else the m might be different in this function than in the caller. //go:nosplit func dolockOSThread() { + if GOARCH == "wasm" { + return // no threads on wasm yet + } _g_ := getg() _g_.m.lockedg.set(_g_) _g_.lockedm.set(_g_.m) @@ -3280,6 +3376,10 @@ func dolockOSThread() { // If the calling goroutine exits without unlocking the thread, // the thread will be terminated. // +// All init functions are run on the startup thread. Calling LockOSThread +// from an init function will cause the main function to be invoked on +// that thread. +// // A goroutine should call LockOSThread before calling OS services or // non-Go library functions that depend on per-thread state. func LockOSThread() { @@ -3309,6 +3409,9 @@ func lockOSThread() { // or else the m might be in different in this function than in the caller. //go:nosplit func dounlockOSThread() { + if GOARCH == "wasm" { + return // no threads on wasm yet + } _g_ := getg() if _g_.m.lockedInt != 0 || _g_.m.lockedExt != 0 { return @@ -3382,6 +3485,7 @@ func _ExternalCode() { _ExternalCode() } func _LostExternalCode() { _LostExternalCode() } func _GC() { _GC() } func _LostSIGPROFDuringAtomic64() { _LostSIGPROFDuringAtomic64() } +func _VDSO() { _VDSO() } // Counts SIGPROFs received while in atomic64 critical section, on mips{,le} var lostAtomic64Count uint64 @@ -3470,7 +3574,7 @@ func sigprof(pc uintptr, gp *g, mp *m) { } if prof.hz != 0 { - if (GOARCH == "mips" || GOARCH == "mipsle") && lostAtomic64Count > 0 { + if (GOARCH == "mips" || GOARCH == "mipsle" || GOARCH == "arm") && lostAtomic64Count > 0 { cpuprof.addLostAtomic64(lostAtomic64Count) lostAtomic64Count = 0 } @@ -3818,8 +3922,17 @@ func checkdead() { return } + // If we are not running under cgo, but we have an extra M then account + // for it. (It is possible to have an extra M on Windows without cgo to + // accommodate callbacks created by syscall.NewCallback. See issue #6751 + // for details.) + var run0 int32 + if !iscgo && cgoHasExtraM { + run0 = 1 + } + run := mcount() - sched.nmidle - sched.nmidlelocked - sched.nmsys - if run > 0 { + if run > run0 { return } if run < 0 { @@ -4215,7 +4328,7 @@ func schedtrace(detailed bool) { if lockedm != nil { id2 = lockedm.id } - print(" G", gp.goid, ": status=", readgstatus(gp), "(", gp.waitreason, ") m=", id1, " lockedm=", id2, "\n") + print(" G", gp.goid, ": status=", readgstatus(gp), "(", gp.waitreason.String(), ") m=", id1, " lockedm=", id2, "\n") } unlock(&allglock) unlock(&sched.lock) @@ -4375,7 +4488,7 @@ func runqempty(_p_ *p) bool { const randomizeScheduler = raceenabled // runqput tries to put g on the local runnable queue. -// If next if false, runqput adds g to the tail of the runnable queue. +// If next is false, runqput adds g to the tail of the runnable queue. // If next is true, runqput puts g in the _p_.runnext slot. // If the run queue is full, runnext puts g on the global queue. // Executed only by the owner P. @@ -4571,6 +4684,11 @@ func setMaxThreads(in int) (out int) { return } +func haveexperiment(name string) bool { + // The gofrontend does not support experiments. + return false +} + //go:nosplit func procPin() int { _g_ := getg() @@ -4618,7 +4736,7 @@ func sync_runtime_canSpin(i int) bool { // Spin only few times and only if running on a multicore machine and // GOMAXPROCS>1 and there is at least one other running P and local runq is empty. // As opposed to runtime mutex we don't do passive spinning here, - // because there can be work on global runq on on other Ps. + // because there can be work on global runq or on other Ps. if i >= active_spin || ncpu <= 1 || gomaxprocs <= int32(sched.npidle+sched.nmspinning)+1 { return false } diff --git a/libgo/go/runtime/proc_test.go b/libgo/go/runtime/proc_test.go index 672e1fa..82a2fe4 100644 --- a/libgo/go/runtime/proc_test.go +++ b/libgo/go/runtime/proc_test.go @@ -28,6 +28,9 @@ func perpetuumMobile() { } func TestStopTheWorldDeadlock(t *testing.T) { + if runtime.GOARCH == "wasm" { + t.Skip("no preemption on wasm yet") + } if testing.Short() { t.Skip("skipping during short test") } @@ -230,6 +233,10 @@ func TestBlockLocked(t *testing.T) { } func TestTimerFairness(t *testing.T) { + if runtime.GOARCH == "wasm" { + t.Skip("no preemption on wasm yet") + } + done := make(chan bool) c := make(chan bool) for i := 0; i < 2; i++ { @@ -256,6 +263,10 @@ func TestTimerFairness(t *testing.T) { } func TestTimerFairness2(t *testing.T) { + if runtime.GOARCH == "wasm" { + t.Skip("no preemption on wasm yet") + } + done := make(chan bool) c := make(chan bool) for i := 0; i < 2; i++ { @@ -290,7 +301,13 @@ var preempt = func() int { } func TestPreemption(t *testing.T) { - t.Skip("gccgo does not implement preemption") + if runtime.Compiler == "gccgo" { + t.Skip("gccgo does not implement preemption") + } + if runtime.GOARCH == "wasm" { + t.Skip("no preemption on wasm yet") + } + // Test that goroutines are preempted at function calls. N := 5 if testing.Short() { @@ -314,7 +331,13 @@ func TestPreemption(t *testing.T) { } func TestPreemptionGC(t *testing.T) { - t.Skip("gccgo does not implement preemption") + if runtime.Compiler == "gccgo" { + t.Skip("gccgo does not implement preemption") + } + if runtime.GOARCH == "wasm" { + t.Skip("no preemption on wasm yet") + } + // Test that pending GC preempts running goroutines. P := 5 N := 10 @@ -387,6 +410,9 @@ func TestNumGoroutine(t *testing.T) { } func TestPingPongHog(t *testing.T) { + if runtime.GOARCH == "wasm" { + t.Skip("no preemption on wasm yet") + } if testing.Short() { t.Skip("skipping in -short mode") } @@ -837,6 +863,10 @@ func TestStealOrder(t *testing.T) { } func TestLockOSThreadNesting(t *testing.T) { + if runtime.GOARCH == "wasm" { + t.Skip("no threads on wasm yet") + } + go func() { e, i := runtime.LockOSCounts() if e != 0 || i != 0 { diff --git a/libgo/go/runtime/rand_test.go b/libgo/go/runtime/rand_test.go index f8831b0..1b84c79 100644 --- a/libgo/go/runtime/rand_test.go +++ b/libgo/go/runtime/rand_test.go @@ -25,7 +25,7 @@ func BenchmarkFastrandHashiter(b *testing.B) { } b.RunParallel(func(pb *testing.PB) { for pb.Next() { - for _ = range m { + for range m { break } } diff --git a/libgo/go/runtime/runtime-lldb_test.go b/libgo/go/runtime/runtime-lldb_test.go index 9a28705..fe3a0eb 100644 --- a/libgo/go/runtime/runtime-lldb_test.go +++ b/libgo/go/runtime/runtime-lldb_test.go @@ -154,7 +154,9 @@ func TestLldbPython(t *testing.T) { t.Fatalf("failed to create file: %v", err) } - cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-N -l", "-o", "a.exe") + // As of 2018-07-17, lldb doesn't support compressed DWARF, so + // disable it for this test. + cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-N -l", "-ldflags=-compressdwarf=false", "-o", "a.exe") cmd.Dir = dir out, err := cmd.CombinedOutput() if err != nil { diff --git a/libgo/go/runtime/runtime1.go b/libgo/go/runtime/runtime1.go index b617f85..8b1b0a0 100644 --- a/libgo/go/runtime/runtime1.go +++ b/libgo/go/runtime/runtime1.go @@ -326,20 +326,21 @@ type dbgVar struct { // existing int var for that value, which may // already have an initial value. var debug struct { - allocfreetrace int32 - cgocheck int32 - efence int32 - gccheckmark int32 - gcpacertrace int32 - gcshrinkstackoff int32 - gcrescanstacks int32 - gcstoptheworld int32 - gctrace int32 - invalidptr int32 - sbrk int32 - scavenge int32 - scheddetail int32 - schedtrace int32 + allocfreetrace int32 + cgocheck int32 + efence int32 + gccheckmark int32 + gcpacertrace int32 + gcshrinkstackoff int32 + gcrescanstacks int32 + gcstoptheworld int32 + gctrace int32 + invalidptr int32 + sbrk int32 + scavenge int32 + scheddetail int32 + schedtrace int32 + tracebackancestors int32 } var dbgvars = []dbgVar{ @@ -357,6 +358,7 @@ var dbgvars = []dbgVar{ {"scavenge", &debug.scavenge}, {"scheddetail", &debug.scheddetail}, {"schedtrace", &debug.schedtrace}, + {"tracebackancestors", &debug.tracebackancestors}, } func parsedebugvars() { diff --git a/libgo/go/runtime/runtime2.go b/libgo/go/runtime/runtime2.go index 2de1cc8..e12e832 100644 --- a/libgo/go/runtime/runtime2.go +++ b/libgo/go/runtime/runtime2.go @@ -353,28 +353,29 @@ type g struct { atomicstatus uint32 // Not for gccgo: stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus goid int64 - waitsince int64 // approx time when the g become blocked - waitreason string // if status==Gwaiting schedlink guintptr - preempt bool // preemption signal, duplicates stackguard0 = stackpreempt - paniconfault bool // panic (instead of crash) on unexpected fault address - preemptscan bool // preempted g does scan for gc - gcscandone bool // g has scanned stack; protected by _Gscan bit in status - gcscanvalid bool // false at start of gc cycle, true if G has not run since last scan; TODO: remove? - throwsplit bool // must not split stack - raceignore int8 // ignore race detection events - sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine - sysexitticks int64 // cputicks when syscall has returned (for tracing) - traceseq uint64 // trace event sequencer - tracelastp puintptr // last P emitted an event for this goroutine + waitsince int64 // approx time when the g become blocked + waitreason waitReason // if status==Gwaiting + preempt bool // preemption signal, duplicates stackguard0 = stackpreempt + paniconfault bool // panic (instead of crash) on unexpected fault address + preemptscan bool // preempted g does scan for gc + gcscandone bool // g has scanned stack; protected by _Gscan bit in status + gcscanvalid bool // false at start of gc cycle, true if G has not run since last scan; TODO: remove? + throwsplit bool // must not split stack + raceignore int8 // ignore race detection events + sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine + sysexitticks int64 // cputicks when syscall has returned (for tracing) + traceseq uint64 // trace event sequencer + tracelastp puintptr // last P emitted an event for this goroutine lockedm muintptr sig uint32 writebuf []byte sigcode0 uintptr sigcode1 uintptr sigpc uintptr - gopc uintptr // pc of go statement that created this goroutine - startpc uintptr // pc of goroutine function + gopc uintptr // pc of go statement that created this goroutine + ancestors *[]ancestorInfo // ancestor information goroutine(s) that created this goroutine (only used if debug.tracebackancestors) + startpc uintptr // pc of goroutine function // Not for gccgo: racectx uintptr waiting *sudog // sudog structures this g is waiting on (that have a valid elem ptr); in lock order // Not for gccgo: cgoCtxt []uintptr // cgo traceback context @@ -476,15 +477,12 @@ type m struct { ncgo int32 // number of cgo calls currently in progress // Not for gccgo: cgoCallersUse uint32 // if non-zero, cgoCallers in use temporarily // Not for gccgo: cgoCallers *cgoCallers // cgo traceback if crashing in cgo call - park note - alllink *m // on allm - schedlink muintptr - mcache *mcache - lockedg guintptr - createstack [32]location // stack that created this thread. - // Not for gccgo: freglo [16]uint32 // d[i] lsb and f[i] - // Not for gccgo: freghi [16]uint32 // d[i] msb and f[i+16] - // Not for gccgo: fflag uint32 // floating point compare flags + park note + alllink *m // on allm + schedlink muintptr + mcache *mcache + lockedg guintptr + createstack [32]location // stack that created this thread. lockedExt uint32 // tracking for external LockOSThread lockedInt uint32 // tracking for internal lockOSThread nextwaitm muintptr // next m waiting for lock @@ -773,6 +771,13 @@ type _panic struct { aborted bool } +// ancestorInfo records details of where a goroutine was started. +type ancestorInfo struct { + pcs []uintptr // pcs from the stack of this goroutine + goid int64 // goroutine id of this goroutine; original goroutine possibly dead + gopc uintptr // pc of go statement that created this goroutine +} + const ( _TraceRuntimeFrames = 1 << iota // include frames for internal runtime functions. _TraceTrap // the initial PC, SP are from a trap, not a return PC from a call @@ -782,6 +787,71 @@ const ( // The maximum number of frames we print for a traceback const _TracebackMaxFrames = 100 +// A waitReason explains why a goroutine has been stopped. +// See gopark. Do not re-use waitReasons, add new ones. +type waitReason uint8 + +const ( + waitReasonZero waitReason = iota // "" + waitReasonGCAssistMarking // "GC assist marking" + waitReasonIOWait // "IO wait" + waitReasonChanReceiveNilChan // "chan receive (nil chan)" + waitReasonChanSendNilChan // "chan send (nil chan)" + waitReasonDumpingHeap // "dumping heap" + waitReasonGarbageCollection // "garbage collection" + waitReasonGarbageCollectionScan // "garbage collection scan" + waitReasonPanicWait // "panicwait" + waitReasonSelect // "select" + waitReasonSelectNoCases // "select (no cases)" + waitReasonGCAssistWait // "GC assist wait" + waitReasonGCSweepWait // "GC sweep wait" + waitReasonChanReceive // "chan receive" + waitReasonChanSend // "chan send" + waitReasonFinalizerWait // "finalizer wait" + waitReasonForceGGIdle // "force gc (idle)" + waitReasonSemacquire // "semacquire" + waitReasonSleep // "sleep" + waitReasonSyncCondWait // "sync.Cond.Wait" + waitReasonTimerGoroutineIdle // "timer goroutine (idle)" + waitReasonTraceReaderBlocked // "trace reader (blocked)" + waitReasonWaitForGCCycle // "wait for GC cycle" + waitReasonGCWorkerIdle // "GC worker (idle)" +) + +var waitReasonStrings = [...]string{ + waitReasonZero: "", + waitReasonGCAssistMarking: "GC assist marking", + waitReasonIOWait: "IO wait", + waitReasonChanReceiveNilChan: "chan receive (nil chan)", + waitReasonChanSendNilChan: "chan send (nil chan)", + waitReasonDumpingHeap: "dumping heap", + waitReasonGarbageCollection: "garbage collection", + waitReasonGarbageCollectionScan: "garbage collection scan", + waitReasonPanicWait: "panicwait", + waitReasonSelect: "select", + waitReasonSelectNoCases: "select (no cases)", + waitReasonGCAssistWait: "GC assist wait", + waitReasonGCSweepWait: "GC sweep wait", + waitReasonChanReceive: "chan receive", + waitReasonChanSend: "chan send", + waitReasonFinalizerWait: "finalizer wait", + waitReasonForceGGIdle: "force gc (idle)", + waitReasonSemacquire: "semacquire", + waitReasonSleep: "sleep", + waitReasonSyncCondWait: "sync.Cond.Wait", + waitReasonTimerGoroutineIdle: "timer goroutine (idle)", + waitReasonTraceReaderBlocked: "trace reader (blocked)", + waitReasonWaitForGCCycle: "wait for GC cycle", + waitReasonGCWorkerIdle: "GC worker (idle)", +} + +func (w waitReason) String() string { + if w < 0 || w >= waitReason(len(waitReasonStrings)) { + return "unknown wait reason" + } + return waitReasonStrings[w] +} + var ( allglen uintptr allm *m @@ -793,23 +863,7 @@ var ( sched schedt newprocs int32 - // Information about what cpu features are available. - // Set on startup in asm_{x86,amd64}.s. - // Packages outside the runtime should not use these - // as they are not an external api. - cpuid_ecx uint32 support_aes bool - - // cpuid_edx uint32 - // cpuid_ebx7 uint32 - // lfenceBeforeRdtsc bool - // support_avx bool - // support_avx2 bool - // support_bmi1 bool - // support_bmi2 bool - -// goarm uint8 // set by cmd/link on arm systems -// framepointer_enabled bool // set by cmd/link ) // Set by the linker so the runtime can determine the buildmode. diff --git a/libgo/go/runtime/runtime_test.go b/libgo/go/runtime/runtime_test.go index 0231043..995ce25 100644 --- a/libgo/go/runtime/runtime_test.go +++ b/libgo/go/runtime/runtime_test.go @@ -169,6 +169,9 @@ func testSetPanicOnFault(t *testing.T, addr uintptr, nfault *int) { if GOOS == "nacl" { t.Skip("nacl doesn't seem to fault on high addresses") } + if GOOS == "js" { + t.Skip("js does not support catching faults") + } defer func() { if err := recover(); err != nil { @@ -266,7 +269,7 @@ func TestTrailingZero(t *testing.T) { */ func TestBadOpen(t *testing.T) { - if GOOS == "windows" || GOOS == "nacl" { + if GOOS == "windows" || GOOS == "nacl" || GOOS == "js" { t.Skip("skipping OS that doesn't have open/read/write/close") } // make sure we get the correct error code if open fails. Same for diff --git a/libgo/go/runtime/rwmutex_test.go b/libgo/go/runtime/rwmutex_test.go index 872b3b0..291a32e 100644 --- a/libgo/go/runtime/rwmutex_test.go +++ b/libgo/go/runtime/rwmutex_test.go @@ -47,6 +47,9 @@ func doTestParallelReaders(numReaders int) { } func TestParallelRWMutexReaders(t *testing.T) { + if GOARCH == "wasm" { + t.Skip("wasm has no threads yet") + } defer GOMAXPROCS(GOMAXPROCS(-1)) // If runtime triggers a forced GC during this test then it will deadlock, // since the goroutines can't be stopped/preempted. diff --git a/libgo/go/runtime/select.go b/libgo/go/runtime/select.go index 9dab052..39c12da 100644 --- a/libgo/go/runtime/select.go +++ b/libgo/go/runtime/select.go @@ -94,7 +94,7 @@ func selparkcommit(gp *g, _ unsafe.Pointer) bool { } func block() { - gopark(nil, nil, "select (no cases)", traceEvGoStop, 1) // forever + gopark(nil, nil, waitReasonSelectNoCases, traceEvGoStop, 1) // forever } // selectgo implements the select statement. @@ -307,7 +307,7 @@ loop: // wait for someone to wake us up gp.param = nil - gopark(selparkcommit, nil, "select", traceEvGoBlockSelect, 1) + gopark(selparkcommit, nil, waitReasonSelect, traceEvGoBlockSelect, 1) sellock(scases, lockorder) diff --git a/libgo/go/runtime/sema.go b/libgo/go/runtime/sema.go index 6e2beec..cb7d3cd 100644 --- a/libgo/go/runtime/sema.go +++ b/libgo/go/runtime/sema.go @@ -15,7 +15,7 @@ // even if, due to races, the wakeup happens before the sleep. // // See Mullender and Cox, ``Semaphores in Plan 9,'' -// http://swtch.com/semaphore.pdf +// https://swtch.com/semaphore.pdf package runtime @@ -141,7 +141,7 @@ func semacquire1(addr *uint32, lifo bool, profile semaProfileFlags) { // Any semrelease after the cansemacquire knows we're waiting // (we set nwait above), so go to sleep. root.queue(addr, s, lifo) - goparkunlock(&root.lock, "semacquire", traceEvGoBlockSync, 4) + goparkunlock(&root.lock, waitReasonSemacquire, traceEvGoBlockSync, 4) if s.ticket != 0 || cansemacquire(addr) { break } @@ -274,7 +274,7 @@ func (root *semaRoot) queue(addr *uint32, s *sudog, lifo bool) { // addresses, it is kept balanced on average by maintaining a heap ordering // on the ticket: s.ticket <= both s.prev.ticket and s.next.ticket. // https://en.wikipedia.org/wiki/Treap - // http://faculty.washington.edu/aragon/pubs/rst89.pdf + // https://faculty.washington.edu/aragon/pubs/rst89.pdf // // s.ticket compared with zero in couple of places, therefore set lowest bit. // It will not affect treap's quality noticeably. @@ -507,7 +507,7 @@ func notifyListWait(l *notifyList, t uint32) { l.tail.next = s } l.tail = s - goparkunlock(&l.lock, "semacquire", traceEvGoBlockCond, 3) + goparkunlock(&l.lock, waitReasonSyncCondWait, traceEvGoBlockCond, 3) if t0 != 0 { blockevent(s.releasetime-t0, 2) } diff --git a/libgo/go/runtime/signal_sighandler.go b/libgo/go/runtime/signal_sighandler.go index 698629d..e4bf7bc 100644 --- a/libgo/go/runtime/signal_sighandler.go +++ b/libgo/go/runtime/signal_sighandler.go @@ -14,6 +14,11 @@ import ( // GOTRACEBACK=crash when a signal is received. var crashing int32 +// testSigtrap is used by the runtime tests. If non-nil, it is called +// on SIGTRAP. If it returns true, the normal behavior on SIGTRAP is +// suppressed. +var testSigtrap func(info *_siginfo_t, ctxt *sigctxt, gp *g) bool + // sighandler is invoked when a signal occurs. The global g will be // set to a gsignal goroutine and we will be running on the alternate // signal stack. The parameter g will be the value of the global g @@ -27,7 +32,7 @@ var crashing int32 //go:nowritebarrierrec func sighandler(sig uint32, info *_siginfo_t, ctxt unsafe.Pointer, gp *g) { _g_ := getg() - c := sigctxt{info, ctxt} + c := &sigctxt{info, ctxt} sigfault, sigpc := getSiginfo(info, ctxt) @@ -36,6 +41,10 @@ func sighandler(sig uint32, info *_siginfo_t, ctxt unsafe.Pointer, gp *g) { return } + if sig == _SIGTRAP && testSigtrap != nil && testSigtrap(info, (*sigctxt)(noescape(unsafe.Pointer(c))), gp) { + return + } + flags := int32(_SigThrow) if sig < uint32(len(sigtable)) { flags = sigtable[sig].flags @@ -45,6 +54,11 @@ func sighandler(sig uint32, info *_siginfo_t, ctxt unsafe.Pointer, gp *g) { // stack. Abort in the signal handler instead. flags = (flags &^ _SigPanic) | _SigThrow } + if isAbortPC(sigpc) { + // On many architectures, the abort function just + // causes a memory fault. Don't turn that into a panic. + flags = _SigThrow + } if c.sigcode() != _SI_USER && flags&_SigPanic != 0 { // Emulate gc by passing arguments out of band, // although we don't really have to. @@ -87,7 +101,7 @@ func sighandler(sig uint32, info *_siginfo_t, ctxt unsafe.Pointer, gp *g) { _g_.m.caughtsig.set(gp) if crashing == 0 { - startpanic() + startpanic_m() } if sig < uint32(len(sigtable)) { diff --git a/libgo/go/runtime/signal_unix.go b/libgo/go/runtime/signal_unix.go index a8f77fa..84623d3 100644 --- a/libgo/go/runtime/signal_unix.go +++ b/libgo/go/runtime/signal_unix.go @@ -112,6 +112,8 @@ func initsig(preinit bool) { // set SA_ONSTACK if necessary. if fwdSig[i] != _SIG_DFL && fwdSig[i] != _SIG_IGN { setsigstack(i) + } else if fwdSig[i] == _SIG_IGN { + sigInitIgnored(i) } continue } @@ -398,14 +400,6 @@ func dieFromSignal(sig uint32) { osyield() osyield() - // On Darwin we may still fail to die, because raise sends the - // signal to the whole process rather than just the current thread, - // and osyield just sleeps briefly rather than letting all other - // threads run. See issue 20315. Sleep longer. - if GOOS == "darwin" { - usleep(100) - } - // If we are still somehow running, just exit with the wrong status. exit(2) } @@ -444,7 +438,10 @@ func raisebadsignal(sig uint32, c *sigctxt) { // re-installing sighandler. At this point we can just // return and the signal will be re-raised and caught by // the default handler with the correct context. - if (isarchive || islibrary) && handler == _SIG_DFL && c.sigcode() != _SI_USER { + // + // On FreeBSD, the libthr sigaction code prevents + // this from working so we fall through to raise. + if GOOS != "freebsd" && (isarchive || islibrary) && handler == _SIG_DFL && c.sigcode() != _SI_USER { return } @@ -464,6 +461,7 @@ func raisebadsignal(sig uint32, c *sigctxt) { setsig(sig, getSigtramp()) } +//go:nosplit func crash() { if GOOS == "darwin" { // OS X core dumps are linear dumps of the mapped memory, diff --git a/libgo/go/runtime/sigqueue.go b/libgo/go/runtime/sigqueue.go index b108c39..cf926a9 100644 --- a/libgo/go/runtime/sigqueue.go +++ b/libgo/go/runtime/sigqueue.go @@ -237,7 +237,18 @@ func signal_ignore(s uint32) { atomic.Store(&sig.ignored[s/32], i) } +// sigInitIgnored marks the signal as already ignored. This is called at +// program start by initsig. In a shared library initsig is called by +// libpreinit, so the runtime may not be initialized yet. +//go:nosplit +func sigInitIgnored(s uint32) { + i := sig.ignored[s/32] + i |= 1 << (s & 31) + atomic.Store(&sig.ignored[s/32], i) +} + // Checked by signal handlers. +//go:linkname signal_ignored os_signal.signal_ignored func signal_ignored(s uint32) bool { i := atomic.Load(&sig.ignored[s/32]) return i&(1<<(s&31)) != 0 diff --git a/libgo/go/runtime/sizeof_test.go b/libgo/go/runtime/sizeof_test.go new file mode 100644 index 0000000..ecda82a --- /dev/null +++ b/libgo/go/runtime/sizeof_test.go @@ -0,0 +1,43 @@ +// Copyright 2018 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 !nacl + +package runtime_test + +import ( + "reflect" + "runtime" + "testing" + "unsafe" +) + +// Assert that the size of important structures do not change unexpectedly. + +func TestSizeof(t *testing.T) { + if runtime.Compiler != "gc" { + t.Skip("skipping size test; specific to gc compiler") + } + + const _64bit = unsafe.Sizeof(uintptr(0)) == 8 + + var tests = []struct { + val interface{} // type as a value + _32bit uintptr // size on 32bit platforms + _64bit uintptr // size on 64bit platforms + }{ + {runtime.G{}, 216, 376}, // g, but exported for testing + } + + for _, tt := range tests { + want := tt._32bit + if _64bit { + want = tt._64bit + } + got := reflect.TypeOf(tt.val).Size() + if want != got { + t.Errorf("unsafe.Sizeof(%T) = %d, want %d", tt.val, got, want) + } + } +} diff --git a/libgo/go/runtime/slice.go b/libgo/go/runtime/slice.go index ec5aa64..2e874cc 100644 --- a/libgo/go/runtime/slice.go +++ b/libgo/go/runtime/slice.go @@ -5,6 +5,7 @@ package runtime import ( + "runtime/internal/sys" "unsafe" ) @@ -34,14 +35,14 @@ type notInHeapSlice struct { // The index is the size of the slice element. var maxElems = [...]uintptr{ ^uintptr(0), - _MaxMem / 1, _MaxMem / 2, _MaxMem / 3, _MaxMem / 4, - _MaxMem / 5, _MaxMem / 6, _MaxMem / 7, _MaxMem / 8, - _MaxMem / 9, _MaxMem / 10, _MaxMem / 11, _MaxMem / 12, - _MaxMem / 13, _MaxMem / 14, _MaxMem / 15, _MaxMem / 16, - _MaxMem / 17, _MaxMem / 18, _MaxMem / 19, _MaxMem / 20, - _MaxMem / 21, _MaxMem / 22, _MaxMem / 23, _MaxMem / 24, - _MaxMem / 25, _MaxMem / 26, _MaxMem / 27, _MaxMem / 28, - _MaxMem / 29, _MaxMem / 30, _MaxMem / 31, _MaxMem / 32, + maxAlloc / 1, maxAlloc / 2, maxAlloc / 3, maxAlloc / 4, + maxAlloc / 5, maxAlloc / 6, maxAlloc / 7, maxAlloc / 8, + maxAlloc / 9, maxAlloc / 10, maxAlloc / 11, maxAlloc / 12, + maxAlloc / 13, maxAlloc / 14, maxAlloc / 15, maxAlloc / 16, + maxAlloc / 17, maxAlloc / 18, maxAlloc / 19, maxAlloc / 20, + maxAlloc / 21, maxAlloc / 22, maxAlloc / 23, maxAlloc / 24, + maxAlloc / 25, maxAlloc / 26, maxAlloc / 27, maxAlloc / 28, + maxAlloc / 29, maxAlloc / 30, maxAlloc / 31, maxAlloc / 32, } // maxSliceCap returns the maximum capacity for a slice. @@ -49,7 +50,15 @@ func maxSliceCap(elemsize uintptr) uintptr { if elemsize < uintptr(len(maxElems)) { return maxElems[elemsize] } - return _MaxMem / elemsize + return maxAlloc / elemsize +} + +func panicmakeslicelen() { + panic(errorString("makeslice: len out of range")) +} + +func panicmakeslicecap() { + panic(errorString("makeslice: cap out of range")) } func makeslice(et *_type, len, cap int) slice { @@ -60,11 +69,11 @@ func makeslice(et *_type, len, cap int) slice { // See issue 4085. maxElements := maxSliceCap(et.size) if len < 0 || uintptr(len) > maxElements { - panic(errorString("makeslice: len out of range")) + panicmakeslicelen() } if cap < len || uintptr(cap) > maxElements { - panic(errorString("makeslice: cap out of range")) + panicmakeslicecap() } p := mallocgc(et.size*uintptr(cap), et, true) @@ -74,12 +83,12 @@ func makeslice(et *_type, len, cap int) slice { func makeslice64(et *_type, len64, cap64 int64) slice { len := int(len64) if int64(len) != len64 { - panic(errorString("makeslice: len out of range")) + panicmakeslicelen() } cap := int(cap64) if int64(cap) != cap64 { - panic(errorString("makeslice: cap out of range")) + panicmakeslicecap() } return makeslice(et, len, cap) @@ -131,20 +140,36 @@ func growslice(et *_type, old slice, cap int) slice { var overflow bool var lenmem, newlenmem, capmem uintptr - const ptrSize = unsafe.Sizeof((*byte)(nil)) - switch et.size { - case 1: + // Specialize for common values of et.size. + // For 1 we don't need any division/multiplication. + // For sys.PtrSize, compiler will optimize division/multiplication into a shift by a constant. + // For powers of 2, use a variable shift. + switch { + case et.size == 1: lenmem = uintptr(old.len) newlenmem = uintptr(cap) capmem = roundupsize(uintptr(newcap)) - overflow = uintptr(newcap) > _MaxMem + overflow = uintptr(newcap) > maxAlloc newcap = int(capmem) - case ptrSize: - lenmem = uintptr(old.len) * ptrSize - newlenmem = uintptr(cap) * ptrSize - capmem = roundupsize(uintptr(newcap) * ptrSize) - overflow = uintptr(newcap) > _MaxMem/ptrSize - newcap = int(capmem / ptrSize) + case et.size == sys.PtrSize: + lenmem = uintptr(old.len) * sys.PtrSize + newlenmem = uintptr(cap) * sys.PtrSize + capmem = roundupsize(uintptr(newcap) * sys.PtrSize) + overflow = uintptr(newcap) > maxAlloc/sys.PtrSize + newcap = int(capmem / sys.PtrSize) + case isPowerOfTwo(et.size): + var shift uintptr + if sys.PtrSize == 8 { + // Mask shift for better code generation. + shift = uintptr(sys.Ctz64(uint64(et.size))) & 63 + } else { + shift = uintptr(sys.Ctz32(uint32(et.size))) & 31 + } + lenmem = uintptr(old.len) << shift + newlenmem = uintptr(cap) << shift + capmem = roundupsize(uintptr(newcap) << shift) + overflow = uintptr(newcap) > (maxAlloc >> shift) + newcap = int(capmem >> shift) default: lenmem = uintptr(old.len) * et.size newlenmem = uintptr(cap) * et.size @@ -167,7 +192,7 @@ func growslice(et *_type, old slice, cap int) slice { // s = append(s, d, d, d, d) // print(len(s), "\n") // } - if cap < old.cap || overflow || capmem > _MaxMem { + if cap < old.cap || overflow || capmem > maxAlloc { panic(errorString("growslice: cap out of range")) } @@ -193,6 +218,10 @@ func growslice(et *_type, old slice, cap int) slice { return slice{p, cap, newcap} } +func isPowerOfTwo(x uintptr) bool { + return x&(x-1) == 0 +} + func slicecopy(to, fm slice, width uintptr) int { if fm.len == 0 || to.len == 0 { return 0 diff --git a/libgo/go/runtime/append_test.go b/libgo/go/runtime/slice_test.go index ef1e812..c2dfb7a 100644 --- a/libgo/go/runtime/append_test.go +++ b/libgo/go/runtime/slice_test.go @@ -31,6 +31,12 @@ func BenchmarkGrowSlice(b *testing.B) { _ = append([]byte(nil), x...) } }) + b.Run("Int16", func(b *testing.B) { + x := make([]int16, 9) + for i := 0; i < b.N; i++ { + _ = append([]int16(nil), x...) + } + }) b.Run("Int", func(b *testing.B) { x := make([]int, 9) for i := 0; i < b.N; i++ { @@ -66,6 +72,36 @@ func BenchmarkGrowSlice(b *testing.B) { }) } +var ( + SinkIntSlice []int + SinkIntPointerSlice []*int +) + +func BenchmarkExtendSlice(b *testing.B) { + var length = 4 // Use a variable to prevent stack allocation of slices. + b.Run("IntSlice", func(b *testing.B) { + s := make([]int, 0, length) + for i := 0; i < b.N; i++ { + s = append(s[:0:length/2], make([]int, length)...) + } + SinkIntSlice = s + }) + b.Run("PointerSlice", func(b *testing.B) { + s := make([]*int, 0, length) + for i := 0; i < b.N; i++ { + s = append(s[:0:length/2], make([]*int, length)...) + } + SinkIntPointerSlice = s + }) + b.Run("NoGrow", func(b *testing.B) { + s := make([]int, 0, length) + for i := 0; i < b.N; i++ { + s = append(s[:0:length], make([]int, length)...) + } + SinkIntSlice = s + }) +} + func BenchmarkAppend(b *testing.B) { b.StopTimer() x := make([]int, 0, N) diff --git a/libgo/go/runtime/string.go b/libgo/go/runtime/string.go index e8df9a6..5296ebd 100644 --- a/libgo/go/runtime/string.go +++ b/libgo/go/runtime/string.go @@ -4,7 +4,10 @@ package runtime -import "unsafe" +import ( + "internal/bytealg" + "unsafe" +) // For gccgo, use go:linkname to rename compiler-called functions to // themselves, so that the compiler will export them. @@ -105,6 +108,11 @@ func slicebytetostring(buf *tmpBuf, b []byte) (str string) { if msanenabled { msanread(unsafe.Pointer(&b[0]), uintptr(l)) } + if l == 1 { + stringStructOf(&str).str = unsafe.Pointer(&staticbytes[b[0]]) + stringStructOf(&str).len = 1 + return + } var p unsafe.Pointer if buf != nil && len(b) <= len(buf) { @@ -232,8 +240,13 @@ func stringStructOf(sp *string) *stringStruct { return (*stringStruct)(unsafe.Pointer(sp)) } -func intstring(buf *[4]byte, v int64) string { - var s string +func intstring(buf *[4]byte, v int64) (s string) { + if v >= 0 && v < runeSelf { + stringStructOf(&s).str = unsafe.Pointer(&staticbytes[v]) + stringStructOf(&s).len = 1 + return + } + var b []byte if buf != nil { b = buf[:] @@ -277,7 +290,7 @@ func rawbyteslice(size int) (b []byte) { // rawruneslice allocates a new rune slice. The rune slice is not zeroed. func rawruneslice(size int) (b []rune) { - if uintptr(size) > _MaxMem/4 { + if uintptr(size) > maxAlloc/4 { throw("out of memory") } mem := roundupsize(uintptr(size) * 4) @@ -291,13 +304,20 @@ func rawruneslice(size int) (b []rune) { } // used by cmd/cgo -func gobytes(p *byte, n int) []byte { +func gobytes(p *byte, n int) (b []byte) { if n == 0 { return make([]byte, 0) } - x := make([]byte, n) - memmove(unsafe.Pointer(&x[0]), unsafe.Pointer(p), uintptr(n)) - return x + + if n < 0 || uintptr(n) > maxAlloc { + panic(errorString("gobytes: length out of range")) + } + + bp := mallocgc(uintptr(n), nil, false) + memmove(bp, unsafe.Pointer(p), uintptr(n)) + + *(*slice)(unsafe.Pointer(&b)) = slice{bp, n, n} + return } func gostring(p *byte) string { @@ -406,19 +426,50 @@ func findnull(s *byte) int { if s == nil { return 0 } - p := (*[_MaxMem/2 - 1]byte)(unsafe.Pointer(s)) - l := 0 - for p[l] != 0 { - l++ + + // Avoid IndexByteString on Plan 9 because it uses SSE instructions + // on x86 machines, and those are classified as floating point instructions, + // which are illegal in a note handler. + if GOOS == "plan9" { + p := (*[maxAlloc/2 - 1]byte)(unsafe.Pointer(s)) + l := 0 + for p[l] != 0 { + l++ + } + return l + } + + // pageSize is the unit we scan at a time looking for NULL. + // It must be the minimum page size for any architecture Go + // runs on. It's okay (just a minor performance loss) if the + // actual system page size is larger than this value. + const pageSize = 4096 + + offset := 0 + ptr := unsafe.Pointer(s) + // IndexByteString uses wide reads, so we need to be careful + // with page boundaries. Call IndexByteString on + // [ptr, endOfPage) interval. + safeLen := int(pageSize - uintptr(ptr)%pageSize) + + for { + t := *(*string)(unsafe.Pointer(&stringStruct{ptr, safeLen})) + // Check one page at a time. + if i := bytealg.IndexByteString(t, 0); i != -1 { + return offset + i + } + // Move to next page + ptr = unsafe.Pointer(uintptr(ptr) + uintptr(safeLen)) + offset += safeLen + safeLen = pageSize } - return l } func findnullw(s *uint16) int { if s == nil { return 0 } - p := (*[_MaxMem/2/2 - 1]uint16)(unsafe.Pointer(s)) + p := (*[maxAlloc/2/2 - 1]uint16)(unsafe.Pointer(s)) l := 0 for p[l] != 0 { l++ @@ -435,7 +486,7 @@ func gostringnocopy(str *byte) string { func gostringw(strw *uint16) string { var buf [8]byte - str := (*[_MaxMem/2/2 - 1]uint16)(unsafe.Pointer(strw)) + str := (*[maxAlloc/2/2 - 1]uint16)(unsafe.Pointer(strw)) n1 := 0 for i := 0; str[i] != 0; i++ { n1 += encoderune(buf[:], rune(str[i])) diff --git a/libgo/go/runtime/string_test.go b/libgo/go/runtime/string_test.go index 555a7fc..03327bb 100644 --- a/libgo/go/runtime/string_test.go +++ b/libgo/go/runtime/string_test.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" "testing" + "unicode/utf8" ) // Strings and slices that don't escape and fit into tmpBuf are stack allocated, @@ -110,6 +111,43 @@ var stringdata = []struct{ name, data string }{ {"MixedLength", "$Ѐࠀက퀀𐀀\U00040000\U0010FFFF"}, } +var sinkInt int + +func BenchmarkRuneCount(b *testing.B) { + // Each sub-benchmark counts the runes in a string in a different way. + b.Run("lenruneslice", func(b *testing.B) { + for _, sd := range stringdata { + b.Run(sd.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkInt += len([]rune(sd.data)) + } + }) + } + }) + b.Run("rangeloop", func(b *testing.B) { + for _, sd := range stringdata { + b.Run(sd.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + n := 0 + for range sd.data { + n++ + } + sinkInt += n + } + }) + } + }) + b.Run("utf8.RuneCountInString", func(b *testing.B) { + for _, sd := range stringdata { + b.Run(sd.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkInt += utf8.RuneCountInString(sd.data) + } + }) + } + }) +} + func BenchmarkRuneIterate(b *testing.B) { b.Run("range", func(b *testing.B) { for _, sd := range stringdata { @@ -125,7 +163,7 @@ func BenchmarkRuneIterate(b *testing.B) { for _, sd := range stringdata { b.Run(sd.name, func(b *testing.B) { for i := 0; i < b.N; i++ { - for _ = range sd.data { + for range sd.data { } } }) @@ -135,7 +173,7 @@ func BenchmarkRuneIterate(b *testing.B) { for _, sd := range stringdata { b.Run(sd.name, func(b *testing.B) { for i := 0; i < b.N; i++ { - for _, _ = range sd.data { + for range sd.data { } } }) diff --git a/libgo/go/runtime/stubs.go b/libgo/go/runtime/stubs.go index 1d21445..1aae4f3 100644 --- a/libgo/go/runtime/stubs.go +++ b/libgo/go/runtime/stubs.go @@ -69,8 +69,13 @@ func systemstack(fn func()) { } } +var badsystemstackMsg = "fatal: systemstack called from unexpected goroutine" + +//go:nosplit +//go:nowritebarrierrec func badsystemstack() { - throw("systemstack called from unexpected goroutine") + sp := stringStructOf(&badsystemstackMsg) + write(2, sp.str, int32(sp.len)) } // memclrNoHeapPointers clears n bytes starting at ptr. @@ -127,7 +132,7 @@ func fastrand() uint32 { //go:nosplit func fastrandn(n uint32) uint32 { // This is similar to fastrand() % n, but faster. - // See http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ + // See https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ return uint32(uint64(fastrand()) * uint64(n) >> 32) } @@ -198,7 +203,6 @@ func publicationBarrier() // getcallerpc returns the program counter (PC) of its caller's caller. // getcallersp returns the stack pointer (SP) of its caller's caller. -// argp must be a pointer to the caller's first function argument. // The implementation may be a compiler intrinsic; there is not // necessarily code implementing this on every platform. // @@ -213,10 +217,7 @@ func publicationBarrier() // the call to f (where f will return). // // The call to getcallerpc and getcallersp must be done in the -// frame being asked about. It would not be correct for f to pass &arg1 -// to another function g and let g call getcallerpc/getcallersp. -// The call inside g might return information about g's caller or -// information about f's caller or complete garbage. +// frame being asked about. // // The result of getcallersp is correct at the time of the return, // but it may be invalidated by any subsequent call to a function @@ -228,7 +229,7 @@ func publicationBarrier() func getcallerpc() uintptr //go:noescape -func getcallersp() uintptr +func getcallersp() uintptr // implemented as an intrinsic on all platforms func asmcgocall(fn, arg unsafe.Pointer) int32 { throw("asmcgocall") @@ -293,12 +294,6 @@ func setIsCgo() { } // For gccgo, to communicate from the C code to the Go code. -//go:linkname setCpuidECX runtime.setCpuidECX -func setCpuidECX(v uint32) { - cpuid_ecx = v -} - -// For gccgo, to communicate from the C code to the Go code. //go:linkname setSupportAES runtime.setSupportAES func setSupportAES(v bool) { support_aes = v @@ -336,6 +331,9 @@ func getSiginfo(*_siginfo_t, unsafe.Pointer) (sigaddr uintptr, sigpc uintptr) // Implemented in C for gccgo. func dumpregs(*_siginfo_t, unsafe.Pointer) +// Implemented in C for gccgo. +func setRandomNumber(uint32) + // Temporary for gccgo until we port proc.go. //go:linkname getsched runtime.getsched func getsched() *schedt { @@ -426,6 +424,15 @@ type bitvector struct { bytedata *uint8 } +// ptrbit returns the i'th bit in bv. +// ptrbit is less efficient than iterating directly over bitvector bits, +// and should only be used in non-performance-critical code. +// See adjustpointers for an example of a high-efficiency walk of a bitvector. +func (bv *bitvector) ptrbit(i uintptr) uint8 { + b := *(addb(bv.bytedata, i/8)) + return (b >> (i % 8)) & 1 +} + // bool2int returns 0 if x is false or 1 if x is true. func bool2int(x bool) int { if x { @@ -433,3 +440,10 @@ func bool2int(x bool) int { } return 0 } + +// abort crashes the runtime in situations where even throw might not +// work. In general it should do something a debugger will recognize +// (e.g., an INT3 on x86). A crash in abort is recognized by the +// signal handler, which will attempt to tear down the runtime +// immediately. +func abort() diff --git a/libgo/go/runtime/stubs2.go b/libgo/go/runtime/stubs2.go index e305b16..1cb910c 100644 --- a/libgo/go/runtime/stubs2.go +++ b/libgo/go/runtime/stubs2.go @@ -5,6 +5,8 @@ // +build !plan9 // +build !windows // +build !nacl +// +build !js +// +build !darwin package runtime @@ -16,7 +18,6 @@ func closefd(fd int32) int32 //extern exit func exit(code int32) -func nanotime() int64 func usleep(usec uint32) //go:noescape diff --git a/libgo/go/runtime/stubs3.go b/libgo/go/runtime/stubs3.go new file mode 100644 index 0000000..5c0786e --- /dev/null +++ b/libgo/go/runtime/stubs3.go @@ -0,0 +1,14 @@ +// Copyright 2018 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 !plan9 +// +build !solaris +// +build !windows +// +build !nacl +// +build !freebsd +// +build !darwin + +package runtime + +func nanotime() int64 diff --git a/libgo/go/runtime/symtab.go b/libgo/go/runtime/symtab.go index 12dc672..861921c 100644 --- a/libgo/go/runtime/symtab.go +++ b/libgo/go/runtime/symtab.go @@ -124,6 +124,7 @@ type funcID uint32 const ( funcID_normal funcID = iota // not a special function + funcID_runtime_main funcID_goexit funcID_jmpdefer funcID_mcall @@ -133,15 +134,13 @@ const ( funcID_asmcgocall funcID_sigpanic funcID_runfinq - funcID_bgsweep - funcID_forcegchelper - funcID_timerproc funcID_gcBgMarkWorker funcID_systemstack_switch funcID_systemstack funcID_cgocallback_gofunc funcID_gogo funcID_externalthreadhandler + funcID_debugCallV1 ) // FuncForPC returns a *Func describing the function that contains the diff --git a/libgo/go/runtime/sys_darwin.go b/libgo/go/runtime/sys_darwin.go new file mode 100644 index 0000000..7efbef7 --- /dev/null +++ b/libgo/go/runtime/sys_darwin.go @@ -0,0 +1,374 @@ +// Copyright 2018 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 runtime + +import "unsafe" + +// Call fn with arg as its argument. Return what fn returns. +// fn is the raw pc value of the entry point of the desired function. +// Switches to the system stack, if not already there. +// Preserves the calling point as the location where a profiler traceback will begin. +//go:nosplit +func libcCall(fn, arg unsafe.Pointer) int32 { + // Leave caller's PC/SP/G around for traceback. + gp := getg() + var mp *m + if gp != nil { + mp = gp.m + } + if mp != nil && mp.libcallsp == 0 { + mp.libcallg.set(gp) + mp.libcallpc = getcallerpc() + // sp must be the last, because once async cpu profiler finds + // all three values to be non-zero, it will use them + mp.libcallsp = getcallersp() + } else { + // Make sure we don't reset libcallsp. This makes + // libcCall reentrant; We remember the g/pc/sp for the + // first call on an M, until that libcCall instance + // returns. Reentrance only matters for signals, as + // libc never calls back into Go. The tricky case is + // where we call libcX from an M and record g/pc/sp. + // Before that call returns, a signal arrives on the + // same M and the signal handling code calls another + // libc function. We don't want that second libcCall + // from within the handler to be recorded, and we + // don't want that call's completion to zero + // libcallsp. + // We don't need to set libcall* while we're in a sighandler + // (even if we're not currently in libc) because we block all + // signals while we're handling a signal. That includes the + // profile signal, which is the one that uses the libcall* info. + mp = nil + } + res := asmcgocall(fn, arg) + if mp != nil { + mp.libcallsp = 0 + } + return res +} + +// The *_trampoline functions convert from the Go calling convention to the C calling convention +// and then call the underlying libc function. They are defined in sys_darwin_$ARCH.s. + +//go:nosplit +//go:cgo_unsafe_args +func pthread_attr_init(attr *pthreadattr) int32 { + return libcCall(unsafe.Pointer(funcPC(pthread_attr_init_trampoline)), unsafe.Pointer(&attr)) +} +func pthread_attr_init_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func pthread_attr_setstacksize(attr *pthreadattr, size uintptr) int32 { + return libcCall(unsafe.Pointer(funcPC(pthread_attr_setstacksize_trampoline)), unsafe.Pointer(&attr)) +} +func pthread_attr_setstacksize_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func pthread_attr_setdetachstate(attr *pthreadattr, state int) int32 { + return libcCall(unsafe.Pointer(funcPC(pthread_attr_setdetachstate_trampoline)), unsafe.Pointer(&attr)) +} +func pthread_attr_setdetachstate_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func pthread_create(attr *pthreadattr, start uintptr, arg unsafe.Pointer) int32 { + return libcCall(unsafe.Pointer(funcPC(pthread_create_trampoline)), unsafe.Pointer(&attr)) +} +func pthread_create_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func raise(sig uint32) { + libcCall(unsafe.Pointer(funcPC(raise_trampoline)), unsafe.Pointer(&sig)) +} +func raise_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func pthread_self() (t pthread) { + libcCall(unsafe.Pointer(funcPC(pthread_self_trampoline)), unsafe.Pointer(&t)) + return +} +func pthread_self_trampoline() + +func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (unsafe.Pointer, int) { + args := struct { + addr unsafe.Pointer + n uintptr + prot, flags, fd int32 + off uint32 + ret1 unsafe.Pointer + ret2 int + }{addr, n, prot, flags, fd, off, nil, 0} + libcCall(unsafe.Pointer(funcPC(mmap_trampoline)), unsafe.Pointer(&args)) + return args.ret1, args.ret2 +} +func mmap_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func munmap(addr unsafe.Pointer, n uintptr) { + libcCall(unsafe.Pointer(funcPC(munmap_trampoline)), unsafe.Pointer(&addr)) +} +func munmap_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func madvise(addr unsafe.Pointer, n uintptr, flags int32) { + libcCall(unsafe.Pointer(funcPC(madvise_trampoline)), unsafe.Pointer(&addr)) +} +func madvise_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func read(fd int32, p unsafe.Pointer, n int32) int32 { + return libcCall(unsafe.Pointer(funcPC(read_trampoline)), unsafe.Pointer(&fd)) +} +func read_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func closefd(fd int32) int32 { + return libcCall(unsafe.Pointer(funcPC(close_trampoline)), unsafe.Pointer(&fd)) +} +func close_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func exit(code int32) { + libcCall(unsafe.Pointer(funcPC(exit_trampoline)), unsafe.Pointer(&code)) +} +func exit_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func usleep(usec uint32) { + libcCall(unsafe.Pointer(funcPC(usleep_trampoline)), unsafe.Pointer(&usec)) +} +func usleep_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func write(fd uintptr, p unsafe.Pointer, n int32) int32 { + return libcCall(unsafe.Pointer(funcPC(write_trampoline)), unsafe.Pointer(&fd)) +} +func write_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func open(name *byte, mode, perm int32) (ret int32) { + return libcCall(unsafe.Pointer(funcPC(open_trampoline)), unsafe.Pointer(&name)) +} +func open_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func nanotime() int64 { + var r struct { + t int64 // raw timer + numer, denom uint32 // conversion factors. nanoseconds = t * numer / denom. + } + libcCall(unsafe.Pointer(funcPC(nanotime_trampoline)), unsafe.Pointer(&r)) + // Note: Apple seems unconcerned about overflow here. See + // https://developer.apple.com/library/content/qa/qa1398/_index.html + // Note also, numer == denom == 1 is common. + t := r.t + if r.numer != 1 { + t *= int64(r.numer) + } + if r.denom != 1 { + t /= int64(r.denom) + } + return t +} +func nanotime_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func walltime() (int64, int32) { + var t timeval + libcCall(unsafe.Pointer(funcPC(walltime_trampoline)), unsafe.Pointer(&t)) + return int64(t.tv_sec), 1000 * t.tv_usec +} +func walltime_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func sigaction(sig uint32, new *usigactiont, old *usigactiont) { + libcCall(unsafe.Pointer(funcPC(sigaction_trampoline)), unsafe.Pointer(&sig)) +} +func sigaction_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func sigprocmask(how uint32, new *sigset, old *sigset) { + libcCall(unsafe.Pointer(funcPC(sigprocmask_trampoline)), unsafe.Pointer(&how)) +} +func sigprocmask_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func sigaltstack(new *stackt, old *stackt) { + if new != nil && new.ss_flags&_SS_DISABLE != 0 && new.ss_size == 0 { + // Despite the fact that Darwin's sigaltstack man page says it ignores the size + // when SS_DISABLE is set, it doesn't. sigaltstack returns ENOMEM + // if we don't give it a reasonable size. + // ref: http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20140421/214296.html + new.ss_size = 32768 + } + libcCall(unsafe.Pointer(funcPC(sigaltstack_trampoline)), unsafe.Pointer(&new)) +} +func sigaltstack_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func raiseproc(sig uint32) { + libcCall(unsafe.Pointer(funcPC(raiseproc_trampoline)), unsafe.Pointer(&sig)) +} +func raiseproc_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func setitimer(mode int32, new, old *itimerval) { + libcCall(unsafe.Pointer(funcPC(setitimer_trampoline)), unsafe.Pointer(&mode)) +} +func setitimer_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32 { + return libcCall(unsafe.Pointer(funcPC(sysctl_trampoline)), unsafe.Pointer(&mib)) +} +func sysctl_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func fcntl(fd, cmd, arg int32) int32 { + return libcCall(unsafe.Pointer(funcPC(fcntl_trampoline)), unsafe.Pointer(&fd)) +} +func fcntl_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func kqueue() int32 { + v := libcCall(unsafe.Pointer(funcPC(kqueue_trampoline)), nil) + return v +} +func kqueue_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32 { + return libcCall(unsafe.Pointer(funcPC(kevent_trampoline)), unsafe.Pointer(&kq)) +} +func kevent_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func pthread_mutex_init(m *pthreadmutex, attr *pthreadmutexattr) int32 { + return libcCall(unsafe.Pointer(funcPC(pthread_mutex_init_trampoline)), unsafe.Pointer(&m)) +} +func pthread_mutex_init_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func pthread_mutex_lock(m *pthreadmutex) int32 { + return libcCall(unsafe.Pointer(funcPC(pthread_mutex_lock_trampoline)), unsafe.Pointer(&m)) +} +func pthread_mutex_lock_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func pthread_mutex_unlock(m *pthreadmutex) int32 { + return libcCall(unsafe.Pointer(funcPC(pthread_mutex_unlock_trampoline)), unsafe.Pointer(&m)) +} +func pthread_mutex_unlock_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func pthread_cond_init(c *pthreadcond, attr *pthreadcondattr) int32 { + return libcCall(unsafe.Pointer(funcPC(pthread_cond_init_trampoline)), unsafe.Pointer(&c)) +} +func pthread_cond_init_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func pthread_cond_wait(c *pthreadcond, m *pthreadmutex) int32 { + return libcCall(unsafe.Pointer(funcPC(pthread_cond_wait_trampoline)), unsafe.Pointer(&c)) +} +func pthread_cond_wait_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func pthread_cond_timedwait_relative_np(c *pthreadcond, m *pthreadmutex, t *timespec) int32 { + return libcCall(unsafe.Pointer(funcPC(pthread_cond_timedwait_relative_np_trampoline)), unsafe.Pointer(&c)) +} +func pthread_cond_timedwait_relative_np_trampoline() + +//go:nosplit +//go:cgo_unsafe_args +func pthread_cond_signal(c *pthreadcond) int32 { + return libcCall(unsafe.Pointer(funcPC(pthread_cond_signal_trampoline)), unsafe.Pointer(&c)) +} +func pthread_cond_signal_trampoline() + +// Not used on Darwin, but must be defined. +func exitThread(wait *uint32) { +} + +//go:nosplit +func closeonexec(fd int32) { + fcntl(fd, _F_SETFD, _FD_CLOEXEC) +} + +// Tell the linker that the libc_* functions are to be found +// in a system library, with the libc_ prefix missing. + +//go:cgo_import_dynamic libc_pthread_attr_init pthread_attr_init "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_pthread_attr_setstacksize pthread_attr_setstacksize "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_pthread_attr_setdetachstate pthread_attr_setdetachstate "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_pthread_create pthread_create "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_exit exit "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_raise raise "/usr/lib/libSystem.B.dylib" + +//go:cgo_import_dynamic libc_open open "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_close close "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_read read "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_write write "/usr/lib/libSystem.B.dylib" + +//go:cgo_import_dynamic libc_mmap mmap "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_munmap munmap "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_madvise madvise "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_error __error "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_usleep usleep "/usr/lib/libSystem.B.dylib" + +//go:cgo_import_dynamic libc_mach_timebase_info mach_timebase_info "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_mach_absolute_time mach_absolute_time "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_gettimeofday gettimeofday "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_sigaction sigaction "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_pthread_sigmask pthread_sigmask "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_sigaltstack sigaltstack "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_getpid getpid "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_kill kill "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_setitimer setitimer "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_fcntl fcntl "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_kqueue kqueue "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_kevent kevent "/usr/lib/libSystem.B.dylib" + +//go:cgo_import_dynamic libc_pthread_mutex_init pthread_mutex_init "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_pthread_mutex_lock pthread_mutex_lock "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_pthread_mutex_unlock pthread_mutex_unlock "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_pthread_cond_init pthread_cond_init "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_pthread_cond_wait pthread_cond_wait "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_pthread_cond_timedwait_relative_np pthread_cond_timedwait_relative_np "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_pthread_cond_signal pthread_cond_signal "/usr/lib/libSystem.B.dylib" + +// Magic incantation to get libSystem actually dynamically linked. +// TODO: Why does the code require this? See cmd/compile/internal/ld/go.go:210 +//go:cgo_import_dynamic _ _ "/usr/lib/libSystem.B.dylib" diff --git a/libgo/go/runtime/sys_wasm.go b/libgo/go/runtime/sys_wasm.go new file mode 100644 index 0000000..9bf710b --- /dev/null +++ b/libgo/go/runtime/sys_wasm.go @@ -0,0 +1,42 @@ +// Copyright 2018 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 runtime + +import ( + "runtime/internal/sys" + "unsafe" +) + +type m0Stack struct { + _ [8192 * sys.StackGuardMultiplier]byte +} + +var wasmStack m0Stack + +func wasmMove() + +func wasmZero() + +func wasmDiv() + +func wasmTruncS() +func wasmTruncU() + +func wasmExit(code int32) + +// adjust Gobuf as it if executed a call to fn with context ctxt +// and then did an immediate gosave. +func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) { + sp := buf.sp + if sys.RegSize > sys.PtrSize { + sp -= sys.PtrSize + *(*uintptr)(unsafe.Pointer(sp)) = 0 + } + sp -= sys.PtrSize + *(*uintptr)(unsafe.Pointer(sp)) = buf.pc + buf.sp = sp + buf.pc = uintptr(fn) + buf.ctxt = ctxt +} diff --git a/libgo/go/runtime/testdata/testprog/abort.go b/libgo/go/runtime/testdata/testprog/abort.go new file mode 100644 index 0000000..9e79d4d --- /dev/null +++ b/libgo/go/runtime/testdata/testprog/abort.go @@ -0,0 +1,23 @@ +// Copyright 2018 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 main + +import _ "unsafe" // for go:linkname + +func init() { + register("Abort", Abort) +} + +//go:linkname runtimeAbort runtime.abort +func runtimeAbort() + +func Abort() { + defer func() { + recover() + panic("BAD: recovered from abort") + }() + runtimeAbort() + println("BAD: after abort") +} diff --git a/libgo/go/runtime/testdata/testprog/numcpu_freebsd.go b/libgo/go/runtime/testdata/testprog/numcpu_freebsd.go index 035c534..42ee154 100644 --- a/libgo/go/runtime/testdata/testprog/numcpu_freebsd.go +++ b/libgo/go/runtime/testdata/testprog/numcpu_freebsd.go @@ -9,12 +9,17 @@ import ( "fmt" "os" "os/exec" + "regexp" "runtime" "strconv" "strings" "syscall" ) +var ( + cpuSetRE = regexp.MustCompile(`(\d,?)+`) +) + func init() { register("FreeBSDNumCPU", FreeBSDNumCPU) register("FreeBSDNumCPUHelper", FreeBSDNumCPUHelper) @@ -105,8 +110,12 @@ func checkNCPU(list []string) error { return fmt.Errorf("could not check against an empty CPU list") } + cListString := cpuSetRE.FindString(listString) + if len(cListString) == 0 { + return fmt.Errorf("invalid cpuset output '%s'", listString) + } // Launch FreeBSDNumCPUHelper() with specified CPUs list. - cmd := exec.Command("cpuset", "-l", listString, os.Args[0], "FreeBSDNumCPUHelper") + cmd := exec.Command("cpuset", "-l", cListString, os.Args[0], "FreeBSDNumCPUHelper") cmdline := strings.Join(cmd.Args, " ") output, err := cmd.CombinedOutput() if err != nil { @@ -120,7 +129,7 @@ func checkNCPU(list []string) error { return fmt.Errorf("fail to parse output from child '%s', error: %s, output: %s", cmdline, err, output) } if n != len(list) { - return fmt.Errorf("runtime.NumCPU() expected to %d, got %d when run with CPU list %s", len(list), n, listString) + return fmt.Errorf("runtime.NumCPU() expected to %d, got %d when run with CPU list %s", len(list), n, cListString) } return nil } diff --git a/libgo/go/runtime/testdata/testprog/timeprof.go b/libgo/go/runtime/testdata/testprog/timeprof.go new file mode 100644 index 0000000..0702885 --- /dev/null +++ b/libgo/go/runtime/testdata/testprog/timeprof.go @@ -0,0 +1,46 @@ +// Copyright 2018 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 main + +import ( + "fmt" + "io/ioutil" + "os" + "runtime/pprof" + "time" +) + +func init() { + register("TimeProf", TimeProf) +} + +func TimeProf() { + f, err := ioutil.TempFile("", "timeprof") + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } + + if err := pprof.StartCPUProfile(f); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } + + t0 := time.Now() + // We should get a profiling signal 100 times a second, + // so running for 1/10 second should be sufficient. + for time.Since(t0) < time.Second/10 { + } + + pprof.StopCPUProfile() + + name := f.Name() + if err := f.Close(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } + + fmt.Println(name) +} diff --git a/libgo/go/runtime/testdata/testprog/traceback_ancestors.go b/libgo/go/runtime/testdata/testprog/traceback_ancestors.go new file mode 100644 index 0000000..fe57c1c --- /dev/null +++ b/libgo/go/runtime/testdata/testprog/traceback_ancestors.go @@ -0,0 +1,53 @@ +// Copyright 2018 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 main + +import ( + "fmt" + "runtime" +) + +func init() { + register("TracebackAncestors", TracebackAncestors) +} + +const numGoroutines = 3 +const numFrames = 2 + +func TracebackAncestors() { + w := make(chan struct{}) + recurseThenCallGo(w, numGoroutines, numFrames) + <-w + printStack() + close(w) +} + +func printStack() { + buf := make([]byte, 1024) + for { + n := runtime.Stack(buf, true) + if n < len(buf) { + fmt.Print(string(buf[:n])) + return + } + buf = make([]byte, 2*len(buf)) + } +} + +func recurseThenCallGo(w chan struct{}, frames int, goroutines int) { + if frames == 0 { + // Signal to TracebackAncestors that we are done recursing and starting goroutines. + w <- struct{}{} + <-w + return + } + if goroutines == 0 { + // Start the next goroutine now that there are no more recursions left + // for this current goroutine. + go recurseThenCallGo(w, frames-1, numFrames) + return + } + recurseThenCallGo(w, frames, goroutines-1) +} diff --git a/libgo/go/runtime/testdata/testprogcgo/bigstack_windows.go b/libgo/go/runtime/testdata/testprogcgo/bigstack_windows.go new file mode 100644 index 0000000..f58fcf9 --- /dev/null +++ b/libgo/go/runtime/testdata/testprogcgo/bigstack_windows.go @@ -0,0 +1,27 @@ +// Copyright 2018 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 main + +/* +typedef void callback(char*); +extern void goBigStack1(char*); +extern void bigStack(callback*); +*/ +import "C" + +func init() { + register("BigStack", BigStack) +} + +func BigStack() { + // Create a large thread stack and call back into Go to test + // if Go correctly determines the stack bounds. + C.bigStack((*C.callback)(C.goBigStack1)) +} + +//export goBigStack1 +func goBigStack1(x *C.char) { + println("OK") +} diff --git a/libgo/go/runtime/testdata/testprogcgo/raceprof.go b/libgo/go/runtime/testdata/testprogcgo/raceprof.go index 466a367..0750ec1 100644 --- a/libgo/go/runtime/testdata/testprogcgo/raceprof.go +++ b/libgo/go/runtime/testdata/testprogcgo/raceprof.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build linux,amd64 +// +build linux,amd64 freebsd,amd64 // +build !gccgo package main diff --git a/libgo/go/runtime/testdata/testprogcgo/racesig.go b/libgo/go/runtime/testdata/testprogcgo/racesig.go index d0c1c3c..a079b3f 100644 --- a/libgo/go/runtime/testdata/testprogcgo/racesig.go +++ b/libgo/go/runtime/testdata/testprogcgo/racesig.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build linux,amd64 +// +build linux,amd64 freebsd,amd64 package main diff --git a/libgo/go/runtime/time.go b/libgo/go/runtime/time.go index b707590..a95d95b 100644 --- a/libgo/go/runtime/time.go +++ b/libgo/go/runtime/time.go @@ -98,8 +98,11 @@ func timeSleep(ns int64) { t.arg = gp tb := t.assignBucket() lock(&tb.lock) - tb.addtimerLocked(t) - goparkunlock(&tb.lock, "sleep", traceEvGoSleep, 2) + if !tb.addtimerLocked(t) { + unlock(&tb.lock) + badTimer() + } + goparkunlock(&tb.lock, waitReasonSleep, traceEvGoSleep, 2) } // startTimer adds t to the timer heap. @@ -128,14 +131,19 @@ func goroutineReady(arg interface{}, seq uintptr) { func addtimer(t *timer) { tb := t.assignBucket() lock(&tb.lock) - tb.addtimerLocked(t) + ok := tb.addtimerLocked(t) unlock(&tb.lock) + if !ok { + badTimer() + } } // Add a timer to the heap and start or kick timerproc if the new timer is // earlier than any of the others. // Timers are locked. -func (tb *timersBucket) addtimerLocked(t *timer) { +// Returns whether all is well: false if the data structure is corrupt +// due to user-level races. +func (tb *timersBucket) addtimerLocked(t *timer) bool { // when must never be negative; otherwise timerproc will overflow // during its delta calculation and never expire other runtime timers. if t.when < 0 { @@ -143,7 +151,9 @@ func (tb *timersBucket) addtimerLocked(t *timer) { } t.i = len(tb.t) tb.t = append(tb.t, t) - siftupTimer(tb.t, t.i) + if !siftupTimer(tb.t, t.i) { + return false + } if t.i == 0 { // siftup moved to top: new earliest deadline. if tb.sleeping { @@ -160,6 +170,7 @@ func (tb *timersBucket) addtimerLocked(t *timer) { expectSystemGoroutine() go timerproc(tb) } + return true } // Delete timer t from the heap. @@ -192,11 +203,19 @@ func deltimer(t *timer) bool { } tb.t[last] = nil tb.t = tb.t[:last] + ok := true if i != last { - siftupTimer(tb.t, i) - siftdownTimer(tb.t, i) + if !siftupTimer(tb.t, i) { + ok = false + } + if !siftdownTimer(tb.t, i) { + ok = false + } } unlock(&tb.lock) + if !ok { + badTimer() + } return true } @@ -222,10 +241,13 @@ func timerproc(tb *timersBucket) { if delta > 0 { break } + ok := true if t.period > 0 { // leave in heap but adjust next time to fire t.when += t.period * (1 + -delta/t.period) - siftdownTimer(tb.t, 0) + if !siftdownTimer(tb.t, 0) { + ok = false + } } else { // remove from heap last := len(tb.t) - 1 @@ -236,7 +258,9 @@ func timerproc(tb *timersBucket) { tb.t[last] = nil tb.t = tb.t[:last] if last > 0 { - siftdownTimer(tb.t, 0) + if !siftdownTimer(tb.t, 0) { + ok = false + } } t.i = -1 // mark as removed } @@ -244,6 +268,9 @@ func timerproc(tb *timersBucket) { arg := t.arg seq := t.seq unlock(&tb.lock) + if !ok { + badTimer() + } if raceenabled { raceacquire(unsafe.Pointer(t)) } @@ -253,7 +280,7 @@ func timerproc(tb *timersBucket) { if delta < 0 || faketime > 0 { // No timers left - put goroutine to sleep. tb.rescheduling = true - goparkunlock(&tb.lock, "timer goroutine (idle)", traceEvGoBlock, 1) + goparkunlock(&tb.lock, waitReasonTimerGoroutineIdle, traceEvGoBlock, 1) continue } // At least one timer pending. Sleep until then. @@ -329,8 +356,20 @@ func timeSleepUntil() int64 { } // Heap maintenance algorithms. - -func siftupTimer(t []*timer, i int) { +// These algorithms check for slice index errors manually. +// Slice index error can happen if the program is using racy +// access to timers. We don't want to panic here, because +// it will cause the program to crash with a mysterious +// "panic holding locks" message. Instead, we panic while not +// holding a lock. +// The races can occur despite the bucket locks because assignBucket +// itself is called without locks, so racy calls can cause a timer to +// change buckets while executing these functions. + +func siftupTimer(t []*timer, i int) bool { + if i >= len(t) { + return false + } when := t[i].when tmp := t[i] for i > 0 { @@ -346,10 +385,14 @@ func siftupTimer(t []*timer, i int) { t[i] = tmp t[i].i = i } + return true } -func siftdownTimer(t []*timer, i int) { +func siftdownTimer(t []*timer, i int) bool { n := len(t) + if i >= n { + return false + } when := t[i].when tmp := t[i] for { @@ -385,6 +428,15 @@ func siftdownTimer(t []*timer, i int) { t[i] = tmp t[i].i = i } + return true +} + +// badTimer is called if the timer data structures have been corrupted, +// presumably due to racy use by the program. We panic here rather than +// panicing due to invalid slice access while holding locks. +// See issue #25686. +func badTimer() { + panic(errorString("racy use of timers")) } // Entry points for net, time to call nanotime. diff --git a/libgo/go/runtime/timeasm.go b/libgo/go/runtime/timeasm.go index d5f5ea3..55b0d07 100644 --- a/libgo/go/runtime/timeasm.go +++ b/libgo/go/runtime/timeasm.go @@ -7,7 +7,7 @@ // so that time.now and nanotime return the same monotonic clock readings. // +build ignore -// +build darwin,amd64 darwin,386 windows +// +build windows package runtime diff --git a/libgo/go/runtime/timestub.go b/libgo/go/runtime/timestub.go index 033734e..9f1d111 100644 --- a/libgo/go/runtime/timestub.go +++ b/libgo/go/runtime/timestub.go @@ -5,15 +5,12 @@ // Declarations for operating systems implementing time.now // indirectly, in terms of walltime and nanotime assembly. -// -build !darwin !amd64,!386 // -build !windows package runtime import _ "unsafe" // for go:linkname -func walltime() (sec int64, nsec int32) - //go:linkname time_now time.now func time_now() (sec int64, nsec int32, mono int64) { sec, nsec = walltime() diff --git a/libgo/go/runtime/timestub2.go b/libgo/go/runtime/timestub2.go new file mode 100644 index 0000000..9ddc6fe --- /dev/null +++ b/libgo/go/runtime/timestub2.go @@ -0,0 +1,11 @@ +// Copyright 2018 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 +// +build !windows +// +build !freebsd + +package runtime + +func walltime() (sec int64, nsec int32) diff --git a/libgo/go/runtime/trace.go b/libgo/go/runtime/trace.go index e2bbb5d..7aed9a9 100644 --- a/libgo/go/runtime/trace.go +++ b/libgo/go/runtime/trace.go @@ -64,7 +64,14 @@ const ( traceEvGoBlockGC = 42 // goroutine blocks on GC assist [timestamp, stack] traceEvGCMarkAssistStart = 43 // GC mark assist start [timestamp, stack] traceEvGCMarkAssistDone = 44 // GC mark assist done [timestamp] - traceEvCount = 45 + traceEvUserTaskCreate = 45 // trace.NewContext [timestamp, internal task id, internal parent task id, stack, name string] + traceEvUserTaskEnd = 46 // end of a task [timestamp, internal task id, stack] + traceEvUserRegion = 47 // trace.WithRegion [timestamp, internal task id, mode(0:start, 1:end), stack, name string] + traceEvUserLog = 48 // trace.Log [timestamp, internal task id, key string id, stack, value string] + traceEvCount = 49 + // Byte is used but only 6 bits are available for event type. + // The remaining 2 bits are used to specify the number of arguments. + // That means, the max event type value is 63. ) const ( @@ -121,11 +128,13 @@ var trace struct { // Dictionary for traceEvString. // - // Currently this is used only at trace setup and for - // func/file:line info after tracing session, so we assume - // single-threaded access. - strings map[string]uint64 - stringSeq uint64 + // TODO: central lock to access the map is not ideal. + // option: pre-assign ids to all user annotation region names and tags + // option: per-P cache + // option: sync.Map like data structure + stringsLock mutex + strings map[string]uint64 + stringSeq uint64 // markWorkerLabels maps gcMarkWorkerMode to string ID. markWorkerLabels [len(gcMarkWorkerModeStrings)]uint64 @@ -379,12 +388,12 @@ func ReadTrace() []byte { trace.headerWritten = true trace.lockOwner = nil unlock(&trace.lock) - return []byte("go 1.10 trace\x00\x00\x00") + return []byte("go 1.11 trace\x00\x00\x00") } // Wait for new data. if trace.fullHead == 0 && !trace.shutdown { trace.reader.set(getg()) - goparkunlock(&trace.lock, "trace reader (blocked)", traceEvGoBlock, 2) + goparkunlock(&trace.lock, waitReasonTraceReaderBlocked, traceEvGoBlock, 2) lock(&trace.lock) } // Write a buffer. @@ -507,12 +516,26 @@ func traceEvent(ev byte, skip int, args ...uint64) { // so if we see trace.enabled == true now, we know it's true for the rest of the function. // Exitsyscall can run even during stopTheWorld. The race with StartTrace/StopTrace // during tracing in exitsyscall is resolved by locking trace.bufLock in traceLockBuffer. + // + // Note trace_userTaskCreate runs the same check. if !trace.enabled && !mp.startingtrace { traceReleaseBuffer(pid) return } + + if skip > 0 { + if getg() == mp.curg { + skip++ // +1 because stack is captured in traceEventLocked. + } + } + traceEventLocked(0, mp, pid, bufp, ev, skip, args...) + traceReleaseBuffer(pid) +} + +func traceEventLocked(extraBytes int, mp *m, pid int32, bufp *traceBufPtr, ev byte, skip int, args ...uint64) { buf := (*bufp).ptr() - const maxSize = 2 + 5*traceBytesPerNumber // event type, length, sequence, timestamp, stack id and two add params + // TODO: test on non-zero extraBytes param. + maxSize := 2 + 5*traceBytesPerNumber + extraBytes // event type, length, sequence, timestamp, stack id and two add params if buf == nil || len(buf.arr)-buf.pos < maxSize { buf = traceFlush(traceBufPtrOf(buf), pid).ptr() (*bufp).set(buf) @@ -555,7 +578,6 @@ func traceEvent(ev byte, skip int, args ...uint64) { // Fill in actual length. *lenp = byte(evSize - 2) } - traceReleaseBuffer(pid) } func traceStackID(mp *m, buf []location, skip int) uint64 { @@ -636,7 +658,20 @@ func traceString(bufp *traceBufPtr, pid int32, s string) (uint64, *traceBufPtr) if s == "" { return 0, bufp } + + lock(&trace.stringsLock) + if raceenabled { + // raceacquire is necessary because the map access + // below is race annotated. + raceacquire(unsafe.Pointer(&trace.stringsLock)) + } + if id, ok := trace.strings[s]; ok { + if raceenabled { + racerelease(unsafe.Pointer(&trace.stringsLock)) + } + unlock(&trace.stringsLock) + return id, bufp } @@ -644,6 +679,11 @@ func traceString(bufp *traceBufPtr, pid int32, s string) (uint64, *traceBufPtr) id := trace.stringSeq trace.strings[s] = id + if raceenabled { + racerelease(unsafe.Pointer(&trace.stringsLock)) + } + unlock(&trace.stringsLock) + // memory allocation in above may trigger tracing and // cause *bufp changes. Following code now works with *bufp, // so there must be no memory allocation or any activities @@ -657,8 +697,16 @@ func traceString(bufp *traceBufPtr, pid int32, s string) (uint64, *traceBufPtr) } buf.byte(traceEvString) buf.varint(id) - buf.varint(uint64(len(s))) - buf.pos += copy(buf.arr[buf.pos:], s) + + // double-check the string and the length can fit. + // Otherwise, truncate the string. + slen := len(s) + if room := len(buf.arr) - buf.pos; room < slen+traceBytesPerNumber { + slen = room + } + + buf.varint(uint64(slen)) + buf.pos += copy(buf.arr[buf.pos:], s[:slen]) (*bufp).set(buf) return id, bufp @@ -1091,3 +1139,78 @@ func traceNextGC() { traceEvent(traceEvNextGC, -1, memstats.next_gc) } } + +// To access runtime functions from runtime/trace. +// See runtime/trace/annotation.go + +//go:linkname trace_userTaskCreate runtime_trace.userTaskCreate +func trace_userTaskCreate(id, parentID uint64, taskType string) { + if !trace.enabled { + return + } + + // Same as in traceEvent. + mp, pid, bufp := traceAcquireBuffer() + if !trace.enabled && !mp.startingtrace { + traceReleaseBuffer(pid) + return + } + + typeStringID, bufp := traceString(bufp, pid, taskType) + traceEventLocked(0, mp, pid, bufp, traceEvUserTaskCreate, 3, id, parentID, typeStringID) + traceReleaseBuffer(pid) +} + +//go:linkname trace_userTaskEnd runtime_trace.userTaskEnd +func trace_userTaskEnd(id uint64) { + traceEvent(traceEvUserTaskEnd, 2, id) +} + +//go:linkname trace_userRegion runtime_trace.userRegion +func trace_userRegion(id, mode uint64, name string) { + if !trace.enabled { + return + } + + mp, pid, bufp := traceAcquireBuffer() + if !trace.enabled && !mp.startingtrace { + traceReleaseBuffer(pid) + return + } + + nameStringID, bufp := traceString(bufp, pid, name) + traceEventLocked(0, mp, pid, bufp, traceEvUserRegion, 3, id, mode, nameStringID) + traceReleaseBuffer(pid) +} + +//go:linkname trace_userLog runtime_trace.userLog +func trace_userLog(id uint64, category, message string) { + if !trace.enabled { + return + } + + mp, pid, bufp := traceAcquireBuffer() + if !trace.enabled && !mp.startingtrace { + traceReleaseBuffer(pid) + return + } + + categoryID, bufp := traceString(bufp, pid, category) + + extraSpace := traceBytesPerNumber + len(message) // extraSpace for the value string + traceEventLocked(extraSpace, mp, pid, bufp, traceEvUserLog, 3, id, categoryID) + // traceEventLocked reserved extra space for val and len(val) + // in buf, so buf now has room for the following. + buf := (*bufp).ptr() + + // double-check the message and its length can fit. + // Otherwise, truncate the message. + slen := len(message) + if room := len(buf.arr) - buf.pos; room < slen+traceBytesPerNumber { + slen = room + } + buf.varint(uint64(slen)) + buf.pos += copy(buf.arr[buf.pos:], message[:slen]) + + traceReleaseBuffer(pid) +} diff --git a/libgo/go/runtime/trace/annotation.go b/libgo/go/runtime/trace/annotation.go new file mode 100644 index 0000000..3545ef3 --- /dev/null +++ b/libgo/go/runtime/trace/annotation.go @@ -0,0 +1,196 @@ +package trace + +import ( + "context" + "fmt" + "sync/atomic" + _ "unsafe" +) + +type traceContextKey struct{} + +// NewTask creates a task instance with the type taskType and returns +// it along with a Context that carries the task. +// If the input context contains a task, the new task is its subtask. +// +// The taskType is used to classify task instances. Analysis tools +// like the Go execution tracer may assume there are only a bounded +// number of unique task types in the system. +// +// The returned end function is used to mark the task's end. +// The trace tool measures task latency as the time between task creation +// and when the end function is called, and provides the latency +// distribution per task type. +// If the end function is called multiple times, only the first +// call is used in the latency measurement. +// +// ctx, task := trace.NewTask(ctx, "awesome task") +// trace.WithRegion(ctx, prepWork) +// // preparation of the task +// go func() { // continue processing the task in a separate goroutine. +// defer task.End() +// trace.WithRegion(ctx, remainingWork) +// } +func NewTask(pctx context.Context, taskType string) (ctx context.Context, task *Task) { + pid := fromContext(pctx).id + id := newID() + userTaskCreate(id, pid, taskType) + s := &Task{id: id} + return context.WithValue(pctx, traceContextKey{}, s), s + + // We allocate a new task and the end function even when + // the tracing is disabled because the context and the detach + // function can be used across trace enable/disable boundaries, + // which complicates the problem. + // + // For example, consider the following scenario: + // - trace is enabled. + // - trace.WithRegion is called, so a new context ctx + // with a new region is created. + // - trace is disabled. + // - trace is enabled again. + // - trace APIs with the ctx is called. Is the ID in the task + // a valid one to use? + // + // TODO(hyangah): reduce the overhead at least when + // tracing is disabled. Maybe the id can embed a tracing + // round number and ignore ids generated from previous + // tracing round. +} + +func fromContext(ctx context.Context) *Task { + if s, ok := ctx.Value(traceContextKey{}).(*Task); ok { + return s + } + return &bgTask +} + +// Task is a data type for tracing a user-defined, logical operation. +type Task struct { + id uint64 + // TODO(hyangah): record parent id? +} + +// End marks the end of the operation represented by the Task. +func (t *Task) End() { + userTaskEnd(t.id) +} + +var lastTaskID uint64 = 0 // task id issued last time + +func newID() uint64 { + // TODO(hyangah): use per-P cache + return atomic.AddUint64(&lastTaskID, 1) +} + +var bgTask = Task{id: uint64(0)} + +// Log emits a one-off event with the given category and message. +// Category can be empty and the API assumes there are only a handful of +// unique categories in the system. +func Log(ctx context.Context, category, message string) { + id := fromContext(ctx).id + userLog(id, category, message) +} + +// Logf is like Log, but the value is formatted using the specified format spec. +func Logf(ctx context.Context, category, format string, args ...interface{}) { + if IsEnabled() { + // Ideally this should be just Log, but that will + // add one more frame in the stack trace. + id := fromContext(ctx).id + userLog(id, category, fmt.Sprintf(format, args...)) + } +} + +const ( + regionStartCode = uint64(0) + regionEndCode = uint64(1) +) + +// WithRegion starts a region associated with its calling goroutine, runs fn, +// and then ends the region. If the context carries a task, the region is +// associated with the task. Otherwise, the region is attached to the background +// task. +// +// The regionType is used to classify regions, so there should be only a +// handful of unique region types. +func WithRegion(ctx context.Context, regionType string, fn func()) { + // NOTE: + // WithRegion helps avoiding misuse of the API but in practice, + // this is very restrictive: + // - Use of WithRegion makes the stack traces captured from + // region start and end are identical. + // - Refactoring the existing code to use WithRegion is sometimes + // hard and makes the code less readable. + // e.g. code block nested deep in the loop with various + // exit point with return values + // - Refactoring the code to use this API with closure can + // cause different GC behavior such as retaining some parameters + // longer. + // This causes more churns in code than I hoped, and sometimes + // makes the code less readable. + + id := fromContext(ctx).id + userRegion(id, regionStartCode, regionType) + defer userRegion(id, regionEndCode, regionType) + fn() +} + +// StartRegion starts a region and returns a function for marking the +// end of the region. The returned Region's End function must be called +// from the same goroutine where the region was started. +// Within each goroutine, regions must nest. That is, regions started +// after this region must be ended before this region can be ended. +// Recommended usage is +// +// defer trace.StartRegion(ctx, "myTracedRegion").End() +// +func StartRegion(ctx context.Context, regionType string) *Region { + if !IsEnabled() { + return noopRegion + } + id := fromContext(ctx).id + userRegion(id, regionStartCode, regionType) + return &Region{id, regionType} +} + +// Region is a region of code whose execution time interval is traced. +type Region struct { + id uint64 + regionType string +} + +var noopRegion = &Region{} + +// End marks the end of the traced code region. +func (r *Region) End() { + if r == noopRegion { + return + } + userRegion(r.id, regionEndCode, r.regionType) +} + +// IsEnabled returns whether tracing is enabled. +// The information is advisory only. The tracing status +// may have changed by the time this function returns. +func IsEnabled() bool { + enabled := atomic.LoadInt32(&tracing.enabled) + return enabled == 1 +} + +// +// Function bodies are defined in runtime/trace.go +// + +// emits UserTaskCreate event. +func userTaskCreate(id, parentID uint64, taskType string) + +// emits UserTaskEnd event. +func userTaskEnd(id uint64) + +// emits UserRegion event. +func userRegion(id, mode uint64, regionType string) + +// emits UserLog event. +func userLog(id uint64, category, message string) diff --git a/libgo/go/runtime/trace/annotation_test.go b/libgo/go/runtime/trace/annotation_test.go new file mode 100644 index 0000000..71abbfc --- /dev/null +++ b/libgo/go/runtime/trace/annotation_test.go @@ -0,0 +1,152 @@ +package trace_test + +import ( + "bytes" + "context" + "fmt" + "internal/trace" + "reflect" + . "runtime/trace" + "strings" + "sync" + "testing" +) + +func BenchmarkStartRegion(b *testing.B) { + b.ReportAllocs() + ctx, task := NewTask(context.Background(), "benchmark") + defer task.End() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + StartRegion(ctx, "region").End() + } + }) +} + +func BenchmarkNewTask(b *testing.B) { + b.ReportAllocs() + pctx, task := NewTask(context.Background(), "benchmark") + defer task.End() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _, task := NewTask(pctx, "task") + task.End() + } + }) +} + +func TestUserTaskRegion(t *testing.T) { + if IsEnabled() { + t.Skip("skipping because -test.trace is set") + } + bgctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + preExistingRegion := StartRegion(bgctx, "pre-existing region") + + buf := new(bytes.Buffer) + if err := Start(buf); err != nil { + t.Fatalf("failed to start tracing: %v", err) + } + + // Beginning of traced execution + var wg sync.WaitGroup + ctx, task := NewTask(bgctx, "task0") // EvUserTaskCreate("task0") + wg.Add(1) + go func() { + defer wg.Done() + defer task.End() // EvUserTaskEnd("task0") + + WithRegion(ctx, "region0", func() { + // EvUserRegionCreate("region0", start) + WithRegion(ctx, "region1", func() { + Log(ctx, "key0", "0123456789abcdef") // EvUserLog("task0", "key0", "0....f") + }) + // EvUserRegion("region0", end) + }) + }() + + wg.Wait() + + preExistingRegion.End() + postExistingRegion := StartRegion(bgctx, "post-existing region") + + // End of traced execution + Stop() + + postExistingRegion.End() + + saveTrace(t, buf, "TestUserTaskRegion") + res, err := trace.Parse(buf, "") + if err == trace.ErrTimeOrder { + // golang.org/issues/16755 + t.Skipf("skipping trace: %v", err) + } + if err != nil { + t.Fatalf("Parse failed: %v", err) + } + + // Check whether we see all user annotation related records in order + type testData struct { + typ byte + strs []string + args []uint64 + setLink bool + } + + var got []testData + tasks := map[uint64]string{} + for _, e := range res.Events { + t.Logf("%s", e) + switch e.Type { + case trace.EvUserTaskCreate: + taskName := e.SArgs[0] + got = append(got, testData{trace.EvUserTaskCreate, []string{taskName}, nil, e.Link != nil}) + if e.Link != nil && e.Link.Type != trace.EvUserTaskEnd { + t.Errorf("Unexpected linked event %q->%q", e, e.Link) + } + tasks[e.Args[0]] = taskName + case trace.EvUserLog: + key, val := e.SArgs[0], e.SArgs[1] + taskName := tasks[e.Args[0]] + got = append(got, testData{trace.EvUserLog, []string{taskName, key, val}, nil, e.Link != nil}) + case trace.EvUserTaskEnd: + taskName := tasks[e.Args[0]] + got = append(got, testData{trace.EvUserTaskEnd, []string{taskName}, nil, e.Link != nil}) + if e.Link != nil && e.Link.Type != trace.EvUserTaskCreate { + t.Errorf("Unexpected linked event %q->%q", e, e.Link) + } + case trace.EvUserRegion: + taskName := tasks[e.Args[0]] + regionName := e.SArgs[0] + got = append(got, testData{trace.EvUserRegion, []string{taskName, regionName}, []uint64{e.Args[1]}, e.Link != nil}) + if e.Link != nil && (e.Link.Type != trace.EvUserRegion || e.Link.SArgs[0] != regionName) { + t.Errorf("Unexpected linked event %q->%q", e, e.Link) + } + } + } + want := []testData{ + {trace.EvUserTaskCreate, []string{"task0"}, nil, true}, + {trace.EvUserRegion, []string{"task0", "region0"}, []uint64{0}, true}, + {trace.EvUserRegion, []string{"task0", "region1"}, []uint64{0}, true}, + {trace.EvUserLog, []string{"task0", "key0", "0123456789abcdef"}, nil, false}, + {trace.EvUserRegion, []string{"task0", "region1"}, []uint64{1}, false}, + {trace.EvUserRegion, []string{"task0", "region0"}, []uint64{1}, false}, + {trace.EvUserTaskEnd, []string{"task0"}, nil, false}, + // Currently, pre-existing region is not recorded to avoid allocations. + // {trace.EvUserRegion, []string{"", "pre-existing region"}, []uint64{1}, false}, + {trace.EvUserRegion, []string{"", "post-existing region"}, []uint64{0}, false}, + } + if !reflect.DeepEqual(got, want) { + pretty := func(data []testData) string { + var s strings.Builder + for _, d := range data { + s.WriteString(fmt.Sprintf("\t%+v\n", d)) + } + return s.String() + } + t.Errorf("Got user region related events\n%+v\nwant:\n%+v", pretty(got), pretty(want)) + } +} diff --git a/libgo/go/runtime/trace/trace.go b/libgo/go/runtime/trace/trace.go index 439f998..7f9d72a 100644 --- a/libgo/go/runtime/trace/trace.go +++ b/libgo/go/runtime/trace/trace.go @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package trace contains facilities for programs to generate trace -// for Go execution tracer. +// Package trace contains facilities for programs to generate traces +// for the Go execution tracer. +// +// Tracing runtime activities // // The execution trace captures a wide range of execution events such as // goroutine creation/blocking/unblocking, syscall enter/exit/block, @@ -12,8 +14,6 @@ // captured for most events. The generated trace can be interpreted // using `go tool trace`. // -// Tracing a Go program -// // Support for tracing tests and benchmarks built with the standard // testing package is built into `go test`. For example, the following // command runs the test in the current directory and writes the trace @@ -25,24 +25,102 @@ // support to a standalone program. See the Example that demonstrates // how to use this API to enable tracing. // -// There is also a standard HTTP interface to profiling data. Adding the -// following line will install handlers under the /debug/pprof/trace URL -// to download live profiles: +// There is also a standard HTTP interface to trace data. Adding the +// following line will install a handler under the /debug/pprof/trace URL +// to download a live trace: // // import _ "net/http/pprof" // -// See the net/http/pprof package for more details. +// See the net/http/pprof package for more details about all of the +// debug endpoints installed by this import. +// +// User annotation +// +// Package trace provides user annotation APIs that can be used to +// log interesting events during execution. +// +// There are three types of user annotations: log messages, regions, +// and tasks. +// +// Log emits a timestamped message to the execution trace along with +// additional information such as the category of the message and +// which goroutine called Log. The execution tracer provides UIs to filter +// and group goroutines using the log category and the message supplied +// in Log. +// +// A region is for logging a time interval during a goroutine's execution. +// By definition, a region starts and ends in the same goroutine. +// Regions can be nested to represent subintervals. +// For example, the following code records four regions in the execution +// trace to trace the durations of sequential steps in a cappuccino making +// operation. +// +// trace.WithRegion(ctx, "makeCappuccino", func() { +// +// // orderID allows to identify a specific order +// // among many cappuccino order region records. +// trace.Log(ctx, "orderID", orderID) +// +// trace.WithRegion(ctx, "steamMilk", steamMilk) +// trace.WithRegion(ctx, "extractCoffee", extractCoffee) +// trace.WithRegion(ctx, "mixMilkCoffee", mixMilkCoffee) +// }) +// +// A task is a higher-level component that aids tracing of logical +// operations such as an RPC request, an HTTP request, or an +// interesting local operation which may require multiple goroutines +// working together. Since tasks can involve multiple goroutines, +// they are tracked via a context.Context object. NewTask creates +// a new task and embeds it in the returned context.Context object. +// Log messages and regions are attached to the task, if any, in the +// Context passed to Log and WithRegion. +// +// For example, assume that we decided to froth milk, extract coffee, +// and mix milk and coffee in separate goroutines. With a task, +// the trace tool can identify the goroutines involved in a specific +// cappuccino order. +// +// ctx, task := trace.NewTask(ctx, "makeCappuccino") +// trace.Log(ctx, "orderID", orderID) +// +// milk := make(chan bool) +// espresso := make(chan bool) +// +// go func() { +// trace.WithRegion(ctx, "steamMilk", steamMilk) +// milk <- true +// }() +// go func() { +// trace.WithRegion(ctx, "extractCoffee", extractCoffee) +// espresso <- true +// }() +// go func() { +// defer task.End() // When assemble is done, the order is complete. +// <-espresso +// <-milk +// trace.WithRegion(ctx, "mixMilkCoffee", mixMilkCoffee) +// }() +// +// +// The trace tool computes the latency of a task by measuring the +// time between the task creation and the task end and provides +// latency distributions for each task type found in the trace. package trace import ( "io" "runtime" + "sync" + "sync/atomic" ) // Start enables tracing for the current program. // While tracing, the trace will be buffered and written to w. // Start returns an error if tracing is already enabled. func Start(w io.Writer) error { + tracing.Lock() + defer tracing.Unlock() + if err := runtime.StartTrace(); err != nil { return err } @@ -55,11 +133,21 @@ func Start(w io.Writer) error { w.Write(data) } }() + atomic.StoreInt32(&tracing.enabled, 1) return nil } // Stop stops the current tracing, if any. // Stop only returns after all the writes for the trace have completed. func Stop() { + tracing.Lock() + defer tracing.Unlock() + atomic.StoreInt32(&tracing.enabled, 0) + runtime.StopTrace() } + +var tracing struct { + sync.Mutex // gate mutators (Start, Stop) + enabled int32 // accessed via atomic +} diff --git a/libgo/go/runtime/trace/trace_stack_test.go b/libgo/go/runtime/trace/trace_stack_test.go index 274cdf7..62c06e6 100644 --- a/libgo/go/runtime/trace/trace_stack_test.go +++ b/libgo/go/runtime/trace/trace_stack_test.go @@ -6,14 +6,17 @@ package trace_test import ( "bytes" + "fmt" "internal/testenv" "internal/trace" "net" "os" "runtime" . "runtime/trace" + "strings" "sync" "testing" + "text/tabwriter" "time" ) @@ -21,7 +24,7 @@ import ( // In particular that we strip bottom uninteresting frames like goexit, // top uninteresting frames (runtime guts). func TestTraceSymbolize(t *testing.T) { - testenv.MustHaveGoBuild(t) + skipTraceSymbolizeTestIfNecessary(t) buf := new(bytes.Buffer) if err := Start(buf); err != nil { @@ -34,28 +37,28 @@ func TestTraceSymbolize(t *testing.T) { // on a channel, in a select or otherwise. So we kick off goroutines // that need to block first in the hope that while we are executing // the rest of the test, they will block. - go func() { + go func() { // func1 select {} }() - go func() { + go func() { // func2 var c chan int c <- 0 }() - go func() { + go func() { // func3 var c chan int <-c }() done1 := make(chan bool) - go func() { + go func() { // func4 <-done1 }() done2 := make(chan bool) - go func() { + go func() { // func5 done2 <- true }() c1 := make(chan int) c2 := make(chan int) - go func() { + go func() { // func6 select { case <-c1: case <-c2: @@ -63,17 +66,17 @@ func TestTraceSymbolize(t *testing.T) { }() var mu sync.Mutex mu.Lock() - go func() { + go func() { // func7 mu.Lock() mu.Unlock() }() var wg sync.WaitGroup wg.Add(1) - go func() { + go func() { // func8 wg.Wait() }() cv := sync.NewCond(&sync.Mutex{}) - go func() { + go func() { // func9 cv.L.Lock() cv.Wait() cv.L.Unlock() @@ -82,7 +85,7 @@ func TestTraceSymbolize(t *testing.T) { if err != nil { t.Fatalf("failed to listen: %v", err) } - go func() { + go func() { // func10 c, err := ln.Accept() if err != nil { t.Errorf("failed to accept: %v", err) @@ -97,7 +100,7 @@ func TestTraceSymbolize(t *testing.T) { defer rp.Close() defer wp.Close() pipeReadDone := make(chan bool) - go func() { + go func() { // func11 var data [1]byte rp.Read(data[:]) pipeReadDone <- true @@ -125,14 +128,16 @@ func TestTraceSymbolize(t *testing.T) { wp.Write(data[:]) <-pipeReadDone + oldGoMaxProcs := runtime.GOMAXPROCS(0) + runtime.GOMAXPROCS(oldGoMaxProcs + 1) + Stop() + + runtime.GOMAXPROCS(oldGoMaxProcs) + events, _ := parseTrace(t, buf) // Now check that the stacks are correct. - type frame struct { - Fn string - Line int - } type eventDesc struct { Type byte Stk []frame @@ -140,90 +145,96 @@ func TestTraceSymbolize(t *testing.T) { want := []eventDesc{ {trace.EvGCStart, []frame{ {"runtime.GC", 0}, - {"runtime/trace_test.TestTraceSymbolize", 107}, + {"runtime/trace_test.TestTraceSymbolize", 0}, {"testing.tRunner", 0}, }}, {trace.EvGoStart, []frame{ - {"runtime/trace_test.TestTraceSymbolize.func1", 37}, + {"runtime/trace_test.TestTraceSymbolize.func1", 0}, }}, {trace.EvGoSched, []frame{ - {"runtime/trace_test.TestTraceSymbolize", 108}, + {"runtime/trace_test.TestTraceSymbolize", 111}, {"testing.tRunner", 0}, }}, {trace.EvGoCreate, []frame{ - {"runtime/trace_test.TestTraceSymbolize", 37}, + {"runtime/trace_test.TestTraceSymbolize", 40}, {"testing.tRunner", 0}, }}, {trace.EvGoStop, []frame{ {"runtime.block", 0}, - {"runtime/trace_test.TestTraceSymbolize.func1", 38}, + {"runtime/trace_test.TestTraceSymbolize.func1", 0}, }}, {trace.EvGoStop, []frame{ {"runtime.chansend1", 0}, - {"runtime/trace_test.TestTraceSymbolize.func2", 42}, + {"runtime/trace_test.TestTraceSymbolize.func2", 0}, }}, {trace.EvGoStop, []frame{ {"runtime.chanrecv1", 0}, - {"runtime/trace_test.TestTraceSymbolize.func3", 46}, + {"runtime/trace_test.TestTraceSymbolize.func3", 0}, }}, {trace.EvGoBlockRecv, []frame{ {"runtime.chanrecv1", 0}, - {"runtime/trace_test.TestTraceSymbolize.func4", 50}, + {"runtime/trace_test.TestTraceSymbolize.func4", 0}, }}, {trace.EvGoUnblock, []frame{ {"runtime.chansend1", 0}, - {"runtime/trace_test.TestTraceSymbolize", 110}, + {"runtime/trace_test.TestTraceSymbolize", 113}, {"testing.tRunner", 0}, }}, {trace.EvGoBlockSend, []frame{ {"runtime.chansend1", 0}, - {"runtime/trace_test.TestTraceSymbolize.func5", 54}, + {"runtime/trace_test.TestTraceSymbolize.func5", 0}, }}, {trace.EvGoUnblock, []frame{ {"runtime.chanrecv1", 0}, - {"runtime/trace_test.TestTraceSymbolize", 111}, + {"runtime/trace_test.TestTraceSymbolize", 114}, {"testing.tRunner", 0}, }}, {trace.EvGoBlockSelect, []frame{ {"runtime.selectgo", 0}, - {"runtime/trace_test.TestTraceSymbolize.func6", 59}, + {"runtime/trace_test.TestTraceSymbolize.func6", 0}, }}, {trace.EvGoUnblock, []frame{ {"runtime.selectgo", 0}, - {"runtime/trace_test.TestTraceSymbolize", 112}, + {"runtime/trace_test.TestTraceSymbolize", 115}, {"testing.tRunner", 0}, }}, {trace.EvGoBlockSync, []frame{ {"sync.(*Mutex).Lock", 0}, - {"runtime/trace_test.TestTraceSymbolize.func7", 67}, + {"runtime/trace_test.TestTraceSymbolize.func7", 0}, }}, {trace.EvGoUnblock, []frame{ {"sync.(*Mutex).Unlock", 0}, - {"runtime/trace_test.TestTraceSymbolize", 116}, + {"runtime/trace_test.TestTraceSymbolize", 0}, {"testing.tRunner", 0}, }}, {trace.EvGoBlockSync, []frame{ {"sync.(*WaitGroup).Wait", 0}, - {"runtime/trace_test.TestTraceSymbolize.func8", 73}, + {"runtime/trace_test.TestTraceSymbolize.func8", 0}, }}, {trace.EvGoUnblock, []frame{ {"sync.(*WaitGroup).Add", 0}, {"sync.(*WaitGroup).Done", 0}, - {"runtime/trace_test.TestTraceSymbolize", 117}, + {"runtime/trace_test.TestTraceSymbolize", 120}, {"testing.tRunner", 0}, }}, {trace.EvGoBlockCond, []frame{ {"sync.(*Cond).Wait", 0}, - {"runtime/trace_test.TestTraceSymbolize.func9", 78}, + {"runtime/trace_test.TestTraceSymbolize.func9", 0}, }}, {trace.EvGoUnblock, []frame{ {"sync.(*Cond).Signal", 0}, - {"runtime/trace_test.TestTraceSymbolize", 118}, + {"runtime/trace_test.TestTraceSymbolize", 0}, {"testing.tRunner", 0}, }}, {trace.EvGoSleep, []frame{ {"time.Sleep", 0}, - {"runtime/trace_test.TestTraceSymbolize", 109}, + {"runtime/trace_test.TestTraceSymbolize", 0}, + {"testing.tRunner", 0}, + }}, + {trace.EvGomaxprocs, []frame{ + {"runtime.startTheWorld", 0}, // this is when the current gomaxprocs is logged. + {"runtime.GOMAXPROCS", 0}, + {"runtime/trace_test.TestTraceSymbolize", 0}, {"testing.tRunner", 0}, }}, } @@ -235,7 +246,7 @@ func TestTraceSymbolize(t *testing.T) { {"net.(*netFD).accept", 0}, {"net.(*TCPListener).accept", 0}, {"net.(*TCPListener).Accept", 0}, - {"runtime/trace_test.TestTraceSymbolize.func10", 86}, + {"runtime/trace_test.TestTraceSymbolize.func10", 0}, }}, {trace.EvGoSysCall, []frame{ {"syscall.read", 0}, @@ -243,7 +254,7 @@ func TestTraceSymbolize(t *testing.T) { {"internal/poll.(*FD).Read", 0}, {"os.(*File).read", 0}, {"os.(*File).Read", 0}, - {"runtime/trace_test.TestTraceSymbolize.func11", 102}, + {"runtime/trace_test.TestTraceSymbolize.func11", 0}, }}, }...) } @@ -264,22 +275,57 @@ func TestTraceSymbolize(t *testing.T) { matched[i] = true } } - for i, m := range matched { - if m { + for i, w := range want { + if matched[i] { continue } - w := want[i] - t.Errorf("did not match event %v at %v:%v", trace.EventDescriptions[w.Type].Name, w.Stk[0].Fn, w.Stk[0].Line) - t.Errorf("seen the following events of this type:") - for _, ev := range events { - if ev.Type != w.Type { - continue - } - for _, f := range ev.Stk { - t.Logf(" %v :: %s:%v", f.Fn, f.File, f.Line) + seen, n := dumpEventStacks(w.Type, events) + t.Errorf("Did not match event %v with stack\n%s\nSeen %d events of the type\n%s", + trace.EventDescriptions[w.Type].Name, dumpFrames(w.Stk), n, seen) + } +} + +func skipTraceSymbolizeTestIfNecessary(t *testing.T) { + testenv.MustHaveGoBuild(t) + if IsEnabled() { + t.Skip("skipping because -test.trace is set") + } +} + +func dumpEventStacks(typ byte, events []*trace.Event) ([]byte, int) { + matched := 0 + o := new(bytes.Buffer) + tw := tabwriter.NewWriter(o, 0, 8, 0, '\t', 0) + for _, ev := range events { + if ev.Type != typ { + continue + } + matched++ + fmt.Fprintf(tw, "Offset %d\n", ev.Off) + for _, f := range ev.Stk { + fname := f.File + if idx := strings.Index(fname, "/go/src/"); idx > 0 { + fname = fname[idx:] } - t.Logf("---") + fmt.Fprintf(tw, " %v\t%s:%d\n", f.Fn, fname, f.Line) } - t.Logf("======") } + tw.Flush() + return o.Bytes(), matched +} + +type frame struct { + Fn string + Line int +} + +func dumpFrames(frames []frame) []byte { + o := new(bytes.Buffer) + tw := tabwriter.NewWriter(o, 0, 8, 0, '\t', 0) + + for _, f := range frames { + fmt.Fprintf(tw, " %v\t :%d\n", f.Fn, f.Line) + } + tw.Flush() + return o.Bytes() } diff --git a/libgo/go/runtime/trace/trace_test.go b/libgo/go/runtime/trace/trace_test.go index 997d486..fc81abc 100644 --- a/libgo/go/runtime/trace/trace_test.go +++ b/libgo/go/runtime/trace/trace_test.go @@ -31,6 +31,9 @@ func TestEventBatch(t *testing.T) { if race.Enabled { t.Skip("skipping in race mode") } + if IsEnabled() { + t.Skip("skipping because -test.trace is set") + } if testing.Short() { t.Skip("skipping in short mode") } @@ -81,6 +84,9 @@ func TestEventBatch(t *testing.T) { } func TestTraceStartStop(t *testing.T) { + if IsEnabled() { + t.Skip("skipping because -test.trace is set") + } buf := new(bytes.Buffer) if err := Start(buf); err != nil { t.Fatalf("failed to start tracing: %v", err) @@ -98,6 +104,9 @@ func TestTraceStartStop(t *testing.T) { } func TestTraceDoubleStart(t *testing.T) { + if IsEnabled() { + t.Skip("skipping because -test.trace is set") + } Stop() buf := new(bytes.Buffer) if err := Start(buf); err != nil { @@ -111,6 +120,9 @@ func TestTraceDoubleStart(t *testing.T) { } func TestTrace(t *testing.T) { + if IsEnabled() { + t.Skip("skipping because -test.trace is set") + } buf := new(bytes.Buffer) if err := Start(buf); err != nil { t.Fatalf("failed to start tracing: %v", err) @@ -168,6 +180,12 @@ func testBrokenTimestamps(t *testing.T, data []byte) { } func TestTraceStress(t *testing.T) { + if runtime.GOOS == "js" { + t.Skip("no os.Pipe on js") + } + if IsEnabled() { + t.Skip("skipping because -test.trace is set") + } var wg sync.WaitGroup done := make(chan bool) @@ -307,6 +325,12 @@ func TestTraceStress(t *testing.T) { // Do a bunch of various stuff (timers, GC, network, etc) in a separate goroutine. // And concurrently with all that start/stop trace 3 times. func TestTraceStressStartStop(t *testing.T) { + if runtime.GOOS == "js" { + t.Skip("no os.Pipe on js") + } + if IsEnabled() { + t.Skip("skipping because -test.trace is set") + } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8)) outerDone := make(chan bool) @@ -454,6 +478,9 @@ func TestTraceStressStartStop(t *testing.T) { } func TestTraceFutileWakeup(t *testing.T) { + if IsEnabled() { + t.Skip("skipping because -test.trace is set") + } buf := new(bytes.Buffer) if err := Start(buf); err != nil { t.Fatalf("failed to start tracing: %v", err) diff --git a/libgo/go/runtime/traceback_gccgo.go b/libgo/go/runtime/traceback_gccgo.go index 9456b1f..e97071e 100644 --- a/libgo/go/runtime/traceback_gccgo.go +++ b/libgo/go/runtime/traceback_gccgo.go @@ -141,8 +141,8 @@ func goroutineheader(gp *g) { } // Override. - if gpstatus == _Gwaiting && gp.waitreason != "" { - status = gp.waitreason + if gpstatus == _Gwaiting && gp.waitreason != waitReasonZero { + status = gp.waitreason.String() } // approx time the G is blocked, in minutes diff --git a/libgo/go/runtime/type.go b/libgo/go/runtime/type.go index 3c08f7e..8fd38c3 100644 --- a/libgo/go/runtime/type.go +++ b/libgo/go/runtime/type.go @@ -20,12 +20,30 @@ type _type struct { hashfn func(unsafe.Pointer, uintptr) uintptr equalfn func(unsafe.Pointer, unsafe.Pointer) bool - gcdata *byte - string *string + gcdata *byte + _string *string *uncommontype ptrToThis *_type } +func (t *_type) string() string { + return *t._string +} + +// pkgpath returns the path of the package where t was defined, if +// available. This is not the same as the reflect package's PkgPath +// method, in that it returns the package path for struct and interface +// types, not just named types. +func (t *_type) pkgpath() string { + if u := t.uncommontype; u != nil { + if u.pkgPath == nil { + return "" + } + return *u.pkgPath + } + return "" +} + // Return whether two type descriptors are equal. // This is gccgo-specific, as gccgo, unlike gc, permits multiple // independent descriptors for a single type. @@ -38,7 +56,7 @@ func eqtype(t1, t2 *_type) bool { case t1.kind != t2.kind || t1.hash != t2.hash: return false default: - return *t1.string == *t2.string + return t1.string() == t2.string() } } diff --git a/libgo/go/runtime/unaligned1.go b/libgo/go/runtime/unaligned1.go index 2f5b63a..86e0df0 100644 --- a/libgo/go/runtime/unaligned1.go +++ b/libgo/go/runtime/unaligned1.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build 386 amd64 amd64p32 arm64 ppc64 ppc64le s390x ppc s390 arm64be riscv64 +// +build 386 amd64 amd64p32 arm64 ppc64 ppc64le s390x wasm ppc s390 arm64be riscv64 package runtime diff --git a/libgo/go/runtime/utf8.go b/libgo/go/runtime/utf8.go index e845451..0ba0dad 100644 --- a/libgo/go/runtime/utf8.go +++ b/libgo/go/runtime/utf8.go @@ -46,6 +46,15 @@ const ( hicb = 0xBF // 1011 1111 ) +// countrunes returns the number of runes in s. +func countrunes(s string) int { + n := 0 + for range s { + n++ + } + return n +} + // decoderune returns the non-ASCII rune at the start of // s[k:] and the index after the rune in s. // diff --git a/libgo/go/sort/genzfunc.go b/libgo/go/sort/genzfunc.go index 3bb7691..66408d2 100644 --- a/libgo/go/sort/genzfunc.go +++ b/libgo/go/sort/genzfunc.go @@ -82,7 +82,7 @@ func main() { } out.Reset() - out.WriteString(`// DO NOT EDIT; AUTO-GENERATED from sort.go using genzfunc.go + out.WriteString(`// Code generated from sort.go using genzfunc.go; DO NOT EDIT. // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/libgo/go/sort/sort.go b/libgo/go/sort/sort.go index a7304af..7282b26 100644 --- a/libgo/go/sort/sort.go +++ b/libgo/go/sort/sort.go @@ -482,7 +482,7 @@ func symMerge(data Interface, a, m, b int) { } } -// Rotate two consecutives blocks u = data[a:m] and v = data[m:b] in data: +// Rotate two consecutive blocks u = data[a:m] and v = data[m:b] in data: // Data of the form 'x u v y' is changed to 'x v u y'. // Rotate performs at most b-a many calls to data.Swap. // Rotate assumes non-degenerate arguments: a < m && m < b. diff --git a/libgo/go/sort/sort_test.go b/libgo/go/sort/sort_test.go index 092135e..3b31143 100644 --- a/libgo/go/sort/sort_test.go +++ b/libgo/go/sort/sort_test.go @@ -194,7 +194,7 @@ func BenchmarkSortString1K_Slice(b *testing.B) { func BenchmarkStableString1K(b *testing.B) { b.StopTimer() unsorted := make([]string, 1<<10) - for i := 0; i < len(data); i++ { + for i := range unsorted { unsorted[i] = strconv.Itoa(i ^ 0x2cc) } data := make([]string, len(unsorted)) @@ -456,7 +456,7 @@ func TestStableBM(t *testing.T) { } // This is based on the "antiquicksort" implementation by M. Douglas McIlroy. -// See http://www.cs.dartmouth.edu/~doug/mdmspe.pdf for more info. +// See https://www.cs.dartmouth.edu/~doug/mdmspe.pdf for more info. type adversaryTestingData struct { t *testing.T data []int // item values, initialized to special gas value and changed by Less diff --git a/libgo/go/sort/zfuncversion.go b/libgo/go/sort/zfuncversion.go index 99c95a2..30067cb 100644 --- a/libgo/go/sort/zfuncversion.go +++ b/libgo/go/sort/zfuncversion.go @@ -1,4 +1,4 @@ -// DO NOT EDIT; AUTO-GENERATED from sort.go using genzfunc.go +// Code generated from sort.go using genzfunc.go; DO NOT EDIT. // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/libgo/go/strconv/atob.go b/libgo/go/strconv/atob.go index 879ceb3..0a49500 100644 --- a/libgo/go/strconv/atob.go +++ b/libgo/go/strconv/atob.go @@ -17,7 +17,7 @@ func ParseBool(str string) (bool, error) { return false, syntaxError("ParseBool", str) } -// FormatBool returns "true" or "false" according to the value of b +// FormatBool returns "true" or "false" according to the value of b. func FormatBool(b bool) string { if b { return "true" diff --git a/libgo/go/strconv/atof_test.go b/libgo/go/strconv/atof_test.go index 3380b20..cf4d47c 100644 --- a/libgo/go/strconv/atof_test.go +++ b/libgo/go/strconv/atof_test.go @@ -137,9 +137,9 @@ var atoftests = []atofTest{ {".e-1", "0", ErrSyntax}, {"1\x00.2", "0", ErrSyntax}, - // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ + // https://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ {"2.2250738585072012e-308", "2.2250738585072014e-308", nil}, - // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ + // https://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ {"2.2250738585072011e-308", "2.225073858507201e-308", nil}, // A very large number (initially wrongly parsed by the fast algorithm). diff --git a/libgo/go/strconv/doc.go b/libgo/go/strconv/doc.go index 7bc1e27..cba8984 100644 --- a/libgo/go/strconv/doc.go +++ b/libgo/go/strconv/doc.go @@ -32,10 +32,10 @@ // // FormatBool, FormatFloat, FormatInt, and FormatUint convert values to strings: // -// s := strconv.FormatBool(true) -// s := strconv.FormatFloat(3.1415, 'E', -1, 64) -// s := strconv.FormatInt(-42, 16) -// s := strconv.FormatUint(42, 16) +// s := strconv.FormatBool(true) +// s := strconv.FormatFloat(3.1415, 'E', -1, 64) +// s := strconv.FormatInt(-42, 16) +// s := strconv.FormatUint(42, 16) // // AppendBool, AppendFloat, AppendInt, and AppendUint are similar but // append the formatted value to a destination slice. diff --git a/libgo/go/strconv/example_test.go b/libgo/go/strconv/example_test.go index 01fbbc0..5c2e8a9 100644 --- a/libgo/go/strconv/example_test.go +++ b/libgo/go/strconv/example_test.go @@ -281,27 +281,23 @@ func ExampleQuoteToASCII() { } func ExampleUnquote() { - test := func(s string) { - t, err := strconv.Unquote(s) - if err != nil { - fmt.Printf("Unquote(%#v): %v\n", s, err) - } else { - fmt.Printf("Unquote(%#v) = %v\n", s, t) - } - } - - s := `\"Fran & Freddie's Diner\t\u263a\"\"` - // If the string doesn't have quotes, it can't be unquoted. - test(s) // invalid syntax - test("`" + s + "`") - test(`"` + s + `"`) - test(`'\u263a'`) + s, err := strconv.Unquote("You can't unquote a string without quotes") + fmt.Printf("%q, %v\n", s, err) + s, err = strconv.Unquote("\"The string must be either double-quoted\"") + fmt.Printf("%q, %v\n", s, err) + s, err = strconv.Unquote("`or backquoted.`") + fmt.Printf("%q, %v\n", s, err) + s, err = strconv.Unquote("'\u263a'") // single character only allowed in single quotes + fmt.Printf("%q, %v\n", s, err) + s, err = strconv.Unquote("'\u2639\u2639'") + fmt.Printf("%q, %v\n", s, err) // Output: - // Unquote("\\\"Fran & Freddie's Diner\\t\\u263a\\\"\\\""): invalid syntax - // Unquote("`\\\"Fran & Freddie's Diner\\t\\u263a\\\"\\\"`") = \"Fran & Freddie's Diner\t\u263a\"\" - // Unquote("\"\\\"Fran & Freddie's Diner\\t\\u263a\\\"\\\"\"") = "Fran & Freddie's Diner ☺"" - // Unquote("'\\u263a'") = ☺ + // "", invalid syntax + // "The string must be either double-quoted", <nil> + // "or backquoted.", <nil> + // "☺", <nil> + // "", invalid syntax } func ExampleUnquoteChar() { diff --git a/libgo/go/strconv/extfloat.go b/libgo/go/strconv/extfloat.go index 7f17bc6..32d3340 100644 --- a/libgo/go/strconv/extfloat.go +++ b/libgo/go/strconv/extfloat.go @@ -4,6 +4,10 @@ package strconv +import ( + "math/bits" +) + // An extFloat represents an extended floating-point number, with more // precision than a float64. It does not try to save bits: the // number represented by the structure is mant*(2^exp), with a negative @@ -15,7 +19,7 @@ type extFloat struct { } // Powers of ten taken from double-conversion library. -// http://code.google.com/p/double-conversion/ +// https://code.google.com/p/double-conversion/ const ( firstPowerOfTen = -348 stepPowerOfTen = 8 @@ -196,38 +200,15 @@ func (f *extFloat) AssignComputeBounds(mant uint64, exp int, neg bool, flt *floa // Normalize normalizes f so that the highest bit of the mantissa is // set, and returns the number by which the mantissa was left-shifted. -func (f *extFloat) Normalize() (shift uint) { - mant, exp := f.mant, f.exp - if mant == 0 { +func (f *extFloat) Normalize() uint { + // bits.LeadingZeros64 would return 64 + if f.mant == 0 { return 0 } - if mant>>(64-32) == 0 { - mant <<= 32 - exp -= 32 - } - if mant>>(64-16) == 0 { - mant <<= 16 - exp -= 16 - } - if mant>>(64-8) == 0 { - mant <<= 8 - exp -= 8 - } - if mant>>(64-4) == 0 { - mant <<= 4 - exp -= 4 - } - if mant>>(64-2) == 0 { - mant <<= 2 - exp -= 2 - } - if mant>>(64-1) == 0 { - mant <<= 1 - exp -= 1 - } - shift = uint(f.exp - exp) - f.mant, f.exp = mant, exp - return + shift := bits.LeadingZeros64(f.mant) + f.mant <<= uint(shift) + f.exp -= shift + return uint(shift) } // Multiply sets f to the product f*g: the result is correctly rounded, diff --git a/libgo/go/strconv/ftoa.go b/libgo/go/strconv/ftoa.go index 8b3d33e..a7ccbe6 100644 --- a/libgo/go/strconv/ftoa.go +++ b/libgo/go/strconv/ftoa.go @@ -35,10 +35,11 @@ var float64info = floatInfo{52, 11, -1023} // 'g' ('e' for large exponents, 'f' otherwise), or // 'G' ('E' for large exponents, 'f' otherwise). // -// The precision prec controls the number of digits -// (excluding the exponent) printed by the 'e', 'E', 'f', 'g', and 'G' formats. +// The precision prec controls the number of digits (excluding the exponent) +// printed by the 'e', 'E', 'f', 'g', and 'G' formats. // For 'e', 'E', and 'f' it is the number of digits after the decimal point. -// For 'g' and 'G' it is the total number of digits. +// For 'g' and 'G' it is the maximum number of significant digits (trailing +// zeros are removed). // The special precision -1 uses the smallest number of digits // necessary such that ParseFloat will return f exactly. func FormatFloat(f float64, fmt byte, prec, bitSize int) string { diff --git a/libgo/go/strconv/ftoa_test.go b/libgo/go/strconv/ftoa_test.go index 976bd2c..1d3030b 100644 --- a/libgo/go/strconv/ftoa_test.go +++ b/libgo/go/strconv/ftoa_test.go @@ -120,9 +120,9 @@ var ftoatests = []ftoaTest{ {0.5, 'f', 0, "0"}, {1.5, 'f', 0, "2"}, - // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ + // https://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ {2.2250738585072012e-308, 'g', -1, "2.2250738585072014e-308"}, - // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ + // https://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ {2.2250738585072011e-308, 'g', -1, "2.225073858507201e-308"}, // Issue 2625. diff --git a/libgo/go/strconv/isprint.go b/libgo/go/strconv/isprint.go index 5837142..f537ba4 100644 --- a/libgo/go/strconv/isprint.go +++ b/libgo/go/strconv/isprint.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// DO NOT EDIT. GENERATED BY -// go run makeisprint.go -output isprint.go +// Code generated by go run makeisprint.go -output isprint.go; DO NOT EDIT. package strconv diff --git a/libgo/go/strconv/itoa.go b/libgo/go/strconv/itoa.go index 78527c8..8afe7af 100644 --- a/libgo/go/strconv/itoa.go +++ b/libgo/go/strconv/itoa.go @@ -4,6 +4,8 @@ package strconv +import "math/bits" + const fastSmalls = true // enable fast path for small integers // FormatUint returns the string representation of i in the given base, @@ -55,11 +57,10 @@ func AppendUint(dst []byte, i uint64, base int) []byte { // small returns the string for an i with 0 <= i < nSmalls. func small(i int) string { - off := 0 if i < 10 { - off = 1 + return digits[i : i+1] } - return smallsString[i*2+off : i*2+2] + return smallsString[i*2 : i*2+2] } const nSmalls = 100 @@ -79,14 +80,6 @@ const host32bit = ^uint(0)>>32 == 0 const digits = "0123456789abcdefghijklmnopqrstuvwxyz" -var shifts = [len(digits) + 1]uint{ - 1 << 1: 1, - 1 << 2: 2, - 1 << 3: 3, - 1 << 4: 4, - 1 << 5: 5, -} - // formatBits computes the string representation of u in the given base. // If neg is set, u is treated as negative int64 value. If append_ is // set, the string is appended to dst and the resulting byte slice is @@ -158,14 +151,17 @@ func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s a[i] = smallsString[is] } - } else if s := shifts[base]; s > 0 { - // base is power of 2: use shifts and masks instead of / and % + } else if isPowerOfTwo(base) { + // It is known that base is a power of two and + // 2 <= base <= len(digits). + // Use shifts and masks instead of / and %. + shift := uint(bits.TrailingZeros(uint(base))) & 31 b := uint64(base) - m := uint(base) - 1 // == 1<<s - 1 + m := uint(base) - 1 // == 1<<shift - 1 for u >= b { i-- a[i] = digits[uint(u)&m] - u >>= s + u >>= shift } // u < base i-- @@ -200,3 +196,7 @@ func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s s = string(a[i:]) return } + +func isPowerOfTwo(x int) bool { + return x&(x-1) == 0 +} diff --git a/libgo/go/strconv/itoa_test.go b/libgo/go/strconv/itoa_test.go index 89c2de6..b5ee3aa 100644 --- a/libgo/go/strconv/itoa_test.go +++ b/libgo/go/strconv/itoa_test.go @@ -200,10 +200,14 @@ func BenchmarkAppendUint(b *testing.B) { } func BenchmarkFormatIntSmall(b *testing.B) { - const smallInt = 42 - for i := 0; i < b.N; i++ { - s := FormatInt(smallInt, 10) - BenchSink += len(s) + smallInts := []int64{7, 42} + for _, smallInt := range smallInts { + b.Run(Itoa(int(smallInt)), func(b *testing.B) { + for i := 0; i < b.N; i++ { + s := FormatInt(smallInt, 10) + BenchSink += len(s) + } + }) } } diff --git a/libgo/go/strconv/makeisprint.go b/libgo/go/strconv/makeisprint.go index 0a3e5b2..1a3248f 100644 --- a/libgo/go/strconv/makeisprint.go +++ b/libgo/go/strconv/makeisprint.go @@ -139,8 +139,7 @@ func main() { fmt.Fprintf(&buf, `// Copyright 2013 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.`+"\n\n") - fmt.Fprintf(&buf, "// DO NOT EDIT. GENERATED BY\n") - fmt.Fprintf(&buf, "// go run makeisprint.go -output isprint.go\n\n") + fmt.Fprintf(&buf, "// Code generated by go run makeisprint.go -output isprint.go; DO NOT EDIT.\n\n") fmt.Fprintf(&buf, "package strconv\n\n") fmt.Fprintf(&buf, "// (%d+%d+%d)*2 + (%d)*4 = %d bytes\n\n", diff --git a/libgo/go/strconv/quote.go b/libgo/go/strconv/quote.go index 156a510..9b7194a 100644 --- a/libgo/go/strconv/quote.go +++ b/libgo/go/strconv/quote.go @@ -237,6 +237,10 @@ func unhex(b byte) (v rune, ok bool) { // If set to zero, it does not permit either escape and allows both quote characters to appear unescaped. func UnquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error) { // easy cases + if len(s) == 0 { + err = ErrSyntax + return + } switch c := s[0]; { case c == quote && (quote == '\'' || quote == '"'): err = ErrSyntax @@ -385,7 +389,9 @@ func Unquote(s string) (string, error) { if !contains(s, '\\') && !contains(s, quote) { switch quote { case '"': - return s, nil + if utf8.ValidString(s) { + return s, nil + } case '\'': r, size := utf8.DecodeRuneInString(s) if size == len(s) && (r != utf8.RuneError || size != 1) { diff --git a/libgo/go/strconv/quote_test.go b/libgo/go/strconv/quote_test.go index a4b5804..cdc9aaf 100644 --- a/libgo/go/strconv/quote_test.go +++ b/libgo/go/strconv/quote_test.go @@ -326,6 +326,36 @@ func TestUnquote(t *testing.T) { } } +// Issue 23685: invalid UTF-8 should not go through the fast path. +func TestUnquoteInvalidUTF8(t *testing.T) { + tests := []struct { + in string + + // one of: + want string + wantErr string + }{ + {in: `"foo"`, want: "foo"}, + {in: `"foo`, wantErr: "invalid syntax"}, + {in: `"` + "\xc0" + `"`, want: "\xef\xbf\xbd"}, + {in: `"a` + "\xc0" + `"`, want: "a\xef\xbf\xbd"}, + {in: `"\t` + "\xc0" + `"`, want: "\t\xef\xbf\xbd"}, + } + for i, tt := range tests { + got, err := Unquote(tt.in) + var gotErr string + if err != nil { + gotErr = err.Error() + } + if gotErr != tt.wantErr { + t.Errorf("%d. Unquote(%q) = err %v; want %q", i, tt.in, err, tt.wantErr) + } + if tt.wantErr == "" && err == nil && got != tt.want { + t.Errorf("%d. Unquote(%q) = %02x; want %02x", i, tt.in, []byte(got), []byte(tt.want)) + } + } +} + func BenchmarkUnquoteEasy(b *testing.B) { for i := 0; i < b.N; i++ { Unquote(`"Give me a rock, paper and scissors and I will move the world."`) diff --git a/libgo/go/strings/builder_test.go b/libgo/go/strings/builder_test.go index ad012bb..da6c021 100644 --- a/libgo/go/strings/builder_test.go +++ b/libgo/go/strings/builder_test.go @@ -85,16 +85,25 @@ func TestBuilderReset(t *testing.T) { } func TestBuilderGrow(t *testing.T) { + if runtime.Compiler == "gccgo" { + t.Skip("skip for gccgo until escape analysis improves") + } for _, growLen := range []int{0, 100, 1000, 10000, 100000} { - var b Builder - b.Grow(growLen) p := bytes.Repeat([]byte{'a'}, growLen) - allocs := numAllocs(func() { b.Write(p) }) - if allocs > 0 { - t.Errorf("growLen=%d: allocation occurred during write", growLen) + allocs := testing.AllocsPerRun(100, func() { + var b Builder + b.Grow(growLen) // should be only alloc, when growLen > 0 + b.Write(p) + if b.String() != string(p) { + t.Fatalf("growLen=%d: bad data written after Grow", growLen) + } + }) + wantAllocs := 1 + if growLen == 0 { + wantAllocs = 0 } - if b.String() != string(p) { - t.Errorf("growLen=%d: bad data written after Grow", growLen) + if g, w := int(allocs), wantAllocs; g != w { + t.Errorf("growLen=%d: got %d allocs during Write; want %v", growLen, g, w) } } } @@ -168,16 +177,17 @@ func TestBuilderWriteByte(t *testing.T) { func TestBuilderAllocs(t *testing.T) { if runtime.Compiler == "gccgo" { - t.Skip("skip for gccgo until escape analysis enabled by default") + t.Skip("skip for gccgo until escape analysis improves") } var b Builder - b.Grow(5) + const msg = "hello" + b.Grow(len(msg) * 2) // because AllocsPerRun does an extra "warm-up" iteration var s string - allocs := numAllocs(func() { + allocs := int(testing.AllocsPerRun(1, func() { b.WriteString("hello") s = b.String() - }) - if want := "hello"; s != want { + })) + if want := msg + msg; s != want { t.Errorf("String: got %#q; want %#q", s, want) } if allocs > 0 { @@ -197,15 +207,6 @@ func TestBuilderAllocs(t *testing.T) { } } -func numAllocs(fn func()) uint64 { - defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) - var m1, m2 runtime.MemStats - runtime.ReadMemStats(&m1) - fn() - runtime.ReadMemStats(&m2) - return m2.Mallocs - m1.Mallocs -} - func TestBuilderCopyPanic(t *testing.T) { tests := []struct { name string @@ -305,3 +306,52 @@ func TestBuilderCopyPanic(t *testing.T) { } } } + +var someBytes = []byte("some bytes sdljlk jsklj3lkjlk djlkjw") + +var sinkS string + +func benchmarkBuilder(b *testing.B, f func(b *testing.B, numWrite int, grow bool)) { + b.Run("1Write_NoGrow", func(b *testing.B) { + b.ReportAllocs() + f(b, 1, false) + }) + b.Run("3Write_NoGrow", func(b *testing.B) { + b.ReportAllocs() + f(b, 3, false) + }) + b.Run("3Write_Grow", func(b *testing.B) { + b.ReportAllocs() + f(b, 3, true) + }) +} + +func BenchmarkBuildString_Builder(b *testing.B) { + benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) { + for i := 0; i < b.N; i++ { + var buf Builder + if grow { + buf.Grow(len(someBytes) * numWrite) + } + for i := 0; i < numWrite; i++ { + buf.Write(someBytes) + } + sinkS = buf.String() + } + }) +} + +func BenchmarkBuildString_ByteBuffer(b *testing.B) { + benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) { + for i := 0; i < b.N; i++ { + var buf bytes.Buffer + if grow { + buf.Grow(len(someBytes) * numWrite) + } + for i := 0; i < numWrite; i++ { + buf.Write(someBytes) + } + sinkS = buf.String() + } + }) +} diff --git a/libgo/go/strings/compare_test.go b/libgo/go/strings/compare_test.go index bc12e42..5d53344 100644 --- a/libgo/go/strings/compare_test.go +++ b/libgo/go/strings/compare_test.go @@ -8,6 +8,7 @@ package strings_test // Benchmarks omitted since the underlying implementation is identical. import ( + "internal/testenv" . "strings" "testing" ) @@ -52,10 +53,21 @@ func TestCompareIdenticalString(t *testing.T) { } func TestCompareStrings(t *testing.T) { - n := 128 + lengths := make([]int, 0) // lengths to test in ascending order + for i := 0; i <= 128; i++ { + lengths = append(lengths, i) + } + lengths = append(lengths, 256, 512, 1024, 1333, 4095, 4096, 4097) + + if !testing.Short() || testenv.Builder() != "" { + lengths = append(lengths, 65535, 65536, 65537, 99999) + } + + n := lengths[len(lengths)-1] a := make([]byte, n+1) b := make([]byte, n+1) - for len := 0; len < 128; len++ { + lastLen := 0 + for _, len := range lengths { // randomish but deterministic data. No 0 or 255. for i := 0; i < len; i++ { a[i] = byte(1 + 31*i%254) @@ -67,21 +79,22 @@ func TestCompareStrings(t *testing.T) { b[i] = 9 } - cmp := Compare(string(a[:len]), string(b[:len])) + sa, sb := string(a), string(b) + cmp := Compare(sa[:len], sb[:len]) if cmp != 0 { t.Errorf(`CompareIdentical(%d) = %d`, len, cmp) } if len > 0 { - cmp = Compare(string(a[:len-1]), string(b[:len])) + cmp = Compare(sa[:len-1], sb[:len]) if cmp != -1 { t.Errorf(`CompareAshorter(%d) = %d`, len, cmp) } - cmp = Compare(string(a[:len]), string(b[:len-1])) + cmp = Compare(sa[:len], sb[:len-1]) if cmp != 1 { t.Errorf(`CompareBshorter(%d) = %d`, len, cmp) } } - for k := 0; k < len; k++ { + for k := lastLen; k < len; k++ { b[k] = a[k] - 1 cmp = Compare(string(a[:len]), string(b[:len])) if cmp != 1 { @@ -94,5 +107,6 @@ func TestCompareStrings(t *testing.T) { } b[k] = a[k] } + lastLen = len } } diff --git a/libgo/go/strings/replace.go b/libgo/go/strings/replace.go index 4752641..58a11a6 100644 --- a/libgo/go/strings/replace.go +++ b/libgo/go/strings/replace.go @@ -18,8 +18,9 @@ type replacer interface { WriteString(w io.Writer, s string) (n int, err error) } -// NewReplacer returns a new Replacer from a list of old, new string pairs. -// Replacements are performed in order, without overlapping matches. +// NewReplacer returns a new Replacer from a list of old, new string +// pairs. Replacements are performed in the order they appear in the +// target string, without overlapping matches. func NewReplacer(oldnew ...string) *Replacer { if len(oldnew)%2 == 1 { panic("strings.NewReplacer: odd argument count") @@ -54,13 +55,21 @@ func NewReplacer(oldnew ...string) *Replacer { return &Replacer{r: &r} } - r := byteStringReplacer{} + r := byteStringReplacer{toReplace: make([]string, 0, len(oldnew)/2)} // The first occurrence of old->new map takes precedence // over the others with the same old string. for i := len(oldnew) - 2; i >= 0; i -= 2 { o := oldnew[i][0] n := oldnew[i+1] - r[o] = []byte(n) + // To avoid counting repetitions multiple times. + if r.replacements[o] == nil { + // We need to use string([]byte{o}) instead of string(o), + // to avoid utf8 encoding of o. + // E. g. byte(150) produces string of length 2. + r.toReplace = append(r.toReplace, string([]byte{o})) + } + r.replacements[o] = []byte(n) + } return &Replacer{r: &r} } @@ -454,34 +463,60 @@ func (r *byteReplacer) WriteString(w io.Writer, s string) (n int, err error) { // byteStringReplacer is the implementation that's used when all the // "old" values are single ASCII bytes but the "new" values vary in size. -// The array contains replacement byte slices indexed by old byte. -// A nil []byte means that the old byte should not be replaced. -type byteStringReplacer [256][]byte +type byteStringReplacer struct { + // replacements contains replacement byte slices indexed by old byte. + // A nil []byte means that the old byte should not be replaced. + replacements [256][]byte + // toReplace keeps a list of bytes to replace. Depending on length of toReplace + // and length of target string it may be faster to use Count, or a plain loop. + // We store single byte as a string, because Count takes a string. + toReplace []string +} + +// countCutOff controls the ratio of a string length to a number of replacements +// at which (*byteStringReplacer).Replace switches algorithms. +// For strings with higher ration of length to replacements than that value, +// we call Count, for each replacement from toReplace. +// For strings, with a lower ratio we use simple loop, because of Count overhead. +// countCutOff is an empirically determined overhead multiplier. +// TODO(tocarip) revisit once we have register-based abi/mid-stack inlining. +const countCutOff = 8 func (r *byteStringReplacer) Replace(s string) string { newSize := len(s) anyChanges := false - for i := 0; i < len(s); i++ { - b := s[i] - if r[b] != nil { - anyChanges = true - // The -1 is because we are replacing 1 byte with len(r[b]) bytes. - newSize += len(r[b]) - 1 + // Is it faster to use Count? + if len(r.toReplace)*countCutOff <= len(s) { + for _, x := range r.toReplace { + if c := Count(s, x); c != 0 { + // The -1 is because we are replacing 1 byte with len(replacements[b]) bytes. + newSize += c * (len(r.replacements[x[0]]) - 1) + anyChanges = true + } + + } + } else { + for i := 0; i < len(s); i++ { + b := s[i] + if r.replacements[b] != nil { + // See above for explanation of -1 + newSize += len(r.replacements[b]) - 1 + anyChanges = true + } } } if !anyChanges { return s } buf := make([]byte, newSize) - bi := buf + j := 0 for i := 0; i < len(s); i++ { b := s[i] - if r[b] != nil { - n := copy(bi, r[b]) - bi = bi[n:] + if r.replacements[b] != nil { + j += copy(buf[j:], r.replacements[b]) } else { - bi[0] = b - bi = bi[1:] + buf[j] = b + j++ } } return string(buf) @@ -492,7 +527,7 @@ func (r *byteStringReplacer) WriteString(w io.Writer, s string) (n int, err erro last := 0 for i := 0; i < len(s); i++ { b := s[i] - if r[b] == nil { + if r.replacements[b] == nil { continue } if last != i { @@ -503,7 +538,7 @@ func (r *byteStringReplacer) WriteString(w io.Writer, s string) (n int, err erro } } last = i + 1 - nw, err := w.Write(r[b]) + nw, err := w.Write(r.replacements[b]) n += nw if err != nil { return n, err diff --git a/libgo/go/strings/search.go b/libgo/go/strings/search.go index f77c879..e5bffbb 100644 --- a/libgo/go/strings/search.go +++ b/libgo/go/strings/search.go @@ -6,8 +6,8 @@ package strings // stringFinder efficiently finds strings in a source text. It's implemented // using the Boyer-Moore string search algorithm: -// http://en.wikipedia.org/wiki/Boyer-Moore_string_search_algorithm -// http://www.cs.utexas.edu/~moore/publications/fstrpos.pdf (note: this aged +// https://en.wikipedia.org/wiki/Boyer-Moore_string_search_algorithm +// https://www.cs.utexas.edu/~moore/publications/fstrpos.pdf (note: this aged // document uses 1-based indexing) type stringFinder struct { // pattern is the string that we are searching for in the text. diff --git a/libgo/go/strings/search_test.go b/libgo/go/strings/search_test.go index 966c05e..c01a393 100644 --- a/libgo/go/strings/search_test.go +++ b/libgo/go/strings/search_test.go @@ -57,7 +57,7 @@ func TestFinderCreation(t *testing.T) { [256]int{'i': 3, 'm': 7, 's': 1}, []int{15, 14, 13, 7, 11, 10, 7, 1}, }, - // From http://www.cs.utexas.edu/~moore/publications/fstrpos.pdf + // From https://www.cs.utexas.edu/~moore/publications/fstrpos.pdf { "abcxxxabc", [256]int{'a': 2, 'b': 1, 'c': 6, 'x': 3}, diff --git a/libgo/go/strings/strings.go b/libgo/go/strings/strings.go index 05e8243..20868be 100644 --- a/libgo/go/strings/strings.go +++ b/libgo/go/strings/strings.go @@ -8,6 +8,7 @@ package strings import ( + "internal/bytealg" "unicode" "unicode/utf8" ) @@ -72,12 +73,16 @@ func hashStrRev(sep string) (uint32, uint32) { return hash, pow } -// countGeneric implements Count. -func countGeneric(s, substr string) int { +// Count counts the number of non-overlapping instances of substr in s. +// If substr is an empty string, Count returns 1 + the number of Unicode code points in s. +func Count(s, substr string) int { // special case if len(substr) == 0 { return utf8.RuneCountInString(s) + 1 } + if len(substr) == 1 { + return bytealg.CountString(s, substr[0]) + } n := 0 for { i := Index(s, substr) @@ -792,6 +797,8 @@ func Trim(s string, cutset string) string { // TrimLeft returns a slice of the string s with all leading // Unicode code points contained in cutset removed. +// +// To remove a prefix, use TrimPrefix instead. func TrimLeft(s string, cutset string) string { if s == "" || cutset == "" { return s @@ -801,6 +808,8 @@ func TrimLeft(s string, cutset string) string { // TrimRight returns a slice of the string s, with all trailing // Unicode code points contained in cutset removed. +// +// To remove a suffix, use TrimSuffix instead. func TrimRight(s string, cutset string) string { if s == "" || cutset == "" { return s @@ -903,9 +912,9 @@ func EqualFold(s, t string) bool { tr, sr = sr, tr } // Fast check for ASCII. - if tr < utf8.RuneSelf && 'A' <= sr && sr <= 'Z' { - // ASCII, and sr is upper case. tr must be lower case. - if tr == sr+'a'-'A' { + if tr < utf8.RuneSelf { + // ASCII only, sr/tr must be upper/lower case + if 'A' <= sr && sr <= 'Z' && tr == sr+'a'-'A' { continue } return false @@ -927,6 +936,85 @@ func EqualFold(s, t string) bool { return s == t } +// Index returns the index of the first instance of substr in s, or -1 if substr is not present in s. +func Index(s, substr string) int { + n := len(substr) + switch { + case n == 0: + return 0 + case n == 1: + return IndexByte(s, substr[0]) + case n == len(s): + if substr == s { + return 0 + } + return -1 + case n > len(s): + return -1 + case n <= bytealg.MaxLen: + // Use brute force when s and substr both are small + if len(s) <= bytealg.MaxBruteForce { + return bytealg.IndexString(s, substr) + } + c := substr[0] + i := 0 + t := s[:len(s)-n+1] + fails := 0 + for i < len(t) { + if t[i] != c { + // IndexByte is faster than bytealg.IndexString, so use it as long as + // we're not getting lots of false positives. + o := IndexByte(t[i:], c) + if o < 0 { + return -1 + } + i += o + } + if s[i:i+n] == substr { + return i + } + fails++ + i++ + // Switch to bytealg.IndexString when IndexByte produces too many false positives. + if fails > bytealg.Cutover(i) { + r := bytealg.IndexString(s[i:], substr) + if r >= 0 { + return r + i + } + return -1 + } + } + return -1 + } + c := substr[0] + i := 0 + t := s[:len(s)-n+1] + fails := 0 + for i < len(t) { + if t[i] != c { + o := IndexByte(t[i:], c) + if o < 0 { + return -1 + } + i += o + } + if s[i:i+n] == substr { + return i + } + i++ + fails++ + if fails >= 4+i>>4 && i < len(t) { + // See comment in ../bytes/bytes_generic.go. + j := indexRabinKarp(s[i:], substr) + if j < 0 { + return -1 + } + return i + j + } + } + return -1 +} + func indexRabinKarp(s, substr string) int { // Rabin-Karp search hashss, pow := hashStr(substr) diff --git a/libgo/go/strings/strings_amd64.go b/libgo/go/strings/strings_amd64.go deleted file mode 100644 index 2441569..0000000 --- a/libgo/go/strings/strings_amd64.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2015 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 ignore - -package strings - -import "internal/cpu" - -//go:noescape - -// indexShortStr returns the index of the first instance of c in s, or -1 if c is not present in s. -// indexShortStr requires 2 <= len(c) <= shortStringLen -func indexShortStr(s, c string) int // ../runtime/asm_amd64.s -func countByte(s string, c byte) int // ../runtime/asm_amd64.s - -var shortStringLen int - -func init() { - if cpu.X86.HasAVX2 { - shortStringLen = 63 - } else { - shortStringLen = 31 - } -} - -// Index returns the index of the first instance of substr in s, or -1 if substr is not present in s. -func Index(s, substr string) int { - n := len(substr) - switch { - case n == 0: - return 0 - case n == 1: - return IndexByte(s, substr[0]) - case n == len(s): - if substr == s { - return 0 - } - return -1 - case n > len(s): - return -1 - case n <= shortStringLen: - // Use brute force when s and substr both are small - if len(s) <= 64 { - return indexShortStr(s, substr) - } - c := substr[0] - i := 0 - t := s[:len(s)-n+1] - fails := 0 - for i < len(t) { - if t[i] != c { - // IndexByte skips 16/32 bytes per iteration, - // so it's faster than indexShortStr. - o := IndexByte(t[i:], c) - if o < 0 { - return -1 - } - i += o - } - if s[i:i+n] == substr { - return i - } - fails++ - i++ - // Switch to indexShortStr when IndexByte produces too many false positives. - // Too many means more that 1 error per 8 characters. - // Allow some errors in the beginning. - if fails > (i+16)/8 { - r := indexShortStr(s[i:], substr) - if r >= 0 { - return r + i - } - return -1 - } - } - return -1 - } - return indexRabinKarp(s, substr) -} - -// Count counts the number of non-overlapping instances of substr in s. -// If substr is an empty string, Count returns 1 + the number of Unicode code points in s. -func Count(s, substr string) int { - if len(substr) == 1 && cpu.X86.HasPOPCNT { - return countByte(s, byte(substr[0])) - } - return countGeneric(s, substr) -} diff --git a/libgo/go/strings/strings_decl.go b/libgo/go/strings/strings_decl.go index 3bae844..9819444 100644 --- a/libgo/go/strings/strings_decl.go +++ b/libgo/go/strings/strings_decl.go @@ -5,4 +5,4 @@ package strings // IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s. -func IndexByte(s string, c byte) int // ../runtime/asm_$GOARCH.s +func IndexByte(s string, c byte) int // in internal/bytealg diff --git a/libgo/go/strings/strings_generic.go b/libgo/go/strings/strings_generic.go deleted file mode 100644 index 60a6f78..0000000 --- a/libgo/go/strings/strings_generic.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2015 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 !amd64,!s390x - -package strings - -// TODO: implements short string optimization on non amd64 platforms -// and get rid of strings_amd64.go - -// Index returns the index of the first instance of substr in s, or -1 if substr is not present in s. -func Index(s, substr string) int { - n := len(substr) - switch { - case n == 0: - return 0 - case n == 1: - return IndexByte(s, substr[0]) - case n == len(s): - if substr == s { - return 0 - } - return -1 - case n > len(s): - return -1 - } - c := substr[0] - i := 0 - t := s[:len(s)-n+1] - fails := 0 - for i < len(t) { - if t[i] != c { - o := IndexByte(t[i:], c) - if o < 0 { - return -1 - } - i += o - } - if s[i:i+n] == substr { - return i - } - i++ - fails++ - if fails >= 4+i>>4 && i < len(t) { - // See comment in ../bytes/bytes_generic.go. - j := indexRabinKarp(s[i:], substr) - if j < 0 { - return -1 - } - return i + j - } - } - return -1 -} - -// Count counts the number of non-overlapping instances of substr in s. -// If substr is an empty string, Count returns 1 + the number of Unicode code points in s. -func Count(s, substr string) int { - return countGeneric(s, substr) -} diff --git a/libgo/go/strings/strings_s390x.go b/libgo/go/strings/strings_s390x.go deleted file mode 100644 index e74e4cd..0000000 --- a/libgo/go/strings/strings_s390x.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2016 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 ignore - -package strings - -//go:noescape - -// indexShortStr returns the index of the first instance of sep in s, -// or -1 if sep is not present in s. -// indexShortStr requires 2 <= len(sep) <= shortStringLen -func indexShortStr(s, sep string) int // ../runtime/asm_$GOARCH.s - -// supportsVX reports whether the vector facility is available. -// indexShortStr must not be called if the vector facility is not -// available. -func supportsVX() bool // ../runtime/asm_s390x.s - -var shortStringLen = -1 - -func init() { - if supportsVX() { - shortStringLen = 64 - } -} - -// Index returns the index of the first instance of substr in s, or -1 if substr is not present in s. -func Index(s, substr string) int { - n := len(substr) - switch { - case n == 0: - return 0 - case n == 1: - return IndexByte(s, substr[0]) - case n == len(s): - if substr == s { - return 0 - } - return -1 - case n > len(s): - return -1 - case n <= shortStringLen: - // Use brute force when s and substr both are small - if len(s) <= 64 { - return indexShortStr(s, substr) - } - c := substr[0] - i := 0 - t := s[:len(s)-n+1] - fails := 0 - for i < len(t) { - if t[i] != c { - // IndexByte skips 16/32 bytes per iteration, - // so it's faster than indexShortStr. - o := IndexByte(t[i:], c) - if o < 0 { - return -1 - } - i += o - } - if s[i:i+n] == substr { - return i - } - fails++ - i++ - // Switch to indexShortStr when IndexByte produces too many false positives. - // Too many means more that 1 error per 8 characters. - // Allow some errors in the beginning. - if fails > (i+16)/8 { - r := indexShortStr(s[i:], substr) - if r >= 0 { - return r + i - } - return -1 - } - } - return -1 - } - return indexRabinKarp(s, substr) -} - -// Count counts the number of non-overlapping instances of substr in s. -// If substr is an empty string, Count returns 1 + the number of Unicode code points in s. -func Count(s, substr string) int { - return countGeneric(s, substr) -} diff --git a/libgo/go/strings/strings_test.go b/libgo/go/strings/strings_test.go index 92122db..3f0c790 100644 --- a/libgo/go/strings/strings_test.go +++ b/libgo/go/strings/strings_test.go @@ -1406,6 +1406,8 @@ var EqualFoldTests = []struct { {"abcdefghijK", "abcdefghij\u212A", true}, {"abcdefghijkz", "abcdefghij\u212Ay", false}, {"abcdefghijKz", "abcdefghij\u212Ay", false}, + {"1", "2", false}, + {"utf-8", "US-ASCII", false}, } func TestEqualFold(t *testing.T) { @@ -1419,6 +1421,16 @@ func TestEqualFold(t *testing.T) { } } +func BenchmarkEqualFold(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, tt := range EqualFoldTests { + if out := EqualFold(tt.s, tt.t); out != tt.out { + b.Fatal("wrong result") + } + } + } +} + var CountTests = []struct { s, sep string num int diff --git a/libgo/go/sync/atomic/64bit_arm.go b/libgo/go/sync/atomic/64bit_arm.go deleted file mode 100644 index 4ef1174..0000000 --- a/libgo/go/sync/atomic/64bit_arm.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package atomic - -func loadUint64(addr *uint64) (val uint64) { - for { - val = *addr - if CompareAndSwapUint64(addr, val, val) { - break - } - } - return -} - -func storeUint64(addr *uint64, val uint64) { - for { - old := *addr - if CompareAndSwapUint64(addr, old, val) { - break - } - } - return -} - -func addUint64(val *uint64, delta uint64) (new uint64) { - for { - old := *val - new = old + delta - if CompareAndSwapUint64(val, old, new) { - break - } - } - return -} - -func swapUint64(addr *uint64, new uint64) (old uint64) { - for { - old = *addr - if CompareAndSwapUint64(addr, old, new) { - break - } - } - return -} - -// Additional ARM-specific assembly routines. -// Declaration here to give assembly routines correct stack maps for arguments. -func armCompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool) -func armCompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool) -func generalCAS64(addr *uint64, old, new uint64) (swapped bool) -func armAddUint32(addr *uint32, delta uint32) (new uint32) -func armAddUint64(addr *uint64, delta uint64) (new uint64) -func armSwapUint32(addr *uint32, new uint32) (old uint32) -func armSwapUint64(addr *uint64, new uint64) (old uint64) -func armLoadUint64(addr *uint64) (val uint64) -func armStoreUint64(addr *uint64, val uint64) diff --git a/libgo/go/sync/atomic/example_test.go b/libgo/go/sync/atomic/example_test.go new file mode 100644 index 0000000..09ae0aa --- /dev/null +++ b/libgo/go/sync/atomic/example_test.go @@ -0,0 +1,76 @@ +// Copyright 2018 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 atomic_test + +import ( + "sync" + "sync/atomic" + "time" +) + +func loadConfig() map[string]string { + return make(map[string]string) +} + +func requests() chan int { + return make(chan int) +} + +// The following example shows how to use Value for periodic program config updates +// and propagation of the changes to worker goroutines. +func ExampleValue_config() { + var config atomic.Value // holds current server configuration + // Create initial config value and store into config. + config.Store(loadConfig()) + go func() { + // Reload config every 10 seconds + // and update config value with the new version. + for { + time.Sleep(10 * time.Second) + config.Store(loadConfig()) + } + }() + // Create worker goroutines that handle incoming requests + // using the latest config value. + for i := 0; i < 10; i++ { + go func() { + for r := range requests() { + c := config.Load() + // Handle request r using config c. + _, _ = r, c + } + }() + } +} + +// The following example shows how to maintain a scalable frequently read, +// but infrequently updated data structure using copy-on-write idiom. +func ExampleValue_readMostly() { + type Map map[string]string + var m atomic.Value + m.Store(make(Map)) + var mu sync.Mutex // used only by writers + // read function can be used to read the data without further synchronization + read := func(key string) (val string) { + m1 := m.Load().(Map) + return m1[key] + } + // insert function can be used to update the data without further synchronization + insert := func(key, val string) { + mu.Lock() // synchronize with other potential writers + defer mu.Unlock() + m1 := m.Load().(Map) // load current value of the data structure + m2 := make(Map) // create a new value + for k, v := range m1 { + m2[k] = v // copy all data from the current object to the new one + } + m2[key] = val // do the update that we need + m.Store(m2) // atomically replace the current object with the new one + // At this point all new readers start working with the new version. + // The old version will be garbage collected once the existing readers + // (if any) are done with it. + } + _, _ = read, insert +} diff --git a/libgo/go/sync/atomic/value_test.go b/libgo/go/sync/atomic/value_test.go index fd90451..fd69ba3 100644 --- a/libgo/go/sync/atomic/value_test.go +++ b/libgo/go/sync/atomic/value_test.go @@ -7,10 +7,8 @@ package atomic_test import ( "math/rand" "runtime" - "sync" . "sync/atomic" "testing" - "time" ) func TestValue(t *testing.T) { @@ -133,68 +131,3 @@ func BenchmarkValueRead(b *testing.B) { } }) } - -// The following example shows how to use Value for periodic program config updates -// and propagation of the changes to worker goroutines. -func ExampleValue_config() { - var config Value // holds current server configuration - // Create initial config value and store into config. - config.Store(loadConfig()) - go func() { - // Reload config every 10 seconds - // and update config value with the new version. - for { - time.Sleep(10 * time.Second) - config.Store(loadConfig()) - } - }() - // Create worker goroutines that handle incoming requests - // using the latest config value. - for i := 0; i < 10; i++ { - go func() { - for r := range requests() { - c := config.Load() - // Handle request r using config c. - _, _ = r, c - } - }() - } -} - -func loadConfig() map[string]string { - return make(map[string]string) -} - -func requests() chan int { - return make(chan int) -} - -// The following example shows how to maintain a scalable frequently read, -// but infrequently updated data structure using copy-on-write idiom. -func ExampleValue_readMostly() { - type Map map[string]string - var m Value - m.Store(make(Map)) - var mu sync.Mutex // used only by writers - // read function can be used to read the data without further synchronization - read := func(key string) (val string) { - m1 := m.Load().(Map) - return m1[key] - } - // insert function can be used to update the data without further synchronization - insert := func(key, val string) { - mu.Lock() // synchronize with other potential writers - defer mu.Unlock() - m1 := m.Load().(Map) // load current value of the data structure - m2 := make(Map) // create a new value - for k, v := range m1 { - m2[k] = v // copy all data from the current object to the new one - } - m2[key] = val // do the update that we need - m.Store(m2) // atomically replace the current object with the new one - // At this point all new readers start working with the new version. - // The old version will be garbage collected once the existing readers - // (if any) are done with it. - } - _, _ = read, insert -} diff --git a/libgo/go/sync/cond.go b/libgo/go/sync/cond.go index 3dcbf1c..b254c93 100644 --- a/libgo/go/sync/cond.go +++ b/libgo/go/sync/cond.go @@ -94,4 +94,5 @@ func (c *copyChecker) check() { type noCopy struct{} // Lock is a no-op used by -copylocks checker from `go vet`. -func (*noCopy) Lock() {} +func (*noCopy) Lock() {} +func (*noCopy) Unlock() {} diff --git a/libgo/go/sync/cond_test.go b/libgo/go/sync/cond_test.go index 9019f8f..9d0d9ad 100644 --- a/libgo/go/sync/cond_test.go +++ b/libgo/go/sync/cond_test.go @@ -4,9 +4,9 @@ package sync_test import ( - . "sync" - + "reflect" "runtime" + . "sync" "testing" "time" ) @@ -251,7 +251,8 @@ func TestCondCopy(t *testing.T) { }() c := Cond{L: &Mutex{}} c.Signal() - c2 := c + var c2 Cond + reflect.ValueOf(&c2).Elem().Set(reflect.ValueOf(&c).Elem()) // c2 := c, hidden from vet c2.Signal() } diff --git a/libgo/go/sync/rwmutex.go b/libgo/go/sync/rwmutex.go index 4e9e819..16a2f92 100644 --- a/libgo/go/sync/rwmutex.go +++ b/libgo/go/sync/rwmutex.go @@ -47,7 +47,7 @@ func (rw *RWMutex) RLock() { } if atomic.AddInt32(&rw.readerCount, 1) < 0 { // A writer is pending, wait for it. - runtime_Semacquire(&rw.readerSem) + runtime_SemacquireMutex(&rw.readerSem, false) } if race.Enabled { race.Enable() @@ -95,7 +95,7 @@ func (rw *RWMutex) Lock() { r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders // Wait for active readers. if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 { - runtime_Semacquire(&rw.writerSem) + runtime_SemacquireMutex(&rw.writerSem, false) } if race.Enabled { race.Enable() @@ -114,7 +114,6 @@ func (rw *RWMutex) Unlock() { if race.Enabled { _ = rw.w.state race.Release(unsafe.Pointer(&rw.readerSem)) - race.Release(unsafe.Pointer(&rw.writerSem)) race.Disable() } diff --git a/libgo/go/sync/waitgroup.go b/libgo/go/sync/waitgroup.go index 2fa7c3e..99dd400 100644 --- a/libgo/go/sync/waitgroup.go +++ b/libgo/go/sync/waitgroup.go @@ -23,16 +23,17 @@ type WaitGroup struct { // 64-bit value: high 32 bits are counter, low 32 bits are waiter count. // 64-bit atomic operations require 64-bit alignment, but 32-bit // compilers do not ensure it. So we allocate 12 bytes and then use - // the aligned 8 bytes in them as state. - state1 [12]byte - sema uint32 + // the aligned 8 bytes in them as state, and the other 4 as storage + // for the sema. + state1 [3]uint32 } -func (wg *WaitGroup) state() *uint64 { +// state returns pointers to the state and sema fields stored within wg.state1. +func (wg *WaitGroup) state() (statep *uint64, semap *uint32) { if uintptr(unsafe.Pointer(&wg.state1))%8 == 0 { - return (*uint64)(unsafe.Pointer(&wg.state1)) + return (*uint64)(unsafe.Pointer(&wg.state1)), &wg.state1[2] } else { - return (*uint64)(unsafe.Pointer(&wg.state1[4])) + return (*uint64)(unsafe.Pointer(&wg.state1[1])), &wg.state1[0] } } @@ -50,7 +51,7 @@ func (wg *WaitGroup) state() *uint64 { // new Add calls must happen after all previous Wait calls have returned. // See the WaitGroup example. func (wg *WaitGroup) Add(delta int) { - statep := wg.state() + statep, semap := wg.state() if race.Enabled { _ = *statep // trigger nil deref early if delta < 0 { @@ -67,7 +68,7 @@ func (wg *WaitGroup) Add(delta int) { // The first increment must be synchronized with Wait. // Need to model this as a read, because there can be // several concurrent wg.counter transitions from 0. - race.Read(unsafe.Pointer(&wg.sema)) + race.Read(unsafe.Pointer(semap)) } if v < 0 { panic("sync: negative WaitGroup counter") @@ -89,7 +90,7 @@ func (wg *WaitGroup) Add(delta int) { // Reset waiters count to 0. *statep = 0 for ; w != 0; w-- { - runtime_Semrelease(&wg.sema, false) + runtime_Semrelease(semap, false) } } @@ -100,7 +101,7 @@ func (wg *WaitGroup) Done() { // Wait blocks until the WaitGroup counter is zero. func (wg *WaitGroup) Wait() { - statep := wg.state() + statep, semap := wg.state() if race.Enabled { _ = *statep // trigger nil deref early race.Disable() @@ -124,9 +125,9 @@ func (wg *WaitGroup) Wait() { // Need to model this is as a write to race with the read in Add. // As a consequence, can do the write only for the first waiter, // otherwise concurrent Waits will race with each other. - race.Write(unsafe.Pointer(&wg.sema)) + race.Write(unsafe.Pointer(semap)) } - runtime_Semacquire(&wg.sema) + runtime_Semacquire(semap) if *statep != 0 { panic("sync: WaitGroup is reused before previous Wait has returned") } diff --git a/libgo/go/sync/waitgroup_test.go b/libgo/go/sync/waitgroup_test.go index e3e3096..4ab438c 100644 --- a/libgo/go/sync/waitgroup_test.go +++ b/libgo/go/sync/waitgroup_test.go @@ -68,6 +68,21 @@ func TestWaitGroupMisuse(t *testing.T) { t.Fatal("Should panic") } +// pollUntilEqual blocks until v, loaded atomically, is +// equal to the target. +func pollUntilEqual(v *uint32, target uint32) { + for { + for i := 0; i < 1e3; i++ { + if atomic.LoadUint32(v) == target { + return + } + } + // yield to avoid deadlock with the garbage collector + // see issue #20072 + runtime.Gosched() + } +} + func TestWaitGroupMisuse2(t *testing.T) { knownRacy(t) if runtime.NumCPU() <= 4 { @@ -94,9 +109,7 @@ func TestWaitGroupMisuse2(t *testing.T) { done <- recover() }() atomic.AddUint32(&here, 1) - for atomic.LoadUint32(&here) != 3 { - // spin - } + pollUntilEqual(&here, 3) wg.Wait() }() go func() { @@ -104,16 +117,12 @@ func TestWaitGroupMisuse2(t *testing.T) { done <- recover() }() atomic.AddUint32(&here, 1) - for atomic.LoadUint32(&here) != 3 { - // spin - } + pollUntilEqual(&here, 3) wg.Add(1) // This is the bad guy. wg.Done() }() atomic.AddUint32(&here, 1) - for atomic.LoadUint32(&here) != 3 { - // spin - } + pollUntilEqual(&here, 3) wg.Done() for j := 0; j < 2; j++ { if err := <-done; err != nil { diff --git a/libgo/go/syscall/dirent.go b/libgo/go/syscall/dirent.go index a09bf05..5c7af42 100644 --- a/libgo/go/syscall/dirent.go +++ b/libgo/go/syscall/dirent.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package syscall diff --git a/libgo/go/syscall/endian_little.go b/libgo/go/syscall/endian_little.go index a5d32ae..0cd2d75 100644 --- a/libgo/go/syscall/endian_little.go +++ b/libgo/go/syscall/endian_little.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // -// +build 386 alpha amd64 amd64p32 arm arm64 ia64 mips64le mipsle mips64p32le nios2 ppc64le riscv64 sh +// +build 386 alpha amd64 amd64p32 arm arm64 ia64 mips64le mipsle mips64p32le nios2 ppc64le riscv64 sh wasm package syscall diff --git a/libgo/go/syscall/env_unix.go b/libgo/go/syscall/env_unix.go index eb93e2e..0b6b711 100644 --- a/libgo/go/syscall/env_unix.go +++ b/libgo/go/syscall/env_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris // Unix environment variables. diff --git a/libgo/go/syscall/env_windows.go b/libgo/go/syscall/env_windows.go index 1606b42..74b154e 100644 --- a/libgo/go/syscall/env_windows.go +++ b/libgo/go/syscall/env_windows.go @@ -57,7 +57,7 @@ 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 + // https://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx for j := 1; j < len(s); j++ { if s[j] == '=' { Unsetenv(s[0:j]) diff --git a/libgo/go/syscall/exec_linux_test.go b/libgo/go/syscall/exec_linux_test.go index 17df8f4..f551e87 100644 --- a/libgo/go/syscall/exec_linux_test.go +++ b/libgo/go/syscall/exec_linux_test.go @@ -84,6 +84,15 @@ func checkUserNS(t *testing.T) { t.Skip("kernel doesn't support user namespaces") } } + + // On Centos 7.5+, user namespaces are disabled if user.max_user_namespaces = 0 + if _, err := os.Stat("/proc/sys/user/max_user_namespaces"); err == nil { + buf, errRead := ioutil.ReadFile("/proc/sys/user/max_user_namespaces") + if errRead == nil && buf[0] == '0' { + t.Skip("kernel doesn't support user namespaces") + } + } + // When running under the Go continuous build, skip tests for // now when under Kubernetes. (where things are root but not quite) // Both of these are our own environment variables. diff --git a/libgo/go/syscall/exec_windows.go b/libgo/go/syscall/exec_windows.go index 91b0e84..c78bad8 100644 --- a/libgo/go/syscall/exec_windows.go +++ b/libgo/go/syscall/exec_windows.go @@ -15,7 +15,7 @@ import ( var ForkLock sync.RWMutex // EscapeArg rewrites command line argument s as prescribed -// in http://msdn.microsoft.com/en-us/library/ms880421. +// in https://msdn.microsoft.com/en-us/library/ms880421. // This function returns "" (2 double quotes) if s is empty. // Alternatively, these transformations are done: // - every back slash (\) is doubled, but only if immediately diff --git a/libgo/go/syscall/export_linux_test.go b/libgo/go/syscall/export_linux_test.go new file mode 100644 index 0000000..274849e --- /dev/null +++ b/libgo/go/syscall/export_linux_test.go @@ -0,0 +1,9 @@ +// Copyright 2018 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 syscall + +var RawSyscallNoError = rawSyscallNoError + +const Sys_GETEUID = sys_GETEUID diff --git a/libgo/go/syscall/forkpipe_bsd.go b/libgo/go/syscall/forkpipe.go index d479284..71890a2 100644 --- a/libgo/go/syscall/forkpipe_bsd.go +++ b/libgo/go/syscall/forkpipe.go @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly netbsd openbsd solaris +// +build darwin dragonfly solaris package syscall +// Try to open a pipe with O_CLOEXEC set on both file descriptors. func forkExecPipe(p []int) error { err := Pipe(p) if err != nil { diff --git a/libgo/go/syscall/exec_freebsd.go b/libgo/go/syscall/forkpipe2.go index 1654b4b..c9a0c49 100644 --- a/libgo/go/syscall/exec_freebsd.go +++ b/libgo/go/syscall/forkpipe2.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build freebsd netbsd openbsd + package syscall func forkExecPipe(p []int) error { diff --git a/libgo/go/syscall/fs_js.go b/libgo/go/syscall/fs_js.go new file mode 100644 index 0000000..00d6c76 --- /dev/null +++ b/libgo/go/syscall/fs_js.go @@ -0,0 +1,501 @@ +// Copyright 2018 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 js,wasm + +package syscall + +import ( + "errors" + "io" + "sync" + "syscall/js" +) + +// Provided by package runtime. +func now() (sec int64, nsec int32) + +var jsProcess = js.Global().Get("process") +var jsFS = js.Global().Get("fs") +var constants = jsFS.Get("constants") + +var ( + nodeWRONLY = constants.Get("O_WRONLY").Int() + nodeRDWR = constants.Get("O_RDWR").Int() + nodeCREATE = constants.Get("O_CREAT").Int() + nodeTRUNC = constants.Get("O_TRUNC").Int() + nodeAPPEND = constants.Get("O_APPEND").Int() + nodeEXCL = constants.Get("O_EXCL").Int() +) + +type jsFile struct { + path string + entries []string + pos int64 + seeked bool +} + +var filesMu sync.Mutex +var files = map[int]*jsFile{ + 0: &jsFile{}, + 1: &jsFile{}, + 2: &jsFile{}, +} + +func fdToFile(fd int) (*jsFile, error) { + filesMu.Lock() + f, ok := files[fd] + filesMu.Unlock() + if !ok { + return nil, EBADF + } + return f, nil +} + +func Open(path string, openmode int, perm uint32) (int, error) { + if err := checkPath(path); err != nil { + return 0, err + } + + flags := 0 + if openmode&O_WRONLY != 0 { + flags |= nodeWRONLY + } + if openmode&O_RDWR != 0 { + flags |= nodeRDWR + } + if openmode&O_CREATE != 0 { + flags |= nodeCREATE + } + if openmode&O_TRUNC != 0 { + flags |= nodeTRUNC + } + if openmode&O_APPEND != 0 { + flags |= nodeAPPEND + } + if openmode&O_EXCL != 0 { + flags |= nodeEXCL + } + if openmode&O_SYNC != 0 { + return 0, errors.New("syscall.Open: O_SYNC is not supported by js/wasm") + } + + jsFD, err := fsCall("openSync", path, flags, perm) + if err != nil { + return 0, err + } + fd := jsFD.Int() + + var entries []string + if stat, err := fsCall("fstatSync", fd); err == nil && stat.Call("isDirectory").Bool() { + dir, err := fsCall("readdirSync", path) + if err != nil { + return 0, err + } + entries = make([]string, dir.Length()) + for i := range entries { + entries[i] = dir.Index(i).String() + } + } + + f := &jsFile{ + path: path, + entries: entries, + } + filesMu.Lock() + files[fd] = f + filesMu.Unlock() + return fd, nil +} + +func Close(fd int) error { + filesMu.Lock() + delete(files, fd) + filesMu.Unlock() + _, err := fsCall("closeSync", fd) + return err +} + +func CloseOnExec(fd int) { + // nothing to do - no exec +} + +func Mkdir(path string, perm uint32) error { + if err := checkPath(path); err != nil { + return err + } + _, err := fsCall("mkdirSync", path, perm) + return err +} + +func ReadDirent(fd int, buf []byte) (int, error) { + f, err := fdToFile(fd) + if err != nil { + return 0, err + } + if f.entries == nil { + return 0, EINVAL + } + + n := 0 + for len(f.entries) > 0 { + entry := f.entries[0] + l := 2 + len(entry) + if l > len(buf) { + break + } + buf[0] = byte(l) + buf[1] = byte(l >> 8) + copy(buf[2:], entry) + buf = buf[l:] + n += l + f.entries = f.entries[1:] + } + + return n, nil +} + +func setStat(st *Stat_t, jsSt js.Value) { + st.Dev = int64(jsSt.Get("dev").Int()) + st.Ino = uint64(jsSt.Get("ino").Int()) + st.Mode = uint32(jsSt.Get("mode").Int()) + st.Nlink = uint32(jsSt.Get("nlink").Int()) + st.Uid = uint32(jsSt.Get("uid").Int()) + st.Gid = uint32(jsSt.Get("gid").Int()) + st.Rdev = int64(jsSt.Get("rdev").Int()) + st.Size = int64(jsSt.Get("size").Int()) + st.Blksize = int32(jsSt.Get("blksize").Int()) + st.Blocks = int32(jsSt.Get("blocks").Int()) + atime := int64(jsSt.Get("atimeMs").Int()) + st.Atime = atime / 1000 + st.AtimeNsec = (atime % 1000) * 1000000 + mtime := int64(jsSt.Get("mtimeMs").Int()) + st.Mtime = mtime / 1000 + st.MtimeNsec = (mtime % 1000) * 1000000 + ctime := int64(jsSt.Get("ctimeMs").Int()) + st.Ctime = ctime / 1000 + st.CtimeNsec = (ctime % 1000) * 1000000 +} + +func Stat(path string, st *Stat_t) error { + if err := checkPath(path); err != nil { + return err + } + jsSt, err := fsCall("statSync", path) + if err != nil { + return err + } + setStat(st, jsSt) + return nil +} + +func Lstat(path string, st *Stat_t) error { + if err := checkPath(path); err != nil { + return err + } + jsSt, err := fsCall("lstatSync", path) + if err != nil { + return err + } + setStat(st, jsSt) + return nil +} + +func Fstat(fd int, st *Stat_t) error { + jsSt, err := fsCall("fstatSync", fd) + if err != nil { + return err + } + setStat(st, jsSt) + return nil +} + +func Unlink(path string) error { + if err := checkPath(path); err != nil { + return err + } + _, err := fsCall("unlinkSync", path) + return err +} + +func Rmdir(path string) error { + if err := checkPath(path); err != nil { + return err + } + _, err := fsCall("rmdirSync", path) + return err +} + +func Chmod(path string, mode uint32) error { + if err := checkPath(path); err != nil { + return err + } + _, err := fsCall("chmodSync", path, mode) + return err +} + +func Fchmod(fd int, mode uint32) error { + _, err := fsCall("fchmodSync", fd, mode) + return err +} + +func Chown(path string, uid, gid int) error { + if err := checkPath(path); err != nil { + return err + } + return ENOSYS +} + +func Fchown(fd int, uid, gid int) error { + return ENOSYS +} + +func Lchown(path string, uid, gid int) error { + if err := checkPath(path); err != nil { + return err + } + return ENOSYS +} + +func UtimesNano(path string, ts []Timespec) error { + if err := checkPath(path); err != nil { + return err + } + if len(ts) != 2 { + return EINVAL + } + atime := ts[0].Sec + mtime := ts[1].Sec + _, err := fsCall("utimesSync", path, atime, mtime) + return err +} + +func Rename(from, to string) error { + if err := checkPath(from); err != nil { + return err + } + if err := checkPath(to); err != nil { + return err + } + _, err := fsCall("renameSync", from, to) + return err +} + +func Truncate(path string, length int64) error { + if err := checkPath(path); err != nil { + return err + } + _, err := fsCall("truncateSync", path, length) + return err +} + +func Ftruncate(fd int, length int64) error { + _, err := fsCall("ftruncateSync", fd, length) + return err +} + +func Getcwd(buf []byte) (n int, err error) { + defer recoverErr(&err) + cwd := jsProcess.Call("cwd").String() + n = copy(buf, cwd) + return n, nil +} + +func Chdir(path string) (err error) { + if err := checkPath(path); err != nil { + return err + } + defer recoverErr(&err) + jsProcess.Call("chdir", path) + return +} + +func Fchdir(fd int) error { + f, err := fdToFile(fd) + if err != nil { + return err + } + return Chdir(f.path) +} + +func Readlink(path string, buf []byte) (n int, err error) { + if err := checkPath(path); err != nil { + return 0, err + } + dst, err := fsCall("readlinkSync", path) + if err != nil { + return 0, err + } + n = copy(buf, dst.String()) + return n, nil +} + +func Link(path, link string) error { + if err := checkPath(path); err != nil { + return err + } + if err := checkPath(link); err != nil { + return err + } + _, err := fsCall("linkSync", path, link) + return err +} + +func Symlink(path, link string) error { + if err := checkPath(path); err != nil { + return err + } + if err := checkPath(link); err != nil { + return err + } + _, err := fsCall("symlinkSync", path, link) + return err +} + +func Fsync(fd int) error { + _, err := fsCall("fsyncSync", fd) + return err +} + +func Read(fd int, b []byte) (int, error) { + f, err := fdToFile(fd) + if err != nil { + return 0, err + } + + if f.seeked { + n, err := Pread(fd, b, f.pos) + f.pos += int64(n) + return n, err + } + + a := js.TypedArrayOf(b) + n, err := fsCall("readSync", fd, a, 0, len(b)) + a.Release() + if err != nil { + return 0, err + } + n2 := n.Int() + f.pos += int64(n2) + return n2, err +} + +func Write(fd int, b []byte) (int, error) { + f, err := fdToFile(fd) + if err != nil { + return 0, err + } + + if f.seeked { + n, err := Pwrite(fd, b, f.pos) + f.pos += int64(n) + return n, err + } + + a := js.TypedArrayOf(b) + n, err := fsCall("writeSync", fd, a, 0, len(b)) + a.Release() + if err != nil { + return 0, err + } + n2 := n.Int() + f.pos += int64(n2) + return n2, err +} + +func Pread(fd int, b []byte, offset int64) (int, error) { + a := js.TypedArrayOf(b) + n, err := fsCall("readSync", fd, a, 0, len(b), offset) + a.Release() + if err != nil { + return 0, err + } + return n.Int(), nil +} + +func Pwrite(fd int, b []byte, offset int64) (int, error) { + a := js.TypedArrayOf(b) + n, err := fsCall("writeSync", fd, a, 0, len(b), offset) + a.Release() + if err != nil { + return 0, err + } + return n.Int(), nil +} + +func Seek(fd int, offset int64, whence int) (int64, error) { + f, err := fdToFile(fd) + if err != nil { + return 0, err + } + + var newPos int64 + switch whence { + case io.SeekStart: + newPos = offset + case io.SeekCurrent: + newPos = f.pos + offset + case io.SeekEnd: + var st Stat_t + if err := Fstat(fd, &st); err != nil { + return 0, err + } + newPos = st.Size + offset + default: + return 0, errnoErr(EINVAL) + } + + if newPos < 0 { + return 0, errnoErr(EINVAL) + } + + f.seeked = true + f.pos = newPos + return newPos, nil +} + +func Dup(fd int) (int, error) { + return 0, ENOSYS +} + +func Dup2(fd, newfd int) error { + return ENOSYS +} + +func Pipe(fd []int) error { + return ENOSYS +} + +func fsCall(name string, args ...interface{}) (res js.Value, err error) { + defer recoverErr(&err) + res = jsFS.Call(name, args...) + return +} + +// checkPath checks that the path is not empty and that it contains no null characters. +func checkPath(path string) error { + if path == "" { + return EINVAL + } + for i := 0; i < len(path); i++ { + if path[i] == '\x00' { + return EINVAL + } + } + return nil +} + +func recoverErr(errPtr *error) { + if err := recover(); err != nil { + jsErr, ok := err.(js.Error) + if !ok { + panic(err) + } + errno, ok := errnoByCode[jsErr.Get("code").String()] + if !ok { + panic(err) + } + *errPtr = errnoErr(Errno(errno)) + } +} diff --git a/libgo/go/syscall/js/callback.go b/libgo/go/syscall/js/callback.go new file mode 100644 index 0000000..9d57307 --- /dev/null +++ b/libgo/go/syscall/js/callback.go @@ -0,0 +1,122 @@ +// Copyright 2018 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 js,wasm + +package js + +import "sync" + +var ( + pendingCallbacks = Global().Get("Array").New() + makeCallbackHelper = Global().Get("Go").Get("_makeCallbackHelper") + makeEventCallbackHelper = Global().Get("Go").Get("_makeEventCallbackHelper") +) + +var ( + callbacksMu sync.Mutex + callbacks = make(map[uint32]func([]Value)) + nextCallbackID uint32 = 1 +) + +// Callback is a Go function that got wrapped for use as a JavaScript callback. +type Callback struct { + Value // the JavaScript function that queues the callback for execution + id uint32 +} + +// NewCallback returns a wrapped callback function. +// +// Invoking the callback in JavaScript will queue the Go function fn for execution. +// This execution happens asynchronously on a special goroutine that handles all callbacks and preserves +// the order in which the callbacks got called. +// As a consequence, if one callback blocks this goroutine, other callbacks will not be processed. +// A blocking callback should therefore explicitly start a new goroutine. +// +// Callback.Release must be called to free up resources when the callback will not be used any more. +func NewCallback(fn func(args []Value)) Callback { + callbackLoopOnce.Do(func() { + go callbackLoop() + }) + + callbacksMu.Lock() + id := nextCallbackID + nextCallbackID++ + callbacks[id] = fn + callbacksMu.Unlock() + return Callback{ + Value: makeCallbackHelper.Invoke(id, pendingCallbacks, jsGo), + id: id, + } +} + +type EventCallbackFlag int + +const ( + // PreventDefault can be used with NewEventCallback to call event.preventDefault synchronously. + PreventDefault EventCallbackFlag = 1 << iota + // StopPropagation can be used with NewEventCallback to call event.stopPropagation synchronously. + StopPropagation + // StopImmediatePropagation can be used with NewEventCallback to call event.stopImmediatePropagation synchronously. + StopImmediatePropagation +) + +// NewEventCallback returns a wrapped callback function, just like NewCallback, but the callback expects to have +// exactly one argument, the event. Depending on flags, it will synchronously call event.preventDefault, +// event.stopPropagation and/or event.stopImmediatePropagation before queuing the Go function fn for execution. +func NewEventCallback(flags EventCallbackFlag, fn func(event Value)) Callback { + c := NewCallback(func(args []Value) { + fn(args[0]) + }) + return Callback{ + Value: makeEventCallbackHelper.Invoke( + flags&PreventDefault != 0, + flags&StopPropagation != 0, + flags&StopImmediatePropagation != 0, + c, + ), + id: c.id, + } +} + +// Release frees up resources allocated for the callback. +// The callback must not be invoked after calling Release. +func (c Callback) Release() { + callbacksMu.Lock() + delete(callbacks, c.id) + callbacksMu.Unlock() +} + +var callbackLoopOnce sync.Once + +func callbackLoop() { + for !jsGo.Get("_callbackShutdown").Bool() { + sleepUntilCallback() + for { + cb := pendingCallbacks.Call("shift") + if cb == Undefined() { + break + } + + id := uint32(cb.Get("id").Int()) + callbacksMu.Lock() + f, ok := callbacks[id] + callbacksMu.Unlock() + if !ok { + Global().Get("console").Call("error", "call to closed callback") + continue + } + + argsObj := cb.Get("args") + args := make([]Value, argsObj.Length()) + for i := range args { + args[i] = argsObj.Index(i) + } + f(args) + } + } +} + +// sleepUntilCallback is defined in the runtime package +func sleepUntilCallback() diff --git a/libgo/go/syscall/js/js.go b/libgo/go/syscall/js/js.go new file mode 100644 index 0000000..336586ca --- /dev/null +++ b/libgo/go/syscall/js/js.go @@ -0,0 +1,382 @@ +// Copyright 2018 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 js,wasm + +// Package js gives access to the WebAssembly host environment when using the js/wasm architecture. +// Its API is based on JavaScript semantics. +// +// This package is EXPERIMENTAL. Its current scope is only to allow tests to run, but not yet to provide a +// comprehensive API for users. It is exempt from the Go compatibility promise. +package js + +import ( + "unsafe" +) + +// ref is used to identify a JavaScript value, since the value itself can not be passed to WebAssembly. +// A JavaScript number (64-bit float, except NaN) is represented by its IEEE 754 binary representation. +// All other values are represented as an IEEE 754 binary representation of NaN with bits 0-31 used as +// an ID and bits 32-33 used to differentiate between string, symbol, function and object. +type ref uint64 + +// nanHead are the upper 32 bits of a ref which are set if the value is not a JavaScript number or NaN itself. +const nanHead = 0x7FF80000 + +// Value represents a JavaScript value. +type Value struct { + ref ref +} + +func makeValue(v ref) Value { + return Value{ref: v} +} + +func predefValue(id uint32) Value { + return Value{ref: nanHead<<32 | ref(id)} +} + +func floatValue(f float64) Value { + if f != f { + return valueNaN + } + return Value{ref: *(*ref)(unsafe.Pointer(&f))} +} + +// Error wraps a JavaScript error. +type Error struct { + // Value is the underlying JavaScript error value. + Value +} + +// Error implements the error interface. +func (e Error) Error() string { + return "JavaScript error: " + e.Get("message").String() +} + +var ( + valueNaN = predefValue(0) + valueUndefined = predefValue(1) + valueNull = predefValue(2) + valueTrue = predefValue(3) + valueFalse = predefValue(4) + valueGlobal = predefValue(5) + memory = predefValue(6) // WebAssembly linear memory + jsGo = predefValue(7) // instance of the Go class in JavaScript + + objectConstructor = valueGlobal.Get("Object") + arrayConstructor = valueGlobal.Get("Array") +) + +// Undefined returns the JavaScript value "undefined". +func Undefined() Value { + return valueUndefined +} + +// Null returns the JavaScript value "null". +func Null() Value { + return valueNull +} + +// Global returns the JavaScript global object, usually "window" or "global". +func Global() Value { + return valueGlobal +} + +// ValueOf returns x as a JavaScript value: +// +// | Go | JavaScript | +// | ---------------------- | ---------------------- | +// | js.Value | [its value] | +// | js.TypedArray | typed array | +// | js.Callback | function | +// | nil | null | +// | bool | boolean | +// | integers and floats | number | +// | string | string | +// | []interface{} | new array | +// | map[string]interface{} | new object | +func ValueOf(x interface{}) Value { + switch x := x.(type) { + case Value: + return x + case TypedArray: + return x.Value + case Callback: + return x.Value + case nil: + return valueNull + case bool: + if x { + return valueTrue + } else { + return valueFalse + } + case int: + return floatValue(float64(x)) + case int8: + return floatValue(float64(x)) + case int16: + return floatValue(float64(x)) + case int32: + return floatValue(float64(x)) + case int64: + return floatValue(float64(x)) + case uint: + return floatValue(float64(x)) + case uint8: + return floatValue(float64(x)) + case uint16: + return floatValue(float64(x)) + case uint32: + return floatValue(float64(x)) + case uint64: + return floatValue(float64(x)) + case uintptr: + return floatValue(float64(x)) + case unsafe.Pointer: + return floatValue(float64(uintptr(x))) + case float32: + return floatValue(float64(x)) + case float64: + return floatValue(x) + case string: + return makeValue(stringVal(x)) + case []interface{}: + a := arrayConstructor.New(len(x)) + for i, s := range x { + a.SetIndex(i, s) + } + return a + case map[string]interface{}: + o := objectConstructor.New() + for k, v := range x { + o.Set(k, v) + } + return o + default: + panic("ValueOf: invalid value") + } +} + +func stringVal(x string) ref + +// Type represents the JavaScript type of a Value. +type Type int + +const ( + TypeUndefined Type = iota + TypeNull + TypeBoolean + TypeNumber + TypeString + TypeSymbol + TypeObject + TypeFunction +) + +func (t Type) String() string { + switch t { + case TypeUndefined: + return "undefined" + case TypeNull: + return "null" + case TypeBoolean: + return "boolean" + case TypeNumber: + return "number" + case TypeString: + return "string" + case TypeSymbol: + return "symbol" + case TypeObject: + return "object" + case TypeFunction: + return "function" + default: + panic("bad type") + } +} + +// Type returns the JavaScript type of the value v. It is similar to JavaScript's typeof operator, +// except that it returns TypeNull instead of TypeObject for null. +func (v Value) Type() Type { + switch v.ref { + case valueUndefined.ref: + return TypeUndefined + case valueNull.ref: + return TypeNull + case valueTrue.ref, valueFalse.ref: + return TypeBoolean + } + if v.isNumber() { + return TypeNumber + } + typeFlag := v.ref >> 32 & 3 + switch typeFlag { + case 1: + return TypeString + case 2: + return TypeSymbol + case 3: + return TypeFunction + default: + return TypeObject + } +} + +// Get returns the JavaScript property p of value v. +func (v Value) Get(p string) Value { + return makeValue(valueGet(v.ref, p)) +} + +func valueGet(v ref, p string) ref + +// Set sets the JavaScript property p of value v to ValueOf(x). +func (v Value) Set(p string, x interface{}) { + valueSet(v.ref, p, ValueOf(x).ref) +} + +func valueSet(v ref, p string, x ref) + +// Index returns JavaScript index i of value v. +func (v Value) Index(i int) Value { + return makeValue(valueIndex(v.ref, i)) +} + +func valueIndex(v ref, i int) ref + +// SetIndex sets the JavaScript index i of value v to ValueOf(x). +func (v Value) SetIndex(i int, x interface{}) { + valueSetIndex(v.ref, i, ValueOf(x).ref) +} + +func valueSetIndex(v ref, i int, x ref) + +func makeArgs(args []interface{}) []ref { + argVals := make([]ref, len(args)) + for i, arg := range args { + argVals[i] = ValueOf(arg).ref + } + return argVals +} + +// Length returns the JavaScript property "length" of v. +func (v Value) Length() int { + return valueLength(v.ref) +} + +func valueLength(v ref) int + +// Call does a JavaScript call to the method m of value v with the given arguments. +// It panics if v has no method m. +// The arguments get mapped to JavaScript values according to the ValueOf function. +func (v Value) Call(m string, args ...interface{}) Value { + res, ok := valueCall(v.ref, m, makeArgs(args)) + if !ok { + if vType := v.Type(); vType != TypeObject && vType != TypeFunction { // check here to avoid overhead in success case + panic(&ValueError{"Value.Call", vType}) + } + if propType := v.Get(m).Type(); propType != TypeFunction { + panic("syscall/js: Value.Call: property " + m + " is not a function, got " + propType.String()) + } + panic(Error{makeValue(res)}) + } + return makeValue(res) +} + +func valueCall(v ref, m string, args []ref) (ref, bool) + +// Invoke does a JavaScript call of the value v with the given arguments. +// It panics if v is not a function. +// The arguments get mapped to JavaScript values according to the ValueOf function. +func (v Value) Invoke(args ...interface{}) Value { + res, ok := valueInvoke(v.ref, makeArgs(args)) + if !ok { + if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case + panic(&ValueError{"Value.Invoke", vType}) + } + panic(Error{makeValue(res)}) + } + return makeValue(res) +} + +func valueInvoke(v ref, args []ref) (ref, bool) + +// New uses JavaScript's "new" operator with value v as constructor and the given arguments. +// It panics if v is not a function. +// The arguments get mapped to JavaScript values according to the ValueOf function. +func (v Value) New(args ...interface{}) Value { + res, ok := valueNew(v.ref, makeArgs(args)) + if !ok { + panic(Error{makeValue(res)}) + } + return makeValue(res) +} + +func valueNew(v ref, args []ref) (ref, bool) + +func (v Value) isNumber() bool { + return v.ref>>32&nanHead != nanHead || v.ref == valueNaN.ref +} + +func (v Value) float(method string) float64 { + if !v.isNumber() { + panic(&ValueError{method, v.Type()}) + } + return *(*float64)(unsafe.Pointer(&v.ref)) +} + +// Float returns the value v as a float64. It panics if v is not a JavaScript number. +func (v Value) Float() float64 { + return v.float("Value.Float") +} + +// Int returns the value v truncated to an int. It panics if v is not a JavaScript number. +func (v Value) Int() int { + return int(v.float("Value.Int")) +} + +// Bool returns the value v as a bool. It panics if v is not a JavaScript boolean. +func (v Value) Bool() bool { + switch v.ref { + case valueTrue.ref: + return true + case valueFalse.ref: + return false + default: + panic(&ValueError{"Value.Bool", v.Type()}) + } +} + +// String returns the value v converted to string according to JavaScript type conversions. +func (v Value) String() string { + str, length := valuePrepareString(v.ref) + b := make([]byte, length) + valueLoadString(str, b) + return string(b) +} + +func valuePrepareString(v ref) (ref, int) + +func valueLoadString(v ref, b []byte) + +// InstanceOf reports whether v is an instance of type t according to JavaScript's instanceof operator. +func (v Value) InstanceOf(t Value) bool { + return valueInstanceOf(v.ref, t.ref) +} + +func valueInstanceOf(v ref, t ref) bool + +// A ValueError occurs when a Value method is invoked on +// a Value that does not support it. Such cases are documented +// in the description of each method. +type ValueError struct { + Method string + Type Type +} + +func (e *ValueError) Error() string { + return "syscall/js: call of " + e.Method + " on " + e.Type.String() +} diff --git a/libgo/go/syscall/js/js_test.go b/libgo/go/syscall/js/js_test.go new file mode 100644 index 0000000..9cc931a --- /dev/null +++ b/libgo/go/syscall/js/js_test.go @@ -0,0 +1,319 @@ +// Copyright 2018 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 js,wasm + +package js_test + +import ( + "fmt" + "math" + "syscall/js" + "testing" +) + +var dummys = js.Global().Call("eval", `({ + someBool: true, + someString: "abc\u1234", + someInt: 42, + someFloat: 42.123, + someArray: [41, 42, 43], + add: function(a, b) { + return a + b; + }, + NaN: NaN, +})`) + +func TestBool(t *testing.T) { + want := true + o := dummys.Get("someBool") + if got := o.Bool(); got != want { + t.Errorf("got %#v, want %#v", got, want) + } + dummys.Set("otherBool", want) + if got := dummys.Get("otherBool").Bool(); got != want { + t.Errorf("got %#v, want %#v", got, want) + } + if dummys.Get("someBool") != dummys.Get("someBool") { + t.Errorf("same value not equal") + } +} + +func TestString(t *testing.T) { + want := "abc\u1234" + o := dummys.Get("someString") + if got := o.String(); got != want { + t.Errorf("got %#v, want %#v", got, want) + } + dummys.Set("otherString", want) + if got := dummys.Get("otherString").String(); got != want { + t.Errorf("got %#v, want %#v", got, want) + } + if dummys.Get("someString") != dummys.Get("someString") { + t.Errorf("same value not equal") + } + + wantInt := "42" + o = dummys.Get("someInt") + if got := o.String(); got != wantInt { + t.Errorf("got %#v, want %#v", got, wantInt) + } +} + +func TestInt(t *testing.T) { + want := 42 + o := dummys.Get("someInt") + if got := o.Int(); got != want { + t.Errorf("got %#v, want %#v", got, want) + } + dummys.Set("otherInt", want) + if got := dummys.Get("otherInt").Int(); got != want { + t.Errorf("got %#v, want %#v", got, want) + } + if dummys.Get("someInt") != dummys.Get("someInt") { + t.Errorf("same value not equal") + } +} + +func TestIntConversion(t *testing.T) { + testIntConversion(t, 0) + testIntConversion(t, 1) + testIntConversion(t, -1) + testIntConversion(t, 1<<20) + testIntConversion(t, -1<<20) + testIntConversion(t, 1<<40) + testIntConversion(t, -1<<40) + testIntConversion(t, 1<<60) + testIntConversion(t, -1<<60) +} + +func testIntConversion(t *testing.T, want int) { + if got := js.ValueOf(want).Int(); got != want { + t.Errorf("got %#v, want %#v", got, want) + } +} + +func TestFloat(t *testing.T) { + want := 42.123 + o := dummys.Get("someFloat") + if got := o.Float(); got != want { + t.Errorf("got %#v, want %#v", got, want) + } + dummys.Set("otherFloat", want) + if got := dummys.Get("otherFloat").Float(); got != want { + t.Errorf("got %#v, want %#v", got, want) + } + if dummys.Get("someFloat") != dummys.Get("someFloat") { + t.Errorf("same value not equal") + } +} + +func TestObject(t *testing.T) { + if dummys.Get("someArray") != dummys.Get("someArray") { + t.Errorf("same value not equal") + } + + // An object and its prototype should not be equal. + proto := js.Global().Get("Object").Get("prototype") + o := js.Global().Call("eval", "new Object()") + if proto == o { + t.Errorf("object equals to its prototype") + } +} + +func TestFrozenObject(t *testing.T) { + o := js.Global().Call("eval", "(function () { let o = new Object(); o.field = 5; Object.freeze(o); return o; })()") + want := 5 + if got := o.Get("field").Int(); want != got { + t.Errorf("got %#v, want %#v", got, want) + } +} + +func TestTypedArrayOf(t *testing.T) { + testTypedArrayOf(t, "[]int8", []int8{0, -42, 0}, -42) + testTypedArrayOf(t, "[]int16", []int16{0, -42, 0}, -42) + testTypedArrayOf(t, "[]int32", []int32{0, -42, 0}, -42) + testTypedArrayOf(t, "[]uint8", []uint8{0, 42, 0}, 42) + testTypedArrayOf(t, "[]uint16", []uint16{0, 42, 0}, 42) + testTypedArrayOf(t, "[]uint32", []uint32{0, 42, 0}, 42) + testTypedArrayOf(t, "[]float32", []float32{0, -42.5, 0}, -42.5) + testTypedArrayOf(t, "[]float64", []float64{0, -42.5, 0}, -42.5) +} + +func testTypedArrayOf(t *testing.T, name string, slice interface{}, want float64) { + t.Run(name, func(t *testing.T) { + a := js.TypedArrayOf(slice) + got := a.Index(1).Float() + a.Release() + if got != want { + t.Errorf("got %#v, want %#v", got, want) + } + }) +} + +func TestNaN(t *testing.T) { + want := js.ValueOf(math.NaN()) + got := dummys.Get("NaN") + if got != want { + t.Errorf("got %#v, want %#v", got, want) + } +} + +func TestUndefined(t *testing.T) { + dummys.Set("test", js.Undefined()) + if dummys == js.Undefined() || dummys.Get("test") != js.Undefined() || dummys.Get("xyz") != js.Undefined() { + t.Errorf("js.Undefined expected") + } +} + +func TestNull(t *testing.T) { + dummys.Set("test1", nil) + dummys.Set("test2", js.Null()) + if dummys == js.Null() || dummys.Get("test1") != js.Null() || dummys.Get("test2") != js.Null() { + t.Errorf("js.Null expected") + } +} + +func TestLength(t *testing.T) { + if got := dummys.Get("someArray").Length(); got != 3 { + t.Errorf("got %#v, want %#v", got, 3) + } +} + +func TestIndex(t *testing.T) { + if got := dummys.Get("someArray").Index(1).Int(); got != 42 { + t.Errorf("got %#v, want %#v", got, 42) + } +} + +func TestSetIndex(t *testing.T) { + dummys.Get("someArray").SetIndex(2, 99) + if got := dummys.Get("someArray").Index(2).Int(); got != 99 { + t.Errorf("got %#v, want %#v", got, 99) + } +} + +func TestCall(t *testing.T) { + var i int64 = 40 + if got := dummys.Call("add", i, 2).Int(); got != 42 { + t.Errorf("got %#v, want %#v", got, 42) + } + if got := dummys.Call("add", js.Global().Call("eval", "40"), 2).Int(); got != 42 { + t.Errorf("got %#v, want %#v", got, 42) + } +} + +func TestInvoke(t *testing.T) { + var i int64 = 40 + if got := dummys.Get("add").Invoke(i, 2).Int(); got != 42 { + t.Errorf("got %#v, want %#v", got, 42) + } +} + +func TestNew(t *testing.T) { + if got := js.Global().Get("Array").New(42).Length(); got != 42 { + t.Errorf("got %#v, want %#v", got, 42) + } +} + +func TestInstanceOf(t *testing.T) { + someArray := js.Global().Get("Array").New() + if got, want := someArray.InstanceOf(js.Global().Get("Array")), true; got != want { + t.Errorf("got %#v, want %#v", got, want) + } + if got, want := someArray.InstanceOf(js.Global().Get("Function")), false; got != want { + t.Errorf("got %#v, want %#v", got, want) + } +} + +func TestType(t *testing.T) { + if got, want := js.Undefined().Type(), js.TypeUndefined; got != want { + t.Errorf("got %s, want %s", got, want) + } + if got, want := js.Null().Type(), js.TypeNull; got != want { + t.Errorf("got %s, want %s", got, want) + } + if got, want := js.ValueOf(true).Type(), js.TypeBoolean; got != want { + t.Errorf("got %s, want %s", got, want) + } + if got, want := js.ValueOf(42).Type(), js.TypeNumber; got != want { + t.Errorf("got %s, want %s", got, want) + } + if got, want := js.ValueOf("test").Type(), js.TypeString; got != want { + t.Errorf("got %s, want %s", got, want) + } + if got, want := js.Global().Get("Symbol").Invoke("test").Type(), js.TypeSymbol; got != want { + t.Errorf("got %s, want %s", got, want) + } + if got, want := js.Global().Get("Array").New().Type(), js.TypeObject; got != want { + t.Errorf("got %s, want %s", got, want) + } + if got, want := js.Global().Get("Array").Type(), js.TypeFunction; got != want { + t.Errorf("got %s, want %s", got, want) + } +} + +type object = map[string]interface{} +type array = []interface{} + +func TestValueOf(t *testing.T) { + a := js.ValueOf(array{0, array{0, 42, 0}, 0}) + if got := a.Index(1).Index(1).Int(); got != 42 { + t.Errorf("got %v, want %v", got, 42) + } + + o := js.ValueOf(object{"x": object{"y": 42}}) + if got := o.Get("x").Get("y").Int(); got != 42 { + t.Errorf("got %v, want %v", got, 42) + } +} + +func TestCallback(t *testing.T) { + c := make(chan struct{}) + cb := js.NewCallback(func(args []js.Value) { + if got := args[0].Int(); got != 42 { + t.Errorf("got %#v, want %#v", got, 42) + } + c <- struct{}{} + }) + defer cb.Release() + js.Global().Call("setTimeout", cb, 0, 42) + <-c +} + +func TestEventCallback(t *testing.T) { + for _, name := range []string{"preventDefault", "stopPropagation", "stopImmediatePropagation"} { + c := make(chan struct{}) + var flags js.EventCallbackFlag + switch name { + case "preventDefault": + flags = js.PreventDefault + case "stopPropagation": + flags = js.StopPropagation + case "stopImmediatePropagation": + flags = js.StopImmediatePropagation + } + cb := js.NewEventCallback(flags, func(event js.Value) { + c <- struct{}{} + }) + defer cb.Release() + + event := js.Global().Call("eval", fmt.Sprintf("({ called: false, %s: function() { this.called = true; } })", name)) + cb.Invoke(event) + if !event.Get("called").Bool() { + t.Errorf("%s not called", name) + } + + <-c + } +} + +func ExampleNewCallback() { + var cb js.Callback + cb = js.NewCallback(func(args []js.Value) { + fmt.Println("button clicked") + cb.Release() // release the callback if the button will not be clicked again + }) + js.Global().Get("document").Call("getElementById", "myButton").Call("addEventListener", "click", cb) +} diff --git a/libgo/go/syscall/js/typedarray.go b/libgo/go/syscall/js/typedarray.go new file mode 100644 index 0000000..afa1548 --- /dev/null +++ b/libgo/go/syscall/js/typedarray.go @@ -0,0 +1,102 @@ +// Copyright 2018 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 js,wasm + +package js + +import ( + "sync" + "unsafe" +) + +var ( + int8Array = Global().Get("Int8Array") + int16Array = Global().Get("Int16Array") + int32Array = Global().Get("Int32Array") + uint8Array = Global().Get("Uint8Array") + uint16Array = Global().Get("Uint16Array") + uint32Array = Global().Get("Uint32Array") + float32Array = Global().Get("Float32Array") + float64Array = Global().Get("Float64Array") +) + +// TypedArray represents a JavaScript typed array. +type TypedArray struct { + Value +} + +// Release frees up resources allocated for the typed array. +// The typed array and its buffer must not be accessed after calling Release. +func (a TypedArray) Release() { + openTypedArraysMutex.Lock() + delete(openTypedArrays, a) + openTypedArraysMutex.Unlock() +} + +var ( + openTypedArraysMutex sync.Mutex + openTypedArrays = make(map[TypedArray]interface{}) +) + +// TypedArrayOf returns a JavaScript typed array backed by the slice's underlying array. +// +// The supported types are []int8, []int16, []int32, []uint8, []uint16, []uint32, []float32 and []float64. +// Passing an unsupported value causes a panic. +// +// TypedArray.Release must be called to free up resources when the typed array will not be used any more. +func TypedArrayOf(slice interface{}) TypedArray { + a := TypedArray{typedArrayOf(slice)} + openTypedArraysMutex.Lock() + openTypedArrays[a] = slice + openTypedArraysMutex.Unlock() + return a +} + +func typedArrayOf(slice interface{}) Value { + switch slice := slice.(type) { + case []int8: + if len(slice) == 0 { + return int8Array.New(memory.Get("buffer"), 0, 0) + } + return int8Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice)) + case []int16: + if len(slice) == 0 { + return int16Array.New(memory.Get("buffer"), 0, 0) + } + return int16Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice)) + case []int32: + if len(slice) == 0 { + return int32Array.New(memory.Get("buffer"), 0, 0) + } + return int32Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice)) + case []uint8: + if len(slice) == 0 { + return uint8Array.New(memory.Get("buffer"), 0, 0) + } + return uint8Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice)) + case []uint16: + if len(slice) == 0 { + return uint16Array.New(memory.Get("buffer"), 0, 0) + } + return uint16Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice)) + case []uint32: + if len(slice) == 0 { + return uint32Array.New(memory.Get("buffer"), 0, 0) + } + return uint32Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice)) + case []float32: + if len(slice) == 0 { + return float32Array.New(memory.Get("buffer"), 0, 0) + } + return float32Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice)) + case []float64: + if len(slice) == 0 { + return float64Array.New(memory.Get("buffer"), 0, 0) + } + return float64Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice)) + default: + panic("TypedArrayOf: not a supported slice") + } +} diff --git a/libgo/go/syscall/libcall_linux.go b/libgo/go/syscall/libcall_linux.go index 5f47784..7903328 100644 --- a/libgo/go/syscall/libcall_linux.go +++ b/libgo/go/syscall/libcall_linux.go @@ -238,15 +238,6 @@ func Getdents(fd int, buf []byte) (n int, err error) { return } -func clen(n []byte) int { - for i := 0; i < len(n); i++ { - if n[i] == 0 { - return i - } - } - return len(n) -} - func ReadDirent(fd int, buf []byte) (n int, err error) { return Getdents(fd, buf) } diff --git a/libgo/go/syscall/net_js.go b/libgo/go/syscall/net_js.go new file mode 100644 index 0000000..d5bf1f4 --- /dev/null +++ b/libgo/go/syscall/net_js.go @@ -0,0 +1,128 @@ +// Copyright 2018 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. + +// js/wasm uses fake networking directly implemented in the net package. +// This file only exists to make the compiler happy. + +// +build js,wasm + +package syscall + +const ( + AF_UNSPEC = iota + AF_UNIX + AF_INET + AF_INET6 +) + +const ( + SOCK_STREAM = 1 + iota + SOCK_DGRAM + SOCK_RAW + SOCK_SEQPACKET +) + +const ( + IPPROTO_IP = 0 + IPPROTO_IPV4 = 4 + IPPROTO_IPV6 = 0x29 + IPPROTO_TCP = 6 + IPPROTO_UDP = 0x11 +) + +const ( + _ = iota + IPV6_V6ONLY + SOMAXCONN + SO_ERROR +) + +// Misc constants expected by package net but not supported. +const ( + _ = iota + F_DUPFD_CLOEXEC + SYS_FCNTL = 500 // unsupported; same value as net_nacl.go +) + +type Sockaddr interface { +} + +type SockaddrInet4 struct { + Port int + Addr [4]byte +} + +type SockaddrInet6 struct { + Port int + ZoneId uint32 + Addr [16]byte +} + +type SockaddrUnix struct { + Name string +} + +func Socket(proto, sotype, unused int) (fd int, err error) { + return 0, ENOSYS +} + +func Bind(fd int, sa Sockaddr) error { + return ENOSYS +} + +func StopIO(fd int) error { + return ENOSYS +} + +func Listen(fd int, backlog int) error { + return ENOSYS +} + +func Accept(fd int) (newfd int, sa Sockaddr, err error) { + return 0, nil, ENOSYS +} + +func Connect(fd int, sa Sockaddr) error { + return ENOSYS +} + +func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) { + return 0, nil, ENOSYS +} + +func Sendto(fd int, p []byte, flags int, to Sockaddr) error { + return ENOSYS +} + +func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn, recvflags int, from Sockaddr, err error) { + return 0, 0, 0, nil, ENOSYS +} + +func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { + return 0, ENOSYS +} + +func GetsockoptInt(fd, level, opt int) (value int, err error) { + return 0, ENOSYS +} + +func SetsockoptInt(fd, level, opt int, value int) error { + return nil +} + +func SetReadDeadline(fd int, t int64) error { + return ENOSYS +} + +func SetWriteDeadline(fd int, t int64) error { + return ENOSYS +} + +func Shutdown(fd int, how int) error { + return ENOSYS +} + +func SetNonblock(fd int, nonblocking bool) error { + return nil +} diff --git a/libgo/go/syscall/pwd_plan9.go b/libgo/go/syscall/pwd_plan9.go index 1248613..1deeaa9 100644 --- a/libgo/go/syscall/pwd_plan9.go +++ b/libgo/go/syscall/pwd_plan9.go @@ -39,6 +39,15 @@ func fixwdLocked() { } } +func fixwd(paths ...string) { + for _, path := range paths { + if path != "" && path[0] != '/' && path[0] != '#' { + Fixwd() + return + } + } +} + // goroutine-specific getwd func getwd() (wd string, err error) { fd, err := open(".", O_RDONLY) @@ -66,6 +75,7 @@ func Getwd() (wd string, err error) { } func Chdir(path string) error { + fixwd(path) wdmu.Lock() defer wdmu.Unlock() diff --git a/libgo/go/syscall/route_freebsd_32bit.go b/libgo/go/syscall/route_freebsd_32bit.go index 5c10b05..ec6f6b7 100644 --- a/libgo/go/syscall/route_freebsd_32bit.go +++ b/libgo/go/syscall/route_freebsd_32bit.go @@ -21,7 +21,7 @@ func (any *anyMessage) parseInterfaceMessage(b []byte) *InterfaceMessage { p := (*InterfaceMessage)(unsafe.Pointer(any)) // FreeBSD 10 and beyond have a restructured mbuf // packet header view. - // See http://svnweb.freebsd.org/base?view=revision&revision=254804. + // See https://svnweb.freebsd.org/base?view=revision&revision=254804. if freebsdVersion >= 1000000 { m := (*ifMsghdr)(unsafe.Pointer(any)) p.Header.Data.Hwassist = uint32(m.Data.Hwassist) diff --git a/libgo/go/syscall/security_windows.go b/libgo/go/syscall/security_windows.go index e2a9dc5..ae8b3a1 100644 --- a/libgo/go/syscall/security_windows.go +++ b/libgo/go/syscall/security_windows.go @@ -30,7 +30,7 @@ const ( ) // This function returns 1 byte BOOLEAN rather than the 4 byte BOOL. -// http://blogs.msdn.com/b/drnick/archive/2007/12/19/windows-and-upn-format-credentials.aspx +// https://blogs.msdn.com/b/drnick/archive/2007/12/19/windows-and-upn-format-credentials.aspx //sys TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint32, translatedName *uint16, nSize *uint32) (err error) [failretval&0xff==0] = secur32.TranslateNameW //sys GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err error) [failretval&0xff==0] = secur32.GetUserNameExW @@ -221,6 +221,7 @@ const ( TOKEN_ADJUST_PRIVILEGES TOKEN_ADJUST_GROUPS TOKEN_ADJUST_DEFAULT + TOKEN_ADJUST_SESSIONID TOKEN_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | @@ -230,7 +231,8 @@ const ( TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | - TOKEN_ADJUST_DEFAULT + TOKEN_ADJUST_DEFAULT | + TOKEN_ADJUST_SESSIONID TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY TOKEN_WRITE = STANDARD_RIGHTS_WRITE | TOKEN_ADJUST_PRIVILEGES | diff --git a/libgo/go/syscall/setuidgid_32_linux.go b/libgo/go/syscall/setuidgid_32_linux.go index 182f5d2..1fe7120 100644 --- a/libgo/go/syscall/setuidgid_32_linux.go +++ b/libgo/go/syscall/setuidgid_32_linux.go @@ -8,6 +8,8 @@ package syscall const ( + sys_GETEUID = SYS_GETEUID32 + sys_SETGID = SYS_SETGID32 sys_SETUID = SYS_SETUID32 ) diff --git a/libgo/go/syscall/setuidgid_linux.go b/libgo/go/syscall/setuidgid_linux.go index bf40d2d..22fa334 100644 --- a/libgo/go/syscall/setuidgid_linux.go +++ b/libgo/go/syscall/setuidgid_linux.go @@ -8,6 +8,8 @@ package syscall const ( + sys_GETEUID = SYS_GETEUID + sys_SETGID = SYS_SETGID sys_SETUID = SYS_SETUID ) diff --git a/libgo/go/syscall/syscall.go b/libgo/go/syscall/syscall.go index 8e785dd..9b74afe 100644 --- a/libgo/go/syscall/syscall.go +++ b/libgo/go/syscall/syscall.go @@ -18,14 +18,11 @@ // err is an operating system error describing the failure. // On most systems, that error has type syscall.Errno. // -// NOTE: This package is locked down. Code outside the standard -// Go repository should be migrated to use the corresponding -// package in the golang.org/x/sys repository. That is also where updates -// required by new systems or versions should be applied. -// Signal, Errno and SysProcAttr are not yet available in -// golang.org/x/sys and must still be referenced from the -// syscall package. See https://golang.org/s/go1.4-syscall -// for more information. +// Deprecated: this package is locked down. Callers should use the +// corresponding package in the golang.org/x/sys repository instead. +// That is also where updates required by new systems or versions +// should be applied. See https://golang.org/s/go1.4-syscall for more +// information. // package syscall diff --git a/libgo/go/syscall/syscall_errno.go b/libgo/go/syscall/syscall_errno.go index 01618d1..810572f 100644 --- a/libgo/go/syscall/syscall_errno.go +++ b/libgo/go/syscall/syscall_errno.go @@ -18,7 +18,7 @@ func (e Errno) Error() string { } func (e Errno) Temporary() bool { - return e == EINTR || e == EMFILE || e == ECONNRESET || e == ECONNABORTED || e.Timeout() + return e == EINTR || e == EMFILE || e.Timeout() } func (e Errno) Timeout() bool { diff --git a/libgo/go/syscall/syscall_js.go b/libgo/go/syscall/syscall_js.go new file mode 100644 index 0000000..6822eec --- /dev/null +++ b/libgo/go/syscall/syscall_js.go @@ -0,0 +1,307 @@ +// Copyright 2018 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 js,wasm + +package syscall + +import ( + "sync" + "unsafe" +) + +const direntSize = 8 + 8 + 2 + 256 + +type Dirent struct { + Reclen uint16 + Name [256]byte +} + +func direntIno(buf []byte) (uint64, bool) { + return 1, true +} + +func direntReclen(buf []byte) (uint64, bool) { + return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen)) +} + +func direntNamlen(buf []byte) (uint64, bool) { + reclen, ok := direntReclen(buf) + if !ok { + return 0, false + } + return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true +} + +const PathMax = 256 + +// 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 { + if 0 <= int(e) && int(e) < len(errorstr) { + s := errorstr[e] + if s != "" { + return s + } + } + return "errno " + itoa(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 +} + +// A Signal is a number describing a process signal. +// It implements the os.Signal interface. +type Signal int + +const ( + _ Signal = iota + SIGCHLD + SIGINT + SIGKILL + SIGTRAP + SIGQUIT +) + +func (s Signal) Signal() {} + +func (s Signal) String() string { + if 0 <= s && int(s) < len(signals) { + str := signals[s] + if str != "" { + return str + } + } + return "signal " + itoa(int(s)) +} + +var signals = [...]string{} + +// File system + +const ( + Stdin = 0 + Stdout = 1 + Stderr = 2 +) + +const ( + O_RDONLY = 0 + O_WRONLY = 1 + O_RDWR = 2 + + O_CREAT = 0100 + O_CREATE = O_CREAT + O_TRUNC = 01000 + O_APPEND = 02000 + O_EXCL = 0200 + O_SYNC = 010000 + + O_CLOEXEC = 0 +) + +const ( + F_DUPFD = 0 + F_GETFD = 1 + F_SETFD = 2 + F_GETFL = 3 + F_SETFL = 4 + F_GETOWN = 5 + F_SETOWN = 6 + F_GETLK = 7 + F_SETLK = 8 + F_SETLKW = 9 + F_RGETLK = 10 + F_RSETLK = 11 + F_CNVT = 12 + F_RSETLKW = 13 + + F_RDLCK = 1 + F_WRLCK = 2 + F_UNLCK = 3 + F_UNLKSYS = 4 +) + +const ( + S_IFMT = 0000370000 + S_IFSHM_SYSV = 0000300000 + S_IFSEMA = 0000270000 + S_IFCOND = 0000260000 + S_IFMUTEX = 0000250000 + S_IFSHM = 0000240000 + S_IFBOUNDSOCK = 0000230000 + S_IFSOCKADDR = 0000220000 + S_IFDSOCK = 0000210000 + + S_IFSOCK = 0000140000 + S_IFLNK = 0000120000 + S_IFREG = 0000100000 + S_IFBLK = 0000060000 + S_IFDIR = 0000040000 + S_IFCHR = 0000020000 + S_IFIFO = 0000010000 + + S_UNSUP = 0000370000 + + S_ISUID = 0004000 + S_ISGID = 0002000 + S_ISVTX = 0001000 + + S_IREAD = 0400 + S_IWRITE = 0200 + S_IEXEC = 0100 + + S_IRWXU = 0700 + S_IRUSR = 0400 + S_IWUSR = 0200 + S_IXUSR = 0100 + + S_IRWXG = 070 + S_IRGRP = 040 + S_IWGRP = 020 + S_IXGRP = 010 + + S_IRWXO = 07 + S_IROTH = 04 + S_IWOTH = 02 + S_IXOTH = 01 +) + +type Stat_t struct { + Dev int64 + Ino uint64 + Mode uint32 + Nlink uint32 + Uid uint32 + Gid uint32 + Rdev int64 + Size int64 + Blksize int32 + Blocks int32 + Atime int64 + AtimeNsec int64 + Mtime int64 + MtimeNsec int64 + Ctime int64 + CtimeNsec int64 +} + +// Processes +// Not supported - just enough for package os. + +var ForkLock sync.RWMutex + +type WaitStatus uint32 + +func (w WaitStatus) Exited() bool { return false } +func (w WaitStatus) ExitStatus() int { return 0 } +func (w WaitStatus) Signaled() bool { return false } +func (w WaitStatus) Signal() Signal { return 0 } +func (w WaitStatus) CoreDump() bool { return false } +func (w WaitStatus) Stopped() bool { return false } +func (w WaitStatus) Continued() bool { return false } +func (w WaitStatus) StopSignal() Signal { return 0 } +func (w WaitStatus) TrapCause() int { return 0 } + +// XXX made up +type Rusage struct { + Utime Timeval + Stime Timeval +} + +// XXX made up +type ProcAttr struct { + Dir string + Env []string + Files []uintptr + Sys *SysProcAttr +} + +type SysProcAttr struct { +} + +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { + return 0, 0, ENOSYS +} + +func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { + return 0, 0, ENOSYS +} + +func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { + return 0, 0, ENOSYS +} + +func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { + return 0, 0, ENOSYS +} + +func Sysctl(key string) (string, error) { + if key == "kern.hostname" { + return "js", nil + } + return "", ENOSYS +} + +const ImplementsGetwd = true + +func Getwd() (wd string, err error) { + var buf [PathMax]byte + n, err := Getcwd(buf[0:]) + if err != nil { + return "", err + } + return string(buf[:n]), nil +} + +func Getegid() int { return 1 } +func Geteuid() int { return 1 } +func Getgid() int { return 1 } +func Getgroups() ([]int, error) { return []int{1}, nil } +func Getppid() int { return 2 } +func Getpid() int { return 3 } +func Gettimeofday(tv *Timeval) error { return ENOSYS } +func Getuid() int { return 1 } +func Kill(pid int, signum Signal) error { return ENOSYS } +func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { + return 0, ENOSYS +} +func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) { + return 0, 0, ENOSYS +} +func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) { + return 0, ENOSYS +} + +type Iovec struct{} // dummy + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int64 +} + +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} +} + +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: usec} +} diff --git a/libgo/go/syscall/syscall_linux.go b/libgo/go/syscall/syscall_linux.go index 338a971..d2d49c3 100644 --- a/libgo/go/syscall/syscall_linux.go +++ b/libgo/go/syscall/syscall_linux.go @@ -6,6 +6,11 @@ package syscall import "unsafe" +func rawSyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr) { + r1, r2, _ = RawSyscall(trap, a1, a2, a3) + return +} + func direntIno(buf []byte) (uint64, bool) { return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino)) } diff --git a/libgo/go/syscall/syscall_linux_test.go b/libgo/go/syscall/syscall_linux_test.go index 2c4d953..99de6eb 100644 --- a/libgo/go/syscall/syscall_linux_test.go +++ b/libgo/go/syscall/syscall_linux_test.go @@ -13,16 +13,139 @@ import ( "os/exec" "os/signal" "path/filepath" + "runtime" + "strconv" + "strings" "syscall" "testing" "time" ) +// chtmpdir changes the working directory to a new temporary directory and +// provides a cleanup function. Used when PWD is read-only. +func chtmpdir(t *testing.T) func() { + oldwd, err := os.Getwd() + if err != nil { + t.Fatalf("chtmpdir: %v", err) + } + d, err := ioutil.TempDir("", "test") + if err != nil { + t.Fatalf("chtmpdir: %v", err) + } + if err := os.Chdir(d); err != nil { + t.Fatalf("chtmpdir: %v", err) + } + return func() { + if err := os.Chdir(oldwd); err != nil { + t.Fatalf("chtmpdir: %v", err) + } + os.RemoveAll(d) + } +} + +func touch(t *testing.T, name string) { + f, err := os.Create(name) + if err != nil { + t.Fatal(err) + } + if err := f.Close(); err != nil { + t.Fatal(err) + } +} + +const ( + _AT_SYMLINK_NOFOLLOW = 0x100 + _AT_FDCWD = -0x64 + _AT_EACCESS = 0x200 + _F_OK = 0 + _R_OK = 4 +) + +func TestFaccessat(t *testing.T) { + defer chtmpdir(t)() + touch(t, "file1") + + err := syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, 0) + if err != nil { + t.Errorf("Faccessat: unexpected error: %v", err) + } + + err = syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, 2) + if err != syscall.EINVAL { + t.Errorf("Faccessat: unexpected error: %v, want EINVAL", err) + } + + err = syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, _AT_EACCESS) + if err != nil { + t.Errorf("Faccessat: unexpected error: %v", err) + } + + err = os.Symlink("file1", "symlink1") + if err != nil { + t.Fatal(err) + } + + err = syscall.Faccessat(_AT_FDCWD, "symlink1", _R_OK, _AT_SYMLINK_NOFOLLOW) + if err != nil { + t.Errorf("Faccessat SYMLINK_NOFOLLOW: unexpected error %v", err) + } + + // We can't really test _AT_SYMLINK_NOFOLLOW, because there + // doesn't seem to be any way to change the mode of a symlink. + // We don't test _AT_EACCESS because such tests are only + // meaningful if run as root. + + err = syscall.Fchmodat(_AT_FDCWD, "file1", 0, 0) + if err != nil { + t.Errorf("Fchmodat: unexpected error %v", err) + } + + err = syscall.Faccessat(_AT_FDCWD, "file1", _F_OK, _AT_SYMLINK_NOFOLLOW) + if err != nil { + t.Errorf("Faccessat: unexpected error: %v", err) + } + + err = syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, _AT_SYMLINK_NOFOLLOW) + if err != syscall.EACCES { + if syscall.Getuid() != 0 { + t.Errorf("Faccessat: unexpected error: %v, want EACCES", err) + } + } +} + +func TestFchmodat(t *testing.T) { + defer chtmpdir(t)() + + touch(t, "file1") + os.Symlink("file1", "symlink1") + + err := syscall.Fchmodat(_AT_FDCWD, "symlink1", 0444, 0) + if err != nil { + t.Fatalf("Fchmodat: unexpected error: %v", err) + } + + fi, err := os.Stat("file1") + if err != nil { + t.Fatal(err) + } + + if fi.Mode() != 0444 { + t.Errorf("Fchmodat: failed to change mode: expected %v, got %v", 0444, fi.Mode()) + } + + err = syscall.Fchmodat(_AT_FDCWD, "symlink1", 0444, _AT_SYMLINK_NOFOLLOW) + if err != syscall.EOPNOTSUPP { + t.Fatalf("Fchmodat: unexpected error: %v, expected EOPNOTSUPP", err) + } +} + func TestMain(m *testing.M) { if os.Getenv("GO_DEATHSIG_PARENT") == "1" { deathSignalParent() } else if os.Getenv("GO_DEATHSIG_CHILD") == "1" { deathSignalChild() + } else if os.Getenv("GO_SYSCALL_NOERROR") == "1" { + syscallNoError() } os.Exit(m.Run()) @@ -166,3 +289,82 @@ func TestParseNetlinkMessage(t *testing.T) { } } } + +func TestSyscallNoError(t *testing.T) { + // On Linux there are currently no syscalls which don't fail and return + // a value larger than 0xfffffffffffff001 so we could test RawSyscall + // vs. RawSyscallNoError on 64bit architectures. + if runtime.GOARCH != "386" && runtime.GOARCH != "arm" { + t.Skip("skipping on non-32bit architecture") + } + + if os.Getuid() != 0 { + t.Skip("skipping root only test") + } + + // Copy the test binary to a location that a non-root user can read/execute + // after we drop privileges + tempDir, err := ioutil.TempDir("", "TestSyscallNoError") + if err != nil { + t.Fatalf("cannot create temporary directory: %v", err) + } + defer os.RemoveAll(tempDir) + os.Chmod(tempDir, 0755) + + tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0])) + + src, err := os.Open(os.Args[0]) + if err != nil { + t.Fatalf("cannot open binary %q, %v", os.Args[0], err) + } + defer src.Close() + + dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err) + } + if _, err := io.Copy(dst, src); err != nil { + t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err) + } + err = dst.Close() + if err != nil { + t.Fatalf("failed to close test binary %q, %v", tmpBinary, err) + } + + uid := uint32(0xfffffffe) + err = os.Chown(tmpBinary, int(uid), -1) + if err != nil { + t.Fatalf("failed to chown test binary %q, %v", tmpBinary, err) + } + + err = os.Chmod(tmpBinary, 0755|os.ModeSetuid) + if err != nil { + t.Fatalf("failed to set setuid bit on test binary %q, %v", tmpBinary, err) + } + + cmd := exec.Command(tmpBinary) + cmd.Env = []string{"GO_SYSCALL_NOERROR=1"} + + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("failed to start first child process: %v", err) + } + + got := strings.TrimSpace(string(out)) + want := strconv.FormatUint(uint64(uid)+1, 10) + " / " + + strconv.FormatUint(uint64(-uid), 10) + " / " + + strconv.FormatUint(uint64(uid), 10) + if got != want { + t.Errorf("expected %s, got %s", want, got) + } +} + +func syscallNoError() { + // Test that the return value from SYS_GETEUID32 (which cannot fail) + // doesn't get treated as an error (see https://golang.org/issue/22924) + euid1, _, e := syscall.RawSyscall(syscall.Sys_GETEUID, 0, 0, 0) + euid2, _ := syscall.RawSyscallNoError(syscall.Sys_GETEUID, 0, 0, 0) + + fmt.Println(uintptr(euid1), "/", int(e), "/", uintptr(euid2)) + os.Exit(0) +} diff --git a/libgo/go/syscall/syscall_plan9_test.go b/libgo/go/syscall/syscall_plan9_test.go new file mode 100644 index 0000000..c0b3af5 --- /dev/null +++ b/libgo/go/syscall/syscall_plan9_test.go @@ -0,0 +1,53 @@ +// Copyright 2018 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 syscall_test + +import ( + "syscall" + "testing" +) + +// testalias checks for aliasing of error strings returned by sys1 and sys2, +// which both call the function named fn in package syscall +func testalias(t *testing.T, fn string, sys1, sys2 func() error) { + err := sys1().Error() + errcopy := string([]byte(err)) + sys2() + if err != errcopy { + t.Errorf("syscall.%s error string changed from %q to %q\n", fn, errcopy, err) + } +} + +// issue 13770: errors cannot be nested in Plan 9 + +func TestPlan9Syserr(t *testing.T) { + testalias(t, + "Syscall", + func() error { + return syscall.Mkdir("/", 0) + }, + func() error { + return syscall.Mkdir("#", 0) + }) + testalias(t, + "Syscall6", + func() error { + return syscall.Mount(0, 0, "", 0, "") + }, + func() error { + return syscall.Mount(-1, 0, "", 0, "") + }) + // originally failed only on plan9_arm + testalias(t, + "seek", + func() error { + _, err := syscall.Seek(0, 0, -1) + return err + }, + func() error { + _, err := syscall.Seek(-1, 0, 0) + return err + }) +} diff --git a/libgo/go/syscall/syscall_test.go b/libgo/go/syscall/syscall_test.go index c3fffda..2a9d90e 100644 --- a/libgo/go/syscall/syscall_test.go +++ b/libgo/go/syscall/syscall_test.go @@ -62,8 +62,8 @@ func TestExecErrPermutedFds(t *testing.T) { } func TestGettimeofday(t *testing.T) { - if runtime.GOOS == "nacl" { - t.Skip("not implemented on nacl") + if runtime.GOOS == "nacl" || runtime.GOOS == "js" { + t.Skip("not implemented on " + runtime.GOOS) } tv := &syscall.Timeval{} if err := syscall.Gettimeofday(tv); err != nil { diff --git a/libgo/go/syscall/syscall_unix.go b/libgo/go/syscall/syscall_unix.go index 61aa1c4..9455c3f 100644 --- a/libgo/go/syscall/syscall_unix.go +++ b/libgo/go/syscall/syscall_unix.go @@ -99,6 +99,16 @@ func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errn return r, 0, err } +// clen returns the index of the first NULL byte in n or len(n) if n contains no NULL byte. +func clen(n []byte) int { + for i := 0; i < len(n); i++ { + if n[i] == 0 { + return i + } + } + return len(n) +} + // Mmap manager, for use by operating system-specific implementations. // Gccgo only has one implementation but we do this to correspond to gc. diff --git a/libgo/go/syscall/tables_nacljs.go b/libgo/go/syscall/tables_nacljs.go new file mode 100644 index 0000000..1c265f2 --- /dev/null +++ b/libgo/go/syscall/tables_nacljs.go @@ -0,0 +1,490 @@ +// Copyright 2013 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 nacl js,wasm + +package syscall + +import "runtime" + +// TODO: generate with runtime/mknacl.sh, allow override with IRT. +const ( + sys_null = 1 + sys_nameservice = 2 + sys_dup = 8 + sys_dup2 = 9 + sys_open = 10 + sys_close = 11 + sys_read = 12 + sys_write = 13 + sys_lseek = 14 + sys_stat = 16 + sys_fstat = 17 + sys_chmod = 18 + sys_isatty = 19 + sys_brk = 20 + sys_mmap = 21 + sys_munmap = 22 + sys_getdents = 23 + sys_mprotect = 24 + sys_list_mappings = 25 + sys_exit = 30 + sys_getpid = 31 + sys_sched_yield = 32 + sys_sysconf = 33 + sys_gettimeofday = 40 + sys_clock = 41 + sys_nanosleep = 42 + sys_clock_getres = 43 + sys_clock_gettime = 44 + sys_mkdir = 45 + sys_rmdir = 46 + sys_chdir = 47 + sys_getcwd = 48 + sys_unlink = 49 + sys_imc_makeboundsock = 60 + sys_imc_accept = 61 + sys_imc_connect = 62 + sys_imc_sendmsg = 63 + sys_imc_recvmsg = 64 + sys_imc_mem_obj_create = 65 + sys_imc_socketpair = 66 + sys_mutex_create = 70 + sys_mutex_lock = 71 + sys_mutex_trylock = 72 + sys_mutex_unlock = 73 + sys_cond_create = 74 + sys_cond_wait = 75 + sys_cond_signal = 76 + sys_cond_broadcast = 77 + sys_cond_timed_wait_abs = 79 + sys_thread_create = 80 + sys_thread_exit = 81 + sys_tls_init = 82 + sys_thread_nice = 83 + sys_tls_get = 84 + sys_second_tls_set = 85 + sys_second_tls_get = 86 + sys_exception_handler = 87 + sys_exception_stack = 88 + sys_exception_clear_flag = 89 + sys_sem_create = 100 + sys_sem_wait = 101 + sys_sem_post = 102 + sys_sem_get_value = 103 + sys_dyncode_create = 104 + sys_dyncode_modify = 105 + sys_dyncode_delete = 106 + sys_test_infoleak = 109 + sys_test_crash = 110 + sys_test_syscall_1 = 111 + sys_test_syscall_2 = 112 + sys_futex_wait_abs = 120 + sys_futex_wake = 121 + sys_pread = 130 + sys_pwrite = 131 + sys_truncate = 140 + sys_lstat = 141 + sys_link = 142 + sys_rename = 143 + sys_symlink = 144 + sys_access = 145 + sys_readlink = 146 + sys_utimes = 147 + sys_get_random_bytes = 150 +) + +// TODO: Auto-generate some day. (Hard-coded in binaries so not likely to change.) +const ( + // native_client/src/trusted/service_runtime/include/sys/errno.h + // The errors are mainly copied from Linux. + EPERM Errno = 1 /* Operation not permitted */ + ENOENT Errno = 2 /* No such file or directory */ + ESRCH Errno = 3 /* No such process */ + EINTR Errno = 4 /* Interrupted system call */ + EIO Errno = 5 /* I/O error */ + ENXIO Errno = 6 /* No such device or address */ + E2BIG Errno = 7 /* Argument list too long */ + ENOEXEC Errno = 8 /* Exec format error */ + EBADF Errno = 9 /* Bad file number */ + ECHILD Errno = 10 /* No child processes */ + EAGAIN Errno = 11 /* Try again */ + ENOMEM Errno = 12 /* Out of memory */ + EACCES Errno = 13 /* Permission denied */ + EFAULT Errno = 14 /* Bad address */ + EBUSY Errno = 16 /* Device or resource busy */ + EEXIST Errno = 17 /* File exists */ + EXDEV Errno = 18 /* Cross-device link */ + ENODEV Errno = 19 /* No such device */ + ENOTDIR Errno = 20 /* Not a directory */ + EISDIR Errno = 21 /* Is a directory */ + EINVAL Errno = 22 /* Invalid argument */ + ENFILE Errno = 23 /* File table overflow */ + EMFILE Errno = 24 /* Too many open files */ + ENOTTY Errno = 25 /* Not a typewriter */ + EFBIG Errno = 27 /* File too large */ + ENOSPC Errno = 28 /* No space left on device */ + ESPIPE Errno = 29 /* Illegal seek */ + EROFS Errno = 30 /* Read-only file system */ + EMLINK Errno = 31 /* Too many links */ + EPIPE Errno = 32 /* Broken pipe */ + ENAMETOOLONG Errno = 36 /* File name too long */ + ENOSYS Errno = 38 /* Function not implemented */ + EDQUOT Errno = 122 /* Quota exceeded */ + EDOM Errno = 33 /* Math arg out of domain of func */ + ERANGE Errno = 34 /* Math result not representable */ + EDEADLK Errno = 35 /* Deadlock condition */ + ENOLCK Errno = 37 /* No record locks available */ + ENOTEMPTY Errno = 39 /* Directory not empty */ + ELOOP Errno = 40 /* Too many symbolic links */ + ENOMSG Errno = 42 /* No message of desired type */ + EIDRM Errno = 43 /* Identifier removed */ + ECHRNG Errno = 44 /* Channel number out of range */ + EL2NSYNC Errno = 45 /* Level 2 not synchronized */ + EL3HLT Errno = 46 /* Level 3 halted */ + EL3RST Errno = 47 /* Level 3 reset */ + ELNRNG Errno = 48 /* Link number out of range */ + EUNATCH Errno = 49 /* Protocol driver not attached */ + ENOCSI Errno = 50 /* No CSI structure available */ + EL2HLT Errno = 51 /* Level 2 halted */ + EBADE Errno = 52 /* Invalid exchange */ + EBADR Errno = 53 /* Invalid request descriptor */ + EXFULL Errno = 54 /* Exchange full */ + ENOANO Errno = 55 /* No anode */ + EBADRQC Errno = 56 /* Invalid request code */ + EBADSLT Errno = 57 /* Invalid slot */ + EDEADLOCK Errno = EDEADLK /* File locking deadlock error */ + EBFONT Errno = 59 /* Bad font file fmt */ + ENOSTR Errno = 60 /* Device not a stream */ + ENODATA Errno = 61 /* No data (for no delay io) */ + ETIME Errno = 62 /* Timer expired */ + ENOSR Errno = 63 /* Out of streams resources */ + ENONET Errno = 64 /* Machine is not on the network */ + ENOPKG Errno = 65 /* Package not installed */ + EREMOTE Errno = 66 /* The object is remote */ + ENOLINK Errno = 67 /* The link has been severed */ + EADV Errno = 68 /* Advertise error */ + ESRMNT Errno = 69 /* Srmount error */ + ECOMM Errno = 70 /* Communication error on send */ + EPROTO Errno = 71 /* Protocol error */ + EMULTIHOP Errno = 72 /* Multihop attempted */ + EDOTDOT Errno = 73 /* Cross mount point (not really error) */ + EBADMSG Errno = 74 /* Trying to read unreadable message */ + EOVERFLOW Errno = 75 /* Value too large for defined data type */ + ENOTUNIQ Errno = 76 /* Given log. name not unique */ + EBADFD Errno = 77 /* f.d. invalid for this operation */ + EREMCHG Errno = 78 /* Remote address changed */ + ELIBACC Errno = 79 /* Can't access a needed shared lib */ + ELIBBAD Errno = 80 /* Accessing a corrupted shared lib */ + ELIBSCN Errno = 81 /* .lib section in a.out corrupted */ + ELIBMAX Errno = 82 /* Attempting to link in too many libs */ + ELIBEXEC Errno = 83 /* Attempting to exec a shared library */ + EILSEQ Errno = 84 + EUSERS Errno = 87 + ENOTSOCK Errno = 88 /* Socket operation on non-socket */ + EDESTADDRREQ Errno = 89 /* Destination address required */ + EMSGSIZE Errno = 90 /* Message too long */ + EPROTOTYPE Errno = 91 /* Protocol wrong type for socket */ + ENOPROTOOPT Errno = 92 /* Protocol not available */ + EPROTONOSUPPORT Errno = 93 /* Unknown protocol */ + ESOCKTNOSUPPORT Errno = 94 /* Socket type not supported */ + EOPNOTSUPP Errno = 95 /* Operation not supported on transport endpoint */ + EPFNOSUPPORT Errno = 96 /* Protocol family not supported */ + EAFNOSUPPORT Errno = 97 /* Address family not supported by protocol family */ + EADDRINUSE Errno = 98 /* Address already in use */ + EADDRNOTAVAIL Errno = 99 /* Address not available */ + ENETDOWN Errno = 100 /* Network interface is not configured */ + ENETUNREACH Errno = 101 /* Network is unreachable */ + ENETRESET Errno = 102 + ECONNABORTED Errno = 103 /* Connection aborted */ + ECONNRESET Errno = 104 /* Connection reset by peer */ + ENOBUFS Errno = 105 /* No buffer space available */ + EISCONN Errno = 106 /* Socket is already connected */ + ENOTCONN Errno = 107 /* Socket is not connected */ + ESHUTDOWN Errno = 108 /* Can't send after socket shutdown */ + ETOOMANYREFS Errno = 109 + ETIMEDOUT Errno = 110 /* Connection timed out */ + ECONNREFUSED Errno = 111 /* Connection refused */ + EHOSTDOWN Errno = 112 /* Host is down */ + EHOSTUNREACH Errno = 113 /* Host is unreachable */ + EALREADY Errno = 114 /* Socket already connected */ + EINPROGRESS Errno = 115 /* Connection already in progress */ + ESTALE Errno = 116 + ENOTSUP Errno = EOPNOTSUPP /* Not supported */ + ENOMEDIUM Errno = 123 /* No medium (in tape drive) */ + ECANCELED Errno = 125 /* Operation canceled. */ + ELBIN Errno = 2048 /* Inode is remote (not really error) */ + EFTYPE Errno = 2049 /* Inappropriate file type or format */ + ENMFILE Errno = 2050 /* No more files */ + EPROCLIM Errno = 2051 + ENOSHARE Errno = 2052 /* No such host or network path */ + ECASECLASH Errno = 2053 /* Filename exists with different case */ + EWOULDBLOCK Errno = EAGAIN /* Operation would block */ +) + +// TODO: Auto-generate some day. (Hard-coded in binaries so not likely to change.) +var errorstr = [...]string{ + EPERM: "Operation not permitted", + ENOENT: "No such file or directory", + ESRCH: "No such process", + EINTR: "Interrupted system call", + EIO: "I/O error", + ENXIO: "No such device or address", + E2BIG: "Argument list too long", + ENOEXEC: "Exec format error", + EBADF: "Bad file number", + ECHILD: "No child processes", + EAGAIN: "Try again", + ENOMEM: "Out of memory", + EACCES: "Permission denied", + EFAULT: "Bad address", + EBUSY: "Device or resource busy", + EEXIST: "File exists", + EXDEV: "Cross-device link", + ENODEV: "No such device", + ENOTDIR: "Not a directory", + EISDIR: "Is a directory", + EINVAL: "Invalid argument", + ENFILE: "File table overflow", + EMFILE: "Too many open files", + ENOTTY: "Not a typewriter", + EFBIG: "File too large", + ENOSPC: "No space left on device", + ESPIPE: "Illegal seek", + EROFS: "Read-only file system", + EMLINK: "Too many links", + EPIPE: "Broken pipe", + ENAMETOOLONG: "File name too long", + ENOSYS: "not implemented on " + runtime.GOOS, + EDQUOT: "Quota exceeded", + EDOM: "Math arg out of domain of func", + ERANGE: "Math result not representable", + EDEADLK: "Deadlock condition", + ENOLCK: "No record locks available", + ENOTEMPTY: "Directory not empty", + ELOOP: "Too many symbolic links", + ENOMSG: "No message of desired type", + EIDRM: "Identifier removed", + ECHRNG: "Channel number out of range", + EL2NSYNC: "Level 2 not synchronized", + EL3HLT: "Level 3 halted", + EL3RST: "Level 3 reset", + ELNRNG: "Link number out of range", + EUNATCH: "Protocol driver not attached", + ENOCSI: "No CSI structure available", + EL2HLT: "Level 2 halted", + EBADE: "Invalid exchange", + EBADR: "Invalid request descriptor", + EXFULL: "Exchange full", + ENOANO: "No anode", + EBADRQC: "Invalid request code", + EBADSLT: "Invalid slot", + EBFONT: "Bad font file fmt", + ENOSTR: "Device not a stream", + ENODATA: "No data (for no delay io)", + ETIME: "Timer expired", + ENOSR: "Out of streams resources", + ENONET: "Machine is not on the network", + ENOPKG: "Package not installed", + EREMOTE: "The object is remote", + ENOLINK: "The link has been severed", + EADV: "Advertise error", + ESRMNT: "Srmount error", + ECOMM: "Communication error on send", + EPROTO: "Protocol error", + EMULTIHOP: "Multihop attempted", + EDOTDOT: "Cross mount point (not really error)", + EBADMSG: "Trying to read unreadable message", + EOVERFLOW: "Value too large for defined data type", + ENOTUNIQ: "Given log. name not unique", + EBADFD: "f.d. invalid for this operation", + EREMCHG: "Remote address changed", + ELIBACC: "Can't access a needed shared lib", + ELIBBAD: "Accessing a corrupted shared lib", + ELIBSCN: ".lib section in a.out corrupted", + ELIBMAX: "Attempting to link in too many libs", + ELIBEXEC: "Attempting to exec a shared library", + ENOTSOCK: "Socket operation on non-socket", + EDESTADDRREQ: "Destination address required", + EMSGSIZE: "Message too long", + EPROTOTYPE: "Protocol wrong type for socket", + ENOPROTOOPT: "Protocol not available", + EPROTONOSUPPORT: "Unknown protocol", + ESOCKTNOSUPPORT: "Socket type not supported", + EOPNOTSUPP: "Operation not supported on transport endpoint", + EPFNOSUPPORT: "Protocol family not supported", + EAFNOSUPPORT: "Address family not supported by protocol family", + EADDRINUSE: "Address already in use", + EADDRNOTAVAIL: "Address not available", + ENETDOWN: "Network interface is not configured", + ENETUNREACH: "Network is unreachable", + ECONNABORTED: "Connection aborted", + ECONNRESET: "Connection reset by peer", + ENOBUFS: "No buffer space available", + EISCONN: "Socket is already connected", + ENOTCONN: "Socket is not connected", + ESHUTDOWN: "Can't send after socket shutdown", + ETIMEDOUT: "Connection timed out", + ECONNREFUSED: "Connection refused", + EHOSTDOWN: "Host is down", + EHOSTUNREACH: "Host is unreachable", + EALREADY: "Socket already connected", + EINPROGRESS: "Connection already in progress", + ENOMEDIUM: "No medium (in tape drive)", + ECANCELED: "Operation canceled.", + ELBIN: "Inode is remote (not really error)", + EFTYPE: "Inappropriate file type or format", + ENMFILE: "No more files", + ENOSHARE: "No such host or network path", + ECASECLASH: "Filename exists with different case", +} + +// Do the interface allocations only once for common +// Errno values. +var ( + errEAGAIN error = EAGAIN + errEINVAL error = EINVAL + errENOENT error = ENOENT +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e Errno) error { + switch e { + case 0: + return nil + case EAGAIN: + return errEAGAIN + case EINVAL: + return errEINVAL + case ENOENT: + return errENOENT + } + return e +} + +var errnoByCode = map[string]Errno{ + "EPERM": EPERM, + "ENOENT": ENOENT, + "ESRCH": ESRCH, + "EINTR": EINTR, + "EIO": EIO, + "ENXIO": ENXIO, + "E2BIG": E2BIG, + "ENOEXEC": ENOEXEC, + "EBADF": EBADF, + "ECHILD": ECHILD, + "EAGAIN": EAGAIN, + "ENOMEM": ENOMEM, + "EACCES": EACCES, + "EFAULT": EFAULT, + "EBUSY": EBUSY, + "EEXIST": EEXIST, + "EXDEV": EXDEV, + "ENODEV": ENODEV, + "ENOTDIR": ENOTDIR, + "EISDIR": EISDIR, + "EINVAL": EINVAL, + "ENFILE": ENFILE, + "EMFILE": EMFILE, + "ENOTTY": ENOTTY, + "EFBIG": EFBIG, + "ENOSPC": ENOSPC, + "ESPIPE": ESPIPE, + "EROFS": EROFS, + "EMLINK": EMLINK, + "EPIPE": EPIPE, + "ENAMETOOLONG": ENAMETOOLONG, + "ENOSYS": ENOSYS, + "EDQUOT": EDQUOT, + "EDOM": EDOM, + "ERANGE": ERANGE, + "EDEADLK": EDEADLK, + "ENOLCK": ENOLCK, + "ENOTEMPTY": ENOTEMPTY, + "ELOOP": ELOOP, + "ENOMSG": ENOMSG, + "EIDRM": EIDRM, + "ECHRNG": ECHRNG, + "EL2NSYNC": EL2NSYNC, + "EL3HLT": EL3HLT, + "EL3RST": EL3RST, + "ELNRNG": ELNRNG, + "EUNATCH": EUNATCH, + "ENOCSI": ENOCSI, + "EL2HLT": EL2HLT, + "EBADE": EBADE, + "EBADR": EBADR, + "EXFULL": EXFULL, + "ENOANO": ENOANO, + "EBADRQC": EBADRQC, + "EBADSLT": EBADSLT, + "EDEADLOCK": EDEADLOCK, + "EBFONT": EBFONT, + "ENOSTR": ENOSTR, + "ENODATA": ENODATA, + "ETIME": ETIME, + "ENOSR": ENOSR, + "ENONET": ENONET, + "ENOPKG": ENOPKG, + "EREMOTE": EREMOTE, + "ENOLINK": ENOLINK, + "EADV": EADV, + "ESRMNT": ESRMNT, + "ECOMM": ECOMM, + "EPROTO": EPROTO, + "EMULTIHOP": EMULTIHOP, + "EDOTDOT": EDOTDOT, + "EBADMSG": EBADMSG, + "EOVERFLOW": EOVERFLOW, + "ENOTUNIQ": ENOTUNIQ, + "EBADFD": EBADFD, + "EREMCHG": EREMCHG, + "ELIBACC": ELIBACC, + "ELIBBAD": ELIBBAD, + "ELIBSCN": ELIBSCN, + "ELIBMAX": ELIBMAX, + "ELIBEXEC": ELIBEXEC, + "EILSEQ": EILSEQ, + "EUSERS": EUSERS, + "ENOTSOCK": ENOTSOCK, + "EDESTADDRREQ": EDESTADDRREQ, + "EMSGSIZE": EMSGSIZE, + "EPROTOTYPE": EPROTOTYPE, + "ENOPROTOOPT": ENOPROTOOPT, + "EPROTONOSUPPORT": EPROTONOSUPPORT, + "ESOCKTNOSUPPORT": ESOCKTNOSUPPORT, + "EOPNOTSUPP": EOPNOTSUPP, + "EPFNOSUPPORT": EPFNOSUPPORT, + "EAFNOSUPPORT": EAFNOSUPPORT, + "EADDRINUSE": EADDRINUSE, + "EADDRNOTAVAIL": EADDRNOTAVAIL, + "ENETDOWN": ENETDOWN, + "ENETUNREACH": ENETUNREACH, + "ENETRESET": ENETRESET, + "ECONNABORTED": ECONNABORTED, + "ECONNRESET": ECONNRESET, + "ENOBUFS": ENOBUFS, + "EISCONN": EISCONN, + "ENOTCONN": ENOTCONN, + "ESHUTDOWN": ESHUTDOWN, + "ETOOMANYREFS": ETOOMANYREFS, + "ETIMEDOUT": ETIMEDOUT, + "ECONNREFUSED": ECONNREFUSED, + "EHOSTDOWN": EHOSTDOWN, + "EHOSTUNREACH": EHOSTUNREACH, + "EALREADY": EALREADY, + "EINPROGRESS": EINPROGRESS, + "ESTALE": ESTALE, + "ENOTSUP": ENOTSUP, + "ENOMEDIUM": ENOMEDIUM, + "ECANCELED": ECANCELED, + "ELBIN": ELBIN, + "EFTYPE": EFTYPE, + "ENMFILE": ENMFILE, + "EPROCLIM": EPROCLIM, + "ENOSHARE": ENOSHARE, + "ECASECLASH": ECASECLASH, + "EWOULDBLOCK": EWOULDBLOCK, +} diff --git a/libgo/go/syscall/timestruct.go b/libgo/go/syscall/timestruct.go index 6ece338..d17811c 100644 --- a/libgo/go/syscall/timestruct.go +++ b/libgo/go/syscall/timestruct.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package syscall diff --git a/libgo/go/testing/benchmark.go b/libgo/go/testing/benchmark.go index 4d569b7..9c7b1be 100644 --- a/libgo/go/testing/benchmark.go +++ b/libgo/go/testing/benchmark.go @@ -489,14 +489,17 @@ func (b *B) Run(name string, f func(b *B)) bool { if !ok { return true } + var pc [maxStackLen]uintptr + n := runtime.Callers(2, pc[:]) sub := &B{ common: common{ - signal: make(chan bool), - name: benchName, - parent: &b.common, - level: b.level + 1, - w: b.w, - chatty: b.chatty, + signal: make(chan bool), + name: benchName, + parent: &b.common, + level: b.level + 1, + creator: pc[:n], + w: b.w, + chatty: b.chatty, }, importPath: b.importPath, benchFunc: f, diff --git a/libgo/go/testing/cover.go b/libgo/go/testing/cover.go index a4ce37f..17c03f5 100644 --- a/libgo/go/testing/cover.go +++ b/libgo/go/testing/cover.go @@ -13,14 +13,17 @@ import ( ) // CoverBlock records the coverage data for a single basic block. +// The fields are 1-indexed, as in an editor: The opening line of +// the file is number 1, for example. Columns are measured +// in bytes. // NOTE: This struct is internal to the testing infrastructure and may change. // It is not covered (yet) by the Go 1 compatibility guidelines. type CoverBlock struct { - Line0 uint32 - Col0 uint16 - Line1 uint32 - Col1 uint16 - Stmts uint16 + Line0 uint32 // Line number for block start. + Col0 uint16 // Column number for block start. + Line1 uint32 // Line number for block end. + Col1 uint16 // Column number for block end. + Stmts uint16 // Number of statements included in this block. } var cover Cover diff --git a/libgo/go/testing/example.go b/libgo/go/testing/example.go index b995550..f4beb76 100644 --- a/libgo/go/testing/example.go +++ b/libgo/go/testing/example.go @@ -5,7 +5,6 @@ package testing import ( - "bytes" "fmt" "io" "os" @@ -72,7 +71,7 @@ func runExample(eg InternalExample) (ok bool) { os.Stdout = w outC := make(chan string) go func() { - var buf bytes.Buffer + var buf strings.Builder _, err := io.Copy(&buf, r) r.Close() if err != nil { diff --git a/libgo/go/testing/helper_test.go b/libgo/go/testing/helper_test.go index f5cb27c..fe8ff05 100644 --- a/libgo/go/testing/helper_test.go +++ b/libgo/go/testing/helper_test.go @@ -28,11 +28,11 @@ helperfuncs_test.go:33: 1 helperfuncs_test.go:21: 2 helperfuncs_test.go:35: 3 helperfuncs_test.go:42: 4 -helperfuncs_test.go:47: 5 --- FAIL: Test/sub (?s) -helperfuncs_test.go:50: 6 -helperfuncs_test.go:21: 7 -helperfuncs_test.go:53: 8 +helperfuncs_test.go:45: 5 +helperfuncs_test.go:21: 6 +helperfuncs_test.go:44: 7 +helperfuncs_test.go:56: 8 ` lines := strings.Split(buf.String(), "\n") durationRE := regexp.MustCompile(`\(.*\)$`) diff --git a/libgo/go/testing/helperfuncs_test.go b/libgo/go/testing/helperfuncs_test.go index 7cb2e2c..f2d54b3 100644 --- a/libgo/go/testing/helperfuncs_test.go +++ b/libgo/go/testing/helperfuncs_test.go @@ -41,17 +41,19 @@ func testHelper(t *T) { } fn("4") - // Check that calling Helper from inside this test entry function - // doesn't have an effect. - t.Helper() - t.Error("5") - t.Run("sub", func(t *T) { - helper(t, "6") - notHelperCallingHelper(t, "7") + helper(t, "5") + notHelperCallingHelper(t, "6") + // Check that calling Helper from inside a subtest entry function + // works as if it were in an ordinary function call. t.Helper() - t.Error("8") + t.Error("7") }) + + // Check that calling Helper from inside a top-level test function + // has no effect. + t.Helper() + t.Error("8") } func parallelTestHelper(t *T) { diff --git a/libgo/go/testing/internal/testdeps/deps.go b/libgo/go/testing/internal/testdeps/deps.go index 4986898..14512e9 100644 --- a/libgo/go/testing/internal/testdeps/deps.go +++ b/libgo/go/testing/internal/testdeps/deps.go @@ -46,10 +46,6 @@ func (TestDeps) StopCPUProfile() { pprof.StopCPUProfile() } -func (TestDeps) WriteHeapProfile(w io.Writer) error { - return pprof.WriteHeapProfile(w) -} - func (TestDeps) WriteProfileTo(name string, w io.Writer, debug int) error { return pprof.Lookup(name).WriteTo(w, debug) } diff --git a/libgo/go/testing/match.go b/libgo/go/testing/match.go index 89e30d0..b18c6e7 100644 --- a/libgo/go/testing/match.go +++ b/libgo/go/testing/match.go @@ -110,7 +110,7 @@ func splitRegexp(s string) []string { } // unique creates a unique name for the given parent and subname by affixing it -// with one ore more counts, if necessary. +// with one or more counts, if necessary. func (m *matcher) unique(parent, subname string) string { name := fmt.Sprintf("%s/%s", parent, subname) empty := subname == "" diff --git a/libgo/go/testing/sub_test.go b/libgo/go/testing/sub_test.go index acf5dea..9af3909 100644 --- a/libgo/go/testing/sub_test.go +++ b/libgo/go/testing/sub_test.go @@ -168,7 +168,7 @@ func TestTRun(t *T) { --- FAIL: failure in parallel test propagates upwards (N.NNs) --- FAIL: failure in parallel test propagates upwards/#00 (N.NNs) --- FAIL: failure in parallel test propagates upwards/#00/par (N.NNs) - `, + `, f: func(t *T) { t.Run("", func(t *T) { t.Parallel() @@ -210,8 +210,8 @@ func TestTRun(t *T) { desc: "skipping after error", output: ` --- FAIL: skipping after error (N.NNs) - sub_test.go:NNN: an error - sub_test.go:NNN: skipped`, + sub_test.go:NNN: an error + sub_test.go:NNN: skipped`, f: func(t *T) { t.Error("an error") t.Skip("skipped") @@ -316,6 +316,81 @@ func TestTRun(t *T) { t.Skip() }, }, { + desc: "subtest calls error on parent", + ok: false, + output: ` +--- FAIL: subtest calls error on parent (N.NNs) + sub_test.go:NNN: first this + sub_test.go:NNN: and now this! + sub_test.go:NNN: oh, and this too`, + maxPar: 1, + f: func(t *T) { + t.Errorf("first this") + outer := t + t.Run("", func(t *T) { + outer.Errorf("and now this!") + }) + t.Errorf("oh, and this too") + }, + }, { + desc: "subtest calls fatal on parent", + ok: false, + output: ` +--- FAIL: subtest calls fatal on parent (N.NNs) + sub_test.go:NNN: first this + sub_test.go:NNN: and now this! + --- FAIL: subtest calls fatal on parent/#00 (N.NNs) + testing.go:NNN: test executed panic(nil) or runtime.Goexit: subtest may have called FailNow on a parent test`, + maxPar: 1, + f: func(t *T) { + outer := t + t.Errorf("first this") + t.Run("", func(t *T) { + outer.Fatalf("and now this!") + }) + t.Errorf("Should not reach here.") + }, + }, { + desc: "subtest calls error on ancestor", + ok: false, + output: ` +--- FAIL: subtest calls error on ancestor (N.NNs) + sub_test.go:NNN: Report to ancestor + --- FAIL: subtest calls error on ancestor/#00 (N.NNs) + sub_test.go:NNN: Still do this + sub_test.go:NNN: Also do this`, + maxPar: 1, + f: func(t *T) { + outer := t + t.Run("", func(t *T) { + t.Run("", func(t *T) { + outer.Errorf("Report to ancestor") + }) + t.Errorf("Still do this") + }) + t.Errorf("Also do this") + }, + }, { + desc: "subtest calls fatal on ancestor", + ok: false, + output: ` +--- FAIL: subtest calls fatal on ancestor (N.NNs) + sub_test.go:NNN: Nope`, + maxPar: 1, + f: func(t *T) { + outer := t + t.Run("", func(t *T) { + for i := 0; i < 4; i++ { + t.Run("", func(t *T) { + outer.Fatalf("Nope") + }) + t.Errorf("Don't do this") + } + t.Errorf("And neither do this") + }) + t.Errorf("Nor this") + }, + }, { desc: "panic on goroutine fail after test exit", ok: false, maxPar: 4, @@ -428,7 +503,7 @@ func TestBRun(t *T) { chatty: true, output: ` --- SKIP: root - sub_test.go:NNN: skipping`, + sub_test.go:NNN: skipping`, f: func(b *B) { b.Skip("skipping") }, }, { desc: "chatty with recursion", @@ -446,8 +521,8 @@ func TestBRun(t *T) { failed: true, output: ` --- FAIL: root - sub_test.go:NNN: an error - sub_test.go:NNN: skipped`, + sub_test.go:NNN: an error + sub_test.go:NNN: skipped`, f: func(b *B) { b.Error("an error") b.Skip("skipped") @@ -518,8 +593,9 @@ func TestBRun(t *T) { } func makeRegexp(s string) string { + s = regexp.QuoteMeta(s) s = strings.Replace(s, ":NNN:", `:\d\d\d:`, -1) - s = strings.Replace(s, "(N.NNs)", `\(\d*\.\d*s\)`, -1) + s = strings.Replace(s, "N\\.NNs", `\d*\.\d*s`, -1) return s } diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go index f56dbf8..a552b36 100644 --- a/libgo/go/testing/testing.go +++ b/libgo/go/testing/testing.go @@ -34,7 +34,7 @@ // its -bench flag is provided. Benchmarks are run sequentially. // // For a description of the testing flags, see -// https://golang.org/cmd/go/#hdr-Description_of_testing_flags. +// https://golang.org/cmd/go/#hdr-Testing_flags // // A sample benchmark function looks like this: // func BenchmarkHello(b *testing.B) { @@ -178,6 +178,9 @@ // } // } // +// The race detector kills the program if it exceeds 8192 concurrent goroutines, +// so use care when running parallel tests with the -race flag set. +// // Run does not return until parallel subtests have completed, providing a way // to clean up after a group of parallel tests: // @@ -257,8 +260,8 @@ var ( coverProfile = flag.String("test.coverprofile", "", "write a coverage profile to `file`") matchList = flag.String("test.list", "", "list tests, examples, and benchmarks matching `regexp` then exit") match = flag.String("test.run", "", "run only tests and examples matching `regexp`") - memProfile = flag.String("test.memprofile", "", "write a memory profile to `file`") - memProfileRate = flag.Int("test.memprofilerate", 0, "set memory profiling `rate` (see runtime.MemProfileRate)") + memProfile = flag.String("test.memprofile", "", "write an allocation profile to `file`") + memProfileRate = flag.Int("test.memprofilerate", 0, "set memory allocation profiling `rate` (see runtime.MemProfileRate)") cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to `file`") blockProfile = flag.String("test.blockprofile", "", "write a goroutine blocking profile to `file`") blockProfileRate = flag.Int("test.blockprofilerate", 1, "set blocking profile `rate` (see runtime.SetBlockProfileRate)") @@ -278,6 +281,10 @@ var ( numFailed uint32 // number of test failures ) +// The maximum number of stack frames to go through when skipping helper functions for +// the purpose of decorating log messages. +const maxStackLen = 50 + // common holds the elements common between T and B and // captures common methods such as Errorf. type common struct { @@ -298,6 +305,7 @@ type common struct { parent *common level int // Nesting depth of test or benchmark. + creator []uintptr // If level > 0, the stack trace at the point where the parent called t.Run. name string // Name of test or benchmark. start time.Time // Time test or benchmark started duration time.Duration @@ -324,15 +332,20 @@ func Verbose() bool { } // frameSkip searches, starting after skip frames, for the first caller frame -// in a function not marked as a helper and returns the frames to skip -// to reach that site. The search stops if it finds a tRunner function that -// was the entry point into the test. +// in a function not marked as a helper and returns that frame. +// The search stops if it finds a tRunner function that +// was the entry point into the test and the test is not a subtest. // This function must be called with c.mu held. -func (c *common) frameSkip(skip int) int { - if c.helpers == nil { - return skip - } - var pc [50]uintptr +func (c *common) frameSkip(skip int) runtime.Frame { + // If the search continues into the parent test, we'll have to hold + // its mu temporarily. If we then return, we need to unlock it. + shouldUnlock := false + defer func() { + if shouldUnlock { + c.mu.Unlock() + } + }() + var pc [maxStackLen]uintptr // Skip two extra frames to account for this function // and runtime.Callers itself. n := runtime.Callers(skip+2, pc[:]) @@ -340,32 +353,54 @@ func (c *common) frameSkip(skip int) int { panic("testing: zero callers found") } frames := runtime.CallersFrames(pc[:n]) - var frame runtime.Frame - more := true - for i := 0; more; i++ { + var firstFrame, prevFrame, frame runtime.Frame + for more := true; more; prevFrame = frame { frame, more = frames.Next() + if firstFrame.PC == 0 { + firstFrame = frame + } if frame.Function == c.runner { // We've gone up all the way to the tRunner calling // the test function (so the user must have // called tb.Helper from inside that test function). - // Only skip up to the test function itself. - return skip + i - 1 + // If this is a top-level test, only skip up to the test function itself. + // If we're in a subtest, continue searching in the parent test, + // starting from the point of the call to Run which created this subtest. + if c.level > 1 { + frames = runtime.CallersFrames(c.creator) + parent := c.parent + // We're no longer looking at the current c after this point, + // so we should unlock its mu, unless it's the original receiver, + // in which case our caller doesn't expect us to do that. + if shouldUnlock { + c.mu.Unlock() + } + c = parent + // Remember to unlock c.mu when we no longer need it, either + // because we went up another nesting level, or because we + // returned. + shouldUnlock = true + c.mu.Lock() + continue + } + return prevFrame } if _, ok := c.helpers[frame.Function]; !ok { // Found a frame that wasn't inside a helper function. - return skip + i + return frame } } - return skip + return firstFrame } // decorate prefixes the string with the file and line of the call site -// and inserts the final newline if needed and indentation tabs for formatting. +// and inserts the final newline if needed and indentation spaces for formatting. // This function must be called with c.mu held. func (c *common) decorate(s string) string { - skip := c.frameSkip(3) // decorate + log + public function. - _, file, line, ok := runtime.Caller(skip) - if ok { + frame := c.frameSkip(3) // decorate + log + public function. + file := frame.File + line := frame.Line + if file != "" { // Truncate file name at last file name separator. if index := strings.LastIndex(file, "/"); index >= 0 { file = file[index+1:] @@ -374,11 +409,13 @@ func (c *common) decorate(s string) string { } } else { file = "???" + } + if line == 0 { line = 1 } - buf := new(bytes.Buffer) - // Every line is indented at least one tab. - buf.WriteByte('\t') + buf := new(strings.Builder) + // Every line is indented at least 4 spaces. + buf.WriteString(" ") fmt.Fprintf(buf, "%s:%d: ", file, line) lines := strings.Split(s, "\n") if l := len(lines); l > 1 && lines[l-1] == "" { @@ -386,8 +423,8 @@ func (c *common) decorate(s string) string { } for i, line := range lines { if i > 0 { - // Second and subsequent lines are indented an extra tab. - buf.WriteString("\n\t\t") + // Second and subsequent lines are indented an additional 4 spaces. + buf.WriteString("\n ") } buf.WriteString(line) } @@ -639,8 +676,6 @@ func (c *common) Skipped() bool { // Helper marks the calling function as a test helper function. // When printing file and line information, that function will be skipped. // Helper may be called simultaneously from multiple goroutines. -// Helper has no effect if it is called directly from a TestXxx/BenchmarkXxx -// function or a subtest/sub-benchmark function. func (c *common) Helper() { c.mu.Lock() defer c.mu.Unlock() @@ -718,6 +753,8 @@ type InternalTest struct { F func(*T) } +var errNilPanicOrGoexit = errors.New("test executed panic(nil) or runtime.Goexit") + func tRunner(t *T, fn func(t *T)) { t.runner = callerName(0) @@ -726,6 +763,10 @@ func tRunner(t *T, fn func(t *T)) { // a call to runtime.Goexit, record the duration and send // a signal saying that the test is done. defer func() { + if t.Failed() { + atomic.AddUint32(&numFailed, 1) + } + if t.raceErrors+race.Errors() > 0 { t.Errorf("race detected during execution of test") } @@ -733,8 +774,17 @@ func tRunner(t *T, fn func(t *T)) { t.duration += time.Since(t.start) // If the test panicked, print any test output before dying. err := recover() + signal := true if !t.finished && err == nil { - err = fmt.Errorf("test executed panic(nil) or runtime.Goexit") + err = errNilPanicOrGoexit + for p := t.parent; p != nil; p = p.parent { + if p.finished { + t.Errorf("%v: subtest may have called FailNow on a parent test", err) + err = nil + signal = false + break + } + } } if err != nil { t.Fail() @@ -769,16 +819,14 @@ func tRunner(t *T, fn func(t *T)) { if t.parent != nil && atomic.LoadInt32(&t.hasSub) == 0 { t.setRan() } - t.signal <- true + t.signal <- signal }() t.start = time.Now() t.raceErrors = -race.Errors() fn(t) - if t.failed { - atomic.AddUint32(&numFailed, 1) - } + // code beyond here will not be executed when FailNow is invoked t.finished = true } @@ -794,6 +842,11 @@ func (t *T) Run(name string, f func(t *T)) bool { if !ok || shouldFailFast() { return true } + // Record the stack trace at the point of this call so that if the subtest + // function - which runs in a separate stack - is marked as a helper, we can + // continue walking the stack into the parent test. + var pc [maxStackLen]uintptr + n := runtime.Callers(2, pc[:]) t = &T{ common: common{ barrier: make(chan bool), @@ -801,6 +854,7 @@ func (t *T) Run(name string, f func(t *T)) bool { name: testName, parent: &t.common, level: t.level + 1, + creator: pc[:n], chatty: t.chatty, }, context: t.context, @@ -822,7 +876,11 @@ func (t *T) Run(name string, f func(t *T)) bool { // without being preempted, even when their parent is a parallel test. This // may especially reduce surprises if *parallel == 1. go tRunner(t, f) - <-t.signal + if !<-t.signal { + // At this point, it is likely that FailNow was called on one of the + // parent tests by one of the subtests. Continue aborting up the chain. + runtime.Goexit() + } return !t.failed } @@ -889,7 +947,6 @@ type matchStringOnly func(pat, str string) (bool, error) func (f matchStringOnly) MatchString(pat, str string) (bool, error) { return f(pat, str) } func (f matchStringOnly) StartCPUProfile(w io.Writer) error { return errMain } func (f matchStringOnly) StopCPUProfile() {} -func (f matchStringOnly) WriteHeapProfile(w io.Writer) error { return errMain } func (f matchStringOnly) WriteProfileTo(string, io.Writer, int) error { return errMain } func (f matchStringOnly) ImportPath() string { return "" } func (f matchStringOnly) StartTestLog(io.Writer) {} @@ -929,7 +986,6 @@ type testDeps interface { StopCPUProfile() StartTestLog(io.Writer) StopTestLog() error - WriteHeapProfile(io.Writer) error WriteProfileTo(string, io.Writer, int) error } @@ -1168,7 +1224,7 @@ func (m *M) writeProfiles() { os.Exit(2) } runtime.GC() // materialize all statistics - if err = m.deps.WriteHeapProfile(f); err != nil { + if err = m.deps.WriteProfileTo("allocs", f, 0); err != nil { fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *memProfile, err) os.Exit(2) } diff --git a/libgo/go/text/scanner/example_test.go b/libgo/go/text/scanner/example_test.go index 9e2d5b7..88b992b 100644 --- a/libgo/go/text/scanner/example_test.go +++ b/libgo/go/text/scanner/example_test.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Go Authors. All rights reserved. +// Copyright 2018 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. @@ -10,6 +10,7 @@ import ( "fmt" "strings" "text/scanner" + "unicode" ) func Example() { @@ -18,6 +19,7 @@ func Example() { if a > 10 { someParsable = text }` + var s scanner.Scanner s.Init(strings.NewReader(src)) s.Filename = "example" @@ -36,3 +38,105 @@ if a > 10 { // example:4:17: text // example:5:1: } } + +func Example_isIdentRune() { + const src = "%var1 var2%" + + var s scanner.Scanner + s.Init(strings.NewReader(src)) + s.Filename = "default" + + for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() { + fmt.Printf("%s: %s\n", s.Position, s.TokenText()) + } + + fmt.Println() + s.Init(strings.NewReader(src)) + s.Filename = "percent" + + // treat leading '%' as part of an identifier + s.IsIdentRune = func(ch rune, i int) bool { + return ch == '%' && i == 0 || unicode.IsLetter(ch) || unicode.IsDigit(ch) && i > 0 + } + + for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() { + fmt.Printf("%s: %s\n", s.Position, s.TokenText()) + } + + // Output: + // default:1:1: % + // default:1:2: var1 + // default:1:7: var2 + // default:1:11: % + // + // percent:1:1: %var1 + // percent:1:7: var2 + // percent:1:11: % +} + +func Example_mode() { + const src = ` + // Comment begins at column 5. + +This line should not be included in the output. + +/* +This multiline comment +should be extracted in +its entirety. +*/ +` + + var s scanner.Scanner + s.Init(strings.NewReader(src)) + s.Filename = "comments" + s.Mode ^= scanner.SkipComments // don't skip comments + + for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() { + txt := s.TokenText() + if strings.HasPrefix(txt, "//") || strings.HasPrefix(txt, "/*") { + fmt.Printf("%s: %s\n", s.Position, txt) + } + } + + // Output: + // comments:2:5: // Comment begins at column 5. + // comments:6:1: /* + // This multiline comment + // should be extracted in + // its entirety. + // */ +} + +func Example_whitespace() { + // tab-separated values + const src = `aa ab ac ad +ba bb bc bd +ca cb cc cd +da db dc dd` + + var ( + col, row int + s scanner.Scanner + tsv [4][4]string // large enough for example above + ) + s.Init(strings.NewReader(src)) + s.Whitespace ^= 1<<'\t' | 1<<'\n' // don't skip tabs and new lines + + for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() { + switch tok { + case '\n': + row++ + col = 0 + case '\t': + col++ + default: + tsv[row][col] = s.TokenText() + } + } + + fmt.Print(tsv) + + // Output: + // [[aa ab ac ad] [ba bb bc bd] [ca cb cc cd] [da db dc dd]] +} diff --git a/libgo/go/text/scanner/scanner.go b/libgo/go/text/scanner/scanner.go index 6fb0422..4e76664 100644 --- a/libgo/go/text/scanner/scanner.go +++ b/libgo/go/text/scanner/scanner.go @@ -621,7 +621,7 @@ redo: case '`': if s.Mode&ScanRawStrings != 0 { s.scanRawString() - tok = String + tok = RawString } ch = s.next() default: diff --git a/libgo/go/text/scanner/scanner_test.go b/libgo/go/text/scanner/scanner_test.go index 3e92d65..9a6b72e 100644 --- a/libgo/go/text/scanner/scanner_test.go +++ b/libgo/go/text/scanner/scanner_test.go @@ -209,10 +209,10 @@ var tokenList = []token{ {String, `"` + f100 + `"`}, {Comment, "// raw strings"}, - {String, "``"}, - {String, "`\\`"}, - {String, "`" + "\n\n/* foobar */\n\n" + "`"}, - {String, "`" + f100 + "`"}, + {RawString, "``"}, + {RawString, "`\\`"}, + {RawString, "`" + "\n\n/* foobar */\n\n" + "`"}, + {RawString, "`" + f100 + "`"}, {Comment, "// individual characters"}, // NUL character is not allowed @@ -463,9 +463,9 @@ func TestError(t *testing.T) { testError(t, `"ab`+"\x80", "<input>:1:4", "illegal UTF-8 encoding", String) testError(t, `"abc`+"\xff", "<input>:1:5", "illegal UTF-8 encoding", String) - testError(t, "`a"+"\x00", "<input>:1:3", "illegal character NUL", String) - testError(t, "`ab"+"\x80", "<input>:1:4", "illegal UTF-8 encoding", String) - testError(t, "`abc"+"\xff", "<input>:1:5", "illegal UTF-8 encoding", String) + testError(t, "`a"+"\x00", "<input>:1:3", "illegal character NUL", RawString) + testError(t, "`ab"+"\x80", "<input>:1:4", "illegal UTF-8 encoding", RawString) + testError(t, "`abc"+"\xff", "<input>:1:5", "illegal UTF-8 encoding", RawString) testError(t, `'\"'`, "<input>:1:3", "illegal char escape", Char) testError(t, `"\'"`, "<input>:1:3", "illegal char escape", String) @@ -480,7 +480,7 @@ func TestError(t *testing.T) { testError(t, `'`+"\n", "<input>:1:2", "literal not terminated", Char) testError(t, `"abc`, "<input>:1:5", "literal not terminated", String) testError(t, `"abc`+"\n", "<input>:1:5", "literal not terminated", String) - testError(t, "`abc\n", "<input>:2:1", "literal not terminated", String) + testError(t, "`abc\n", "<input>:2:1", "literal not terminated", RawString) testError(t, `/*/`, "<input>:1:4", "comment not terminated", EOF) } diff --git a/libgo/go/text/tabwriter/tabwriter.go b/libgo/go/text/tabwriter/tabwriter.go index ae6c7a2..36d999b 100644 --- a/libgo/go/text/tabwriter/tabwriter.go +++ b/libgo/go/text/tabwriter/tabwriter.go @@ -12,7 +12,6 @@ package tabwriter import ( - "bytes" "io" "unicode/utf8" ) @@ -99,25 +98,50 @@ type Writer struct { flags uint // current state - buf bytes.Buffer // collected text excluding tabs or line breaks - pos int // buffer position up to which cell.width of incomplete cell has been computed - cell cell // current incomplete cell; cell.width is up to buf[pos] excluding ignored sections - endChar byte // terminating char of escaped sequence (Escape for escapes, '>', ';' for HTML tags/entities, or 0) - lines [][]cell // list of lines; each line is a list of cells - widths []int // list of column widths in runes - re-used during formatting + buf []byte // collected text excluding tabs or line breaks + pos int // buffer position up to which cell.width of incomplete cell has been computed + cell cell // current incomplete cell; cell.width is up to buf[pos] excluding ignored sections + endChar byte // terminating char of escaped sequence (Escape for escapes, '>', ';' for HTML tags/entities, or 0) + lines [][]cell // list of lines; each line is a list of cells + widths []int // list of column widths in runes - re-used during formatting } -func (b *Writer) addLine() { b.lines = append(b.lines, []cell{}) } +// addLine adds a new line. +// flushed is a hint indicating whether the underlying writer was just flushed. +// If so, the previous line is not likely to be a good indicator of the new line's cells. +func (b *Writer) addLine(flushed bool) { + // Grow slice instead of appending, + // as that gives us an opportunity + // to re-use an existing []cell. + if n := len(b.lines) + 1; n <= cap(b.lines) { + b.lines = b.lines[:n] + b.lines[n-1] = b.lines[n-1][:0] + } else { + b.lines = append(b.lines, nil) + } + + if !flushed { + // The previous line is probably a good indicator + // of how many cells the current line will have. + // If the current line's capacity is smaller than that, + // abandon it and make a new one. + if n := len(b.lines); n >= 2 { + if prev := len(b.lines[n-2]); prev > cap(b.lines[n-1]) { + b.lines[n-1] = make([]cell, 0, prev) + } + } + } +} // Reset the current state. func (b *Writer) reset() { - b.buf.Reset() + b.buf = b.buf[:0] b.pos = 0 b.cell = cell{} b.endChar = 0 b.lines = b.lines[0:0] b.widths = b.widths[0:0] - b.addLine() + b.addLine(true) } // Internal representation (current state): @@ -212,7 +236,7 @@ func (b *Writer) dump() { for i, line := range b.lines { print("(", i, ") ") for _, c := range line { - print("[", string(b.buf.Bytes()[pos:pos+c.size]), "]") + print("[", string(b.buf[pos:pos+c.size]), "]") pos += c.size } print("\n") @@ -294,7 +318,7 @@ func (b *Writer) writeLines(pos0 int, line0, line1 int) (pos int) { // non-empty cell useTabs = false if b.flags&AlignRight == 0 { // align left - b.write0(b.buf.Bytes()[pos : pos+c.size]) + b.write0(b.buf[pos : pos+c.size]) pos += c.size if j < len(b.widths) { b.writePadding(c.width, b.widths[j], false) @@ -303,7 +327,7 @@ func (b *Writer) writeLines(pos0 int, line0, line1 int) (pos int) { if j < len(b.widths) { b.writePadding(c.width, b.widths[j], false) } - b.write0(b.buf.Bytes()[pos : pos+c.size]) + b.write0(b.buf[pos : pos+c.size]) pos += c.size } } @@ -312,7 +336,7 @@ func (b *Writer) writeLines(pos0 int, line0, line1 int) (pos int) { if i+1 == len(b.lines) { // last buffered line - we don't have a newline, so just write // any outstanding buffered data - b.write0(b.buf.Bytes()[pos : pos+b.cell.size]) + b.write0(b.buf[pos : pos+b.cell.size]) pos += b.cell.size } else { // not the last line - write newline @@ -387,14 +411,14 @@ func (b *Writer) format(pos0 int, line0, line1 int) (pos int) { // Append text to current cell. func (b *Writer) append(text []byte) { - b.buf.Write(text) + b.buf = append(b.buf, text...) b.cell.size += len(text) } // Update the cell width. func (b *Writer) updateWidth() { - b.cell.width += utf8.RuneCount(b.buf.Bytes()[b.pos:b.buf.Len()]) - b.pos = b.buf.Len() + b.cell.width += utf8.RuneCount(b.buf[b.pos:]) + b.pos = len(b.buf) } // To escape a text segment, bracket it with Escape characters. @@ -434,7 +458,7 @@ func (b *Writer) endEscape() { case ';': b.cell.width++ // entity, count as one rune } - b.pos = b.buf.Len() + b.pos = len(b.buf) b.endChar = 0 } @@ -508,7 +532,7 @@ func (b *Writer) Write(buf []byte) (n int, err error) { ncells := b.terminateCell(ch == '\t') if ch == '\n' || ch == '\f' { // terminate line - b.addLine() + b.addLine(ch == '\f') if ch == '\f' || ncells == 1 { // A '\f' always forces a flush. Otherwise, if the previous // line has only one cell which does not have an impact on diff --git a/libgo/go/text/tabwriter/tabwriter_test.go b/libgo/go/text/tabwriter/tabwriter_test.go index 9d3111e..07bae0c 100644 --- a/libgo/go/text/tabwriter/tabwriter_test.go +++ b/libgo/go/text/tabwriter/tabwriter_test.go @@ -5,7 +5,10 @@ package tabwriter_test import ( + "bytes" + "fmt" "io" + "io/ioutil" "testing" . "text/tabwriter" ) @@ -650,3 +653,79 @@ func TestPanicDuringWrite(t *testing.T) { io.WriteString(w, "a\n\n") // the second \n triggers a call to w.Write and thus a panic t.Errorf("failed to panic during Write") } + +func BenchmarkTable(b *testing.B) { + for _, w := range [...]int{1, 10, 100} { + // Build a line with w cells. + line := bytes.Repeat([]byte("a\t"), w) + line = append(line, '\n') + for _, h := range [...]int{10, 1000, 100000} { + b.Run(fmt.Sprintf("%dx%d", w, h), func(b *testing.B) { + b.Run("new", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + w := NewWriter(ioutil.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings + // Write the line h times. + for j := 0; j < h; j++ { + w.Write(line) + } + w.Flush() + } + }) + + b.Run("reuse", func(b *testing.B) { + b.ReportAllocs() + w := NewWriter(ioutil.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings + for i := 0; i < b.N; i++ { + // Write the line h times. + for j := 0; j < h; j++ { + w.Write(line) + } + w.Flush() + } + }) + }) + } + } +} + +func BenchmarkPyramid(b *testing.B) { + for _, x := range [...]int{10, 100, 1000} { + // Build a line with x cells. + line := bytes.Repeat([]byte("a\t"), x) + b.Run(fmt.Sprintf("%d", x), func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + w := NewWriter(ioutil.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings + // Write increasing prefixes of that line. + for j := 0; j < x; j++ { + w.Write(line[:j*2]) + w.Write([]byte{'\n'}) + } + w.Flush() + } + }) + } +} + +func BenchmarkRagged(b *testing.B) { + var lines [8][]byte + for i, w := range [8]int{6, 2, 9, 5, 5, 7, 3, 8} { + // Build a line with w cells. + lines[i] = bytes.Repeat([]byte("a\t"), w) + } + for _, h := range [...]int{10, 100, 1000} { + b.Run(fmt.Sprintf("%d", h), func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + w := NewWriter(ioutil.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings + // Write the lines in turn h times. + for j := 0; j < h; j++ { + w.Write(lines[j%len(lines)]) + w.Write([]byte{'\n'}) + } + w.Flush() + } + }) + } +} diff --git a/libgo/go/text/template/doc.go b/libgo/go/text/template/doc.go index d174ebd..4b24306 100644 --- a/libgo/go/text/template/doc.go +++ b/libgo/go/text/template/doc.go @@ -69,6 +69,7 @@ data, defined in detail in the corresponding sections that follow. */ // {{/* a comment */}} +// {{- /* a comment with white space trimmed from preceding and following text */ -}} // A comment; discarded. May contain newlines. // Comments do not nest and must start and end at the // delimiters, as shown here. @@ -121,7 +122,7 @@ data, defined in detail in the corresponding sections that follow. A block is shorthand for defining a template {{define "name"}} T1 {{end}} and then executing it in place - {{template "name" .}} + {{template "name" pipeline}} The typical use is to define a set of root templates that are then customized by redefining the block templates within. @@ -241,6 +242,10 @@ The initialization has syntax where $variable is the name of the variable. An action that declares a variable produces no output. +Variables previously declared can also be assigned, using the syntax + + $variable = pipeline + If a "range" action initializes a variable, the variable is set to the successive elements of the iteration. Also, a "range" may declare two variables, separated by a comma: diff --git a/libgo/go/text/template/exec.go b/libgo/go/text/template/exec.go index 2923dd9..7ee60bd 100644 --- a/libgo/go/text/template/exec.go +++ b/libgo/go/text/template/exec.go @@ -19,9 +19,16 @@ import ( // templates. This limit is only practically reached by accidentally // recursive template invocations. This limit allows us to return // an error instead of triggering a stack overflow. -// For gccgo we make this 1000 rather than 100000 to avoid stack overflow -// on non-split-stack systems. -const maxExecDepth = 1000 +var maxExecDepth = initMaxExecDepth() + +func initMaxExecDepth() int { + // For gccgo we make this 1000 rather than 100000 to avoid + // stack overflow on non-split-stack systems. + if runtime.GOARCH == "wasm" || runtime.Compiler == "gccgo" { + return 1000 + } + return 100000 +} // state represents the state of an execution. It's not part of the // template so that multiple executions of the same template @@ -55,8 +62,20 @@ func (s *state) pop(mark int) { s.vars = s.vars[0:mark] } -// setVar overwrites the top-nth variable on the stack. Used by range iterations. -func (s *state) setVar(n int, value reflect.Value) { +// setVar overwrites the last declared variable with the given name. +// Used by variable assignments. +func (s *state) setVar(name string, value reflect.Value) { + for i := s.mark() - 1; i >= 0; i-- { + if s.vars[i].name == name { + s.vars[i].value = value + return + } + } + s.errorf("undefined variable: %s", name) +} + +// setTopVar overwrites the top-nth variable on the stack. Used by range iterations. +func (s *state) setTopVar(n int, value reflect.Value) { s.vars[len(s.vars)-n].value = value } @@ -73,6 +92,10 @@ func (s *state) varValue(name string) reflect.Value { var zero reflect.Value +type missingValType struct{} + +var missingVal = reflect.ValueOf(missingValType{}) + // at marks the state to be on node n, for error reporting. func (s *state) at(node parse.Node) { s.node = node @@ -319,11 +342,11 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { oneIteration := func(index, elem reflect.Value) { // Set top var (lexically the second if there are two) to the element. if len(r.Pipe.Decl) > 0 { - s.setVar(1, elem) + s.setTopVar(1, elem) } // Set next var (lexically the first if there are two) to the index. if len(r.Pipe.Decl) > 1 { - s.setVar(2, index) + s.setTopVar(2, index) } s.walk(elem, r.List) s.pop(mark) @@ -403,6 +426,7 @@ func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value ref return } s.at(pipe) + value = missingVal for _, cmd := range pipe.Cmds { value = s.evalCommand(dot, cmd, value) // previous value is this one's final arg. // If the object has type interface{}, dig down one level to the thing inside. @@ -411,13 +435,17 @@ func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value ref } } for _, variable := range pipe.Decl { - s.push(variable.Ident[0], value) + if pipe.IsAssign { + s.setVar(variable.Ident[0], value) + } else { + s.push(variable.Ident[0], value) + } } return value } func (s *state) notAFunction(args []parse.Node, final reflect.Value) { - if len(args) > 1 || final.IsValid() { + if len(args) > 1 || final != missingVal { s.errorf("can't give argument to non-function %s", args[0]) } } @@ -521,7 +549,7 @@ func (s *state) evalVariableNode(dot reflect.Value, variable *parse.VariableNode func (s *state) evalFieldChain(dot, receiver reflect.Value, node parse.Node, ident []string, args []parse.Node, final reflect.Value) reflect.Value { n := len(ident) for i := 0; i < n-1; i++ { - receiver = s.evalField(dot, ident[i], node, nil, zero, receiver) + receiver = s.evalField(dot, ident[i], node, nil, missingVal, receiver) } // Now if it's a method, it gets the arguments. return s.evalField(dot, ident[n-1], node, args, final, receiver) @@ -558,7 +586,7 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, if method := ptr.MethodByName(fieldName); method.IsValid() { return s.evalCall(dot, method, node, fieldName, args, final) } - hasArgs := len(args) > 1 || final.IsValid() + hasArgs := len(args) > 1 || final != missingVal // It's not a method; must be a field of a struct or an element of a map. switch receiver.Kind() { case reflect.Struct: @@ -620,7 +648,7 @@ func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, a } typ := fun.Type() numIn := len(args) - if final.IsValid() { + if final != missingVal { numIn++ } numFixed := len(args) @@ -630,7 +658,7 @@ func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, a s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args)) } } else if numIn != typ.NumIn() { - s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args)) + s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), numIn) } if !goodFunc(typ) { // TODO: This could still be a confusing error; maybe goodFunc should provide info. @@ -651,7 +679,7 @@ func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, a } } // Add final value if necessary. - if final.IsValid() { + if final != missingVal { t := typ.In(typ.NumIn() - 1) if typ.IsVariadic() { if numIn-1 < numFixed { @@ -693,8 +721,12 @@ func canBeNil(typ reflect.Type) bool { // validateType guarantees that the value is valid and assignable to the type. func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value { if !value.IsValid() { - if typ == nil || canBeNil(typ) { + if typ == nil { // An untyped nil interface{}. Accept as a proper nil value. + return reflect.ValueOf(nil) + } + if canBeNil(typ) { + // Like above, but use the zero value of the non-nil type. return reflect.Zero(typ) } s.errorf("invalid value; expected %s", typ) @@ -740,15 +772,15 @@ func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) refle } s.errorf("cannot assign nil to %s", typ) case *parse.FieldNode: - return s.validateType(s.evalFieldNode(dot, arg, []parse.Node{n}, zero), typ) + return s.validateType(s.evalFieldNode(dot, arg, []parse.Node{n}, missingVal), typ) case *parse.VariableNode: - return s.validateType(s.evalVariableNode(dot, arg, nil, zero), typ) + return s.validateType(s.evalVariableNode(dot, arg, nil, missingVal), typ) case *parse.PipeNode: return s.validateType(s.evalPipeline(dot, arg), typ) case *parse.IdentifierNode: - return s.validateType(s.evalFunction(dot, arg, arg, nil, zero), typ) + return s.validateType(s.evalFunction(dot, arg, arg, nil, missingVal), typ) case *parse.ChainNode: - return s.validateType(s.evalChainNode(dot, arg, nil, zero), typ) + return s.validateType(s.evalChainNode(dot, arg, nil, missingVal), typ) } switch typ.Kind() { case reflect.Bool: @@ -849,9 +881,9 @@ func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Valu case *parse.DotNode: return dot case *parse.FieldNode: - return s.evalFieldNode(dot, n, nil, zero) + return s.evalFieldNode(dot, n, nil, missingVal) case *parse.IdentifierNode: - return s.evalFunction(dot, n, n, nil, zero) + return s.evalFunction(dot, n, n, nil, missingVal) case *parse.NilNode: // NilNode is handled in evalArg, the only place that calls here. s.errorf("evalEmptyInterface: nil (can't happen)") @@ -860,7 +892,7 @@ func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Valu case *parse.StringNode: return reflect.ValueOf(n.Text) case *parse.VariableNode: - return s.evalVariableNode(dot, n, nil, zero) + return s.evalVariableNode(dot, n, nil, missingVal) case *parse.PipeNode: return s.evalPipeline(dot, n) } diff --git a/libgo/go/text/template/exec_test.go b/libgo/go/text/template/exec_test.go index d0cda6b..6f40d80 100644 --- a/libgo/go/text/template/exec_test.go +++ b/libgo/go/text/template/exec_test.go @@ -304,6 +304,13 @@ var execTests = []execTest{ {"$.I", "{{$.I}}", "17", tVal, true}, {"$.U.V", "{{$.U.V}}", "v", tVal, true}, {"declare in action", "{{$x := $.U.V}}{{$x}}", "v", tVal, true}, + {"simple assignment", "{{$x := 2}}{{$x = 3}}{{$x}}", "3", tVal, true}, + {"nested assignment", + "{{$x := 2}}{{if true}}{{$x = 3}}{{end}}{{$x}}", + "3", tVal, true}, + {"nested assignment changes the last declaration", + "{{$x := 1}}{{if true}}{{$x := 2}}{{if true}}{{$x = 3}}{{end}}{{end}}{{$x}}", + "1", tVal, true}, // Type with String method. {"V{6666}.String()", "-{{.V0}}-", "-<6666>-", tVal, true}, @@ -330,6 +337,10 @@ var execTests = []execTest{ {"empty with struct", "{{.Empty4}}", "{UinEmpty}", tVal, true}, {"empty with struct, field", "{{.Empty4.V}}", "UinEmpty", tVal, true}, + // Edge cases with <no value> with an interface value + {"field on interface", "{{.foo}}", "<no value>", nil, true}, + {"field on parenthesized interface", "{{(.).foo}}", "<no value>", nil, true}, + // Method calls. {".Method0", "-{{.Method0}}-", "-M0-", tVal, true}, {".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true}, @@ -378,6 +389,11 @@ var execTests = []execTest{ {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true}, {"pipeline func", "-{{call .VariadicFunc `llo` | call .VariadicFunc `he` }}-", "-<he+<llo>>-", tVal, true}, + // Nil values aren't missing arguments. + {"nil pipeline", "{{ .Empty0 | call .NilOKFunc }}", "true", tVal, true}, + {"nil call arg", "{{ call .NilOKFunc .Empty0 }}", "true", tVal, true}, + {"bad nil pipeline", "{{ .Empty0 | .VariadicFunc }}", "", tVal, false}, + // Parenthesized expressions {"parens in pipeline", "{{printf `%d %d %d` (1) (2 | add 3) (add 4 (add 5 6))}}", "1 5 15", tVal, true}, @@ -432,6 +448,8 @@ var execTests = []execTest{ {"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`, "<script>alert("XSS");</script>", nil, true}, {"html", `{{html .PS}}`, "a string", tVal, true}, + {"html typed nil", `{{html .NIL}}`, "<nil>", tVal, true}, + {"html untyped nil", `{{html .Empty0}}`, "<no value>", tVal, true}, // JavaScript. {"js", `{{js .}}`, `It\'d be nice.`, `It'd be nice.`, true}, diff --git a/libgo/go/text/template/parse/lex.go b/libgo/go/text/template/parse/lex.go index e112cb7..fc259f3 100644 --- a/libgo/go/text/template/parse/lex.go +++ b/libgo/go/text/template/parse/lex.go @@ -42,7 +42,8 @@ const ( itemChar // printable ASCII character; grab bag for comma etc. itemCharConstant // character constant itemComplex // complex constant (1+2i); imaginary is just a number - itemColonEquals // colon-equals (':=') introducing a declaration + itemAssign // equals ('=') introducing an assignment + itemDeclare // colon-equals (':=') introducing a declaration itemEOF itemField // alphanumeric identifier starting with '.' itemIdentifier // alphanumeric identifier not starting with '.' @@ -366,11 +367,13 @@ func lexInsideAction(l *lexer) stateFn { return l.errorf("unclosed action") case isSpace(r): return lexSpace + case r == '=': + l.emit(itemAssign) case r == ':': if l.next() != '=' { return l.errorf("expected :=") } - l.emit(itemColonEquals) + l.emit(itemDeclare) case r == '|': l.emit(itemPipe) case r == '"': diff --git a/libgo/go/text/template/parse/lex_test.go b/libgo/go/text/template/parse/lex_test.go index cb01cd9..6e7ece9 100644 --- a/libgo/go/text/template/parse/lex_test.go +++ b/libgo/go/text/template/parse/lex_test.go @@ -16,7 +16,7 @@ var itemName = map[itemType]string{ itemChar: "char", itemCharConstant: "charconst", itemComplex: "complex", - itemColonEquals: ":=", + itemDeclare: ":=", itemEOF: "EOF", itemField: "field", itemIdentifier: "identifier", @@ -210,7 +210,7 @@ var lexTests = []lexTest{ tLeft, mkItem(itemVariable, "$c"), tSpace, - mkItem(itemColonEquals, ":="), + mkItem(itemDeclare, ":="), tSpace, mkItem(itemIdentifier, "printf"), tSpace, @@ -262,7 +262,7 @@ var lexTests = []lexTest{ tLeft, mkItem(itemVariable, "$v"), tSpace, - mkItem(itemColonEquals, ":="), + mkItem(itemDeclare, ":="), tSpace, mkItem(itemNumber, "3"), tRight, @@ -276,7 +276,7 @@ var lexTests = []lexTest{ tSpace, mkItem(itemVariable, "$w"), tSpace, - mkItem(itemColonEquals, ":="), + mkItem(itemDeclare, ":="), tSpace, mkItem(itemNumber, "3"), tRight, diff --git a/libgo/go/text/template/parse/node.go b/libgo/go/text/template/parse/node.go index 55ff46c..dca83da 100644 --- a/libgo/go/text/template/parse/node.go +++ b/libgo/go/text/template/parse/node.go @@ -144,14 +144,15 @@ func (t *TextNode) Copy() Node { type PipeNode struct { NodeType Pos - tr *Tree - Line int // The line number in the input. Deprecated: Kept for compatibility. - Decl []*VariableNode // Variable declarations in lexical order. - Cmds []*CommandNode // The commands in lexical order. + tr *Tree + Line int // The line number in the input. Deprecated: Kept for compatibility. + IsAssign bool // The variables are being assigned, not declared. + Decl []*VariableNode // Variables in lexical order. + Cmds []*CommandNode // The commands in lexical order. } -func (t *Tree) newPipeline(pos Pos, line int, decl []*VariableNode) *PipeNode { - return &PipeNode{tr: t, NodeType: NodePipe, Pos: pos, Line: line, Decl: decl} +func (t *Tree) newPipeline(pos Pos, line int, vars []*VariableNode) *PipeNode { + return &PipeNode{tr: t, NodeType: NodePipe, Pos: pos, Line: line, Decl: vars} } func (p *PipeNode) append(command *CommandNode) { @@ -186,11 +187,12 @@ func (p *PipeNode) CopyPipe() *PipeNode { if p == nil { return p } - var decl []*VariableNode + var vars []*VariableNode for _, d := range p.Decl { - decl = append(decl, d.Copy().(*VariableNode)) + vars = append(vars, d.Copy().(*VariableNode)) } - n := p.tr.newPipeline(p.Pos, p.Line, decl) + n := p.tr.newPipeline(p.Pos, p.Line, vars) + n.IsAssign = p.IsAssign for _, c := range p.Cmds { n.append(c.Copy().(*CommandNode)) } @@ -317,7 +319,7 @@ func (i *IdentifierNode) Copy() Node { return NewIdentifier(i.Ident).SetTree(i.tr).SetPos(i.Pos) } -// VariableNode holds a list of variable names, possibly with chained field +// AssignNode holds a list of variable names, possibly with chained field // accesses. The dollar sign is part of the (first) name. type VariableNode struct { NodeType diff --git a/libgo/go/text/template/parse/parse.go b/libgo/go/text/template/parse/parse.go index a91a544..cb9b44e 100644 --- a/libgo/go/text/template/parse/parse.go +++ b/libgo/go/text/template/parse/parse.go @@ -383,10 +383,11 @@ func (t *Tree) action() (n Node) { // Pipeline: // declarations? command ('|' command)* func (t *Tree) pipeline(context string) (pipe *PipeNode) { - var decl []*VariableNode + decl := false + var vars []*VariableNode token := t.peekNonSpace() pos := token.pos - // Are there declarations? + // Are there declarations or assignments? for { if v := t.peekNonSpace(); v.typ == itemVariable { t.next() @@ -395,26 +396,33 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) { // argument variable rather than a declaration. So remember the token // adjacent to the variable so we can push it back if necessary. tokenAfterVariable := t.peek() - if next := t.peekNonSpace(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") { + next := t.peekNonSpace() + switch { + case next.typ == itemAssign, next.typ == itemDeclare, + next.typ == itemChar && next.val == ",": t.nextNonSpace() variable := t.newVariable(v.pos, v.val) - decl = append(decl, variable) + vars = append(vars, variable) t.vars = append(t.vars, v.val) + if next.typ == itemDeclare { + decl = true + } if next.typ == itemChar && next.val == "," { - if context == "range" && len(decl) < 2 { + if context == "range" && len(vars) < 2 { continue } t.errorf("too many declarations in %s", context) } - } else if tokenAfterVariable.typ == itemSpace { + case tokenAfterVariable.typ == itemSpace: t.backup3(v, tokenAfterVariable) - } else { + default: t.backup2(v) } } break } - pipe = t.newPipeline(pos, token.line, decl) + pipe = t.newPipeline(pos, token.line, vars) + pipe.IsAssign = !decl for { switch token := t.nextNonSpace(); token.typ { case itemRightDelim, itemRightParen: diff --git a/libgo/go/text/template/parse/parse_test.go b/libgo/go/text/template/parse/parse_test.go index 81f14ac..c1f80c1 100644 --- a/libgo/go/text/template/parse/parse_test.go +++ b/libgo/go/text/template/parse/parse_test.go @@ -259,9 +259,9 @@ var parseTests = []parseTest{ {"adjacent args", "{{printf 3`x`}}", hasError, ""}, {"adjacent args with .", "{{printf `x`.}}", hasError, ""}, {"extra end after if", "{{if .X}}a{{else if .Y}}b{{end}}{{end}}", hasError, ""}, - // Equals (and other chars) do not assignments make (yet). + // Other kinds of assignments and operators aren't available yet. {"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"}, - {"bug0b", "{{$x = 1}}{{$x}}", hasError, ""}, + {"bug0b", "{{$x += 1}}{{$x}}", hasError, ""}, {"bug0c", "{{$x ! 2}}{{$x}}", hasError, ""}, {"bug0d", "{{$x % 3}}{{$x}}", hasError, ""}, // Check the parse fails for := rather than comma. diff --git a/libgo/go/text/template/template.go b/libgo/go/text/template/template.go index 2246f67..41cdd56 100644 --- a/libgo/go/text/template/template.go +++ b/libgo/go/text/template/template.go @@ -125,9 +125,7 @@ func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error nt = t.New(name) } // Even if nt == t, we need to install it in the common.tmpl map. - if replace, err := t.associate(nt, tree); err != nil { - return nil, err - } else if replace || nt.Tree == nil { + if t.associate(nt, tree) || nt.Tree == nil { nt.Tree = tree } return nt, nil @@ -212,15 +210,15 @@ func (t *Template) Parse(text string) (*Template, error) { // associate installs the new template into the group of templates associated // with t. The two are already known to share the common structure. // The boolean return value reports whether to store this tree as t.Tree. -func (t *Template) associate(new *Template, tree *parse.Tree) (bool, error) { +func (t *Template) associate(new *Template, tree *parse.Tree) bool { if new.common != t.common { panic("internal error: associate not common") } if old := t.tmpl[new.name]; old != nil && parse.IsEmptyTree(tree.Root) && old.Tree != nil { // If a template by that name exists, // don't replace it with an empty template. - return false, nil + return false } t.tmpl[new.name] = new - return true, nil + return true } diff --git a/libgo/go/time/example_test.go b/libgo/go/time/example_test.go index 8c64506..494a416 100644 --- a/libgo/go/time/example_test.go +++ b/libgo/go/time/example_test.go @@ -74,17 +74,17 @@ func ExampleDuration_Truncate() { } for _, t := range trunc { - fmt.Printf("t.Truncate(%6s) = %s\n", t, d.Truncate(t).String()) + fmt.Printf("d.Truncate(%6s) = %s\n", t, d.Truncate(t).String()) } // Output: - // t.Truncate( 1ns) = 1h15m30.918273645s - // t.Truncate( 1µs) = 1h15m30.918273s - // t.Truncate( 1ms) = 1h15m30.918s - // t.Truncate( 1s) = 1h15m30s - // t.Truncate( 2s) = 1h15m30s - // t.Truncate( 1m0s) = 1h15m0s - // t.Truncate( 10m0s) = 1h10m0s - // t.Truncate(1h0m0s) = 1h0m0s + // d.Truncate( 1ns) = 1h15m30.918273645s + // d.Truncate( 1µs) = 1h15m30.918273s + // d.Truncate( 1ms) = 1h15m30.918s + // d.Truncate( 1s) = 1h15m30s + // d.Truncate( 2s) = 1h15m30s + // d.Truncate( 1m0s) = 1h15m0s + // d.Truncate( 10m0s) = 1h10m0s + // d.Truncate(1h0m0s) = 1h0m0s } func ExampleParseDuration() { @@ -598,3 +598,10 @@ func ExampleTime_AppendFormat() { // Output: // Time: 11:00AM } + +func ExampleFixedZone() { + loc := time.FixedZone("UTC-8", -8*60*60) + t := time.Date(2009, time.November, 10, 23, 0, 0, 0, loc) + fmt.Println("The time is:", t.Format(time.RFC822)) + // Output: The time is: 10 Nov 09 23:00 UTC-8 +} diff --git a/libgo/go/time/format.go b/libgo/go/time/format.go index a60474f..237f287 100644 --- a/libgo/go/time/format.go +++ b/libgo/go/time/format.go @@ -1059,7 +1059,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) // 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.unixSec()) + name, offset, _, _ := local.lookup(t.unixSec()) if offset == zoneOffset && (zoneName == "" || name == zoneName) { t.setLoc(local) return t, nil @@ -1117,6 +1117,11 @@ func parseTimeZone(value string) (length int, ok bool) { length = parseGMT(value) return length, true } + // Special Case 3: Some time zones are not named, but have +/-00 format + if value[0] == '+' || value[0] == '-' { + length = parseSignedOffset(value) + return length, true + } // How many upper-case letters are there? Need at least three, at most five. var nUpper int for nUpper = 0; nUpper < 6; nUpper++ { @@ -1153,21 +1158,29 @@ func parseGMT(value string) int { if len(value) == 0 { return 3 } + + return 3 + parseSignedOffset(value) +} + +// parseSignedOffset parses a signed timezone offset (e.g. "+03" or "-04"). +// The function checks for a signed number in the range -14 through +12 excluding zero. +// Returns length of the found offset string or 0 otherwise +func parseSignedOffset(value string) int { sign := value[0] if sign != '-' && sign != '+' { - return 3 + return 0 } x, rem, err := leadingInt(value[1:]) if err != nil { - return 3 + return 0 } if sign == '-' { x = -x } if x == 0 || x < -14 || 12 < x { - return 3 + return 0 } - return 3 + len(value) - len(rem) + return len(value) - len(rem) } func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) { diff --git a/libgo/go/time/format_test.go b/libgo/go/time/format_test.go index d6cf674..72dd592 100644 --- a/libgo/go/time/format_test.go +++ b/libgo/go/time/format_test.go @@ -429,6 +429,8 @@ var parseTimeZoneTests = []ParseTimeZoneTest{ {"ESASTT hi", 0, false}, // run of upper-case letters too long. {"ESATY hi", 0, false}, // five letters must end in T. {"WITA hi", 4, true}, // Issue #18251 + {"+03 hi", 3, true}, // Issue #24071 + {"-04 hi", 3, true}, // Issue #24071 } func TestParseTimeZone(t *testing.T) { diff --git a/libgo/go/time/internal_test.go b/libgo/go/time/internal_test.go index e3f458f..ba5e00e 100644 --- a/libgo/go/time/internal_test.go +++ b/libgo/go/time/internal_test.go @@ -81,3 +81,8 @@ func CheckRuntimeTimerOverflow() { // So we fall back to hope: We hope we don't hang here. <-t.C } + +var ( + MinMonoTime = Time{wall: 1 << 63, ext: -1 << 63, loc: UTC} + MaxMonoTime = Time{wall: 1 << 63, ext: 1<<63 - 1, loc: UTC} +) diff --git a/libgo/go/time/sleep_test.go b/libgo/go/time/sleep_test.go index 670ff1de..e6a08fc 100644 --- a/libgo/go/time/sleep_test.go +++ b/libgo/go/time/sleep_test.go @@ -360,7 +360,7 @@ func TestSleepZeroDeadlock(t *testing.T) { func testReset(d Duration) error { t0 := NewTimer(2 * d) Sleep(d) - if t0.Reset(3*d) != true { + if !t0.Reset(3 * d) { return errors.New("resetting unfired timer returned false") } Sleep(2 * d) @@ -376,7 +376,7 @@ func testReset(d Duration) error { return errors.New("reset timer did not fire") } - if t0.Reset(50*Millisecond) != false { + if t0.Reset(50 * Millisecond) { return errors.New("resetting expired timer returned true") } return nil diff --git a/libgo/go/time/sys_unix.go b/libgo/go/time/sys_unix.go index 3827965..f4756b1 100644 --- a/libgo/go/time/sys_unix.go +++ b/libgo/go/time/sys_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package time diff --git a/libgo/go/time/tick.go b/libgo/go/time/tick.go index 3d69320..e4cd43a 100644 --- a/libgo/go/time/tick.go +++ b/libgo/go/time/tick.go @@ -40,8 +40,8 @@ func NewTicker(d Duration) *Ticker { } // Stop turns off a ticker. After Stop, no more ticks will be sent. -// Stop does not close the channel, to prevent a read from the channel succeeding -// incorrectly. +// Stop does not close the channel, to prevent a concurrent goroutine +// reading from the channel from seeing an erroneous "tick". func (t *Ticker) Stop() { stopTimer(&t.r) } diff --git a/libgo/go/time/time.go b/libgo/go/time/time.go index 9390968..2374043 100644 --- a/libgo/go/time/time.go +++ b/libgo/go/time/time.go @@ -51,6 +51,10 @@ // readings. If either t or u contains no monotonic clock reading, these // operations fall back to using the wall clock readings. // +// On some systems the monotonic clock will stop if the computer goes to sleep. +// On such a system, t.Sub(u) may not accurately reflect the actual +// time that passed between t and u. +// // Because the monotonic clock reading has no meaning outside // the current process, the serialized forms generated by t.GobEncode, // t.MarshalBinary, t.MarshalJSON, and t.MarshalText omit the monotonic @@ -158,7 +162,7 @@ func (t *Time) sec() int64 { if t.wall&hasMonotonic != 0 { return wallToInternal + int64(t.wall<<1>>(nsecShift+1)) } - return int64(t.ext) + return t.ext } // unixSec returns the time's seconds since Jan 1 1970 (Unix time). @@ -205,7 +209,7 @@ func (t *Time) stripMono() { // setMono is a no-op. func (t *Time) setMono(m int64) { if t.wall&hasMonotonic == 0 { - sec := int64(t.ext) + sec := t.ext if sec < minWall || maxWall < sec { return } @@ -323,7 +327,14 @@ var days = [...]string{ } // String returns the English name of the day ("Sunday", "Monday", ...). -func (d Weekday) String() string { return days[d] } +func (d Weekday) String() string { + if Sunday <= d && d <= Saturday { + return days[d] + } + buf := make([]byte, 20) + n := fmtInt(buf, uint64(d)) + return "%!Weekday(" + string(buf[n:]) + ")" +} // Computations on time. // @@ -445,7 +456,7 @@ func (t Time) abs() uint64 { if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd { sec += int64(l.cacheZone.offset) } else { - _, offset, _, _, _ := l.lookup(sec) + _, offset, _, _ := l.lookup(sec) sec += int64(offset) } } @@ -466,7 +477,7 @@ func (t Time) locabs() (name string, offset int, abs uint64) { name = l.cacheZone.name offset = l.cacheZone.offset } else { - name, offset, _, _, _ = l.lookup(sec) + name, offset, _, _ = l.lookup(sec) } sec += int64(offset) } else { @@ -855,7 +866,7 @@ func (t Time) Add(d Duration) Time { t.addSec(dsec) if t.wall&hasMonotonic != 0 { te := t.ext + int64(d) - if d < 0 && te > int64(t.ext) || d > 0 && te < int64(t.ext) { + if d < 0 && te > t.ext || d > 0 && te < t.ext { // Monotonic clock reading now out of range; degrade to wall-only. t.stripMono() } else { @@ -871,8 +882,8 @@ func (t Time) Add(d Duration) Time { // To compute t-d for a duration d, use t.Add(-d). func (t Time) Sub(u Time) Duration { if t.wall&u.wall&hasMonotonic != 0 { - te := int64(t.ext) - ue := int64(u.ext) + te := t.ext + ue := u.ext d := Duration(te - ue) if d < 0 && te > ue { return maxDuration // t - u is positive out of range @@ -1065,7 +1076,9 @@ func (t Time) Local() Time { return t } -// In returns t with the location information set to loc. +// In returns a copy of t representating the same time instant, but +// with the copy's location information set to loc for display +// purposes. // // In panics if loc is nil. func (t Time) In(loc *Location) Time { @@ -1088,12 +1101,13 @@ func (t Time) Location() *Location { // 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.unixSec()) + name, offset, _, _ = t.loc.lookup(t.unixSec()) return } // Unix returns t as a Unix time, the number of seconds elapsed -// since January 1, 1970 UTC. +// since January 1, 1970 UTC. The result does not depend on the +// location associated with t. func (t Time) Unix() int64 { return t.unixSec() } @@ -1102,7 +1116,8 @@ func (t Time) Unix() int64 { // since January 1, 1970 UTC. The result is undefined if the Unix time // in nanoseconds cannot be represented by an int64 (a date before the year // 1678 or after 2262). Note that this means the result of calling UnixNano -// on the zero Time is undefined. +// on the zero Time is undefined. The result does not depend on the +// location associated with t. func (t Time) UnixNano() int64 { return (t.unixSec())*1e9 + int64(t.nsec()) } @@ -1181,7 +1196,7 @@ func (t *Time) UnmarshalBinary(data []byte) error { if offset == -1*60 { t.setLoc(&utcLoc) - } else if _, localoff, _, _, _ := Local.lookup(t.unixSec()); offset == localoff { + } else if _, localoff, _, _ := Local.lookup(t.unixSec()); offset == localoff { t.setLoc(Local) } else { t.setLoc(FixedZone("", offset)) @@ -1366,13 +1381,13 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T // 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) + _, offset, start, end := loc.lookup(unix) if offset != 0 { switch utc := unix - int64(offset); { case utc < start: - _, offset, _, _, _ = loc.lookup(start - 1) + _, offset, _, _ = loc.lookup(start - 1) case utc >= end: - _, offset, _, _, _ = loc.lookup(end) + _, offset, _, _ = loc.lookup(end) } unix -= int64(offset) } diff --git a/libgo/go/time/time_test.go b/libgo/go/time/time_test.go index fd464c0..432a67d 100644 --- a/libgo/go/time/time_test.go +++ b/libgo/go/time/time_test.go @@ -9,11 +9,13 @@ import ( "encoding/gob" "encoding/json" "fmt" + "internal/race" "math/big" "math/rand" "os" "runtime" "strings" + "sync" "testing" "testing/quick" . "time" @@ -673,7 +675,7 @@ var gobTests = []Time{ Date(0, 1, 2, 3, 4, 5, 6, UTC), Date(7, 8, 9, 10, 11, 12, 13, FixedZone("", 0)), Unix(81985467080890095, 0x76543210), // Time.sec: 0x0123456789ABCDEF - {}, // nil location + {}, // nil location Date(1, 2, 3, 4, 5, 6, 7, FixedZone("", 32767*60)), Date(1, 2, 3, 4, 5, 6, 7, FixedZone("", -32768*60)), } @@ -978,6 +980,8 @@ var subTests = []struct { {Date(2300, 1, 1, 0, 0, 0, 0, UTC), Date(2000, 1, 1, 0, 0, 0, 0, UTC), Duration(maxDuration)}, {Date(2000, 1, 1, 0, 0, 0, 0, UTC), Date(2290, 1, 1, 0, 0, 0, 0, UTC), -290*365*24*Hour - 71*24*Hour}, {Date(2000, 1, 1, 0, 0, 0, 0, UTC), Date(2300, 1, 1, 0, 0, 0, 0, UTC), Duration(minDuration)}, + {MinMonoTime, MaxMonoTime, minDuration}, + {MaxMonoTime, MinMonoTime, maxDuration}, } func TestSub(t *testing.T) { @@ -1319,6 +1323,16 @@ func TestZeroMonthString(t *testing.T) { } } +// Issue 24692: Out of range weekday panics +func TestWeekdayString(t *testing.T) { + if got, want := Weekday(Tuesday).String(), "Tuesday"; got != want { + t.Errorf("Tuesday weekday = %q; want %q", got, want) + } + if got, want := Weekday(14).String(), "%!Weekday(14)"; got != want { + t.Errorf("14th weekday = %q; want %q", got, want) + } +} + func TestReadFileLimit(t *testing.T) { const zero = "/dev/zero" if _, err := os.Stat(zero); err != nil { @@ -1329,3 +1343,38 @@ func TestReadFileLimit(t *testing.T) { t.Errorf("readFile(%q) error = %v; want error containing 'is too large'", zero, err) } } + +// Issue 25686: hard crash on concurrent timer access. +// This test deliberately invokes a race condition. +// We are testing that we don't crash with "fatal error: panic holding locks". +func TestConcurrentTimerReset(t *testing.T) { + if race.Enabled { + t.Skip("skipping test under race detector") + } + + // We expect this code to panic rather than crash. + // Don't worry if it doesn't panic. + catch := func(i int) { + if e := recover(); e != nil { + t.Logf("panic in goroutine %d, as expected, with %q", i, e) + } else { + t.Logf("no panic in goroutine %d", i) + } + } + + const goroutines = 8 + const tries = 1000 + var wg sync.WaitGroup + wg.Add(goroutines) + timer := NewTimer(Hour) + for i := 0; i < goroutines; i++ { + go func(i int) { + defer wg.Done() + defer catch(i) + for j := 0; j < tries; j++ { + timer.Reset(Hour + Duration(i*j)) + } + }(i) + } + wg.Wait() +} diff --git a/libgo/go/time/zoneinfo.go b/libgo/go/time/zoneinfo.go index 96ff8d3..d2bc642 100644 --- a/libgo/go/time/zoneinfo.go +++ b/libgo/go/time/zoneinfo.go @@ -108,13 +108,12 @@ func FixedZone(name string, offset int) *Location { // 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) { +func (l *Location) lookup(sec int64) (name string, offset int, start, end int64) { l = l.get() if len(l.zone) == 0 { name = "UTC" offset = 0 - isDST = false start = alpha end = omega return @@ -123,7 +122,6 @@ func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start 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 @@ -133,7 +131,6 @@ func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start zone := &l.zone[l.lookupFirstZone()] name = zone.name offset = zone.offset - isDST = zone.isDST start = alpha if len(l.tx) > 0 { end = l.tx[0].when @@ -162,7 +159,6 @@ func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start zone := &l.zone[tx[lo].index] name = zone.name offset = zone.offset - isDST = zone.isDST start = tx[lo].when // end = maintained during the search return @@ -173,7 +169,7 @@ func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start // times. // // The reference implementation in localtime.c from -// http://www.iana.org/time-zones/repository/releases/tzcode2013g.tar.gz +// https://www.iana.org/time-zones/repository/releases/tzcode2013g.tar.gz // implements the following algorithm for these cases: // 1) If the first zone is unused by the transitions, use it. // 2) Otherwise, if there are transition times, and the first @@ -235,7 +231,7 @@ func (l *Location) lookupName(name string, unix int64) (offset int, ok bool) { for i := range l.zone { zone := &l.zone[i] if zone.name == name { - nam, offset, _, _, _ := l.lookup(unix - int64(zone.offset)) + nam, offset, _, _ := l.lookup(unix - int64(zone.offset)) if nam == zone.name { return offset, true } diff --git a/libgo/go/time/zoneinfo_abbrs_windows.go b/libgo/go/time/zoneinfo_abbrs_windows.go index db0bbfd..5411325 100644 --- a/libgo/go/time/zoneinfo_abbrs_windows.go +++ b/libgo/go/time/zoneinfo_abbrs_windows.go @@ -16,10 +16,11 @@ var abbrs = map[string]abbr{ "Egypt Standard Time": {"EET", "EET"}, // Africa/Cairo "Morocco Standard Time": {"WET", "WEST"}, // Africa/Casablanca "South Africa Standard Time": {"SAST", "SAST"}, // Africa/Johannesburg + "Sudan Standard Time": {"CAT", "CAT"}, // Africa/Khartoum "W. Central Africa Standard Time": {"WAT", "WAT"}, // Africa/Lagos "E. Africa Standard Time": {"EAT", "EAT"}, // Africa/Nairobi "Libya Standard Time": {"EET", "EET"}, // Africa/Tripoli - "Namibia Standard Time": {"WAT", "WAST"}, // Africa/Windhoek + "Namibia Standard Time": {"CAT", "CAT"}, // Africa/Windhoek "Aleutian Standard Time": {"HST", "HDT"}, // America/Adak "Alaskan Standard Time": {"AKST", "AKDT"}, // America/Anchorage "Tocantins Standard Time": {"-03", "-03"}, // America/Araguaina @@ -35,7 +36,7 @@ var abbrs = map[string]abbr{ "Central Brazilian Standard Time": {"-04", "-03"}, // America/Cuiaba "Mountain Standard Time": {"MST", "MDT"}, // America/Denver "Greenland Standard Time": {"-03", "-02"}, // America/Godthab - "Turks And Caicos Standard Time": {"AST", "AST"}, // America/Grand_Turk + "Turks And Caicos Standard Time": {"AST", "EDT"}, // America/Grand_Turk "Central America Standard Time": {"CST", "CST"}, // America/Guatemala "Atlantic Standard Time": {"AST", "ADT"}, // America/Halifax "Cuba Standard Time": {"CST", "CDT"}, // America/Havana @@ -109,41 +110,41 @@ var abbrs = map[string]abbr{ "Lord Howe Standard Time": {"+1030", "+11"}, // Australia/Lord_Howe "W. Australia Standard Time": {"AWST", "AWST"}, // Australia/Perth "AUS Eastern Standard Time": {"AEST", "AEDT"}, // Australia/Sydney - "UTC": {"GMT", "GMT"}, // Etc/GMT - "UTC-11": {"-11", "-11"}, // Etc/GMT+11 - "Dateline Standard Time": {"-12", "-12"}, // Etc/GMT+12 - "UTC-02": {"-02", "-02"}, // Etc/GMT+2 - "UTC-08": {"-08", "-08"}, // Etc/GMT+8 - "UTC-09": {"-09", "-09"}, // Etc/GMT+9 - "UTC+12": {"+12", "+12"}, // Etc/GMT-12 - "UTC+13": {"+13", "+13"}, // Etc/GMT-13 - "Astrakhan Standard Time": {"+04", "+04"}, // Europe/Astrakhan - "W. Europe Standard Time": {"CET", "CEST"}, // Europe/Berlin - "GTB Standard Time": {"EET", "EEST"}, // Europe/Bucharest - "Central Europe Standard Time": {"CET", "CEST"}, // Europe/Budapest - "E. Europe Standard Time": {"EET", "EEST"}, // Europe/Chisinau - "Turkey Standard Time": {"+03", "+03"}, // Europe/Istanbul - "Kaliningrad Standard Time": {"EET", "EET"}, // Europe/Kaliningrad - "FLE Standard Time": {"EET", "EEST"}, // Europe/Kiev - "GMT Standard Time": {"GMT", "BST"}, // Europe/London - "Belarus Standard Time": {"+03", "+03"}, // Europe/Minsk - "Russian Standard Time": {"MSK", "MSK"}, // Europe/Moscow - "Romance Standard Time": {"CET", "CEST"}, // Europe/Paris - "Russia Time Zone 3": {"+04", "+04"}, // Europe/Samara - "Saratov Standard Time": {"+03", "+04"}, // Europe/Saratov - "Central European Standard Time": {"CET", "CEST"}, // Europe/Warsaw - "Mauritius Standard Time": {"+04", "+04"}, // Indian/Mauritius - "Samoa Standard Time": {"+13", "+14"}, // Pacific/Apia - "New Zealand Standard Time": {"NZST", "NZDT"}, // Pacific/Auckland - "Bougainville Standard Time": {"+11", "+11"}, // Pacific/Bougainville - "Chatham Islands Standard Time": {"+1245", "+1345"}, // Pacific/Chatham - "Easter Island Standard Time": {"-06", "-05"}, // Pacific/Easter - "Fiji Standard Time": {"+12", "+13"}, // Pacific/Fiji - "Central Pacific Standard Time": {"+11", "+11"}, // Pacific/Guadalcanal - "Hawaiian Standard Time": {"HST", "HST"}, // Pacific/Honolulu - "Line Islands Standard Time": {"+14", "+14"}, // Pacific/Kiritimati - "Marquesas Standard Time": {"-0930", "-0930"}, // Pacific/Marquesas - "Norfolk Standard Time": {"+11", "+11"}, // Pacific/Norfolk - "West Pacific Standard Time": {"+10", "+10"}, // Pacific/Port_Moresby - "Tonga Standard Time": {"+13", "+14"}, // Pacific/Tongatapu + "UTC": {"GMT", "GMT"}, // Etc/GMT + "UTC-11": {"-11", "-11"}, // Etc/GMT+11 + "Dateline Standard Time": {"-12", "-12"}, // Etc/GMT+12 + "UTC-02": {"-02", "-02"}, // Etc/GMT+2 + "UTC-08": {"-08", "-08"}, // Etc/GMT+8 + "UTC-09": {"-09", "-09"}, // Etc/GMT+9 + "UTC+12": {"+12", "+12"}, // Etc/GMT-12 + "UTC+13": {"+13", "+13"}, // Etc/GMT-13 + "Astrakhan Standard Time": {"+04", "+04"}, // Europe/Astrakhan + "W. Europe Standard Time": {"CET", "CEST"}, // Europe/Berlin + "GTB Standard Time": {"EET", "EEST"}, // Europe/Bucharest + "Central Europe Standard Time": {"CET", "CEST"}, // Europe/Budapest + "E. Europe Standard Time": {"EET", "EEST"}, // Europe/Chisinau + "Turkey Standard Time": {"+03", "+03"}, // Europe/Istanbul + "Kaliningrad Standard Time": {"EET", "EET"}, // Europe/Kaliningrad + "FLE Standard Time": {"EET", "EEST"}, // Europe/Kiev + "GMT Standard Time": {"GMT", "BST"}, // Europe/London + "Belarus Standard Time": {"+03", "+03"}, // Europe/Minsk + "Russian Standard Time": {"MSK", "MSK"}, // Europe/Moscow + "Romance Standard Time": {"CET", "CEST"}, // Europe/Paris + "Russia Time Zone 3": {"+04", "+04"}, // Europe/Samara + "Saratov Standard Time": {"+04", "+04"}, // Europe/Saratov + "Central European Standard Time": {"CET", "CEST"}, // Europe/Warsaw + "Mauritius Standard Time": {"+04", "+04"}, // Indian/Mauritius + "Samoa Standard Time": {"+13", "+14"}, // Pacific/Apia + "New Zealand Standard Time": {"NZST", "NZDT"}, // Pacific/Auckland + "Bougainville Standard Time": {"+11", "+11"}, // Pacific/Bougainville + "Chatham Islands Standard Time": {"+1245", "+1345"}, // Pacific/Chatham + "Easter Island Standard Time": {"-06", "-05"}, // Pacific/Easter + "Fiji Standard Time": {"+12", "+13"}, // Pacific/Fiji + "Central Pacific Standard Time": {"+11", "+11"}, // Pacific/Guadalcanal + "Hawaiian Standard Time": {"HST", "HST"}, // Pacific/Honolulu + "Line Islands Standard Time": {"+14", "+14"}, // Pacific/Kiritimati + "Marquesas Standard Time": {"-0930", "-0930"}, // Pacific/Marquesas + "Norfolk Standard Time": {"+11", "+11"}, // Pacific/Norfolk + "West Pacific Standard Time": {"+10", "+10"}, // Pacific/Port_Moresby + "Tonga Standard Time": {"+13", "+13"}, // Pacific/Tongatapu } diff --git a/libgo/go/time/zoneinfo_read.go b/libgo/go/time/zoneinfo_read.go index 839b37a..20f84f0 100644 --- a/libgo/go/time/zoneinfo_read.go +++ b/libgo/go/time/zoneinfo_read.go @@ -4,7 +4,7 @@ // Parse "zoneinfo" time zone file. // This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others. -// See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo, +// See tzfile(5), https://en.wikipedia.org/wiki/Zoneinfo, // and ftp://munnari.oz.au/pub/oldtz/ package time diff --git a/libgo/go/time/zoneinfo_test.go b/libgo/go/time/zoneinfo_test.go index de96416..9bf8c4f 100644 --- a/libgo/go/time/zoneinfo_test.go +++ b/libgo/go/time/zoneinfo_test.go @@ -32,7 +32,7 @@ func TestEnvVarUsage(t *testing.T) { defer time.ResetZoneinfoForTesting() if zoneinfo := time.ZoneinfoForTesting(); testZoneinfo != *zoneinfo { - t.Errorf("zoneinfo does not match env variable: got %q want %q", zoneinfo, testZoneinfo) + t.Errorf("zoneinfo does not match env variable: got %q want %q", *zoneinfo, testZoneinfo) } } diff --git a/libgo/go/time/zoneinfo_unix.go b/libgo/go/time/zoneinfo_unix.go index 32dc7d5..fca8e54 100644 --- a/libgo/go/time/zoneinfo_unix.go +++ b/libgo/go/time/zoneinfo_unix.go @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin,386 darwin,amd64 dragonfly freebsd linux,!android nacl netbsd openbsd solaris +// +build aix darwin,386 darwin,amd64 dragonfly freebsd js,wasm linux,!android nacl netbsd openbsd solaris // Parse "zoneinfo" time zone file. // This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others. -// See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo, +// See tzfile(5), https://en.wikipedia.org/wiki/Zoneinfo, // and ftp://munnari.oz.au/pub/oldtz/ package time diff --git a/libgo/go/unicode/letter.go b/libgo/go/unicode/letter.go index 4d9fc67..8be9a7b 100644 --- a/libgo/go/unicode/letter.go +++ b/libgo/go/unicode/letter.go @@ -206,9 +206,10 @@ func IsTitle(r rune) bool { } // to maps the rune using the specified case mapping. -func to(_case int, r rune, caseRange []CaseRange) rune { +// It additionally reports whether caseRange contained a mapping for r. +func to(_case int, r rune, caseRange []CaseRange) (mappedRune rune, foundMapping bool) { if _case < 0 || MaxCase <= _case { - return ReplacementChar // as reasonable an error as any + return ReplacementChar, false // as reasonable an error as any } // binary search over ranges lo := 0 @@ -229,9 +230,9 @@ func to(_case int, r rune, caseRange []CaseRange) rune { // bit in the sequence offset. // The constants UpperCase and TitleCase are even while LowerCase // is odd so we take the low bit from _case. - return rune(cr.Lo) + ((r-rune(cr.Lo))&^1 | rune(_case&1)) + return rune(cr.Lo) + ((r-rune(cr.Lo))&^1 | rune(_case&1)), true } - return r + delta + return r + delta, true } if r < rune(cr.Lo) { hi = m @@ -239,12 +240,13 @@ func to(_case int, r rune, caseRange []CaseRange) rune { lo = m + 1 } } - return r + return r, false } // To maps the rune to the specified case: UpperCase, LowerCase, or TitleCase. func To(_case int, r rune) rune { - return to(_case, r, CaseRanges) + r, _ = to(_case, r, CaseRanges) + return r } // ToUpper maps the rune to upper case. @@ -282,8 +284,8 @@ func ToTitle(r rune) rune { // ToUpper maps the rune to upper case giving priority to the special mapping. func (special SpecialCase) ToUpper(r rune) rune { - r1 := to(UpperCase, r, []CaseRange(special)) - if r1 == r { + r1, hadMapping := to(UpperCase, r, []CaseRange(special)) + if r1 == r && !hadMapping { r1 = ToUpper(r) } return r1 @@ -291,8 +293,8 @@ func (special SpecialCase) ToUpper(r rune) rune { // ToTitle maps the rune to title case giving priority to the special mapping. func (special SpecialCase) ToTitle(r rune) rune { - r1 := to(TitleCase, r, []CaseRange(special)) - if r1 == r { + r1, hadMapping := to(TitleCase, r, []CaseRange(special)) + if r1 == r && !hadMapping { r1 = ToTitle(r) } return r1 @@ -300,8 +302,8 @@ func (special SpecialCase) ToTitle(r rune) rune { // ToLower maps the rune to lower case giving priority to the special mapping. func (special SpecialCase) ToLower(r rune) rune { - r1 := to(LowerCase, r, []CaseRange(special)) - if r1 == r { + r1, hadMapping := to(LowerCase, r, []CaseRange(special)) + if r1 == r && !hadMapping { r1 = ToLower(r) } return r1 diff --git a/libgo/go/unicode/letter_test.go b/libgo/go/unicode/letter_test.go index 3fe72ff..19ee535 100644 --- a/libgo/go/unicode/letter_test.go +++ b/libgo/go/unicode/letter_test.go @@ -9,6 +9,7 @@ import ( "fmt" "runtime" "sort" + "strings" "testing" . "unicode" ) @@ -551,3 +552,14 @@ func TestLatinOffset(t *testing.T) { } } } + +func TestSpecialCaseNoMapping(t *testing.T) { + // Issue 25636 + // no change for rune 'A', zero delta, under upper/lower/title case change. + var noChangeForCapitalA = CaseRange{'A', 'A', [MaxCase]rune{0, 0, 0}} + got := strings.ToLowerSpecial(SpecialCase([]CaseRange{noChangeForCapitalA}), "ABC") + want := "Abc" + if got != want { + t.Errorf("got %q; want %q", got, want) + } +} diff --git a/libgo/go/unicode/utf16/utf16_test.go b/libgo/go/unicode/utf16/utf16_test.go index d258f0b..4ecaabe 100644 --- a/libgo/go/unicode/utf16/utf16_test.go +++ b/libgo/go/unicode/utf16/utf16_test.go @@ -124,7 +124,7 @@ var surrogateTests = []struct { r rune want bool }{ - // from http://en.wikipedia.org/wiki/UTF-16 + // from https://en.wikipedia.org/wiki/UTF-16 {'\u007A', false}, // LATIN SMALL LETTER Z {'\u6C34', false}, // CJK UNIFIED IDEOGRAPH-6C34 (water) {'\uFEFF', false}, // Byte Order Mark diff --git a/libgo/go/unicode/utf8/utf8_test.go b/libgo/go/unicode/utf8/utf8_test.go index dc9c425..359461b 100644 --- a/libgo/go/unicode/utf8/utf8_test.go +++ b/libgo/go/unicode/utf8/utf8_test.go @@ -212,14 +212,25 @@ func TestSequencing(t *testing.T) { } } -// Check that a range loop and a []int conversion visit the same runes. +func runtimeRuneCount(s string) int { + return len([]rune(s)) // Replaced by gc with call to runtime.countrunes(s). +} + +// Check that a range loop, len([]rune(string)) optimization and +// []rune conversions visit the same runes. // Not really a test of this package, but the assumption is used here and -// it's good to verify -func TestIntConversion(t *testing.T) { +// it's good to verify. +func TestRuntimeConversion(t *testing.T) { for _, ts := range testStrings { + count := RuneCountInString(ts) + if n := runtimeRuneCount(ts); n != count { + t.Errorf("%q: len([]rune()) counted %d runes; got %d from RuneCountInString", ts, n, count) + break + } + runes := []rune(ts) - if RuneCountInString(ts) != len(runes) { - t.Errorf("%q: expected %d runes; got %d", ts, len(runes), RuneCountInString(ts)) + if n := len(runes); n != count { + t.Errorf("%q: []rune() has length %d; got %d from RuneCountInString", ts, n, count) break } i := 0 diff --git a/libgo/gotool-packages.txt b/libgo/gotool-packages.txt index 012faf6..7631923 100644 --- a/libgo/gotool-packages.txt +++ b/libgo/gotool-packages.txt @@ -4,6 +4,7 @@ cmd/go/internal/cache cmd/go/internal/cfg cmd/go/internal/clean cmd/go/internal/cmdflag +cmd/go/internal/dirhash cmd/go/internal/doc cmd/go/internal/envcmd cmd/go/internal/fix @@ -11,15 +12,32 @@ cmd/go/internal/fmtcmd cmd/go/internal/generate cmd/go/internal/get cmd/go/internal/help +cmd/go/internal/imports cmd/go/internal/list cmd/go/internal/load +cmd/go/internal/modcmd +cmd/go/internal/modconv +cmd/go/internal/modfetch +cmd/go/internal/modfetch/codehost +cmd/go/internal/modfile +cmd/go/internal/modget +cmd/go/internal/modinfo +cmd/go/internal/modload +cmd/go/internal/module +cmd/go/internal/mvs +cmd/go/internal/par cmd/go/internal/run +cmd/go/internal/search +cmd/go/internal/semver cmd/go/internal/str cmd/go/internal/test cmd/go/internal/tool +cmd/go/internal/txtar cmd/go/internal/version cmd/go/internal/vet cmd/go/internal/web +cmd/go/internal/web2 +cmd/go/internal/webtest cmd/go/internal/work cmd/internal/browser cmd/internal/buildid diff --git a/libgo/libgo-packages.txt b/libgo/libgo-packages.txt index 70c12ba..c3c16cd 100644 --- a/libgo/libgo-packages.txt +++ b/libgo/libgo-packages.txt @@ -19,7 +19,8 @@ crypto/dsa crypto/ecdsa crypto/elliptic crypto/hmac -crypto/internal/cipherhw +crypto/internal/randutil +crypto/internal/subtle crypto/md5 crypto/rand crypto/rc4 @@ -70,12 +71,16 @@ go/printer go/scanner go/token go/types +golang_org/x/crypto/internal/chacha20 golang_org/x/crypto/chacha20poly1305 golang_org/x/crypto/chacha20poly1305/internal/chacha20 golang_org/x/crypto/cryptobyte golang_org/x/crypto/cryptobyte/asn1 golang_org/x/crypto/curve25519 golang_org/x/crypto/poly1305 +golang_org/x/net/dns/dnsmessage +golang_org/x/net/http/httpguts +golang_org/x/net/http/httpproxy golang_org/x/net/http2/hpack golang_org/x/net/idna golang_org/x/net/internal/nettest @@ -103,6 +108,9 @@ image/internal/imageutil image/jpeg image/png index/suffixarray +internal/bytealg +internal/cpu +internal/goroot internal/nettrace internal/poll internal/race diff --git a/libgo/match.sh b/libgo/match.sh index fb80013..d280620 100755 --- a/libgo/match.sh +++ b/libgo/match.sh @@ -116,7 +116,7 @@ for f in $gofiles; do aix | android | darwin | dragonfly | freebsd | linux | nacl | netbsd | openbsd | plan9 | solaris | windows) tag1=nonmatchingtag ;; - 386 | amd64 | amd64p32 | arm | armbe | arm64 | arm64be | alpha | ia64 | m68k | mips | mipsle | mips64 | mips64le | mips64p32 | mips64p32le | nios2 | ppc | ppc64 | ppc64le | riscv64 | s390 | s390x | sh | shbe | sparc | sparc64) + 386 | amd64 | amd64p32 | arm | armbe | arm64 | arm64be | alpha | ia64 | m68k | mips | mipsle | mips64 | mips64le | mips64p32 | mips64p32le | nios2 | ppc | ppc64 | ppc64le | riscv64 | s390 | s390x | sh | shbe | sparc | sparc64 | wasm) tag1=nonmatchingtag ;; esac @@ -128,7 +128,7 @@ for f in $gofiles; do aix | android | darwin | dragonfly | freebsd | linux | nacl | netbsd | openbsd | plan9 | solaris | windows) tag2=nonmatchingtag ;; - 386 | amd64 | amd64p32 | arm | armbe | arm64 | arm64be | alpha | ia64 | m68k | mips | mipsle | mips64 | mips64le | mips64p32 | mips64p32le | nios2 | ppc | ppc64 | ppc64le | riscv64 | s390 | s390x | sh | shbe | sparc | sparc64) + 386 | amd64 | amd64p32 | arm | armbe | arm64 | arm64be | alpha | ia64 | m68k | mips | mipsle | mips64 | mips64le | mips64p32 | mips64p32le | nios2 | ppc | ppc64 | ppc64le | riscv64 | s390 | s390x | sh | shbe | sparc | sparc64 | wasm) tag2=nonmatchingtag ;; esac diff --git a/libgo/merge.sh b/libgo/merge.sh index 4ee57d6..9d9f57c 100755 --- a/libgo/merge.sh +++ b/libgo/merge.sh @@ -128,7 +128,7 @@ echo ${rev} > VERSION (cd ${NEWDIR}/src && find . -name '*.go' -print) | while read f; do skip=false case "$f" in - ./cmd/cgo/* | ./cmd/go/* | ./cmd/gofmt/* | ./cmd/internal/browser/* | ./cmd/internal/objabi/* | ./cmd/internal/buildid/*) + ./cmd/buildid/* | ./cmd/cgo/* | ./cmd/go/* | ./cmd/gofmt/* | ./cmd/testjson/* | ./cmd/vet/* | ./cmd/internal/browser/* | ./cmd/internal/buildid/* | ./cmd/internal/edit/* | ./cmd/internal/objabi/* | ./cmd/internal/testj2on/* ) ;; ./cmd/*) skip=true diff --git a/libgo/misc/cgo/life/main.go b/libgo/misc/cgo/life/main.go index 45376fd..145a273b 100644 --- a/libgo/misc/cgo/life/main.go +++ b/libgo/misc/cgo/life/main.go @@ -1,4 +1,4 @@ -// cmpout -tags=use_go_run +// run -tags=use_go_run // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/libgo/misc/cgo/stdio/chain.go b/libgo/misc/cgo/stdio/chain.go index 0fa813c..cdc3852 100644 --- a/libgo/misc/cgo/stdio/chain.go +++ b/libgo/misc/cgo/stdio/chain.go @@ -1,4 +1,4 @@ -// cmpout -tags=use_go_run +// run -tags=use_go_run // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/libgo/misc/cgo/stdio/fib.go b/libgo/misc/cgo/stdio/fib.go index 56e3255..58f185c 100644 --- a/libgo/misc/cgo/stdio/fib.go +++ b/libgo/misc/cgo/stdio/fib.go @@ -1,4 +1,4 @@ -// cmpout -tags=use_go_run +// run -tags=use_go_run // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/libgo/misc/cgo/stdio/hello.go b/libgo/misc/cgo/stdio/hello.go index 63bff4c..56220d3 100644 --- a/libgo/misc/cgo/stdio/hello.go +++ b/libgo/misc/cgo/stdio/hello.go @@ -1,4 +1,4 @@ -// cmpout -tags=use_go_run +// run -tags=use_go_run // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/libgo/misc/cgo/test/basic.go b/libgo/misc/cgo/test/basic.go index 3ceb4ce..2655a66 100644 --- a/libgo/misc/cgo/test/basic.go +++ b/libgo/misc/cgo/test/basic.go @@ -31,6 +31,8 @@ struct S { int x; }; +const char *cstr = "abcefghijklmnopqrstuvwxyzABCEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + extern enum E myConstFunc(struct S* const ctx, int const id, struct S **const filter); enum E myConstFunc(struct S *const ctx, int const id, struct S **const filter) { return 0; } @@ -149,6 +151,18 @@ func benchCgoCall(b *testing.B) { } } +var sinkString string + +func benchGoString(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkString = C.GoString(C.cstr) + } + const want = "abcefghijklmnopqrstuvwxyzABCEFGHIJKLMNOPQRSTUVWXYZ1234567890" + if sinkString != want { + b.Fatalf("%q != %q", sinkString, want) + } +} + // Issue 2470. func testUnsignedInt(t *testing.T) { a := (int64)(C.UINT32VAL) diff --git a/libgo/misc/cgo/test/cgo_test.go b/libgo/misc/cgo/test/cgo_test.go index cfacb9c..ccacc50 100644 --- a/libgo/misc/cgo/test/cgo_test.go +++ b/libgo/misc/cgo/test/cgo_test.go @@ -87,5 +87,11 @@ func Test6907(t *testing.T) { test6907(t) } func Test6907Go(t *testing.T) { test6907Go(t) } func Test21897(t *testing.T) { test21897(t) } func Test22906(t *testing.T) { test22906(t) } +func Test24206(t *testing.T) { test24206(t) } +func Test25143(t *testing.T) { test25143(t) } +func Test23356(t *testing.T) { test23356(t) } +func Test26066(t *testing.T) { test26066(t) } +func Test26213(t *testing.T) { test26213(t) } -func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } +func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } +func BenchmarkGoString(b *testing.B) { benchGoString(b) } diff --git a/libgo/misc/cgo/test/issue18146.go b/libgo/misc/cgo/test/issue18146.go index a58aa1b..8b7bb77 100644 --- a/libgo/misc/cgo/test/issue18146.go +++ b/libgo/misc/cgo/test/issue18146.go @@ -22,6 +22,10 @@ import ( ) func test18146(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + if runtime.GOOS == "darwin" { t.Skipf("skipping flaky test on %s; see golang.org/issue/18202", runtime.GOOS) } @@ -33,10 +37,6 @@ func test18146(t *testing.T) { attempts := 1000 threads := 4 - if testing.Short() { - attempts = 100 - } - // Restrict the number of attempts based on RLIMIT_NPROC. // Tediously, RLIMIT_NPROC was left out of the syscall package, // probably because it is not in POSIX.1, so we define it here. diff --git a/libgo/misc/cgo/test/issue21897.go b/libgo/misc/cgo/test/issue21897.go index d13246b..454a141 100644 --- a/libgo/misc/cgo/test/issue21897.go +++ b/libgo/misc/cgo/test/issue21897.go @@ -2,7 +2,16 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin,cgo,!internal +// We skip this test in race mode because, for unknown reasons, +// linking against CoreFoundation on macOS 10.10 causes mmap to ignore +// the hint address, which makes the Go allocator incompatible with +// TSAN. See golang.org/issue/26475. +// +// TODO(austin): Once support for macOS 10.10 is dropped, remove the +// race constraint (and the one in issue21897b.go). See +// golang.org/issue/26513. + +// +build darwin,cgo,!internal,!race package cgotest diff --git a/libgo/misc/cgo/test/issue21897b.go b/libgo/misc/cgo/test/issue21897b.go index 08b5f4d..e143bad 100644 --- a/libgo/misc/cgo/test/issue21897b.go +++ b/libgo/misc/cgo/test/issue21897b.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !darwin !cgo internal +// +build !darwin !cgo internal race package cgotest diff --git a/libgo/misc/cgo/test/issue23356.go b/libgo/misc/cgo/test/issue23356.go new file mode 100644 index 0000000..1c39012 --- /dev/null +++ b/libgo/misc/cgo/test/issue23356.go @@ -0,0 +1,19 @@ +// Copyright 2018 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 cgotest + +// int a(void) { return 5; }; +// int r(void) { return 3; }; +import "C" +import "testing" + +func test23356(t *testing.T) { + if got, want := C.a(), C.int(5); got != want { + t.Errorf("C.a() == %v, expected %v", got, want) + } + if got, want := C.r(), C.int(3); got != want { + t.Errorf("C.r() == %v, expected %v", got, want) + } +} diff --git a/libgo/misc/cgo/test/issue23555.go b/libgo/misc/cgo/test/issue23555.go new file mode 100644 index 0000000..5fa44e6 --- /dev/null +++ b/libgo/misc/cgo/test/issue23555.go @@ -0,0 +1,11 @@ +// Copyright 2018 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. + +// Test that we can have two identical cgo packages in a single binary. +// No runtime test; just make sure it compiles. + +package cgotest + +import _ "./issue23555a" +import _ "./issue23555b" diff --git a/libgo/misc/cgo/test/issue23555a/a.go b/libgo/misc/cgo/test/issue23555a/a.go new file mode 100644 index 0000000..cb6626b --- /dev/null +++ b/libgo/misc/cgo/test/issue23555a/a.go @@ -0,0 +1,12 @@ +// Copyright 2018 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 issue23555 + +// #include <stdlib.h> +import "C" + +func X() { + C.free(C.malloc(10)) +} diff --git a/libgo/misc/cgo/test/issue23555b/a.go b/libgo/misc/cgo/test/issue23555b/a.go new file mode 100644 index 0000000..cb6626b --- /dev/null +++ b/libgo/misc/cgo/test/issue23555b/a.go @@ -0,0 +1,12 @@ +// Copyright 2018 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 issue23555 + +// #include <stdlib.h> +import "C" + +func X() { + C.free(C.malloc(10)) +} diff --git a/libgo/misc/cgo/test/issue23720.go b/libgo/misc/cgo/test/issue23720.go new file mode 100644 index 0000000..934fff3 --- /dev/null +++ b/libgo/misc/cgo/test/issue23720.go @@ -0,0 +1,22 @@ +// Copyright 2018 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. + +// Test that we can pass compatible typedefs. +// No runtime test; just make sure it compiles. + +package cgotest + +/* +typedef int *issue23720A; + +typedef const int *issue23720B; + +void issue23720F(issue23720B a) {} +*/ +import "C" + +func Issue23720F() { + var x C.issue23720A + C.issue23720F(x) +} diff --git a/libgo/misc/cgo/test/issue24161_darwin_test.go b/libgo/misc/cgo/test/issue24161_darwin_test.go new file mode 100644 index 0000000..48072ff --- /dev/null +++ b/libgo/misc/cgo/test/issue24161_darwin_test.go @@ -0,0 +1,39 @@ +// Copyright 2018 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. + +// See issue21897.go and golang.org/issue/26475 for why this is +// skipped in race mode. +// +// TODO(austin): Once support for macOS 10.10 is dropped, remove the +// race constraint. See golang.org/issue/26513. + +// +build !race + +package cgotest + +import ( + "testing" + + "./issue24161arg" + "./issue24161e0" + "./issue24161e1" + "./issue24161e2" + "./issue24161res" +) + +func Test24161Arg(t *testing.T) { + issue24161arg.Test(t) +} +func Test24161Res(t *testing.T) { + issue24161res.Test(t) +} +func Test24161Example0(t *testing.T) { + issue24161e0.Test(t) +} +func Test24161Example1(t *testing.T) { + issue24161e1.Test(t) +} +func Test24161Example2(t *testing.T) { + issue24161e2.Test(t) +} diff --git a/libgo/misc/cgo/test/issue24161arg/def.go b/libgo/misc/cgo/test/issue24161arg/def.go new file mode 100644 index 0000000..d33479a --- /dev/null +++ b/libgo/misc/cgo/test/issue24161arg/def.go @@ -0,0 +1,17 @@ +// Copyright 2018 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 + +package issue24161arg + +/* +#cgo LDFLAGS: -framework CoreFoundation +#include <CoreFoundation/CoreFoundation.h> +*/ +import "C" + +func test24161array() C.CFArrayRef { + return C.CFArrayCreate(0, nil, 0, nil) +} diff --git a/libgo/misc/cgo/test/issue24161arg/use.go b/libgo/misc/cgo/test/issue24161arg/use.go new file mode 100644 index 0000000..3e74944 --- /dev/null +++ b/libgo/misc/cgo/test/issue24161arg/use.go @@ -0,0 +1,19 @@ +// Copyright 2018 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 + +package issue24161arg + +/* +#cgo LDFLAGS: -framework CoreFoundation +#include <CoreFoundation/CoreFoundation.h> +*/ +import "C" +import "testing" + +func Test(t *testing.T) { + a := test24161array() + C.CFArrayCreateCopy(0, a) +} diff --git a/libgo/misc/cgo/test/issue24161e0/main.go b/libgo/misc/cgo/test/issue24161e0/main.go new file mode 100644 index 0000000..cbc1dee --- /dev/null +++ b/libgo/misc/cgo/test/issue24161e0/main.go @@ -0,0 +1,29 @@ +// Copyright 2018 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 + +package issue24161e0 + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework CoreFoundation -framework Security +#include <TargetConditionals.h> +#include <CoreFoundation/CoreFoundation.h> +#include <Security/Security.h> +#if TARGET_OS_IPHONE == 0 && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101200 + typedef CFStringRef SecKeyAlgorithm; + static CFDataRef SecKeyCreateSignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef dataToSign, CFErrorRef *error){return NULL;} + #define kSecKeyAlgorithmECDSASignatureDigestX962SHA1 foo() + static SecKeyAlgorithm foo(void){return NULL;} +#endif +*/ +import "C" +import "testing" + +func f1() { + C.SecKeyCreateSignature(0, C.kSecKeyAlgorithmECDSASignatureDigestX962SHA1, 0, nil) +} + +func Test(t *testing.T) {} diff --git a/libgo/misc/cgo/test/issue24161e1/main.go b/libgo/misc/cgo/test/issue24161e1/main.go new file mode 100644 index 0000000..eb48fc0 --- /dev/null +++ b/libgo/misc/cgo/test/issue24161e1/main.go @@ -0,0 +1,38 @@ +// Copyright 2018 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 + +package issue24161e1 + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework CoreFoundation -framework Security +#include <TargetConditionals.h> +#include <CoreFoundation/CoreFoundation.h> +#include <Security/Security.h> +#if TARGET_OS_IPHONE == 0 && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101200 + typedef CFStringRef SecKeyAlgorithm; + static CFDataRef SecKeyCreateSignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef dataToSign, CFErrorRef *error){return NULL;} + #define kSecKeyAlgorithmECDSASignatureDigestX962SHA1 foo() + static SecKeyAlgorithm foo(void){return NULL;} +#endif +*/ +import "C" +import ( + "fmt" + "testing" +) + +func f1() { + C.SecKeyCreateSignature(0, C.kSecKeyAlgorithmECDSASignatureDigestX962SHA1, 0, nil) +} + +func f2(e C.CFErrorRef) { + if desc := C.CFErrorCopyDescription(e); desc != 0 { + fmt.Println(desc) + } +} + +func Test(t *testing.T) {} diff --git a/libgo/misc/cgo/test/issue24161e2/main.go b/libgo/misc/cgo/test/issue24161e2/main.go new file mode 100644 index 0000000..1951c86 --- /dev/null +++ b/libgo/misc/cgo/test/issue24161e2/main.go @@ -0,0 +1,40 @@ +// Copyright 2018 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 + +package issue24161e2 + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework CoreFoundation -framework Security +#include <TargetConditionals.h> +#include <CoreFoundation/CoreFoundation.h> +#include <Security/Security.h> +#if TARGET_OS_IPHONE == 0 && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101200 + typedef CFStringRef SecKeyAlgorithm; + static CFDataRef SecKeyCreateSignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef dataToSign, CFErrorRef *error){return NULL;} + #define kSecKeyAlgorithmECDSASignatureDigestX962SHA1 foo() + static SecKeyAlgorithm foo(void){return NULL;} +#endif +*/ +import "C" +import ( + "fmt" + "testing" +) + +var _ C.CFStringRef + +func f1() { + C.SecKeyCreateSignature(0, C.kSecKeyAlgorithmECDSASignatureDigestX962SHA1, 0, nil) +} + +func f2(e C.CFErrorRef) { + if desc := C.CFErrorCopyDescription(e); desc != 0 { + fmt.Println(desc) + } +} + +func Test(t *testing.T) {} diff --git a/libgo/misc/cgo/test/issue24161res/restype.go b/libgo/misc/cgo/test/issue24161res/restype.go new file mode 100644 index 0000000..e5719f2 --- /dev/null +++ b/libgo/misc/cgo/test/issue24161res/restype.go @@ -0,0 +1,23 @@ +// Copyright 2018 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 + +package issue24161res + +/* +#cgo LDFLAGS: -framework CoreFoundation +#include <CoreFoundation/CoreFoundation.h> +*/ +import "C" +import ( + "reflect" + "testing" +) + +func Test(t *testing.T) { + if k := reflect.TypeOf(C.CFArrayCreate(0, nil, 0, nil)).Kind(); k != reflect.Uintptr { + t.Fatalf("bad kind %s\n", k) + } +} diff --git a/libgo/misc/cgo/test/issue24206.go b/libgo/misc/cgo/test/issue24206.go new file mode 100644 index 0000000..5fec68e --- /dev/null +++ b/libgo/misc/cgo/test/issue24206.go @@ -0,0 +1,54 @@ +// +build amd64,linux + +// Copyright 2018 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 cgotest + +// Test that C.GoString uses IndexByte in safe manner. + +/* +#include <sys/mman.h> + +// Returns string with null byte at the last valid address +char* dangerousString1() { + int pageSize = 4096; + char *data = mmap(0, 2 * pageSize, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, 0, 0); + mprotect(data + pageSize,pageSize,PROT_NONE); + int start = pageSize - 123 - 1; // last 123 bytes of first page + 1 null byte + int i = start; + for (; i < pageSize; i++) { + data[i] = 'x'; + } + data[pageSize -1 ] = 0; + return data+start; +} + +char* dangerousString2() { + int pageSize = 4096; + char *data = mmap(0, 3 * pageSize, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, 0, 0); + mprotect(data + 2 * pageSize,pageSize,PROT_NONE); + int start = pageSize - 123 - 1; // last 123 bytes of first page + 1 null byte + int i = start; + for (; i < 2 * pageSize; i++) { + data[i] = 'x'; + } + data[2*pageSize -1 ] = 0; + return data+start; +} +*/ +import "C" + +import ( + "testing" +) + +func test24206(t *testing.T) { + if l := len(C.GoString(C.dangerousString1())); l != 123 { + t.Errorf("Incorrect string length - got %d, want 123", l) + } + if l := len(C.GoString(C.dangerousString2())); l != 4096+123 { + t.Errorf("Incorrect string length - got %d, want %d", l, 4096+123) + } +} diff --git a/libgo/misc/cgo/test/issue24206_generic.go b/libgo/misc/cgo/test/issue24206_generic.go new file mode 100644 index 0000000..27c4d65 --- /dev/null +++ b/libgo/misc/cgo/test/issue24206_generic.go @@ -0,0 +1,13 @@ +// +build !amd64 !linux + +// Copyright 2018 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 cgotest + +import "testing" + +func test24206(t *testing.T) { + t.Skip("Skipping on non-amd64 or non-linux system") +} diff --git a/libgo/misc/cgo/test/issue25143.go b/libgo/misc/cgo/test/issue25143.go new file mode 100644 index 0000000..607bfe4 --- /dev/null +++ b/libgo/misc/cgo/test/issue25143.go @@ -0,0 +1,22 @@ +// Copyright 2018 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 cgotest + +import "C" +import "testing" + +func issue25143sum(ns ...C.int) C.int { + total := C.int(0) + for _, n := range ns { + total += n + } + return total +} + +func test25143(t *testing.T) { + if got, want := issue25143sum(1, 2, 3), C.int(6); got != want { + t.Errorf("issue25143sum(1, 2, 3) == %v, expected %v", got, want) + } +} diff --git a/libgo/misc/cgo/test/issue26066.go b/libgo/misc/cgo/test/issue26066.go new file mode 100644 index 0000000..21028e7 --- /dev/null +++ b/libgo/misc/cgo/test/issue26066.go @@ -0,0 +1,19 @@ +// Copyright 2018 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. + +// Wrong type of constant with GCC 8 and newer. + +package cgotest + +// const unsigned long long int issue26066 = (const unsigned long long) -1; +import "C" + +import "testing" + +func test26066(t *testing.T) { + var i = int64(C.issue26066) + if i != -1 { + t.Errorf("got %d, want -1", i) + } +} diff --git a/libgo/misc/cgo/test/issue26213/jni.h b/libgo/misc/cgo/test/issue26213/jni.h new file mode 100644 index 0000000..0c76979 --- /dev/null +++ b/libgo/misc/cgo/test/issue26213/jni.h @@ -0,0 +1,29 @@ +// Copyright 2018 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. + +// It's going to be hard to include a whole real JVM to test this. +// So we'll simulate a really easy JVM using just the parts we need. + +// This is the relevant part of jni.h. + +// On Android NDK16, jobject is defined like this in C and C++ +typedef void* jobject; + +typedef jobject jclass; +typedef jobject jthrowable; +typedef jobject jstring; +typedef jobject jarray; +typedef jarray jbooleanArray; +typedef jarray jbyteArray; +typedef jarray jcharArray; +typedef jarray jshortArray; +typedef jarray jintArray; +typedef jarray jlongArray; +typedef jarray jfloatArray; +typedef jarray jdoubleArray; +typedef jarray jobjectArray; + +typedef jobject jweak; + +// Note: jvalue is already a non-pointer type due to it being a C union. diff --git a/libgo/misc/cgo/test/issue26213/test26213.go b/libgo/misc/cgo/test/issue26213/test26213.go new file mode 100644 index 0000000..5d1f637 --- /dev/null +++ b/libgo/misc/cgo/test/issue26213/test26213.go @@ -0,0 +1,46 @@ +// Copyright 2018 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 issue26213 + +/* +#include "jni.h" +*/ +import "C" +import ( + "testing" +) + +func Test26213(t *testing.T) { + var x1 C.jobject = 0 // Note: 0, not nil. That makes sure we use uintptr for these types. + _ = x1 + var x2 C.jclass = 0 + _ = x2 + var x3 C.jthrowable = 0 + _ = x3 + var x4 C.jstring = 0 + _ = x4 + var x5 C.jarray = 0 + _ = x5 + var x6 C.jbooleanArray = 0 + _ = x6 + var x7 C.jbyteArray = 0 + _ = x7 + var x8 C.jcharArray = 0 + _ = x8 + var x9 C.jshortArray = 0 + _ = x9 + var x10 C.jintArray = 0 + _ = x10 + var x11 C.jlongArray = 0 + _ = x11 + var x12 C.jfloatArray = 0 + _ = x12 + var x13 C.jdoubleArray = 0 + _ = x13 + var x14 C.jobjectArray = 0 + _ = x14 + var x15 C.jweak = 0 + _ = x15 +} diff --git a/libgo/misc/cgo/test/issue26430.go b/libgo/misc/cgo/test/issue26430.go new file mode 100644 index 0000000..3ad5420 --- /dev/null +++ b/libgo/misc/cgo/test/issue26430.go @@ -0,0 +1,10 @@ +// Copyright 2018 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. + +// Issue 26430: incomplete typedef leads to inconsistent typedefs error. +// No runtime test; just make sure it compiles. + +package cgotest + +import _ "./issue26430" diff --git a/libgo/misc/cgo/test/issue26430/a.go b/libgo/misc/cgo/test/issue26430/a.go new file mode 100644 index 0000000..fbaa46b --- /dev/null +++ b/libgo/misc/cgo/test/issue26430/a.go @@ -0,0 +1,13 @@ +// Copyright 2018 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 a + +// typedef struct S ST; +// static ST* F() { return 0; } +import "C" + +func F1() { + C.F() +} diff --git a/libgo/misc/cgo/test/issue26430/b.go b/libgo/misc/cgo/test/issue26430/b.go new file mode 100644 index 0000000..a7c527c --- /dev/null +++ b/libgo/misc/cgo/test/issue26430/b.go @@ -0,0 +1,13 @@ +// Copyright 2018 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 a + +// typedef struct S ST; +// struct S { int f; }; +import "C" + +func F2(p *C.ST) { + p.f = 1 +} diff --git a/libgo/misc/cgo/test/issue26517.go b/libgo/misc/cgo/test/issue26517.go new file mode 100644 index 0000000..c1bf1c9 --- /dev/null +++ b/libgo/misc/cgo/test/issue26517.go @@ -0,0 +1,23 @@ +// Copyright 2018 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 cgotest + +// Introduce two pointer types which are distinct, but have the same +// base type. Make sure that both of those pointer types get resolved +// correctly. Before the fix for 26517 if one of these pointer types +// was resolved before the other one was processed, the second one +// would never be resolved. +// Before this issue was fixed this test failed on Windows, +// where va_list expands to a named char* type. + +/* +#include <stdarg.h> +typedef va_list TypeOne; +typedef char *TypeTwo; +*/ +import "C" + +var a C.TypeOne +var b C.TypeTwo diff --git a/libgo/misc/cgo/test/issue26743.go b/libgo/misc/cgo/test/issue26743.go new file mode 100644 index 0000000..35c8473 --- /dev/null +++ b/libgo/misc/cgo/test/issue26743.go @@ -0,0 +1,10 @@ +// Copyright 2018 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. + +// Issue 26743: typedef of uint leads to inconsistent typedefs error. +// No runtime test; just make sure it compiles. + +package cgotest + +import _ "./issue26743" diff --git a/libgo/misc/cgo/test/issue26743/a.go b/libgo/misc/cgo/test/issue26743/a.go new file mode 100644 index 0000000..a3df179 --- /dev/null +++ b/libgo/misc/cgo/test/issue26743/a.go @@ -0,0 +1,11 @@ +// Copyright 2018 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 issue26743 + +// typedef unsigned int uint; +// int C1(uint x) { return x; } +import "C" + +var V1 = C.C1(0) diff --git a/libgo/misc/cgo/test/issue26743/b.go b/libgo/misc/cgo/test/issue26743/b.go new file mode 100644 index 0000000..c5f1ae4 --- /dev/null +++ b/libgo/misc/cgo/test/issue26743/b.go @@ -0,0 +1,9 @@ +// Copyright 2018 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 issue26743 + +import "C" + +var V2 C.uint diff --git a/libgo/misc/cgo/test/issue4029.c b/libgo/misc/cgo/test/issue4029.c index 7205c5a..30646ad 100644 --- a/libgo/misc/cgo/test/issue4029.c +++ b/libgo/misc/cgo/test/issue4029.c @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !windows +// +build !windows,!static #include <stdint.h> #include <dlfcn.h> diff --git a/libgo/misc/cgo/test/issue4029.go b/libgo/misc/cgo/test/issue4029.go index 8e468d3..1bf029d 100644 --- a/libgo/misc/cgo/test/issue4029.go +++ b/libgo/misc/cgo/test/issue4029.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !windows +// +build !windows,!static package cgotest diff --git a/libgo/misc/cgo/test/issue4029w.go b/libgo/misc/cgo/test/issue4029w.go index 18c7201..eee33f7 100644 --- a/libgo/misc/cgo/test/issue4029w.go +++ b/libgo/misc/cgo/test/issue4029w.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build windows +// +build windows static package cgotest diff --git a/libgo/misc/cgo/test/issue9400/asm_arm.s b/libgo/misc/cgo/test/issue9400/asm_arm.s index 166d53f..96c2785 100644 --- a/libgo/misc/cgo/test/issue9400/asm_arm.s +++ b/libgo/misc/cgo/test/issue9400/asm_arm.s @@ -9,7 +9,7 @@ TEXT cas<>(SB),NOSPLIT,$0 MOVW $0xffff0fc0, R15 // R15 is PC -TEXT ·RewindAndSetgid(SB),NOSPLIT,$-4-0 +TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0 // Save link register MOVW R14, R4 diff --git a/libgo/misc/cgo/test/issue9400/asm_arm64.s b/libgo/misc/cgo/test/issue9400/asm_arm64.s index 9bb5081..2ebbfcc 100644 --- a/libgo/misc/cgo/test/issue9400/asm_arm64.s +++ b/libgo/misc/cgo/test/issue9400/asm_arm64.s @@ -6,7 +6,7 @@ #include "textflag.h" -TEXT ·RewindAndSetgid(SB),NOSPLIT,$-8-0 +TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0 // Save link register MOVD R30, R9 diff --git a/libgo/misc/cgo/test/issue9400/asm_mipsx.s b/libgo/misc/cgo/test/issue9400/asm_mipsx.s index ddf33e9..7a92735 100644 --- a/libgo/misc/cgo/test/issue9400/asm_mipsx.s +++ b/libgo/misc/cgo/test/issue9400/asm_mipsx.s @@ -7,7 +7,7 @@ #include "textflag.h" -TEXT ·RewindAndSetgid(SB),NOSPLIT,$-4-0 +TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0 // Rewind stack pointer so anything that happens on the stack // will clobber the test pattern created by the caller ADDU $(1024*8), R29 diff --git a/libgo/misc/cgo/test/issue9400/stubs.go b/libgo/misc/cgo/test/issue9400/stubs.go index 7b50cef..e431c5a 100644 --- a/libgo/misc/cgo/test/issue9400/stubs.go +++ b/libgo/misc/cgo/test/issue9400/stubs.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !gccgo +// +build gc package issue9400 diff --git a/libgo/misc/cgo/test/sigprocmask.c b/libgo/misc/cgo/test/sigprocmask.c index bd99647..e77ba5b 100644 --- a/libgo/misc/cgo/test/sigprocmask.c +++ b/libgo/misc/cgo/test/sigprocmask.c @@ -4,10 +4,12 @@ // +build !windows +#include <errno.h> #include <signal.h> #include <stdlib.h> #include <pthread.h> #include <stdio.h> +#include <time.h> #include <unistd.h> extern void IntoGoAndBack(); @@ -28,11 +30,22 @@ static void* sigthreadfunc(void* unused) { } int RunSigThread() { + int tries; pthread_t thread; int r; + struct timespec ts; - r = pthread_create(&thread, NULL, &sigthreadfunc, NULL); - if (r != 0) - return r; - return pthread_join(thread, NULL); + for (tries = 0; tries < 20; tries++) { + r = pthread_create(&thread, NULL, &sigthreadfunc, NULL); + if (r == 0) { + return pthread_join(thread, NULL); + } + if (r != EAGAIN) { + return r; + } + ts.tv_sec = 0; + ts.tv_nsec = (tries + 1) * 1000 * 1000; // Milliseconds. + nanosleep(&ts, NULL); + } + return EAGAIN; } diff --git a/libgo/misc/cgo/test/sigprocmask.go b/libgo/misc/cgo/test/sigprocmask.go index 39b658e..e2b939f 100644 --- a/libgo/misc/cgo/test/sigprocmask.go +++ b/libgo/misc/cgo/test/sigprocmask.go @@ -32,7 +32,7 @@ func IntoGoAndBack() { func testSigprocmask(t *testing.T) { if r := C.RunSigThread(); r != 0 { - t.Error("pthread_create/pthread_join failed") + t.Errorf("pthread_create/pthread_join failed: %d", r) } if !blocked { t.Error("Go runtime unblocked SIGIO") diff --git a/libgo/misc/cgo/test/test26213.go b/libgo/misc/cgo/test/test26213.go new file mode 100644 index 0000000..176a7ec --- /dev/null +++ b/libgo/misc/cgo/test/test26213.go @@ -0,0 +1,15 @@ +// Copyright 2018 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 cgotest + +import ( + "testing" + + "./issue26213" +) + +func test26213(t *testing.T) { + issue26213.Test26213(t) +} diff --git a/libgo/misc/cgo/testcarchive/carchive_test.go b/libgo/misc/cgo/testcarchive/carchive_test.go index 4a8cc0f..457ac0d 100644 --- a/libgo/misc/cgo/testcarchive/carchive_test.go +++ b/libgo/misc/cgo/testcarchive/carchive_test.go @@ -13,6 +13,7 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "runtime" "strings" "syscall" @@ -178,6 +179,28 @@ func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) { t.Logf("%s", out) t.Fatal(err) } + + checkLineComments(t, libgoh) +} + +var badLineRegexp = regexp.MustCompile(`(?m)^#line [0-9]+ "/.*$`) + +// checkLineComments checks that the export header generated by +// -buildmode=c-archive doesn't have any absolute paths in the #line +// comments. We don't want those paths because they are unhelpful for +// the user and make the files change based on details of the location +// of GOPATH. +func checkLineComments(t *testing.T, hdrname string) { + hdr, err := ioutil.ReadFile(hdrname) + if err != nil { + if !os.IsNotExist(err) { + t.Error(err) + } + return + } + if line := badLineRegexp.Find(hdr); line != nil { + t.Errorf("bad #line directive with absolute path in %s: %q", hdrname, line) + } } func TestInstall(t *testing.T) { @@ -226,6 +249,7 @@ func TestEarlySignalHandler(t *testing.T) { t.Logf("%s", out) t.Fatal(err) } + checkLineComments(t, "libgo2.h") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a") if runtime.Compiler == "gccgo" { @@ -258,6 +282,7 @@ func TestSignalForwarding(t *testing.T) { t.Logf("%s", out) t.Fatal(err) } + checkLineComments(t, "libgo2.h") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") if runtime.Compiler == "gccgo" { @@ -283,6 +308,9 @@ func TestSignalForwarding(t *testing.T) { } func TestSignalForwardingExternal(t *testing.T) { + if GOOS == "freebsd" { + t.Skipf("skipping on %s/%s; signal always goes to the Go runtime", GOOS, GOARCH) + } checkSignalForwardingTest(t) defer func() { @@ -298,6 +326,7 @@ func TestSignalForwardingExternal(t *testing.T) { t.Logf("%s", out) t.Fatal(err) } + checkLineComments(t, "libgo2.h") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") if runtime.Compiler == "gccgo" { @@ -413,6 +442,7 @@ func TestOsSignal(t *testing.T) { t.Logf("%s", out) t.Fatal(err) } + checkLineComments(t, "libgo3.h") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a") if runtime.Compiler == "gccgo" { @@ -448,6 +478,7 @@ func TestSigaltstack(t *testing.T) { t.Logf("%s", out) t.Fatal(err) } + checkLineComments(t, "libgo4.h") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a") if runtime.Compiler == "gccgo" { @@ -465,7 +496,7 @@ func TestSigaltstack(t *testing.T) { } const testar = `#!/usr/bin/env bash -while expr $1 : '[-]' >/dev/null; do +while [[ $1 == -* ]] >/dev/null; do shift done echo "testar" > $1 @@ -505,6 +536,7 @@ func TestExtar(t *testing.T) { t.Logf("%s", out) t.Fatal(err) } + checkLineComments(t, "libgo4.h") if _, err := os.Stat("testar.ran"); err != nil { if os.IsNotExist(err) { @@ -617,6 +649,7 @@ func TestSIGPROF(t *testing.T) { t.Logf("%s", out) t.Fatal(err) } + checkLineComments(t, "libgo6.h") ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a") if runtime.Compiler == "gccgo" { @@ -658,6 +691,7 @@ func TestCompileWithoutShared(t *testing.T) { if err != nil { t.Fatal(err) } + checkLineComments(t, "libgo2.h") exe := "./testnoshared" + exeSuffix diff --git a/libgo/misc/cgo/testcarchive/main5.c b/libgo/misc/cgo/testcarchive/main5.c index 2437bf0..897b70d 100644 --- a/libgo/misc/cgo/testcarchive/main5.c +++ b/libgo/misc/cgo/testcarchive/main5.c @@ -85,6 +85,8 @@ int main(int argc, char** argv) { printf("write(2) unexpectedly succeeded\n"); return 0; } + printf("did not receieve SIGPIPE\n"); + return 0; } default: printf("Unknown test: %d\n", test); diff --git a/libgo/misc/cgo/testcarchive/src/libgo3/libgo3.go b/libgo/misc/cgo/testcarchive/src/libgo3/libgo3.go index e276a3c..3725f7a 100644 --- a/libgo/misc/cgo/testcarchive/src/libgo3/libgo3.go +++ b/libgo/misc/cgo/testcarchive/src/libgo3/libgo3.go @@ -29,13 +29,13 @@ func ResetSIGIO() { signal.Reset(syscall.SIGIO) } -// SawSIGIO returns whether we saw a SIGIO within a brief pause. +// SawSIGIO reports whether we saw a SIGIO. //export SawSIGIO func SawSIGIO() C.int { select { case <-sigioChan: return 1 - case <-time.After(100 * time.Millisecond): + case <-time.After(5 * time.Second): return 0 } } diff --git a/libgo/misc/cgo/testcshared/cshared_test.go b/libgo/misc/cgo/testcshared/cshared_test.go index e43422d..89b19d6 100644 --- a/libgo/misc/cgo/testcshared/cshared_test.go +++ b/libgo/misc/cgo/testcshared/cshared_test.go @@ -201,6 +201,16 @@ func run(t *testing.T, env []string, args ...string) string { t.Helper() cmd := exec.Command(args[0], args[1:]...) cmd.Env = env + + if GOOS != "windows" { + // TestUnexportedSymbols relies on file descriptor 30 + // being closed when the program starts, so enforce + // that in all cases. (The first three descriptors are + // stdin/stdout/stderr, so we just need to make sure + // that cmd.ExtraFiles[27] exists and is nil.) + cmd.ExtraFiles = make([]*os.File, 28) + } + out, err := cmd.CombinedOutput() if err != nil { t.Fatalf("command failed: %v\n%v\n%s\n", args, err, out) @@ -322,7 +332,11 @@ func TestExportedSymbolsWithDynamicLoad(t *testing.T) { createHeadersOnce(t) - runCC(t, "-o", cmd, "main1.c", "-ldl") + if GOOS != "freebsd" { + runCC(t, "-o", cmd, "main1.c", "-ldl") + } else { + runCC(t, "-o", cmd, "main1.c") + } adbPush(t, cmd) defer os.Remove(bin) @@ -411,7 +425,11 @@ func testSignalHandlers(t *testing.T, pkgname, cfile, cmd string) { "-o", libname, pkgname, ) adbPush(t, libname) - runCC(t, "-pthread", "-o", cmd, cfile, "-ldl") + if GOOS != "freebsd" { + runCC(t, "-pthread", "-o", cmd, cfile, "-ldl") + } else { + runCC(t, "-pthread", "-o", cmd, cfile) + } adbPush(t, cmd) bin := cmdToRun(cmd) diff --git a/libgo/misc/cgo/testcshared/main2.c b/libgo/misc/cgo/testcshared/main2.c index 1a3f1d7..f89bcca 100644 --- a/libgo/misc/cgo/testcshared/main2.c +++ b/libgo/misc/cgo/testcshared/main2.c @@ -21,7 +21,7 @@ int main(void) { // The descriptor will be initialized in a thread, so we have to // give a chance to get opened. - for (i = 0; i < 1000; i++) { + for (i = 0; i < 200; i++) { n = read(fd, buf, sizeof buf); if (n >= 0) break; @@ -33,7 +33,7 @@ int main(void) { // An EBADF error means that the shared library has not opened the // descriptor yet. ts.tv_sec = 0; - ts.tv_nsec = 1000000; + ts.tv_nsec = 10000000; nanosleep(&ts, NULL); } diff --git a/libgo/misc/cgo/testgodefs/fieldtypedef.go b/libgo/misc/cgo/testgodefs/fieldtypedef.go new file mode 100644 index 0000000..45c0bf8 --- /dev/null +++ b/libgo/misc/cgo/testgodefs/fieldtypedef.go @@ -0,0 +1,18 @@ +// Copyright 2018 The Go Authors. All rights reserve d. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// +build ignore + +package main + +/* +struct S1 { int f1; }; +struct S2 { struct S1 s1; }; +typedef struct S1 S1Type; +typedef struct S2 S2Type; +*/ +import "C" + +type S1 C.S1Type +type S2 C.S2Type diff --git a/libgo/misc/cgo/testgodefs/test.bash b/libgo/misc/cgo/testgodefs/test.bash index a82ff93..012d007 100644 --- a/libgo/misc/cgo/testgodefs/test.bash +++ b/libgo/misc/cgo/testgodefs/test.bash @@ -7,7 +7,7 @@ # We are testing cgo -godefs, which translates Go files that use # import "C" into Go files with Go definitions of types defined in the # import "C" block. Add more tests here. -FILE_PREFIXES="anonunion issue8478" +FILE_PREFIXES="anonunion issue8478 fieldtypedef" RM= for FP in $FILE_PREFIXES diff --git a/libgo/misc/cgo/testplugin/src/issue25756/main.go b/libgo/misc/cgo/testplugin/src/issue25756/main.go new file mode 100644 index 0000000..817daf4 --- /dev/null +++ b/libgo/misc/cgo/testplugin/src/issue25756/main.go @@ -0,0 +1,52 @@ +// Copyright 2018 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. + +// Run the game of life in C using Go for parallelization. + +package main + +import ( + "flag" + "fmt" + "plugin" +) + +const MAXDIM = 100 + +var dim = flag.Int("dim", 16, "board dimensions") +var gen = flag.Int("gen", 10, "generations") + +func main() { + flag.Parse() + + var a [MAXDIM * MAXDIM]int32 + for i := 2; i < *dim; i += 8 { + for j := 2; j < *dim-3; j += 8 { + for y := 0; y < 3; y++ { + a[i**dim+j+y] = 1 + } + } + } + + p, err := plugin.Open("life.so") + if err != nil { + panic(err) + } + f, err := p.Lookup("Run") + if err != nil { + panic(err) + } + f.(func(int, int, int, []int32))(*gen, *dim, *dim, a[:]) + + for i := 0; i < *dim; i++ { + for j := 0; j < *dim; j++ { + if a[i**dim+j] == 0 { + fmt.Print(" ") + } else { + fmt.Print("X") + } + } + fmt.Print("\n") + } +} diff --git a/libgo/misc/cgo/testplugin/src/issue25756/plugin/c-life.c b/libgo/misc/cgo/testplugin/src/issue25756/plugin/c-life.c new file mode 100644 index 0000000..f853163 --- /dev/null +++ b/libgo/misc/cgo/testplugin/src/issue25756/plugin/c-life.c @@ -0,0 +1,56 @@ +// 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. + +#include <assert.h> +#include "life.h" +#include "_cgo_export.h" + +const int MYCONST = 0; + +// Do the actual manipulation of the life board in C. This could be +// done easily in Go, we are just using C for demonstration +// purposes. +void +Step(int x, int y, int *a, int *n) +{ + struct GoStart_return r; + + // Use Go to start 4 goroutines each of which handles 1/4 of the + // board. + r = GoStart(0, x, y, 0, x / 2, 0, y / 2, a, n); + assert(r.r0 == 0 && r.r1 == 100); // test multiple returns + r = GoStart(1, x, y, x / 2, x, 0, y / 2, a, n); + assert(r.r0 == 1 && r.r1 == 101); // test multiple returns + GoStart(2, x, y, 0, x / 2, y / 2, y, a, n); + GoStart(3, x, y, x / 2, x, y / 2, y, a, n); + GoWait(0); + GoWait(1); + GoWait(2); + GoWait(3); +} + +// The actual computation. This is called in parallel. +void +DoStep(int xdim, int ydim, int xstart, int xend, int ystart, int yend, int *a, int *n) +{ + int x, y, c, i, j; + + for(x = xstart; x < xend; x++) { + for(y = ystart; y < yend; y++) { + c = 0; + for(i = -1; i <= 1; i++) { + for(j = -1; j <= 1; j++) { + if(x+i >= 0 && x+i < xdim && + y+j >= 0 && y+j < ydim && + (i != 0 || j != 0)) + c += a[(x+i)*xdim + (y+j)] != 0; + } + } + if(c == 3 || (c == 2 && a[x*xdim + y] != 0)) + n[x*xdim + y] = 1; + else + n[x*xdim + y] = 0; + } + } +} diff --git a/libgo/misc/cgo/testplugin/src/issue25756/plugin/life.go b/libgo/misc/cgo/testplugin/src/issue25756/plugin/life.go new file mode 100644 index 0000000..675a192 --- /dev/null +++ b/libgo/misc/cgo/testplugin/src/issue25756/plugin/life.go @@ -0,0 +1,39 @@ +// 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 main + +// #include "life.h" +import "C" + +import "unsafe" + +func Run(gen, x, y int, a []int32) { + n := make([]int32, x*y) + for i := 0; i < gen; i++ { + C.Step(C.int(x), C.int(y), (*C.int)(unsafe.Pointer(&a[0])), (*C.int)(unsafe.Pointer(&n[0]))) + copy(a, n) + } +} + +// Keep the channels visible from Go. +var chans [4]chan bool + +//export GoStart +// Double return value is just for testing. +func GoStart(i, xdim, ydim, xstart, xend, ystart, yend C.int, a *C.int, n *C.int) (int, int) { + c := make(chan bool, int(C.MYCONST)) + go func() { + C.DoStep(xdim, ydim, xstart, xend, ystart, yend, a, n) + c <- true + }() + chans[i] = c + return int(i), int(i + 100) +} + +//export GoWait +func GoWait(i C.int) { + <-chans[i] + chans[i] = nil +} diff --git a/libgo/misc/cgo/testplugin/src/issue25756/plugin/life.h b/libgo/misc/cgo/testplugin/src/issue25756/plugin/life.h new file mode 100644 index 0000000..11d2b97 --- /dev/null +++ b/libgo/misc/cgo/testplugin/src/issue25756/plugin/life.h @@ -0,0 +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. + +extern void Step(int, int, int *, int *); +extern void DoStep(int, int, int, int, int, int, int *, int *); +extern const int MYCONST; diff --git a/libgo/misc/cgo/testplugin/test.bash b/libgo/misc/cgo/testplugin/test.bash index df38204..bf8ed3c 100644 --- a/libgo/misc/cgo/testplugin/test.bash +++ b/libgo/misc/cgo/testplugin/test.bash @@ -15,7 +15,7 @@ goos=$(go env GOOS) goarch=$(go env GOARCH) function cleanup() { - rm -f plugin*.so unnamed*.so iface*.so issue* + rm -f plugin*.so unnamed*.so iface*.so life.so issue* rm -rf host pkg sub iface } trap cleanup EXIT @@ -90,3 +90,12 @@ GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue22295 src/issue22295.pkg/m GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o issue24351.so src/issue24351/plugin.go GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue24351 src/issue24351/main.go ./issue24351 + +# Test for issue 25756 +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o life.so issue25756/plugin +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue25756 src/issue25756/main.go +# Fails intermittently, but 20 runs should cause the failure +for i in `seq 1 20`; +do + ./issue25756 > /dev/null +done diff --git a/libgo/misc/cgo/testsanitizers/cc_test.go b/libgo/misc/cgo/testsanitizers/cc_test.go index 306844b..f09ad52 100644 --- a/libgo/misc/cgo/testsanitizers/cc_test.go +++ b/libgo/misc/cgo/testsanitizers/cc_test.go @@ -381,12 +381,13 @@ func (c *config) checkRuntime() (skip bool, err error) { return false, err } cmd.Args = append(cmd.Args, "-dM", "-E", "../../../src/runtime/cgo/libcgo.h") + cmdStr := strings.Join(cmd.Args, " ") out, err := cmd.CombinedOutput() if err != nil { - return false, fmt.Errorf("%#q exited with %v\n%s", strings.Join(cmd.Args, " "), err, out) + return false, fmt.Errorf("%#q exited with %v\n%s", cmdStr, err, out) } if !bytes.Contains(out, []byte("#define CGO_TSAN")) { - return true, fmt.Errorf("%#q did not define CGO_TSAN") + return true, fmt.Errorf("%#q did not define CGO_TSAN", cmdStr) } return false, nil } diff --git a/libgo/misc/cgo/testsanitizers/msan_test.go b/libgo/misc/cgo/testsanitizers/msan_test.go index af5afa9..88b90d3 100644 --- a/libgo/misc/cgo/testsanitizers/msan_test.go +++ b/libgo/misc/cgo/testsanitizers/msan_test.go @@ -27,6 +27,7 @@ func TestMSAN(t *testing.T) { {src: "msan3.go"}, {src: "msan4.go"}, {src: "msan5.go"}, + {src: "msan6.go"}, {src: "msan_fail.go", wantErr: true}, } for _, tc := range cases { diff --git a/libgo/misc/cgo/testsanitizers/src/msan6.go b/libgo/misc/cgo/testsanitizers/src/msan6.go new file mode 100644 index 0000000..003989c --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/src/msan6.go @@ -0,0 +1,72 @@ +// Copyright 2018 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 main + +// A C function returning a value on the Go stack could leave the Go +// stack marked as uninitialized, potentially causing a later error +// when the stack is used for something else. Issue 26209. + +/* +#cgo LDFLAGS: -fsanitize=memory +#cgo CPPFLAGS: -fsanitize=memory + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +typedef struct { + uintptr_t a[20]; +} S; + +S f() { + S *p; + + p = (S *)(malloc(sizeof(S))); + p->a[0] = 0; + return *p; +} +*/ +import "C" + +// allocateStack extends the stack so that stack copying doesn't +// confuse the msan data structures. +//go:noinline +func allocateStack(i int) int { + if i == 0 { + return i + } + return allocateStack(i - 1) +} + +// F1 marks a chunk of stack as uninitialized. +// C.f returns an uninitialized struct on the stack, so msan will mark +// the stack as uninitialized. +//go:noinline +func F1() uintptr { + s := C.f() + return uintptr(s.a[0]) +} + +// F2 allocates a struct on the stack and converts it to an empty interface, +// which will call msanread and see that the data appears uninitialized. +//go:noinline +func F2() interface{} { + return C.S{} +} + +func poisonStack(i int) int { + if i == 0 { + return int(F1()) + } + F1() + r := poisonStack(i - 1) + F2() + return r +} + +func main() { + allocateStack(16384) + poisonStack(128) +} diff --git a/libgo/misc/cgo/testsanitizers/tsan_test.go b/libgo/misc/cgo/testsanitizers/tsan_test.go index ec4e003..1d769a9 100644 --- a/libgo/misc/cgo/testsanitizers/tsan_test.go +++ b/libgo/misc/cgo/testsanitizers/tsan_test.go @@ -5,11 +5,15 @@ package sanitizers_test import ( + "runtime" "strings" "testing" ) func TestTSAN(t *testing.T) { + if runtime.GOARCH == "arm64" { + t.Skip("skipping test; see https://golang.org/issue/25682") + } t.Parallel() requireOvercommit(t) config := configure("thread") diff --git a/libgo/misc/cgo/testshared/shared_test.go b/libgo/misc/cgo/testshared/shared_test.go index a296005..846a271 100644 --- a/libgo/misc/cgo/testshared/shared_test.go +++ b/libgo/misc/cgo/testshared/shared_test.go @@ -905,3 +905,9 @@ func TestGlobal(t *testing.T) { AssertIsLinkedTo(t, "./bin/global", soname) AssertHasRPath(t, "./bin/global", gorootInstallDir) } + +// Run a test using -linkshared of an installed shared package. +// Issue 26400. +func TestTestInstalledShared(t *testing.T) { + goCmd(nil, "test", "-linkshared", "-test.short", "sync/atomic") +} diff --git a/libgo/misc/cgo/testshared/src/depBase/asm.s b/libgo/misc/cgo/testshared/src/depBase/asm.s index f203f8b..a8acf77f0 100644 --- a/libgo/misc/cgo/testshared/src/depBase/asm.s +++ b/libgo/misc/cgo/testshared/src/depBase/asm.s @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//+build !gccgo +// +build !gccgo #include "textflag.h" diff --git a/libgo/misc/cgo/testshared/src/depBase/dep.go b/libgo/misc/cgo/testshared/src/depBase/dep.go index 569c210..e7cc7c8 100644 --- a/libgo/misc/cgo/testshared/src/depBase/dep.go +++ b/libgo/misc/cgo/testshared/src/depBase/dep.go @@ -1,3 +1,7 @@ +// Copyright 2016 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 depBase import ( diff --git a/libgo/misc/cgo/testshared/src/depBase/gccgo.go b/libgo/misc/cgo/testshared/src/depBase/gccgo.go index 3e2b69b..2b02a1e 100644 --- a/libgo/misc/cgo/testshared/src/depBase/gccgo.go +++ b/libgo/misc/cgo/testshared/src/depBase/gccgo.go @@ -1,4 +1,8 @@ -//+build gccgo +// Copyright 2016 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 gccgo package depBase diff --git a/libgo/misc/cgo/testshared/src/depBase/stubs.go b/libgo/misc/cgo/testshared/src/depBase/stubs.go index 96573c1..04534f3 100644 --- a/libgo/misc/cgo/testshared/src/depBase/stubs.go +++ b/libgo/misc/cgo/testshared/src/depBase/stubs.go @@ -1,4 +1,8 @@ -//+build !gccgo +// Copyright 2016 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 !gccgo package depBase diff --git a/libgo/misc/cgo/testshared/src/trivial/trivial.go b/libgo/misc/cgo/testshared/src/trivial/trivial.go index da29a2c..6ade47c 100644 --- a/libgo/misc/cgo/testshared/src/trivial/trivial.go +++ b/libgo/misc/cgo/testshared/src/trivial/trivial.go @@ -1,4 +1,9 @@ package main func main() { + // This is enough to make sure that the executable references + // a type descriptor, which was the cause of + // https://golang.org/issue/25970. + c := make(chan int) + _ = c } diff --git a/libgo/runtime/go-caller.c b/libgo/runtime/go-caller.c index 7f36955..9d22f9f 100644 --- a/libgo/runtime/go-caller.c +++ b/libgo/runtime/go-caller.c @@ -210,17 +210,6 @@ Caller (int skip) /* Look up the function name, file name, and line number for a PC. */ struct funcfileline_return -{ - String retfn; - String retfile; - intgo retline; -}; - -struct funcfileline_return -runtime_funcfileline (uintptr targetpc, int32 index) - __asm__ (GOSYM_PREFIX "runtime.funcfileline"); - -struct funcfileline_return runtime_funcfileline (uintptr targetpc, int32 index) { struct funcfileline_return ret; diff --git a/libgo/runtime/go-runtime-error.c b/libgo/runtime/go-runtime-error.c index 5edbeeb..f1c1650 100644 --- a/libgo/runtime/go-runtime-error.c +++ b/libgo/runtime/go-runtime-error.c @@ -60,16 +60,29 @@ extern void __go_runtime_error (int32) __attribute__ ((noreturn)); void __go_runtime_error (int32 i) { + struct funcfileline_return fileline; + bool in_runtime; + + fileline = runtime_funcfileline ((uintptr) runtime_getcallerpc(), 0); + in_runtime = (fileline.retfn.len > 0 + && (__builtin_strncmp ((const char *) fileline.retfn.str, + "runtime.", 8) + == 0)); + switch (i) { case SLICE_INDEX_OUT_OF_BOUNDS: case ARRAY_INDEX_OUT_OF_BOUNDS: case STRING_INDEX_OUT_OF_BOUNDS: + if (in_runtime) + runtime_throw ("index out of range"); runtime_panicstring ("index out of range"); case SLICE_SLICE_OUT_OF_BOUNDS: case ARRAY_SLICE_OUT_OF_BOUNDS: case STRING_SLICE_OUT_OF_BOUNDS: + if (in_runtime) + runtime_throw ("slice bounds out of range"); runtime_panicstring ("slice bounds out of range"); case NIL_DEREFERENCE: diff --git a/libgo/runtime/panic.c b/libgo/runtime/panic.c index 8cb6bff..9cd69ee 100644 --- a/libgo/runtime/panic.c +++ b/libgo/runtime/panic.c @@ -37,3 +37,11 @@ runtime_panicstring(const char *s) runtime_newErrorCString(s, &err); runtime_panic(err); } + +extern void runtime_abort(void) __asm__(GOSYM_PREFIX "runtime.abort"); + +void +runtime_abort() +{ + abort(); +} diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h index 0856618..3c94532 100644 --- a/libgo/runtime/runtime.h +++ b/libgo/runtime/runtime.h @@ -403,8 +403,6 @@ void runtime_panic(Eface) /* * runtime c-called (but written in Go) */ -void runtime_newTypeAssertionError(const String*, const String*, const String*, const String*, Eface*) - __asm__ (GOSYM_PREFIX "runtime.NewTypeAssertionError"); void runtime_newErrorCString(const char*, Eface*) __asm__ (GOSYM_PREFIX "runtime.NewErrorCString"); @@ -479,10 +477,10 @@ extern void *getitab(const struct __go_type_descriptor *, __asm__ (GOSYM_PREFIX "runtime.getitab"); extern void runtime_cpuinit(void); +extern void setRandomNumber(uint32) + __asm__ (GOSYM_PREFIX "runtime.setRandomNumber"); extern void setIsCgo(void) __asm__ (GOSYM_PREFIX "runtime.setIsCgo"); -extern void setCpuidECX(uint32) - __asm__ (GOSYM_PREFIX "runtime.setCpuidECX"); extern void setSupportAES(bool) __asm__ (GOSYM_PREFIX "runtime.setSupportAES"); extern void typedmemmove(const Type *, void *, const void *) @@ -493,3 +491,14 @@ extern Sched* runtime_getsched(void) __asm__ (GOSYM_PREFIX "runtime.getsched"); extern void setpagesize(uintptr_t) __asm__(GOSYM_PREFIX "runtime.setpagesize"); + +struct funcfileline_return +{ + String retfn; + String retfile; + intgo retline; +}; + +struct funcfileline_return +runtime_funcfileline (uintptr targetpc, int32 index) + __asm__ (GOSYM_PREFIX "runtime.funcfileline"); diff --git a/libgo/runtime/runtime_c.c b/libgo/runtime/runtime_c.c index c65a7e0..6d77af4 100644 --- a/libgo/runtime/runtime_c.c +++ b/libgo/runtime/runtime_c.c @@ -33,6 +33,17 @@ runtime_atoi(const byte *p, intgo len) return n; } +// A random number from the GNU/Linux auxv array. +static uint32 randomNumber; + +// Set the random number from Go code. + +void +setRandomNumber(uint32 r) +{ + randomNumber = r; +} + #if defined(__i386__) || defined(__x86_64__) || defined (__s390__) || defined (__s390x__) // When cputicks is just asm instructions, skip the split stack @@ -85,8 +96,8 @@ runtime_cputicks(void) #else // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand(). // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. - // TODO: need more entropy to better seed fastrand. - return runtime_nanotime(); + // randomNumber provides better seeding of fastrand. + return runtime_nanotime() + randomNumber; #endif } @@ -193,7 +204,6 @@ runtime_cpuinit() } } if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) { - setCpuidECX(ecx); #if defined(__i386__) if ((edx & bit_SSE2) != 0) { hasSSE2 = true; diff --git a/libgo/testsuite/gotest b/libgo/testsuite/gotest index d9353d1..5631930 100755 --- a/libgo/testsuite/gotest +++ b/libgo/testsuite/gotest @@ -314,7 +314,7 @@ x) aix | android | darwin | dragonfly | freebsd | linux | nacl | netbsd | openbsd | plan9 | solaris | windows) tag1=nonmatchingtag ;; - 386 | amd64 | amd64p32 | arm | armbe | arm64 | arm64be | alpha | ia64 | m68k | mips | mipsle | mips64 | mips64le | mips64p32 | mips64p32le·| nios2 | ppc | ppc64 | ppc64le | riscv64 | s390 | s390x | sh | shbe | sparc | sparc64) + 386 | amd64 | amd64p32 | arm | armbe | arm64 | arm64be | alpha | ia64 | m68k | mips | mipsle | mips64 | mips64le | mips64p32 | mips64p32le·| nios2 | ppc | ppc64 | ppc64le | riscv64 | s390 | s390x | sh | shbe | sparc | sparc64 | wasm) tag1=nonmatchingtag ;; esac @@ -326,7 +326,7 @@ x) aix | android | darwin | dragonfly | freebsd | linux | nacl | netbsd | openbsd | plan9 | solaris | windows) tag2=nonmatchingtag ;; - 386 | amd64 | amd64p32 | arm | armbe | arm64 | arm64be | alpha | ia64 | m68k | mips | mipsle | mips64 | mips64le | mips64p32 | mips64p32le·| nios2 | ppc | ppc64 | ppc64le | riscv64 | s390 | s390x | sh | shbe | sparc | sparc64) + 386 | amd64 | amd64p32 | arm | armbe | arm64 | arm64be | alpha | ia64 | m68k | mips | mipsle | mips64 | mips64le | mips64p32 | mips64p32le·| nios2 | ppc | ppc64 | ppc64le | riscv64 | s390 | s390x | sh | shbe | sparc | sparc64 | wasm) tag2=nonmatchingtag ;; esac |