aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilippo Valsorda <filippo@golang.org>2020-08-17 18:12:19 -0400
committerFilippo Valsorda <filippo@golang.org>2020-08-17 18:12:19 -0400
commita15df605fc4aa6347494fd55a5c4c6544b0eb981 (patch)
tree978711ebd88092dbdc5c4b34d9f13b15f49580fa
parent6e6e0b73d6e19872946e08b65c7cac3b0289e2f0 (diff)
parent0fdc3801bfd43d6f55e4ea5bf095e1ea55430339 (diff)
downloadgo-a15df605fc4aa6347494fd55a5c4c6544b0eb981.tar.gz
go-a15df605fc4aa6347494fd55a5c4c6544b0eb981.zip
[dev.boringcrypto.go1.15] all: merge go1.15 into dev.boringcrypto.go1.15
Change-Id: I6eecfb4730f88ea8546ec88afdffe33f45dedac9
-rw-r--r--CONTRIBUTORS131
-rw-r--r--api/go1.15.txt2
-rw-r--r--doc/go1.15.html117
-rw-r--r--misc/cgo/test/test.go13
-rw-r--r--misc/cgo/testshared/shared_test.go13
-rw-r--r--src/cmd/cgo/out.go4
-rw-r--r--src/cmd/compile/internal/amd64/ssa.go6
-rw-r--r--src/cmd/compile/internal/ssa/addressingmodes.go17
-rw-r--r--src/cmd/compile/internal/ssa/gen/AMD64Ops.go34
-rw-r--r--src/cmd/compile/internal/ssa/opGen.go320
-rw-r--r--src/cmd/compile/internal/ssa/prove.go8
-rw-r--r--src/cmd/go.mod1
-rw-r--r--src/cmd/go.sum2
-rw-r--r--src/cmd/go/alldocs.go2
-rw-r--r--src/cmd/go/internal/help/helpdoc.go2
-rw-r--r--src/cmd/go/internal/load/pkg.go29
-rw-r--r--src/cmd/go/testdata/script/list_err_stack.txt27
-rw-r--r--src/cmd/gofmt/gofmt.go8
-rw-r--r--src/cmd/link/internal/ld/lib.go11
-rw-r--r--src/cmd/link/link_test.go9
-rw-r--r--src/cmd/vendor/modules.txt3
-rw-r--r--src/compress/flate/deflate_test.go56
-rw-r--r--src/compress/flate/deflatefast.go49
-rw-r--r--src/compress/flate/huffman_bit_writer.go1
-rw-r--r--src/compress/flate/writer_test.go63
-rw-r--r--src/crypto/ed25519/ed25519.go6
-rw-r--r--src/crypto/ed25519/ed25519_noasm.go15
-rw-r--r--src/crypto/ed25519/ed25519_s390x.go51
-rw-r--r--src/crypto/ed25519/ed25519_s390x.s161
-rw-r--r--src/crypto/ed25519/ed25519_test.go44
-rw-r--r--src/crypto/x509/internal/macos/corefoundation.go (renamed from src/crypto/x509/internal/macOS/corefoundation.go)0
-rw-r--r--src/crypto/x509/internal/macos/corefoundation.s (renamed from src/crypto/x509/internal/macOS/corefoundation.s)0
-rw-r--r--src/crypto/x509/internal/macos/security.go (renamed from src/crypto/x509/internal/macOS/security.go)0
-rw-r--r--src/crypto/x509/internal/macos/security.s (renamed from src/crypto/x509/internal/macOS/security.s)0
-rw-r--r--src/crypto/x509/root_darwin_amd64.go2
-rw-r--r--src/crypto/x509/root_windows.go46
-rw-r--r--src/crypto/x509/verify.go26
-rw-r--r--src/crypto/x509/verify_test.go890
-rw-r--r--src/encoding/binary/varint.go5
-rw-r--r--src/encoding/binary/varint_test.go18
-rw-r--r--src/go/build/deps_test.go26
-rw-r--r--src/go/format/format.go8
-rw-r--r--src/go/format/format_test.go4
-rw-r--r--src/go/printer/nodes.go18
-rw-r--r--src/go/printer/performance_test.go2
-rw-r--r--src/go/printer/printer.go17
-rw-r--r--src/go/printer/printer_test.go8
-rw-r--r--src/go/printer/testdata/go2numbers.norm (renamed from src/go/printer/testdata/go2numbers.stdfmt)0
-rw-r--r--src/html/template/clone_test.go2
-rw-r--r--src/html/template/content_test.go6
-rw-r--r--src/html/template/escape_test.go2
-rw-r--r--src/html/template/exec_test.go1708
-rw-r--r--src/html/template/multi_test.go246
-rw-r--r--src/html/template/testdata/file1.tmpl2
-rw-r--r--src/html/template/testdata/file2.tmpl2
-rw-r--r--src/html/template/testdata/tmpl1.tmpl3
-rw-r--r--src/html/template/testdata/tmpl2.tmpl3
-rw-r--r--src/net/http/server.go43
-rw-r--r--src/net/http/transport.go73
-rw-r--r--src/net/http/transport_test.go44
-rw-r--r--src/net/ipsock_plan9.go28
-rw-r--r--src/net/textproto/reader.go2
-rw-r--r--src/os/pipe_test.go19
-rw-r--r--src/os/readfrom_linux.go5
-rw-r--r--src/os/readfrom_linux_test.go30
-rw-r--r--src/runtime/lockrank_off.go4
-rw-r--r--src/runtime/lockrank_on.go4
-rw-r--r--src/runtime/mpagealloc.go43
-rw-r--r--src/runtime/mpagealloc_test.go57
-rw-r--r--src/runtime/mranges.go19
-rw-r--r--src/runtime/os_linux_x86.go35
-rw-r--r--src/runtime/os_openbsd.go1
-rw-r--r--src/runtime/proc.go69
-rw-r--r--src/runtime/race/README6
-rw-r--r--src/runtime/race/race_linux_amd64.sysobin518680 -> 521752 bytes
-rw-r--r--src/runtime/race/race_linux_arm64.sysobin497656 -> 500584 bytes
-rw-r--r--src/runtime/race/race_linux_ppc64le.sysobin619560 -> 623824 bytes
-rw-r--r--src/runtime/race/syso_test.go39
-rw-r--r--src/runtime/sys_darwin.go2
-rw-r--r--src/runtime/sys_linux_386.s38
-rw-r--r--src/runtime/sys_linux_amd64.s56
-rw-r--r--src/runtime/sys_linux_arm.s36
-rw-r--r--src/runtime/sys_linux_arm64.s34
-rw-r--r--src/runtime/sys_linux_mips64x.s38
-rw-r--r--src/runtime/sys_linux_ppc64x.s40
-rw-r--r--src/sync/runtime2.go4
-rw-r--r--src/sync/runtime2_lockrank.go4
-rw-r--r--src/syscall/exec_linux.go2
-rw-r--r--src/testing/testing.go6
-rw-r--r--src/text/template/multi_test.go1
-rw-r--r--test/codegen/memops.go23
-rw-r--r--test/fixedbugs/issue33308.go12
-rw-r--r--test/fixedbugs/issue40367.go41
-rw-r--r--test/prove.go6
94 files changed, 4044 insertions, 1069 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 798002cfb5..cebc92f53f 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -31,6 +31,7 @@ Aaron Cannon <cannona@fireantproductions.com>
Aaron France <aaron.l.france@gmail.com>
Aaron Jacobs <jacobsa@google.com>
Aaron Kemp <kemp.aaron@gmail.com>
+Aaron Patterson <tenderlove@ruby-lang.org>
Aaron Stein <aaronstein12@gmail.com>
Aaron Torres <tcboox@gmail.com>
Aaron Zinman <aaron@azinman.com>
@@ -58,6 +59,7 @@ Adrian Hesketh <adrianhesketh@hushmail.com>
Adrian Nos <nos.adrian@gmail.com>
Adrian O'Grady <elpollouk@gmail.com>
Adrien Bustany <adrien-xx-google@bustany.org>
+Adrien Delorme <adrien.delorme@icloud.com>
Adrien Petel <peteladrien@gmail.com>
Aécio Júnior <aeciodantasjunior@gmail.com>
Aeneas Rekkas (arekkas) <aeneas@ory.am>
@@ -114,6 +116,7 @@ Alex Zhirov <azhirov@google.com>
Alexander Demakin <alexander.demakin@gmail.com>
Alexander Döring <email@alexd.ch>
Alexander F Rødseth <alexander.rodseth@appeartv.com>
+Alexander Greim <alexxx@iltempo.de>
Alexander Guz <kalimatas@gmail.com>
Alexander Kauer <alexander@affine.space>
Alexander Kucherenko <alxkchr@gmail.com>
@@ -122,6 +125,7 @@ Alexander Lourier <aml@rulezz.ru>
Alexander Menzhinsky <amenzhinsky@gmail.com>
Alexander Morozov <lk4d4math@gmail.com>
Alexander Neumann <alexander@bumpern.de>
+Alexander Nohe <alex.nohe427@gmail.com>
Alexander Orlov <alexander.orlov@loxal.net>
Alexander Pantyukhin <apantykhin@gmail.com>
Alexander Polcyn <apolcyn@google.com>
@@ -149,6 +153,7 @@ Alexey Semenyuk <alexsemenyuk88@gmail.com>
Alexis Hildebrandt <surryhill@gmail.com>
Alexis Hunt <lexer@google.com>
Alexis Imperial-Legrand <ail@google.com>
+Ali Farooq <ali.farooq0@pm.me>
Ali Rizvi-Santiago <arizvisa@gmail.com>
Aliaksandr Valialkin <valyala@gmail.com>
Alif Rachmawadi <subosito@gmail.com>
@@ -156,14 +161,17 @@ Allan Simon <allan.simon@supinfo.com>
Allen Li <ayatane@google.com>
Alok Menghrajani <alok.menghrajani@gmail.com>
Aman Gupta <aman@tmm1.net>
+Amarjeet Anand <amarjeetanandsingh@gmail.com>
Amir Mohammad Saied <amir@gluegadget.com>
Amr Mohammed <merodiro@gmail.com>
Amrut Joshi <amrut.joshi@gmail.com>
+An Long <aisk1988@gmail.com>
An Xiao <hac@zju.edu.cn>
Anand K. Mistry <anand@mistry.ninja>
Anders Pearson <anders@columbia.edu>
Anderson Queiroz <contato@andersonq.eti.br>
André Carvalho <asantostc@gmail.com>
+André Martins <aanm90@gmail.com>
Andre Nathan <andrenth@gmail.com>
Andrea Nodari <andrea.nodari91@gmail.com>
Andrea Spadaccini <spadaccio@google.com>
@@ -187,9 +195,11 @@ Andrew Braunstein <awbraunstein@gmail.com>
Andrew Bursavich <abursavich@gmail.com>
Andrew Ekstedt <andrew.ekstedt@gmail.com>
Andrew Etter <andrew.etter@gmail.com>
+Andrew G. Morgan <agm@google.com>
Andrew Gerrand <adg@golang.org>
Andrew Harding <andrew@spacemonkey.com>
Andrew Jackura <ajackura@google.com>
+Andrew Louis <alouis@digitalocean.com>
Andrew Lutomirski <andy@luto.us>
Andrew Medvedev <andrew.y.medvedev@gmail.com>
Andrew Pilloud <andrewpilloud@igneoussystems.com>
@@ -219,6 +229,7 @@ Andy Lindeman <andy@lindeman.io>
Andy Maloney <asmaloney@gmail.com>
Andy Pan <panjf2000@gmail.com>
Andy Walker <walkeraj@gmail.com>
+Andy Wang <cbeuw.andy@gmail.com>
Andzej Maciusovic <andzej.maciusovic@gmail.com>
Anfernee Yongkun Gui <anfernee.gui@gmail.com>
Angelo Bulfone <mbulfone@gmail.com>
@@ -226,6 +237,7 @@ Anh Hai Trinh <anh.hai.trinh@gmail.com>
Anit Gandhi <anitgandhi@gmail.com>
Ankit Goyal <ankit3goyal@gmail.com>
Anmol Sethi <anmol@aubble.com>
+Annirudh Prasad <annirudh@wandb.com>
Anschel Schaffer-Cohen <anschelsc@gmail.com>
Anthony Alves <cvballa3g0@gmail.com>
Anthony Canino <anthony.canino1@gmail.com>
@@ -239,15 +251,18 @@ Anthony Woods <awoods@raintank.io>
Antoine GIRARD <sapk@sapk.fr>
Antoine Martin <antoine97.martin@gmail.com>
Anton Gyllenberg <anton@iki.fi>
+Anton Kuklin <anton.a.kuklin@gmail.com>
Antonin Amand <antonin.amand@gmail.com>
Antonio Antelo <aantelov87@gmail.com>
Antonio Bibiano <antbbn@gmail.com>
Antonio Huete Jimenez <tuxillo@quantumachine.net>
Antonio Murdaca <runcom@redhat.com>
Antonio Troina <thoeni@gmail.com>
+Anze Kolar <me@akolar.com>
Aofei Sheng <aofei@aofeisheng.com>
Apisak Darakananda <pongad@gmail.com>
Aram Hăvărneanu <aram@mgk.ro>
+Araragi Hokuto <kanseihonbucho@protonmail.com>
Arash Bina <arash@arash.io>
Arda Güçlü <ardaguclu@gmail.com>
Areski Belaid <areski@gmail.com>
@@ -273,6 +288,7 @@ Audrius Butkevicius <audrius.butkevicius@gmail.com>
Augusto Roman <aroman@gmail.com>
Aulus Egnatius Varialus <varialus@gmail.com>
Aurélien Rainone <aurelien.rainone@gmail.com>
+Aurélio A. Heckert <aurium@gmail.com>
Austin Clements <austin@google.com> <aclements@csail.mit.edu>
Avi Flax <avi@timehop.com>
awaw fumin <awawfumin@gmail.com>
@@ -315,6 +331,7 @@ Benoit Sigoure <tsunanet@gmail.com>
Berengar Lehr <Berengar.Lehr@gmx.de>
Berkant Ipek <41230766+0xbkt@users.noreply.github.com>
Bharath Thiruveedula <tbharath91@gmail.com>
+Bhavin Gandhi <bhavin7392@gmail.com>
Bill Neubauer <wcn@golang.org> <wcn@google.com> <bill.neubauer@gmail.com>
Bill O'Farrell <billo@ca.ibm.com>
Bill Prin <waprin@google.com>
@@ -322,6 +339,7 @@ Bill Thiede <couchmoney@gmail.com>
Bill Zissimopoulos <billziss@navimatics.com>
Billie Harold Cleek <bhcleek@gmail.com>
Billy Lynch <wlynch@google.com>
+Billy Zaelani Malik <m.billyzaelani@gmail.com>
Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Bjorn Tillenius <bjorn@tillenius.me>
Bjorn Tipling <bjorn.tipling@gmail.com>
@@ -331,12 +349,15 @@ Blake Mesdag <blakemesdag@gmail.com>
Blake Mizerany <blake.mizerany@gmail.com>
Blixt <me@blixt.nyc>
Bob Briski <rbriski@gmail.com>
+Bob McNaughton <bobmcn@gmail.com>
Bob Potter <bobby.potter@gmail.com>
Bobby DeSimone <bobbydesimone@gmail.com>
Bobby Powers <bobbypowers@gmail.com>
+Boqin Qin <bobbqqin@gmail.com>
Boris Nagaev <nagaev@google.com>
Borja Clemente <borja.clemente@gmail.com>
Brad Burch <brad.burch@gmail.com>
+Brad Erickson <bderickson@gmail.com>
Brad Fitzpatrick <bradfitz@golang.org> <bradfitz@gmail.com>
Brad Garcia <bgarcia@golang.org>
Brad Jones <rbjones@google.com>
@@ -351,6 +372,7 @@ Brandon Bennett <bbennett@fb.com>
Brandon Gilmore <varz@google.com>
Brandon Philips <brandon@ifup.org>
Brandon Ryan <bjryan19@gmail.com>
+Brayden Cloud <bcloud@google.com>
Brendan Daniel Tracey <tracey.brendan@gmail.com>
Brendan O'Dea <bod@golang.org>
Brett Cannon <bcannon@gmail.com>
@@ -390,6 +412,7 @@ Carlos Castillo <cookieo9@gmail.com>
Carlos Cirello <uldericofilho@gmail.com>
Carlos Eduardo <me@carlosedp.com>
Carlos Eduardo Seo <cseo@linux.vnet.ibm.com>
+Carlos Iriarte <ciriarte@gmail.com>
Carlos Souza <carloshrsouza@gmail.com>
Carolyn Van Slyck <me@carolynvanslyck.com>
Carrie Bynon <cbynon@gmail.com>
@@ -405,6 +428,7 @@ Chad Rosier <mrosier.qdt@qualcommdatacenter.com>
ChaiShushan <chaishushan@gmail.com>
Changkun Ou <hi@changkun.us>
Channing Kimble-Brown <channing@golang.org>
+Chao Xu <xuchao@google.com>
Charles Fenwick Elliott <Charles@FenwickElliott.io>
Charles Kenney <charlesc.kenney@gmail.com>
Charles L. Dorian <cldorian@gmail.com>
@@ -426,6 +450,7 @@ Chris Howey <howeyc@gmail.com>
Chris Hundt <hundt@google.com>
Chris Jones <chris@cjones.org> <chris.jones.yar@gmail.com>
Chris Kastorff <encryptio@gmail.com>
+Chris Le Roy <brompwnie@users.noreply.github.com>
Chris Lennert <calennert@gmail.com>
Chris Liles <caveryliles@gmail.com>
Chris Manghane <cmang@golang.org>
@@ -475,6 +500,7 @@ Conrad Meyer <cemeyer@cs.washington.edu>
Conrado Gouvea <conradoplg@gmail.com>
Constantin Konstantinidis <constantinkonstantinidis@gmail.com>
Corey Thomasson <cthom.lists@gmail.com>
+Corne van der Plas <vdplas@gmail.com>
Cosmos Nicolaou <cnicolaou@google.com>
Costin Chirvasuta <ctin@google.com>
Craig Citro <craigcitro@google.com>
@@ -506,9 +532,11 @@ Daniel Ingram <ingramds@appstate.edu>
Daniel Johansson <dajo2002@gmail.com>
Daniel Kerwin <d.kerwin@gini.net>
Daniel Krech <eikeon@eikeon.com>
+Daniel Kumor <rdkumor@gmail.com>
Daniel Langner <s8572327@gmail.com>
Daniel Lidén <daniel.liden.87@gmail.com>
Daniel Lublin <daniel@lublin.se>
+Daniel Mangum <georgedanielmangum@gmail.com>
Daniel Martí <mvdan@mvdan.cc>
Daniel Morsing <daniel.morsing@gmail.com>
Daniel Nadasi <dnadasi@google.com>
@@ -519,6 +547,8 @@ Daniel Speichert <daniel@speichert.pl>
Daniel Theophanes <kardianos@gmail.com>
Daniel Upton <daniel@floppy.co>
Daniela Petruzalek <daniela.petruzalek@gmail.com>
+Danish Dua <danishdua@google.com>
+Danish Prakash <grafitykoncept@gmail.com>
Danny Rosseau <daniel.rosseau@gmail.com>
Daria Kolistratova <daria.kolistratova@intel.com>
Darien Raymond <admin@v2ray.com>
@@ -542,6 +572,7 @@ David Brophy <dave@brophy.uk>
David Bürgin <676c7473@gmail.com>
David Calavera <david.calavera@gmail.com>
David Carlier <devnexen@gmail.com>
+David Carter <fresco.raja@gmail.com>
David Chase <drchase@google.com>
David Covert <davidhcovert@gmail.com>
David Crawshaw <david.crawshaw@zentus.com> <crawshaw@google.com> <crawshaw@golang.org>
@@ -550,6 +581,7 @@ David Finkel <david.finkel@gmail.com>
David Forsythe <dforsythe@gmail.com>
David G. Andersen <dave.andersen@gmail.com>
David Glasser <glasser@meteor.com>
+David Golden <david@autopragmatic.com>
David Heuschmann <heuschmann.d@gmail.com>
David Howden <dhowden@gmail.com>
David Hubbard <dsp@google.com>
@@ -574,6 +606,7 @@ David Volquartz Lebech <david@lebech.info>
David Wimmer <davidlwimmer@gmail.com>
Davies Liu <davies.liu@gmail.com>
Davor Kapsa <davor.kapsa@gmail.com>
+Dean Eigenmann <7621705+decanus@users.noreply.github.com>
Dean Prichard <dean.prichard@gmail.com>
Deepak Jois <deepak.jois@gmail.com>
Denis Bernard <db047h@gmail.com>
@@ -619,6 +652,7 @@ Dmitry Mottl <dmitry.mottl@gmail.com>
Dmitry Neverov <dmitry.neverov@gmail.com>
Dmitry Savintsev <dsavints@gmail.com>
Dmitry Yakunin <nonamezeil@gmail.com>
+Doga Fincan <doga@icloud.com>
Domas Tamašauskas <puerdomus@gmail.com>
Domen Ipavec <domen@ipavec.net>
Dominic Green <dominicgreen1@gmail.com>
@@ -642,6 +676,7 @@ Dustin Sallings <dsallings@gmail.com>
Dustin Shields-Cloues <dcloues@gmail.com>
Dvir Volk <dvir@everything.me> <dvirsky@gmail.com>
Dylan Waits <dylan@waits.io>
+Ed Schouten <ed@nuxi.nl>
Edan Bedrik <3d4nb3@gmail.com>
Eddie Scholtz <escholtz@google.com>
Eden Li <eden.li@gmail.com>
@@ -659,11 +694,13 @@ Elena Grahovac <elena@grahovac.me>
Eli Bendersky <eliben@google.com>
Elias Naur <mail@eliasnaur.com> <elias.naur@gmail.com>
Elliot Morrison-Reed <elliotmr@gmail.com>
+Ellison Leão <ellisonleao@gmail.com>
Emerson Lin <linyintor@gmail.com>
Emil Hessman <emil@hessman.se>
Emil Mursalimov <mursalimovemeel@gmail.com>
Emilien Kenler <hello@emilienkenler.com>
Emmanuel Odeke <emm.odeke@gmail.com> <odeke@ualberta.ca>
+Emrecan Bati <emrecanbati@gmail.com>
Eno Compton <enocom@google.com>
Eoghan Sherry <ejsherry@gmail.com>
Eric Biggers <ebiggers@google.com>
@@ -682,6 +719,7 @@ Eric Rescorla <ekr@rtfm.com>
Eric Roshan-Eisner <eric.d.eisner@gmail.com>
Eric Rutherford <erutherford@gmail.com>
Eric Rykwalder <e.rykwalder@gmail.com>
+Erick Tryzelaar <etryzelaar@google.com>
Erik Aigner <aigner.erik@gmail.com>
Erik Dubbelboer <erik@dubbelboer.com>
Erik St. Martin <alakriti@gmail.com>
@@ -694,6 +732,7 @@ Esko Luontola <esko.luontola@gmail.com>
Ethan Burns <eaburns@google.com>
Ethan Miller <eamiller@us.ibm.com>
Euan Kemp <euank@euank.com>
+Eugene Formanenko <mo4islona@gmail.com>
Eugene Kalinin <e.v.kalinin@gmail.com>
Evan Broder <evan@stripe.com>
Evan Brown <evanbrown@google.com>
@@ -705,6 +744,7 @@ Evan Kroske <evankroske@google.com>
Evan Martin <evan.martin@gmail.com>
Evan Phoenix <evan@phx.io>
Evan Shaw <chickencha@gmail.com>
+Evgeniy Kulikov <tuxuls@gmail.com>
Evgeniy Polyakov <zbr@ioremap.net>
Ewan Chou <coocood@gmail.com>
Ewan Valentine <ewan.valentine89@gmail.com>
@@ -725,8 +765,10 @@ Fedor Indutny <fedor@indutny.com>
Fedor Korotkiy <dartslon@gmail.com>
Felipe Oliveira <felipeweb.programador@gmail.com>
Felix Bünemann <Felix.Buenemann@gmail.com>
+Felix Cornelius <9767036+fcornelius@users.noreply.github.com>
Felix Geisendörfer <haimuiba@gmail.com>
Felix Kollmann <fk@konsorten.de>
+Ferenc Szabo <frncmx@gmail.com>
Filip Gruszczyński <gruszczy@gmail.com>
Filip Haglund <drathier@users.noreply.github.com>
Filip Stanis <fstanis@google.com>
@@ -774,6 +816,7 @@ Gary Elliott <garyelliott@google.com>
Gaurish Sharma <contact@gaurishsharma.com>
Gautham Thambidorai <gautham.dorai@gmail.com>
Gauthier Jolly <gauthier.jolly@gmail.com>
+Gawen Arab <gawen.arab@c.zen.ly>
Geert-Johan Riemer <gjr19912@gmail.com>
Genevieve Luyt <genevieve.luyt@gmail.com>
Gengliang Wang <ltnwgl@gmail.com>
@@ -795,6 +838,7 @@ Gianguido Sora` <g.sora4@gmail.com>
Gideon Jan-Wessel Redelinghuys <gjredelinghuys@gmail.com>
Giles Lean <giles.lean@pobox.com>
Giovanni Bajo <rasky@develer.com>
+GitHub User @aca (50316549) <acadx0@gmail.com>
GitHub User @ajnirp (1688456) <ajnirp@users.noreply.github.com>
GitHub User @ajz01 (4744634) <ajzdenek@gmail.com>
GitHub User @alkesh26 (1019076) <alkesh26@gmail.com>
@@ -805,12 +849,18 @@ GitHub User @bakape (7851952) <bakape@gmail.com>
GitHub User @bgadrian (830001) <aditza8@gmail.com>
GitHub User @bontequero (2674999) <bontequero@gmail.com>
GitHub User @cch123 (384546) <buaa.cch@gmail.com>
+GitHub User @chainhelen (7046329) <chainhelen@gmail.com>
GitHub User @chanxuehong (3416908) <chanxuehong@gmail.com>
+GitHub User @cncal (23520240) <flycalvin@qq.com>
GitHub User @DQNEO (188741) <dqneoo@gmail.com>
+GitHub User @Dreamacro (8615343) <chuainian@gmail.com>
GitHub User @dupoxy (1143957) <dupoxy@users.noreply.github.com>
GitHub User @erifan (31343225) <eric.fang@arm.com>
GitHub User @esell (9735165) <eujon.sellers@gmail.com>
+GitHub User @fatedier (7346661) <fatedier@gmail.com>
GitHub User @frennkie (6499251) <mail@rhab.de>
+GitHub User @geedchin (11672310) <geedchin@gmail.com>
+GitHub User @GrigoriyMikhalkin (3637857) <grigoriymikhalkin@gmail.com>
GitHub User @hengwu0 (41297446) <41297446+hengwu0@users.noreply.github.com>
GitHub User @itchyny (375258) <itchyny@hatena.ne.jp>
GitHub User @jinmiaoluo (39730824) <jinmiaoluo@icloud.com>
@@ -820,11 +870,13 @@ GitHub User @kc1212 (1093806) <kc1212@users.noreply.github.com>
GitHub User @Kropekk (13366453) <kamilkropiewnicki@gmail.com>
GitHub User @linguohua (3434367) <lghchinaidea@gmail.com>
GitHub User @LotusFenn (13775899) <fenn.lotus@gmail.com>
+GitHub User @ly303550688 (11519839) <yang.liu636@gmail.com>
GitHub User @madiganz (18340029) <zacharywmadigan@gmail.com>
GitHub User @maltalex (10195391) <code@bit48.net>
GitHub User @Matts966 (28551465) <Matts966@users.noreply.github.com>
GitHub User @micnncim (21333876) <micnncim@gmail.com>
GitHub User @mkishere (224617) <224617+mkishere@users.noreply.github.com>
+GitHub User @nu50218 (40682920) <nu_ll@icloud.com>
GitHub User @OlgaVlPetrova (44112727) <OVPpetrova@gmail.com>
GitHub User @pityonline (438222) <pityonline@gmail.com>
GitHub User @po3rin (29445112) <abctail30@gmail.com>
@@ -836,6 +888,7 @@ GitHub User @shogo-ma (9860598) <Choroma194@gmail.com>
GitHub User @skanehira (7888591) <sho19921005@gmail.com>
GitHub User @tatsumack (4510569) <tatsu.mack@gmail.com>
GitHub User @tell-k (26263) <ffk2005@gmail.com>
+GitHub User @tennashi (10219626) <tennashio@gmail.com>
GitHub User @uhei (2116845) <uhei@users.noreply.github.com>
GitHub User @uropek (39370426) <uropek@gmail.com>
GitHub User @utkarsh-extc (53217283) <utkarsh.extc@gmail.com>
@@ -861,6 +914,7 @@ Greg Thelen <gthelen@google.com>
Greg Ward <greg@gerg.ca>
Grégoire Delattre <gregoire.delattre@gmail.com>
Gregory Man <man.gregory@gmail.com>
+Gregory Petrosyan <gregory.petrosyan@gmail.com>
Guilherme Caruso <gui.martinscaruso@gmail.com>
Guilherme Garnier <guilherme.garnier@gmail.com>
Guilherme Goncalves <guilhermeaugustosg@gmail.com>
@@ -917,6 +971,7 @@ Hitoshi Mitake <mitake.hitoshi@gmail.com>
Holden Huang <ttyh061@gmail.com>
Hong Ruiqi <hongruiqi@gmail.com>
Hongfei Tan <feilengcui008@gmail.com>
+Horacio Duran <horacio.duran@gmail.com>
Horst Rutter <hhrutter@gmail.com>
Hossein Sheikh Attar <hattar@google.com>
Howard Zhang <howard.zhang@arm.com>
@@ -927,6 +982,7 @@ Huan Du <i@huandu.me>
Hugues Bruant <hugues.bruant@gmail.com>
Huy Le <huy.dinh.le.89@gmail.com>
Hyang-Ah Hana Kim <hakim@google.com> <hyangah@gmail.com>
+Hyoyoung Chang <hyoyoung@gmail.com>
Ian Cottrell <iancottrell@google.com>
Ian Davis <nospam@iandavis.com>
Ian Gudger <ian@loosescre.ws>
@@ -986,6 +1042,7 @@ Jake B <doogie1012@gmail.com>
Jakob Borg <jakob@nym.se>
Jakob Weisblat <jakobw@mit.edu>
Jakub Čajka <jcajka@redhat.com>
+Jakub Kaczmarzyk <jakubk@mit.edu>
Jakub Ryszard Czarnowicz <j.czarnowicz@gmail.com>
Jamal Carvalho <jamal.a.carvalho@gmail.com>
James Aguilar <jaguilar@google.com>
@@ -1032,6 +1089,7 @@ Jan Steinke <jan.steinke@gmail.com>
Jan Ziak <0xe2.0x9a.0x9b@gmail.com>
Jani Monoses <jani.monoses@ubuntu.com> <jani.monoses@gmail.com>
Jannis Andrija Schnitzer <jannis@schnitzer.im>
+Jared Allard <jaredallard@users.noreply.github.com>
Jared Culp <jculp14@gmail.com>
Jaroslavas Počepko <jp@webmaster.ms>
Jason A. Donenfeld <Jason@zx2c4.com>
@@ -1086,8 +1144,11 @@ Jerrin Shaji George <jerrinsg@gmail.com>
Jess Frazelle <me@jessfraz.com>
Jesse Szwedko <jesse.szwedko@gmail.com>
Jesús Espino <jespinog@gmail.com>
+Jia Zhan <jzhan@uber.com>
+Jiacai Liu <jiacai2050@gmail.com>
Jianing Yu <jnyu@google.com>
Jianqiao Li <jianqiaoli@google.com>
+Jie Ma <jienius@outlook.com>
Jihyun Yu <yjh0502@gmail.com>
Jim Cote <jfcote87@gmail.com>
Jim Kingdon <jim@bolt.me>
@@ -1135,6 +1196,7 @@ John Howard Palevich <jack.palevich@gmail.com>
John Jeffery <jjeffery@sp.com.au>
John Jenkins <twodopeshaggy@gmail.com>
John Leidegren <john.leidegren@gmail.com>
+John McCabe <john@johnmccabe.net>
John Moore <johnkenneth.moore@gmail.com>
John Newlin <jnewlin@google.com>
John Papandriopoulos <jpap.code@gmail.com>
@@ -1146,6 +1208,7 @@ John Tuley <john@tuley.org>
John Weldon <johnweldon4@gmail.com>
Johnny Luo <johnnyluo1980@gmail.com>
Jon Chen <jchen@justin.tv>
+Jon Johnson <jonjohnson@google.com>
Jonas Bernoulli <jonas@bernoul.li>
Jonathan Allie <jonallie@google.com>
Jonathan Amsterdam <jba@google.com>
@@ -1165,6 +1228,7 @@ Jonathon Lacher <jonathon.lacher@gmail.com>
Jongmin Kim <atomaths@gmail.com>
Joonas Kuorilehto <joneskoo@derbian.fi>
Joop Kiefte <ikojba@gmail.com> <joop@kiefte.net>
+Jordan Christiansen <xordspar0@gmail.com>
Jordan Krage <jmank88@gmail.com>
Jordan Lewis <jordanthelewis@gmail.com>
Jordan Liggitt <liggitt@google.com>
@@ -1177,6 +1241,7 @@ Josa Gesell <josa@gesell.me>
Jose Luis Vázquez González <josvazg@gmail.com>
Joseph Bonneau <jcb@google.com>
Joseph Holsten <joseph@josephholsten.com>
+Josh Baum <joshbaum@google.com>
Josh Bleecher Snyder <josharian@gmail.com>
Josh Chorlton <jchorlton@gmail.com>
Josh Deprez <josh.deprez@gmail.com>
@@ -1185,8 +1250,10 @@ Josh Hoak <jhoak@google.com>
Josh Holland <jrh@joshh.co.uk>
Josh Roppo <joshroppo@gmail.com>
Josh Varga <josh.varga@gmail.com>
+Joshua Bezaleel Abednego <joshua.bezaleel@gmail.com>
Joshua Boelter <joshua.boelter@intel.com>
Joshua Chase <jcjoshuachase@gmail.com>
+Joshua Crowgey <jcrowgey@uw.edu>
Joshua M. Clulow <josh.clulow@joyent.com>
Joshua Rubin <joshua@rubixconsulting.com>
Josselin Costanzi <josselin@costanzi.fr>
@@ -1265,6 +1332,7 @@ Kenji Yano <kenji.yano@gmail.com>
Kenneth Shaw <kenshaw@gmail.com>
Kenny Grant <kennygrant@gmail.com>
Kenta Mori <zoncoen@gmail.com>
+Kerollos Magdy <kerolloz@yahoo.com>
Ketan Parmar <ketanbparmar@gmail.com>
Kevan Swanberg <kevswanberg@gmail.com>
Kevin Ballard <kevin@sb.org>
@@ -1277,10 +1345,14 @@ Kevin Malachowski <chowski@google.com>
Kevin Ruffin <kruffin@gmail.com>
Kevin Vu <kevin.m.vu@gmail.com>
Kevin Zita <bleedgreenandgold@gmail.com>
+Keyan Pishdadian <kpishdadian@gmail.com>
+Kezhu Wang <kezhuw@gmail.com>
+Khosrow Moossavi <khos2ow@gmail.com>
Kieran Colford <kieran@kcolford.com>
Kim Shrier <kshrier@racktopsystems.com>
Kim Yongbin <kybinz@gmail.com>
Kir Kolyshkin <kolyshkin@gmail.com>
+Kirill Korotaev <kirillx@gmail.com>
Kirill Motkov <Motkov.Kirill@gmail.com>
Kirill Smelkov <kirr@nexedi.com>
Kirill Tatchihin <kirabsuir@gmail.com>
@@ -1308,6 +1380,7 @@ Kyle Consalus <consalus@gmail.com>
Kyle Isom <kyle@gokyle.net>
Kyle Jones <kyle@kyledj.com>
Kyle Lemons <kyle@kylelemons.net> <kevlar@google.com>
+Kyle Nusbaum <kyle@datadog.com>
Kyle Shannon <kyle@pobox.com>
Kyle Spiers <eiais@google.com>
Kyle Wood <kyle@kylewood.cc>
@@ -1339,6 +1412,8 @@ Leonardo Comelli <leonardo.comelli@gmail.com>
Leonel Quinteros <leonel.quinteros@gmail.com>
Lev Shamardin <shamardin@gmail.com>
Lewin Bormann <lewin.bormann@gmail.com>
+Liam Haworth <liam@haworth.id.au>
+Lily Chung <lilithkchung@gmail.com>
Lion Yang <lion@aosc.xyz>
Liz Rice <liz@lizrice.com>
Lloyd Dewolf <foolswisdom@gmail.com>
@@ -1396,6 +1471,7 @@ Marcel van Lohuizen <mpvl@golang.org>
Marcelo Cantos <marcelo.cantos@gmail.com>
Marcelo E. Magallon <marcelo.magallon@gmail.com>
Marco Hennings <marco.hennings@freiheit.com>
+Marcus Weiner <marcus.weiner@gmail.com>
Marcus Willock <crazcalm@gmail.com>
Marga Manterola <marga@google.com>
Mariano Cano <mariano@smallstep.com>
@@ -1426,6 +1502,7 @@ Markus Duft <markus.duft@salomon.at>
Markus Sonderegger <marraison@gmail.com>
Markus Zimmermann <zimmski@gmail.com>
Marten Seemann <martenseemann@gmail.com>
+Martin Asquino <martin.asquino@gmail.com>
Martin Bertschler <mbertschler@gmail.com>
Martin Garton <garton@gmail.com>
Martin Habbecke <marhab@google.com>
@@ -1449,6 +1526,7 @@ Maryan Hratson <gmarik@gmail.com>
Masahiro Furudate <masahiro.furudate@gmail.com>
Masahiro Wakame <vvakame@gmail.com>
Masaki Yoshida <yoshida.masaki@gmail.com>
+Masaya Watanabe <sfbgwm30@gmail.com>
Mat Byczkowski <mbyczkowski@gmail.com>
Mat Ryer <thatmatryer@gmail.com>
Máté Gulyás <mgulyas86@gmail.com>
@@ -1495,6 +1573,7 @@ Max Ushakov <ushmax@gmail.com>
Maxim Eryomenko <moeryomenko@gmail.com>
Maxim Khitrov <max@mxcrypt.com>
Maxim Pimenov <mpimenov@google.com>
+Maxim Pugachev <pugachev.mm@gmail.com>
Maxim Ushakov <ushakov@google.com>
Maxime de Roucy <maxime.deroucy@gmail.com>
Máximo Cuadros Ortiz <mcuadros@gmail.com>
@@ -1549,6 +1628,7 @@ Michal Bohuslávek <mbohuslavek@gmail.com>
Michal Cierniak <cierniak@google.com>
Michał Derkacz <ziutek@lnet.pl>
Michal Franc <lam.michal.franc@gmail.com>
+Michał Łowicki <mlowicki@gmail.com>
Michal Pristas <michal.pristas@gmail.com>
Michal Rostecki <mrostecki@suse.de>
Michalis Kargakis <michaliskargakis@gmail.com>
@@ -1556,6 +1636,7 @@ Michel Lespinasse <walken@google.com>
Mickael Kerjean <mickael.kerjean@gmail.com>
Mickey Reiss <mickeyreiss@gmail.com>
Miek Gieben <miek@miek.nl> <remigius.gieben@gmail.com>
+Miguel Acero <acero@google.com>
Miguel Mendez <stxmendez@gmail.com>
Miguel Molina <hi@mvader.me>
Mihai Borobocea <MihaiBorobocea@gmail.com>
@@ -1582,6 +1663,7 @@ Mikio Hara <mikioh.mikioh@gmail.com>
Mikkel Krautz <mikkel@krautz.dk> <krautz@gmail.com>
Mikołaj Baranowski <mikolajb@gmail.com>
Milan Knezevic <milan.knezevic@mips.com>
+Milan Patel <bicelot3@gmail.com>
Milutin Jovanović <jovanovic.milutin@gmail.com>
MinJae Kwon <mingrammer@gmail.com>
Miquel Sabaté Solà <mikisabate@gmail.com>
@@ -1603,8 +1685,10 @@ Mrunal Patel <mrunalp@gmail.com>
Muhammad Falak R Wani <falakreyaz@gmail.com>
Muhammed Uluyol <uluyol0@gmail.com>
Muir Manders <muir@mnd.rs>
+Mukesh Sharma <sharma.mukesh439@gmail.com>
Mura Li <mura_li@castech.com.tw>
Mykhailo Lesyk <mikhail@lesyk.org>
+Naman Aggarwal <aggarwal.nam@gmail.com>
Nan Deng <monnand@gmail.com>
Nao Yonashiro <owan.orisano@gmail.com>
Naoki Kanatani <k12naoki@gmail.com>
@@ -1612,6 +1696,7 @@ Nate Wilkinson <nathanwilk7@gmail.com>
Nathan Cantelmo <n.cantelmo@gmail.com>
Nathan Caza <mastercactapus@gmail.com>
Nathan Dias <nathan.dias@orijtech.com>
+Nathan Fiscaletti <nathan.fiscaletti@vrazo.com>
Nathan Humphreys <nkhumphreys@gmail.com>
Nathan John Youngman <nj@nathany.com>
Nathan Otterness <otternes@cs.unc.edu>
@@ -1621,6 +1706,7 @@ Nathan Youngman <git@nathany.com>
Nathan(yinian) Hu <nathanhu@google.com>
Nathaniel Cook <nvcook42@gmail.com>
Naveen Kumar Sangi <naveenkumarsangi@protonmail.com>
+Neeilan Selvalingam <neeilan96@gmail.com>
Neelesh Chandola <neelesh.c98@gmail.com>
Neil Lyons <nwjlyons@googlemail.com>
Neuman Vong <neuman.vong@gmail.com>
@@ -1661,17 +1747,20 @@ Nikita Vanyasin <nikita.vanyasin@gmail.com>
Niklas Schnelle <niklas.schnelle@gmail.com>
Niko Dziemba <niko@dziemba.com>
Nikolay Turpitko <nikolay@turpitko.com>
+Nikson Kanti Paul <nikson.sust@gmail.com>
Nils Larsgård <nilsmagnus@gmail.com>
Nir Soffer <nirsof@gmail.com>
Niranjan Godbole <niranjan8192@gmail.com>
Nishanth Shanmugham <nishanth.gerrard@gmail.com>
Noah Campbell <noahcampbell@gmail.com>
+Noah Goldman <noahg34@gmail.com>
Noble Johnson <noblepoly@gmail.com>
Nodir Turakulov <nodir@google.com>
Noel Georgi <git@frezbo.com>
Norberto Lopes <nlopes.ml@gmail.com>
Norman B. Lancaster <qbradq@gmail.com>
Nuno Cruces <ncruces@users.noreply.github.com>
+Obeyda Djeffal <djefobey@gmail.com>
Odin Ugedal <odin@ugedal.com>
Oleg Bulatov <dmage@yandex-team.ru>
Oleg Vakheta <helginet@gmail.com>
@@ -1689,6 +1778,7 @@ Omar Jarjur <ojarjur@google.com>
Oryan Moshe <iamoryanmoshe@gmail.com>
Osamu TONOMORI <osamingo@gmail.com>
Özgür Kesim <oec-go@kesim.org>
+Pablo Caderno <kaderno@gmail.com>
Pablo Lalloni <plalloni@gmail.com>
Pablo Rozas Larraondo <pablo.larraondo@anu.edu.au>
Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
@@ -1702,6 +1792,8 @@ Parker Moore <parkrmoore@gmail.com>
Parminder Singh <parmsingh101@gmail.com>
Pascal Dierich <pascal@pascaldierich.com>
Pascal S. de Kloe <pascal@quies.net>
+Paschalis Tsilias <paschalis.tsilias@gmail.com>
+Pasi Tähkäpää <pasi.tahkapaa@gmail.com>
Pat Moroney <pat@pat.email>
Patrick Barker <barkerp@vmware.com>
Patrick Crosby <patrick@stathat.com>
@@ -1718,6 +1810,7 @@ Paul A Querna <paul.querna@gmail.com>
Paul Borman <borman@google.com>
Paul Boyd <boyd.paul2@gmail.com>
Paul Chang <paulchang@google.com>
+Paul D. Weber <x0bdev@gmail.com>
Paul Hammond <paul@paulhammond.org>
Paul Hankin <paulhankin@google.com>
Paul Jolly <paul@myitcv.org.uk>
@@ -1743,8 +1836,10 @@ Pavel Zinovkin <pavel.zinovkin@gmail.com>
Pavlo Sumkin <ymkins@gmail.com>
Pawel Knap <pawelknap88@gmail.com>
Pawel Szczur <filemon@google.com>
+Pei Xian Chee <luciolas1991@gmail.com>
Percy Wegmann <ox.to.a.cart@gmail.com>
Perry Abbott <perry.j.abbott@gmail.com>
+Petar Dambovaliev <petar.atanasov.1987@gmail.com>
Petar Maymounkov <petarm@gmail.com>
Peter Armitage <peter.armitage@gmail.com>
Peter Bourgon <peter@bourgon.org>
@@ -1781,6 +1876,7 @@ Philip Hofer <phofer@umich.edu>
Philip K. Warren <pkwarren@gmail.com>
Philip Nelson <me@pnelson.ca>
Philipp Stephani <phst@google.com>
+Pierre Carru <pierre.carru@eshard.com>
Pierre Durand <pierredurand@gmail.com>
Pierre Prinetti <pierreprinetti@gmail.com>
Pierre Roullon <pierre.roullon@gmail.com>
@@ -1789,11 +1885,14 @@ Pieter Droogendijk <pieter@binky.org.uk>
Pietro Gagliardi <pietro10@mac.com>
Piyush Mishra <piyush@codeitout.com>
Plekhanov Maxim <kishtatix@gmail.com>
+Polina Osadcha <polliosa@google.com>
Pontus Leitzler <leitzler@gmail.com>
+Povilas Versockas <p.versockas@gmail.com>
Prasanga Siripala <pj@pjebs.com.au>
Prasanna Swaminathan <prasanna@mediamath.com>
Prashant Agrawal <prashant.a.vjti@gmail.com>
Prashant Varanasi <prashant@prashantv.com>
+Praveen Kumar <praveen+git@kumar.in>
Pravendra Singh <hackpravj@gmail.com>
Preetam Jinka <pj@preet.am>
Pure White <wu.purewhite@gmail.com>
@@ -1804,6 +1903,7 @@ Quan Yong Zhai <qyzhai@gmail.com>
Quentin Perez <qperez@ocs.online.net>
Quentin Renard <contact@asticode.com>
Quentin Smith <quentin@golang.org>
+Quey-Liang Kao <s101062801@m101.nthu.edu.tw>
Quinn Slack <sqs@sourcegraph.com>
Quinten Yearsley <qyearsley@chromium.org>
Quoc-Viet Nguyen <afelion@gmail.com>
@@ -1831,6 +1931,7 @@ Reilly Watson <reillywatson@gmail.com>
Reinaldo de Souza Jr <juniorz@gmail.com>
Remi Gillig <remigillig@gmail.com>
Rémy Oudompheng <oudomphe@phare.normalesup.org> <remyoudompheng@gmail.com>
+Ren Ogaki <re.yuz77777@gmail.com>
Rens Rikkerink <Ikkerens@users.noreply.github.com>
Rhys Hiltner <rhys@justin.tv>
Ricardo Padilha <ricardospadilha@gmail.com>
@@ -1842,6 +1943,8 @@ Richard Eric Gavaletz <gavaletz@gmail.com>
Richard Gibson <richard.gibson@gmail.com>
Richard Miller <miller.research@gmail.com>
Richard Musiol <mail@richard-musiol.de> <neelance@gmail.com>
+Richard Ulmer <codesoap@mailbox.org>
+Richard Wilkes <wilkes@me.com>
Rick Arnold <rickarnoldjr@gmail.com>
Rick Hudson <rlh@golang.org>
Rick Sayre <whorfin@gmail.com>
@@ -1860,6 +1963,7 @@ Robert Figueiredo <robfig@gmail.com>
Robert Griesemer <gri@golang.org>
Robert Hencke <robert.hencke@gmail.com>
Robert Iannucci <iannucci@google.com>
+Robert Kuska <rkuska@gmail.com>
Robert Obryk <robryk@gmail.com>
Robert Sesek <rsesek@google.com>
Robert Snedegar <roberts@google.com>
@@ -1878,6 +1982,7 @@ Roger Pau Monné <royger@gmail.com>
Roger Peppe <rogpeppe@gmail.com>
Rohan Challa <rohan@golang.org>
Rohan Verma <rohanverma2004@gmail.com>
+Rohith Ravi <entombedvirus@gmail.com>
Roland Illig <roland.illig@gmx.de>
Roland Shoemaker <rolandshoemaker@gmail.com>
Romain Baugue <romain.baugue@elwinar.com>
@@ -1887,6 +1992,7 @@ Roman Shchekin <mrqtros@gmail.com>
Ron Hashimoto <mail@h2so5.net>
Ron Minnich <rminnich@gmail.com>
Ross Chater <rdchater@gmail.com>
+Ross Kinsey <rossikinsey@gmail.com>
Ross Light <light@google.com> <rlight2@gmail.com>
Ross Smith II <ross@smithii.com>
Rowan Marshall <rowanajmarshall@gmail.com>
@@ -1921,6 +2027,8 @@ Sakeven Jiang <jc5930@sina.cn>
Salmān Aljammāz <s@0x65.net>
Sam Arnold <sarnold64@bloomberg.net>
Sam Boyer <tech@samboyer.org>
+Sam Chen <chenxsan@gmail.com>
+Sam Cross <samgcdev@gmail.com>
Sam Ding <samding@ca.ibm.com>
Sam Hug <samuel.b.hug@gmail.com>
Sam Thorogood <thorogood@google.com> <sam.thorogood@gmail.com>
@@ -1972,6 +2080,7 @@ Sergey 'SnakE' Gromov <snake.scaly@gmail.com>
Sergey Arseev <sergey.arseev@intel.com>
Sergey Dobrodey <sergey.dobrodey@synesis.ru>
Sergey Frolov <sfrolov@google.com>
+Sergey Glushchenko <gsserge@gmail.com>
Sergey Ivanov <ser1325@gmail.com>
Sergey Lukjanov <me@slukjanov.name>
Sergey Mishin <sergeymishine@gmail.com>
@@ -1987,7 +2096,9 @@ Seth Vargo <sethvargo@gmail.com>
Shahar Kohanim <skohanim@gmail.com>
Shamil Garatuev <garatuev@gmail.com>
Shane Hansen <shanemhansen@gmail.com>
+Shang Jian Ding <sding3@ncsu.edu>
Shaozhen Ding <dsz0111@gmail.com>
+Shaquille Wyan Que <shaqqywyan@gmail.com>
Shaun Dunning <shaun.dunning@uservoice.com>
Shawn Elliott <selliott@microsoft.com>
Shawn Ledbetter <sledbetter@google.com>
@@ -2008,6 +2119,7 @@ Shubham Sharma <shubham.sha12@gmail.com>
Shun Fan <sfan@google.com>
Silvan Jegen <s.jegen@gmail.com>
Simarpreet Singh <simar@linux.com>
+Simon Drake <simondrake1990@gmail.com>
Simon Ferquel <simon.ferquel@docker.com>
Simon Jefford <simon.jefford@gmail.com>
Simon Rawet <simon@rawet.se>
@@ -2018,6 +2130,8 @@ Sina Siadat <siadat@gmail.com>
Sjoerd Siebinga <sjoerd.siebinga@gmail.com>
Sokolov Yura <funny.falcon@gmail.com>
Song Gao <song@gao.io>
+Soojin Nam <jsunam@gmail.com>
+Søren L. Hansen <soren@linux2go.dk>
Spencer Kocot <spencerkocot@gmail.com>
Spencer Nelson <s@spenczar.com>
Spencer Tung <spencertung@google.com>
@@ -2074,6 +2188,7 @@ Taavi Kivisik <taavi.kivisik@gmail.com>
Tad Fisher <tadfisher@gmail.com>
Tad Glines <tad.glines@gmail.com>
Tadas Valiukas <tadovas@gmail.com>
+Tadeo Kondrak <me@tadeo.ca>
Taesu Pyo <pyotaesu@gmail.com>
Tai Le <letientai299@gmail.com>
Taj Khattra <taj.khattra@gmail.com>
@@ -2083,6 +2198,7 @@ Takeshi YAMANASHI <9.nashi@gmail.com>
Takuto Ikuta <tikuta@google.com>
Takuya Ueda <uedatakuya@gmail.com>
Tal Shprecher <tshprecher@gmail.com>
+Tamás Gulácsi <tgulacsi78@gmail.com>
Tamir Duberstein <tamird@gmail.com>
Tao Qingyun <qingyunha@gmail.com>
Tao Shen <shentaoskyking@gmail.com>
@@ -2102,6 +2218,7 @@ Tetsuo Kiso <tetsuokiso9@gmail.com>
Than McIntosh <thanm@google.com>
Thanabodee Charoenpiriyakij <wingyminus@gmail.com>
Thanatat Tamtan <acoshift@gmail.com>
+The Hatsune Daishi <nao20010128@gmail.com>
Thiago Avelino <t@avelino.xxx>
Thiago Fransosi Farina <thiago.farina@gmail.com> <tfarina@chromium.org>
Thomas Alan Copeland <talan.copeland@gmail.com>
@@ -2128,9 +2245,11 @@ Tim Ebringer <tim.ebringer@gmail.com>
Tim Heckman <t@heckman.io>
Tim Henderson <tim.tadh@gmail.com>
Tim Hockin <thockin@google.com>
+Tim Möhlmann <muhlemmer@gmail.com>
Tim Swast <swast@google.com>
Tim Wright <tenortim@gmail.com>
Tim Xu <xiaoxubeii@gmail.com>
+Timmy Douglas <timmyd983@gmail.com>
Timo Savola <timo.savola@gmail.com>
Timo Truyts <alkaloid.btx@gmail.com>
Timothy Studd <tim@timstudd.com>
@@ -2149,6 +2268,7 @@ Tom Lanyon <tomlanyon@google.com>
Tom Levy <tomlevy93@gmail.com>
Tom Limoncelli <tal@whatexit.org>
Tom Linford <tomlinford@gmail.com>
+Tom Parkin <tom.parkin@gmail.com>
Tom Payne <twpayne@gmail.com>
Tom Szymanski <tgs@google.com>
Tom Thorogood <me+google@tomthorogood.co.uk>
@@ -2162,6 +2282,7 @@ Tony Reix <tony.reix@bull.net>
Tony Walker <walkert.uk@gmail.com>
Tooru Takahashi <tooru.takahashi134@gmail.com>
Tor Andersson <tor.andersson@gmail.com>
+Torben Schinke <torben.schinke@neotos.de>
Tormod Erevik Lea <tormodlea@gmail.com>
Toshihiro Shiino <shiino.toshihiro@gmail.com>
Toshiki Shima <hayabusa1419@gmail.com>
@@ -2178,12 +2299,15 @@ Tristan Ooohry <ooohry@gmail.com>
Tristan Rice <rice@fn.lc>
Troels Thomsen <troels@thomsen.io>
Trung Nguyen <trung.n.k@gmail.com>
+Tsuji Daishiro <dram.dt.shonan@gmail.com>
Tudor Golubenco <tudor.g@gmail.com>
Tugdual Saunier <tugdual.saunier@gmail.com>
Tuo Shan <sturbo89@gmail.com> <shantuo@google.com>
Tyler Bui-Palsulich <tpalsulich@google.com>
Tyler Bunnell <tylerbunnell@gmail.com>
Tyler Treat <ttreat31@gmail.com>
+Tyson Andre <tysonandre775@gmail.com>
+Tzach Shabtay <tzachshabtay@gmail.com>
Tzu-Jung Lee <roylee17@currant.com>
Udalov Max <re.udalov@gmail.com>
Ugorji Nwoke <ugorji@gmail.com>
@@ -2217,6 +2341,7 @@ Visweswara R <r.visweswara@gmail.com>
Vitaly Zdanevich <zdanevich.vitaly@ya.ru>
Vitor De Mario <vitordemario@gmail.com>
Vivek Sekhar <vsekhar@google.com>
+Vivian Liang <vliang88@gmail.com>
Vlad Krasnov <vlad@cloudflare.com>
Vladimir Evgrafov <evgrafov.vladimir@gmail.com>
Vladimir Kovpak <cn007b@gmail.com>
@@ -2231,6 +2356,7 @@ Volodymyr Paprotski <vpaprots@ca.ibm.com>
W. Trevor King <wking@tremily.us>
Wade Simmons <wade@wades.im>
Wagner Riffel <wgrriffel@gmail.com>
+Walt Della <walt@javins.net>
Walter Poupore <wpoupore@google.com>
Wander Lairson Costa <wcosta@mozilla.com>
Wang Xuerui <git@xen0n.name>
@@ -2274,12 +2400,15 @@ Xudong Zheng <7pkvm5aw@slicealias.com>
Xuyang Kang <xuyangkang@gmail.com>
Yamagishi Kazutoshi <ykzts@desire.sh>
Yan Zou <yzou@google.com>
+Yang Hau <vulxj0j8j8@gmail.com>
Yang Tian <linuxty@gmail.com>
Yann Hodique <yhodique@google.com>
Yann Kerhervé <yann.kerherve@gmail.com>
Yann Salaün <yannsalaun1@gmail.com>
+Yannic Bonenberger <contact@yannic-bonenberger.com>
Yao Zhang <lunaria21@gmail.com>
Yaron de Leeuw <jarondl@google.com>
+Yaroslav Vorobiov <yar.vorobiov@gmail.com>
Yasha Bubnov <girokompass@gmail.com>
Yasser Abdolmaleki <yasser@yasser.ca>
Yasuharu Goto <matope.ono@gmail.com>
@@ -2298,6 +2427,7 @@ Yoshiyuki Mineo <yoshiyuki.mineo@gmail.com>
Yosuke Akatsuka <yosuke.akatsuka@gmail.com>
Yu Heng Zhang <annita.zhang@cn.ibm.com>
Yu Xuan Zhang <zyxsh@cn.ibm.com>
+Yuichi Kishimoto <yk2220s@gmail.com>
Yuichi Nishiwaki <yuichi.nishiwaki@gmail.com>
Yuji Yaginuma <yuuji.yaginuma@gmail.com>
Yuki OKUSHI <huyuumi.dev@gmail.com>
@@ -2318,6 +2448,7 @@ Zak <zrjknill@gmail.com>
Zakatell Kanda <hi@zkanda.io>
Zellyn Hunter <zellyn@squareup.com> <zellyn@gmail.com>
Zev Goldstein <zev.goldstein@gmail.com>
+Zhang Boyang <zhangboyang.id@gmail.com>
Zheng Dayu <davidzheng23@gmail.com>
Zheng Xu <zheng.xu@arm.com>
Zhengyu He <hzy@google.com>
diff --git a/api/go1.15.txt b/api/go1.15.txt
index b51837cf38..dd90506eba 100644
--- a/api/go1.15.txt
+++ b/api/go1.15.txt
@@ -112,8 +112,6 @@ pkg debug/pe, const IMAGE_SUBSYSTEM_WINDOWS_GUI = 2
pkg debug/pe, const IMAGE_SUBSYSTEM_WINDOWS_GUI ideal-int
pkg debug/pe, const IMAGE_SUBSYSTEM_XBOX = 14
pkg debug/pe, const IMAGE_SUBSYSTEM_XBOX ideal-int
-pkg go/printer, const StdFormat = 16
-pkg go/printer, const StdFormat Mode
pkg math/big, method (*Int) FillBytes([]uint8) []uint8
pkg net, method (*Resolver) LookupIP(context.Context, string, string) ([]IP, error)
pkg net/url, method (*URL) EscapedFragment() string
diff --git a/doc/go1.15.html b/doc/go1.15.html
index ce6894d2e0..8872d71138 100644
--- a/doc/go1.15.html
+++ b/doc/go1.15.html
@@ -14,13 +14,21 @@ Do not send CLs removing the interior tags from such phrases.
main ul li { margin: 0.5em 0; }
</style>
-<h2 id="introduction">DRAFT RELEASE NOTES — Introduction to Go 1.15</h2>
+<h2 id="introduction">Introduction to Go 1.15</h2>
<p>
- <strong>
- Go 1.15 is not yet released. These are work-in-progress
- release notes. Go 1.15 is expected to be released in August 2020.
- </strong>
+ The latest Go release, version 1.15, arrives six months after <a href="go1.14">Go 1.14</a>.
+ Most of its changes are in the implementation of the toolchain, runtime, and libraries.
+ As always, the release maintains the Go 1 <a href="/doc/go1compat.html">promise of compatibility</a>.
+ We expect almost all Go programs to continue to compile and run as before.
+</p>
+
+<p>
+ Go 1.15 includes <a href="#linker">substantial improvements to the linker</a>,
+ improves <a href="#runtime">allocation for small objects at high core counts</a>, and
+ deprecates <a href="#commonname">X.509 CommonName</a>.
+ <code>GOPROXY</code> now supports skipping proxies that return errors and
+ a new <a href="#time/tzdata">embedded tzdata package</a> has been added.
</p>
<h2 id="language">Changes to the language</h2>
@@ -94,6 +102,16 @@ Do not send CLs removing the interior tags from such phrases.
preemption.
</p>
+<h3 id="386">386</h3>
+
+<p><!-- golang.org/issue/40255 -->
+ Go 1.15 is the last release to support x87-only floating-point
+ hardware (<code>GO386=387</code>). Future releases will require at
+ least SSE2 support on 386, raising Go's
+ minimum <code>GOARCH=386</code> requirement to the Intel Pentium 4
+ (released in 2000) or AMD Opteron/Athlon 64 (released in 2003).
+</p>
+
<h2 id="tools">Tools</h2>
<h3 id="go-command">Go command</h3>
@@ -336,8 +354,10 @@ Do not send CLs removing the interior tags from such phrases.
</p>
<p><!-- CL 207877 -->
- TODO: <a href="https://golang.org/cl/207877">https://golang.org/cl/207877</a>: Revert -buildmode=pie to internal linking.
- The linker defaults to internal linking mode for PIE on linux/amd64 and linux/arm64, which does require a C linker.
+ The linker now defaults to internal linking mode
+ for <code>-buildmode=pie</code> on
+ <code>linux/amd64</code> and <code>linux/arm64</code>, so these
+ configurations no longer require a C linker.
</p>
<h2 id="objdump">Objdump</h2>
@@ -374,6 +394,23 @@ Do not send CLs removing the interior tags from such phrases.
documentation</a> for more information.
</p>
+<h3 id="commonname">X.509 CommonName deprecation</h3>
+
+<p><!-- CL 231379 -->
+ The deprecated, legacy behavior of treating the <code>CommonName</code>
+ field on X.509 certificates as a host name when no Subject Alternative Names
+ are present is now disabled by default. It can be temporarily re-enabled by
+ adding the value <code>x509ignoreCN=0</code> to the <code>GODEBUG</code>
+ environment variable.
+</p>
+
+<p>
+ Note that if the <code>CommonName</code> is an invalid host name, it's always
+ ignored, regardless of <code>GODEBUG</code> settings. Invalid names include
+ those with any characters other than letters, digits, hyphens and underscores,
+ and those with empty labels or trailing dots.
+</p>
+
<h3 id="minor_library_changes">Minor changes to the library</h3>
<p>
@@ -495,6 +532,17 @@ Do not send CLs removing the interior tags from such phrases.
fields <code>OCSPResponse</code> and <code>SignedCertificateTimestamps</code>
are now repopulated on client-side resumed connections.
</p>
+
+ <p><!-- CL 227840 -->
+ <a href="/pkg/crypto/tls/#Conn"><code>tls.Conn</code></a>
+ now returns an opaque error on permanently broken connections, wrapping
+ the temporary
+ <a href="/pkg/net/http/#Error"><code>net.Error</code></a>. To access the
+ original <code>net.Error</code>, use
+ <a href="/pkg/errors/#As"><code>errors.As</code></a> (or
+ <a href="/pkg/errors/#Unwrap"><code>errors.Unwrap</code></a>) instead of a
+ type assertion.
+ </p>
</dd>
</dl><!-- crypto/tls -->
@@ -511,15 +559,6 @@ Do not send CLs removing the interior tags from such phrases.
certificates with trailing dots.
</p>
- <p><!-- CL 231379 -->
- The deprecated, legacy behavior of treating the <code>CommonName</code>
- field as a hostname when no Subject Alternative Names are present is now
- disabled by default. It can be temporarily re-enabled by adding the value
- <code>x509ignoreCN=0</code> to the <code>GODEBUG</code> environment
- variable. If the <code>CommonName</code> is an invalid hostname, it's
- always ignored.
- </p>
-
<p><!-- CL 217298 -->
The new <a href="/pkg/crypto/x509/#CreateRevocationList"><code>CreateRevocationList</code></a>
function and <a href="/pkg/crypto/x509/#RevocationList"><code>RevocationList</code></a> type
@@ -618,11 +657,6 @@ Do not send CLs removing the interior tags from such phrases.
<dl id="encoding/json"><dt><a href="/pkg/encoding/json/">encoding/json</a></dt>
<dd>
- <p><!-- CL 191783 -->
- Decoding a JSON array into a slice no longer reuses any existing slice elements,
- following the rules that the package documentation already stated.
- </p>
-
<p><!-- CL 199837 -->
The package now has an internal limit to the maximum depth of
nesting when decoding. This reduces the possibility that a
@@ -635,8 +669,8 @@ Do not send CLs removing the interior tags from such phrases.
<dl id="flag"><dt><a href="/pkg/flag/">flag</a></dt>
<dd>
<p><!-- CL 221427 -->
- When the flag package sees <code>-h</code> or <code>-help</code>, and
- those flags are not defined, it now prints a usage message.
+ When the <code>flag</code> package sees <code>-h</code> or <code>-help</code>,
+ and those flags are not defined, it now prints a usage message.
If the <a href="/pkg/flag/#FlagSet"><code>FlagSet</code></a> was created with
<a href="/pkg/flag/#ExitOnError"><code>ExitOnError</code></a>,
<a href="/pkg/flag/#FlagSet.Parse"><code>FlagSet.Parse</code></a> would then
@@ -656,15 +690,18 @@ Do not send CLs removing the interior tags from such phrases.
</dd>
</dl><!-- fmt -->
-<dl id="go/printer"><dt><a href="/pkg/go/printer/">go/printer</a></dt>
+<dl id="go/format"><dt><a href="/pkg/go/format/">go/format</a></dt>
<dd>
- <p><!-- CL 231461 -->
- The new <a href="/pkg/go/printer/#Mode"><code>Mode</code></a>
- value <a href="/pkg/go/printer/#StdFormat"><code>StdFormat</code></a>
- directs the printer to apply standard formatting changes while
- printing the output.
+ <p><!-- golang.org/issue/37476, CL 231461, CL 240683 -->
+ The <a href="/pkg/go/format/#Source"><code>Source</code></a> and
+ <a href="/pkg/go/format/#Node"><code>Node</code></a> functions
+ now canonicalize number literal prefixes and exponents as part
+ of formatting Go source code. This matches the behavior of the
+ <a href="/pkg/cmd/gofmt/"><code>gofmt</code></a> command as it
+ was implemented <a href="/doc/go1.13#gofmt">since Go 1.13</a>.
+ </p>
</dd>
-</dl><!-- go/printer -->
+</dl><!-- go/format -->
<dl id="html/template"><dt><a href="/pkg/html/template/">html/template</a></dt>
<dd>
@@ -698,6 +735,16 @@ Do not send CLs removing the interior tags from such phrases.
</dd>
</dl><!-- math/big -->
+<dl id="math/cmplx"><dt><a href="/pkg/math/cmplx/">math/cmplx</a></dt>
+ <dd>
+ <p><!-- CL 220689 -->
+ The functions in this package were updated to conform to the C99 standard
+ (Annex G IEC 60559-compatible complex arithmetic) with respect to handling
+ of special arguments such as infinity, NaN and signed zero.
+ </p>
+ </dd>
+</dl><!-- math/cmplx-->
+
<dl id="net"><dt><a href="/pkg/net/">net</a></dt>
<dd>
<p><!-- CL 228645 -->
@@ -868,9 +915,9 @@ Do not send CLs removing the interior tags from such phrases.
<dl id="pkg-runtime-pprof"><dt><a href="/pkg/runtime/pprof/">runtime/pprof</a></dt>
<dd>
<p><!-- CL 189318 -->
- The goroutine profile includes the profile labels associated with each goroutine
- at the time of profiling. This feature is not yet implemented for the profile
- reported with <code>debug=2</code>.
+ The goroutine profile now includes the profile labels associated with each
+ goroutine at the time of profiling. This feature is not yet implemented for
+ the profile reported with <code>debug=2</code>.
</p>
</dd>
</dl>
@@ -901,6 +948,7 @@ Do not send CLs removing the interior tags from such phrases.
<a href="/pkg/sync/#Map.Delete"><code>Map.Delete</code></a>
is more efficient.
</p>
+ </dd>
</dl><!-- sync -->
<dl id="syscall"><dt><a href="/pkg/syscall/">syscall</a></dt>
@@ -956,7 +1004,8 @@ Do not send CLs removing the interior tags from such phrases.
</p>
<p><!-- CL 229085 -->
- TODO: <a href="https://golang.org/cl/229085">https://golang.org/cl/229085</a>: reformat test chatty output
+ <code>go</code> <code>test</code> <code>-v</code> now groups output by
+ test name, rather than printing the test name on each line.
</p>
</dd>
</dl><!-- testing -->
diff --git a/misc/cgo/test/test.go b/misc/cgo/test/test.go
index 8c69ad91ac..35bc3a1447 100644
--- a/misc/cgo/test/test.go
+++ b/misc/cgo/test/test.go
@@ -901,6 +901,12 @@ typedef struct S32579 { unsigned char data[1]; } S32579;
// issue 38649
// Test that #define'd type aliases work.
#define netbsd_gid unsigned int
+
+// issue 40494
+// Inconsistent handling of tagged enum and union types.
+enum Enum40494 { X_40494 };
+union Union40494 { int x; };
+void issue40494(enum Enum40494 e, union Union40494* up) {}
*/
import "C"
@@ -2204,3 +2210,10 @@ var issue38649 C.netbsd_gid = 42
// issue 39877
var issue39877 *C.void = nil
+
+// issue 40494
+// No runtime test; just make sure it compiles.
+
+func Issue40494() {
+ C.issue40494(C.enum_Enum40494(C.X_40494), (*C.union_Union40494)(nil))
+}
diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go
index f8dabbe7a0..5e0893784b 100644
--- a/misc/cgo/testshared/shared_test.go
+++ b/misc/cgo/testshared/shared_test.go
@@ -462,6 +462,7 @@ func TestTrivialExecutable(t *testing.T) {
run(t, "trivial executable", "../../bin/trivial")
AssertIsLinkedTo(t, "../../bin/trivial", soname)
AssertHasRPath(t, "../../bin/trivial", gorootInstallDir)
+ checkSize(t, "../../bin/trivial", 100000) // it is 19K on linux/amd64, 100K should be enough
}
// Build a trivial program in PIE mode that links against the shared runtime and check it runs.
@@ -470,6 +471,18 @@ func TestTrivialExecutablePIE(t *testing.T) {
run(t, "trivial executable", "./trivial.pie")
AssertIsLinkedTo(t, "./trivial.pie", soname)
AssertHasRPath(t, "./trivial.pie", gorootInstallDir)
+ checkSize(t, "./trivial.pie", 100000) // it is 19K on linux/amd64, 100K should be enough
+}
+
+// Check that the file size does not exceed a limit.
+func checkSize(t *testing.T, f string, limit int64) {
+ fi, err := os.Stat(f)
+ if err != nil {
+ t.Fatalf("stat failed: %v", err)
+ }
+ if sz := fi.Size(); sz > limit {
+ t.Errorf("file too large: got %d, want <= %d", sz, limit)
+ }
}
// Build a division test program and check it runs.
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 6c221473e0..4064f0ae41 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -123,7 +123,9 @@ func (p *Package) writeDefs() {
// Moreover, empty file name makes compile emit no source debug info at all.
var buf bytes.Buffer
noSourceConf.Fprint(&buf, fset, def.Go)
- if bytes.HasPrefix(buf.Bytes(), []byte("_Ctype_")) {
+ if bytes.HasPrefix(buf.Bytes(), []byte("_Ctype_")) ||
+ strings.HasPrefix(name, "_Ctype_enum_") ||
+ strings.HasPrefix(name, "_Ctype_union_") {
// This typedef is of the form `typedef a b` and should be an alias.
fmt.Fprintf(fgo2, "= ")
}
diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go
index 47cb422ab1..9d8a0920b3 100644
--- a/src/cmd/compile/internal/amd64/ssa.go
+++ b/src/cmd/compile/internal/amd64/ssa.go
@@ -874,7 +874,11 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
ssa.OpAMD64SUBLloadidx1, ssa.OpAMD64SUBLloadidx4, ssa.OpAMD64SUBLloadidx8, ssa.OpAMD64SUBQloadidx1, ssa.OpAMD64SUBQloadidx8,
ssa.OpAMD64ANDLloadidx1, ssa.OpAMD64ANDLloadidx4, ssa.OpAMD64ANDLloadidx8, ssa.OpAMD64ANDQloadidx1, ssa.OpAMD64ANDQloadidx8,
ssa.OpAMD64ORLloadidx1, ssa.OpAMD64ORLloadidx4, ssa.OpAMD64ORLloadidx8, ssa.OpAMD64ORQloadidx1, ssa.OpAMD64ORQloadidx8,
- ssa.OpAMD64XORLloadidx1, ssa.OpAMD64XORLloadidx4, ssa.OpAMD64XORLloadidx8, ssa.OpAMD64XORQloadidx1, ssa.OpAMD64XORQloadidx8:
+ ssa.OpAMD64XORLloadidx1, ssa.OpAMD64XORLloadidx4, ssa.OpAMD64XORLloadidx8, ssa.OpAMD64XORQloadidx1, ssa.OpAMD64XORQloadidx8,
+ ssa.OpAMD64ADDSSloadidx1, ssa.OpAMD64ADDSSloadidx4, ssa.OpAMD64ADDSDloadidx1, ssa.OpAMD64ADDSDloadidx8,
+ ssa.OpAMD64SUBSSloadidx1, ssa.OpAMD64SUBSSloadidx4, ssa.OpAMD64SUBSDloadidx1, ssa.OpAMD64SUBSDloadidx8,
+ ssa.OpAMD64MULSSloadidx1, ssa.OpAMD64MULSSloadidx4, ssa.OpAMD64MULSDloadidx1, ssa.OpAMD64MULSDloadidx8,
+ ssa.OpAMD64DIVSSloadidx1, ssa.OpAMD64DIVSSloadidx4, ssa.OpAMD64DIVSDloadidx1, ssa.OpAMD64DIVSDloadidx8:
p := s.Prog(v.Op.Asm())
r, i := v.Args[1].Reg(), v.Args[2].Reg()
diff --git a/src/cmd/compile/internal/ssa/addressingmodes.go b/src/cmd/compile/internal/ssa/addressingmodes.go
index 78c979b7cb..97a5ab4f03 100644
--- a/src/cmd/compile/internal/ssa/addressingmodes.go
+++ b/src/cmd/compile/internal/ssa/addressingmodes.go
@@ -321,6 +321,23 @@ var combine = map[[2]Op]Op{
[2]Op{OpAMD64XORQconstmodify, OpAMD64LEAQ1}: OpAMD64XORQconstmodifyidx1,
[2]Op{OpAMD64XORQconstmodify, OpAMD64LEAQ8}: OpAMD64XORQconstmodifyidx8,
+ [2]Op{OpAMD64ADDSSload, OpAMD64LEAQ1}: OpAMD64ADDSSloadidx1,
+ [2]Op{OpAMD64ADDSSload, OpAMD64LEAQ4}: OpAMD64ADDSSloadidx4,
+ [2]Op{OpAMD64ADDSDload, OpAMD64LEAQ1}: OpAMD64ADDSDloadidx1,
+ [2]Op{OpAMD64ADDSDload, OpAMD64LEAQ8}: OpAMD64ADDSDloadidx8,
+ [2]Op{OpAMD64SUBSSload, OpAMD64LEAQ1}: OpAMD64SUBSSloadidx1,
+ [2]Op{OpAMD64SUBSSload, OpAMD64LEAQ4}: OpAMD64SUBSSloadidx4,
+ [2]Op{OpAMD64SUBSDload, OpAMD64LEAQ1}: OpAMD64SUBSDloadidx1,
+ [2]Op{OpAMD64SUBSDload, OpAMD64LEAQ8}: OpAMD64SUBSDloadidx8,
+ [2]Op{OpAMD64MULSSload, OpAMD64LEAQ1}: OpAMD64MULSSloadidx1,
+ [2]Op{OpAMD64MULSSload, OpAMD64LEAQ4}: OpAMD64MULSSloadidx4,
+ [2]Op{OpAMD64MULSDload, OpAMD64LEAQ1}: OpAMD64MULSDloadidx1,
+ [2]Op{OpAMD64MULSDload, OpAMD64LEAQ8}: OpAMD64MULSDloadidx8,
+ [2]Op{OpAMD64DIVSSload, OpAMD64LEAQ1}: OpAMD64DIVSSloadidx1,
+ [2]Op{OpAMD64DIVSSload, OpAMD64LEAQ4}: OpAMD64DIVSSloadidx4,
+ [2]Op{OpAMD64DIVSDload, OpAMD64LEAQ1}: OpAMD64DIVSDloadidx1,
+ [2]Op{OpAMD64DIVSDload, OpAMD64LEAQ8}: OpAMD64DIVSDloadidx8,
+
// 386
[2]Op{Op386MOVBload, Op386ADDL}: Op386MOVBloadidx1,
[2]Op{Op386MOVWload, Op386ADDL}: Op386MOVWloadidx1,
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
index cd9cb515c0..a3b29049df 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
@@ -149,14 +149,15 @@ func init() {
gpstorexchg = regInfo{inputs: []regMask{gp, gpspsb, 0}, outputs: []regMask{gp}}
cmpxchg = regInfo{inputs: []regMask{gp, ax, gp, 0}, outputs: []regMask{gp, 0}, clobbers: ax}
- fp01 = regInfo{inputs: nil, outputs: fponly}
- fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
- fp31 = regInfo{inputs: []regMask{fp, fp, fp}, outputs: fponly}
- fp21load = regInfo{inputs: []regMask{fp, gpspsb, 0}, outputs: fponly}
- fpgp = regInfo{inputs: fponly, outputs: gponly}
- gpfp = regInfo{inputs: gponly, outputs: fponly}
- fp11 = regInfo{inputs: fponly, outputs: fponly}
- fp2flags = regInfo{inputs: []regMask{fp, fp}}
+ fp01 = regInfo{inputs: nil, outputs: fponly}
+ fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
+ fp31 = regInfo{inputs: []regMask{fp, fp, fp}, outputs: fponly}
+ fp21load = regInfo{inputs: []regMask{fp, gpspsb, 0}, outputs: fponly}
+ fp21loadidx = regInfo{inputs: []regMask{fp, gpspsb, gpspsb, 0}, outputs: fponly}
+ fpgp = regInfo{inputs: fponly, outputs: gponly}
+ gpfp = regInfo{inputs: gponly, outputs: fponly}
+ fp11 = regInfo{inputs: fponly, outputs: fponly}
+ fp2flags = regInfo{inputs: []regMask{fp, fp}}
fpload = regInfo{inputs: []regMask{gpspsb, 0}, outputs: fponly}
fploadidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}, outputs: fponly}
@@ -201,6 +202,23 @@ func init() {
{name: "DIVSSload", argLength: 3, reg: fp21load, asm: "DIVSS", aux: "SymOff", resultInArg0: true, faultOnNilArg1: true, symEffect: "Read"}, // fp32 arg0 / tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{name: "DIVSDload", argLength: 3, reg: fp21load, asm: "DIVSD", aux: "SymOff", resultInArg0: true, faultOnNilArg1: true, symEffect: "Read"}, // fp64 arg0 / tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
+ {name: "ADDSSloadidx1", argLength: 4, reg: fp21loadidx, asm: "ADDSS", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 + tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
+ {name: "ADDSSloadidx4", argLength: 4, reg: fp21loadidx, asm: "ADDSS", scale: 4, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 + tmp, tmp loaded from arg1+4*arg2+auxint+aux, arg3 = mem
+ {name: "ADDSDloadidx1", argLength: 4, reg: fp21loadidx, asm: "ADDSD", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 + tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
+ {name: "ADDSDloadidx8", argLength: 4, reg: fp21loadidx, asm: "ADDSD", scale: 8, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 + tmp, tmp loaded from arg1+8*arg2+auxint+aux, arg3 = mem
+ {name: "SUBSSloadidx1", argLength: 4, reg: fp21loadidx, asm: "SUBSS", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 - tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
+ {name: "SUBSSloadidx4", argLength: 4, reg: fp21loadidx, asm: "SUBSS", scale: 4, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 - tmp, tmp loaded from arg1+4*arg2+auxint+aux, arg3 = mem
+ {name: "SUBSDloadidx1", argLength: 4, reg: fp21loadidx, asm: "SUBSD", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 - tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
+ {name: "SUBSDloadidx8", argLength: 4, reg: fp21loadidx, asm: "SUBSD", scale: 8, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 - tmp, tmp loaded from arg1+8*arg2+auxint+aux, arg3 = mem
+ {name: "MULSSloadidx1", argLength: 4, reg: fp21loadidx, asm: "MULSS", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 * tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
+ {name: "MULSSloadidx4", argLength: 4, reg: fp21loadidx, asm: "MULSS", scale: 4, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 * tmp, tmp loaded from arg1+4*arg2+auxint+aux, arg3 = mem
+ {name: "MULSDloadidx1", argLength: 4, reg: fp21loadidx, asm: "MULSD", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 * tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
+ {name: "MULSDloadidx8", argLength: 4, reg: fp21loadidx, asm: "MULSD", scale: 8, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 * tmp, tmp loaded from arg1+8*arg2+auxint+aux, arg3 = mem
+ {name: "DIVSSloadidx1", argLength: 4, reg: fp21loadidx, asm: "DIVSS", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 / tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
+ {name: "DIVSSloadidx4", argLength: 4, reg: fp21loadidx, asm: "DIVSS", scale: 4, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 / tmp, tmp loaded from arg1+4*arg2+auxint+aux, arg3 = mem
+ {name: "DIVSDloadidx1", argLength: 4, reg: fp21loadidx, asm: "DIVSD", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 / tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
+ {name: "DIVSDloadidx8", argLength: 4, reg: fp21loadidx, asm: "DIVSD", scale: 8, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 / tmp, tmp loaded from arg1+8*arg2+auxint+aux, arg3 = mem
+
// binary ops
{name: "ADDQ", argLength: 2, reg: gp21sp, asm: "ADDQ", commutative: true, clobberFlags: true}, // arg0 + arg1
{name: "ADDL", argLength: 2, reg: gp21sp, asm: "ADDL", commutative: true, clobberFlags: true}, // arg0 + arg1
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index d27682e3b3..9efa1bfcc4 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -572,6 +572,22 @@ const (
OpAMD64MULSDload
OpAMD64DIVSSload
OpAMD64DIVSDload
+ OpAMD64ADDSSloadidx1
+ OpAMD64ADDSSloadidx4
+ OpAMD64ADDSDloadidx1
+ OpAMD64ADDSDloadidx8
+ OpAMD64SUBSSloadidx1
+ OpAMD64SUBSSloadidx4
+ OpAMD64SUBSDloadidx1
+ OpAMD64SUBSDloadidx8
+ OpAMD64MULSSloadidx1
+ OpAMD64MULSSloadidx4
+ OpAMD64MULSDloadidx1
+ OpAMD64MULSDloadidx8
+ OpAMD64DIVSSloadidx1
+ OpAMD64DIVSSloadidx4
+ OpAMD64DIVSDloadidx1
+ OpAMD64DIVSDloadidx8
OpAMD64ADDQ
OpAMD64ADDL
OpAMD64ADDQconst
@@ -6577,6 +6593,310 @@ var opcodeTable = [...]opInfo{
},
},
{
+ name: "ADDSSloadidx1",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.AADDSS,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "ADDSSloadidx4",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.AADDSS,
+ scale: 4,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "ADDSDloadidx1",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.AADDSD,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "ADDSDloadidx8",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.AADDSD,
+ scale: 8,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "SUBSSloadidx1",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.ASUBSS,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "SUBSSloadidx4",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.ASUBSS,
+ scale: 4,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "SUBSDloadidx1",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.ASUBSD,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "SUBSDloadidx8",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.ASUBSD,
+ scale: 8,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "MULSSloadidx1",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.AMULSS,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "MULSSloadidx4",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.AMULSS,
+ scale: 4,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "MULSDloadidx1",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.AMULSD,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "MULSDloadidx8",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.AMULSD,
+ scale: 8,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "DIVSSloadidx1",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.ADIVSS,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "DIVSSloadidx4",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.ADIVSS,
+ scale: 4,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "DIVSDloadidx1",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.ADIVSD,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
+ name: "DIVSDloadidx8",
+ auxType: auxSymOff,
+ argLen: 4,
+ resultInArg0: true,
+ symEffect: SymRead,
+ asm: x86.ADIVSD,
+ scale: 8,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
+ {
name: "ADDQ",
argLen: 2,
commutative: true,
diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go
index a8e43d0114..6c6be39d34 100644
--- a/src/cmd/compile/internal/ssa/prove.go
+++ b/src/cmd/compile/internal/ssa/prove.go
@@ -1051,6 +1051,11 @@ func addLocalInductiveFacts(ft *factsTable, b *Block) {
//
// If all of these conditions are true, then i1 < max and i1 >= min.
+ // To ensure this is a loop header node.
+ if len(b.Preds) != 2 {
+ return
+ }
+
for _, i1 := range b.Values {
if i1.Op != OpPhi {
continue
@@ -1093,6 +1098,9 @@ func addLocalInductiveFacts(ft *factsTable, b *Block) {
}
br = negative
}
+ if br == unknown {
+ continue
+ }
tr, has := domainRelationTable[control.Op]
if !has {
diff --git a/src/cmd/go.mod b/src/cmd/go.mod
index 274ef0d9f0..6d57ceee79 100644
--- a/src/cmd/go.mod
+++ b/src/cmd/go.mod
@@ -10,4 +10,5 @@ require (
golang.org/x/mod v0.3.0
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 // indirect
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054
+ golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316 // indirect
)
diff --git a/src/cmd/go.sum b/src/cmd/go.sum
index 30a0be03e4..3fc693e3bf 100644
--- a/src/cmd/go.sum
+++ b/src/cmd/go.sum
@@ -34,4 +34,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316 h1:Jhw4VC65LaKnpq9FvcK+a8ZzrFm3D+UygvMMrhkOw70=
+golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index 2316fb9991..68bad3cff1 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -1700,6 +1700,8 @@
// GOCACHE
// The directory where the go command will store cached
// information for reuse in future builds.
+// GOMODCACHE
+// The directory where the go command will store downloaded modules.
// GODEBUG
// Enable various debugging facilities. See 'go doc runtime'
// for details.
diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go
index b937a6155e..e1f0521ea4 100644
--- a/src/cmd/go/internal/help/helpdoc.go
+++ b/src/cmd/go/internal/help/helpdoc.go
@@ -493,6 +493,8 @@ General-purpose environment variables:
GOCACHE
The directory where the go command will store cached
information for reuse in future builds.
+ GOMODCACHE
+ The directory where the go command will store downloaded modules.
GODEBUG
Enable various debugging facilities. See 'go doc runtime'
for details.
diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go
index e0bd71b40d..ddbb3b9be0 100644
--- a/src/cmd/go/internal/load/pkg.go
+++ b/src/cmd/go/internal/load/pkg.go
@@ -239,11 +239,25 @@ func (p *Package) setLoadPackageDataError(err error, path string, stk *ImportSta
err = &NoGoError{Package: p}
}
+ // Take only the first error from a scanner.ErrorList. PackageError only
+ // has room for one position, so we report the first error with a position
+ // instead of all of the errors without a position.
+ var pos string
+ var isScanErr bool
+ if scanErr, ok := err.(scanner.ErrorList); ok && len(scanErr) > 0 {
+ isScanErr = true // For stack push/pop below.
+
+ scanPos := scanErr[0].Pos
+ scanPos.Filename = base.ShortPath(scanPos.Filename)
+ pos = scanPos.String()
+ err = errors.New(scanErr[0].Msg)
+ }
+
// Report the error on the importing package if the problem is with the import declaration
// for example, if the package doesn't exist or if the import path is malformed.
// On the other hand, don't include a position if the problem is with the imported package,
// for example there are no Go files (NoGoError), or there's a problem in the imported
- // package's source files themselves.
+ // package's source files themselves (scanner errors).
//
// TODO(matloob): Perhaps make each of those the errors in the first group
// (including modload.ImportMissingError, and the corresponding
@@ -254,22 +268,11 @@ func (p *Package) setLoadPackageDataError(err error, path string, stk *ImportSta
// to make it easier to check for them? That would save us from having to
// move the modload errors into this package to avoid a package import cycle,
// and from having to export an error type for the errors produced in build.
- if !isMatchErr && nogoErr != nil {
+ if !isMatchErr && (nogoErr != nil || isScanErr) {
stk.Push(path)
defer stk.Pop()
}
- // Take only the first error from a scanner.ErrorList. PackageError only
- // has room for one position, so we report the first error with a position
- // instead of all of the errors without a position.
- var pos string
- if scanErr, ok := err.(scanner.ErrorList); ok && len(scanErr) > 0 {
- scanPos := scanErr[0].Pos
- scanPos.Filename = base.ShortPath(scanPos.Filename)
- pos = scanPos.String()
- err = errors.New(scanErr[0].Msg)
- }
-
p.Error = &PackageError{
ImportStack: stk.Copy(),
Pos: pos,
diff --git a/src/cmd/go/testdata/script/list_err_stack.txt b/src/cmd/go/testdata/script/list_err_stack.txt
new file mode 100644
index 0000000000..a7be9fde6d
--- /dev/null
+++ b/src/cmd/go/testdata/script/list_err_stack.txt
@@ -0,0 +1,27 @@
+
+# golang.org/issue/40544: regression in error stacks for parse errors
+
+env GO111MODULE=off
+cd sandbox/foo
+go list -e -json .
+stdout '"sandbox/foo"'
+stdout '"sandbox/bar"'
+stdout '"Pos": "..(/|\\\\)bar(/|\\\\)bar.go:1:1"'
+stdout '"Err": "expected ''package'', found ackage"'
+
+env GO111MODULE=on
+go list -e -json .
+stdout '"sandbox/foo"'
+stdout '"sandbox/bar"'
+stdout '"Pos": "..(/|\\\\)bar(/|\\\\)bar.go:1:1"'
+stdout '"Err": "expected ''package'', found ackage"'
+
+-- sandbox/go.mod --
+module sandbox
+
+-- sandbox/foo/foo.go --
+package pkg
+
+import "sandbox/bar"
+-- sandbox/bar/bar.go --
+ackage bar \ No newline at end of file
diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go
index 679fdd7b8a..8c56af7559 100644
--- a/src/cmd/gofmt/gofmt.go
+++ b/src/cmd/gofmt/gofmt.go
@@ -40,7 +40,13 @@ var (
// Keep these in sync with go/format/format.go.
const (
tabWidth = 8
- printerMode = printer.UseSpaces | printer.TabIndent | printer.StdFormat
+ printerMode = printer.UseSpaces | printer.TabIndent | printerNormalizeNumbers
+
+ // printerNormalizeNumbers means to canonicalize number literal prefixes
+ // and exponents while printing. See https://golang.org/doc/go1.13#gofmt.
+ //
+ // This value is defined in go/printer specifically for go/format and cmd/gofmt.
+ printerNormalizeNumbers = 1 << 30
)
var (
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index b1bfd6e96b..6197f4d99a 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -2192,17 +2192,6 @@ func ldshlibsyms(ctxt *Link, shlib string) {
l.SetSymElfType(s, elf.ST_TYPE(elfsym.Info))
su.SetSize(int64(elfsym.Size))
if elfsym.Section != elf.SHN_UNDEF {
- // If it's not undefined, mark the symbol as reachable
- // so as to protect it from dead code elimination,
- // even if there aren't any explicit references to it.
- // Under the previous sym.Symbol based regime this
- // wasn't necessary, but for the loader-based deadcode
- // it is definitely needed.
- //
- // FIXME: have a more general/flexible mechanism for this?
- //
- l.SetAttrReachable(s, true)
-
// Set .File for the library that actually defines the symbol.
l.SetSymPkg(s, libpath)
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
index dc7adcb5fb..8f417ec8db 100644
--- a/src/cmd/link/link_test.go
+++ b/src/cmd/link/link_test.go
@@ -541,6 +541,13 @@ func TestOldLink(t *testing.T) {
testenv.MustHaveGoBuild(t)
+ // Check that the old linker exists (we don't ship it in binary releases,
+ // see issue 39509).
+ cmd := exec.Command(testenv.GoToolPath(t), "tool", "-n", "oldlink")
+ if err := cmd.Run(); err != nil {
+ t.Skip("skipping because cannot find installed cmd/oldlink binary")
+ }
+
tmpdir, err := ioutil.TempDir("", "TestOldLink")
if err != nil {
t.Fatal(err)
@@ -553,7 +560,7 @@ func TestOldLink(t *testing.T) {
t.Fatal(err)
}
- cmd := exec.Command(testenv.GoToolPath(t), "run", "-gcflags=all=-go115newobj=false", "-asmflags=all=-go115newobj=false", "-ldflags=-go115newobj=false", src)
+ cmd = exec.Command(testenv.GoToolPath(t), "run", "-gcflags=all=-go115newobj=false", "-asmflags=all=-go115newobj=false", "-ldflags=-go115newobj=false", src)
if out, err := cmd.CombinedOutput(); err != nil {
t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
}
diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt
index 7ee7478b19..21fc78c237 100644
--- a/src/cmd/vendor/modules.txt
+++ b/src/cmd/vendor/modules.txt
@@ -84,6 +84,7 @@ golang.org/x/tools/go/cfg
golang.org/x/tools/go/types/objectpath
golang.org/x/tools/go/types/typeutil
golang.org/x/tools/internal/analysisinternal
-# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
+# golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316
+## explicit
golang.org/x/xerrors
golang.org/x/xerrors/internal
diff --git a/src/compress/flate/deflate_test.go b/src/compress/flate/deflate_test.go
index 4b1ed549a4..49a0345fd1 100644
--- a/src/compress/flate/deflate_test.go
+++ b/src/compress/flate/deflate_test.go
@@ -512,33 +512,57 @@ func TestWriterReset(t *testing.T) {
t.Errorf("level %d Writer not reset after Reset", level)
}
}
- testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, NoCompression) })
- testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, DefaultCompression) })
- testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, BestCompression) })
- dict := []byte("we are the world")
- testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, NoCompression, dict) })
- testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, DefaultCompression, dict) })
- testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, BestCompression, dict) })
+
+ levels := []int{0, 1, 2, 5, 9}
+ for _, level := range levels {
+ t.Run(fmt.Sprint(level), func(t *testing.T) {
+ testResetOutput(t, level, nil)
+ })
+ }
+
+ t.Run("dict", func(t *testing.T) {
+ for _, level := range levels {
+ t.Run(fmt.Sprint(level), func(t *testing.T) {
+ testResetOutput(t, level, nil)
+ })
+ }
+ })
}
-func testResetOutput(t *testing.T, newWriter func(w io.Writer) (*Writer, error)) {
+func testResetOutput(t *testing.T, level int, dict []byte) {
+ writeData := func(w *Writer) {
+ msg := []byte("now is the time for all good gophers")
+ w.Write(msg)
+ w.Flush()
+
+ hello := []byte("hello world")
+ for i := 0; i < 1024; i++ {
+ w.Write(hello)
+ }
+
+ fill := bytes.Repeat([]byte("x"), 65000)
+ w.Write(fill)
+ }
+
buf := new(bytes.Buffer)
- w, err := newWriter(buf)
+ var w *Writer
+ var err error
+ if dict == nil {
+ w, err = NewWriter(buf, level)
+ } else {
+ w, err = NewWriterDict(buf, level, dict)
+ }
if err != nil {
t.Fatalf("NewWriter: %v", err)
}
- b := []byte("hello world")
- for i := 0; i < 1024; i++ {
- w.Write(b)
- }
+
+ writeData(w)
w.Close()
out1 := buf.Bytes()
buf2 := new(bytes.Buffer)
w.Reset(buf2)
- for i := 0; i < 1024; i++ {
- w.Write(b)
- }
+ writeData(w)
w.Close()
out2 := buf2.Bytes()
diff --git a/src/compress/flate/deflatefast.go b/src/compress/flate/deflatefast.go
index 08298b76bb..24f8be9d5d 100644
--- a/src/compress/flate/deflatefast.go
+++ b/src/compress/flate/deflatefast.go
@@ -4,6 +4,8 @@
package flate
+import "math"
+
// This encoding algorithm, which prioritizes speed over output size, is
// based on Snappy's LZ77-style encoder: github.com/golang/snappy
@@ -12,6 +14,13 @@ const (
tableSize = 1 << tableBits // Size of the table.
tableMask = tableSize - 1 // Mask for table indices. Redundant, but can eliminate bounds checks.
tableShift = 32 - tableBits // Right-shift to get the tableBits most significant bits of a uint32.
+
+ // Reset the buffer offset when reaching this.
+ // Offsets are stored between blocks as int32 values.
+ // Since the offset we are checking against is at the beginning
+ // of the buffer, we need to subtract the current and input
+ // buffer to not risk overflowing the int32.
+ bufferReset = math.MaxInt32 - maxStoreBlockSize*2
)
func load32(b []byte, i int32) uint32 {
@@ -59,8 +68,8 @@ func newDeflateFast() *deflateFast {
// to dst and returns the result.
func (e *deflateFast) encode(dst []token, src []byte) []token {
// Ensure that e.cur doesn't wrap.
- if e.cur > 1<<30 {
- e.resetAll()
+ if e.cur >= bufferReset {
+ e.shiftOffsets()
}
// This check isn't in the Snappy implementation, but there, the caller
@@ -264,22 +273,32 @@ func (e *deflateFast) reset() {
e.cur += maxMatchOffset
// Protect against e.cur wraparound.
- if e.cur > 1<<30 {
- e.resetAll()
+ if e.cur >= bufferReset {
+ e.shiftOffsets()
}
}
-// resetAll resets the deflateFast struct and is only called in rare
-// situations to prevent integer overflow. It manually resets each field
-// to avoid causing large stack growth.
+// shiftOffsets will shift down all match offset.
+// This is only called in rare situations to prevent integer overflow.
//
-// See https://golang.org/issue/18636.
-func (e *deflateFast) resetAll() {
- // This is equivalent to:
- // *e = deflateFast{cur: maxStoreBlockSize, prev: e.prev[:0]}
- e.cur = maxStoreBlockSize
- e.prev = e.prev[:0]
- for i := range e.table {
- e.table[i] = tableEntry{}
+// See https://golang.org/issue/18636 and https://github.com/golang/go/issues/34121.
+func (e *deflateFast) shiftOffsets() {
+ if len(e.prev) == 0 {
+ // We have no history; just clear the table.
+ for i := range e.table[:] {
+ e.table[i] = tableEntry{}
+ }
+ e.cur = maxMatchOffset
+ return
+ }
+
+ // Shift down everything in the table that isn't already too far away.
+ for i := range e.table[:] {
+ v := e.table[i].offset - e.cur + maxMatchOffset
+ if v < 0 {
+ v = 0
+ }
+ e.table[i].offset = v
}
+ e.cur = maxMatchOffset
}
diff --git a/src/compress/flate/huffman_bit_writer.go b/src/compress/flate/huffman_bit_writer.go
index 3e19061f8b..f111f9f592 100644
--- a/src/compress/flate/huffman_bit_writer.go
+++ b/src/compress/flate/huffman_bit_writer.go
@@ -634,6 +634,7 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) {
w.literalFreq[endBlockMarker] = 1
const numLiterals = endBlockMarker + 1
+ w.offsetFreq[0] = 1
const numOffsets = 1
w.literalEncoding.generate(w.literalFreq, 15)
diff --git a/src/compress/flate/writer_test.go b/src/compress/flate/writer_test.go
index c4d36aa37e..881cb71cc3 100644
--- a/src/compress/flate/writer_test.go
+++ b/src/compress/flate/writer_test.go
@@ -173,3 +173,66 @@ func testDeterministic(i int, t *testing.T) {
t.Errorf("level %d did not produce deterministic result, result mismatch, len(a) = %d, len(b) = %d", i, len(b1b), len(b2b))
}
}
+
+// TestDeflateFast_Reset will test that encoding is consistent
+// across a warparound of the table offset.
+// See https://github.com/golang/go/issues/34121
+func TestDeflateFast_Reset(t *testing.T) {
+ buf := new(bytes.Buffer)
+ n := 65536
+
+ for i := 0; i < n; i++ {
+ fmt.Fprintf(buf, "asdfasdfasdfasdf%d%dfghfgujyut%dyutyu\n", i, i, i)
+ }
+ // This is specific to level 1.
+ const level = 1
+ in := buf.Bytes()
+ offset := 1
+ if testing.Short() {
+ offset = 256
+ }
+
+ // We do an encode with a clean buffer to compare.
+ var want bytes.Buffer
+ w, err := NewWriter(&want, level)
+ if err != nil {
+ t.Fatalf("NewWriter: level %d: %v", level, err)
+ }
+
+ // Output written 3 times.
+ w.Write(in)
+ w.Write(in)
+ w.Write(in)
+ w.Close()
+
+ for ; offset <= 256; offset *= 2 {
+ w, err := NewWriter(ioutil.Discard, level)
+ if err != nil {
+ t.Fatalf("NewWriter: level %d: %v", level, err)
+ }
+
+ // Reset until we are right before the wraparound.
+ // Each reset adds maxMatchOffset to the offset.
+ for i := 0; i < (bufferReset-len(in)-offset-maxMatchOffset)/maxMatchOffset; i++ {
+ // skip ahead to where we are close to wrap around...
+ w.d.reset(nil)
+ }
+ var got bytes.Buffer
+ w.Reset(&got)
+
+ // Write 3 times, close.
+ for i := 0; i < 3; i++ {
+ _, err = w.Write(in)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ err = w.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(got.Bytes(), want.Bytes()) {
+ t.Fatalf("output did not match at wraparound, len(want) = %d, len(got) = %d", want.Len(), got.Len())
+ }
+ }
+}
diff --git a/src/crypto/ed25519/ed25519.go b/src/crypto/ed25519/ed25519.go
index 5766970f82..6f59bb5cff 100644
--- a/src/crypto/ed25519/ed25519.go
+++ b/src/crypto/ed25519/ed25519.go
@@ -154,7 +154,7 @@ func Sign(privateKey PrivateKey, message []byte) []byte {
return signature
}
-func signGeneric(signature, privateKey, message []byte) {
+func sign(signature, privateKey, message []byte) {
if l := len(privateKey); l != PrivateKeySize {
panic("ed25519: bad private key length: " + strconv.Itoa(l))
}
@@ -201,10 +201,6 @@ func signGeneric(signature, privateKey, message []byte) {
// Verify reports whether sig is a valid signature of message by publicKey. It
// will panic if len(publicKey) is not PublicKeySize.
func Verify(publicKey PublicKey, message, sig []byte) bool {
- return verify(publicKey, message, sig)
-}
-
-func verifyGeneric(publicKey PublicKey, message, sig []byte) bool {
if l := len(publicKey); l != PublicKeySize {
panic("ed25519: bad public key length: " + strconv.Itoa(l))
}
diff --git a/src/crypto/ed25519/ed25519_noasm.go b/src/crypto/ed25519/ed25519_noasm.go
deleted file mode 100644
index caa84f74fb..0000000000
--- a/src/crypto/ed25519/ed25519_noasm.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2020 The Go 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
-
-package ed25519
-
-func sign(signature, privateKey, message []byte) {
- signGeneric(signature, privateKey, message)
-}
-
-func verify(publicKey PublicKey, message, sig []byte) bool {
- return verifyGeneric(publicKey, message, sig)
-}
diff --git a/src/crypto/ed25519/ed25519_s390x.go b/src/crypto/ed25519/ed25519_s390x.go
deleted file mode 100644
index c8627a0652..0000000000
--- a/src/crypto/ed25519/ed25519_s390x.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2020 The Go 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 ed25519
-
-import (
- "internal/cpu"
- "strconv"
-)
-
-//go:noescape
-func kdsaSign(message, signature, privateKey []byte) bool
-
-//go:noescape
-func kdsaVerify(message, signature, publicKey []byte) bool
-
-// sign does a check to see if hardware has Edwards Curve instruction available.
-// If it does, use the hardware implementation. Otherwise, use the generic version.
-func sign(signature, privateKey, message []byte) {
- if cpu.S390X.HasEDDSA {
- if l := len(privateKey); l != PrivateKeySize {
- panic("ed25519: bad private key length: " + strconv.Itoa(l))
- }
-
- ret := kdsaSign(message, signature, privateKey[:32])
- if !ret {
- panic("ed25519: kdsa sign has a failure")
- }
- return
- }
- signGeneric(signature, privateKey, message)
-}
-
-// verify does a check to see if hardware has Edwards Curve instruction available.
-// If it does, use the hardware implementation for eddsa verfication. Otherwise, the generic
-// version is used
-func verify(publicKey PublicKey, message, sig []byte) bool {
- if cpu.S390X.HasEDDSA {
- if l := len(publicKey); l != PublicKeySize {
- panic("ed25519: bad public key length: " + strconv.Itoa(l))
- }
-
- if len(sig) != SignatureSize || sig[63]&224 != 0 {
- return false
- }
-
- return kdsaVerify(message, sig, publicKey)
- }
- return verifyGeneric(publicKey, message, sig)
-}
diff --git a/src/crypto/ed25519/ed25519_s390x.s b/src/crypto/ed25519/ed25519_s390x.s
deleted file mode 100644
index 1c77b51a78..0000000000
--- a/src/crypto/ed25519/ed25519_s390x.s
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2020 The Go 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 "textflag.h"
-
-// func kdsaSign(message, signature, privateKey []byte) bool
-TEXT ·kdsaSign(SB), $4096-73
- // The kdsa instruction takes function code,
- // buffer's location, message's location and message len
- // as parameters. Out of those, the function code and buffer's location
- // should be placed in R0 and R1 respectively. The message's location
- // and message length should be placed in an even-odd register pair. (e.g: R2 and R3)
-
- // The content of parameter block(buffer) looks like the following:
- // Signature R, Signature S and Private Key all take 32 bytes.
- // In the signing case, the signatures(R and S) will be generated by
- // the signing instruction and get placed in the locations shown in the parameter block.
- // 0 +---------------+
- // | Signature(R) |
- // 32 +---------------+
- // | Signature(S) |
- // 64 +---------------+
- // | Private Key |
- // 96 +---------------+
- // | Reserved |
- // 112+---------------+
- // | |
- // | ... |
- // | |
- // 4088 +---------------+
-
- // The following code section setups the buffer from stack:
- // Get the address of the buffer stack variable.
- MOVD $buffer-4096(SP), R1
-
- // Zero the buffer.
- MOVD R1, R2
- MOVD $(4096/256), R0 // number of 256 byte chunks to clear
-
-clear:
- XC $256, (R2), (R2)
- MOVD $256(R2), R2
- BRCTG R0, clear
-
- MOVD $40, R0 // EDDSA-25519 sign has a function code of 40
- LMG message+0(FP), R2, R3 // R2=base R3=len
- LMG signature+24(FP), R4, R5 // R4=base R5=len
- LMG privateKey+48(FP), R6, R7 // R6=base R7=len
-
- // Checks the length of signature and private key
- CMPBNE R5, $64, panic
- CMPBNE R7, $32, panic
-
- // The instruction uses RFC 8032's private key, which is the first 32 bytes
- // of the private key in this package. So we copy that into the buffer.
- MVC $32, (R6), 64(R1)
-
-loop:
- WORD $0xB93A0002 // The KDSA instruction
- BVS loop // The instruction is exectued by hardware and can be interrupted. This does a retry when that happens.
- BNE error
-
-success:
- // The signatures generated are in big-endian form, so we
- // need to reverse the bytes of Signature(R) and Signature(S) in the buffers to transform
- // them from big-endian to little-endian.
-
- // Transform Signature(R) from big endian to little endian and copy into the signature
- MVCIN $32, 31(R1), (R4)
-
- // Transform Signature(S) from big endian to little endian and copy into the signature
- MVCIN $32, 63(R1), 32(R4)
-
- MOVB $1, ret+72(FP)
- RET
-
-error:
- // return false
- MOVB $0, ret+72(FP)
- RET
-
-panic:
- UNDEF
-
-// func kdsaVerify(message, signature, publicKey []byte) bool
-TEXT ·kdsaVerify(SB), $4096-73
- // The kdsa instruction takes function code,
- // buffer's location, message's location and message len
- // as parameters. Out of those, the function code and buffer's location
- // should be placed in R0 and R1 respectively. The message's location
- // and message length should be placed in an even-odd register pair. (e.g: R2 and R3)
-
- // The parameter block(buffer) is similar to that of signing, except that
- // we use public key for verification, and Signatures(R and S) are provided
- // as input parameters to the parameter block.
- // 0 +---------------+
- // | Signature(R) |
- // 32 +---------------+
- // | Signature(S) |
- // 64 +---------------+
- // | Public Key |
- // 96 +---------------+
- // | Reserved |
- // 112+---------------+
- // | |
- // | ... |
- // | |
- // 4088 +---------------+
-
- // The following code section setups the buffer from stack:
- // Get the address of the buffer stack variable.
- MOVD $buffer-4096(SP), R1
-
- // Zero the buffer.
- MOVD R1, R2
- MOVD $(4096/256), R0 // number of 256 byte chunks to clear
-
-clear:
- XC $256, (R2), (R2)
- MOVD $256(R2), R2
- BRCTG R0, clear
-
- MOVD $32, R0 // EDDSA-25519 verify has a function code of 32
- LMG message+0(FP), R2, R3 // R2=base R3=len
- LMG signature+24(FP), R4, R5 // R4=base R5=len
- LMG publicKey+48(FP), R6, R7 // R6=base R7=len
-
- // Checks the length of public key and signature
- CMPBNE R5, $64, panic
- CMPBNE R7, $32, panic
-
-verify:
- // The instruction needs Signature(R), Signature(S) and public key
- // to be in big-endian form during computation. Therefore,
- // we do the transformation (from little endian to big endian) and copy those into the buffer.
-
- // Transform Signature(R) from little endian to big endian and copy into the buffer
- MVCIN $32, 31(R4), (R1)
-
- // Transform Signature(S) from little endian to big endian and copy into the buffer
- MVCIN $32, 63(R4), 32(R1)
-
- // Transform Public Key from little endian to big endian and copy into the buffer
- MVCIN $32, 31(R6), 64(R1)
-
-verifyLoop:
- WORD $0xB93A0002 // KDSA instruction
- BVS verifyLoop // Retry upon hardware interrupt
- BNE error
-
-success:
- MOVB $1, ret+72(FP)
- RET
-
-error:
- MOVB $0, ret+72(FP)
- RET
-
-panic:
- UNDEF
diff --git a/src/crypto/ed25519/ed25519_test.go b/src/crypto/ed25519/ed25519_test.go
index f77d463721..adb09e409a 100644
--- a/src/crypto/ed25519/ed25519_test.go
+++ b/src/crypto/ed25519/ed25519_test.go
@@ -26,14 +26,6 @@ func (zeroReader) Read(buf []byte) (int, error) {
return len(buf), nil
}
-// signGenericWrapper is identical to Sign except that it unconditionally calls signGeneric directly
-// rather than going through the sign function that might call assembly code.
-func signGenericWrapper(privateKey PrivateKey, msg []byte) []byte {
- sig := make([]byte, SignatureSize)
- signGeneric(sig, privateKey, msg)
- return sig
-}
-
func TestUnmarshalMarshal(t *testing.T) {
pub, _, _ := GenerateKey(rand.Reader)
@@ -53,33 +45,22 @@ func TestUnmarshalMarshal(t *testing.T) {
}
func TestSignVerify(t *testing.T) {
- t.Run("Generic", func(t *testing.T) { testSignVerify(t, signGenericWrapper, verifyGeneric) })
- t.Run("Native", func(t *testing.T) { testSignVerify(t, Sign, Verify) })
-}
-
-func testSignVerify(t *testing.T, signImpl func(privateKey PrivateKey, message []byte) []byte,
- verifyImpl func(publicKey PublicKey, message, sig []byte) bool) {
var zero zeroReader
public, private, _ := GenerateKey(zero)
message := []byte("test message")
- sig := signImpl(private, message)
- if !verifyImpl(public, message, sig) {
+ sig := Sign(private, message)
+ if !Verify(public, message, sig) {
t.Errorf("valid signature rejected")
}
wrongMessage := []byte("wrong message")
- if verifyImpl(public, wrongMessage, sig) {
+ if Verify(public, wrongMessage, sig) {
t.Errorf("signature of different message accepted")
}
}
func TestCryptoSigner(t *testing.T) {
- t.Run("Generic", func(t *testing.T) { testCryptoSigner(t, verifyGeneric) })
- t.Run("Native", func(t *testing.T) { testCryptoSigner(t, Verify) })
-}
-
-func testCryptoSigner(t *testing.T, verifyImpl func(publicKey PublicKey, message, sig []byte) bool) {
var zero zeroReader
public, private, _ := GenerateKey(zero)
@@ -102,7 +83,7 @@ func testCryptoSigner(t *testing.T, verifyImpl func(publicKey PublicKey, message
t.Fatalf("error from Sign(): %s", err)
}
- if !verifyImpl(public, message, signature) {
+ if !Verify(public, message, signature) {
t.Errorf("Verify failed on signature from Sign()")
}
}
@@ -130,12 +111,6 @@ func TestEqual(t *testing.T) {
}
func TestGolden(t *testing.T) {
- t.Run("Generic", func(t *testing.T) { testGolden(t, signGenericWrapper, verifyGeneric) })
- t.Run("Native", func(t *testing.T) { testGolden(t, Sign, Verify) })
-}
-
-func testGolden(t *testing.T, signImpl func(privateKey PrivateKey, message []byte) []byte,
- verifyImpl func(publicKey PublicKey, message, sig []byte) bool) {
// sign.input.gz is a selection of test cases from
// https://ed25519.cr.yp.to/python/sign.input
testDataZ, err := os.Open("testdata/sign.input.gz")
@@ -177,12 +152,12 @@ func testGolden(t *testing.T, signImpl func(privateKey PrivateKey, message []byt
copy(priv[:], privBytes)
copy(priv[32:], pubKey)
- sig2 := signImpl(priv[:], msg)
+ sig2 := Sign(priv[:], msg)
if !bytes.Equal(sig, sig2[:]) {
t.Errorf("different signature result on line %d: %x vs %x", lineNo, sig, sig2)
}
- if !verifyImpl(pubKey, msg, sig2) {
+ if !Verify(pubKey, msg, sig2) {
t.Errorf("signature failed to verify on line %d", lineNo)
}
@@ -206,11 +181,6 @@ func testGolden(t *testing.T, signImpl func(privateKey PrivateKey, message []byt
}
func TestMalleability(t *testing.T) {
- t.Run("Generic", func(t *testing.T) { testMalleability(t, verifyGeneric) })
- t.Run("Native", func(t *testing.T) { testMalleability(t, Verify) })
-}
-
-func testMalleability(t *testing.T, verifyImpl func(publicKey PublicKey, message, sig []byte) bool) {
// https://tools.ietf.org/html/rfc8032#section-5.1.7 adds an additional test
// that s be in [0, order). This prevents someone from adding a multiple of
// order to s and obtaining a second valid signature for the same message.
@@ -229,7 +199,7 @@ func testMalleability(t *testing.T, verifyImpl func(publicKey PublicKey, message
0xb1, 0x08, 0xc3, 0xbd, 0xae, 0x36, 0x9e, 0xf5, 0x49, 0xfa,
}
- if verifyImpl(publicKey, msg, sig) {
+ if Verify(publicKey, msg, sig) {
t.Fatal("non-canonical signature accepted")
}
}
diff --git a/src/crypto/x509/internal/macOS/corefoundation.go b/src/crypto/x509/internal/macos/corefoundation.go
index 359694fabf..359694fabf 100644
--- a/src/crypto/x509/internal/macOS/corefoundation.go
+++ b/src/crypto/x509/internal/macos/corefoundation.go
diff --git a/src/crypto/x509/internal/macOS/corefoundation.s b/src/crypto/x509/internal/macos/corefoundation.s
index 8f6be47e4b..8f6be47e4b 100644
--- a/src/crypto/x509/internal/macOS/corefoundation.s
+++ b/src/crypto/x509/internal/macos/corefoundation.s
diff --git a/src/crypto/x509/internal/macOS/security.go b/src/crypto/x509/internal/macos/security.go
index 64fe206390..64fe206390 100644
--- a/src/crypto/x509/internal/macOS/security.go
+++ b/src/crypto/x509/internal/macos/security.go
diff --git a/src/crypto/x509/internal/macOS/security.s b/src/crypto/x509/internal/macos/security.s
index 1630c55bab..1630c55bab 100644
--- a/src/crypto/x509/internal/macOS/security.s
+++ b/src/crypto/x509/internal/macos/security.s
diff --git a/src/crypto/x509/root_darwin_amd64.go b/src/crypto/x509/root_darwin_amd64.go
index ccc37b8ec5..ce88de025e 100644
--- a/src/crypto/x509/root_darwin_amd64.go
+++ b/src/crypto/x509/root_darwin_amd64.go
@@ -8,7 +8,7 @@ package x509
import (
"bytes"
- "crypto/x509/internal/macOS"
+ macOS "crypto/x509/internal/macos"
"fmt"
"os"
"strings"
diff --git a/src/crypto/x509/root_windows.go b/src/crypto/x509/root_windows.go
index 34d585318d..1e0f3acb67 100644
--- a/src/crypto/x509/root_windows.go
+++ b/src/crypto/x509/root_windows.go
@@ -88,6 +88,9 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e
switch status {
case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
return CertificateInvalidError{c, Expired, ""}
+ case syscall.CERT_TRUST_IS_NOT_VALID_FOR_USAGE:
+ return CertificateInvalidError{c, IncompatibleUsage, ""}
+ // TODO(filippo): surface more error statuses.
default:
return UnknownAuthorityError{c, nil, nil}
}
@@ -138,11 +141,19 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex
return nil
}
+// windowsExtKeyUsageOIDs are the C NUL-terminated string representations of the
+// OIDs for use with the Windows API.
+var windowsExtKeyUsageOIDs = make(map[ExtKeyUsage][]byte, len(extKeyUsageOIDs))
+
+func init() {
+ for _, eku := range extKeyUsageOIDs {
+ windowsExtKeyUsageOIDs[eku.extKeyUsage] = []byte(eku.oid.String() + "\x00")
+ }
+}
+
// systemVerify is like Verify, except that it uses CryptoAPI calls
// to build certificate chains and verify them.
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
- hasDNSName := opts != nil && len(opts.DNSName) > 0
-
storeCtx, err := createStoreContext(c, opts)
if err != nil {
return nil, err
@@ -152,17 +163,26 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
para := new(syscall.CertChainPara)
para.Size = uint32(unsafe.Sizeof(*para))
- // If there's a DNSName set in opts, assume we're verifying
- // a certificate from a TLS server.
- if hasDNSName {
- oids := []*byte{
- &syscall.OID_PKIX_KP_SERVER_AUTH[0],
- // Both IE and Chrome allow certificates with
- // Server Gated Crypto as well. Some certificates
- // in the wild require them.
- &syscall.OID_SERVER_GATED_CRYPTO[0],
- &syscall.OID_SGC_NETSCAPE[0],
+ keyUsages := opts.KeyUsages
+ if len(keyUsages) == 0 {
+ keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
+ }
+ oids := make([]*byte, 0, len(keyUsages))
+ for _, eku := range keyUsages {
+ if eku == ExtKeyUsageAny {
+ oids = nil
+ break
+ }
+ if oid, ok := windowsExtKeyUsageOIDs[eku]; ok {
+ oids = append(oids, &oid[0])
}
+ // Like the standard verifier, accept SGC EKUs as equivalent to ServerAuth.
+ if eku == ExtKeyUsageServerAuth {
+ oids = append(oids, &syscall.OID_SERVER_GATED_CRYPTO[0])
+ oids = append(oids, &syscall.OID_SGC_NETSCAPE[0])
+ }
+ }
+ if oids != nil {
para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
para.RequestedUsage.Usage.Length = uint32(len(oids))
para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
@@ -208,7 +228,7 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
return nil, err
}
- if hasDNSName {
+ if opts != nil && len(opts.DNSName) > 0 {
err = checkChainSSLServerPolicy(c, chainCtx, opts)
if err != nil {
return nil, err
diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go
index d47f8382ae..50f4d4ac80 100644
--- a/src/crypto/x509/verify.go
+++ b/src/crypto/x509/verify.go
@@ -199,7 +199,7 @@ type VerifyOptions struct {
IsBoring func(*Certificate) bool
// DNSName, if set, is checked against the leaf certificate with
- // Certificate.VerifyHostname.
+ // Certificate.VerifyHostname or the platform verifier.
DNSName string
// Intermediates is an optional pool of certificates that are not trust
@@ -214,20 +214,16 @@ type VerifyOptions struct {
// chain. If zero, the current time is used.
CurrentTime time.Time
- // KeyUsage specifies which Extended Key Usage values are acceptable. A leaf
- // certificate is accepted if it contains any of the listed values. An empty
- // list means ExtKeyUsageServerAuth. To accept any key usage, include
- // ExtKeyUsageAny.
- //
- // Certificate chains are required to nest these extended key usage values.
- // (This matches the Windows CryptoAPI behavior, but not the spec.)
+ // KeyUsages specifies which Extended Key Usage values are acceptable. A
+ // chain is accepted if it allows any of the listed values. An empty list
+ // means ExtKeyUsageServerAuth. To accept any key usage, include ExtKeyUsageAny.
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 pathological
// certificates from consuming excessive amounts of CPU time when
- // validating.
+ // validating. It does not apply to the platform verifier.
MaxConstraintComparisions int
}
@@ -747,8 +743,9 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
// needed. If successful, it returns one or more chains where the first
// element of the chain is c and the last element is from opts.Roots.
//
-// If opts.Roots is nil and system roots are unavailable the returned error
-// will be of type SystemRootsError.
+// If opts.Roots is nil, the platform verifier might be used, and
+// verification details might differ from what is described below. If system
+// roots are unavailable the returned error will be of type SystemRootsError.
//
// Name constraints in the intermediates will be applied to all names claimed
// in the chain, not just opts.DNSName. Thus it is invalid for a leaf to claim
@@ -762,9 +759,10 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
// it indicates that at least one additional label must be prepended to
// the constrained name to be considered valid.
//
-// Extended Key Usage values are enforced down a chain, so an intermediate or
-// root that enumerates EKUs prevents a leaf from asserting an EKU not in that
-// list.
+// Extended Key Usage values are enforced nested down a chain, so an intermediate
+// or root that enumerates EKUs prevents a leaf from asserting an EKU not in that
+// list. (While this is not specified, it is common practice in order to limit
+// the types of certificates a CA can issue.)
//
// WARNING: this function doesn't do any revocation checking.
func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go
index 650b2d2fc6..76d1ab9a47 100644
--- a/src/crypto/x509/verify_test.go
+++ b/src/crypto/x509/verify_test.go
@@ -21,34 +21,24 @@ import (
)
type verifyTest struct {
- leaf string
- intermediates []string
- roots []string
- currentTime int64
- dnsName string
- systemSkip bool
- keyUsages []ExtKeyUsage
- testSystemRootsError bool
- sha2 bool
- ignoreCN bool
-
- errorCallback func(*testing.T, int, error) bool
+ name string
+ leaf string
+ intermediates []string
+ roots []string
+ currentTime int64
+ dnsName string
+ systemSkip bool
+ systemLax bool
+ keyUsages []ExtKeyUsage
+ ignoreCN bool
+
+ errorCallback func(*testing.T, error)
expectedChains [][]string
}
var verifyTests = []verifyTest{
{
- leaf: googleLeaf,
- intermediates: []string{giag2Intermediate},
- currentTime: 1395785200,
- dnsName: "www.google.com",
- testSystemRootsError: true,
-
- // Without any roots specified we should get a system roots
- // error.
- errorCallback: expectSystemRootsError,
- },
- {
+ name: "Valid",
leaf: googleLeaf,
intermediates: []string{giag2Intermediate},
roots: []string{geoTrustRoot},
@@ -60,6 +50,7 @@ var verifyTests = []verifyTest{
},
},
{
+ name: "MixedCase",
leaf: googleLeaf,
intermediates: []string{giag2Intermediate},
roots: []string{geoTrustRoot},
@@ -71,6 +62,7 @@ var verifyTests = []verifyTest{
},
},
{
+ name: "HostnameMismatch",
leaf: googleLeaf,
intermediates: []string{giag2Intermediate},
roots: []string{geoTrustRoot},
@@ -80,6 +72,7 @@ var verifyTests = []verifyTest{
errorCallback: expectHostnameError("certificate is valid for"),
},
{
+ name: "IPMissing",
leaf: googleLeaf,
intermediates: []string{giag2Intermediate},
roots: []string{geoTrustRoot},
@@ -89,6 +82,7 @@ var verifyTests = []verifyTest{
errorCallback: expectHostnameError("doesn't contain any IP SANs"),
},
{
+ name: "Expired",
leaf: googleLeaf,
intermediates: []string{giag2Intermediate},
roots: []string{geoTrustRoot},
@@ -98,6 +92,7 @@ var verifyTests = []verifyTest{
errorCallback: expectExpired,
},
{
+ name: "MissingIntermediate",
leaf: googleLeaf,
roots: []string{geoTrustRoot},
currentTime: 1395785200,
@@ -109,6 +104,7 @@ var verifyTests = []verifyTest{
errorCallback: expectAuthorityUnknown,
},
{
+ name: "RootInIntermediates",
leaf: googleLeaf,
intermediates: []string{geoTrustRoot, giag2Intermediate},
roots: []string{geoTrustRoot},
@@ -119,31 +115,50 @@ var verifyTests = []verifyTest{
{"Google", "Google Internet Authority", "GeoTrust"},
},
// CAPI doesn't build the chain with the duplicated GeoTrust
- // entry so the results don't match. Thus we skip this test
- // until that's fixed.
+ // entry so the results don't match.
+ systemLax: true,
+ },
+ {
+ name: "dnssec-exp",
+ leaf: dnssecExpLeaf,
+ intermediates: []string{startComIntermediate},
+ roots: []string{startComRoot},
+ currentTime: 1302726541,
+
+ // The StartCom root is not trusted by Windows when the default
+ // ServerAuth EKU is requested.
systemSkip: true,
+
+ expectedChains: [][]string{
+ {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
+ },
},
{
+ name: "dnssec-exp/AnyEKU",
leaf: dnssecExpLeaf,
intermediates: []string{startComIntermediate},
roots: []string{startComRoot},
currentTime: 1302726541,
+ keyUsages: []ExtKeyUsage{ExtKeyUsageAny},
expectedChains: [][]string{
{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
},
},
{
+ name: "dnssec-exp/RootInIntermediates",
leaf: dnssecExpLeaf,
intermediates: []string{startComIntermediate, startComRoot},
roots: []string{startComRoot},
currentTime: 1302726541,
+ systemSkip: true, // see dnssec-exp test
expectedChains: [][]string{
{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
},
},
{
+ name: "InvalidHash",
leaf: googleLeafWithInvalidHash,
intermediates: []string{giag2Intermediate},
roots: []string{geoTrustRoot},
@@ -152,50 +167,52 @@ var verifyTests = []verifyTest{
// The specific error message may not occur when using system
// verification.
- systemSkip: true,
+ systemLax: true,
errorCallback: expectHashError,
},
+ // EKULeaf tests use an unconstrained chain leading to a leaf certificate
+ // with an E-mail Protection EKU but not a Server Auth one, checking that
+ // the EKUs on the leaf are enforced.
{
- // The default configuration should reject an S/MIME chain.
- leaf: smimeLeaf,
- roots: []string{smimeIntermediate},
- currentTime: 1339436154,
+ name: "EKULeaf",
+ leaf: smimeLeaf,
+ intermediates: []string{smimeIntermediate},
+ roots: []string{smimeRoot},
+ currentTime: 1594673418,
- // Key usage not implemented for Windows yet.
- systemSkip: true,
errorCallback: expectUsageError,
},
{
- leaf: smimeLeaf,
- roots: []string{smimeIntermediate},
- currentTime: 1339436154,
- keyUsages: []ExtKeyUsage{ExtKeyUsageServerAuth},
+ name: "EKULeafExplicit",
+ leaf: smimeLeaf,
+ intermediates: []string{smimeIntermediate},
+ roots: []string{smimeRoot},
+ currentTime: 1594673418,
+ keyUsages: []ExtKeyUsage{ExtKeyUsageServerAuth},
- // Key usage not implemented for Windows yet.
- systemSkip: true,
errorCallback: expectUsageError,
},
{
- leaf: smimeLeaf,
- roots: []string{smimeIntermediate},
- currentTime: 1339436154,
- keyUsages: []ExtKeyUsage{ExtKeyUsageEmailProtection},
+ name: "EKULeafValid",
+ leaf: smimeLeaf,
+ intermediates: []string{smimeIntermediate},
+ roots: []string{smimeRoot},
+ currentTime: 1594673418,
+ keyUsages: []ExtKeyUsage{ExtKeyUsageEmailProtection},
- // Key usage not implemented for Windows yet.
- systemSkip: true,
expectedChains: [][]string{
- {"Ryan Hurst", "GlobalSign PersonalSign 2 CA - G2"},
+ {"CORPORATIVO FICTICIO ACTIVO", "EAEko Herri Administrazioen CA - CA AAPP Vascas (2)", "IZENPE S.A."},
},
},
{
+ name: "SGCIntermediate",
leaf: megaLeaf,
intermediates: []string{comodoIntermediate1},
roots: []string{comodoRoot},
currentTime: 1360431182,
- // CryptoAPI can find alternative validation paths so we don't
- // perform this test with system validation.
- systemSkip: true,
+ // CryptoAPI can find alternative validation paths.
+ systemLax: true,
expectedChains: [][]string{
{"mega.co.nz", "EssentialSSL CA", "COMODO Certification Authority"},
},
@@ -203,6 +220,7 @@ var verifyTests = []verifyTest{
{
// Check that a name constrained intermediate works even when
// it lists multiple constraints.
+ name: "MultipleConstraints",
leaf: nameConstraintsLeaf,
intermediates: []string{nameConstraintsIntermediate1, nameConstraintsIntermediate2},
roots: []string{globalSignRoot},
@@ -221,17 +239,16 @@ var verifyTests = []verifyTest{
{
// Check that SHA-384 intermediates (which are popping up)
// work.
+ name: "SHA-384",
leaf: moipLeafCert,
intermediates: []string{comodoIntermediateSHA384, comodoRSAAuthority},
roots: []string{addTrustRoot},
currentTime: 1397502195,
dnsName: "api.moip.com.br",
- // CryptoAPI can find alternative validation paths so we don't
- // perform this test with system validation.
- systemSkip: true,
+ // CryptoAPI can find alternative validation paths.
+ systemLax: true,
- sha2: true,
expectedChains: [][]string{
{
"api.moip.com.br",
@@ -244,11 +261,12 @@ var verifyTests = []verifyTest{
{
// Putting a certificate as a root directly should work as a
// way of saying “exactly this”.
+ name: "LeafInRoots",
leaf: selfSigned,
roots: []string{selfSigned},
currentTime: 1471624472,
dnsName: "foo.example",
- systemSkip: true,
+ systemSkip: true, // does not chain to a system root
expectedChains: [][]string{
{"Acme Co"},
@@ -257,11 +275,12 @@ var verifyTests = []verifyTest{
{
// Putting a certificate as a root directly should not skip
// other checks however.
+ name: "LeafInRootsInvalid",
leaf: selfSigned,
roots: []string{selfSigned},
currentTime: 1471624472,
dnsName: "notfoo.example",
- systemSkip: true,
+ systemSkip: true, // does not chain to a system root
errorCallback: expectHostnameError("certificate is valid for"),
},
@@ -269,87 +288,95 @@ var verifyTests = []verifyTest{
// The issuer name in the leaf doesn't exactly match the
// subject name in the root. Go does not perform
// canonicalization and so should reject this. See issue 14955.
+ name: "IssuerSubjectMismatch",
leaf: issuerSubjectMatchLeaf,
roots: []string{issuerSubjectMatchRoot},
currentTime: 1475787715,
- systemSkip: true,
+ systemSkip: true, // does not chain to a system root
errorCallback: expectSubjectIssuerMismatcthError,
},
{
// An X.509 v1 certificate should not be accepted as an
// intermediate.
+ name: "X509v1Intermediate",
leaf: x509v1TestLeaf,
intermediates: []string{x509v1TestIntermediate},
roots: []string{x509v1TestRoot},
currentTime: 1481753183,
- systemSkip: true,
+ systemSkip: true, // does not chain to a system root
errorCallback: expectNotAuthorizedError,
},
{
// If any SAN extension is present (even one without any DNS
// names), the CN should be ignored.
+ name: "IgnoreCNWithSANs",
leaf: ignoreCNWithSANLeaf,
dnsName: "foo.example.com",
roots: []string{ignoreCNWithSANRoot},
currentTime: 1486684488,
- systemSkip: true,
+ systemSkip: true, // does not chain to a system root
errorCallback: expectHostnameError("certificate is not valid for any names"),
},
{
// Test that excluded names are respected.
+ name: "ExcludedNames",
leaf: excludedNamesLeaf,
dnsName: "bender.local",
intermediates: []string{excludedNamesIntermediate},
roots: []string{excludedNamesRoot},
currentTime: 1486684488,
- systemSkip: true,
+ systemSkip: true, // does not chain to a system root
errorCallback: expectNameConstraintsError,
},
{
// Test that unknown critical extensions in a leaf cause a
// verify error.
+ name: "CriticalExtLeaf",
leaf: criticalExtLeafWithExt,
dnsName: "example.com",
intermediates: []string{criticalExtIntermediate},
roots: []string{criticalExtRoot},
currentTime: 1486684488,
- systemSkip: true,
+ systemSkip: true, // does not chain to a system root
errorCallback: expectUnhandledCriticalExtension,
},
{
// Test that unknown critical extensions in an intermediate
// cause a verify error.
+ name: "CriticalExtIntermediate",
leaf: criticalExtLeaf,
dnsName: "example.com",
intermediates: []string{criticalExtIntermediateWithExt},
roots: []string{criticalExtRoot},
currentTime: 1486684488,
- systemSkip: true,
+ systemSkip: true, // does not chain to a system root
errorCallback: expectUnhandledCriticalExtension,
},
{
// Test that invalid CN are ignored.
+ name: "InvalidCN",
leaf: invalidCNWithoutSAN,
dnsName: "foo,invalid",
roots: []string{invalidCNRoot},
currentTime: 1540000000,
- systemSkip: true,
+ systemSkip: true, // does not chain to a system root
errorCallback: expectHostnameError("Common Name is not a valid hostname"),
},
{
// Test that valid CN are respected.
+ name: "ValidCN",
leaf: validCNWithoutSAN,
dnsName: "foo.example.com",
roots: []string{invalidCNRoot},
currentTime: 1540000000,
- systemSkip: true,
+ systemSkip: true, // does not chain to a system root
expectedChains: [][]string{
{"foo.example.com", "Test root"},
@@ -357,31 +384,34 @@ var verifyTests = []verifyTest{
},
// Replicate CN tests with ignoreCN = true
{
+ name: "IgnoreCNWithSANs/ignoreCN",
leaf: ignoreCNWithSANLeaf,
dnsName: "foo.example.com",
roots: []string{ignoreCNWithSANRoot},
currentTime: 1486684488,
- systemSkip: true,
+ systemSkip: true, // does not chain to a system root
ignoreCN: true,
errorCallback: expectHostnameError("certificate is not valid for any names"),
},
{
+ name: "InvalidCN/ignoreCN",
leaf: invalidCNWithoutSAN,
dnsName: "foo,invalid",
roots: []string{invalidCNRoot},
currentTime: 1540000000,
- systemSkip: true,
+ systemSkip: true, // does not chain to a system root
ignoreCN: true,
- errorCallback: expectHostnameError("not valid for any names"),
+ errorCallback: expectHostnameError("certificate is not valid for any names"),
},
{
+ name: "ValidCN/ignoreCN",
leaf: validCNWithoutSAN,
dnsName: "foo.example.com",
roots: []string{invalidCNRoot},
currentTime: 1540000000,
- systemSkip: true,
+ systemSkip: true, // does not chain to a system root
ignoreCN: true,
errorCallback: expectHostnameError("certificate relies on legacy Common Name field"),
@@ -389,11 +419,12 @@ var verifyTests = []verifyTest{
{
// A certificate with an AKID should still chain to a parent without SKID.
// See Issue 30079.
+ name: "AKIDNoSKID",
leaf: leafWithAKID,
roots: []string{rootWithoutSKID},
currentTime: 1550000000,
dnsName: "example",
- systemSkip: true,
+ systemSkip: true, // does not chain to a system root
expectedChains: [][]string{
{"Acme LLC", "Acme Co"},
@@ -401,98 +432,70 @@ var verifyTests = []verifyTest{
},
}
-func expectHostnameError(msg string) func(*testing.T, int, error) bool {
- return func(t *testing.T, i int, err error) (ok bool) {
+func expectHostnameError(msg string) func(*testing.T, error) {
+ return func(t *testing.T, err error) {
if _, ok := err.(HostnameError); !ok {
- t.Errorf("#%d: error was not a HostnameError: %v", i, err)
- return false
+ t.Fatalf("error was not a HostnameError: %v", err)
}
if !strings.Contains(err.Error(), msg) {
- t.Errorf("#%d: HostnameError did not contain %q: %v", i, msg, err)
+ t.Fatalf("HostnameError did not contain %q: %v", msg, err)
}
- return true
}
}
-func expectExpired(t *testing.T, i int, err error) (ok bool) {
+func expectExpired(t *testing.T, err error) {
if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != Expired {
- t.Errorf("#%d: error was not Expired: %v", i, err)
- return false
+ t.Fatalf("error was not Expired: %v", err)
}
- return true
}
-func expectUsageError(t *testing.T, i int, err error) (ok bool) {
+func expectUsageError(t *testing.T, err error) {
if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != IncompatibleUsage {
- t.Errorf("#%d: error was not IncompatibleUsage: %v", i, err)
- return false
+ t.Fatalf("error was not IncompatibleUsage: %v", err)
}
- return true
}
-func expectAuthorityUnknown(t *testing.T, i int, err error) (ok bool) {
+func expectAuthorityUnknown(t *testing.T, err error) {
e, ok := err.(UnknownAuthorityError)
if !ok {
- t.Errorf("#%d: error was not UnknownAuthorityError: %v", i, err)
- return false
+ t.Fatalf("error was not UnknownAuthorityError: %v", err)
}
if e.Cert == nil {
- t.Errorf("#%d: error was UnknownAuthorityError, but missing Cert: %v", i, err)
- return false
- }
- return true
-}
-
-func expectSystemRootsError(t *testing.T, i int, err error) bool {
- if _, ok := err.(SystemRootsError); !ok {
- t.Errorf("#%d: error was not SystemRootsError: %v", i, err)
- return false
+ t.Fatalf("error was UnknownAuthorityError, but missing Cert: %v", err)
}
- return true
}
-func expectHashError(t *testing.T, i int, err error) bool {
+func expectHashError(t *testing.T, err error) {
if err == nil {
- t.Errorf("#%d: no error resulted from invalid hash", i)
- return false
+ t.Fatalf("no error resulted from invalid hash")
}
if expected := "algorithm unimplemented"; !strings.Contains(err.Error(), expected) {
- t.Errorf("#%d: error resulting from invalid hash didn't contain '%s', rather it was: %v", i, expected, err)
- return false
+ t.Fatalf("error resulting from invalid hash didn't contain '%s', rather it was: %v", expected, err)
}
- return true
}
-func expectSubjectIssuerMismatcthError(t *testing.T, i int, err error) (ok bool) {
+func expectSubjectIssuerMismatcthError(t *testing.T, err error) {
if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != NameMismatch {
- t.Errorf("#%d: error was not a NameMismatch: %v", i, err)
- return false
+ t.Fatalf("error was not a NameMismatch: %v", err)
}
- return true
}
-func expectNameConstraintsError(t *testing.T, i int, err error) (ok bool) {
+func expectNameConstraintsError(t *testing.T, err error) {
if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != CANotAuthorizedForThisName {
- t.Errorf("#%d: error was not a CANotAuthorizedForThisName: %v", i, err)
- return false
+ t.Fatalf("error was not a CANotAuthorizedForThisName: %v", err)
}
- return true
}
-func expectNotAuthorizedError(t *testing.T, i int, err error) (ok bool) {
+func expectNotAuthorizedError(t *testing.T, err error) {
if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != NotAuthorizedToSign {
- t.Errorf("#%d: error was not a NotAuthorizedToSign: %v", i, err)
- return false
+ t.Fatalf("error was not a NotAuthorizedToSign: %v", err)
}
- return true
}
-func expectUnhandledCriticalExtension(t *testing.T, i int, err error) (ok bool) {
+func expectUnhandledCriticalExtension(t *testing.T, err error) {
if _, ok := err.(UnhandledCriticalExtension); !ok {
- t.Errorf("#%d: error was not an UnhandledCriticalExtension: %v", i, err)
- return false
+ t.Fatalf("error was not an UnhandledCriticalExtension: %v", err)
}
- return true
}
func certificateFromPEM(pemBytes string) (*Certificate, error) {
@@ -503,107 +506,91 @@ func certificateFromPEM(pemBytes string) (*Certificate, error) {
return ParseCertificate(block.Bytes)
}
-func testVerify(t *testing.T, useSystemRoots bool) {
- defer func(savedIgnoreCN bool) {
- ignoreCN = savedIgnoreCN
- }(ignoreCN)
- for i, test := range verifyTests {
- if useSystemRoots && test.systemSkip {
- continue
- }
- if runtime.GOOS == "windows" && test.testSystemRootsError {
- continue
- }
-
- ignoreCN = test.ignoreCN
- opts := VerifyOptions{
- Intermediates: NewCertPool(),
- DNSName: test.dnsName,
- CurrentTime: time.Unix(test.currentTime, 0),
- KeyUsages: test.keyUsages,
- }
+func testVerify(t *testing.T, test verifyTest, useSystemRoots bool) {
+ defer func(savedIgnoreCN bool) { ignoreCN = savedIgnoreCN }(ignoreCN)
- if !useSystemRoots {
- opts.Roots = NewCertPool()
- for j, root := range test.roots {
- ok := opts.Roots.AppendCertsFromPEM([]byte(root))
- if !ok {
- t.Errorf("#%d: failed to parse root #%d", i, j)
- return
- }
- }
- }
+ ignoreCN = test.ignoreCN
+ opts := VerifyOptions{
+ Intermediates: NewCertPool(),
+ DNSName: test.dnsName,
+ CurrentTime: time.Unix(test.currentTime, 0),
+ KeyUsages: test.keyUsages,
+ }
- for j, intermediate := range test.intermediates {
- ok := opts.Intermediates.AppendCertsFromPEM([]byte(intermediate))
+ if !useSystemRoots {
+ opts.Roots = NewCertPool()
+ for j, root := range test.roots {
+ ok := opts.Roots.AppendCertsFromPEM([]byte(root))
if !ok {
- t.Errorf("#%d: failed to parse intermediate #%d", i, j)
- return
+ t.Fatalf("failed to parse root #%d", j)
}
}
+ }
- leaf, err := certificateFromPEM(test.leaf)
- if err != nil {
- t.Errorf("#%d: failed to parse leaf: %v", i, err)
- return
- }
-
- var oldSystemRoots *CertPool
- if test.testSystemRootsError {
- oldSystemRoots = systemRootsPool()
- systemRoots = nil
- opts.Roots = nil
+ for j, intermediate := range test.intermediates {
+ ok := opts.Intermediates.AppendCertsFromPEM([]byte(intermediate))
+ if !ok {
+ t.Fatalf("failed to parse intermediate #%d", j)
}
+ }
- chains, err := leaf.Verify(opts)
+ leaf, err := certificateFromPEM(test.leaf)
+ if err != nil {
+ t.Fatalf("failed to parse leaf: %v", err)
+ }
- if test.testSystemRootsError {
- systemRoots = oldSystemRoots
- }
+ chains, err := leaf.Verify(opts)
- if test.errorCallback == nil && err != nil {
- t.Errorf("#%d: unexpected error: %v", i, err)
- }
- if test.errorCallback != nil {
- if !test.errorCallback(t, i, err) {
- return
+ if test.errorCallback == nil && err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if test.errorCallback != nil {
+ if useSystemRoots && test.systemLax {
+ if err == nil {
+ t.Fatalf("expected error")
}
+ } else {
+ test.errorCallback(t, err)
}
+ }
- if len(chains) != len(test.expectedChains) {
- t.Errorf("#%d: wanted %d chains, got %d", i, len(test.expectedChains), len(chains))
- }
+ if len(chains) != len(test.expectedChains) {
+ t.Errorf("wanted %d chains, got %d", len(test.expectedChains), len(chains))
+ }
- // We check that each returned chain matches a chain from
- // expectedChains but an entry in expectedChains can't match
- // two chains.
- seenChains := make([]bool, len(chains))
- NextOutputChain:
- for _, chain := range chains {
- TryNextExpected:
- for j, expectedChain := range test.expectedChains {
- if seenChains[j] {
- continue
- }
- if len(chain) != len(expectedChain) {
- continue
- }
- for k, cert := range chain {
- if !strings.Contains(nameToKey(&cert.Subject), expectedChain[k]) {
- continue TryNextExpected
- }
+ // We check that each returned chain matches a chain from
+ // expectedChains but an entry in expectedChains can't match
+ // two chains.
+ seenChains := make([]bool, len(chains))
+NextOutputChain:
+ for _, chain := range chains {
+ TryNextExpected:
+ for j, expectedChain := range test.expectedChains {
+ if seenChains[j] {
+ continue
+ }
+ if len(chain) != len(expectedChain) {
+ continue
+ }
+ for k, cert := range chain {
+ if !strings.Contains(nameToKey(&cert.Subject), expectedChain[k]) {
+ continue TryNextExpected
}
- // we matched
- seenChains[j] = true
- continue NextOutputChain
}
- t.Errorf("#%d: No expected chain matched %s", i, chainToDebugString(chain))
+ // we matched
+ seenChains[j] = true
+ continue NextOutputChain
}
+ t.Errorf("no expected chain matched %s", chainToDebugString(chain))
}
}
func TestGoVerify(t *testing.T) {
- testVerify(t, false)
+ for _, test := range verifyTests {
+ t.Run(test.name, func(t *testing.T) {
+ testVerify(t, test, false)
+ })
+ }
}
func TestSystemVerify(t *testing.T) {
@@ -611,7 +598,14 @@ func TestSystemVerify(t *testing.T) {
t.Skipf("skipping verify test using system APIs on %q", runtime.GOOS)
}
- testVerify(t, true)
+ for _, test := range verifyTests {
+ t.Run(test.name, func(t *testing.T) {
+ if test.systemSkip {
+ t.SkipNow()
+ }
+ testVerify(t, test, true)
+ })
+ }
}
func chainToDebugString(chain []*Certificate) string {
@@ -648,8 +642,7 @@ tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
------END CERTIFICATE-----
-`
+-----END CERTIFICATE-----`
const giag2Intermediate = `-----BEGIN CERTIFICATE-----
MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
@@ -674,8 +667,7 @@ zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza
HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto
WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6
yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx
------END CERTIFICATE-----
-`
+-----END CERTIFICATE-----`
const googleLeaf = `-----BEGIN CERTIFICATE-----
MIIEdjCCA16gAwIBAgIIcR5k4dkoe04wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
@@ -702,8 +694,7 @@ tJAW0kYGJ+wqKm53wG/JaOADTnnq2Mt/j6F2uvjgN/ouns1nRHufIvd370N0LeH+
orKqTuAPzXK7imQk6+OycYABbqCtC/9qmwRd8wwn7sF97DtYfK8WuNHtFalCAwyi
8LxJJYJCLWoMhZ+V8GZm+FOex5qkQAjnZrtNlbQJ8ro4r+rpKXtmMFFhfa+7L+PA
Kom08eUK8skxAzfDDijZPh10VtJ66uBoiDPdT+uCBehcBIcmSTrKjFGX
------END CERTIFICATE-----
-`
+-----END CERTIFICATE-----`
// googleLeafWithInvalidHash is the same as googleLeaf, but the signature
// algorithm in the certificate contains a nonsense OID.
@@ -732,8 +723,7 @@ tJAW0kYGJ+wqKm53wG/JaOADTnnq2Mt/j6F2uvjgN/ouns1nRHufIvd370N0LeH+
orKqTuAPzXK7imQk6+OycYABbqCtC/9qmwRd8wwn7sF97DtYfK8WuNHtFalCAwyi
8LxJJYJCLWoMhZ+V8GZm+FOex5qkQAjnZrtNlbQJ8ro4r+rpKXtmMFFhfa+7L+PA
Kom08eUK8skxAzfDDijZPh10VtJ66uBoiDPdT+uCBehcBIcmSTrKjFGX
------END CERTIFICATE-----
-`
+-----END CERTIFICATE-----`
const dnssecExpLeaf = `-----BEGIN CERTIFICATE-----
MIIGzTCCBbWgAwIBAgIDAdD6MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ
@@ -858,58 +848,127 @@ NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
-----END CERTIFICATE-----`
const smimeLeaf = `-----BEGIN CERTIFICATE-----
-MIIFBjCCA+6gAwIBAgISESFvrjT8XcJTEe6rBlPptILlMA0GCSqGSIb3DQEBBQUA
-MFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSowKAYD
-VQQDEyFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiAyIENBIC0gRzIwHhcNMTIwMTIz
-MTYzNjU5WhcNMTUwMTIzMTYzNjU5WjCBlDELMAkGA1UEBhMCVVMxFjAUBgNVBAgT
-DU5ldyBIYW1zcGhpcmUxEzARBgNVBAcTClBvcnRzbW91dGgxGTAXBgNVBAoTEEds
-b2JhbFNpZ24sIEluYy4xEzARBgNVBAMTClJ5YW4gSHVyc3QxKDAmBgkqhkiG9w0B
-CQEWGXJ5YW4uaHVyc3RAZ2xvYmFsc2lnbi5jb20wggEiMA0GCSqGSIb3DQEBAQUA
-A4IBDwAwggEKAoIBAQC4ASSTvavmsFQAob60ukSSwOAL9nT/s99ltNUCAf5fPH5j
-NceMKxaQse2miOmRRIXaykcq1p/TbI70Ztce38r2mbOwqDHHPVi13GxJEyUXWgaR
-BteDMu5OGyWNG1kchVsGWpbstT0Z4v0md5m1BYFnxB20ebJyOR2lXDxsFK28nnKV
-+5eMj76U8BpPQ4SCH7yTMG6y0XXsB3cCrBKr2o3TOYgEKv+oNnbaoMt3UxMt9nSf
-9jyIshjqfnT5Aew3CUNMatO55g5FXXdIukAweg1YSb1ls05qW3sW00T3d7dQs9/7
-NuxCg/A2elmVJSoy8+MLR8JSFEf/aMgjO/TyLg/jAgMBAAGjggGPMIIBizAOBgNV
-HQ8BAf8EBAMCBaAwTQYDVR0gBEYwRDBCBgorBgEEAaAyASgKMDQwMgYIKwYBBQUH
-AgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMCQGA1Ud
-EQQdMBuBGXJ5YW4uaHVyc3RAZ2xvYmFsc2lnbi5jb20wCQYDVR0TBAIwADAdBgNV
-HSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwQwYDVR0fBDwwOjA4oDagNIYyaHR0
-cDovL2NybC5nbG9iYWxzaWduLmNvbS9ncy9nc3BlcnNvbmFsc2lnbjJnMi5jcmww
-VQYIKwYBBQUHAQEESTBHMEUGCCsGAQUFBzAChjlodHRwOi8vc2VjdXJlLmdsb2Jh
-bHNpZ24uY29tL2NhY2VydC9nc3BlcnNvbmFsc2lnbjJnMi5jcnQwHQYDVR0OBBYE
-FFWiECe0/L72eVYqcWYnLV6SSjzhMB8GA1UdIwQYMBaAFD8V0m18L+cxnkMKBqiU
-bCw7xe5lMA0GCSqGSIb3DQEBBQUAA4IBAQAhQi6hLPeudmf3IBF4IDzCvRI0FaYd
-BKfprSk/H0PDea4vpsLbWpA0t0SaijiJYtxKjlM4bPd+2chb7ejatDdyrZIzmDVy
-q4c30/xMninGKokpYA11/Ve+i2dvjulu65qasrtQRGybAuuZ67lrp/K3OMFgjV5N
-C3AHYLzvNU4Dwc4QQ1BaMOg6KzYSrKbABRZajfrpC9uiePsv7mDIXLx/toBPxWNl
-a5vJm5DrZdn7uHdvBCE6kMykbOLN5pmEK0UIlwKh6Qi5XD0pzlVkEZliFkBMJgub
-d/eF7xeg7TKPWC5xyOFp9SdMolJM7LTC3wnSO3frBAev+q/nGs9Xxyvs
+MIIIPDCCBiSgAwIBAgIQaMDxFS0pOMxZZeOBxoTJtjANBgkqhkiG9w0BAQsFADCB
+nTELMAkGA1UEBhMCRVMxFDASBgNVBAoMC0laRU5QRSBTLkEuMTowOAYDVQQLDDFB
+WlogWml1cnRhZ2lyaSBwdWJsaWtvYSAtIENlcnRpZmljYWRvIHB1YmxpY28gU0NB
+MTwwOgYDVQQDDDNFQUVrbyBIZXJyaSBBZG1pbmlzdHJhemlvZW4gQ0EgLSBDQSBB
+QVBQIFZhc2NhcyAoMikwHhcNMTcwNzEyMDg1MzIxWhcNMjEwNzEyMDg1MzIxWjCC
+AQwxDzANBgNVBAoMBklaRU5QRTE4MDYGA1UECwwvWml1cnRhZ2lyaSBrb3Jwb3Jh
+dGlib2EtQ2VydGlmaWNhZG8gY29ycG9yYXRpdm8xQzBBBgNVBAsMOkNvbmRpY2lv
+bmVzIGRlIHVzbyBlbiB3d3cuaXplbnBlLmNvbSBub2xhIGVyYWJpbGkgamFraXRl
+a28xFzAVBgNVBC4TDi1kbmkgOTk5OTk5ODlaMSQwIgYDVQQDDBtDT1JQT1JBVElW
+TyBGSUNUSUNJTyBBQ1RJVk8xFDASBgNVBCoMC0NPUlBPUkFUSVZPMREwDwYDVQQE
+DAhGSUNUSUNJTzESMBAGA1UEBRMJOTk5OTk5ODlaMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAwVOMwUDfBtsH0XuxYnb+v/L774jMH8valX7RPH8cl2Lb
+SiqSo0RchW2RGA2d1yuYHlpChC9jGmt0X/g66/E/+q2hUJlfJtqVDJFwtFYV4u2S
+yzA3J36V4PRkPQrKxAsbzZriFXAF10XgiHQz9aVeMMJ9GBhmh9+DK8Tm4cMF6i8l
++AuC35KdngPF1x0ealTYrYZplpEJFO7CiW42aLi6vQkDR2R7nmZA4AT69teqBWsK
+0DZ93/f0G/3+vnWwNTBF0lB6dIXoaz8OMSyHLqGnmmAtMrzbjAr/O/WWgbB/BqhR
+qjJQ7Ui16cuDldXaWQ/rkMzsxmsAox0UF+zdQNvXUQIDAQABo4IDBDCCAwAwgccG
+A1UdEgSBvzCBvIYVaHR0cDovL3d3dy5pemVucGUuY29tgQ9pbmZvQGl6ZW5wZS5j
+b22kgZEwgY4xRzBFBgNVBAoMPklaRU5QRSBTLkEuIC0gQ0lGIEEwMTMzNzI2MC1S
+TWVyYy5WaXRvcmlhLUdhc3RlaXogVDEwNTUgRjYyIFM4MUMwQQYDVQQJDDpBdmRh
+IGRlbCBNZWRpdGVycmFuZW8gRXRvcmJpZGVhIDE0IC0gMDEwMTAgVml0b3JpYS1H
+YXN0ZWl6MB4GA1UdEQQXMBWBE2ZpY3RpY2lvQGl6ZW5wZS5ldXMwDgYDVR0PAQH/
+BAQDAgXgMCkGA1UdJQQiMCAGCCsGAQUFBwMCBggrBgEFBQcDBAYKKwYBBAGCNxQC
+AjAdBgNVHQ4EFgQUyeoOD4cgcljKY0JvrNuX2waFQLAwHwYDVR0jBBgwFoAUwKlK
+90clh/+8taaJzoLSRqiJ66MwggEnBgNVHSAEggEeMIIBGjCCARYGCisGAQQB8zkB
+AQEwggEGMDMGCCsGAQUFBwIBFidodHRwOi8vd3d3Lml6ZW5wZS5jb20vcnBhc2Nh
+Y29ycG9yYXRpdm8wgc4GCCsGAQUFBwICMIHBGoG+Wml1cnRhZ2lyaWEgRXVza2Fs
+IEF1dG9ub21pYSBFcmtpZGVnb2tvIHNla3RvcmUgcHVibGlrb2tvIGVyYWt1bmRl
+ZW4gYmFybmUtc2FyZWV0YW4gYmFrYXJyaWsgZXJhYmlsIGRhaXRla2UuIFVzbyBy
+ZXN0cmluZ2lkbyBhbCBhbWJpdG8gZGUgcmVkZXMgaW50ZXJuYXMgZGUgRW50aWRh
+ZGVzIGRlbCBTZWN0b3IgUHVibGljbyBWYXNjbzAyBggrBgEFBQcBAQQmMCQwIgYI
+KwYBBQUHMAGGFmh0dHA6Ly9vY3NwLml6ZW5wZS5jb20wOgYDVR0fBDMwMTAvoC2g
+K4YpaHR0cDovL2NybC5pemVucGUuY29tL2NnaS1iaW4vY3JsaW50ZXJuYTIwDQYJ
+KoZIhvcNAQELBQADggIBAIy5PQ+UZlCRq6ig43vpHwlwuD9daAYeejV0Q+ZbgWAE
+GtO0kT/ytw95ZEJMNiMw3fYfPRlh27ThqiT0VDXZJDlzmn7JZd6QFcdXkCsiuv4+
+ZoXAg/QwnA3SGUUO9aVaXyuOIIuvOfb9MzoGp9xk23SMV3eiLAaLMLqwB5DTfBdt
+BGI7L1MnGJBv8RfP/TL67aJ5bgq2ri4S8vGHtXSjcZ0+rCEOLJtmDNMnTZxancg3
+/H5edeNd+n6Z48LO+JHRxQufbC4mVNxVLMIP9EkGUejlq4E4w6zb5NwCQczJbSWL
+i31rk2orsNsDlyaLGsWZp3JSNX6RmodU4KAUPor4jUJuUhrrm3Spb73gKlV/gcIw
+bCE7mML1Kss3x1ySaXsis6SZtLpGWKkW2iguPWPs0ydV6RPhmsCxieMwPPIJ87vS
+5IejfgyBae7RSuAIHyNFy4uI5xwvwUFf6OZ7az8qtW7ImFOgng3Ds+W9k1S2CNTx
+d0cnKTfA6IpjGo8EeHcxnIXT8NPImWaRj0qqonvYady7ci6U4m3lkNSdXNn1afgw
+mYust+gxVtOZs1gk2MUCgJ1V1X+g7r/Cg7viIn6TLkLrpS1kS1hvMqkl9M+7XqPo
+Qd95nJKOkusQpy99X4dF/lfbYAQnnjnqh3DLD2gvYObXFaAYFaiBKTiMTV2X72F+
-----END CERTIFICATE-----`
const smimeIntermediate = `-----BEGIN CERTIFICATE-----
-MIIEFjCCAv6gAwIBAgILBAAAAAABL07hL1IwDQYJKoZIhvcNAQEFBQAwVzELMAkG
-A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
-b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw
-MDBaFw0xOTA0MTMxMDAwMDBaMFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
-YWxTaWduIG52LXNhMSowKAYDVQQDEyFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiAy
-IENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBa0H5Nez4
-En3dIlFpX7e5E0YndxQ74xOBbz7kdBd+DLX0LOQMjVPU3DAgKL9ujhH+ZhHkURbH
-3X/94TQSUL/z2JjsaQvS0NqyZXHhM5eeuquzOJRzEQ8+odETzHg2G0Erv7yjSeww
-gkwDWDJnYUDlOjYTDUEG6+i+8Mn425reo4I0E277wD542kmVWeW7+oHv5dZo9e1Q
-yWwiKTEP6BEQVVSBgThXMG4traSSDRUt3T1eQTZx5EObpiBEBO4OTqiBTJfg4vEI
-YgkXzKLpnfszTB6YMDpR9/QS6p3ANB3kfAb+t6udSO3WCst0DGrwHDLBFGDR4UeY
-T5KGGnI7cWL7AgMBAAGjgeUwgeIwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI
-MAYBAf8CAQAwHQYDVR0OBBYEFD8V0m18L+cxnkMKBqiUbCw7xe5lMEcGA1UdIARA
-MD4wPAYEVR0gADA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWdu
-LmNvbS9yZXBvc2l0b3J5LzAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmds
-b2JhbHNpZ24ubmV0L3Jvb3QuY3JsMB8GA1UdIwQYMBaAFGB7ZhpFDZfKiVAvfQTN
-NKj//P1LMA0GCSqGSIb3DQEBBQUAA4IBAQBDc3nMpMxJMQMcYUCB3+C73UpvwDE8
-eCOr7t2F/uaQKKcyqqstqLZc6vPwI/rcE9oDHugY5QEjQzIBIEaTnN6P0vege2IX
-eCOr7t2F/uaQKKcyqqstqLZc6vPwI/rcE9oDHugY5QEjQzIBIEaTnN6P0vege2IX
-YEvTWbWwGdPytDFPYIl3/6OqNSXSnZ7DxPcdLJq2uyiga8PB/TTIIHYkdM2+1DE0
-7y3rH/7TjwDVD7SLu5/SdOfKskuMPTjOEvz3K161mymW06klVhubCIWOro/Gx1Q2
-2FQOZ7/2k4uYoOdBTSlb8kTAuzZNgIE0rB2BIYCTz/P6zZIKW0ogbRSH
+MIIHNzCCBSGgAwIBAgIQJMXIqlZvjuhMvqcFXOFkpDALBgkqhkiG9w0BAQswODEL
+MAkGA1UEBhMCRVMxFDASBgNVBAoMC0laRU5QRSBTLkEuMRMwEQYDVQQDDApJemVu
+cGUuY29tMB4XDTEwMTAyMDA4MjMzM1oXDTM3MTIxMjIzMDAwMFowgZ0xCzAJBgNV
+BAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjE6MDgGA1UECwwxQVpaIFppdXJ0
+YWdpcmkgcHVibGlrb2EgLSBDZXJ0aWZpY2FkbyBwdWJsaWNvIFNDQTE8MDoGA1UE
+AwwzRUFFa28gSGVycmkgQWRtaW5pc3RyYXppb2VuIENBIC0gQ0EgQUFQUCBWYXNj
+YXMgKDIpMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoIM7nEdI0N1h
+rR5T4xuV/usKDoMIasaiKvfLhbwxaNtTt+a7W/6wV5bv3svQFIy3sUXjjdzV1nG2
+To2wo/YSPQiOt8exWvOapvL21ogiof+kelWnXFjWaKJI/vThHYLgIYEMj/y4HdtU
+ojI646rZwqsb4YGAopwgmkDfUh5jOhV2IcYE3TgJAYWVkj6jku9PLaIsHiarAHjD
+PY8dig8a4SRv0gm5Yk7FXLmW1d14oxQBDeHZ7zOEXfpafxdEDO2SNaRJjpkh8XRr
+PGqkg2y1Q3gT6b4537jz+StyDIJ3omylmlJsGCwqT7p8mEqjGJ5kC5I2VnjXKuNn
+soShc72khWZVUJiJo5SGuAkNE2ZXqltBVm5Jv6QweQKsX6bkcMc4IZok4a+hx8FM
+8IBpGf/I94pU6HzGXqCyc1d46drJgDY9mXa+6YDAJFl3xeXOOW2iGCfwXqhiCrKL
+MYvyMZzqF3QH5q4nb3ZnehYvraeMFXJXDn+Utqp8vd2r7ShfQJz01KtM4hgKdgSg
+jtW+shkVVN5ng/fPN85ovfAH2BHXFfHmQn4zKsYnLitpwYM/7S1HxlT61cdQ7Nnk
+3LZTYEgAoOmEmdheklT40WAYakksXGM5VrzG7x9S7s1Tm+Vb5LSThdHC8bxxwyTb
+KsDRDNJ84N9fPDO6qHnzaL2upQ43PycCAwEAAaOCAdkwggHVMIHHBgNVHREEgb8w
+gbyGFWh0dHA6Ly93d3cuaXplbnBlLmNvbYEPaW5mb0BpemVucGUuY29tpIGRMIGO
+MUcwRQYDVQQKDD5JWkVOUEUgUy5BLiAtIENJRiBBMDEzMzcyNjAtUk1lcmMuVml0
+b3JpYS1HYXN0ZWl6IFQxMDU1IEY2MiBTODFDMEEGA1UECQw6QXZkYSBkZWwgTWVk
+aXRlcnJhbmVvIEV0b3JiaWRlYSAxNCAtIDAxMDEwIFZpdG9yaWEtR2FzdGVpejAP
+BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUwKlK90cl
+h/+8taaJzoLSRqiJ66MwHwYDVR0jBBgwFoAUHRxlDqjyJXu0kc/ksbHmvVV0bAUw
+OgYDVR0gBDMwMTAvBgRVHSAAMCcwJQYIKwYBBQUHAgEWGWh0dHA6Ly93d3cuaXpl
+bnBlLmNvbS9jcHMwNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8v
+b2NzcC5pemVucGUuY29tOjgwOTQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2Ny
+bC5pemVucGUuY29tL2NnaS1iaW4vYXJsMjALBgkqhkiG9w0BAQsDggIBAMbjc3HM
+3DG9ubWPkzsF0QsktukpujbTTcGk4h20G7SPRy1DiiTxrRzdAMWGjZioOP3/fKCS
+M539qH0M+gsySNie+iKlbSZJUyE635T1tKw+G7bDUapjlH1xyv55NC5I6wCXGC6E
+3TEP5B/E7dZD0s9E4lS511ubVZivFgOzMYo1DO96diny/N/V1enaTCpRl1qH1OyL
+xUYTijV4ph2gL6exwuG7pxfRcVNHYlrRaXWfTz3F6NBKyULxrI3P/y6JAtN1GqT4
+VF/+vMygx22n0DufGepBwTQz6/rr1ulSZ+eMnuJiTXgh/BzQnkUsXTb8mHII25iR
+0oYF2qAsk6ecWbLiDpkHKIDHmML21MZE13MS8NSvTHoqJO4LyAmDe6SaeNHtrPlK
+b6mzE1BN2ug+ZaX8wLA5IMPFaf0jKhb/Cxu8INsxjt00brsErCc9ip1VNaH0M4bi
+1tGxfiew2436FaeyUxW7Pl6G5GgkNbuUc7QIoRy06DdU/U38BxW3uyJMY60zwHvS
+FlKAn0OvYp4niKhAJwaBVN3kowmJuOU5Rid+TUnfyxbJ9cttSgzaF3hP/N4zgMEM
+5tikXUskeckt8LUK96EH0QyssavAMECUEb/xrupyRdYWwjQGvNLq6T5+fViDGyOw
+k+lzD44wofy8paAy9uC9Owae0zMEzhcsyRm7
+-----END CERTIFICATE-----`
+
+const smimeRoot = `-----BEGIN CERTIFICATE-----
+MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4
+MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6
+ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD
+VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j
+b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq
+scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO
+xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H
+LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX
+uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD
+yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+
+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q
+rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN
+BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L
+hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB
+QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+
+HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu
+Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg
+QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB
+BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
+MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA
+A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb
+laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56
+awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo
+JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw
+LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT
+VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk
+LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb
+UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/
+QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+
+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls
+QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
-----END CERTIFICATE-----`
var megaLeaf = `-----BEGIN CERTIFICATE-----
@@ -1315,50 +1374,7 @@ vRAvOtNiKtPzFeQVdbRPOskC4rcHyPeiDAMAMixeLi63+CFty4da3r5lRezeedCE
cw3ESZzThBwWqvPOtJdpXdm+r57pDW8qD+/0lY8wfImMNkQAyCUCLg/1Lxt/hrBj
-----END CERTIFICATE-----`
-const issuerSubjectMatchRoot = `
-Certificate:
- Data:
- Version: 3 (0x2)
- Serial Number: 161640039802297062 (0x23e42c281e55ae6)
- Signature Algorithm: sha256WithRSAEncryption
- Issuer: O=Golang, CN=Root ca
- Validity
- Not Before: Jan 1 00:00:00 2015 GMT
- Not After : Jan 1 00:00:00 2025 GMT
- Subject: O=Golang, CN=Root ca
- Subject Public Key Info:
- Public Key Algorithm: rsaEncryption
- Public-Key: (1024 bit)
- Modulus:
- 00:e9:0e:7f:11:0c:e6:5a:e6:86:83:70:f6:51:07:
- 2e:02:78:11:f5:b2:24:92:38:ee:26:62:02:c7:94:
- f1:3e:a1:77:6a:c0:8f:d5:22:68:b6:5d:e2:4c:da:
- e0:85:11:35:c2:92:72:49:8d:81:b4:88:97:6b:b7:
- fc:b2:44:5b:d9:4d:06:70:f9:0c:c6:8f:e9:b3:df:
- a3:6a:84:6c:43:59:be:9d:b2:d0:76:9b:c3:d7:fa:
- 99:59:c3:b8:e5:f3:53:03:bd:49:d6:b3:cc:a2:43:
- fe:ad:c2:0b:b9:01:b8:56:29:94:03:24:a7:0d:28:
- 21:29:a9:ae:94:5b:4a:f9:9f
- Exponent: 65537 (0x10001)
- X509v3 extensions:
- X509v3 Key Usage: critical
- Certificate Sign
- X509v3 Extended Key Usage:
- TLS Web Server Authentication, TLS Web Client Authentication
- X509v3 Basic Constraints: critical
- CA:TRUE
- X509v3 Subject Key Identifier:
- 40:37:D7:01:FB:40:2F:B8:1C:7E:54:04:27:8C:59:01
- Signature Algorithm: sha256WithRSAEncryption
- 6f:84:df:49:e0:99:d4:71:66:1d:32:86:56:cb:ea:5a:6b:0e:
- 00:6a:d1:5a:6e:1f:06:23:07:ff:cb:d1:1a:74:e4:24:43:0b:
- aa:2a:a0:73:75:25:82:bc:bf:3f:a9:f8:48:88:ac:ed:3a:94:
- 3b:0d:d3:88:c8:67:44:61:33:df:71:6c:c5:af:ed:16:8c:bf:
- 82:f9:49:bb:e3:2a:07:53:36:37:25:77:de:91:a4:77:09:7f:
- 6f:b2:91:58:c4:05:89:ea:8e:fa:e1:3b:19:ef:f8:f6:94:b7:
- 7b:27:e6:e4:84:dd:2b:f5:93:f5:3c:d8:86:c5:38:01:56:5c:
- 9f:6d
------BEGIN CERTIFICATE-----
+const issuerSubjectMatchRoot = `-----BEGIN CERTIFICATE-----
MIICIDCCAYmgAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwIzEPMA0GA1UE
ChMGR29sYW5nMRAwDgYDVQQDEwdSb290IGNhMB4XDTE1MDEwMTAwMDAwMFoXDTI1
MDEwMTAwMDAwMFowIzEPMA0GA1UEChMGR29sYW5nMRAwDgYDVQQDEwdSb290IGNh
@@ -1373,53 +1389,7 @@ RGEz33Fsxa/tFoy/gvlJu+MqB1M2NyV33pGkdwl/b7KRWMQFieqO+uE7Ge/49pS3
eyfm5ITdK/WT9TzYhsU4AVZcn20=
-----END CERTIFICATE-----`
-const issuerSubjectMatchLeaf = `
-Certificate:
- Data:
- Version: 3 (0x2)
- Serial Number: 16785088708916013734 (0xe8f09d3fe25beaa6)
- Signature Algorithm: sha256WithRSAEncryption
- Issuer: O=Golang, CN=Root CA
- Validity
- Not Before: Jan 1 00:00:00 2015 GMT
- Not After : Jan 1 00:00:00 2025 GMT
- Subject: O=Golang, CN=Leaf
- Subject Public Key Info:
- Public Key Algorithm: rsaEncryption
- Public-Key: (1024 bit)
- Modulus:
- 00:db:46:7d:93:2e:12:27:06:48:bc:06:28:21:ab:
- 7e:c4:b6:a2:5d:fe:1e:52:45:88:7a:36:47:a5:08:
- 0d:92:42:5b:c2:81:c0:be:97:79:98:40:fb:4f:6d:
- 14:fd:2b:13:8b:c2:a5:2e:67:d8:d4:09:9e:d6:22:
- 38:b7:4a:0b:74:73:2b:c2:34:f1:d1:93:e5:96:d9:
- 74:7b:f3:58:9f:6c:61:3c:c0:b0:41:d4:d9:2b:2b:
- 24:23:77:5b:1c:3b:bd:75:5d:ce:20:54:cf:a1:63:
- 87:1d:1e:24:c4:f3:1d:1a:50:8b:aa:b6:14:43:ed:
- 97:a7:75:62:f4:14:c8:52:d7
- Exponent: 65537 (0x10001)
- X509v3 extensions:
- X509v3 Key Usage: critical
- Digital Signature, Key Encipherment
- X509v3 Extended Key Usage:
- TLS Web Server Authentication, TLS Web Client Authentication
- X509v3 Basic Constraints: critical
- CA:FALSE
- X509v3 Subject Key Identifier:
- 9F:91:16:1F:43:43:3E:49:A6:DE:6D:B6:80:D7:9F:60
- X509v3 Authority Key Identifier:
- keyid:40:37:D7:01:FB:40:2F:B8:1C:7E:54:04:27:8C:59:01
-
- Signature Algorithm: sha256WithRSAEncryption
- 8d:86:05:da:89:f5:1d:c5:16:14:41:b9:34:87:2b:5c:38:99:
- e3:d9:5a:5b:7a:5b:de:0b:5c:08:45:09:6f:1c:9d:31:5f:08:
- ca:7a:a3:99:da:83:0b:22:be:4f:02:35:91:4e:5d:5c:37:bf:
- 89:22:58:7d:30:76:d2:2f:d0:a0:ee:77:9e:77:c0:d6:19:eb:
- ec:a0:63:35:6a:80:9b:80:1a:80:de:64:bc:40:38:3c:22:69:
- ad:46:26:a2:3d:ea:f4:c2:92:49:16:03:96:ae:64:21:b9:7c:
- ee:64:91:47:81:aa:b4:0c:09:2b:12:1a:b2:f3:af:50:b3:b1:
- ce:24
------BEGIN CERTIFICATE-----
+const issuerSubjectMatchLeaf = `-----BEGIN CERTIFICATE-----
MIICODCCAaGgAwIBAgIJAOjwnT/iW+qmMA0GCSqGSIb3DQEBCwUAMCMxDzANBgNV
BAoTBkdvbGFuZzEQMA4GA1UEAxMHUm9vdCBDQTAeFw0xNTAxMDEwMDAwMDBaFw0y
NTAxMDEwMDAwMDBaMCAxDzANBgNVBAoTBkdvbGFuZzENMAsGA1UEAxMETGVhZjCB
@@ -1432,11 +1402,9 @@ Q0M+SabebbaA159gMBsGA1UdIwQUMBKAEEA31wH7QC+4HH5UBCeMWQEwDQYJKoZI
hvcNAQELBQADgYEAjYYF2on1HcUWFEG5NIcrXDiZ49laW3pb3gtcCEUJbxydMV8I
ynqjmdqDCyK+TwI1kU5dXDe/iSJYfTB20i/QoO53nnfA1hnr7KBjNWqAm4AagN5k
vEA4PCJprUYmoj3q9MKSSRYDlq5kIbl87mSRR4GqtAwJKxIasvOvULOxziQ=
------END CERTIFICATE-----
-`
+-----END CERTIFICATE-----`
-const x509v1TestRoot = `
------BEGIN CERTIFICATE-----
+const x509v1TestRoot = `-----BEGIN CERTIFICATE-----
MIICIDCCAYmgAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwIzEPMA0GA1UE
ChMGR29sYW5nMRAwDgYDVQQDEwdSb290IENBMB4XDTE1MDEwMTAwMDAwMFoXDTI1
MDEwMTAwMDAwMFowIzEPMA0GA1UEChMGR29sYW5nMRAwDgYDVQQDEwdSb290IENB
@@ -1451,8 +1419,7 @@ h2NtN34ard0hEfHc8qW8mkXdsysVmq6cPvFYaHz+dBtkHuHDoy8YQnC0zdN/WyYB
/1JmacUUofl+HusHuLkDxmadogI=
-----END CERTIFICATE-----`
-const x509v1TestIntermediate = `
------BEGIN CERTIFICATE-----
+const x509v1TestIntermediate = `-----BEGIN CERTIFICATE-----
MIIByjCCATMCCQCCdEMsT8ykqTANBgkqhkiG9w0BAQsFADAjMQ8wDQYDVQQKEwZH
b2xhbmcxEDAOBgNVBAMTB1Jvb3QgQ0EwHhcNMTUwMTAxMDAwMDAwWhcNMjUwMTAx
MDAwMDAwWjAwMQ8wDQYDVQQKEwZHb2xhbmcxHTAbBgNVBAMTFFguNTA5djEgaW50
@@ -1465,8 +1432,7 @@ zWE77kJDibzd141u21ZbLsKvEdUJXjla43bdyMmEqf5VGpC3D4sFt3QVH7lGeRur
x5Wlq1u3YDL/j6s1nU2dQ3ySB/oP7J+vQ9V4QeM+
-----END CERTIFICATE-----`
-const x509v1TestLeaf = `
------BEGIN CERTIFICATE-----
+const x509v1TestLeaf = `-----BEGIN CERTIFICATE-----
MIICMzCCAZygAwIBAgIJAPo99mqJJrpJMA0GCSqGSIb3DQEBCwUAMDAxDzANBgNV
BAoTBkdvbGFuZzEdMBsGA1UEAxMUWC41MDl2MSBpbnRlcm1lZGlhdGUwHhcNMTUw
MTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjArMQ8wDQYDVQQKEwZHb2xhbmcxGDAW
@@ -1481,8 +1447,7 @@ CwUAA4GBADYzYUvaToO/ucBskPdqXV16AaakIhhSENswYVSl97/sODaxsjishKq9
/jt8qszOXCv2vYdUTPNuPqufXLWMoirpuXrr1liJDmedCcAHepY/
-----END CERTIFICATE-----`
-const ignoreCNWithSANRoot = `
------BEGIN CERTIFICATE-----
+const ignoreCNWithSANRoot = `-----BEGIN CERTIFICATE-----
MIIDPzCCAiegAwIBAgIIJkzCwkNrPHMwDQYJKoZIhvcNAQELBQAwMDEQMA4GA1UE
ChMHVEVTVElORzEcMBoGA1UEAxMTKipUZXN0aW5nKiogUm9vdCBDQTAeFw0xNTAx
MDEwMDAwMDBaFw0yNTAxMDEwMDAwMDBaMDAxEDAOBgNVBAoTB1RFU1RJTkcxHDAa
@@ -1503,8 +1468,7 @@ aSLjI/Ya0zwUARMmyZ3RRGCyhIarPb20mKSaMf1/Nb23pS3k1QgmZhk5pAnXYsWu
BJ6bvwEAasFiLGP6Zbdmxb2hIA==
-----END CERTIFICATE-----`
-const ignoreCNWithSANLeaf = `
------BEGIN CERTIFICATE-----
+const ignoreCNWithSANLeaf = `-----BEGIN CERTIFICATE-----
MIIDaTCCAlGgAwIBAgIJAONakvRTxgJhMA0GCSqGSIb3DQEBCwUAMDAxEDAOBgNV
BAoTB1RFU1RJTkcxHDAaBgNVBAMTEyoqVGVzdGluZyoqIFJvb3QgQ0EwHhcNMTUw
MTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjAsMRAwDgYDVQQKEwdURVNUSU5HMRgw
@@ -1526,8 +1490,7 @@ j2kBQyvnyKsXHLAKUoUOpd6t/1PHrfXnGj+HmzZNloJ/BZ1kiWb4eLvMljoLGkZn
xZbqP3Krgjj4XNaXjg==
-----END CERTIFICATE-----`
-const excludedNamesLeaf = `
------BEGIN CERTIFICATE-----
+const excludedNamesLeaf = `-----BEGIN CERTIFICATE-----
MIID4DCCAsigAwIBAgIHDUSFtJknhzANBgkqhkiG9w0BAQsFADCBnjELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU
MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5
@@ -1549,11 +1512,9 @@ hDt8MCFJ8eSjCyKdtZh1MPMLrLVymmJV+Rc9JUUYM9TIeERkpl0rskcO1YGewkYt
qKlWE+0S16+pzsWvKn831uylqwIb8ANBPsCX4aM4muFBHavSWAHgRO+P+yXVw8Q+
VQDnMHUe5PbZd1/+1KKVs1K/CkBCtoHNHp1d/JT+2zUQJphwja9CcgfFdVhSnHL4
oEEOFtqVMIuQfR2isi08qW/JGOHc4sFoLYB8hvdaxKWSE19A
------END CERTIFICATE-----
-`
+-----END CERTIFICATE-----`
-const excludedNamesIntermediate = `
------BEGIN CERTIFICATE-----
+const excludedNamesIntermediate = `-----BEGIN CERTIFICATE-----
MIIDzTCCArWgAwIBAgIHDUSFqYeczDANBgkqhkiG9w0BAQsFADCBmTELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU
MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5
@@ -1577,8 +1538,7 @@ LbIjZCSfgZnk/LK1KU1j91FI2bc2ULYZvAC1PAg8/zvIgxn6YM2Q7ZsdEgWw0FpS
zMBX1/lk4wkFckeUIlkD55Y=
-----END CERTIFICATE-----`
-const excludedNamesRoot = `
------BEGIN CERTIFICATE-----
+const excludedNamesRoot = `-----BEGIN CERTIFICATE-----
MIIEGTCCAwGgAwIBAgIHDUSFpInn/zANBgkqhkiG9w0BAQsFADCBozELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU
MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5
@@ -1603,46 +1563,16 @@ yU1yRHUqUYpN0DWFpsPbBqgM6uUAVO2ayBFhPgWUaqkmSbZ/Nq7isGvknaTmcIwT
+NQCZDd5eFeU8PpNX7rgaYE4GPq+EEmLVCBYmdctr8QVdqJ//8Xu3+1phjDy
-----END CERTIFICATE-----`
-const invalidCNRoot = `
------BEGIN 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-----
+-----END CERTIFICATE-----`
+
+const invalidCNWithoutSAN = `-----BEGIN CERTIFICATE-----
MIIBJDCBywIUB7q8t9mrDAL+UB1OFaMN5BEWFKIwCgYIKoZIzj0EAwIwFDESMBAG
A1UECwwJVGVzdCByb290MB4XDTE4MDcxMTE4MzUyMVoXDTI4MDcwODE4MzUyMVow
FjEUMBIGA1UEAwwLZm9vLGludmFsaWQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
@@ -1650,38 +1580,9 @@ 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-----
+-----END CERTIFICATE-----`
+
+const validCNWithoutSAN = `-----BEGIN CERTIFICATE-----
MIIBJzCBzwIUB7q8t9mrDAL+UB1OFaMN5BEWFKQwCgYIKoZIzj0EAwIwFDESMBAG
A1UECwwJVGVzdCByb290MB4XDTE4MDcxMTE4NDcyNFoXDTI4MDcwODE4NDcyNFow
GjEYMBYGA1UEAwwPZm9vLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D
@@ -1689,48 +1590,9 @@ AQcDQgAEp6Z8IjOnR38Iky1fYTUu2kVndvKXcxiwARJKGtW3b0E8uwVp9AZd/+sr
p4ULTPdFToFAeqnGHbu62bkms8pQkDAKBggqhkjOPQQDAgNHADBEAiBTbNe3WWFR
cqUYo0sNUuoV+tCTMDJUS+0PWIW4qBqCOwIgFHdLDn5PCk9kJpfc0O2qZx03hdq0
h7olHCpY9yMRiz0=
------END CERTIFICATE-----
-`
-
-const (
- rootWithoutSKID = `
-Certificate:
- Data:
- Version: 3 (0x2)
- Serial Number:
- 78:29:2a:dc:2f:12:39:7f:c9:33:93:ea:61:39:7d:70
- Signature Algorithm: ecdsa-with-SHA256
- Issuer: O = Acme Co
- Validity
- Not Before: Feb 4 22:56:34 2019 GMT
- Not After : Feb 1 22:56:34 2029 GMT
- Subject: O = Acme Co
- Subject Public Key Info:
- Public Key Algorithm: id-ecPublicKey
- Public-Key: (256 bit)
- pub:
- 04:84:a6:8c:69:53:af:87:4b:39:64:fe:04:24:e6:
- d8:fc:d6:46:39:35:0e:92:dc:48:08:7e:02:5f:1e:
- 07:53:5c:d9:e0:56:c5:82:07:f6:a3:e2:ad:f6:ad:
- be:a0:4e:03:87:39:67:0c:9c:46:91:68:6b:0e:8e:
- f8:49:97:9d:5b
- ASN1 OID: prime256v1
- NIST CURVE: P-256
- X509v3 extensions:
- X509v3 Key Usage: critical
- Digital Signature, Key Encipherment, Certificate Sign
- X509v3 Extended Key Usage:
- TLS Web Server Authentication
- X509v3 Basic Constraints: critical
- CA:TRUE
- X509v3 Subject Alternative Name:
- DNS:example
- Signature Algorithm: ecdsa-with-SHA256
- 30:46:02:21:00:c6:81:61:61:42:8d:37:e7:d0:c3:72:43:44:
- 17:bd:84:ff:88:81:68:9a:99:08:ab:3c:3a:c0:1e:ea:8c:ba:
- c0:02:21:00:de:c9:fa:e5:5e:c6:e2:db:23:64:43:a9:37:42:
- 72:92:7f:6e:89:38:ea:9e:2a:a7:fd:2f:ea:9a:ff:20:21:e7
------BEGIN CERTIFICATE-----
+-----END CERTIFICATE-----`
+
+const rootWithoutSKID = `-----BEGIN CERTIFICATE-----
MIIBbzCCARSgAwIBAgIQeCkq3C8SOX/JM5PqYTl9cDAKBggqhkjOPQQDAjASMRAw
DgYDVQQKEwdBY21lIENvMB4XDTE5MDIwNDIyNTYzNFoXDTI5MDIwMTIyNTYzNFow
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABISm
@@ -1739,49 +1601,9 @@ ZwycRpFoaw6O+EmXnVujTDBKMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MBIGA1UdEQQLMAmCB2V4YW1wbGUwCgYI
KoZIzj0EAwIDSQAwRgIhAMaBYWFCjTfn0MNyQ0QXvYT/iIFompkIqzw6wB7qjLrA
AiEA3sn65V7G4tsjZEOpN0Jykn9uiTjqniqn/S/qmv8gIec=
------END CERTIFICATE-----
-`
- leafWithAKID = `
- Certificate:
- Data:
- Version: 3 (0x2)
- Serial Number:
- f0:8a:62:f0:03:84:a2:cf:69:63:ad:71:3b:b6:5d:8c
- Signature Algorithm: ecdsa-with-SHA256
- Issuer: O = Acme Co
- Validity
- Not Before: Feb 4 23:06:52 2019 GMT
- Not After : Feb 1 23:06:52 2029 GMT
- Subject: O = Acme LLC
- Subject Public Key Info:
- Public Key Algorithm: id-ecPublicKey
- Public-Key: (256 bit)
- pub:
- 04:5a:4e:4d:fb:ff:17:f7:b6:13:e8:29:45:34:81:
- 39:ff:8c:9c:d9:8c:0a:9f:dd:b5:97:4c:2b:20:91:
- 1c:4f:6b:be:53:27:66:ec:4a:ad:08:93:6d:66:36:
- 0c:02:70:5d:01:ca:7f:c3:29:e9:4f:00:ba:b4:14:
- ec:c5:c3:34:b3
- ASN1 OID: prime256v1
- NIST CURVE: P-256
- X509v3 extensions:
- X509v3 Key Usage: critical
- Digital Signature, Key Encipherment
- X509v3 Extended Key Usage:
- TLS Web Server Authentication
- X509v3 Basic Constraints: critical
- CA:FALSE
- X509v3 Authority Key Identifier:
- keyid:C2:2B:5F:91:78:34:26:09:42:8D:6F:51:B2:C5:AF:4C:0B:DE:6A:42
-
- X509v3 Subject Alternative Name:
- DNS:example
- Signature Algorithm: ecdsa-with-SHA256
- 30:44:02:20:64:e0:ba:56:89:63:ce:22:5e:4f:22:15:fd:3c:
- 35:64:9a:3a:6b:7b:9a:32:a0:7f:f7:69:8c:06:f0:00:58:b8:
- 02:20:09:e4:9f:6d:8b:9e:38:e1:b6:01:d5:ee:32:a4:94:65:
- 93:2a:78:94:bb:26:57:4b:c7:dd:6c:3d:40:2b:63:90
------BEGIN CERTIFICATE-----
+-----END CERTIFICATE-----`
+
+const leafWithAKID = `-----BEGIN CERTIFICATE-----
MIIBjTCCATSgAwIBAgIRAPCKYvADhKLPaWOtcTu2XYwwCgYIKoZIzj0EAwIwEjEQ
MA4GA1UEChMHQWNtZSBDbzAeFw0xOTAyMDQyMzA2NTJaFw0yOTAyMDEyMzA2NTJa
MBMxETAPBgNVBAoTCEFjbWUgTExDMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
@@ -1791,9 +1613,7 @@ CCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUwitfkXg0JglCjW9R
ssWvTAveakIwEgYDVR0RBAswCYIHZXhhbXBsZTAKBggqhkjOPQQDAgNHADBEAiBk
4LpWiWPOIl5PIhX9PDVkmjpre5oyoH/3aYwG8ABYuAIgCeSfbYueOOG2AdXuMqSU
ZZMqeJS7JldLx91sPUArY5A=
------END CERTIFICATE-----
-`
-)
+-----END CERTIFICATE-----`
var unknownAuthorityErrorTests = []struct {
cert string
@@ -2129,3 +1949,33 @@ func TestLongChain(t *testing.T) {
}
t.Logf("verification took %v", time.Since(start))
}
+
+func TestSystemRootsError(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("Windows does not use (or support) systemRoots")
+ }
+
+ defer func(oldSystemRoots *CertPool) { systemRoots = oldSystemRoots }(systemRootsPool())
+
+ opts := VerifyOptions{
+ Intermediates: NewCertPool(),
+ DNSName: "www.google.com",
+ CurrentTime: time.Unix(1395785200, 0),
+ }
+
+ if ok := opts.Intermediates.AppendCertsFromPEM([]byte(giag2Intermediate)); !ok {
+ t.Fatalf("failed to parse intermediate")
+ }
+
+ leaf, err := certificateFromPEM(googleLeaf)
+ if err != nil {
+ t.Fatalf("failed to parse leaf: %v", err)
+ }
+
+ systemRoots = nil
+
+ _, err = leaf.Verify(opts)
+ if _, ok := err.(SystemRootsError); !ok {
+ t.Errorf("error was not SystemRootsError: %v", err)
+ }
+}
diff --git a/src/encoding/binary/varint.go b/src/encoding/binary/varint.go
index bcb8ac9a45..38af61075c 100644
--- a/src/encoding/binary/varint.go
+++ b/src/encoding/binary/varint.go
@@ -106,13 +106,13 @@ var overflow = errors.New("binary: varint overflows a 64-bit integer")
func ReadUvarint(r io.ByteReader) (uint64, error) {
var x uint64
var s uint
- for i := 0; ; i++ {
+ for i := 0; i < MaxVarintLen64; i++ {
b, err := r.ReadByte()
if err != nil {
return x, err
}
if b < 0x80 {
- if i > 9 || i == 9 && b > 1 {
+ if i == 9 && b > 1 {
return x, overflow
}
return x | uint64(b)<<s, nil
@@ -120,6 +120,7 @@ func ReadUvarint(r io.ByteReader) (uint64, error) {
x |= uint64(b&0x7f) << s
s += 7
}
+ return x, overflow
}
// ReadVarint reads an encoded signed integer from r and returns it as an int64.
diff --git a/src/encoding/binary/varint_test.go b/src/encoding/binary/varint_test.go
index ca411ecbd6..6ef4c99505 100644
--- a/src/encoding/binary/varint_test.go
+++ b/src/encoding/binary/varint_test.go
@@ -121,21 +121,27 @@ func TestBufferTooSmall(t *testing.T) {
}
}
-func testOverflow(t *testing.T, buf []byte, n0 int, err0 error) {
+func testOverflow(t *testing.T, buf []byte, x0 uint64, n0 int, err0 error) {
x, n := Uvarint(buf)
if x != 0 || n != n0 {
t.Errorf("Uvarint(%v): got x = %d, n = %d; want 0, %d", buf, x, n, n0)
}
- x, err := ReadUvarint(bytes.NewReader(buf))
- if x != 0 || err != err0 {
- t.Errorf("ReadUvarint(%v): got x = %d, err = %s; want 0, %s", buf, x, err, err0)
+ r := bytes.NewReader(buf)
+ len := r.Len()
+ x, err := ReadUvarint(r)
+ if x != x0 || err != err0 {
+ t.Errorf("ReadUvarint(%v): got x = %d, err = %s; want %d, %s", buf, x, err, x0, err0)
+ }
+ if read := len - r.Len(); read > MaxVarintLen64 {
+ t.Errorf("ReadUvarint(%v): read more than MaxVarintLen64 bytes, got %d", buf, read)
}
}
func TestOverflow(t *testing.T) {
- testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x2}, -10, overflow)
- testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1, 0, 0}, -13, overflow)
+ testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x2}, 0, -10, overflow)
+ testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1, 0, 0}, 0, -13, overflow)
+ testOverflow(t, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 1<<64-1, 0, overflow) // 11 bytes, should overflow
}
func TestNonCanonicalZero(t *testing.T) {
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index 62d6e6296b..f8b152e5d4 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -10,6 +10,7 @@ package build
import (
"bytes"
"fmt"
+ "internal/testenv"
"io/ioutil"
"os"
"path/filepath"
@@ -385,7 +386,7 @@ var depsRules = `
< golang.org/x/crypto/poly1305
< golang.org/x/crypto/chacha20poly1305
< golang.org/x/crypto/hkdf
- < crypto/x509/internal/macOS
+ < crypto/x509/internal/macos
< crypto/x509/pkix
< crypto/x509
< crypto/tls;
@@ -517,8 +518,7 @@ func listStdPkgs(goroot string) ([]string, error) {
}
func TestDependencies(t *testing.T) {
- iOS := runtime.GOOS == "darwin" && runtime.GOARCH == "arm64"
- if iOS {
+ if !testenv.HasSrc() {
// Tests run in a limited file system and we do not
// provide access to every source file.
t.Skipf("skipping on %s/%s, missing full GOROOT", runtime.GOOS, runtime.GOARCH)
@@ -806,3 +806,23 @@ func (p *depsParser) nextToken() string {
}
}
}
+
+// TestStdlibLowercase tests that all standard library package names are
+// lowercase. See Issue 40065.
+func TestStdlibLowercase(t *testing.T) {
+ if !testenv.HasSrc() {
+ t.Skipf("skipping on %s/%s, missing full GOROOT", runtime.GOOS, runtime.GOARCH)
+ }
+
+ ctxt := Default
+ all, err := listStdPkgs(ctxt.GOROOT)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, pkgname := range all {
+ if strings.ToLower(pkgname) != pkgname {
+ t.Errorf("package %q should not use upper-case path", pkgname)
+ }
+ }
+}
diff --git a/src/go/format/format.go b/src/go/format/format.go
index 84afbb066a..a603d9630e 100644
--- a/src/go/format/format.go
+++ b/src/go/format/format.go
@@ -27,7 +27,13 @@ import (
// Keep these in sync with cmd/gofmt/gofmt.go.
const (
tabWidth = 8
- printerMode = printer.UseSpaces | printer.TabIndent | printer.StdFormat
+ printerMode = printer.UseSpaces | printer.TabIndent | printerNormalizeNumbers
+
+ // printerNormalizeNumbers means to canonicalize number literal prefixes
+ // and exponents while printing. See https://golang.org/doc/go1.13#gofmt.
+ //
+ // This value is defined in go/printer specifically for go/format and cmd/gofmt.
+ printerNormalizeNumbers = 1 << 30
)
var config = printer.Config{Mode: printerMode, Tabwidth: tabWidth}
diff --git a/src/go/format/format_test.go b/src/go/format/format_test.go
index aee51e2da1..58e088ede3 100644
--- a/src/go/format/format_test.go
+++ b/src/go/format/format_test.go
@@ -58,8 +58,8 @@ func TestNode(t *testing.T) {
diff(t, buf.Bytes(), src)
}
-// Node is documented to not modify the AST. Test that it is so, even when
-// formatting changes are applied due to printer.StdFormat mode being used.
+// Node is documented to not modify the AST.
+// Test that it is so even when numbers are normalized.
func TestNodeNoModify(t *testing.T) {
const (
src = "package p\n\nconst _ = 0000000123i\n"
diff --git a/src/go/printer/nodes.go b/src/go/printer/nodes.go
index 0360c4606e..95b9e91891 100644
--- a/src/go/printer/nodes.go
+++ b/src/go/printer/nodes.go
@@ -791,8 +791,8 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
}
case *ast.BasicLit:
- if p.Config.Mode&StdFormat != 0 {
- x = normalizeNumbers(x)
+ if p.Config.Mode&normalizeNumbers != 0 {
+ x = normalizedNumber(x)
}
p.print(x)
@@ -974,11 +974,15 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
}
}
-// normalizeNumbers rewrites base prefixes and exponents to
-// use lower-case letters, and removes leading 0's from
-// integer imaginary literals. It leaves hexadecimal digits
-// alone.
-func normalizeNumbers(lit *ast.BasicLit) *ast.BasicLit {
+// normalizedNumber rewrites base prefixes and exponents
+// of numbers to use lower-case letters (0X123 to 0x123 and 1.2E3 to 1.2e3),
+// and removes leading 0's from integer imaginary literals (0765i to 765i).
+// It leaves hexadecimal digits alone.
+//
+// normalizedNumber doesn't modify the ast.BasicLit value lit points to.
+// If lit is not a number or a number in canonical format already,
+// lit is returned as is. Otherwise a new ast.BasicLit is created.
+func normalizedNumber(lit *ast.BasicLit) *ast.BasicLit {
if lit.Kind != token.INT && lit.Kind != token.FLOAT && lit.Kind != token.IMAG {
return lit // not a number - nothing to do
}
diff --git a/src/go/printer/performance_test.go b/src/go/printer/performance_test.go
index 3f34bfcc32..2e67154e6b 100644
--- a/src/go/printer/performance_test.go
+++ b/src/go/printer/performance_test.go
@@ -20,7 +20,7 @@ import (
var testfile *ast.File
func testprint(out io.Writer, file *ast.File) {
- if err := (&Config{TabIndent | UseSpaces | StdFormat, 8, 0}).Fprint(out, fset, file); err != nil {
+ if err := (&Config{TabIndent | UseSpaces | normalizeNumbers, 8, 0}).Fprint(out, fset, file); err != nil {
log.Fatalf("print error: %s", err)
}
}
diff --git a/src/go/printer/printer.go b/src/go/printer/printer.go
index 9d0add40b6..0077afeaff 100644
--- a/src/go/printer/printer.go
+++ b/src/go/printer/printer.go
@@ -1276,7 +1276,22 @@ const (
TabIndent // use tabs for indentation independent of UseSpaces
UseSpaces // use spaces instead of tabs for alignment
SourcePos // emit //line directives to preserve original source positions
- StdFormat // apply standard formatting changes (exact byte output may change between versions of Go)
+)
+
+// The mode below is not included in printer's public API because
+// editing code text is deemed out of scope. Because this mode is
+// unexported, it's also possible to modify or remove it based on
+// the evolving needs of go/format and cmd/gofmt without breaking
+// users. See discussion in CL 240683.
+const (
+ // normalizeNumbers means to canonicalize number
+ // literal prefixes and exponents while printing.
+ //
+ // This value is known in and used by go/format and cmd/gofmt.
+ // It is currently more convenient and performant for those
+ // packages to apply number normalization during printing,
+ // rather than by modifying the AST in advance.
+ normalizeNumbers Mode = 1 << 30
)
// A Config node controls the output of Fprint.
diff --git a/src/go/printer/printer_test.go b/src/go/printer/printer_test.go
index 1e9d47ce73..b64bc6bfb7 100644
--- a/src/go/printer/printer_test.go
+++ b/src/go/printer/printer_test.go
@@ -33,7 +33,7 @@ type checkMode uint
const (
export checkMode = 1 << iota
rawFormat
- stdFormat
+ normNumber
idempotent
)
@@ -58,8 +58,8 @@ func format(src []byte, mode checkMode) ([]byte, error) {
if mode&rawFormat != 0 {
cfg.Mode |= RawFormat
}
- if mode&stdFormat != 0 {
- cfg.Mode |= StdFormat
+ if mode&normNumber != 0 {
+ cfg.Mode |= normalizeNumbers
}
// print AST
@@ -205,7 +205,7 @@ var data = []entry{
{"slow.input", "slow.golden", idempotent},
{"complit.input", "complit.x", export},
{"go2numbers.input", "go2numbers.golden", idempotent},
- {"go2numbers.input", "go2numbers.stdfmt", stdFormat | idempotent},
+ {"go2numbers.input", "go2numbers.norm", normNumber | idempotent},
}
func TestFiles(t *testing.T) {
diff --git a/src/go/printer/testdata/go2numbers.stdfmt b/src/go/printer/testdata/go2numbers.norm
index 855f0fc608..855f0fc608 100644
--- a/src/go/printer/testdata/go2numbers.stdfmt
+++ b/src/go/printer/testdata/go2numbers.norm
diff --git a/src/html/template/clone_test.go b/src/html/template/clone_test.go
index e292321d93..c9c619f0d4 100644
--- a/src/html/template/clone_test.go
+++ b/src/html/template/clone_test.go
@@ -15,7 +15,7 @@ import (
"text/template/parse"
)
-func TestAddParseTree(t *testing.T) {
+func TestAddParseTreeHTML(t *testing.T) {
root := Must(New("root").Parse(`{{define "a"}} {{.}} {{template "b"}} {{.}} "></a>{{end}}`))
tree, err := parse.Parse("t", `{{define "b"}}<a href="{{end}}`, "", "", nil, nil)
if err != nil {
diff --git a/src/html/template/content_test.go b/src/html/template/content_test.go
index bd8652769b..b7a39d4814 100644
--- a/src/html/template/content_test.go
+++ b/src/html/template/content_test.go
@@ -401,11 +401,11 @@ func TestTypedContent(t *testing.T) {
}
// Test that we print using the String method. Was issue 3073.
-type stringer struct {
+type myStringer struct {
v int
}
-func (s *stringer) String() string {
+func (s *myStringer) String() string {
return fmt.Sprintf("string=%d", s.v)
}
@@ -418,7 +418,7 @@ func (s *errorer) Error() string {
}
func TestStringer(t *testing.T) {
- s := &stringer{3}
+ s := &myStringer{3}
b := new(bytes.Buffer)
tmpl := Must(New("x").Parse("{{.}}"))
if err := tmpl.Execute(b, s); err != nil {
diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
index c709660f06..fbc84a7592 100644
--- a/src/html/template/escape_test.go
+++ b/src/html/template/escape_test.go
@@ -1821,7 +1821,7 @@ func TestIndirectPrint(t *testing.T) {
}
// This is a test for issue 3272.
-func TestEmptyTemplate(t *testing.T) {
+func TestEmptyTemplateHTML(t *testing.T) {
page := Must(New("page").ParseFiles(os.DevNull))
if err := page.ExecuteTemplate(os.Stdout, "page", "nothing"); err == nil {
t.Fatal("expected error")
diff --git a/src/html/template/exec_test.go b/src/html/template/exec_test.go
new file mode 100644
index 0000000000..ec2bfcccab
--- /dev/null
+++ b/src/html/template/exec_test.go
@@ -0,0 +1,1708 @@
+// 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.
+
+// Tests for template execution, copied from text/template.
+
+package template
+
+import (
+ "bytes"
+ "errors"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "reflect"
+ "strings"
+ "testing"
+ "text/template"
+)
+
+var debug = flag.Bool("debug", false, "show the errors produced by the tests")
+
+// T has lots of interesting pieces to use to test execution.
+type T struct {
+ // Basics
+ True bool
+ I int
+ U16 uint16
+ X, S string
+ FloatZero float64
+ ComplexZero complex128
+ // Nested structs.
+ U *U
+ // Struct with String method.
+ V0 V
+ V1, V2 *V
+ // Struct with Error method.
+ W0 W
+ W1, W2 *W
+ // Slices
+ SI []int
+ SICap []int
+ SIEmpty []int
+ SB []bool
+ // Arrays
+ AI [3]int
+ // Maps
+ MSI map[string]int
+ MSIone map[string]int // one element, for deterministic output
+ MSIEmpty map[string]int
+ MXI map[interface{}]int
+ MII map[int]int
+ MI32S map[int32]string
+ MI64S map[int64]string
+ MUI32S map[uint32]string
+ MUI64S map[uint64]string
+ MI8S map[int8]string
+ MUI8S map[uint8]string
+ SMSI []map[string]int
+ // Empty interfaces; used to see if we can dig inside one.
+ Empty0 interface{} // nil
+ Empty1 interface{}
+ Empty2 interface{}
+ Empty3 interface{}
+ Empty4 interface{}
+ // Non-empty interfaces.
+ NonEmptyInterface I
+ NonEmptyInterfacePtS *I
+ NonEmptyInterfaceNil I
+ NonEmptyInterfaceTypedNil I
+ // Stringer.
+ Str fmt.Stringer
+ Err error
+ // Pointers
+ PI *int
+ PS *string
+ PSI *[]int
+ NIL *int
+ // Function (not method)
+ BinaryFunc func(string, string) string
+ VariadicFunc func(...string) string
+ VariadicFuncInt func(int, ...string) string
+ NilOKFunc func(*int) bool
+ ErrFunc func() (string, error)
+ PanicFunc func() string
+ // Template to test evaluation of templates.
+ Tmpl *Template
+ // Unexported field; cannot be accessed by template.
+ unexported int
+}
+
+type S []string
+
+func (S) Method0() string {
+ return "M0"
+}
+
+type U struct {
+ V string
+}
+
+type V struct {
+ j int
+}
+
+func (v *V) String() string {
+ if v == nil {
+ return "nilV"
+ }
+ return fmt.Sprintf("<%d>", v.j)
+}
+
+type W struct {
+ k int
+}
+
+func (w *W) Error() string {
+ if w == nil {
+ return "nilW"
+ }
+ return fmt.Sprintf("[%d]", w.k)
+}
+
+var siVal = I(S{"a", "b"})
+
+var tVal = &T{
+ True: true,
+ I: 17,
+ U16: 16,
+ X: "x",
+ S: "xyz",
+ U: &U{"v"},
+ V0: V{6666},
+ V1: &V{7777}, // leave V2 as nil
+ W0: W{888},
+ W1: &W{999}, // leave W2 as nil
+ SI: []int{3, 4, 5},
+ SICap: make([]int, 5, 10),
+ AI: [3]int{3, 4, 5},
+ SB: []bool{true, false},
+ MSI: map[string]int{"one": 1, "two": 2, "three": 3},
+ MSIone: map[string]int{"one": 1},
+ MXI: map[interface{}]int{"one": 1},
+ MII: map[int]int{1: 1},
+ MI32S: map[int32]string{1: "one", 2: "two"},
+ MI64S: map[int64]string{2: "i642", 3: "i643"},
+ MUI32S: map[uint32]string{2: "u322", 3: "u323"},
+ MUI64S: map[uint64]string{2: "ui642", 3: "ui643"},
+ MI8S: map[int8]string{2: "i82", 3: "i83"},
+ MUI8S: map[uint8]string{2: "u82", 3: "u83"},
+ SMSI: []map[string]int{
+ {"one": 1, "two": 2},
+ {"eleven": 11, "twelve": 12},
+ },
+ Empty1: 3,
+ Empty2: "empty2",
+ Empty3: []int{7, 8},
+ Empty4: &U{"UinEmpty"},
+ NonEmptyInterface: &T{X: "x"},
+ NonEmptyInterfacePtS: &siVal,
+ NonEmptyInterfaceTypedNil: (*T)(nil),
+ Str: bytes.NewBuffer([]byte("foozle")),
+ Err: errors.New("erroozle"),
+ PI: newInt(23),
+ PS: newString("a string"),
+ PSI: newIntSlice(21, 22, 23),
+ BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) },
+ VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
+ VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") },
+ NilOKFunc: func(s *int) bool { return s == nil },
+ ErrFunc: func() (string, error) { return "bla", nil },
+ PanicFunc: func() string { panic("test panic") },
+ Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
+}
+
+var tSliceOfNil = []*T{nil}
+
+// A non-empty interface.
+type I interface {
+ Method0() string
+}
+
+var iVal I = tVal
+
+// Helpers for creation.
+func newInt(n int) *int {
+ return &n
+}
+
+func newString(s string) *string {
+ return &s
+}
+
+func newIntSlice(n ...int) *[]int {
+ p := new([]int)
+ *p = make([]int, len(n))
+ copy(*p, n)
+ return p
+}
+
+// Simple methods with and without arguments.
+func (t *T) Method0() string {
+ return "M0"
+}
+
+func (t *T) Method1(a int) int {
+ return a
+}
+
+func (t *T) Method2(a uint16, b string) string {
+ return fmt.Sprintf("Method2: %d %s", a, b)
+}
+
+func (t *T) Method3(v interface{}) string {
+ return fmt.Sprintf("Method3: %v", v)
+}
+
+func (t *T) Copy() *T {
+ n := new(T)
+ *n = *t
+ return n
+}
+
+func (t *T) MAdd(a int, b []int) []int {
+ v := make([]int, len(b))
+ for i, x := range b {
+ v[i] = x + a
+ }
+ return v
+}
+
+var myError = errors.New("my error")
+
+// MyError returns a value and an error according to its argument.
+func (t *T) MyError(error bool) (bool, error) {
+ if error {
+ return true, myError
+ }
+ return false, nil
+}
+
+// A few methods to test chaining.
+func (t *T) GetU() *U {
+ return t.U
+}
+
+func (u *U) TrueFalse(b bool) string {
+ if b {
+ return "true"
+ }
+ return ""
+}
+
+func typeOf(arg interface{}) string {
+ return fmt.Sprintf("%T", arg)
+}
+
+type execTest struct {
+ name string
+ input string
+ output string
+ data interface{}
+ ok bool
+}
+
+// bigInt and bigUint are hex string representing numbers either side
+// of the max int boundary.
+// We do it this way so the test doesn't depend on ints being 32 bits.
+var (
+ bigInt = fmt.Sprintf("0x%x", int(1<<uint(reflect.TypeOf(0).Bits()-1)-1))
+ bigUint = fmt.Sprintf("0x%x", uint(1<<uint(reflect.TypeOf(0).Bits()-1)))
+)
+
+var execTests = []execTest{
+ // Trivial cases.
+ {"empty", "", "", nil, true},
+ {"text", "some text", "some text", nil, true},
+ {"nil action", "{{nil}}", "", nil, false},
+
+ // Ideal constants.
+ {"ideal int", "{{typeOf 3}}", "int", 0, true},
+ {"ideal float", "{{typeOf 1.0}}", "float64", 0, true},
+ {"ideal exp float", "{{typeOf 1e1}}", "float64", 0, true},
+ {"ideal complex", "{{typeOf 1i}}", "complex128", 0, true},
+ {"ideal int", "{{typeOf " + bigInt + "}}", "int", 0, true},
+ {"ideal too big", "{{typeOf " + bigUint + "}}", "", 0, false},
+ {"ideal nil without type", "{{nil}}", "", 0, false},
+
+ // Fields of structs.
+ {".X", "-{{.X}}-", "-x-", tVal, true},
+ {".U.V", "-{{.U.V}}-", "-v-", tVal, true},
+ {".unexported", "{{.unexported}}", "", tVal, false},
+
+ // Fields on maps.
+ {"map .one", "{{.MSI.one}}", "1", tVal, true},
+ {"map .two", "{{.MSI.two}}", "2", tVal, true},
+ {"map .NO", "{{.MSI.NO}}", "", tVal, true}, // NOTE: <no value> in text/template
+ {"map .one interface", "{{.MXI.one}}", "1", tVal, true},
+ {"map .WRONG args", "{{.MSI.one 1}}", "", tVal, false},
+ {"map .WRONG type", "{{.MII.one}}", "", tVal, false},
+
+ // Dots of all kinds to test basic evaluation.
+ {"dot int", "<{{.}}>", "&lt;13>", 13, true},
+ {"dot uint", "<{{.}}>", "&lt;14>", uint(14), true},
+ {"dot float", "<{{.}}>", "&lt;15.1>", 15.1, true},
+ {"dot bool", "<{{.}}>", "&lt;true>", true, true},
+ {"dot complex", "<{{.}}>", "&lt;(16.2-17i)>", 16.2 - 17i, true},
+ {"dot string", "<{{.}}>", "&lt;hello>", "hello", true},
+ {"dot slice", "<{{.}}>", "&lt;[-1 -2 -3]>", []int{-1, -2, -3}, true},
+ {"dot map", "<{{.}}>", "&lt;map[two:22]>", map[string]int{"two": 22}, true},
+ {"dot struct", "<{{.}}>", "&lt;{7 seven}>", struct {
+ a int
+ b string
+ }{7, "seven"}, true},
+
+ // Variables.
+ {"$ int", "{{$}}", "123", 123, true},
+ {"$.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}, // NOTE: -<6666>- in text/template
+ {"&V{7777}.String()", "-{{.V1}}-", "-&lt;7777&gt;-", tVal, true},
+ {"(*V)(nil).String()", "-{{.V2}}-", "-nilV-", tVal, true},
+
+ // Type with Error method.
+ {"W{888}.Error()", "-{{.W0}}-", "-{888}-", tVal, true}, // NOTE: -[888] in text/template
+ {"&W{999}.Error()", "-{{.W1}}-", "-[999]-", tVal, true},
+ {"(*W)(nil).Error()", "-{{.W2}}-", "-nilW-", tVal, true},
+
+ // Pointers.
+ {"*int", "{{.PI}}", "23", tVal, true},
+ {"*string", "{{.PS}}", "a string", tVal, true},
+ {"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true},
+ {"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true},
+ {"NIL", "{{.NIL}}", "&lt;nil&gt;", tVal, true},
+
+ // Empty interfaces holding values.
+ {"empty nil", "{{.Empty0}}", "", tVal, true}, // NOTE: <no value> in text/template
+ {"empty with int", "{{.Empty1}}", "3", tVal, true},
+ {"empty with string", "{{.Empty2}}", "empty2", tVal, true},
+ {"empty with slice", "{{.Empty3}}", "[7 8]", tVal, true},
+ {"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}}", "", nil, true}, // NOTE: <no value> in text/template
+ {"field on parenthesized interface", "{{(.).foo}}", "", nil, true}, // NOTE: <no value> in text/template
+
+ // Issue 31810: Parenthesized first element of pipeline with arguments.
+ // See also TestIssue31810.
+ {"unparenthesized non-function", "{{1 2}}", "", nil, false},
+ {"parenthesized non-function", "{{(1) 2}}", "", nil, false},
+ {"parenthesized non-function with no args", "{{(1)}}", "1", nil, true}, // This is fine.
+
+ // Method calls.
+ {".Method0", "-{{.Method0}}-", "-M0-", tVal, true},
+ {".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true},
+ {".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true},
+ {".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true},
+ {".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true},
+ {".Method2(.U16, $x)", "{{if $x := .X}}-{{.Method2 .U16 $x}}{{end}}-", "-Method2: 16 x-", tVal, true},
+ {".Method3(nil constant)", "-{{.Method3 nil}}-", "-Method3: &lt;nil&gt;-", tVal, true},
+ {".Method3(nil value)", "-{{.Method3 .MXI.unset}}-", "-Method3: &lt;nil&gt;-", tVal, true},
+ {"method on var", "{{if $x := .}}-{{$x.Method2 .U16 $x.X}}{{end}}-", "-Method2: 16 x-", tVal, true},
+ {"method on chained var",
+ "{{range .MSIone}}{{if $.U.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}",
+ "true", tVal, true},
+ {"chained method",
+ "{{range .MSIone}}{{if $.GetU.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}",
+ "true", tVal, true},
+ {"chained method on variable",
+ "{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}",
+ "true", tVal, true},
+ {".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true},
+ {".NilOKFunc nil", "{{call .NilOKFunc nil}}", "true", tVal, true},
+ {"method on nil value from slice", "-{{range .}}{{.Method1 1234}}{{end}}-", "-1234-", tSliceOfNil, true},
+ {"method on typed nil interface value", "{{.NonEmptyInterfaceTypedNil.Method0}}", "M0", tVal, true},
+
+ // Function call builtin.
+ {".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true},
+ {".VariadicFunc0", "{{call .VariadicFunc}}", "&lt;&gt;", tVal, true},
+ {".VariadicFunc2", "{{call .VariadicFunc `he` `llo`}}", "&lt;he&#43;llo&gt;", tVal, true},
+ {".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=&lt;he&#43;llo&gt;", tVal, true},
+ {"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true},
+ {"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true},
+ {"Interface Call", `{{stringer .S}}`, "foozle", map[string]interface{}{"S": bytes.NewBufferString("foozle")}, true},
+ {".ErrFunc", "{{call .ErrFunc}}", "bla", tVal, true},
+ {"call nil", "{{call nil}}", "", tVal, false},
+
+ // Erroneous function calls (check args).
+ {".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false},
+ {".BinaryFuncTooMany", "{{call .BinaryFunc `1` `2` `3`}}", "", tVal, false},
+ {".BinaryFuncBad0", "{{call .BinaryFunc 1 3}}", "", tVal, false},
+ {".BinaryFuncBad1", "{{call .BinaryFunc `1` 3}}", "", tVal, false},
+ {".VariadicFuncBad0", "{{call .VariadicFunc 3}}", "", tVal, false},
+ {".VariadicFuncIntBad0", "{{call .VariadicFuncInt}}", "", tVal, false},
+ {".VariadicFuncIntBad`", "{{call .VariadicFuncInt `x`}}", "", tVal, false},
+ {".VariadicFuncNilBad", "{{call .VariadicFunc nil}}", "", tVal, false},
+
+ // Pipelines.
+ {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true},
+ {"pipeline func", "-{{call .VariadicFunc `llo` | call .VariadicFunc `he` }}-", "-&lt;he&#43;&lt;llo&gt;&gt;-", 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},
+
+ // Parenthesized expressions with field accesses
+ {"parens: $ in paren", "{{($).X}}", "x", tVal, true},
+ {"parens: $.GetU in paren", "{{($.GetU).V}}", "v", tVal, true},
+ {"parens: $ in paren in pipe", "{{($ | echo).X}}", "x", tVal, true},
+ {"parens: spaces and args", `{{(makemap "up" "down" "left" "right").left}}`, "right", tVal, true},
+
+ // If.
+ {"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true},
+ {"if false", "{{if false}}TRUE{{else}}FALSE{{end}}", "FALSE", tVal, true},
+ {"if nil", "{{if nil}}TRUE{{end}}", "", tVal, false},
+ {"if on typed nil interface value", "{{if .NonEmptyInterfaceTypedNil}}TRUE{{ end }}", "", tVal, true},
+ {"if 1", "{{if 1}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
+ {"if 0", "{{if 0}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
+ {"if 1.5", "{{if 1.5}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
+ {"if 0.0", "{{if .FloatZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
+ {"if 1.5i", "{{if 1.5i}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
+ {"if 0.0i", "{{if .ComplexZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
+ {"if emptystring", "{{if ``}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"if string", "{{if `notempty`}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
+ {"if emptyslice", "{{if .SIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"if slice", "{{if .SI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
+ {"if emptymap", "{{if .MSIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"if map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
+ {"if map unset", "{{if .MXI.none}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
+ {"if map not unset", "{{if not .MXI.none}}ZERO{{else}}NON-ZERO{{end}}", "ZERO", tVal, true},
+ {"if $x with $y int", "{{if $x := true}}{{with $y := .I}}{{$x}},{{$y}}{{end}}{{end}}", "true,17", tVal, true},
+ {"if $x with $x int", "{{if $x := true}}{{with $x := .I}}{{$x}},{{end}}{{$x}}{{end}}", "17,true", tVal, true},
+ {"if else if", "{{if false}}FALSE{{else if true}}TRUE{{end}}", "TRUE", tVal, true},
+ {"if else chain", "{{if eq 1 3}}1{{else if eq 2 3}}2{{else if eq 3 3}}3{{end}}", "3", tVal, true},
+
+ // Print etc.
+ {"print", `{{print "hello, print"}}`, "hello, print", tVal, true},
+ {"print 123", `{{print 1 2 3}}`, "1 2 3", tVal, true},
+ {"print nil", `{{print nil}}`, "&lt;nil&gt;", tVal, true},
+ {"println", `{{println 1 2 3}}`, "1 2 3\n", tVal, true},
+ {"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true},
+ {"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true},
+ {"printf complex", `{{printf "%g" 1+7i}}`, "(1&#43;7i)", tVal, true},
+ {"printf string", `{{printf "%s" "hello"}}`, "hello", tVal, true},
+ {"printf function", `{{printf "%#q" zeroArgs}}`, "`zeroArgs`", tVal, true},
+ {"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true},
+ {"printf method", `{{printf "%s" .Method0}}`, "M0", tVal, true},
+ {"printf dot", `{{with .I}}{{printf "%d" .}}{{end}}`, "17", tVal, true},
+ {"printf var", `{{with $x := .I}}{{printf "%d" $x}}{{end}}`, "17", tVal, true},
+ {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) M0", tVal, true},
+
+ // HTML.
+ {"html", `{{html "<script>alert(\"XSS\");</script>"}}`,
+ "&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", nil, true},
+ {"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`,
+ "&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", nil, true},
+ {"html", `{{html .PS}}`, "a string", tVal, true},
+ {"html typed nil", `{{html .NIL}}`, "&lt;nil&gt;", tVal, true},
+ {"html untyped nil", `{{html .Empty0}}`, "&lt;nil&gt;", tVal, true}, // NOTE: "&lt;no value&gt;" in text/template
+
+ // JavaScript.
+ {"js", `{{js .}}`, `It\&#39;d be nice.`, `It'd be nice.`, true},
+
+ // URL query.
+ {"urlquery", `{{"http://www.example.org/"|urlquery}}`, "http%3A%2F%2Fwww.example.org%2F", nil, true},
+
+ // Booleans
+ {"not", "{{not true}} {{not false}}", "false true", nil, true},
+ {"and", "{{and false 0}} {{and 1 0}} {{and 0 true}} {{and 1 1}}", "false 0 0 1", nil, true},
+ {"or", "{{or 0 0}} {{or 1 0}} {{or 0 true}} {{or 1 1}}", "0 1 true 1", nil, true},
+ {"boolean if", "{{if and true 1 `hi`}}TRUE{{else}}FALSE{{end}}", "TRUE", tVal, true},
+ {"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true},
+
+ // Indexing.
+ {"slice[0]", "{{index .SI 0}}", "3", tVal, true},
+ {"slice[1]", "{{index .SI 1}}", "4", tVal, true},
+ {"slice[HUGE]", "{{index .SI 10}}", "", tVal, false},
+ {"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false},
+ {"slice[nil]", "{{index .SI nil}}", "", tVal, false},
+ {"map[one]", "{{index .MSI `one`}}", "1", tVal, true},
+ {"map[two]", "{{index .MSI `two`}}", "2", tVal, true},
+ {"map[NO]", "{{index .MSI `XXX`}}", "0", tVal, true},
+ {"map[nil]", "{{index .MSI nil}}", "", tVal, false},
+ {"map[``]", "{{index .MSI ``}}", "0", tVal, true},
+ {"map[WRONG]", "{{index .MSI 10}}", "", tVal, false},
+ {"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true},
+ {"nil[1]", "{{index nil 1}}", "", tVal, false},
+ {"map MI64S", "{{index .MI64S 2}}", "i642", tVal, true},
+ {"map MI32S", "{{index .MI32S 2}}", "two", tVal, true},
+ {"map MUI64S", "{{index .MUI64S 3}}", "ui643", tVal, true},
+ {"map MI8S", "{{index .MI8S 3}}", "i83", tVal, true},
+ {"map MUI8S", "{{index .MUI8S 2}}", "u82", tVal, true},
+ {"index of an interface field", "{{index .Empty3 0}}", "7", tVal, true},
+
+ // Slicing.
+ {"slice[:]", "{{slice .SI}}", "[3 4 5]", tVal, true},
+ {"slice[1:]", "{{slice .SI 1}}", "[4 5]", tVal, true},
+ {"slice[1:2]", "{{slice .SI 1 2}}", "[4]", tVal, true},
+ {"slice[-1:]", "{{slice .SI -1}}", "", tVal, false},
+ {"slice[1:-2]", "{{slice .SI 1 -2}}", "", tVal, false},
+ {"slice[1:2:-1]", "{{slice .SI 1 2 -1}}", "", tVal, false},
+ {"slice[2:1]", "{{slice .SI 2 1}}", "", tVal, false},
+ {"slice[2:2:1]", "{{slice .SI 2 2 1}}", "", tVal, false},
+ {"out of range", "{{slice .SI 4 5}}", "", tVal, false},
+ {"out of range", "{{slice .SI 2 2 5}}", "", tVal, false},
+ {"len(s) < indexes < cap(s)", "{{slice .SICap 6 10}}", "[0 0 0 0]", tVal, true},
+ {"len(s) < indexes < cap(s)", "{{slice .SICap 6 10 10}}", "[0 0 0 0]", tVal, true},
+ {"indexes > cap(s)", "{{slice .SICap 10 11}}", "", tVal, false},
+ {"indexes > cap(s)", "{{slice .SICap 6 10 11}}", "", tVal, false},
+ {"array[:]", "{{slice .AI}}", "[3 4 5]", tVal, true},
+ {"array[1:]", "{{slice .AI 1}}", "[4 5]", tVal, true},
+ {"array[1:2]", "{{slice .AI 1 2}}", "[4]", tVal, true},
+ {"string[:]", "{{slice .S}}", "xyz", tVal, true},
+ {"string[0:1]", "{{slice .S 0 1}}", "x", tVal, true},
+ {"string[1:]", "{{slice .S 1}}", "yz", tVal, true},
+ {"string[1:2]", "{{slice .S 1 2}}", "y", tVal, true},
+ {"out of range", "{{slice .S 1 5}}", "", tVal, false},
+ {"3-index slice of string", "{{slice .S 1 2 2}}", "", tVal, false},
+ {"slice of an interface field", "{{slice .Empty3 0 1}}", "[7]", tVal, true},
+
+ // Len.
+ {"slice", "{{len .SI}}", "3", tVal, true},
+ {"map", "{{len .MSI }}", "3", tVal, true},
+ {"len of int", "{{len 3}}", "", tVal, false},
+ {"len of nothing", "{{len .Empty0}}", "", tVal, false},
+ {"len of an interface field", "{{len .Empty3}}", "2", tVal, true},
+
+ // With.
+ {"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true},
+ {"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true},
+ {"with 1", "{{with 1}}{{.}}{{else}}ZERO{{end}}", "1", tVal, true},
+ {"with 0", "{{with 0}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true},
+ {"with 1.5", "{{with 1.5}}{{.}}{{else}}ZERO{{end}}", "1.5", tVal, true},
+ {"with 0.0", "{{with .FloatZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true},
+ {"with 1.5i", "{{with 1.5i}}{{.}}{{else}}ZERO{{end}}", "(0&#43;1.5i)", tVal, true},
+ {"with 0.0i", "{{with .ComplexZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true},
+ {"with emptystring", "{{with ``}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"with string", "{{with `notempty`}}{{.}}{{else}}EMPTY{{end}}", "notempty", tVal, true},
+ {"with emptyslice", "{{with .SIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"with slice", "{{with .SI}}{{.}}{{else}}EMPTY{{end}}", "[3 4 5]", tVal, true},
+ {"with emptymap", "{{with .MSIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true},
+ {"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "UinEmpty", tVal, true},
+ {"with $x int", "{{with $x := .I}}{{$x}}{{end}}", "17", tVal, true},
+ {"with $x struct.U.V", "{{with $x := $}}{{$x.U.V}}{{end}}", "v", tVal, true},
+ {"with variable and action", "{{with $x := $}}{{$y := $.U.V}}{{$y}}{{end}}", "v", tVal, true},
+ {"with on typed nil interface value", "{{with .NonEmptyInterfaceTypedNil}}TRUE{{ end }}", "", tVal, true},
+
+ // Range.
+ {"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true},
+ {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true},
+ {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true},
+ {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true},
+ {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true},
+ {"range map", "{{range .MSI}}-{{.}}-{{end}}", "-1--3--2-", tVal, true},
+ {"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true},
+ {"range map else", "{{range .MSI}}-{{.}}-{{else}}EMPTY{{end}}", "-1--3--2-", tVal, true},
+ {"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true},
+ {"range empty nil", "{{range .Empty0}}-{{.}}-{{end}}", "", tVal, true},
+ {"range $x SI", "{{range $x := .SI}}<{{$x}}>{{end}}", "&lt;3>&lt;4>&lt;5>", tVal, true},
+ {"range $x $y SI", "{{range $x, $y := .SI}}<{{$x}}={{$y}}>{{end}}", "&lt;0=3>&lt;1=4>&lt;2=5>", tVal, true},
+ {"range $x MSIone", "{{range $x := .MSIone}}<{{$x}}>{{end}}", "&lt;1>", tVal, true},
+ {"range $x $y MSIone", "{{range $x, $y := .MSIone}}<{{$x}}={{$y}}>{{end}}", "&lt;one=1>", tVal, true},
+ {"range $x PSI", "{{range $x := .PSI}}<{{$x}}>{{end}}", "&lt;21>&lt;22>&lt;23>", tVal, true},
+ {"declare in range", "{{range $x := .PSI}}<{{$foo:=$x}}{{$x}}>{{end}}", "&lt;21>&lt;22>&lt;23>", tVal, true},
+ {"range count", `{{range $i, $x := count 5}}[{{$i}}]{{$x}}{{end}}`, "[0]a[1]b[2]c[3]d[4]e", tVal, true},
+ {"range nil count", `{{range $i, $x := count 0}}{{else}}empty{{end}}`, "empty", tVal, true},
+
+ // Cute examples.
+ {"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true},
+ {"or as if false", `{{or .SIEmpty "slice is empty"}}`, "slice is empty", tVal, true},
+
+ // Error handling.
+ {"error method, error", "{{.MyError true}}", "", tVal, false},
+ {"error method, no error", "{{.MyError false}}", "false", tVal, true},
+
+ // Numbers
+ {"decimal", "{{print 1234}}", "1234", tVal, true},
+ {"decimal _", "{{print 12_34}}", "1234", tVal, true},
+ {"binary", "{{print 0b101}}", "5", tVal, true},
+ {"binary _", "{{print 0b_1_0_1}}", "5", tVal, true},
+ {"BINARY", "{{print 0B101}}", "5", tVal, true},
+ {"octal0", "{{print 0377}}", "255", tVal, true},
+ {"octal", "{{print 0o377}}", "255", tVal, true},
+ {"octal _", "{{print 0o_3_7_7}}", "255", tVal, true},
+ {"OCTAL", "{{print 0O377}}", "255", tVal, true},
+ {"hex", "{{print 0x123}}", "291", tVal, true},
+ {"hex _", "{{print 0x1_23}}", "291", tVal, true},
+ {"HEX", "{{print 0X123ABC}}", "1194684", tVal, true},
+ {"float", "{{print 123.4}}", "123.4", tVal, true},
+ {"float _", "{{print 0_0_1_2_3.4}}", "123.4", tVal, true},
+ {"hex float", "{{print +0x1.ep+2}}", "7.5", tVal, true},
+ {"hex float _", "{{print +0x_1.e_0p+0_2}}", "7.5", tVal, true},
+ {"HEX float", "{{print +0X1.EP+2}}", "7.5", tVal, true},
+ {"print multi", "{{print 1_2_3_4 7.5_00_00_00}}", "1234 7.5", tVal, true},
+ {"print multi2", "{{print 1234 0x0_1.e_0p+02}}", "1234 7.5", tVal, true},
+
+ // Fixed bugs.
+ // Must separate dot and receiver; otherwise args are evaluated with dot set to variable.
+ {"bug0", "{{range .MSIone}}{{if $.Method1 .}}X{{end}}{{end}}", "X", tVal, true},
+ // Do not loop endlessly in indirect for non-empty interfaces.
+ // The bug appears with *interface only; looped forever.
+ {"bug1", "{{.Method0}}", "M0", &iVal, true},
+ // Was taking address of interface field, so method set was empty.
+ {"bug2", "{{$.NonEmptyInterface.Method0}}", "M0", tVal, true},
+ // Struct values were not legal in with - mere oversight.
+ {"bug3", "{{with $}}{{.Method0}}{{end}}", "M0", tVal, true},
+ // Nil interface values in if.
+ {"bug4", "{{if .Empty0}}non-nil{{else}}nil{{end}}", "nil", tVal, true},
+ // Stringer.
+ {"bug5", "{{.Str}}", "foozle", tVal, true},
+ {"bug5a", "{{.Err}}", "erroozle", tVal, true},
+ // Args need to be indirected and dereferenced sometimes.
+ {"bug6a", "{{vfunc .V0 .V1}}", "vfunc", tVal, true},
+ {"bug6b", "{{vfunc .V0 .V0}}", "vfunc", tVal, true},
+ {"bug6c", "{{vfunc .V1 .V0}}", "vfunc", tVal, true},
+ {"bug6d", "{{vfunc .V1 .V1}}", "vfunc", tVal, true},
+ // Legal parse but illegal execution: non-function should have no arguments.
+ {"bug7a", "{{3 2}}", "", tVal, false},
+ {"bug7b", "{{$x := 1}}{{$x 2}}", "", tVal, false},
+ {"bug7c", "{{$x := 1}}{{3 | $x}}", "", tVal, false},
+ // Pipelined arg was not being type-checked.
+ {"bug8a", "{{3|oneArg}}", "", tVal, false},
+ {"bug8b", "{{4|dddArg 3}}", "", tVal, false},
+ // A bug was introduced that broke map lookups for lower-case names.
+ {"bug9", "{{.cause}}", "neglect", map[string]string{"cause": "neglect"}, true},
+ // Field chain starting with function did not work.
+ {"bug10", "{{mapOfThree.three}}-{{(mapOfThree).three}}", "3-3", 0, true},
+ // Dereferencing nil pointer while evaluating function arguments should not panic. Issue 7333.
+ {"bug11", "{{valueString .PS}}", "", T{}, false},
+ // 0xef gave constant type float64. Issue 8622.
+ {"bug12xe", "{{printf `%T` 0xef}}", "int", T{}, true},
+ {"bug12xE", "{{printf `%T` 0xEE}}", "int", T{}, true},
+ {"bug12Xe", "{{printf `%T` 0Xef}}", "int", T{}, true},
+ {"bug12XE", "{{printf `%T` 0XEE}}", "int", T{}, true},
+ // Chained nodes did not work as arguments. Issue 8473.
+ {"bug13", "{{print (.Copy).I}}", "17", tVal, true},
+ // Didn't protect against nil or literal values in field chains.
+ {"bug14a", "{{(nil).True}}", "", tVal, false},
+ {"bug14b", "{{$x := nil}}{{$x.anything}}", "", tVal, false},
+ {"bug14c", `{{$x := (1.0)}}{{$y := ("hello")}}{{$x.anything}}{{$y.true}}`, "", tVal, false},
+ // Didn't call validateType on function results. Issue 10800.
+ {"bug15", "{{valueString returnInt}}", "", tVal, false},
+ // Variadic function corner cases. Issue 10946.
+ {"bug16a", "{{true|printf}}", "", tVal, false},
+ {"bug16b", "{{1|printf}}", "", tVal, false},
+ {"bug16c", "{{1.1|printf}}", "", tVal, false},
+ {"bug16d", "{{'x'|printf}}", "", tVal, false},
+ {"bug16e", "{{0i|printf}}", "", tVal, false},
+ {"bug16f", "{{true|twoArgs \"xxx\"}}", "", tVal, false},
+ {"bug16g", "{{\"aaa\" |twoArgs \"bbb\"}}", "twoArgs=bbbaaa", tVal, true},
+ {"bug16h", "{{1|oneArg}}", "", tVal, false},
+ {"bug16i", "{{\"aaa\"|oneArg}}", "oneArg=aaa", tVal, true},
+ {"bug16j", "{{1+2i|printf \"%v\"}}", "(1&#43;2i)", tVal, true},
+ {"bug16k", "{{\"aaa\"|printf }}", "aaa", tVal, true},
+ {"bug17a", "{{.NonEmptyInterface.X}}", "x", tVal, true},
+ {"bug17b", "-{{.NonEmptyInterface.Method1 1234}}-", "-1234-", tVal, true},
+ {"bug17c", "{{len .NonEmptyInterfacePtS}}", "2", tVal, true},
+ {"bug17d", "{{index .NonEmptyInterfacePtS 0}}", "a", tVal, true},
+ {"bug17e", "{{range .NonEmptyInterfacePtS}}-{{.}}-{{end}}", "-a--b-", tVal, true},
+
+ // More variadic function corner cases. Some runes would get evaluated
+ // as constant floats instead of ints. Issue 34483.
+ {"bug18a", "{{eq . '.'}}", "true", '.', true},
+ {"bug18b", "{{eq . 'e'}}", "true", 'e', true},
+ {"bug18c", "{{eq . 'P'}}", "true", 'P', true},
+}
+
+func zeroArgs() string {
+ return "zeroArgs"
+}
+
+func oneArg(a string) string {
+ return "oneArg=" + a
+}
+
+func twoArgs(a, b string) string {
+ return "twoArgs=" + a + b
+}
+
+func dddArg(a int, b ...string) string {
+ return fmt.Sprintln(a, b)
+}
+
+// count returns a channel that will deliver n sequential 1-letter strings starting at "a"
+func count(n int) chan string {
+ if n == 0 {
+ return nil
+ }
+ c := make(chan string)
+ go func() {
+ for i := 0; i < n; i++ {
+ c <- "abcdefghijklmnop"[i : i+1]
+ }
+ close(c)
+ }()
+ return c
+}
+
+// vfunc takes a *V and a V
+func vfunc(V, *V) string {
+ return "vfunc"
+}
+
+// valueString takes a string, not a pointer.
+func valueString(v string) string {
+ return "value is ignored"
+}
+
+// returnInt returns an int
+func returnInt() int {
+ return 7
+}
+
+func add(args ...int) int {
+ sum := 0
+ for _, x := range args {
+ sum += x
+ }
+ return sum
+}
+
+func echo(arg interface{}) interface{} {
+ return arg
+}
+
+func makemap(arg ...string) map[string]string {
+ if len(arg)%2 != 0 {
+ panic("bad makemap")
+ }
+ m := make(map[string]string)
+ for i := 0; i < len(arg); i += 2 {
+ m[arg[i]] = arg[i+1]
+ }
+ return m
+}
+
+func stringer(s fmt.Stringer) string {
+ return s.String()
+}
+
+func mapOfThree() interface{} {
+ return map[string]int{"three": 3}
+}
+
+func testExecute(execTests []execTest, template *Template, t *testing.T) {
+ b := new(bytes.Buffer)
+ funcs := FuncMap{
+ "add": add,
+ "count": count,
+ "dddArg": dddArg,
+ "echo": echo,
+ "makemap": makemap,
+ "mapOfThree": mapOfThree,
+ "oneArg": oneArg,
+ "returnInt": returnInt,
+ "stringer": stringer,
+ "twoArgs": twoArgs,
+ "typeOf": typeOf,
+ "valueString": valueString,
+ "vfunc": vfunc,
+ "zeroArgs": zeroArgs,
+ }
+ for _, test := range execTests {
+ var tmpl *Template
+ var err error
+ if template == nil {
+ tmpl, err = New(test.name).Funcs(funcs).Parse(test.input)
+ } else {
+ tmpl, err = template.Clone()
+ if err != nil {
+ t.Errorf("%s: clone error: %s", test.name, err)
+ continue
+ }
+ tmpl, err = tmpl.New(test.name).Funcs(funcs).Parse(test.input)
+ }
+ if err != nil {
+ t.Errorf("%s: parse error: %s", test.name, err)
+ continue
+ }
+ b.Reset()
+ err = tmpl.Execute(b, test.data)
+ switch {
+ case !test.ok && err == nil:
+ t.Errorf("%s: expected error; got none", test.name)
+ continue
+ case test.ok && err != nil:
+ t.Errorf("%s: unexpected execute error: %s", test.name, err)
+ continue
+ case !test.ok && err != nil:
+ // expected error, got one
+ if *debug {
+ fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
+ }
+ }
+ result := b.String()
+ if result != test.output {
+ t.Errorf("%s: expected\n\t%q\ngot\n\t%q", test.name, test.output, result)
+ }
+ }
+}
+
+func TestExecute(t *testing.T) {
+ testExecute(execTests, nil, t)
+}
+
+var delimPairs = []string{
+ "", "", // default
+ "{{", "}}", // same as default
+ "|", "|", // same
+ "(日)", "(本)", // peculiar
+}
+
+func TestDelims(t *testing.T) {
+ const hello = "Hello, world"
+ var value = struct{ Str string }{hello}
+ for i := 0; i < len(delimPairs); i += 2 {
+ text := ".Str"
+ left := delimPairs[i+0]
+ trueLeft := left
+ right := delimPairs[i+1]
+ trueRight := right
+ if left == "" { // default case
+ trueLeft = "{{"
+ }
+ if right == "" { // default case
+ trueRight = "}}"
+ }
+ text = trueLeft + text + trueRight
+ // Now add a comment
+ text += trueLeft + "/*comment*/" + trueRight
+ // Now add an action containing a string.
+ text += trueLeft + `"` + trueLeft + `"` + trueRight
+ // At this point text looks like `{{.Str}}{{/*comment*/}}{{"{{"}}`.
+ tmpl, err := New("delims").Delims(left, right).Parse(text)
+ if err != nil {
+ t.Fatalf("delim %q text %q parse err %s", left, text, err)
+ }
+ var b = new(bytes.Buffer)
+ err = tmpl.Execute(b, value)
+ if err != nil {
+ t.Fatalf("delim %q exec err %s", left, err)
+ }
+ if b.String() != hello+trueLeft {
+ t.Errorf("expected %q got %q", hello+trueLeft, b.String())
+ }
+ }
+}
+
+// Check that an error from a method flows back to the top.
+func TestExecuteError(t *testing.T) {
+ b := new(bytes.Buffer)
+ tmpl := New("error")
+ _, err := tmpl.Parse("{{.MyError true}}")
+ if err != nil {
+ t.Fatalf("parse error: %s", err)
+ }
+ err = tmpl.Execute(b, tVal)
+ if err == nil {
+ t.Errorf("expected error; got none")
+ } else if !strings.Contains(err.Error(), myError.Error()) {
+ if *debug {
+ fmt.Printf("test execute error: %s\n", err)
+ }
+ t.Errorf("expected myError; got %s", err)
+ }
+}
+
+const execErrorText = `line 1
+line 2
+line 3
+{{template "one" .}}
+{{define "one"}}{{template "two" .}}{{end}}
+{{define "two"}}{{template "three" .}}{{end}}
+{{define "three"}}{{index "hi" $}}{{end}}`
+
+// Check that an error from a nested template contains all the relevant information.
+func TestExecError(t *testing.T) {
+ tmpl, err := New("top").Parse(execErrorText)
+ if err != nil {
+ t.Fatal("parse error:", err)
+ }
+ var b bytes.Buffer
+ err = tmpl.Execute(&b, 5) // 5 is out of range indexing "hi"
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ const want = `template: top:7:20: executing "three" at <index "hi" $>: error calling index: index out of range: 5`
+ got := err.Error()
+ if got != want {
+ t.Errorf("expected\n%q\ngot\n%q", want, got)
+ }
+}
+
+func TestJSEscaping(t *testing.T) {
+ testCases := []struct {
+ in, exp string
+ }{
+ {`a`, `a`},
+ {`'foo`, `\'foo`},
+ {`Go "jump" \`, `Go \"jump\" \\`},
+ {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`},
+ {"unprintable \uFDFF", `unprintable \uFDFF`},
+ {`<html>`, `\u003Chtml\u003E`},
+ {`no = in attributes`, `no \u003D in attributes`},
+ {`&#x27; does not become HTML entity`, `\u0026#x27; does not become HTML entity`},
+ }
+ for _, tc := range testCases {
+ s := JSEscapeString(tc.in)
+ if s != tc.exp {
+ t.Errorf("JS escaping [%s] got [%s] want [%s]", tc.in, s, tc.exp)
+ }
+ }
+}
+
+// A nice example: walk a binary tree.
+
+type Tree struct {
+ Val int
+ Left, Right *Tree
+}
+
+// Use different delimiters to test Set.Delims.
+// Also test the trimming of leading and trailing spaces.
+const treeTemplate = `
+ (- define "tree" -)
+ [
+ (- .Val -)
+ (- with .Left -)
+ (template "tree" . -)
+ (- end -)
+ (- with .Right -)
+ (- template "tree" . -)
+ (- end -)
+ ]
+ (- end -)
+`
+
+func TestTree(t *testing.T) {
+ var tree = &Tree{
+ 1,
+ &Tree{
+ 2, &Tree{
+ 3,
+ &Tree{
+ 4, nil, nil,
+ },
+ nil,
+ },
+ &Tree{
+ 5,
+ &Tree{
+ 6, nil, nil,
+ },
+ nil,
+ },
+ },
+ &Tree{
+ 7,
+ &Tree{
+ 8,
+ &Tree{
+ 9, nil, nil,
+ },
+ nil,
+ },
+ &Tree{
+ 10,
+ &Tree{
+ 11, nil, nil,
+ },
+ nil,
+ },
+ },
+ }
+ tmpl, err := New("root").Delims("(", ")").Parse(treeTemplate)
+ if err != nil {
+ t.Fatal("parse error:", err)
+ }
+ var b bytes.Buffer
+ const expect = "[1[2[3[4]][5[6]]][7[8[9]][10[11]]]]"
+ // First by looking up the template.
+ err = tmpl.Lookup("tree").Execute(&b, tree)
+ if err != nil {
+ t.Fatal("exec error:", err)
+ }
+ result := b.String()
+ if result != expect {
+ t.Errorf("expected %q got %q", expect, result)
+ }
+ // Then direct to execution.
+ b.Reset()
+ err = tmpl.ExecuteTemplate(&b, "tree", tree)
+ if err != nil {
+ t.Fatal("exec error:", err)
+ }
+ result = b.String()
+ if result != expect {
+ t.Errorf("expected %q got %q", expect, result)
+ }
+}
+
+func TestExecuteOnNewTemplate(t *testing.T) {
+ // This is issue 3872.
+ New("Name").Templates()
+ // This is issue 11379.
+ // new(Template).Templates() // TODO: crashes
+ // new(Template).Parse("") // TODO: crashes
+ // new(Template).New("abc").Parse("") // TODO: crashes
+ // new(Template).Execute(nil, nil) // TODO: crashes; returns an error (but does not crash)
+ // new(Template).ExecuteTemplate(nil, "XXX", nil) // TODO: crashes; returns an error (but does not crash)
+}
+
+const testTemplates = `{{define "one"}}one{{end}}{{define "two"}}two{{end}}`
+
+func TestMessageForExecuteEmpty(t *testing.T) {
+ // Test a truly empty template.
+ tmpl := New("empty")
+ var b bytes.Buffer
+ err := tmpl.Execute(&b, 0)
+ if err == nil {
+ t.Fatal("expected initial error")
+ }
+ got := err.Error()
+ want := `template: "empty" is an incomplete or empty template` // NOTE: text/template has extra "empty: " in message
+ if got != want {
+ t.Errorf("expected error %s got %s", want, got)
+ }
+
+ // Add a non-empty template to check that the error is helpful.
+ tmpl = New("empty")
+ tests, err := New("").Parse(testTemplates)
+ if err != nil {
+ t.Fatal(err)
+ }
+ tmpl.AddParseTree("secondary", tests.Tree)
+ err = tmpl.Execute(&b, 0)
+ if err == nil {
+ t.Fatal("expected second error")
+ }
+ got = err.Error()
+ if got != want {
+ t.Errorf("expected error %s got %s", want, got)
+ }
+ // Make sure we can execute the secondary.
+ err = tmpl.ExecuteTemplate(&b, "secondary", 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestFinalForPrintf(t *testing.T) {
+ tmpl, err := New("").Parse(`{{"x" | printf}}`)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var b bytes.Buffer
+ err = tmpl.Execute(&b, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+type cmpTest struct {
+ expr string
+ truth string
+ ok bool
+}
+
+var cmpTests = []cmpTest{
+ {"eq true true", "true", true},
+ {"eq true false", "false", true},
+ {"eq 1+2i 1+2i", "true", true},
+ {"eq 1+2i 1+3i", "false", true},
+ {"eq 1.5 1.5", "true", true},
+ {"eq 1.5 2.5", "false", true},
+ {"eq 1 1", "true", true},
+ {"eq 1 2", "false", true},
+ {"eq `xy` `xy`", "true", true},
+ {"eq `xy` `xyz`", "false", true},
+ {"eq .Uthree .Uthree", "true", true},
+ {"eq .Uthree .Ufour", "false", true},
+ {"eq 3 4 5 6 3", "true", true},
+ {"eq 3 4 5 6 7", "false", true},
+ {"ne true true", "false", true},
+ {"ne true false", "true", true},
+ {"ne 1+2i 1+2i", "false", true},
+ {"ne 1+2i 1+3i", "true", true},
+ {"ne 1.5 1.5", "false", true},
+ {"ne 1.5 2.5", "true", true},
+ {"ne 1 1", "false", true},
+ {"ne 1 2", "true", true},
+ {"ne `xy` `xy`", "false", true},
+ {"ne `xy` `xyz`", "true", true},
+ {"ne .Uthree .Uthree", "false", true},
+ {"ne .Uthree .Ufour", "true", true},
+ {"lt 1.5 1.5", "false", true},
+ {"lt 1.5 2.5", "true", true},
+ {"lt 1 1", "false", true},
+ {"lt 1 2", "true", true},
+ {"lt `xy` `xy`", "false", true},
+ {"lt `xy` `xyz`", "true", true},
+ {"lt .Uthree .Uthree", "false", true},
+ {"lt .Uthree .Ufour", "true", true},
+ {"le 1.5 1.5", "true", true},
+ {"le 1.5 2.5", "true", true},
+ {"le 2.5 1.5", "false", true},
+ {"le 1 1", "true", true},
+ {"le 1 2", "true", true},
+ {"le 2 1", "false", true},
+ {"le `xy` `xy`", "true", true},
+ {"le `xy` `xyz`", "true", true},
+ {"le `xyz` `xy`", "false", true},
+ {"le .Uthree .Uthree", "true", true},
+ {"le .Uthree .Ufour", "true", true},
+ {"le .Ufour .Uthree", "false", true},
+ {"gt 1.5 1.5", "false", true},
+ {"gt 1.5 2.5", "false", true},
+ {"gt 1 1", "false", true},
+ {"gt 2 1", "true", true},
+ {"gt 1 2", "false", true},
+ {"gt `xy` `xy`", "false", true},
+ {"gt `xy` `xyz`", "false", true},
+ {"gt .Uthree .Uthree", "false", true},
+ {"gt .Uthree .Ufour", "false", true},
+ {"gt .Ufour .Uthree", "true", true},
+ {"ge 1.5 1.5", "true", true},
+ {"ge 1.5 2.5", "false", true},
+ {"ge 2.5 1.5", "true", true},
+ {"ge 1 1", "true", true},
+ {"ge 1 2", "false", true},
+ {"ge 2 1", "true", true},
+ {"ge `xy` `xy`", "true", true},
+ {"ge `xy` `xyz`", "false", true},
+ {"ge `xyz` `xy`", "true", true},
+ {"ge .Uthree .Uthree", "true", true},
+ {"ge .Uthree .Ufour", "false", true},
+ {"ge .Ufour .Uthree", "true", true},
+ // Mixing signed and unsigned integers.
+ {"eq .Uthree .Three", "true", true},
+ {"eq .Three .Uthree", "true", true},
+ {"le .Uthree .Three", "true", true},
+ {"le .Three .Uthree", "true", true},
+ {"ge .Uthree .Three", "true", true},
+ {"ge .Three .Uthree", "true", true},
+ {"lt .Uthree .Three", "false", true},
+ {"lt .Three .Uthree", "false", true},
+ {"gt .Uthree .Three", "false", true},
+ {"gt .Three .Uthree", "false", true},
+ {"eq .Ufour .Three", "false", true},
+ {"lt .Ufour .Three", "false", true},
+ {"gt .Ufour .Three", "true", true},
+ {"eq .NegOne .Uthree", "false", true},
+ {"eq .Uthree .NegOne", "false", true},
+ {"ne .NegOne .Uthree", "true", true},
+ {"ne .Uthree .NegOne", "true", true},
+ {"lt .NegOne .Uthree", "true", true},
+ {"lt .Uthree .NegOne", "false", true},
+ {"le .NegOne .Uthree", "true", true},
+ {"le .Uthree .NegOne", "false", true},
+ {"gt .NegOne .Uthree", "false", true},
+ {"gt .Uthree .NegOne", "true", true},
+ {"ge .NegOne .Uthree", "false", true},
+ {"ge .Uthree .NegOne", "true", true},
+ {"eq (index `x` 0) 'x'", "true", true}, // The example that triggered this rule.
+ {"eq (index `x` 0) 'y'", "false", true},
+ {"eq .V1 .V2", "true", true},
+ {"eq .Ptr .Ptr", "true", true},
+ {"eq .Ptr .NilPtr", "false", true},
+ {"eq .NilPtr .NilPtr", "true", true},
+ {"eq .Iface1 .Iface1", "true", true},
+ {"eq .Iface1 .Iface2", "false", true},
+ {"eq .Iface2 .Iface2", "true", true},
+ // Errors
+ {"eq `xy` 1", "", false}, // Different types.
+ {"eq 2 2.0", "", false}, // Different types.
+ {"lt true true", "", false}, // Unordered types.
+ {"lt 1+0i 1+0i", "", false}, // Unordered types.
+ {"eq .Ptr 1", "", false}, // Incompatible types.
+ {"eq .Ptr .NegOne", "", false}, // Incompatible types.
+ {"eq .Map .Map", "", false}, // Uncomparable types.
+ {"eq .Map .V1", "", false}, // Uncomparable types.
+}
+
+func TestComparison(t *testing.T) {
+ b := new(bytes.Buffer)
+ var cmpStruct = struct {
+ Uthree, Ufour uint
+ NegOne, Three int
+ Ptr, NilPtr *int
+ Map map[int]int
+ V1, V2 V
+ Iface1, Iface2 fmt.Stringer
+ }{
+ Uthree: 3,
+ Ufour: 4,
+ NegOne: -1,
+ Three: 3,
+ Ptr: new(int),
+ Iface1: b,
+ }
+ for _, test := range cmpTests {
+ text := fmt.Sprintf("{{if %s}}true{{else}}false{{end}}", test.expr)
+ tmpl, err := New("empty").Parse(text)
+ if err != nil {
+ t.Fatalf("%q: %s", test.expr, err)
+ }
+ b.Reset()
+ err = tmpl.Execute(b, &cmpStruct)
+ if test.ok && err != nil {
+ t.Errorf("%s errored incorrectly: %s", test.expr, err)
+ continue
+ }
+ if !test.ok && err == nil {
+ t.Errorf("%s did not error", test.expr)
+ continue
+ }
+ if b.String() != test.truth {
+ t.Errorf("%s: want %s; got %s", test.expr, test.truth, b.String())
+ }
+ }
+}
+
+func TestMissingMapKey(t *testing.T) {
+ data := map[string]int{
+ "x": 99,
+ }
+ tmpl, err := New("t1").Parse("{{.x}} {{.y}}")
+ if err != nil {
+ t.Fatal(err)
+ }
+ var b bytes.Buffer
+ // By default, just get "<no value>" // NOTE: not in html/template, get empty string
+ err = tmpl.Execute(&b, data)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := "99 "
+ got := b.String()
+ if got != want {
+ t.Errorf("got %q; expected %q", got, want)
+ }
+ // Same if we set the option explicitly to the default.
+ tmpl.Option("missingkey=default")
+ b.Reset()
+ err = tmpl.Execute(&b, data)
+ if err != nil {
+ t.Fatal("default:", err)
+ }
+ got = b.String()
+ if got != want {
+ t.Errorf("got %q; expected %q", got, want)
+ }
+ // Next we ask for a zero value
+ tmpl.Option("missingkey=zero")
+ b.Reset()
+ err = tmpl.Execute(&b, data)
+ if err != nil {
+ t.Fatal("zero:", err)
+ }
+ want = "99 0"
+ got = b.String()
+ if got != want {
+ t.Errorf("got %q; expected %q", got, want)
+ }
+ // Now we ask for an error.
+ tmpl.Option("missingkey=error")
+ err = tmpl.Execute(&b, data)
+ if err == nil {
+ t.Errorf("expected error; got none")
+ }
+ // same Option, but now a nil interface: ask for an error
+ err = tmpl.Execute(&b, nil)
+ t.Log(err)
+ if err == nil {
+ t.Errorf("expected error for nil-interface; got none")
+ }
+}
+
+// Test that the error message for multiline unterminated string
+// refers to the line number of the opening quote.
+func TestUnterminatedStringError(t *testing.T) {
+ _, err := New("X").Parse("hello\n\n{{`unterminated\n\n\n\n}}\n some more\n\n")
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ str := err.Error()
+ if !strings.Contains(str, "X:3: unexpected unterminated raw quoted string") {
+ t.Fatalf("unexpected error: %s", str)
+ }
+}
+
+const alwaysErrorText = "always be failing"
+
+var alwaysError = errors.New(alwaysErrorText)
+
+type ErrorWriter int
+
+func (e ErrorWriter) Write(p []byte) (int, error) {
+ return 0, alwaysError
+}
+
+func TestExecuteGivesExecError(t *testing.T) {
+ // First, a non-execution error shouldn't be an ExecError.
+ tmpl, err := New("X").Parse("hello")
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = tmpl.Execute(ErrorWriter(0), 0)
+ if err == nil {
+ t.Fatal("expected error; got none")
+ }
+ if err.Error() != alwaysErrorText {
+ t.Errorf("expected %q error; got %q", alwaysErrorText, err)
+ }
+ // This one should be an ExecError.
+ tmpl, err = New("X").Parse("hello, {{.X.Y}}")
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = tmpl.Execute(ioutil.Discard, 0)
+ if err == nil {
+ t.Fatal("expected error; got none")
+ }
+ eerr, ok := err.(template.ExecError)
+ if !ok {
+ t.Fatalf("did not expect ExecError %s", eerr)
+ }
+ expect := "field X in type int"
+ if !strings.Contains(err.Error(), expect) {
+ t.Errorf("expected %q; got %q", expect, err)
+ }
+}
+
+func funcNameTestFunc() int {
+ return 0
+}
+
+func TestGoodFuncNames(t *testing.T) {
+ names := []string{
+ "_",
+ "a",
+ "a1",
+ "a1",
+ "Ӵ",
+ }
+ for _, name := range names {
+ tmpl := New("X").Funcs(
+ FuncMap{
+ name: funcNameTestFunc,
+ },
+ )
+ if tmpl == nil {
+ t.Fatalf("nil result for %q", name)
+ }
+ }
+}
+
+func TestBadFuncNames(t *testing.T) {
+ names := []string{
+ "",
+ "2",
+ "a-b",
+ }
+ for _, name := range names {
+ testBadFuncName(name, t)
+ }
+}
+
+func testBadFuncName(name string, t *testing.T) {
+ t.Helper()
+ defer func() {
+ recover()
+ }()
+ New("X").Funcs(
+ FuncMap{
+ name: funcNameTestFunc,
+ },
+ )
+ // If we get here, the name did not cause a panic, which is how Funcs
+ // reports an error.
+ t.Errorf("%q succeeded incorrectly as function name", name)
+}
+
+func TestBlock(t *testing.T) {
+ const (
+ input = `a({{block "inner" .}}bar({{.}})baz{{end}})b`
+ want = `a(bar(hello)baz)b`
+ overlay = `{{define "inner"}}foo({{.}})bar{{end}}`
+ want2 = `a(foo(goodbye)bar)b`
+ )
+ tmpl, err := New("outer").Parse(input)
+ if err != nil {
+ t.Fatal(err)
+ }
+ tmpl2, err := Must(tmpl.Clone()).Parse(overlay)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var buf bytes.Buffer
+ if err := tmpl.Execute(&buf, "hello"); err != nil {
+ t.Fatal(err)
+ }
+ if got := buf.String(); got != want {
+ t.Errorf("got %q, want %q", got, want)
+ }
+
+ buf.Reset()
+ if err := tmpl2.Execute(&buf, "goodbye"); err != nil {
+ t.Fatal(err)
+ }
+ if got := buf.String(); got != want2 {
+ t.Errorf("got %q, want %q", got, want2)
+ }
+}
+
+func TestEvalFieldErrors(t *testing.T) {
+ tests := []struct {
+ name, src string
+ value interface{}
+ want string
+ }{
+ {
+ // Check that calling an invalid field on nil pointer
+ // prints a field error instead of a distracting nil
+ // pointer error. https://golang.org/issue/15125
+ "MissingFieldOnNil",
+ "{{.MissingField}}",
+ (*T)(nil),
+ "can't evaluate field MissingField in type *template.T",
+ },
+ {
+ "MissingFieldOnNonNil",
+ "{{.MissingField}}",
+ &T{},
+ "can't evaluate field MissingField in type *template.T",
+ },
+ {
+ "ExistingFieldOnNil",
+ "{{.X}}",
+ (*T)(nil),
+ "nil pointer evaluating *template.T.X",
+ },
+ {
+ "MissingKeyOnNilMap",
+ "{{.MissingKey}}",
+ (*map[string]string)(nil),
+ "nil pointer evaluating *map[string]string.MissingKey",
+ },
+ {
+ "MissingKeyOnNilMapPtr",
+ "{{.MissingKey}}",
+ (*map[string]string)(nil),
+ "nil pointer evaluating *map[string]string.MissingKey",
+ },
+ {
+ "MissingKeyOnMapPtrToNil",
+ "{{.MissingKey}}",
+ &map[string]string{},
+ "<nil>",
+ },
+ }
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ tmpl := Must(New("tmpl").Parse(tc.src))
+ err := tmpl.Execute(ioutil.Discard, tc.value)
+ got := "<nil>"
+ if err != nil {
+ got = err.Error()
+ }
+ if !strings.HasSuffix(got, tc.want) {
+ t.Fatalf("got error %q, want %q", got, tc.want)
+ }
+ })
+ }
+}
+
+func TestMaxExecDepth(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in -short mode")
+ }
+ tmpl := Must(New("tmpl").Parse(`{{template "tmpl" .}}`))
+ err := tmpl.Execute(ioutil.Discard, nil)
+ got := "<nil>"
+ if err != nil {
+ got = err.Error()
+ }
+ const want = "exceeded maximum template depth"
+ if !strings.Contains(got, want) {
+ t.Errorf("got error %q; want %q", got, want)
+ }
+}
+
+func TestAddrOfIndex(t *testing.T) {
+ // golang.org/issue/14916.
+ // Before index worked on reflect.Values, the .String could not be
+ // found on the (incorrectly unaddressable) V value,
+ // in contrast to range, which worked fine.
+ // Also testing that passing a reflect.Value to tmpl.Execute works.
+ texts := []string{
+ `{{range .}}{{.String}}{{end}}`,
+ `{{with index . 0}}{{.String}}{{end}}`,
+ }
+ for _, text := range texts {
+ tmpl := Must(New("tmpl").Parse(text))
+ var buf bytes.Buffer
+ err := tmpl.Execute(&buf, reflect.ValueOf([]V{{1}}))
+ if err != nil {
+ t.Fatalf("%s: Execute: %v", text, err)
+ }
+ if buf.String() != "&lt;1&gt;" {
+ t.Fatalf("%s: template output = %q, want %q", text, &buf, "&lt;1&gt;")
+ }
+ }
+}
+
+func TestInterfaceValues(t *testing.T) {
+ // golang.org/issue/17714.
+ // Before index worked on reflect.Values, interface values
+ // were always implicitly promoted to the underlying value,
+ // except that nil interfaces were promoted to the zero reflect.Value.
+ // Eliminating a round trip to interface{} and back to reflect.Value
+ // eliminated this promotion, breaking these cases.
+ tests := []struct {
+ text string
+ out string
+ }{
+ {`{{index .Nil 1}}`, "ERROR: index of untyped nil"},
+ {`{{index .Slice 2}}`, "2"},
+ {`{{index .Slice .Two}}`, "2"},
+ {`{{call .Nil 1}}`, "ERROR: call of nil"},
+ {`{{call .PlusOne 1}}`, "2"},
+ {`{{call .PlusOne .One}}`, "2"},
+ {`{{and (index .Slice 0) true}}`, "0"},
+ {`{{and .Zero true}}`, "0"},
+ {`{{and (index .Slice 1) false}}`, "false"},
+ {`{{and .One false}}`, "false"},
+ {`{{or (index .Slice 0) false}}`, "false"},
+ {`{{or .Zero false}}`, "false"},
+ {`{{or (index .Slice 1) true}}`, "1"},
+ {`{{or .One true}}`, "1"},
+ {`{{not (index .Slice 0)}}`, "true"},
+ {`{{not .Zero}}`, "true"},
+ {`{{not (index .Slice 1)}}`, "false"},
+ {`{{not .One}}`, "false"},
+ {`{{eq (index .Slice 0) .Zero}}`, "true"},
+ {`{{eq (index .Slice 1) .One}}`, "true"},
+ {`{{ne (index .Slice 0) .Zero}}`, "false"},
+ {`{{ne (index .Slice 1) .One}}`, "false"},
+ {`{{ge (index .Slice 0) .One}}`, "false"},
+ {`{{ge (index .Slice 1) .Zero}}`, "true"},
+ {`{{gt (index .Slice 0) .One}}`, "false"},
+ {`{{gt (index .Slice 1) .Zero}}`, "true"},
+ {`{{le (index .Slice 0) .One}}`, "true"},
+ {`{{le (index .Slice 1) .Zero}}`, "false"},
+ {`{{lt (index .Slice 0) .One}}`, "true"},
+ {`{{lt (index .Slice 1) .Zero}}`, "false"},
+ }
+
+ for _, tt := range tests {
+ tmpl := Must(New("tmpl").Parse(tt.text))
+ var buf bytes.Buffer
+ err := tmpl.Execute(&buf, map[string]interface{}{
+ "PlusOne": func(n int) int {
+ return n + 1
+ },
+ "Slice": []int{0, 1, 2, 3},
+ "One": 1,
+ "Two": 2,
+ "Nil": nil,
+ "Zero": 0,
+ })
+ if strings.HasPrefix(tt.out, "ERROR:") {
+ e := strings.TrimSpace(strings.TrimPrefix(tt.out, "ERROR:"))
+ if err == nil || !strings.Contains(err.Error(), e) {
+ t.Errorf("%s: Execute: %v, want error %q", tt.text, err, e)
+ }
+ continue
+ }
+ if err != nil {
+ t.Errorf("%s: Execute: %v", tt.text, err)
+ continue
+ }
+ if buf.String() != tt.out {
+ t.Errorf("%s: template output = %q, want %q", tt.text, &buf, tt.out)
+ }
+ }
+}
+
+// Check that panics during calls are recovered and returned as errors.
+func TestExecutePanicDuringCall(t *testing.T) {
+ funcs := map[string]interface{}{
+ "doPanic": func() string {
+ panic("custom panic string")
+ },
+ }
+ tests := []struct {
+ name string
+ input string
+ data interface{}
+ wantErr string
+ }{
+ {
+ "direct func call panics",
+ "{{doPanic}}", (*T)(nil),
+ `template: t:1:2: executing "t" at <doPanic>: error calling doPanic: custom panic string`,
+ },
+ {
+ "indirect func call panics",
+ "{{call doPanic}}", (*T)(nil),
+ `template: t:1:7: executing "t" at <doPanic>: error calling doPanic: custom panic string`,
+ },
+ {
+ "direct method call panics",
+ "{{.GetU}}", (*T)(nil),
+ `template: t:1:2: executing "t" at <.GetU>: error calling GetU: runtime error: invalid memory address or nil pointer dereference`,
+ },
+ {
+ "indirect method call panics",
+ "{{call .GetU}}", (*T)(nil),
+ `template: t:1:7: executing "t" at <.GetU>: error calling GetU: runtime error: invalid memory address or nil pointer dereference`,
+ },
+ {
+ "func field call panics",
+ "{{call .PanicFunc}}", tVal,
+ `template: t:1:2: executing "t" at <call .PanicFunc>: error calling call: test panic`,
+ },
+ {
+ "method call on nil interface",
+ "{{.NonEmptyInterfaceNil.Method0}}", tVal,
+ `template: t:1:23: executing "t" at <.NonEmptyInterfaceNil.Method0>: nil pointer evaluating template.I.Method0`,
+ },
+ }
+ for _, tc := range tests {
+ b := new(bytes.Buffer)
+ tmpl, err := New("t").Funcs(funcs).Parse(tc.input)
+ if err != nil {
+ t.Fatalf("parse error: %s", err)
+ }
+ err = tmpl.Execute(b, tc.data)
+ if err == nil {
+ t.Errorf("%s: expected error; got none", tc.name)
+ } else if !strings.Contains(err.Error(), tc.wantErr) {
+ if *debug {
+ fmt.Printf("%s: test execute error: %s\n", tc.name, err)
+ }
+ t.Errorf("%s: expected error:\n%s\ngot:\n%s", tc.name, tc.wantErr, err)
+ }
+ }
+}
+
+// Issue 31810. Check that a parenthesized first argument behaves properly.
+func TestIssue31810(t *testing.T) {
+ t.Skip("broken in html/template")
+
+ // A simple value with no arguments is fine.
+ var b bytes.Buffer
+ const text = "{{ (.) }}"
+ tmpl, err := New("").Parse(text)
+ if err != nil {
+ t.Error(err)
+ }
+ err = tmpl.Execute(&b, "result")
+ if err != nil {
+ t.Error(err)
+ }
+ if b.String() != "result" {
+ t.Errorf("%s got %q, expected %q", text, b.String(), "result")
+ }
+
+ // Even a plain function fails - need to use call.
+ f := func() string { return "result" }
+ b.Reset()
+ err = tmpl.Execute(&b, f)
+ if err == nil {
+ t.Error("expected error with no call, got none")
+ }
+
+ // Works if the function is explicitly called.
+ const textCall = "{{ (call .) }}"
+ tmpl, err = New("").Parse(textCall)
+ b.Reset()
+ err = tmpl.Execute(&b, f)
+ if err != nil {
+ t.Error(err)
+ }
+ if b.String() != "result" {
+ t.Errorf("%s got %q, expected %q", textCall, b.String(), "result")
+ }
+}
diff --git a/src/html/template/multi_test.go b/src/html/template/multi_test.go
new file mode 100644
index 0000000000..50526c5b65
--- /dev/null
+++ b/src/html/template/multi_test.go
@@ -0,0 +1,246 @@
+// 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.
+
+// Tests for multiple-template execution, copied from text/template.
+
+package template
+
+import (
+ "bytes"
+ "testing"
+ "text/template/parse"
+)
+
+var multiExecTests = []execTest{
+ {"empty", "", "", nil, true},
+ {"text", "some text", "some text", nil, true},
+ {"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true},
+ {"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true},
+ {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true},
+ {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true},
+ {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true},
+ {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true},
+ {"variable declared by template", `{{template "nested" $x:=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true},
+
+ // User-defined function: test argument evaluator.
+ {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true},
+ {"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true},
+}
+
+// These strings are also in testdata/*.
+const multiText1 = `
+ {{define "x"}}TEXT{{end}}
+ {{define "dotV"}}{{.V}}{{end}}
+`
+
+const multiText2 = `
+ {{define "dot"}}{{.}}{{end}}
+ {{define "nested"}}{{template "dot" .}}{{end}}
+`
+
+func TestMultiExecute(t *testing.T) {
+ // Declare a couple of templates first.
+ template, err := New("root").Parse(multiText1)
+ if err != nil {
+ t.Fatalf("parse error for 1: %s", err)
+ }
+ _, err = template.Parse(multiText2)
+ if err != nil {
+ t.Fatalf("parse error for 2: %s", err)
+ }
+ testExecute(multiExecTests, template, t)
+}
+
+func TestParseFiles(t *testing.T) {
+ _, err := ParseFiles("DOES NOT EXIST")
+ if err == nil {
+ t.Error("expected error for non-existent file; got none")
+ }
+ template := New("root")
+ _, err = template.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl")
+ if err != nil {
+ t.Fatalf("error parsing files: %v", err)
+ }
+ testExecute(multiExecTests, template, t)
+}
+
+func TestParseGlob(t *testing.T) {
+ _, err := ParseGlob("DOES NOT EXIST")
+ if err == nil {
+ t.Error("expected error for non-existent file; got none")
+ }
+ _, err = New("error").ParseGlob("[x")
+ if err == nil {
+ t.Error("expected error for bad pattern; got none")
+ }
+ template := New("root")
+ _, err = template.ParseGlob("testdata/file*.tmpl")
+ if err != nil {
+ t.Fatalf("error parsing files: %v", err)
+ }
+ testExecute(multiExecTests, template, t)
+}
+
+// In these tests, actual content (not just template definitions) comes from the parsed files.
+
+var templateFileExecTests = []execTest{
+ {"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\n\ny\ntemplate2\n\nx\n", 0, true},
+}
+
+func TestParseFilesWithData(t *testing.T) {
+ template, err := New("root").ParseFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl")
+ if err != nil {
+ t.Fatalf("error parsing files: %v", err)
+ }
+ testExecute(templateFileExecTests, template, t)
+}
+
+func TestParseGlobWithData(t *testing.T) {
+ template, err := New("root").ParseGlob("testdata/tmpl*.tmpl")
+ if err != nil {
+ t.Fatalf("error parsing files: %v", err)
+ }
+ testExecute(templateFileExecTests, template, t)
+}
+
+const (
+ cloneText1 = `{{define "a"}}{{template "b"}}{{template "c"}}{{end}}`
+ cloneText2 = `{{define "b"}}b{{end}}`
+ cloneText3 = `{{define "c"}}root{{end}}`
+ cloneText4 = `{{define "c"}}clone{{end}}`
+)
+
+// Issue 7032
+func TestAddParseTreeToUnparsedTemplate(t *testing.T) {
+ master := "{{define \"master\"}}{{end}}"
+ tmpl := New("master")
+ tree, err := parse.Parse("master", master, "", "", nil)
+ if err != nil {
+ t.Fatalf("unexpected parse err: %v", err)
+ }
+ masterTree := tree["master"]
+ tmpl.AddParseTree("master", masterTree) // used to panic
+}
+
+func TestRedefinition(t *testing.T) {
+ var tmpl *Template
+ var err error
+ if tmpl, err = New("tmpl1").Parse(`{{define "test"}}foo{{end}}`); err != nil {
+ t.Fatalf("parse 1: %v", err)
+ }
+ if _, err = tmpl.Parse(`{{define "test"}}bar{{end}}`); err != nil {
+ t.Fatalf("got error %v, expected nil", err)
+ }
+ if _, err = tmpl.New("tmpl2").Parse(`{{define "test"}}bar{{end}}`); err != nil {
+ t.Fatalf("got error %v, expected nil", err)
+ }
+}
+
+// Issue 10879
+func TestEmptyTemplateCloneCrash(t *testing.T) {
+ t1 := New("base")
+ t1.Clone() // used to panic
+}
+
+// Issue 10910, 10926
+func TestTemplateLookUp(t *testing.T) {
+ t.Skip("broken on html/template") // TODO
+ t1 := New("foo")
+ if t1.Lookup("foo") != nil {
+ t.Error("Lookup returned non-nil value for undefined template foo")
+ }
+ t1.New("bar")
+ if t1.Lookup("bar") != nil {
+ t.Error("Lookup returned non-nil value for undefined template bar")
+ }
+ t1.Parse(`{{define "foo"}}test{{end}}`)
+ if t1.Lookup("foo") == nil {
+ t.Error("Lookup returned nil value for defined template")
+ }
+}
+
+func TestParse(t *testing.T) {
+ // In multiple calls to Parse with the same receiver template, only one call
+ // can contain text other than space, comments, and template definitions
+ t1 := New("test")
+ if _, err := t1.Parse(`{{define "test"}}{{end}}`); err != nil {
+ t.Fatalf("parsing test: %s", err)
+ }
+ if _, err := t1.Parse(`{{define "test"}}{{/* this is a comment */}}{{end}}`); err != nil {
+ t.Fatalf("parsing test: %s", err)
+ }
+ if _, err := t1.Parse(`{{define "test"}}foo{{end}}`); err != nil {
+ t.Fatalf("parsing test: %s", err)
+ }
+}
+
+func TestEmptyTemplate(t *testing.T) {
+ cases := []struct {
+ defn []string
+ in string
+ want string
+ }{
+ {[]string{"x", "y"}, "", "y"},
+ {[]string{""}, "once", ""},
+ {[]string{"", ""}, "twice", ""},
+ {[]string{"{{.}}", "{{.}}"}, "twice", "twice"},
+ {[]string{"{{/* a comment */}}", "{{/* a comment */}}"}, "comment", ""},
+ {[]string{"{{.}}", ""}, "twice", "twice"}, // TODO: should want "" not "twice"
+ }
+
+ for i, c := range cases {
+ root := New("root")
+
+ var (
+ m *Template
+ err error
+ )
+ for _, d := range c.defn {
+ m, err = root.New(c.in).Parse(d)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ buf := &bytes.Buffer{}
+ if err := m.Execute(buf, c.in); err != nil {
+ t.Error(i, err)
+ continue
+ }
+ if buf.String() != c.want {
+ t.Errorf("expected string %q: got %q", c.want, buf.String())
+ }
+ }
+}
+
+// Issue 19249 was a regression in 1.8 caused by the handling of empty
+// templates added in that release, which got different answers depending
+// on the order templates appeared in the internal map.
+func TestIssue19294(t *testing.T) {
+ // The empty block in "xhtml" should be replaced during execution
+ // by the contents of "stylesheet", but if the internal map associating
+ // names with templates is built in the wrong order, the empty block
+ // looks non-empty and this doesn't happen.
+ var inlined = map[string]string{
+ "stylesheet": `{{define "stylesheet"}}stylesheet{{end}}`,
+ "xhtml": `{{block "stylesheet" .}}{{end}}`,
+ }
+ all := []string{"stylesheet", "xhtml"}
+ for i := 0; i < 100; i++ {
+ res, err := New("title.xhtml").Parse(`{{template "xhtml" .}}`)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, name := range all {
+ _, err := res.New(name).Parse(inlined[name])
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ var buf bytes.Buffer
+ res.Execute(&buf, 0)
+ if buf.String() != "stylesheet" {
+ t.Fatalf("iteration %d: got %q; expected %q", i, buf.String(), "stylesheet")
+ }
+ }
+}
diff --git a/src/html/template/testdata/file1.tmpl b/src/html/template/testdata/file1.tmpl
new file mode 100644
index 0000000000..febf9d9f89
--- /dev/null
+++ b/src/html/template/testdata/file1.tmpl
@@ -0,0 +1,2 @@
+{{define "x"}}TEXT{{end}}
+{{define "dotV"}}{{.V}}{{end}}
diff --git a/src/html/template/testdata/file2.tmpl b/src/html/template/testdata/file2.tmpl
new file mode 100644
index 0000000000..39bf6fb9ee
--- /dev/null
+++ b/src/html/template/testdata/file2.tmpl
@@ -0,0 +1,2 @@
+{{define "dot"}}{{.}}{{end}}
+{{define "nested"}}{{template "dot" .}}{{end}}
diff --git a/src/html/template/testdata/tmpl1.tmpl b/src/html/template/testdata/tmpl1.tmpl
new file mode 100644
index 0000000000..b72b3a340c
--- /dev/null
+++ b/src/html/template/testdata/tmpl1.tmpl
@@ -0,0 +1,3 @@
+template1
+{{define "x"}}x{{end}}
+{{template "y"}}
diff --git a/src/html/template/testdata/tmpl2.tmpl b/src/html/template/testdata/tmpl2.tmpl
new file mode 100644
index 0000000000..16beba6e7d
--- /dev/null
+++ b/src/html/template/testdata/tmpl2.tmpl
@@ -0,0 +1,3 @@
+template2
+{{define "y"}}y{{end}}
+{{template "x"}}
diff --git a/src/net/http/server.go b/src/net/http/server.go
index a995a50658..d41b5f6f48 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -425,6 +425,16 @@ type response struct {
wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive"
wantsClose bool // HTTP request has Connection "close"
+ // canWriteContinue is a boolean value accessed as an atomic int32
+ // that says whether or not a 100 Continue header can be written
+ // to the connection.
+ // writeContinueMu must be held while writing the header.
+ // These two fields together synchronize the body reader
+ // (the expectContinueReader, which wants to write 100 Continue)
+ // against the main writer.
+ canWriteContinue atomicBool
+ writeContinueMu sync.Mutex
+
w *bufio.Writer // buffers output in chunks to chunkWriter
cw chunkWriter
@@ -515,6 +525,7 @@ type atomicBool int32
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
+func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
// declareTrailer is called for each Trailer header when the
// response header is written. It notes that a header will need to be
@@ -878,21 +889,27 @@ type expectContinueReader struct {
resp *response
readCloser io.ReadCloser
closed bool
- sawEOF bool
+ sawEOF atomicBool
}
func (ecr *expectContinueReader) Read(p []byte) (n int, err error) {
if ecr.closed {
return 0, ErrBodyReadAfterClose
}
- if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked() {
- ecr.resp.wroteContinue = true
- ecr.resp.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n")
- ecr.resp.conn.bufw.Flush()
+ w := ecr.resp
+ if !w.wroteContinue && w.canWriteContinue.isSet() && !w.conn.hijacked() {
+ w.wroteContinue = true
+ w.writeContinueMu.Lock()
+ if w.canWriteContinue.isSet() {
+ w.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n")
+ w.conn.bufw.Flush()
+ w.canWriteContinue.setFalse()
+ }
+ w.writeContinueMu.Unlock()
}
n, err = ecr.readCloser.Read(p)
if err == io.EOF {
- ecr.sawEOF = true
+ ecr.sawEOF.setTrue()
}
return
}
@@ -1311,7 +1328,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
// because we don't know if the next bytes on the wire will be
// the body-following-the-timer or the subsequent request.
// See Issue 11549.
- if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF {
+ if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF.isSet() {
w.closeAfterReply = true
}
@@ -1561,6 +1578,17 @@ func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err er
}
return 0, ErrHijacked
}
+
+ if w.canWriteContinue.isSet() {
+ // Body reader wants to write 100 Continue but hasn't yet.
+ // Tell it not to. The store must be done while holding the lock
+ // because the lock makes sure that there is not an active write
+ // this very moment.
+ w.writeContinueMu.Lock()
+ w.canWriteContinue.setFalse()
+ w.writeContinueMu.Unlock()
+ }
+
if !w.wroteHeader {
w.WriteHeader(StatusOK)
}
@@ -1872,6 +1900,7 @@ func (c *conn) serve(ctx context.Context) {
if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
// Wrap the Body reader with one that replies on the connection
req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
+ w.canWriteContinue.setTrue()
}
} else if req.Header.get("Expect") != "" {
w.sendExpectationFailed()
diff --git a/src/net/http/transport.go b/src/net/http/transport.go
index a41e732d98..d37b52b13d 100644
--- a/src/net/http/transport.go
+++ b/src/net/http/transport.go
@@ -100,7 +100,7 @@ type Transport struct {
idleLRU connLRU
reqMu sync.Mutex
- reqCanceler map[*Request]func(error)
+ reqCanceler map[cancelKey]func(error)
altMu sync.Mutex // guards changing altProto only
altProto atomic.Value // of nil or map[string]RoundTripper, key is URI scheme
@@ -273,6 +273,13 @@ type Transport struct {
ForceAttemptHTTP2 bool
}
+// A cancelKey is the key of the reqCanceler map.
+// We wrap the *Request in this type since we want to use the original request,
+// not any transient one created by roundTrip.
+type cancelKey struct {
+ req *Request
+}
+
func (t *Transport) writeBufferSize() int {
if t.WriteBufferSize > 0 {
return t.WriteBufferSize
@@ -433,9 +440,10 @@ func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error) {
// optional extra headers to write and stores any error to return
// from roundTrip.
type transportRequest struct {
- *Request // original request, not to be mutated
- extra Header // extra headers to write, or nil
- trace *httptrace.ClientTrace // optional
+ *Request // original request, not to be mutated
+ extra Header // extra headers to write, or nil
+ trace *httptrace.ClientTrace // optional
+ cancelKey cancelKey
mu sync.Mutex // guards err
err error // first setError value for mapRoundTripError to consider
@@ -512,6 +520,7 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) {
}
origReq := req
+ cancelKey := cancelKey{origReq}
req = setupRewindBody(req)
if altRT := t.alternateRoundTripper(req); altRT != nil {
@@ -546,7 +555,7 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) {
}
// treq gets modified by roundTrip, so we need to recreate for each retry.
- treq := &transportRequest{Request: req, trace: trace}
+ treq := &transportRequest{Request: req, trace: trace, cancelKey: cancelKey}
cm, err := t.connectMethodForRequest(treq)
if err != nil {
req.closeBody()
@@ -559,7 +568,7 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) {
// to send it requests.
pconn, err := t.getConn(treq, cm)
if err != nil {
- t.setReqCanceler(req, nil)
+ t.setReqCanceler(cancelKey, nil)
req.closeBody()
return nil, err
}
@@ -567,7 +576,7 @@ 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.setReqCanceler(cancelKey, nil) // not cancelable with CancelRequest
resp, err = pconn.alt.RoundTrip(req)
} else {
resp, err = pconn.roundTrip(treq)
@@ -753,14 +762,14 @@ func (t *Transport) CloseIdleConnections() {
// cancelable context instead. CancelRequest cannot cancel HTTP/2
// requests.
func (t *Transport) CancelRequest(req *Request) {
- t.cancelRequest(req, errRequestCanceled)
+ t.cancelRequest(cancelKey{req}, errRequestCanceled)
}
// Cancel an in-flight request, recording the error value.
-func (t *Transport) cancelRequest(req *Request, err error) {
+func (t *Transport) cancelRequest(key cancelKey, err error) {
t.reqMu.Lock()
- cancel := t.reqCanceler[req]
- delete(t.reqCanceler, req)
+ cancel := t.reqCanceler[key]
+ delete(t.reqCanceler, key)
t.reqMu.Unlock()
if cancel != nil {
cancel(err)
@@ -1093,16 +1102,16 @@ func (t *Transport) removeIdleConnLocked(pconn *persistConn) bool {
return removed
}
-func (t *Transport) setReqCanceler(r *Request, fn func(error)) {
+func (t *Transport) setReqCanceler(key cancelKey, fn func(error)) {
t.reqMu.Lock()
defer t.reqMu.Unlock()
if t.reqCanceler == nil {
- t.reqCanceler = make(map[*Request]func(error))
+ t.reqCanceler = make(map[cancelKey]func(error))
}
if fn != nil {
- t.reqCanceler[r] = fn
+ t.reqCanceler[key] = fn
} else {
- delete(t.reqCanceler, r)
+ delete(t.reqCanceler, key)
}
}
@@ -1110,17 +1119,17 @@ func (t *Transport) setReqCanceler(r *Request, fn func(error)) {
// for the request, we don't set the function and return false.
// Since CancelRequest will clear the canceler, we can use the return value to detect if
// the request was canceled since the last setReqCancel call.
-func (t *Transport) replaceReqCanceler(r *Request, fn func(error)) bool {
+func (t *Transport) replaceReqCanceler(key cancelKey, fn func(error)) bool {
t.reqMu.Lock()
defer t.reqMu.Unlock()
- _, ok := t.reqCanceler[r]
+ _, ok := t.reqCanceler[key]
if !ok {
return false
}
if fn != nil {
- t.reqCanceler[r] = fn
+ t.reqCanceler[key] = fn
} else {
- delete(t.reqCanceler, r)
+ delete(t.reqCanceler, key)
}
return true
}
@@ -1324,12 +1333,12 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (pc *persi
// set request canceler to some non-nil function so we
// can detect whether it was cleared between now and when
// we enter roundTrip
- t.setReqCanceler(req, func(error) {})
+ t.setReqCanceler(treq.cancelKey, func(error) {})
return pc, nil
}
cancelc := make(chan error, 1)
- t.setReqCanceler(req, func(err error) { cancelc <- err })
+ t.setReqCanceler(treq.cancelKey, func(err error) { cancelc <- err })
// Queue for permission to dial.
t.queueForDial(w)
@@ -2078,7 +2087,7 @@ func (pc *persistConn) readLoop() {
}
if !hasBody || bodyWritable {
- pc.t.setReqCanceler(rc.req, nil)
+ pc.t.setReqCanceler(rc.cancelKey, nil)
// Put the idle conn back into the pool before we send the response
// so if they process it quickly and make another request, they'll
@@ -2151,7 +2160,7 @@ func (pc *persistConn) readLoop() {
// reading the response body. (or for cancellation or death)
select {
case bodyEOF := <-waitForBodyRead:
- pc.t.setReqCanceler(rc.req, nil) // before pc might return to idle pool
+ pc.t.setReqCanceler(rc.cancelKey, nil) // before pc might return to idle pool
alive = alive &&
bodyEOF &&
!pc.sawEOF &&
@@ -2165,7 +2174,7 @@ func (pc *persistConn) readLoop() {
pc.t.CancelRequest(rc.req)
case <-rc.req.Context().Done():
alive = false
- pc.t.cancelRequest(rc.req, rc.req.Context().Err())
+ pc.t.cancelRequest(rc.cancelKey, rc.req.Context().Err())
case <-pc.closech:
alive = false
}
@@ -2408,9 +2417,10 @@ type responseAndError struct {
}
type requestAndChan struct {
- _ incomparable
- req *Request
- ch chan responseAndError // unbuffered; always send in select on callerGone
+ _ incomparable
+ req *Request
+ cancelKey cancelKey
+ ch chan responseAndError // unbuffered; always send in select on callerGone
// whether the Transport (as opposed to the user client code)
// added the Accept-Encoding gzip header. If the Transport
@@ -2472,7 +2482,7 @@ var (
func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) {
testHookEnterRoundTrip()
- if !pc.t.replaceReqCanceler(req.Request, pc.cancelRequest) {
+ if !pc.t.replaceReqCanceler(req.cancelKey, pc.cancelRequest) {
pc.t.putOrCloseIdleConn(pc)
return nil, errRequestCanceled
}
@@ -2524,7 +2534,7 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
defer func() {
if err != nil {
- pc.t.setReqCanceler(req.Request, nil)
+ pc.t.setReqCanceler(req.cancelKey, nil)
}
}()
@@ -2540,6 +2550,7 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
resc := make(chan responseAndError)
pc.reqch <- requestAndChan{
req: req.Request,
+ cancelKey: req.cancelKey,
ch: resc,
addedGzip: requestedGzip,
continueCh: continueCh,
@@ -2591,10 +2602,10 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
}
return re.res, nil
case <-cancelChan:
- pc.t.CancelRequest(req.Request)
+ pc.t.cancelRequest(req.cancelKey, errRequestCanceled)
cancelChan = nil
case <-ctxDoneChan:
- pc.t.cancelRequest(req.Request, req.Context().Err())
+ pc.t.cancelRequest(req.cancelKey, req.Context().Err())
cancelChan = nil
ctxDoneChan = nil
}
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index 31a41f5351..0a47687d9a 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -2364,6 +2364,50 @@ func TestTransportCancelRequest(t *testing.T) {
}
}
+func testTransportCancelRequestInDo(t *testing.T, body io.Reader) {
+ setParallel(t)
+ defer afterTest(t)
+ if testing.Short() {
+ t.Skip("skipping test in -short mode")
+ }
+ unblockc := make(chan bool)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ <-unblockc
+ }))
+ defer ts.Close()
+ defer close(unblockc)
+
+ c := ts.Client()
+ tr := c.Transport.(*Transport)
+
+ donec := make(chan bool)
+ req, _ := NewRequest("GET", ts.URL, body)
+ go func() {
+ defer close(donec)
+ c.Do(req)
+ }()
+ start := time.Now()
+ timeout := 10 * time.Second
+ for time.Since(start) < timeout {
+ time.Sleep(100 * time.Millisecond)
+ tr.CancelRequest(req)
+ select {
+ case <-donec:
+ return
+ default:
+ }
+ }
+ t.Errorf("Do of canceled request has not returned after %v", timeout)
+}
+
+func TestTransportCancelRequestInDo(t *testing.T) {
+ testTransportCancelRequestInDo(t, nil)
+}
+
+func TestTransportCancelRequestWithBodyInDo(t *testing.T) {
+ testTransportCancelRequestInDo(t, bytes.NewBuffer([]byte{0}))
+}
+
func TestTransportCancelRequestInDial(t *testing.T) {
defer afterTest(t)
if testing.Short() {
diff --git a/src/net/ipsock_plan9.go b/src/net/ipsock_plan9.go
index 99d3e3971a..23082366aa 100644
--- a/src/net/ipsock_plan9.go
+++ b/src/net/ipsock_plan9.go
@@ -206,9 +206,9 @@ func dialPlan9Blocking(ctx context.Context, net string, laddr, raddr Addr) (fd *
return nil, err
}
if la := plan9LocalAddr(laddr); la == "" {
- _, err = f.WriteString("connect " + dest)
+ err = hangupCtlWrite(ctx, proto, f, "connect "+dest)
} else {
- _, err = f.WriteString("connect " + dest + " " + la)
+ err = hangupCtlWrite(ctx, proto, f, "connect "+dest+" "+la)
}
if err != nil {
f.Close()
@@ -339,3 +339,27 @@ func plan9LocalAddr(addr Addr) string {
}
return ip.String() + "!" + itoa(port)
}
+
+func hangupCtlWrite(ctx context.Context, proto string, ctl *os.File, msg string) error {
+ if proto != "tcp" {
+ _, err := ctl.WriteString(msg)
+ return err
+ }
+ written := make(chan struct{})
+ errc := make(chan error)
+ go func() {
+ select {
+ case <-ctx.Done():
+ ctl.WriteString("hangup")
+ errc <- mapErr(ctx.Err())
+ case <-written:
+ errc <- nil
+ }
+ }()
+ _, err := ctl.WriteString(msg)
+ close(written)
+ if e := <-errc; err == nil && e != nil { // we hung up
+ return e
+ }
+ return err
+}
diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go
index d26e981ae4..a00fd2395f 100644
--- a/src/net/textproto/reader.go
+++ b/src/net/textproto/reader.go
@@ -88,7 +88,7 @@ func (r *Reader) readLineSlice() ([]byte, error) {
// The first call to ReadContinuedLine will return "Line 1 continued..."
// and the second will return "Line 2".
//
-// A line consisting of only white space is never continued.
+// Empty lines are never continued.
//
func (r *Reader) ReadContinuedLine() (string, error) {
line, err := r.readContinuedLineSlice(noValidation)
diff --git a/src/os/pipe_test.go b/src/os/pipe_test.go
index 2e93e3946a..429bd813c2 100644
--- a/src/os/pipe_test.go
+++ b/src/os/pipe_test.go
@@ -104,6 +104,25 @@ func TestStdPipe(t *testing.T) {
}
}
}
+
+ // Test redirecting stdout but not stderr. Issue 40076.
+ cmd := osexec.Command(os.Args[0], "-test.run", "TestStdPipeHelper")
+ cmd.Stdout = w
+ var stderr bytes.Buffer
+ cmd.Stderr = &stderr
+ cmd.Env = append(os.Environ(), "GO_TEST_STD_PIPE_HELPER=1")
+ if err := cmd.Run(); err == nil {
+ t.Errorf("unexpected success of write to closed stdout")
+ } else if ee, ok := err.(*osexec.ExitError); !ok {
+ t.Errorf("unexpected exec error type %T: %v", err, err)
+ } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
+ t.Errorf("unexpected wait status type %T: %v", ee.Sys(), ee.Sys())
+ } else if !ws.Signaled() || ws.Signal() != syscall.SIGPIPE {
+ t.Errorf("unexpected exit status %v for write to closed stdout", err)
+ }
+ if output := stderr.Bytes(); len(output) > 0 {
+ t.Errorf("unexpected output on stderr: %s", output)
+ }
}
// This is a helper for TestStdPipe. It's not a test in itself.
diff --git a/src/os/readfrom_linux.go b/src/os/readfrom_linux.go
index ed275e1ba6..63ea45cf65 100644
--- a/src/os/readfrom_linux.go
+++ b/src/os/readfrom_linux.go
@@ -32,6 +32,11 @@ func (f *File) readFrom(r io.Reader) (written int64, handled bool, err error) {
if !ok {
return 0, false, nil
}
+ if src.checkValid("ReadFrom") != nil {
+ // Avoid returning the error as we report handled as false,
+ // leave further error handling as the responsibility of the caller.
+ return 0, false, nil
+ }
written, handled, err = pollCopyFileRange(&f.pfd, &src.pfd, remain)
if lr != nil {
diff --git a/src/os/readfrom_linux_test.go b/src/os/readfrom_linux_test.go
index b6f5cb7034..00faf39fe5 100644
--- a/src/os/readfrom_linux_test.go
+++ b/src/os/readfrom_linux_test.go
@@ -8,6 +8,7 @@ import (
"bytes"
"internal/poll"
"io"
+ "io/ioutil"
"math/rand"
. "os"
"path/filepath"
@@ -170,6 +171,35 @@ func TestCopyFileRange(t *testing.T) {
mustContainData(t, dst, data)
})
})
+ t.Run("Nil", func(t *testing.T) {
+ var nilFile *File
+ anyFile, err := ioutil.TempFile("", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer Remove(anyFile.Name())
+ defer anyFile.Close()
+
+ if _, err := io.Copy(nilFile, nilFile); err != ErrInvalid {
+ t.Errorf("io.Copy(nilFile, nilFile) = %v, want %v", err, ErrInvalid)
+ }
+ if _, err := io.Copy(anyFile, nilFile); err != ErrInvalid {
+ t.Errorf("io.Copy(anyFile, nilFile) = %v, want %v", err, ErrInvalid)
+ }
+ if _, err := io.Copy(nilFile, anyFile); err != ErrInvalid {
+ t.Errorf("io.Copy(nilFile, anyFile) = %v, want %v", err, ErrInvalid)
+ }
+
+ if _, err := nilFile.ReadFrom(nilFile); err != ErrInvalid {
+ t.Errorf("nilFile.ReadFrom(nilFile) = %v, want %v", err, ErrInvalid)
+ }
+ if _, err := anyFile.ReadFrom(nilFile); err != ErrInvalid {
+ t.Errorf("anyFile.ReadFrom(nilFile) = %v, want %v", err, ErrInvalid)
+ }
+ if _, err := nilFile.ReadFrom(anyFile); err != ErrInvalid {
+ t.Errorf("nilFile.ReadFrom(anyFile) = %v, want %v", err, ErrInvalid)
+ }
+ })
}
func testCopyFileRange(t *testing.T, size int64, limit int64) {
diff --git a/src/runtime/lockrank_off.go b/src/runtime/lockrank_off.go
index 891589c0f2..425ca8dd93 100644
--- a/src/runtime/lockrank_off.go
+++ b/src/runtime/lockrank_off.go
@@ -1,3 +1,7 @@
+// Copyright 2020 The Go 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 !goexperiment.staticlockranking
package runtime
diff --git a/src/runtime/lockrank_on.go b/src/runtime/lockrank_on.go
index cf4151ff46..fbc5ff58b7 100644
--- a/src/runtime/lockrank_on.go
+++ b/src/runtime/lockrank_on.go
@@ -1,3 +1,7 @@
+// Copyright 2020 The Go 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 goexperiment.staticlockranking
package runtime
diff --git a/src/runtime/mpagealloc.go b/src/runtime/mpagealloc.go
index 60f7f9ff58..8b3c62c375 100644
--- a/src/runtime/mpagealloc.go
+++ b/src/runtime/mpagealloc.go
@@ -233,16 +233,12 @@ type pageAlloc struct {
// The address to start an allocation search with. It must never
// point to any memory that is not contained in inUse, i.e.
- // inUse.contains(searchAddr) must always be true.
+ // inUse.contains(searchAddr.addr()) must always be true. The one
+ // exception to this rule is that it may take on the value of
+ // maxOffAddr to indicate that the heap is exhausted.
//
- // When added with arenaBaseOffset, we guarantee that
- // all valid heap addresses (when also added with
- // arenaBaseOffset) below this value are allocated and
- // not worth searching.
- //
- // Note that adding in arenaBaseOffset transforms addresses
- // to a new address space with a linear view of the full address
- // space on architectures with segmented address spaces.
+ // We guarantee that all valid heap addresses below this value
+ // are allocated and not worth searching.
searchAddr offAddr
// start and end represent the chunk indices
@@ -518,6 +514,30 @@ func (s *pageAlloc) allocRange(base, npages uintptr) uintptr {
return uintptr(scav) * pageSize
}
+// findMappedAddr returns the smallest mapped offAddr that is
+// >= addr. That is, if addr refers to mapped memory, then it is
+// returned. If addr is higher than any mapped region, then
+// it returns maxOffAddr.
+//
+// s.mheapLock must be held.
+func (s *pageAlloc) findMappedAddr(addr offAddr) offAddr {
+ // If we're not in a test, validate first by checking mheap_.arenas.
+ // This is a fast path which is only safe to use outside of testing.
+ ai := arenaIndex(addr.addr())
+ if s.test || mheap_.arenas[ai.l1()] == nil || mheap_.arenas[ai.l1()][ai.l2()] == nil {
+ vAddr, ok := s.inUse.findAddrGreaterEqual(addr.addr())
+ if ok {
+ return offAddr{vAddr}
+ } else {
+ // The candidate search address is greater than any
+ // known address, which means we definitely have no
+ // free memory left.
+ return maxOffAddr
+ }
+ }
+ return addr
+}
+
// find searches for the first (address-ordered) contiguous free region of
// npages in size and returns a base address for that region.
//
@@ -526,6 +546,7 @@ func (s *pageAlloc) allocRange(base, npages uintptr) uintptr {
//
// find also computes and returns a candidate s.searchAddr, which may or
// may not prune more of the address space than s.searchAddr already does.
+// This candidate is always a valid s.searchAddr.
//
// find represents the slow path and the full radix tree search.
//
@@ -695,7 +716,7 @@ nextLevel:
// We found a sufficiently large run of free pages straddling
// some boundary, so compute the address and return it.
addr := levelIndexToOffAddr(l, i).add(uintptr(base) * pageSize).addr()
- return addr, firstFree.base
+ return addr, s.findMappedAddr(firstFree.base)
}
if l == 0 {
// We're at level zero, so that means we've exhausted our search.
@@ -741,7 +762,7 @@ nextLevel:
// found an even narrower free window.
searchAddr := chunkBase(ci) + uintptr(searchIdx)*pageSize
foundFree(offAddr{searchAddr}, chunkBase(ci+1)-searchAddr)
- return addr, firstFree.base
+ return addr, s.findMappedAddr(firstFree.base)
}
// alloc allocates npages worth of memory from the page heap, returning the base
diff --git a/src/runtime/mpagealloc_test.go b/src/runtime/mpagealloc_test.go
index 89a4a2502c..65ba71d459 100644
--- a/src/runtime/mpagealloc_test.go
+++ b/src/runtime/mpagealloc_test.go
@@ -612,6 +612,63 @@ func TestPageAllocAlloc(t *testing.T) {
baseChunkIdx + chunkIdxBigJump: {{0, PallocChunkPages}},
},
}
+
+ // Test to check for issue #40191. Essentially, the candidate searchAddr
+ // discovered by find may not point to mapped memory, so we need to handle
+ // that explicitly.
+ //
+ // chunkIdxSmallOffset is an offset intended to be used within chunkIdxBigJump.
+ // It is far enough within chunkIdxBigJump that the summaries at the beginning
+ // of an address range the size of chunkIdxBigJump will not be mapped in.
+ const chunkIdxSmallOffset = 0x503
+ tests["DiscontiguousBadSearchAddr"] = test{
+ before: map[ChunkIdx][]BitRange{
+ // The mechanism for the bug involves three chunks, A, B, and C, which are
+ // far apart in the address space. In particular, B is chunkIdxBigJump +
+ // chunkIdxSmalloffset chunks away from B, and C is 2*chunkIdxBigJump chunks
+ // away from A. A has 1 page free, B has several (NOT at the end of B), and
+ // C is totally free.
+ // Note that B's free memory must not be at the end of B because the fast
+ // path in the page allocator will check if the searchAddr even gives us
+ // enough space to place the allocation in a chunk before accessing the
+ // summary.
+ BaseChunkIdx + chunkIdxBigJump*0: {{0, PallocChunkPages - 1}},
+ BaseChunkIdx + chunkIdxBigJump*1 + chunkIdxSmallOffset: {
+ {0, PallocChunkPages - 10},
+ {PallocChunkPages - 1, 1},
+ },
+ BaseChunkIdx + chunkIdxBigJump*2: {},
+ },
+ scav: map[ChunkIdx][]BitRange{
+ BaseChunkIdx + chunkIdxBigJump*0: {},
+ BaseChunkIdx + chunkIdxBigJump*1 + chunkIdxSmallOffset: {},
+ BaseChunkIdx + chunkIdxBigJump*2: {},
+ },
+ hits: []hit{
+ // We first allocate into A to set the page allocator's searchAddr to the
+ // end of that chunk. That is the only purpose A serves.
+ {1, PageBase(BaseChunkIdx, PallocChunkPages-1), 0},
+ // Then, we make a big allocation that doesn't fit into B, and so must be
+ // fulfilled by C.
+ //
+ // On the way to fulfilling the allocation into C, we estimate searchAddr
+ // using the summary structure, but that will give us a searchAddr of
+ // B's base address minus chunkIdxSmallOffset chunks. These chunks will
+ // not be mapped.
+ {100, PageBase(baseChunkIdx+chunkIdxBigJump*2, 0), 0},
+ // Now we try to make a smaller allocation that can be fulfilled by B.
+ // In an older implementation of the page allocator, this will segfault,
+ // because this last allocation will first try to access the summary
+ // for B's base address minus chunkIdxSmallOffset chunks in the fast path,
+ // and this will not be mapped.
+ {9, PageBase(baseChunkIdx+chunkIdxBigJump*1+chunkIdxSmallOffset, PallocChunkPages-10), 0},
+ },
+ after: map[ChunkIdx][]BitRange{
+ BaseChunkIdx + chunkIdxBigJump*0: {{0, PallocChunkPages}},
+ BaseChunkIdx + chunkIdxBigJump*1 + chunkIdxSmallOffset: {{0, PallocChunkPages}},
+ BaseChunkIdx + chunkIdxBigJump*2: {{0, 100}},
+ },
+ }
}
for name, v := range tests {
v := v
diff --git a/src/runtime/mranges.go b/src/runtime/mranges.go
index e23d0778eb..2c0eb2c2dd 100644
--- a/src/runtime/mranges.go
+++ b/src/runtime/mranges.go
@@ -188,6 +188,25 @@ func (a *addrRanges) findSucc(addr uintptr) int {
return len(a.ranges)
}
+// findAddrGreaterEqual returns the smallest address represented by a
+// that is >= addr. Thus, if the address is represented by a,
+// then it returns addr. The second return value indicates whether
+// such an address exists for addr in a. That is, if addr is larger than
+// any address known to a, the second return value will be false.
+func (a *addrRanges) findAddrGreaterEqual(addr uintptr) (uintptr, bool) {
+ i := a.findSucc(addr)
+ if i == 0 {
+ return a.ranges[0].base.addr(), true
+ }
+ if a.ranges[i-1].contains(addr) {
+ return addr, true
+ }
+ if i < len(a.ranges) {
+ return a.ranges[i].base.addr(), true
+ }
+ return 0, false
+}
+
// contains returns true if a covers the address addr.
func (a *addrRanges) contains(addr uintptr) bool {
i := a.findSucc(addr)
diff --git a/src/runtime/os_linux_x86.go b/src/runtime/os_linux_x86.go
index d001e6ee59..97f870707d 100644
--- a/src/runtime/os_linux_x86.go
+++ b/src/runtime/os_linux_x86.go
@@ -7,7 +7,10 @@
package runtime
-import "runtime/internal/atomic"
+import (
+ "runtime/internal/atomic"
+ "unsafe"
+)
//go:noescape
func uname(utsname *new_utsname) int
@@ -55,6 +58,36 @@ func osArchInit() {
return
}
+ if major == 5 && minor == 4 && patch < 2 {
+ // All 5.4 versions of Ubuntu are patched.
+ procVersion := []byte("/proc/version\000")
+ f := open(&procVersion[0], _O_RDONLY, 0)
+ if f >= 0 {
+ var buf [512]byte
+ p := noescape(unsafe.Pointer(&buf[0]))
+ n := read(f, p, int32(len(buf)))
+ closefd(f)
+
+ needle := []byte("Ubuntu")
+ contains:
+ for i, c := range buf[:n] {
+ if c != needle[0] {
+ continue
+ }
+ if int(n)-i < len(needle) {
+ break
+ }
+ for j, c2 := range needle {
+ if c2 != buf[i+j] {
+ continue contains
+ }
+ }
+ // This is an Ubuntu system.
+ return
+ }
+ }
+ }
+
if major == 5 && (minor == 2 || minor == 3 && patch < 15 || minor == 4 && patch < 2) {
gsignalInitQuirk = mlockGsignal
if m0.gsignal != nil {
diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go
index b486b83688..cd3565df5b 100644
--- a/src/runtime/os_openbsd.go
+++ b/src/runtime/os_openbsd.go
@@ -340,6 +340,7 @@ func osStackRemap(s *mspan, flags int32) {
}
}
+//go:nosplit
func raise(sig uint32) {
thrkill(getthrid(), int(sig))
}
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 2399f0a1d3..035822216d 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -132,7 +132,7 @@ func main() {
if GOARCH != "wasm" { // no threads on wasm yet, so no sysmon
systemstack(func() {
- newm(sysmon, nil)
+ newm(sysmon, nil, -1)
})
}
@@ -562,7 +562,7 @@ func schedinit() {
stackinit()
mallocinit()
fastrandinit() // must run before mcommoninit
- mcommoninit(_g_.m)
+ mcommoninit(_g_.m, -1)
cpuinit() // must run before alginit
alginit() // maps must not be used before this call
modulesinit() // provides activeModules
@@ -623,7 +623,22 @@ func checkmcount() {
}
}
-func mcommoninit(mp *m) {
+// mReserveID returns the next ID to use for a new m. This new m is immediately
+// considered 'running' by checkdead.
+//
+// sched.lock must be held.
+func mReserveID() int64 {
+ if sched.mnext+1 < sched.mnext {
+ throw("runtime: thread ID overflow")
+ }
+ id := sched.mnext
+ sched.mnext++
+ checkmcount()
+ return id
+}
+
+// Pre-allocated ID may be passed as 'id', or omitted by passing -1.
+func mcommoninit(mp *m, id int64) {
_g_ := getg()
// g0 stack won't make sense for user (and is not necessary unwindable).
@@ -632,12 +647,12 @@ func mcommoninit(mp *m) {
}
lock(&sched.lock)
- if sched.mnext+1 < sched.mnext {
- throw("runtime: thread ID overflow")
+
+ if id >= 0 {
+ mp.id = id
+ } else {
+ mp.id = mReserveID()
}
- mp.id = sched.mnext
- sched.mnext++
- checkmcount()
mp.fastrand[0] = uint32(int64Hash(uint64(mp.id), fastrandseed))
mp.fastrand[1] = uint32(int64Hash(uint64(cputicks()), ^fastrandseed))
@@ -1068,7 +1083,7 @@ func startTheWorldWithSema(emitTraceEvent bool) int64 {
notewakeup(&mp.park)
} else {
// Start M to run P. Do not start another M below.
- newm(nil, p)
+ newm(nil, p, -1)
}
}
@@ -1413,12 +1428,13 @@ type cgothreadstart struct {
// Allocate a new m unassociated with any thread.
// Can use p for allocation context if needed.
// fn is recorded as the new m's m.mstartfn.
+// id is optional pre-allocated m ID. Omit by passing -1.
//
// This function is allowed to have write barriers even if the caller
// isn't because it borrows _p_.
//
//go:yeswritebarrierrec
-func allocm(_p_ *p, fn func()) *m {
+func allocm(_p_ *p, fn func(), id int64) *m {
_g_ := getg()
acquirem() // disable GC because it can be called from sysmon
if _g_.m.p == 0 {
@@ -1447,7 +1463,7 @@ func allocm(_p_ *p, fn func()) *m {
mp := new(m)
mp.mstartfn = fn
- mcommoninit(mp)
+ mcommoninit(mp, id)
// In case of cgo or Solaris or illumos or Darwin, pthread_create will make us a stack.
// Windows and Plan 9 will layout sched stack on OS stack.
@@ -1586,7 +1602,7 @@ func oneNewExtraM() {
// The sched.pc will never be returned to, but setting it to
// goexit makes clear to the traceback routines where
// the goroutine stack ends.
- mp := allocm(nil, nil)
+ mp := allocm(nil, nil, -1)
gp := malg(4096)
gp.sched.pc = funcPC(goexit) + sys.PCQuantum
gp.sched.sp = gp.stack.hi
@@ -1757,9 +1773,11 @@ var newmHandoff struct {
// Create a new m. It will start off with a call to fn, or else the scheduler.
// fn needs to be static and not a heap allocated closure.
// May run with m.p==nil, so write barriers are not allowed.
+//
+// id is optional pre-allocated m ID. Omit by passing -1.
//go:nowritebarrierrec
-func newm(fn func(), _p_ *p) {
- mp := allocm(_p_, fn)
+func newm(fn func(), _p_ *p, id int64) {
+ mp := allocm(_p_, fn, id)
mp.nextp.set(_p_)
mp.sigmask = initSigmask
if gp := getg(); gp != nil && gp.m != nil && (gp.m.lockedExt != 0 || gp.m.incgo) && GOOS != "plan9" {
@@ -1828,7 +1846,7 @@ func startTemplateThread() {
releasem(mp)
return
}
- newm(templateThread, nil)
+ newm(templateThread, nil, -1)
releasem(mp)
}
@@ -1923,16 +1941,31 @@ func startm(_p_ *p, spinning bool) {
}
}
mp := mget()
- unlock(&sched.lock)
if mp == nil {
+ // No M is available, we must drop sched.lock and call newm.
+ // However, we already own a P to assign to the M.
+ //
+ // Once sched.lock is released, another G (e.g., in a syscall),
+ // could find no idle P while checkdead finds a runnable G but
+ // no running M's because this new M hasn't started yet, thus
+ // throwing in an apparent deadlock.
+ //
+ // Avoid this situation by pre-allocating the ID for the new M,
+ // thus marking it as 'running' before we drop sched.lock. This
+ // new M will eventually run the scheduler to execute any
+ // queued G's.
+ id := mReserveID()
+ unlock(&sched.lock)
+
var fn func()
if spinning {
// The caller incremented nmspinning, so set m.spinning in the new M.
fn = mspinning
}
- newm(fn, _p_)
+ newm(fn, _p_, id)
return
}
+ unlock(&sched.lock)
if mp.spinning {
throw("startm: m is spinning")
}
@@ -5192,7 +5225,9 @@ func runqputbatch(pp *p, q *gQueue, qsize int) {
atomic.StoreRel(&pp.runqtail, t)
if !q.empty() {
+ lock(&sched.lock)
globrunqputbatch(q, int32(qsize))
+ unlock(&sched.lock)
}
}
diff --git a/src/runtime/race/README b/src/runtime/race/README
index 65378c8ca6..34485f0fb2 100644
--- a/src/runtime/race/README
+++ b/src/runtime/race/README
@@ -6,8 +6,8 @@ To update the .syso files use golang.org/x/build/cmd/racebuild.
race_darwin_amd64.syso built with LLVM 3496d6e4bea9cb99cb382939b7e79a50a3b863a5 and Go 553e003414d3aa90cc39830ee22f08453d9f3408.
race_freebsd_amd64.syso built with LLVM 3496d6e4bea9cb99cb382939b7e79a50a3b863a5 and Go 553e003414d3aa90cc39830ee22f08453d9f3408.
-race_linux_amd64.syso built with LLVM 3496d6e4bea9cb99cb382939b7e79a50a3b863a5 and Go 553e003414d3aa90cc39830ee22f08453d9f3408.
-race_linux_ppc64le.syso built with LLVM 3496d6e4bea9cb99cb382939b7e79a50a3b863a5 and Go 553e003414d3aa90cc39830ee22f08453d9f3408.
+race_linux_amd64.syso built with LLVM 6c75db8b4bc59eace18143ce086419d37da24746 and Go 7388956b76ce15a11346cebefcf6193db044caaf.
+race_linux_ppc64le.syso built with LLVM 6c75db8b4bc59eace18143ce086419d37da24746 and Go 7388956b76ce15a11346cebefcf6193db044caaf.
race_netbsd_amd64.syso built with LLVM 3496d6e4bea9cb99cb382939b7e79a50a3b863a5 and Go 553e003414d3aa90cc39830ee22f08453d9f3408.
race_windows_amd64.syso built with LLVM 3496d6e4bea9cb99cb382939b7e79a50a3b863a5 and Go 553e003414d3aa90cc39830ee22f08453d9f3408.
-race_linux_arm64.syso built with LLVM 3496d6e4bea9cb99cb382939b7e79a50a3b863a5 and Go 553e003414d3aa90cc39830ee22f08453d9f3408.
+race_linux_arm64.syso built with LLVM 6c75db8b4bc59eace18143ce086419d37da24746 and Go 7388956b76ce15a11346cebefcf6193db044caaf.
diff --git a/src/runtime/race/race_linux_amd64.syso b/src/runtime/race/race_linux_amd64.syso
index 255b2e5c08..d31f85df56 100644
--- a/src/runtime/race/race_linux_amd64.syso
+++ b/src/runtime/race/race_linux_amd64.syso
Binary files differ
diff --git a/src/runtime/race/race_linux_arm64.syso b/src/runtime/race/race_linux_arm64.syso
index f15c5995e6..7c74171b0f 100644
--- a/src/runtime/race/race_linux_arm64.syso
+++ b/src/runtime/race/race_linux_arm64.syso
Binary files differ
diff --git a/src/runtime/race/race_linux_ppc64le.syso b/src/runtime/race/race_linux_ppc64le.syso
index 2bf5029659..a3c72bec55 100644
--- a/src/runtime/race/race_linux_ppc64le.syso
+++ b/src/runtime/race/race_linux_ppc64le.syso
Binary files differ
diff --git a/src/runtime/race/syso_test.go b/src/runtime/race/syso_test.go
new file mode 100644
index 0000000000..db846c5d2a
--- /dev/null
+++ b/src/runtime/race/syso_test.go
@@ -0,0 +1,39 @@
+// Copyright 2020 The Go 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 !android,!js,!ppc64le
+
+// Note: we don't run on Android or ppc64 because if there is any non-race test
+// file in this package, the OS tries to link the .syso file into the
+// test (even when we're not in race mode), which fails. I'm not sure
+// why, but easiest to just punt - as long as a single builder runs
+// this test, we're good.
+
+package race
+
+import (
+ "bytes"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "testing"
+)
+
+func TestIssue37485(t *testing.T) {
+ files, err := filepath.Glob("./*.syso")
+ if err != nil {
+ t.Fatalf("can't find syso files: %s", err)
+ }
+ for _, f := range files {
+ cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), "tool", "nm", f)
+ res, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("nm of %s failed: %s", f, err)
+ continue
+ }
+ if bytes.Contains(res, []byte("getauxval")) {
+ t.Errorf("%s contains getauxval", f)
+ }
+ }
+}
diff --git a/src/runtime/sys_darwin.go b/src/runtime/sys_darwin.go
index 28c500a710..06474434c9 100644
--- a/src/runtime/sys_darwin.go
+++ b/src/runtime/sys_darwin.go
@@ -129,7 +129,7 @@ func syscall_rawSyscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintpt
// syscallNoErr is used in crypto/x509 to call into Security.framework and CF.
-//go:linkname crypto_x509_syscall crypto/x509/internal/macOS.syscall
+//go:linkname crypto_x509_syscall crypto/x509/internal/macos.syscall
//go:nosplit
//go:cgo_unsafe_args
func crypto_x509_syscall(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1 uintptr) {
diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s
index 1b28098ad9..5b9b638ad7 100644
--- a/src/runtime/sys_linux_386.s
+++ b/src/runtime/sys_linux_386.s
@@ -222,7 +222,7 @@ TEXT runtime·mincore(SB),NOSPLIT,$0-16
RET
// func walltime1() (sec int64, nsec int32)
-TEXT runtime·walltime1(SB), NOSPLIT, $0-12
+TEXT runtime·walltime1(SB), NOSPLIT, $8-12
// We don't know how much stack space the VDSO code will need,
// so switch to g0.
@@ -233,6 +233,13 @@ TEXT runtime·walltime1(SB), NOSPLIT, $0-12
MOVL g_m(AX), SI // SI unchanged by C code.
// Set vdsoPC and vdsoSP for SIGPROF traceback.
+ // Save the old values on stack and restore them on exit,
+ // so this function is reentrant.
+ MOVL m_vdsoPC(SI), CX
+ MOVL m_vdsoSP(SI), DX
+ MOVL CX, 0(SP)
+ MOVL DX, 4(SP)
+
LEAL sec+0(FP), DX
MOVL -4(DX), CX
MOVL CX, m_vdsoPC(SI)
@@ -276,7 +283,15 @@ finish:
MOVL 12(SP), BX // nsec
MOVL BP, SP // Restore real SP
- MOVL $0, m_vdsoSP(SI)
+ // Restore vdsoPC, vdsoSP
+ // We don't worry about being signaled between the two stores.
+ // If we are not in a signal handler, we'll restore vdsoSP to 0,
+ // and no one will care about vdsoPC. If we are in a signal handler,
+ // we cannot receive another signal.
+ MOVL 4(SP), CX
+ MOVL CX, m_vdsoSP(SI)
+ MOVL 0(SP), CX
+ MOVL CX, m_vdsoPC(SI)
// sec is in AX, nsec in BX
MOVL AX, sec_lo+0(FP)
@@ -286,7 +301,7 @@ finish:
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
-TEXT runtime·nanotime1(SB), NOSPLIT, $0-8
+TEXT runtime·nanotime1(SB), NOSPLIT, $8-8
// Switch to g0 stack. See comment above in runtime·walltime.
MOVL SP, BP // Save old SP; BP unchanged by C code.
@@ -296,6 +311,13 @@ TEXT runtime·nanotime1(SB), NOSPLIT, $0-8
MOVL g_m(AX), SI // SI unchanged by C code.
// Set vdsoPC and vdsoSP for SIGPROF traceback.
+ // Save the old values on stack and restore them on exit,
+ // so this function is reentrant.
+ MOVL m_vdsoPC(SI), CX
+ MOVL m_vdsoSP(SI), DX
+ MOVL CX, 0(SP)
+ MOVL DX, 4(SP)
+
LEAL ret+0(FP), DX
MOVL -4(DX), CX
MOVL CX, m_vdsoPC(SI)
@@ -332,7 +354,15 @@ finish:
MOVL 12(SP), BX // nsec
MOVL BP, SP // Restore real SP
- MOVL $0, m_vdsoSP(SI)
+ // Restore vdsoPC, vdsoSP
+ // We don't worry about being signaled between the two stores.
+ // If we are not in a signal handler, we'll restore vdsoSP to 0,
+ // and no one will care about vdsoPC. If we are in a signal handler,
+ // we cannot receive another signal.
+ MOVL 4(SP), CX
+ MOVL CX, m_vdsoSP(SI)
+ MOVL 0(SP), CX
+ MOVL CX, m_vdsoPC(SI)
// sec is in AX, nsec in BX
// convert to DX:AX nsec
diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s
index 58d3bc54b4..fe9c6bce85 100644
--- a/src/runtime/sys_linux_amd64.s
+++ b/src/runtime/sys_linux_amd64.s
@@ -206,7 +206,7 @@ TEXT runtime·mincore(SB),NOSPLIT,$0-28
// func walltime1() (sec int64, nsec int32)
// non-zero frame-size means bp is saved and restored
-TEXT runtime·walltime1(SB),NOSPLIT,$8-12
+TEXT runtime·walltime1(SB),NOSPLIT,$16-12
// We don't know how much stack space the VDSO code will need,
// so switch to g0.
// In particular, a kernel configured with CONFIG_OPTIMIZE_INLINING=n
@@ -221,6 +221,13 @@ TEXT runtime·walltime1(SB),NOSPLIT,$8-12
MOVQ g_m(AX), BX // BX unchanged by C code.
// Set vdsoPC and vdsoSP for SIGPROF traceback.
+ // Save the old values on stack and restore them on exit,
+ // so this function is reentrant.
+ MOVQ m_vdsoPC(BX), CX
+ MOVQ m_vdsoSP(BX), DX
+ MOVQ CX, 0(SP)
+ MOVQ DX, 8(SP)
+
LEAQ sec+0(FP), DX
MOVQ -8(DX), CX
MOVQ CX, m_vdsoPC(BX)
@@ -244,8 +251,17 @@ noswitch:
CALL AX
MOVQ 0(SP), AX // sec
MOVQ 8(SP), DX // nsec
+ret:
MOVQ BP, SP // Restore real SP
- MOVQ $0, m_vdsoSP(BX)
+ // Restore vdsoPC, vdsoSP
+ // We don't worry about being signaled between the two stores.
+ // If we are not in a signal handler, we'll restore vdsoSP to 0,
+ // and no one will care about vdsoPC. If we are in a signal handler,
+ // we cannot receive another signal.
+ MOVQ 8(SP), CX
+ MOVQ CX, m_vdsoSP(BX)
+ MOVQ 0(SP), CX
+ MOVQ CX, m_vdsoPC(BX)
MOVQ AX, sec+0(FP)
MOVL DX, nsec+8(FP)
RET
@@ -257,15 +273,10 @@ fallback:
MOVQ 0(SP), AX // sec
MOVL 8(SP), DX // usec
IMULQ $1000, DX
- MOVQ BP, SP // Restore real SP
- MOVQ $0, m_vdsoSP(BX)
- MOVQ AX, sec+0(FP)
- MOVL DX, nsec+8(FP)
- RET
+ JMP ret
// func nanotime1() int64
-// non-zero frame-size means bp is saved and restored
-TEXT runtime·nanotime1(SB),NOSPLIT,$8-8
+TEXT runtime·nanotime1(SB),NOSPLIT,$16-8
// Switch to g0 stack. See comment above in runtime·walltime.
MOVQ SP, BP // Save old SP; BP unchanged by C code.
@@ -275,6 +286,13 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$8-8
MOVQ g_m(AX), BX // BX unchanged by C code.
// Set vdsoPC and vdsoSP for SIGPROF traceback.
+ // Save the old values on stack and restore them on exit,
+ // so this function is reentrant.
+ MOVQ m_vdsoPC(BX), CX
+ MOVQ m_vdsoSP(BX), DX
+ MOVQ CX, 0(SP)
+ MOVQ DX, 8(SP)
+
LEAQ ret+0(FP), DX
MOVQ -8(DX), CX
MOVQ CX, m_vdsoPC(BX)
@@ -298,8 +316,17 @@ noswitch:
CALL AX
MOVQ 0(SP), AX // sec
MOVQ 8(SP), DX // nsec
+ret:
MOVQ BP, SP // Restore real SP
- MOVQ $0, m_vdsoSP(BX)
+ // Restore vdsoPC, vdsoSP
+ // We don't worry about being signaled between the two stores.
+ // If we are not in a signal handler, we'll restore vdsoSP to 0,
+ // and no one will care about vdsoPC. If we are in a signal handler,
+ // we cannot receive another signal.
+ MOVQ 8(SP), CX
+ MOVQ CX, m_vdsoSP(BX)
+ MOVQ 0(SP), CX
+ MOVQ CX, m_vdsoPC(BX)
// sec is in AX, nsec in DX
// return nsec in AX
IMULQ $1000000000, AX
@@ -313,15 +340,8 @@ fallback:
CALL AX
MOVQ 0(SP), AX // sec
MOVL 8(SP), DX // usec
- MOVQ BP, SP // Restore real SP
- MOVQ $0, m_vdsoSP(BX)
IMULQ $1000, DX
- // sec is in AX, nsec in DX
- // return nsec in AX
- IMULQ $1000000000, AX
- ADDQ DX, AX
- MOVQ AX, ret+0(FP)
- RET
+ JMP ret
TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0-28
MOVL how+0(FP), DI
diff --git a/src/runtime/sys_linux_arm.s b/src/runtime/sys_linux_arm.s
index e103da56dc..475f52344c 100644
--- a/src/runtime/sys_linux_arm.s
+++ b/src/runtime/sys_linux_arm.s
@@ -242,7 +242,7 @@ TEXT runtime·mincore(SB),NOSPLIT,$0
MOVW R0, ret+12(FP)
RET
-TEXT runtime·walltime1(SB),NOSPLIT,$0-12
+TEXT runtime·walltime1(SB),NOSPLIT,$8-12
// We don't know how much stack space the VDSO code will need,
// so switch to g0.
@@ -252,6 +252,13 @@ TEXT runtime·walltime1(SB),NOSPLIT,$0-12
MOVW g_m(g), R5 // R5 is unchanged by C code.
// Set vdsoPC and vdsoSP for SIGPROF traceback.
+ // Save the old values on stack and restore them on exit,
+ // so this function is reentrant.
+ MOVW m_vdsoPC(R5), R1
+ MOVW m_vdsoSP(R5), R2
+ MOVW R1, 4(R13)
+ MOVW R2, 8(R13)
+
MOVW LR, m_vdsoPC(R5)
MOVW R13, m_vdsoSP(R5)
@@ -312,8 +319,15 @@ finish:
MOVW 12(R13), R2 // nsec
MOVW R4, R13 // Restore real SP
- MOVW $0, R1
+ // Restore vdsoPC, vdsoSP
+ // We don't worry about being signaled between the two stores.
+ // If we are not in a signal handler, we'll restore vdsoSP to 0,
+ // and no one will care about vdsoPC. If we are in a signal handler,
+ // we cannot receive another signal.
+ MOVW 8(R13), R1
MOVW R1, m_vdsoSP(R5)
+ MOVW 4(R13), R1
+ MOVW R1, m_vdsoPC(R5)
MOVW R0, sec_lo+0(FP)
MOVW R1, sec_hi+4(FP)
@@ -321,7 +335,7 @@ finish:
RET
// int64 nanotime1(void)
-TEXT runtime·nanotime1(SB),NOSPLIT,$0-8
+TEXT runtime·nanotime1(SB),NOSPLIT,$8-8
// Switch to g0 stack. See comment above in runtime·walltime.
// Save old SP. Use R13 instead of SP to avoid linker rewriting the offsets.
@@ -330,6 +344,13 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$0-8
MOVW g_m(g), R5 // R5 is unchanged by C code.
// Set vdsoPC and vdsoSP for SIGPROF traceback.
+ // Save the old values on stack and restore them on exit,
+ // so this function is reentrant.
+ MOVW m_vdsoPC(R5), R1
+ MOVW m_vdsoSP(R5), R2
+ MOVW R1, 4(R13)
+ MOVW R2, 8(R13)
+
MOVW LR, m_vdsoPC(R5)
MOVW R13, m_vdsoSP(R5)
@@ -390,8 +411,15 @@ finish:
MOVW 12(R13), R2 // nsec
MOVW R4, R13 // Restore real SP
- MOVW $0, R4
+ // Restore vdsoPC, vdsoSP
+ // We don't worry about being signaled between the two stores.
+ // If we are not in a signal handler, we'll restore vdsoSP to 0,
+ // and no one will care about vdsoPC. If we are in a signal handler,
+ // we cannot receive another signal.
+ MOVW 8(R13), R4
MOVW R4, m_vdsoSP(R5)
+ MOVW 4(R13), R4
+ MOVW R4, m_vdsoPC(R5)
MOVW $1000000000, R3
MULLU R0, R3, (R1, R0)
diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s
index b23e3b9a11..198a5bacef 100644
--- a/src/runtime/sys_linux_arm64.s
+++ b/src/runtime/sys_linux_arm64.s
@@ -214,6 +214,13 @@ TEXT runtime·walltime1(SB),NOSPLIT,$24-12
MOVD g_m(g), R21 // R21 = m
// Set vdsoPC and vdsoSP for SIGPROF traceback.
+ // Save the old values on stack and restore them on exit,
+ // so this function is reentrant.
+ MOVD m_vdsoPC(R21), R2
+ MOVD m_vdsoSP(R21), R3
+ MOVD R2, 8(RSP)
+ MOVD R3, 16(RSP)
+
MOVD LR, m_vdsoPC(R21)
MOVD R20, m_vdsoSP(R21)
@@ -269,7 +276,15 @@ finish:
MOVD 8(RSP), R5 // nsec
MOVD R20, RSP // restore SP
- MOVD $0, m_vdsoSP(R21) // clear vdsoSP
+ // Restore vdsoPC, vdsoSP
+ // We don't worry about being signaled between the two stores.
+ // If we are not in a signal handler, we'll restore vdsoSP to 0,
+ // and no one will care about vdsoPC. If we are in a signal handler,
+ // we cannot receive another signal.
+ MOVD 16(RSP), R1
+ MOVD R1, m_vdsoSP(R21)
+ MOVD 8(RSP), R1
+ MOVD R1, m_vdsoPC(R21)
MOVD R3, sec+0(FP)
MOVW R5, nsec+8(FP)
@@ -282,6 +297,13 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$24-8
MOVD g_m(g), R21 // R21 = m
// Set vdsoPC and vdsoSP for SIGPROF traceback.
+ // Save the old values on stack and restore them on exit,
+ // so this function is reentrant.
+ MOVD m_vdsoPC(R21), R2
+ MOVD m_vdsoSP(R21), R3
+ MOVD R2, 8(RSP)
+ MOVD R3, 16(RSP)
+
MOVD LR, m_vdsoPC(R21)
MOVD R20, m_vdsoSP(R21)
@@ -337,7 +359,15 @@ finish:
MOVD 8(RSP), R5 // nsec
MOVD R20, RSP // restore SP
- MOVD $0, m_vdsoSP(R21) // clear vdsoSP
+ // Restore vdsoPC, vdsoSP
+ // We don't worry about being signaled between the two stores.
+ // If we are not in a signal handler, we'll restore vdsoSP to 0,
+ // and no one will care about vdsoPC. If we are in a signal handler,
+ // we cannot receive another signal.
+ MOVD 16(RSP), R1
+ MOVD R1, m_vdsoSP(R21)
+ MOVD 8(RSP), R1
+ MOVD R1, m_vdsoPC(R21)
// sec is in R3, nsec in R5
// return nsec in R3
diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s
index 6668a0fd86..afad056d06 100644
--- a/src/runtime/sys_linux_mips64x.s
+++ b/src/runtime/sys_linux_mips64x.s
@@ -214,13 +214,20 @@ TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28
RET
// func walltime1() (sec int64, nsec int32)
-TEXT runtime·walltime1(SB),NOSPLIT,$16
+TEXT runtime·walltime1(SB),NOSPLIT,$16-12
MOVV R29, R16 // R16 is unchanged by C code
MOVV R29, R1
MOVV g_m(g), R17 // R17 = m
// Set vdsoPC and vdsoSP for SIGPROF traceback.
+ // Save the old values on stack and restore them on exit,
+ // so this function is reentrant.
+ MOVV m_vdsoPC(R17), R2
+ MOVV m_vdsoSP(R17), R3
+ MOVV R2, 8(R29)
+ MOVV R3, 16(R29)
+
MOVV R31, m_vdsoPC(R17)
MOVV R29, m_vdsoSP(R17)
@@ -249,7 +256,15 @@ finish:
MOVV 8(R29), R5 // nsec
MOVV R16, R29 // restore SP
- MOVV R0, m_vdsoSP(R17) // clear vdsoSP
+ // Restore vdsoPC, vdsoSP
+ // We don't worry about being signaled between the two stores.
+ // If we are not in a signal handler, we'll restore vdsoSP to 0,
+ // and no one will care about vdsoPC. If we are in a signal handler,
+ // we cannot receive another signal.
+ MOVV 16(R29), R1
+ MOVV R1, m_vdsoSP(R17)
+ MOVV 8(R29), R1
+ MOVV R1, m_vdsoPC(R17)
MOVV R3, sec+0(FP)
MOVW R5, nsec+8(FP)
@@ -260,13 +275,20 @@ fallback:
SYSCALL
JMP finish
-TEXT runtime·nanotime1(SB),NOSPLIT,$16
+TEXT runtime·nanotime1(SB),NOSPLIT,$16-8
MOVV R29, R16 // R16 is unchanged by C code
MOVV R29, R1
MOVV g_m(g), R17 // R17 = m
// Set vdsoPC and vdsoSP for SIGPROF traceback.
+ // Save the old values on stack and restore them on exit,
+ // so this function is reentrant.
+ MOVV m_vdsoPC(R17), R2
+ MOVV m_vdsoSP(R17), R3
+ MOVV R2, 8(R29)
+ MOVV R3, 16(R29)
+
MOVV R31, m_vdsoPC(R17)
MOVV R29, m_vdsoSP(R17)
@@ -295,7 +317,15 @@ finish:
MOVV 8(R29), R5 // nsec
MOVV R16, R29 // restore SP
- MOVV R0, m_vdsoSP(R17) // clear vdsoSP
+ // Restore vdsoPC, vdsoSP
+ // We don't worry about being signaled between the two stores.
+ // If we are not in a signal handler, we'll restore vdsoSP to 0,
+ // and no one will care about vdsoPC. If we are in a signal handler,
+ // we cannot receive another signal.
+ MOVV 16(R29), R1
+ MOVV R1, m_vdsoSP(R17)
+ MOVV 8(R29), R1
+ MOVV R1, m_vdsoPC(R17)
// sec is in R3, nsec in R5
// return nsec in R3
diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s
index 8629fe3233..fd69ee70a5 100644
--- a/src/runtime/sys_linux_ppc64x.s
+++ b/src/runtime/sys_linux_ppc64x.s
@@ -185,7 +185,7 @@ TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28
RET
// func walltime1() (sec int64, nsec int32)
-TEXT runtime·walltime1(SB),NOSPLIT,$16
+TEXT runtime·walltime1(SB),NOSPLIT,$16-12
MOVD R1, R15 // R15 is unchanged by C code
MOVD g_m(g), R21 // R21 = m
@@ -196,6 +196,13 @@ TEXT runtime·walltime1(SB),NOSPLIT,$16
BEQ fallback
// Set vdsoPC and vdsoSP for SIGPROF traceback.
+ // Save the old values on stack and restore them on exit,
+ // so this function is reentrant.
+ MOVD m_vdsoPC(R21), R4
+ MOVD m_vdsoSP(R21), R5
+ MOVD R4, 32(R1)
+ MOVD R5, 40(R1)
+
MOVD LR, R14
MOVD R14, m_vdsoPC(R21)
MOVD R15, m_vdsoSP(R21)
@@ -214,11 +221,20 @@ noswitch:
MOVD R1, R4
BL (CTR) // Call from VDSO
MOVD $0, R0 // Restore R0
- MOVD R0, m_vdsoSP(R21) // Clear vdsoSP
MOVD 0(R1), R3 // sec
MOVD 8(R1), R5 // nsec
MOVD R15, R1 // Restore SP
+ // Restore vdsoPC, vdsoSP
+ // We don't worry about being signaled between the two stores.
+ // If we are not in a signal handler, we'll restore vdsoSP to 0,
+ // and no one will care about vdsoPC. If we are in a signal handler,
+ // we cannot receive another signal.
+ MOVD 40(R1), R6
+ MOVD R6, m_vdsoSP(R21)
+ MOVD 32(R1), R6
+ MOVD R6, m_vdsoPC(R21)
+
finish:
MOVD R3, sec+0(FP)
MOVW R5, nsec+8(FP)
@@ -232,7 +248,7 @@ fallback:
MOVD 40(R1), R5
JMP finish
-TEXT runtime·nanotime1(SB),NOSPLIT,$16
+TEXT runtime·nanotime1(SB),NOSPLIT,$16-8
MOVD $1, R3 // CLOCK_MONOTONIC
MOVD R1, R15 // R15 is unchanged by C code
@@ -243,6 +259,13 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$16
BEQ fallback
// Set vdsoPC and vdsoSP for SIGPROF traceback.
+ // Save the old values on stack and restore them on exit,
+ // so this function is reentrant.
+ MOVD m_vdsoPC(R21), R4
+ MOVD m_vdsoSP(R21), R5
+ MOVD R4, 32(R1)
+ MOVD R5, 40(R1)
+
MOVD LR, R14 // R14 is unchanged by C code
MOVD R14, m_vdsoPC(R21)
MOVD R15, m_vdsoSP(R21)
@@ -261,11 +284,20 @@ noswitch:
MOVD R1, R4
BL (CTR) // Call from VDSO
MOVD $0, R0 // Restore R0
- MOVD $0, m_vdsoSP(R21) // Clear vdsoSP
MOVD 0(R1), R3 // sec
MOVD 8(R1), R5 // nsec
MOVD R15, R1 // Restore SP
+ // Restore vdsoPC, vdsoSP
+ // We don't worry about being signaled between the two stores.
+ // If we are not in a signal handler, we'll restore vdsoSP to 0,
+ // and no one will care about vdsoPC. If we are in a signal handler,
+ // we cannot receive another signal.
+ MOVD 40(R1), R6
+ MOVD R6, m_vdsoSP(R21)
+ MOVD 32(R1), R6
+ MOVD R6, m_vdsoPC(R21)
+
finish:
// sec is in R3, nsec in R5
// return nsec in R3
diff --git a/src/sync/runtime2.go b/src/sync/runtime2.go
index 931edad9f1..f10c4e8e0e 100644
--- a/src/sync/runtime2.go
+++ b/src/sync/runtime2.go
@@ -1,3 +1,7 @@
+// Copyright 2020 The Go 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 !goexperiment.staticlockranking
package sync
diff --git a/src/sync/runtime2_lockrank.go b/src/sync/runtime2_lockrank.go
index 5a68e901fa..aaa1c27626 100644
--- a/src/sync/runtime2_lockrank.go
+++ b/src/sync/runtime2_lockrank.go
@@ -1,3 +1,7 @@
+// Copyright 2020 The Go 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 goexperiment.staticlockranking
package sync
diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go
index 23d7343d3a..b7351cda82 100644
--- a/src/syscall/exec_linux.go
+++ b/src/syscall/exec_linux.go
@@ -465,7 +465,7 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att
}
_, _, err1 = RawSyscall(SYS_DUP3, uintptr(fd[i]), uintptr(nextfd), O_CLOEXEC)
if _SYS_dup != SYS_DUP3 && err1 == ENOSYS {
- _, _, err1 = RawSyscall(_SYS_dup, uintptr(pipe), uintptr(nextfd), 0)
+ _, _, err1 = RawSyscall(_SYS_dup, uintptr(fd[i]), uintptr(nextfd), 0)
if err1 != 0 {
goto childerror
}
diff --git a/src/testing/testing.go b/src/testing/testing.go
index 85da6bb02a..061142b9ab 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// Package testing provides support for automated testing of Go packages.
-// It is intended to be used in concert with the ``go test'' command, which automates
+// It is intended to be used in concert with the "go test" command, which automates
// execution of any function of the form
// func TestXxx(*testing.T)
// where Xxx does not start with a lowercase letter. The function name
@@ -14,8 +14,8 @@
// To write a new test suite, create a file whose name ends _test.go that
// contains the TestXxx functions as described here. Put the file in the same
// package as the one being tested. The file will be excluded from regular
-// package builds but will be included when the ``go test'' command is run.
-// For more detail, run ``go help test'' and ``go help testflag''.
+// package builds but will be included when the "go test" command is run.
+// For more detail, run "go help test" and "go help testflag".
//
// A simple test function looks like this:
//
diff --git a/src/text/template/multi_test.go b/src/text/template/multi_test.go
index bf1f1b2701..34d2378e38 100644
--- a/src/text/template/multi_test.go
+++ b/src/text/template/multi_test.go
@@ -359,6 +359,7 @@ func TestEmptyTemplate(t *testing.T) {
in string
want string
}{
+ {[]string{"x", "y"}, "", "y"},
{[]string{""}, "once", ""},
{[]string{"", ""}, "twice", ""},
{[]string{"{{.}}", "{{.}}"}, "twice", "twice"},
diff --git a/test/codegen/memops.go b/test/codegen/memops.go
index cd35910c12..a234283146 100644
--- a/test/codegen/memops.go
+++ b/test/codegen/memops.go
@@ -354,3 +354,26 @@ func idxCompare(i int) int {
}
return 1
}
+
+func idxFloatOps(a []float64, b []float32, i int) (float64, float32) {
+ c := float64(7)
+ // amd64: `ADDSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
+ c += a[i+1]
+ // amd64: `SUBSD\t16\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
+ c -= a[i+2]
+ // amd64: `MULSD\t24\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
+ c *= a[i+3]
+ // amd64: `DIVSD\t32\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
+ c /= a[i+4]
+
+ d := float32(8)
+ // amd64: `ADDSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
+ d += b[i+1]
+ // amd64: `SUBSS\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
+ d -= b[i+2]
+ // amd64: `MULSS\t12\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
+ d *= b[i+3]
+ // amd64: `DIVSS\t16\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
+ d /= b[i+4]
+ return c, d
+}
diff --git a/test/fixedbugs/issue33308.go b/test/fixedbugs/issue33308.go
new file mode 100644
index 0000000000..b0fd6a450c
--- /dev/null
+++ b/test/fixedbugs/issue33308.go
@@ -0,0 +1,12 @@
+// errorcheck
+
+// Copyright 2020 The Go 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 the compiler does not crash on a []byte conversion of an
+// untyped expression.
+package p
+
+var v uint
+var x = []byte((1 << v) + 1) // ERROR "cannot convert"
diff --git a/test/fixedbugs/issue40367.go b/test/fixedbugs/issue40367.go
new file mode 100644
index 0000000000..0dc5ad7120
--- /dev/null
+++ b/test/fixedbugs/issue40367.go
@@ -0,0 +1,41 @@
+// run
+
+// Copyright 2020 The Go 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
+
+func case1() {
+ rates := []int32{1,2,3,4,5,6}
+ var sink [6]int
+ j := len(sink)
+ for star, _ := range rates {
+ if star+1 < 1 {
+ panic("")
+ }
+ j--
+ sink[j] = j
+ }
+}
+
+func case2() {
+ i := 0
+ var sink [3]int
+ j := len(sink)
+top:
+ j--
+ sink[j] = j
+ if i < 2 {
+ i++
+ if i < 1 {
+ return
+ }
+ goto top
+ }
+}
+
+func main() {
+ case1()
+ case2()
+} \ No newline at end of file
diff --git a/test/prove.go b/test/prove.go
index d37021d283..3c19c513b6 100644
--- a/test/prove.go
+++ b/test/prove.go
@@ -670,7 +670,8 @@ func oforuntil(b []int) {
i := 0
if len(b) > i {
top:
- println(b[i]) // ERROR "Induction variable: limits \[0,\?\), increment 1$" "Proved IsInBounds$"
+ // TODO: remove the todo of next line once we complete the following optimization of CL 244579
+ // println(b[i]) // todo: ERROR "Induction variable: limits \[0,\?\), increment 1$" "Proved IsInBounds$"
i++
if i < len(b) {
goto top
@@ -720,7 +721,8 @@ func range1(b []int) {
// range2 elements are larger, so they use the general form of a range loop.
func range2(b [][32]int) {
for i, v := range b {
- b[i][0] = v[0] + 1 // ERROR "Induction variable: limits \[0,\?\), increment 1$" "Proved IsInBounds$"
+ // TODO: remove the todo of next line once we complete the following optimization of CL 244579
+ b[i][0] = v[0] + 1 // todo: ERROR "Induction variable: limits \[0,\?\), increment 1$" "Proved IsInBounds$"
if i < len(b) { // ERROR "Proved Less64$"
println("x")
}