mirror of
https://github.com/golang/go.git
synced 2026-01-29 23:22:06 +03:00
Compare commits
86 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edfd6f2848 | ||
|
|
3445c3512b | ||
|
|
4298b46e23 | ||
|
|
2ba9d45f9c | ||
|
|
399ce80738 | ||
|
|
bce174c435 | ||
|
|
36fcde1676 | ||
|
|
9c0a6cec5b | ||
|
|
f3529ca961 | ||
|
|
de0e241719 | ||
|
|
d9bbff5010 | ||
|
|
5846dc870e | ||
|
|
b6f70c0ec9 | ||
|
|
be0254a141 | ||
|
|
efed90aedc | ||
|
|
67d894ee65 | ||
|
|
e2ecfdf7b4 | ||
|
|
e98cafae04 | ||
|
|
73f86d2a78 | ||
|
|
83b181c68b | ||
|
|
a68b7d324c | ||
|
|
846c00ed3d | ||
|
|
8ca58ff90b | ||
|
|
51122090e1 | ||
|
|
f758dabf52 | ||
|
|
f296b7a6f0 | ||
|
|
14c92c5f80 | ||
|
|
f4274e64aa | ||
|
|
b7eca1cf3e | ||
|
|
6143ce337f | ||
|
|
b3382665ab | ||
|
|
6936700f21 | ||
|
|
1b41fcbce5 | ||
|
|
c4b92691b0 | ||
|
|
cd65852fee | ||
|
|
768e5d1e4d | ||
|
|
96745b980c | ||
|
|
bec8e9c9bd | ||
|
|
2e44aa30f0 | ||
|
|
c8d1e4cf83 | ||
|
|
ea3a94c92e | ||
|
|
edea4a79e8 | ||
|
|
8980ff45cf | ||
|
|
cdd55a324b | ||
|
|
e6f15fab0a | ||
|
|
9d7dad18db | ||
|
|
612ef03a23 | ||
|
|
b43b463d8f | ||
|
|
21f453b848 | ||
|
|
74870669fc | ||
|
|
ca153f4db7 | ||
|
|
f75a45c4d7 | ||
|
|
ab9d037401 | ||
|
|
564c76a268 | ||
|
|
b620f6fde5 | ||
|
|
e577ba98d8 | ||
|
|
229247d33b | ||
|
|
c5125098b2 | ||
|
|
adba22a9ae | ||
|
|
fd85ff5ee0 | ||
|
|
8e804f19b6 | ||
|
|
6717d27be2 | ||
|
|
9c41c1d8dc | ||
|
|
2e08d80732 | ||
|
|
76a6adcf3a | ||
|
|
0e9f7ac7ca | ||
|
|
e6036e7da5 | ||
|
|
c54e36905b | ||
|
|
329317472f | ||
|
|
99f8de7339 | ||
|
|
3dcb516d42 | ||
|
|
20a838ab94 | ||
|
|
c49910abc3 | ||
|
|
6a7f08952e | ||
|
|
8ced42e78b | ||
|
|
f63e55b541 | ||
|
|
17acbdb357 | ||
|
|
babeec29aa | ||
|
|
b4dca6416f | ||
|
|
f5293d77a9 | ||
|
|
51534757da | ||
|
|
d898c7b544 | ||
|
|
29ccdfc853 | ||
|
|
3f0cdedfdd | ||
|
|
a068054af1 | ||
|
|
331b8661a0 |
22
CONTRIBUTORS
22
CONTRIBUTORS
@@ -26,6 +26,7 @@
|
||||
|
||||
Aamir Khan <syst3m.w0rm@gmail.com>
|
||||
Aaron Beitch <aaronb@arista.com>
|
||||
Aaron Bieber <deftly@gmail.com>
|
||||
Aaron Cannon <cannona@fireantproductions.com>
|
||||
Aaron France <aaron.l.france@gmail.com>
|
||||
Aaron Jacobs <jacobsa@google.com>
|
||||
@@ -48,6 +49,7 @@ Adam Shannon <adamkshannon@gmail.com>
|
||||
Adam Shelton <aashelt90@gmail.com>
|
||||
Adam Sindelar <adamsh@google.com>
|
||||
Adam Thomason <athomason@gmail.com>
|
||||
Adam Williams <pwnfactory@gmail.com>
|
||||
Adam Woodbeck <adam@woodbeck.net>
|
||||
Adarsh Ravichandran <adarshravichandran91@gmail.com>
|
||||
Aditya Harindar <aditya.harindar@gmail.com>
|
||||
@@ -276,6 +278,7 @@ Awn Umar <awn@cryptolosophy.io>
|
||||
Axel Wagner <axel.wagner.hh@googlemail.com>
|
||||
Ayan George <ayan@ayan.net>
|
||||
Ayanamist Yang <ayanamist@gmail.com>
|
||||
Ayke van Laethem <aykevanlaethem@gmail.com>
|
||||
Aymerick Jéhanne <aymerick@jehanne.org>
|
||||
Azat Kaumov <kaumov.a.r@gmail.com>
|
||||
Baiju Muthukadan <baiju.m.mail@gmail.com>
|
||||
@@ -338,6 +341,7 @@ Brad Jones <rbjones@google.com>
|
||||
Brad Morgan <brad@morgabra.com>
|
||||
Brad Whitaker <bwhitaker@fastly.com>
|
||||
Braden Bassingthwaite <bbassingthwaite@vendasta.com>
|
||||
Bradford Lamson-Scribner <brad.lamson@gmail.com>
|
||||
Bradley Falzon <brad@teambrad.net>
|
||||
Brady Catherman <brady@gmail.com>
|
||||
Brady Sullivan <brady@bsull.com>
|
||||
@@ -351,6 +355,7 @@ Brett Cannon <bcannon@gmail.com>
|
||||
Brett Merrill <brett.j.merrill94@gmail.com>
|
||||
Brian Dellisanti <briandellisanti@gmail.com>
|
||||
Brian Downs <brian.downs@gmail.com>
|
||||
Brian Falk <falk@logicparty.org>
|
||||
Brian G. Merrell <bgmerrell@gmail.com>
|
||||
Brian Gitonga Marete <marete@toshnix.com> <bgmarete@gmail.com> <bgm@google.com>
|
||||
Brian Kennedy <btkennedy@gmail.com>
|
||||
@@ -404,6 +409,7 @@ Charles L. Dorian <cldorian@gmail.com>
|
||||
Charles Lee <zombie.fml@gmail.com>
|
||||
Charles Weill <weill@google.com>
|
||||
Chauncy Cullitan <chauncyc@google.com>
|
||||
Chen Zhihan <energiehund@gmail.com>
|
||||
Cherry Zhang <cherryyz@google.com>
|
||||
Chew Choon Keat <choonkeat@gmail.com>
|
||||
Cholerae Hu <choleraehyq@gmail.com>
|
||||
@@ -442,6 +448,7 @@ Christopher Cahoon <chris.cahoon@gmail.com>
|
||||
Christopher Guiney <chris@guiney.net>
|
||||
Christopher Henderson <chris@chenderson.org>
|
||||
Christopher Koch <chrisko@google.com>
|
||||
Christopher Loessl <cloessl+github@gmail.com>
|
||||
Christopher Nelson <nadiasvertex@gmail.com>
|
||||
Christopher Nielsen <m4dh4tt3r@gmail.com>
|
||||
Christopher Redden <christopher.redden@gmail.com>
|
||||
@@ -739,12 +746,14 @@ Frank Somers <fsomers@arista.com>
|
||||
Frederic Guillot <frederic.guillot@gmail.com>
|
||||
Frederick Kelly Mayle III <frederickmayle@gmail.com>
|
||||
Frederik Ring <frederik.ring@gmail.com>
|
||||
Frederik Zipp <fzipp@gmx.de>
|
||||
Fredrik Enestad <fredrik.enestad@soundtrackyourbrand.com>
|
||||
Fredrik Forsmo <fredrik.forsmo@gmail.com>
|
||||
Fredrik Wallgren <fredrik.wallgren@gmail.com>
|
||||
Frew Schmidt <github@frew.co>
|
||||
Frithjof Schulze <schulze@math.uni-hannover.de> <sfrithjof@gmail.com>
|
||||
Frits van Bommel <fvbommel@gmail.com>
|
||||
Fujimoto Kyosuke <kyoro.f@gmail.com>
|
||||
Fumitoshi Ukai <ukai@google.com>
|
||||
G. Hussain Chinoy <ghchinoy@gmail.com>
|
||||
Gaal Yahas <gaal@google.com>
|
||||
@@ -803,6 +812,7 @@ GitHub User @frennkie (6499251) <mail@rhab.de>
|
||||
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>
|
||||
GitHub User @jopbrown (6345470) <msshane2008@gmail.com>
|
||||
GitHub User @kazyshr (30496953) <kazyshr0301@gmail.com>
|
||||
GitHub User @kc1212 (1093806) <kc1212@users.noreply.github.com>
|
||||
GitHub User @Kropekk (13366453) <kamilkropiewnicki@gmail.com>
|
||||
@@ -828,6 +838,7 @@ 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>
|
||||
GitHub User @witchard (4994659) <witchard@hotmail.co.uk>
|
||||
GitHub User @yah01 (12216890) <kagaminehuan@gmail.com>
|
||||
GitHub User @yuanhh (1298735) <yuan415030@gmail.com>
|
||||
GitHub User @zikaeroh (48577114) <zikaeroh@gmail.com>
|
||||
GitHub User @ZZMarquis (7624583) <zhonglingjian3821@163.com>
|
||||
@@ -897,6 +908,7 @@ Heschi Kreinick <heschi@google.com>
|
||||
Hidetatsu Yaginuma <ygnmhdtt@gmail.com>
|
||||
Hilko Bengen <bengen@hilluzination.de>
|
||||
Hiroaki Nakamura <hnakamur@gmail.com>
|
||||
Hiromichi Ema <ema.hiro@gmail.com>
|
||||
Hironao OTSUBO <motemen@gmail.com>
|
||||
Hiroshi Ioka <hirochachacha@gmail.com>
|
||||
Hitoshi Mitake <mitake.hitoshi@gmail.com>
|
||||
@@ -973,6 +985,7 @@ Jakob Borg <jakob@nym.se>
|
||||
Jakob Weisblat <jakobw@mit.edu>
|
||||
Jakub Čajka <jcajka@redhat.com>
|
||||
Jakub Ryszard Czarnowicz <j.czarnowicz@gmail.com>
|
||||
Jamal Carvalho <jamal.a.carvalho@gmail.com>
|
||||
James Aguilar <jaguilar@google.com>
|
||||
James Bardin <j.bardin@gmail.com>
|
||||
James Chacon <jchacon@google.com>
|
||||
@@ -1020,6 +1033,7 @@ Jannis Andrija Schnitzer <jannis@schnitzer.im>
|
||||
Jared Culp <jculp14@gmail.com>
|
||||
Jaroslavas Počepko <jp@webmaster.ms>
|
||||
Jason A. Donenfeld <Jason@zx2c4.com>
|
||||
Jason Baker <jason-baker@users.noreply.github.com>
|
||||
Jason Barnett <jason.w.barnett@gmail.com>
|
||||
Jason Buberel <jbuberel@google.com>
|
||||
Jason Chu <jasonchujc@gmail.com>
|
||||
@@ -1213,6 +1227,7 @@ Kamil Chmielewski <kamil.chm@gmail.com>
|
||||
Kamil Kisiel <kamil@kamilkisiel.net> <kamil.kisiel@gmail.com>
|
||||
Kamil Rytarowski <krytarowski@users.noreply.github.com>
|
||||
Kang Hu <hukangustc@gmail.com>
|
||||
Kanta Ebihara <kantaebihara@gmail.com>
|
||||
Karan Dhiman <karandhi@ca.ibm.com>
|
||||
Karel Pazdera <pazderak@gmail.com>
|
||||
Karoly Negyesi <chx1975@gmail.com>
|
||||
@@ -1252,6 +1267,7 @@ Ketan Parmar <ketanbparmar@gmail.com>
|
||||
Kevan Swanberg <kevswanberg@gmail.com>
|
||||
Kevin Ballard <kevin@sb.org>
|
||||
Kevin Burke <kev@inburke.com>
|
||||
Kévin Dunglas <dunglas@gmail.com>
|
||||
Kevin Gillette <extemporalgenome@gmail.com>
|
||||
Kevin Kirsche <kev.kirsche@gmail.com>
|
||||
Kevin Klues <klueska@gmail.com> <klueska@google.com>
|
||||
@@ -1265,6 +1281,7 @@ Kim Yongbin <kybinz@gmail.com>
|
||||
Kir Kolyshkin <kolyshkin@gmail.com>
|
||||
Kirill Motkov <Motkov.Kirill@gmail.com>
|
||||
Kirill Smelkov <kirr@nexedi.com>
|
||||
Kirill Tatchihin <kirabsuir@gmail.com>
|
||||
Kirk Han <kirk91.han@gmail.com>
|
||||
Kirklin McDonald <kirklin.mcdonald@gmail.com>
|
||||
Klaus Post <klauspost@gmail.com>
|
||||
@@ -1378,6 +1395,7 @@ Marcelo E. Magallon <marcelo.magallon@gmail.com>
|
||||
Marco Hennings <marco.hennings@freiheit.com>
|
||||
Marcus Willock <crazcalm@gmail.com>
|
||||
Marga Manterola <marga@google.com>
|
||||
Mariano Cano <mariano@smallstep.com>
|
||||
Marin Bašić <marin.basic02@gmail.com>
|
||||
Mario Arranz <marioarranzr@gmail.com>
|
||||
Marius A. Eriksen <marius@grailbio.com>
|
||||
@@ -1949,6 +1967,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 Ivanov <ser1325@gmail.com>
|
||||
Sergey Lukjanov <me@slukjanov.name>
|
||||
Sergey Mishin <sergeymishine@gmail.com>
|
||||
Sergey Mudrik <sergey.mudrik@gmail.com>
|
||||
@@ -2090,6 +2109,7 @@ Thomas Desrosiers <thomasdesr@gmail.com>
|
||||
Thomas Habets <habets@google.com>
|
||||
Thomas Kappler <tkappler@gmail.com>
|
||||
Thomas Meson <zllak@hycik.org>
|
||||
Thomas Symborski <thomas.symborski@gmail.com>
|
||||
Thomas Wanielista <tomwans@gmail.com>
|
||||
Thorben Krueger <thorben.krueger@gmail.com>
|
||||
Thordur Bjornsson <thorduri@secnorth.net>
|
||||
@@ -2130,6 +2150,7 @@ Tom Thorogood <me+google@tomthorogood.co.uk>
|
||||
Tom Wilkie <tom@weave.works>
|
||||
Tomas Dabasinskas <tomas@dabasinskas.net>
|
||||
Tommy Schaefer <tommy.schaefer@teecom.com>
|
||||
Tomohiro Kusumoto <zabio1192@gmail.com>
|
||||
Tomoya Ishizaki <zaq1tomo@gmail.com>
|
||||
Tonis Tiigi <tonistiigi@gmail.com>
|
||||
Tony Reix <tony.reix@bull.net>
|
||||
@@ -2240,6 +2261,7 @@ Xi Ruoyao <xry23333@gmail.com>
|
||||
Xia Bin <snyh@snyh.org>
|
||||
Xiangdong Ji <xiangdong.ji@arm.com>
|
||||
Xing Xing <mikespook@gmail.com>
|
||||
Xingqang Bai <bxq2011hust@qq.com>
|
||||
Xu Fei <badgangkiller@gmail.com>
|
||||
Xudong Zhang <felixmelon@gmail.com>
|
||||
Xudong Zheng <7pkvm5aw@slicealias.com>
|
||||
|
||||
@@ -395,3 +395,14 @@ func (w *Watchdog) Start() {
|
||||
The cost of race detection varies by program, but for a typical program, memory
|
||||
usage may increase by 5-10x and execution time by 2-20x.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The race detector currently allocates an extra 8 bytes per <code>defer</code>
|
||||
and <code>recover</code> statement. Those extra allocations <a
|
||||
href="https://golang.org/issue/26813">are not recovered until the goroutine
|
||||
exits</a>. This means that if you have a long-running goroutine that is
|
||||
periodically issuing <code>defer</code> and <code>recover</code> calls,
|
||||
the program memory usage may grow without bound. These memory allocations
|
||||
will not show up in the output of <code>runtime.ReadMemStats</code> or
|
||||
<code>runtime/pprof</code>.
|
||||
</p>
|
||||
|
||||
@@ -34,6 +34,7 @@ We encourage all Go users to subscribe to
|
||||
<p>A <a href="/doc/devel/release.html">summary</a> of the changes between Go releases. Notes for the major releases:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="/doc/go1.14">Go 1.14</a> <small>(February 2020)</small></li>
|
||||
<li><a href="/doc/go1.13">Go 1.13</a> <small>(September 2019)</small></li>
|
||||
<li><a href="/doc/go1.12">Go 1.12</a> <small>(February 2019)</small></li>
|
||||
<li><a href="/doc/go1.11">Go 1.11</a> <small>(August 2018)</small></li>
|
||||
|
||||
@@ -14,24 +14,24 @@ 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.14</h2>
|
||||
<h2 id="introduction">Introduction to Go 1.14</h2>
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
Go 1.14 is not yet released. These are work-in-progress
|
||||
release notes. Go 1.14 is expected to be released in February 2020.
|
||||
</strong>
|
||||
The latest Go release, version 1.14, arrives six months after <a href="go1.13">Go 1.13</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>
|
||||
Module support in the <code>go</code> command is now ready for production use,
|
||||
and we encourage all users to migrate to Go modules for dependency management.
|
||||
If you are unable to migrate due to a problem in the Go toolchain,
|
||||
please ensure that the problem has an
|
||||
<a href="https://golang.org/issue?q=is%3Aissue+is%3Aopen+label%3Amodules">open issue</a>
|
||||
filed. (If the issue is not on the <code>Go1.15</code> milestone, please let us
|
||||
know why it prevents you from migrating so that we can prioritize it
|
||||
appropriately.)
|
||||
Module support in the <code>go</code> command is now ready for production use,
|
||||
and we encourage all users to <a href="https://blog.golang.org/migrating-to-go-modules">migrate to Go
|
||||
modules for dependency management</a>. If you are unable to migrate due to a problem in the Go
|
||||
toolchain, please ensure that the problem has an
|
||||
<a href="https://golang.org/issue?q=is%3Aissue+is%3Aopen+label%3Amodules">open issue</a>
|
||||
filed. (If the issue is not on the <code>Go1.15</code> milestone, please let us
|
||||
know why it prevents you from migrating so that we can prioritize it
|
||||
appropriately.)
|
||||
</p>
|
||||
|
||||
<h2 id="language">Changes to the language</h2>
|
||||
@@ -77,6 +77,18 @@ appropriately.)
|
||||
(Data Execution Prevention)</a> enabled.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 202439 -->
|
||||
On Windows, creating a file
|
||||
via <a href="/pkg/os#CreateFile"><code>os.OpenFile</code></a> with
|
||||
the <a href="/pkg/os/#O_CREATE"><code>os.O_CREATE</code></a> flag, or
|
||||
via <a href="/pkg/syscall#Open"><code>syscall.Open</code></a> with
|
||||
the <a href="/pkg/syscall#O_CREAT"><code>syscall.O_CREAT</code></a>
|
||||
flag, will now create the file as read-only if the
|
||||
bit <code>0o200</code> (owner write permission) is not set in the
|
||||
permission argument. This makes the behavior on Windows more like
|
||||
that on Unix systems.
|
||||
</p>
|
||||
|
||||
<h3 id="wasm">WebAssembly</h3>
|
||||
|
||||
<p><!-- CL 203600 -->
|
||||
@@ -108,7 +120,7 @@ appropriately.)
|
||||
<h3 id="freebsd">FreeBSD</h3>
|
||||
|
||||
<p><!-- CL 199919 -->
|
||||
Go now supports the 64-bit ARM architecture on FreeBSD (the
|
||||
Go now supports the 64-bit ARM architecture on FreeBSD 12.0 or later (the
|
||||
<code>freebsd/arm64</code> port).
|
||||
</p>
|
||||
|
||||
@@ -393,7 +405,7 @@ appropriately.)
|
||||
<p><!-- CL 202117 -->
|
||||
This release includes experimental support for compiler-inserted
|
||||
coverage instrumentation for fuzzing.
|
||||
See <a href="https://golang.org/issue/14565">the issue</a> for more
|
||||
See <a href="https://golang.org/issue/14565">issue 14565</a> for more
|
||||
details.
|
||||
This API may change in future releases.
|
||||
</p>
|
||||
@@ -454,7 +466,15 @@ appropriately.)
|
||||
certificate, and letting the package automatically select the best one.
|
||||
Note that the performance of this selection is going to be poor unless the
|
||||
<a href="/pkg/crypto/tls/#Certificate.Leaf"><code>Certificate.Leaf</code></a>
|
||||
field is set.
|
||||
field is set. The
|
||||
<a href="/pkg/crypto/tls/#Config.NameToCertificate"><code>Config.NameToCertificate</code></a>
|
||||
field, which only supports associating a single certificate with
|
||||
a give name, is now deprecated and should be left as <code>nil</code>.
|
||||
Similarly the
|
||||
<a href="/pkg/crypto/tls/#Config.BuildNameToCertificate"><code>Config.BuildNameToCertificate</code></a>
|
||||
method, which builds the <code>NameToCertificate</code> field
|
||||
from the leaf certificates, is now deprecated and should not be
|
||||
called.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 175517 -->
|
||||
@@ -582,6 +602,13 @@ appropriately.)
|
||||
was never a documented feature. For proper escaping, see <a
|
||||
href="/pkg/encoding/json/#HTMLEscape"><code>HTMLEscape</code></a>.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 195045 -->
|
||||
<a href="/pkg/encoding/json/#Number"><code>Number</code></a> no longer
|
||||
accepts invalid numbers, to follow the documented behavior more closely.
|
||||
If a program needs to accept invalid numbers like the empty string,
|
||||
consider wrapping the type with <a href="/pkg/encoding/json/#Unmarshaler"><code>Unmarshaler</code></a>.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- encoding/json -->
|
||||
|
||||
@@ -752,6 +779,19 @@ appropriately.)
|
||||
</dd>
|
||||
</dl><!-- net/textproto -->
|
||||
|
||||
<dl id="net/url"><dt><a href="/pkg/net/url/">net/url</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 185117 -->
|
||||
When parsing of a URL fails
|
||||
(for example by <a href="/pkg/net/url/#Parse"><code>Parse</code></a>
|
||||
or <a href="/pkg/net/url/#ParseRequestURI"><code>ParseRequestURI</code></a>),
|
||||
the resulting <a href="/pkg/net/url/#Error.Error"><code>Error</code></a> message
|
||||
will now quote the unparsable URL.
|
||||
This provides clearer structure and consistency with other parsing errors.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- net/url -->
|
||||
|
||||
<dl id="os/signal"><dt><a href="/pkg/os/signal/">os/signal</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 187739 -->
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<p>
|
||||
<a href="/dl/" target="_blank">Official binary
|
||||
distributions</a> are available for the FreeBSD (release 10-STABLE and above),
|
||||
Linux, macOS (10.10 and above), and Windows operating systems and
|
||||
Linux, macOS (10.11 and above), and Windows operating systems and
|
||||
the 32-bit (<code>386</code>) and 64-bit (<code>amd64</code>) x86 processor
|
||||
architectures.
|
||||
</p>
|
||||
@@ -49,7 +49,7 @@ If your OS or architecture is not on the list, you may be able to
|
||||
<tr><td colspan="3"><hr></td></tr>
|
||||
<tr><td>FreeBSD 10.3 or later</td> <td>amd64, 386</td> <td>Debian GNU/kFreeBSD not supported</td></tr>
|
||||
<tr valign='top'><td>Linux 2.6.23 or later with glibc</td> <td>amd64, 386, arm, arm64,<br>s390x, ppc64le</td> <td>CentOS/RHEL 5.x not supported.<br>Install from source for other libc.</td></tr>
|
||||
<tr><td>macOS 10.10 or later</td> <td>amd64</td> <td>use the clang or gcc<sup>†</sup> that comes with Xcode<sup>‡</sup> for <code>cgo</code> support</td></tr>
|
||||
<tr><td>macOS 10.11 or later</td> <td>amd64</td> <td>use the clang or gcc<sup>†</sup> that comes with Xcode<sup>‡</sup> for <code>cgo</code> support</td></tr>
|
||||
<tr valign='top'><td>Windows 7, Server 2008R2 or later</td> <td>amd64, 386</td> <td>use MinGW (<code>386</code>) or MinGW-W64 (<code>amd64</code>) gcc<sup>†</sup>.<br>No need for cygwin or msys.</td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -124,6 +124,11 @@ typedef struct {
|
||||
} Issue31891B;
|
||||
|
||||
void callIssue31891(void);
|
||||
|
||||
typedef struct {
|
||||
int i;
|
||||
} Issue38408, *PIssue38408;
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -552,3 +557,8 @@ func useIssue31891B(c *C.Issue31891B) {}
|
||||
func test31891(t *testing.T) {
|
||||
C.callIssue31891()
|
||||
}
|
||||
|
||||
// issue 38408
|
||||
// A typedef pointer can be used as the element type.
|
||||
// No runtime test; just make sure it compiles.
|
||||
var _ C.PIssue38408 = &C.Issue38408{i: 1}
|
||||
|
||||
33
misc/cgo/testgodefs/testdata/issue37479.go
vendored
Normal file
33
misc/cgo/testgodefs/testdata/issue37479.go
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// 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 ignore
|
||||
|
||||
package main
|
||||
|
||||
/*
|
||||
typedef struct A A;
|
||||
|
||||
typedef struct {
|
||||
struct A *next;
|
||||
struct A **prev;
|
||||
} N;
|
||||
|
||||
struct A
|
||||
{
|
||||
N n;
|
||||
};
|
||||
|
||||
typedef struct B
|
||||
{
|
||||
A* a;
|
||||
} B;
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type N C.N
|
||||
|
||||
type A C.A
|
||||
|
||||
type B C.B
|
||||
23
misc/cgo/testgodefs/testdata/issue37621.go
vendored
Normal file
23
misc/cgo/testgodefs/testdata/issue37621.go
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// 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 ignore
|
||||
|
||||
package main
|
||||
|
||||
/*
|
||||
struct tt {
|
||||
long long a;
|
||||
long long b;
|
||||
};
|
||||
|
||||
struct s {
|
||||
struct tt ts[3];
|
||||
};
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type TT C.struct_tt
|
||||
|
||||
type S C.struct_s
|
||||
8
misc/cgo/testgodefs/testdata/main.go
vendored
8
misc/cgo/testgodefs/testdata/main.go
vendored
@@ -11,5 +11,13 @@ var v2 = v1.L
|
||||
// Test that P, Q, and R all point to byte.
|
||||
var v3 = Issue8478{P: (*byte)(nil), Q: (**byte)(nil), R: (***byte)(nil)}
|
||||
|
||||
// Test that N, A and B are fully defined
|
||||
var v4 = N{}
|
||||
var v5 = A{}
|
||||
var v6 = B{}
|
||||
|
||||
// Test that S is fully defined
|
||||
var v7 = S{}
|
||||
|
||||
func main() {
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ var filePrefixes = []string{
|
||||
"anonunion",
|
||||
"issue8478",
|
||||
"fieldtypedef",
|
||||
"issue37479",
|
||||
"issue37621",
|
||||
}
|
||||
|
||||
func TestGoDefs(t *testing.T) {
|
||||
|
||||
@@ -1,17 +1,41 @@
|
||||
This directory contains helper file for trace viewer (`go tool trace`).
|
||||
## Resources for Go's trace viewer
|
||||
|
||||
`trace_viewer_full.html` was generated by following
|
||||
[instructions](https://github.com/catapult-project/catapult/blob/master/tracing/docs/embedding-trace-viewer.md)
|
||||
on revision `dc970d3e1f7b3da5a2849de70ff253acdb70148f`
|
||||
of [catapult](https://github.com/catapult-project/catapult) using:
|
||||
Go execution trace UI (`go tool trace`) embeds
|
||||
Chrome's trace viewer (Catapult) following the
|
||||
[instructions](
|
||||
https://chromium.googlesource.com/catapult/+/refs/heads/master/tracing/docs/embedding-trace-viewer.md). This directory contains
|
||||
the helper files to embed Chrome's trace viewer.
|
||||
|
||||
The current resources were generated/copied from
|
||||
[`Catapult@9508452e18f130c98499cb4c4f1e1efaedee8962`](
|
||||
https://chromium.googlesource.com/catapult/+/9508452e18f130c98499cb4c4f1e1efaedee8962).
|
||||
|
||||
### Updating `trace_viewer_full.html`
|
||||
|
||||
The file was generated by catapult's `vulcanize_trace_viewer` command.
|
||||
```
|
||||
catapult$ ./tracing/bin/vulcanize_trace_viewer --config=full
|
||||
catapult$ cp tracing/bin/trace_viewer_full.html $GOROOT/misc/trace/trace_viewer_lean.html
|
||||
$ git clone https://chromium.googlesource.com/catapult
|
||||
$ cd catapult
|
||||
$ ./tracing/bin/vulcanize_trace_viewer --config=full
|
||||
$ cp tracing/bin/trace_viewer_full.html $GOROOT/misc/trace/trace_viewer_full.html
|
||||
```
|
||||
|
||||
We are supposed to use --config=lean (produces smaller html),
|
||||
but it is broken at the moment:
|
||||
https://github.com/catapult-project/catapult/issues/2247
|
||||
|
||||
### Updating `webcomponents.min.js`
|
||||
|
||||
`webcomponents.min.js` is necessary to let the trace viewer page
|
||||
to import the `trace_viewer_full.html`.
|
||||
This is copied from the catapult repo.
|
||||
|
||||
```
|
||||
$ cp third_party/polymer/components/webcomponentsjs/webcomponents.min.js $GOROOT/misc/trace/webcomponents.min.js
|
||||
```
|
||||
|
||||
## Licenses
|
||||
|
||||
The license for trace-viewer is as follows:
|
||||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
//
|
||||
@@ -40,3 +64,42 @@ The license for trace-viewer is as follows:
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The license for webcomponents.min.js is as follows:
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
||||
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
* Code distributed by Google as part of the polymer project is also
|
||||
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
// Copyright (c) 2014 The Polymer Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
File diff suppressed because one or more lines are too long
14
misc/trace/webcomponents.min.js
vendored
Normal file
14
misc/trace/webcomponents.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -2060,6 +2060,10 @@ var goIdent = make(map[string]*ast.Ident)
|
||||
// that may contain a pointer. This is used for cgo pointer checking.
|
||||
var unionWithPointer = make(map[ast.Expr]bool)
|
||||
|
||||
// anonymousStructTag provides a consistent tag for an anonymous struct.
|
||||
// The same dwarf.StructType pointer will always get the same tag.
|
||||
var anonymousStructTag = make(map[*dwarf.StructType]string)
|
||||
|
||||
func (c *typeConv) Init(ptrSize, intSize int64) {
|
||||
c.ptrSize = ptrSize
|
||||
c.intSize = intSize
|
||||
@@ -2243,7 +2247,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
||||
// Translate to zero-length array instead.
|
||||
count = 0
|
||||
}
|
||||
sub := c.loadType(dt.Type, pos, key)
|
||||
sub := c.Type(dt.Type, pos)
|
||||
t.Align = sub.Align
|
||||
t.Go = &ast.ArrayType{
|
||||
Len: c.intExpr(count),
|
||||
@@ -2388,7 +2392,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
||||
c.ptrs[key] = append(c.ptrs[key], t)
|
||||
|
||||
case *dwarf.QualType:
|
||||
t1 := c.loadType(dt.Type, pos, key)
|
||||
t1 := c.Type(dt.Type, pos)
|
||||
t.Size = t1.Size
|
||||
t.Align = t1.Align
|
||||
t.Go = t1.Go
|
||||
@@ -2408,8 +2412,12 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
||||
break
|
||||
}
|
||||
if tag == "" {
|
||||
tag = "__" + strconv.Itoa(tagGen)
|
||||
tagGen++
|
||||
tag = anonymousStructTag[dt]
|
||||
if tag == "" {
|
||||
tag = "__" + strconv.Itoa(tagGen)
|
||||
tagGen++
|
||||
anonymousStructTag[dt] = tag
|
||||
}
|
||||
} else if t.C.Empty() {
|
||||
t.C.Set(dt.Kind + " " + tag)
|
||||
}
|
||||
@@ -2472,7 +2480,13 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
||||
}
|
||||
name := c.Ident("_Ctype_" + dt.Name)
|
||||
goIdent[name.Name] = name
|
||||
sub := c.loadType(dt.Type, pos, key)
|
||||
akey := ""
|
||||
if c.anonymousStructTypedef(dt) {
|
||||
// only load type recursively for typedefs of anonymous
|
||||
// structs, see issues 37479 and 37621.
|
||||
akey = key
|
||||
}
|
||||
sub := c.loadType(dt.Type, pos, akey)
|
||||
if c.badPointerTypedef(dt) {
|
||||
// Treat this typedef as a uintptr.
|
||||
s := *sub
|
||||
@@ -2993,6 +3007,13 @@ func fieldPrefix(fld []*ast.Field) string {
|
||||
return prefix
|
||||
}
|
||||
|
||||
// anonymousStructTypedef reports whether dt is a C typedef for an anonymous
|
||||
// struct.
|
||||
func (c *typeConv) anonymousStructTypedef(dt *dwarf.TypedefType) bool {
|
||||
st, ok := dt.Type.(*dwarf.StructType)
|
||||
return ok && st.StructName == ""
|
||||
}
|
||||
|
||||
// badPointerTypedef reports whether t is a C typedef that should not be considered a pointer in Go.
|
||||
// A typedef is bad if C code sometimes stores non-pointers in this type.
|
||||
// TODO: Currently our best solution is to find these manually and list them as
|
||||
|
||||
@@ -186,6 +186,7 @@ func algtype1(t *types.Type) (AlgKind, *types.Type) {
|
||||
|
||||
// genhash returns a symbol which is the closure used to compute
|
||||
// the hash of a value of type t.
|
||||
// Note: the generated function must match runtime.typehash exactly.
|
||||
func genhash(t *types.Type) *obj.LSym {
|
||||
switch algtype(t) {
|
||||
default:
|
||||
|
||||
@@ -542,7 +542,7 @@ func methtype(t *types.Type) *types.Type {
|
||||
// Is type src assignment compatible to type dst?
|
||||
// If so, return op code to use in conversion.
|
||||
// If not, return 0.
|
||||
func assignop(src *types.Type, dst *types.Type, why *string) Op {
|
||||
func assignop(src, dst *types.Type, why *string) Op {
|
||||
if why != nil {
|
||||
*why = ""
|
||||
}
|
||||
@@ -665,7 +665,8 @@ func assignop(src *types.Type, dst *types.Type, why *string) Op {
|
||||
// Can we convert a value of type src to a value of type dst?
|
||||
// If so, return op code to use in conversion (maybe OCONVNOP).
|
||||
// If not, return 0.
|
||||
func convertop(src *types.Type, dst *types.Type, why *string) Op {
|
||||
// srcConstant indicates whether the value of type src is a constant.
|
||||
func convertop(srcConstant bool, src, dst *types.Type, why *string) Op {
|
||||
if why != nil {
|
||||
*why = ""
|
||||
}
|
||||
@@ -741,6 +742,13 @@ func convertop(src *types.Type, dst *types.Type, why *string) Op {
|
||||
return OCONV
|
||||
}
|
||||
|
||||
// Special case for constant conversions: any numeric
|
||||
// conversion is potentially okay. We'll validate further
|
||||
// within evconst. See #38117.
|
||||
if srcConstant && (src.IsInteger() || src.IsFloat() || src.IsComplex()) && (dst.IsInteger() || dst.IsFloat() || dst.IsComplex()) {
|
||||
return OCONV
|
||||
}
|
||||
|
||||
// 6. src is an integer or has type []byte or []rune
|
||||
// and dst is a string type.
|
||||
if src.IsInteger() && dst.IsString() {
|
||||
|
||||
@@ -1634,7 +1634,7 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||
return n
|
||||
}
|
||||
var why string
|
||||
n.Op = convertop(t, n.Type, &why)
|
||||
n.Op = convertop(n.Left.Op == OLITERAL, t, n.Type, &why)
|
||||
if n.Op == 0 {
|
||||
if !n.Diag() && !n.Type.Broke() && !n.Left.Diag() {
|
||||
yyerror("cannot convert %L to type %v%s", n.Left, n.Type, why)
|
||||
|
||||
@@ -73,9 +73,11 @@ func dse(f *Func) {
|
||||
}
|
||||
|
||||
// Walk backwards looking for dead stores. Keep track of shadowed addresses.
|
||||
// An "address" is an SSA Value which encodes both the address and size of
|
||||
// the write. This code will not remove dead stores to the same address
|
||||
// of different types.
|
||||
// A "shadowed address" is a pointer and a size describing a memory region that
|
||||
// is known to be written. We keep track of shadowed addresses in the shadowed
|
||||
// map, mapping the ID of the address to the size of the shadowed region.
|
||||
// Since we're walking backwards, writes to a shadowed region are useless,
|
||||
// as they will be immediately overwritten.
|
||||
shadowed.clear()
|
||||
v := last
|
||||
|
||||
@@ -93,17 +95,13 @@ func dse(f *Func) {
|
||||
sz = v.AuxInt
|
||||
}
|
||||
if shadowedSize := int64(shadowed.get(v.Args[0].ID)); shadowedSize != -1 && shadowedSize >= sz {
|
||||
// Modify store into a copy
|
||||
// Modify the store/zero into a copy of the memory state,
|
||||
// effectively eliding the store operation.
|
||||
if v.Op == OpStore {
|
||||
// store addr value mem
|
||||
v.SetArgs1(v.Args[2])
|
||||
} else {
|
||||
// zero addr mem
|
||||
typesz := v.Args[0].Type.Elem().Size()
|
||||
if sz != typesz {
|
||||
f.Fatalf("mismatched zero/store sizes: %d and %d [%s]",
|
||||
sz, typesz, v.LongString())
|
||||
}
|
||||
v.SetArgs1(v.Args[1])
|
||||
}
|
||||
v.Aux = nil
|
||||
|
||||
@@ -49,11 +49,11 @@ var gogcflags = os.Getenv("GO_GCFLAGS")
|
||||
// optimizedLibs usually means "not running in a noopt test builder".
|
||||
var optimizedLibs = (!strings.Contains(gogcflags, "-N") && !strings.Contains(gogcflags, "-l"))
|
||||
|
||||
// TestNexting go-builds a file, then uses a debugger (default gdb, optionally delve)
|
||||
// TestNexting go-builds a file, then uses a debugger (default delve, optionally gdb)
|
||||
// to next through the generated executable, recording each line landed at, and
|
||||
// then compares those lines with reference file(s).
|
||||
// Flag -u updates the reference file(s).
|
||||
// Flag -d changes the debugger to delve (and uses delve-specific reference files)
|
||||
// Flag -g changes the debugger to gdb (and uses gdb-specific reference files)
|
||||
// Flag -v is ever-so-slightly verbose.
|
||||
// Flag -n is for dry-run, and prints the shell and first debug commands.
|
||||
//
|
||||
@@ -83,9 +83,9 @@ var optimizedLibs = (!strings.Contains(gogcflags, "-N") && !strings.Contains(gog
|
||||
// to indicate normalization of Strings, (hex) addresses, and numbers.
|
||||
// "O" is an explicit indication that we expect it to be optimized out.
|
||||
// For example:
|
||||
/*
|
||||
if len(os.Args) > 1 { //gdb-dbg=(hist/A,cannedInput/A) //dlv-dbg=(hist/A,cannedInput/A)
|
||||
*/
|
||||
//
|
||||
// if len(os.Args) > 1 { //gdb-dbg=(hist/A,cannedInput/A) //dlv-dbg=(hist/A,cannedInput/A)
|
||||
//
|
||||
// TODO: not implemented for Delve yet, but this is the plan
|
||||
//
|
||||
// After a compiler change that causes a difference in the debug behavior, check
|
||||
@@ -93,7 +93,7 @@ var optimizedLibs = (!strings.Contains(gogcflags, "-N") && !strings.Contains(gog
|
||||
// go test debug_test.go -args -u
|
||||
// (for Delve)
|
||||
// go test debug_test.go -args -u -d
|
||||
|
||||
//
|
||||
func TestNexting(t *testing.T) {
|
||||
skipReasons := "" // Many possible skip reasons, list all that apply
|
||||
if testing.Short() {
|
||||
@@ -108,7 +108,13 @@ func TestNexting(t *testing.T) {
|
||||
// Various architectures tend to differ slightly sometimes, and keeping them
|
||||
// all in sync is a pain for people who don't have them all at hand,
|
||||
// so limit testing to amd64 (for now)
|
||||
skipReasons += "not run when testing gdb (-g) unless forced (-f) or linux-amd64"
|
||||
skipReasons += "not run when testing gdb (-g) unless forced (-f) or linux-amd64; "
|
||||
}
|
||||
|
||||
if !*useGdb && !*force && testenv.Builder() == "linux-386-longtest" {
|
||||
// The latest version of Delve does support linux/386. However, the version currently
|
||||
// installed in the linux-386-longtest builder does not. See golang.org/issue/39309.
|
||||
skipReasons += "not run when testing delve on linux-386-longtest builder unless forced (-f); "
|
||||
}
|
||||
|
||||
if *useGdb {
|
||||
|
||||
@@ -977,25 +977,22 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass - deallocate any phi inputs which are now dead.
|
||||
// Second pass - deallocate all in-register phi inputs.
|
||||
for i, v := range phis {
|
||||
if !s.values[v.ID].needReg {
|
||||
continue
|
||||
}
|
||||
a := v.Args[idx]
|
||||
if !regValLiveSet.contains(a.ID) {
|
||||
// Input is dead beyond the phi, deallocate
|
||||
// anywhere else it might live.
|
||||
s.freeRegs(s.values[a.ID].regs)
|
||||
} else {
|
||||
// Input is still live.
|
||||
r := phiRegs[i]
|
||||
if r == noRegister {
|
||||
continue
|
||||
}
|
||||
if regValLiveSet.contains(a.ID) {
|
||||
// Input value is still live (it is used by something other than Phi).
|
||||
// Try to move it around before kicking out, if there is a free register.
|
||||
// We generate a Copy in the predecessor block and record it. It will be
|
||||
// deleted if never used.
|
||||
r := phiRegs[i]
|
||||
if r == noRegister {
|
||||
continue
|
||||
}
|
||||
// deleted later if never used.
|
||||
//
|
||||
// Pick a free register. At this point some registers used in the predecessor
|
||||
// block may have been deallocated. Those are the ones used for Phis. Exclude
|
||||
// them (and they are not going to be helpful anyway).
|
||||
@@ -1011,8 +1008,8 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||
s.assignReg(r2, a, c)
|
||||
s.endRegs[p.ID] = append(s.endRegs[p.ID], endReg{r2, a, c})
|
||||
}
|
||||
s.freeReg(r)
|
||||
}
|
||||
s.freeReg(r)
|
||||
}
|
||||
|
||||
// Copy phi ops into new schedule.
|
||||
@@ -1852,6 +1849,11 @@ func (s *regAllocState) shuffle(stacklive [][]ID) {
|
||||
e.process()
|
||||
}
|
||||
}
|
||||
|
||||
if s.f.pass.debug > regDebug {
|
||||
fmt.Printf("post shuffle %s\n", s.f.Name)
|
||||
fmt.Println(s.f.String())
|
||||
}
|
||||
}
|
||||
|
||||
type edgeState struct {
|
||||
|
||||
@@ -724,6 +724,40 @@ var tests = []test{
|
||||
},
|
||||
},
|
||||
|
||||
// Merging comments with -src.
|
||||
{
|
||||
"merge comments with -src A",
|
||||
[]string{"-src", p + "/merge", `A`},
|
||||
[]string{
|
||||
`A doc`,
|
||||
`func A`,
|
||||
`A comment`,
|
||||
},
|
||||
[]string{
|
||||
`Package A doc`,
|
||||
`Package B doc`,
|
||||
`B doc`,
|
||||
`B comment`,
|
||||
`B doc`,
|
||||
},
|
||||
},
|
||||
{
|
||||
"merge comments with -src B",
|
||||
[]string{"-src", p + "/merge", `B`},
|
||||
[]string{
|
||||
`B doc`,
|
||||
`func B`,
|
||||
`B comment`,
|
||||
},
|
||||
[]string{
|
||||
`Package A doc`,
|
||||
`Package B doc`,
|
||||
`A doc`,
|
||||
`A comment`,
|
||||
`A doc`,
|
||||
},
|
||||
},
|
||||
|
||||
// No dups with -u. Issue 21797.
|
||||
{
|
||||
"case matching on, no dups",
|
||||
|
||||
7
src/cmd/doc/testdata/merge/aa.go
vendored
Normal file
7
src/cmd/doc/testdata/merge/aa.go
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// Package comment A.
|
||||
package merge
|
||||
|
||||
// A doc.
|
||||
func A() {
|
||||
// A comment.
|
||||
}
|
||||
7
src/cmd/doc/testdata/merge/bb.go
vendored
Normal file
7
src/cmd/doc/testdata/merge/bb.go
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// Package comment B.
|
||||
package merge
|
||||
|
||||
// B doc.
|
||||
func B() {
|
||||
// B comment.
|
||||
}
|
||||
@@ -9,5 +9,5 @@ require (
|
||||
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d
|
||||
golang.org/x/mod v0.2.0
|
||||
golang.org/x/sys v0.0.0-20200131233351-9e5cf931a04b // indirect
|
||||
golang.org/x/tools v0.0.0-20200131233409-575de47986ce
|
||||
golang.org/x/tools v0.0.0-20200602230032-c00d67ef29d0
|
||||
)
|
||||
|
||||
@@ -22,8 +22,8 @@ golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200131233409-575de47986ce h1:Uglradbb4KfUWaYasZhlsDsGRwHHvRsHoNAEONef0W8=
|
||||
golang.org/x/tools v0.0.0-20200131233409-575de47986ce/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200602230032-c00d67ef29d0 h1:6txNFSnY+tteYoO+hf01EpdYcYZiurdC9MDIrcUzEu4=
|
||||
golang.org/x/tools v0.0.0-20200602230032-c00d67ef29d0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -2506,13 +2506,21 @@
|
||||
// The "go get" command remains permitted to update go.mod even with -mod=readonly,
|
||||
// and the "go mod" commands do not take the -mod flag (or any other build flags).
|
||||
//
|
||||
// If invoked with -mod=vendor, the go command assumes that the vendor
|
||||
// directory holds the correct copies of dependencies and ignores
|
||||
// the dependency descriptions in go.mod.
|
||||
// If invoked with -mod=vendor, the go command loads packages from the main
|
||||
// module's vendor directory instead of downloading modules to and loading packages
|
||||
// from the module cache. The go command assumes the vendor directory holds
|
||||
// correct copies of dependencies, and it does not compute the set of required
|
||||
// module versions from go.mod files. However, the go command does check that
|
||||
// vendor/modules.txt (generated by 'go mod vendor') contains metadata consistent
|
||||
// with go.mod.
|
||||
//
|
||||
// If invoked with -mod=mod, the go command loads modules from the module cache
|
||||
// even if there is a vendor directory present.
|
||||
//
|
||||
// If the go command is not invoked with a -mod flag and the vendor directory
|
||||
// is present and the "go" version in go.mod is 1.14 or higher, the go command
|
||||
// will act as if it were invoked with -mod=vendor.
|
||||
//
|
||||
// Pseudo-versions
|
||||
//
|
||||
// The go.mod file and the go command more generally use semantic versions as
|
||||
@@ -2710,22 +2718,28 @@
|
||||
//
|
||||
// Modules and vendoring
|
||||
//
|
||||
// When using modules, the go command completely ignores vendor directories.
|
||||
// When using modules, the go command typically satisfies dependencies by
|
||||
// downloading modules from their sources and using those downloaded copies
|
||||
// (after verification, as described in the previous section). Vendoring may
|
||||
// be used to allow interoperation with older versions of Go, or to ensure
|
||||
// that all files used for a build are stored together in a single file tree.
|
||||
//
|
||||
// By default, the go command satisfies dependencies by downloading modules
|
||||
// from their sources and using those downloaded copies (after verification,
|
||||
// as described in the previous section). To allow interoperation with older
|
||||
// versions of Go, or to ensure that all files used for a build are stored
|
||||
// together in a single file tree, 'go mod vendor' creates a directory named
|
||||
// vendor in the root directory of the main module and stores there all the
|
||||
// packages from dependency modules that are needed to support builds and
|
||||
// tests of packages in the main module.
|
||||
// The command 'go mod vendor' constructs a directory named vendor in the main
|
||||
// module's root directory that contains copies of all packages needed to support
|
||||
// builds and tests of packages in the main module. 'go mod vendor' also
|
||||
// creates the file vendor/modules.txt that contains metadata about vendored
|
||||
// packages and module versions. This file should be kept consistent with go.mod:
|
||||
// when vendoring is used, 'go mod vendor' should be run after go.mod is updated.
|
||||
//
|
||||
// To build using the main module's top-level vendor directory to satisfy
|
||||
// dependencies (disabling use of the usual network sources and local
|
||||
// caches), use 'go build -mod=vendor'. Note that only the main module's
|
||||
// top-level vendor directory is used; vendor directories in other locations
|
||||
// are still ignored.
|
||||
// If the vendor directory is present in the main module's root directory, it will
|
||||
// be used automatically if the "go" version in the main module's go.mod file is
|
||||
// 1.14 or higher. Build commands like 'go build' and 'go test' will load packages
|
||||
// from the vendor directory instead of accessing the network or the local module
|
||||
// cache. To explicitly enable vendoring, invoke the go command with the flag
|
||||
// -mod=vendor. To disable vendoring, use the flag -mod=mod.
|
||||
//
|
||||
// Unlike vendoring in GOPATH, the go command ignores vendor directories in
|
||||
// locations other than the main module's root directory.
|
||||
//
|
||||
//
|
||||
// Module authentication using go.sum
|
||||
|
||||
@@ -3955,45 +3955,6 @@ func TestCgoFlagContainsSpace(t *testing.T) {
|
||||
tg.grepStderrNot(`"-L[^"]+c flags".*"-L[^"]+c flags"`, "found too many quoted ld flags")
|
||||
}
|
||||
|
||||
// Issue 9737: verify that GOARM and GO386 affect the computed build ID.
|
||||
func TestBuildIDContainsArchModeEnv(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping in short mode")
|
||||
}
|
||||
|
||||
var tg *testgoData
|
||||
testWith := func(before, after func()) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
tg = testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.tempFile("src/mycmd/x.go", `package main
|
||||
func main() {}`)
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
|
||||
tg.cd(tg.path("src/mycmd"))
|
||||
tg.setenv("GOOS", "linux")
|
||||
before()
|
||||
tg.run("install", "mycmd")
|
||||
after()
|
||||
tg.wantStale("mycmd", "stale dependency", "should be stale after environment variable change")
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("386", testWith(func() {
|
||||
tg.setenv("GOARCH", "386")
|
||||
tg.setenv("GO386", "387")
|
||||
}, func() {
|
||||
tg.setenv("GO386", "sse2")
|
||||
}))
|
||||
|
||||
t.Run("arm", testWith(func() {
|
||||
tg.setenv("GOARCH", "arm")
|
||||
tg.setenv("GOARM", "5")
|
||||
}, func() {
|
||||
tg.setenv("GOARM", "7")
|
||||
}))
|
||||
}
|
||||
|
||||
func TestListTests(t *testing.T) {
|
||||
tooSlow(t)
|
||||
var tg *testgoData
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/str"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
@@ -438,7 +439,7 @@ func (g *Generator) exec(words []string) {
|
||||
cmd.Stderr = os.Stderr
|
||||
// Run the command in the package directory.
|
||||
cmd.Dir = g.dir
|
||||
cmd.Env = append(cfg.OrigEnv, g.env...)
|
||||
cmd.Env = str.StringList(cfg.OrigEnv, g.env)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
g.errorf("running %q: %s", words[0], err)
|
||||
|
||||
@@ -6,6 +6,7 @@ package modcmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -67,12 +68,10 @@ func verifyMod(mod module.Version) bool {
|
||||
_, zipErr = os.Stat(zip)
|
||||
}
|
||||
dir, dirErr := modfetch.DownloadDir(mod)
|
||||
if dirErr == nil {
|
||||
_, dirErr = os.Stat(dir)
|
||||
}
|
||||
data, err := ioutil.ReadFile(zip + "hash")
|
||||
if err != nil {
|
||||
if zipErr != nil && os.IsNotExist(zipErr) && dirErr != nil && os.IsNotExist(dirErr) {
|
||||
if zipErr != nil && errors.Is(zipErr, os.ErrNotExist) &&
|
||||
dirErr != nil && errors.Is(dirErr, os.ErrNotExist) {
|
||||
// Nothing downloaded yet. Nothing to verify.
|
||||
return true
|
||||
}
|
||||
@@ -81,7 +80,7 @@ func verifyMod(mod module.Version) bool {
|
||||
}
|
||||
h := string(bytes.TrimSpace(data))
|
||||
|
||||
if zipErr != nil && os.IsNotExist(zipErr) {
|
||||
if zipErr != nil && errors.Is(zipErr, os.ErrNotExist) {
|
||||
// ok
|
||||
} else {
|
||||
hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
|
||||
@@ -93,7 +92,7 @@ func verifyMod(mod module.Version) bool {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
if dirErr != nil && os.IsNotExist(dirErr) {
|
||||
if dirErr != nil && errors.Is(dirErr, os.ErrNotExist) {
|
||||
// ok
|
||||
} else {
|
||||
hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash)
|
||||
|
||||
@@ -7,6 +7,7 @@ package modfetch
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -56,8 +57,11 @@ func CachePath(m module.Version, suffix string) (string, error) {
|
||||
return filepath.Join(dir, encVer+"."+suffix), nil
|
||||
}
|
||||
|
||||
// DownloadDir returns the directory to which m should be downloaded.
|
||||
// Note that the directory may not yet exist.
|
||||
// DownloadDir returns the directory to which m should have been downloaded.
|
||||
// An error will be returned if the module path or version cannot be escaped.
|
||||
// An error satisfying errors.Is(err, os.ErrNotExist) will be returned
|
||||
// along with the directory if the directory does not exist or if the directory
|
||||
// is not completely populated.
|
||||
func DownloadDir(m module.Version) (string, error) {
|
||||
if PkgMod == "" {
|
||||
return "", fmt.Errorf("internal error: modfetch.PkgMod not set")
|
||||
@@ -76,9 +80,39 @@ func DownloadDir(m module.Version) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(PkgMod, enc+"@"+encVer), nil
|
||||
|
||||
dir := filepath.Join(PkgMod, enc+"@"+encVer)
|
||||
if fi, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
return dir, err
|
||||
} else if err != nil {
|
||||
return dir, &DownloadDirPartialError{dir, err}
|
||||
} else if !fi.IsDir() {
|
||||
return dir, &DownloadDirPartialError{dir, errors.New("not a directory")}
|
||||
}
|
||||
partialPath, err := CachePath(m, "partial")
|
||||
if err != nil {
|
||||
return dir, err
|
||||
}
|
||||
if _, err := os.Stat(partialPath); err == nil {
|
||||
return dir, &DownloadDirPartialError{dir, errors.New("not completely extracted")}
|
||||
} else if !os.IsNotExist(err) {
|
||||
return dir, err
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// DownloadDirPartialError is returned by DownloadDir if a module directory
|
||||
// exists but was not completely populated.
|
||||
//
|
||||
// DownloadDirPartialError is equivalent to os.ErrNotExist.
|
||||
type DownloadDirPartialError struct {
|
||||
Dir string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *DownloadDirPartialError) Error() string { return fmt.Sprintf("%s: %v", e.Dir, e.Err) }
|
||||
func (e *DownloadDirPartialError) Is(err error) bool { return err == os.ErrNotExist }
|
||||
|
||||
// lockVersion locks a file within the module cache that guards the downloading
|
||||
// and extraction of the zipfile for the given module version.
|
||||
func lockVersion(mod module.Version) (unlock func(), err error) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"cmd/go/internal/lockedfile"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/renameio"
|
||||
"cmd/go/internal/robustio"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/sumdb/dirhash"
|
||||
@@ -45,24 +46,27 @@ func Download(mod module.Version) (dir string, err error) {
|
||||
err error
|
||||
}
|
||||
c := downloadCache.Do(mod, func() interface{} {
|
||||
dir, err := DownloadDir(mod)
|
||||
dir, err := download(mod)
|
||||
if err != nil {
|
||||
return cached{"", err}
|
||||
}
|
||||
if err := download(mod, dir); err != nil {
|
||||
return cached{"", err}
|
||||
}
|
||||
checkMod(mod)
|
||||
return cached{dir, nil}
|
||||
}).(cached)
|
||||
return c.dir, c.err
|
||||
}
|
||||
|
||||
func download(mod module.Version, dir string) (err error) {
|
||||
// If the directory exists, the module has already been extracted.
|
||||
fi, err := os.Stat(dir)
|
||||
if err == nil && fi.IsDir() {
|
||||
return nil
|
||||
func download(mod module.Version) (dir string, err error) {
|
||||
// If the directory exists, and no .partial file exists,
|
||||
// the module has already been completely extracted.
|
||||
// .partial files may be created when future versions of cmd/go
|
||||
// extract module zip directories in place instead of extracting
|
||||
// to a random temporary directory and renaming.
|
||||
dir, err = DownloadDir(mod)
|
||||
if err == nil {
|
||||
return dir, nil
|
||||
} else if dir == "" || !errors.Is(err, os.ErrNotExist) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// To avoid cluttering the cache with extraneous files,
|
||||
@@ -70,22 +74,24 @@ func download(mod module.Version, dir string) (err error) {
|
||||
// Invoke DownloadZip before locking the file.
|
||||
zipfile, err := DownloadZip(mod)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
unlock, err := lockVersion(mod)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
// Check whether the directory was populated while we were waiting on the lock.
|
||||
fi, err = os.Stat(dir)
|
||||
if err == nil && fi.IsDir() {
|
||||
return nil
|
||||
_, dirErr := DownloadDir(mod)
|
||||
if dirErr == nil {
|
||||
return dir, nil
|
||||
}
|
||||
_, dirExists := dirErr.(*DownloadDirPartialError)
|
||||
|
||||
// Clean up any remaining temporary directories from previous runs.
|
||||
// Clean up any remaining temporary directories from previous runs, as well
|
||||
// as partially extracted diectories created by future versions of cmd/go.
|
||||
// This is only safe to do because the lock file ensures that their writers
|
||||
// are no longer active.
|
||||
parentDir := filepath.Dir(dir)
|
||||
@@ -95,6 +101,19 @@ func download(mod module.Version, dir string) (err error) {
|
||||
RemoveAll(path) // best effort
|
||||
}
|
||||
}
|
||||
if dirExists {
|
||||
if err := RemoveAll(dir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
partialPath, err := CachePath(mod, "partial")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.Remove(partialPath); err != nil && !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Extract the zip file to a temporary directory, then rename it to the
|
||||
// final path. That way, we can use the existence of the source directory to
|
||||
@@ -102,11 +121,11 @@ func download(mod module.Version, dir string) (err error) {
|
||||
// the entire directory (e.g. as an attempt to prune out file corruption)
|
||||
// the module cache will still be left in a recoverable state.
|
||||
if err := os.MkdirAll(parentDir, 0777); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
tmpDir, err := ioutil.TempDir(parentDir, tmpPrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
@@ -116,11 +135,11 @@ func download(mod module.Version, dir string) (err error) {
|
||||
|
||||
if err := modzip.Unzip(tmpDir, mod, zipfile); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "-> %s\n", err)
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := os.Rename(tmpDir, dir); err != nil {
|
||||
return err
|
||||
if err := robustio.Rename(tmpDir, dir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !cfg.ModCacheRW {
|
||||
@@ -128,7 +147,7 @@ func download(mod module.Version, dir string) (err error) {
|
||||
// os.Rename was observed to fail for read-only directories on macOS.
|
||||
makeDirsReadOnly(dir)
|
||||
}
|
||||
return nil
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
var downloadZipCache par.Cache
|
||||
|
||||
@@ -148,9 +148,7 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
|
||||
}
|
||||
dir, err := modfetch.DownloadDir(mod)
|
||||
if err == nil {
|
||||
if info, err := os.Stat(dir); err == nil && info.IsDir() {
|
||||
m.Dir = dir
|
||||
}
|
||||
m.Dir = dir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,13 +176,21 @@ not need updates, such as in a continuous integration and testing system.
|
||||
The "go get" command remains permitted to update go.mod even with -mod=readonly,
|
||||
and the "go mod" commands do not take the -mod flag (or any other build flags).
|
||||
|
||||
If invoked with -mod=vendor, the go command assumes that the vendor
|
||||
directory holds the correct copies of dependencies and ignores
|
||||
the dependency descriptions in go.mod.
|
||||
If invoked with -mod=vendor, the go command loads packages from the main
|
||||
module's vendor directory instead of downloading modules to and loading packages
|
||||
from the module cache. The go command assumes the vendor directory holds
|
||||
correct copies of dependencies, and it does not compute the set of required
|
||||
module versions from go.mod files. However, the go command does check that
|
||||
vendor/modules.txt (generated by 'go mod vendor') contains metadata consistent
|
||||
with go.mod.
|
||||
|
||||
If invoked with -mod=mod, the go command loads modules from the module cache
|
||||
even if there is a vendor directory present.
|
||||
|
||||
If the go command is not invoked with a -mod flag and the vendor directory
|
||||
is present and the "go" version in go.mod is 1.14 or higher, the go command
|
||||
will act as if it were invoked with -mod=vendor.
|
||||
|
||||
Pseudo-versions
|
||||
|
||||
The go.mod file and the go command more generally use semantic versions as
|
||||
@@ -380,22 +388,28 @@ the format of the cached downloaded packages.
|
||||
|
||||
Modules and vendoring
|
||||
|
||||
When using modules, the go command completely ignores vendor directories.
|
||||
When using modules, the go command typically satisfies dependencies by
|
||||
downloading modules from their sources and using those downloaded copies
|
||||
(after verification, as described in the previous section). Vendoring may
|
||||
be used to allow interoperation with older versions of Go, or to ensure
|
||||
that all files used for a build are stored together in a single file tree.
|
||||
|
||||
By default, the go command satisfies dependencies by downloading modules
|
||||
from their sources and using those downloaded copies (after verification,
|
||||
as described in the previous section). To allow interoperation with older
|
||||
versions of Go, or to ensure that all files used for a build are stored
|
||||
together in a single file tree, 'go mod vendor' creates a directory named
|
||||
vendor in the root directory of the main module and stores there all the
|
||||
packages from dependency modules that are needed to support builds and
|
||||
tests of packages in the main module.
|
||||
The command 'go mod vendor' constructs a directory named vendor in the main
|
||||
module's root directory that contains copies of all packages needed to support
|
||||
builds and tests of packages in the main module. 'go mod vendor' also
|
||||
creates the file vendor/modules.txt that contains metadata about vendored
|
||||
packages and module versions. This file should be kept consistent with go.mod:
|
||||
when vendoring is used, 'go mod vendor' should be run after go.mod is updated.
|
||||
|
||||
To build using the main module's top-level vendor directory to satisfy
|
||||
dependencies (disabling use of the usual network sources and local
|
||||
caches), use 'go build -mod=vendor'. Note that only the main module's
|
||||
top-level vendor directory is used; vendor directories in other locations
|
||||
are still ignored.
|
||||
If the vendor directory is present in the main module's root directory, it will
|
||||
be used automatically if the "go" version in the main module's go.mod file is
|
||||
1.14 or higher. Build commands like 'go build' and 'go test' will load packages
|
||||
from the vendor directory instead of accessing the network or the local module
|
||||
cache. To explicitly enable vendoring, invoke the go command with the flag
|
||||
-mod=vendor. To disable vendoring, use the flag -mod=mod.
|
||||
|
||||
Unlike vendoring in GOPATH, the go command ignores vendor directories in
|
||||
locations other than the main module's root directory.
|
||||
`,
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const arbitraryTimeout = 500 * time.Millisecond
|
||||
const arbitraryTimeout = 2000 * time.Millisecond
|
||||
|
||||
// retry retries ephemeral errors from f up to an arbitrary timeout
|
||||
// to work around filesystem flakiness on Windows and Darwin.
|
||||
|
||||
@@ -1142,7 +1142,7 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
|
||||
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Dir = a.Package.Dir
|
||||
cmd.Env = base.EnvForDir(cmd.Dir, cfg.OrigEnv)
|
||||
cmd.Env = base.EnvForDir(cmd.Dir, cfg.OrigEnv[:len(cfg.OrigEnv):len(cfg.OrigEnv)])
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stdout
|
||||
|
||||
@@ -1224,6 +1224,14 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
|
||||
if len(out) == 0 {
|
||||
fmt.Fprintf(cmd.Stdout, "%s\n", err)
|
||||
}
|
||||
// NOTE(golang.org/issue/37555): test2json reports that a test passes
|
||||
// unless "FAIL" is printed at the beginning of a line. The test may not
|
||||
// actually print that if it panics, exits, or terminates abnormally,
|
||||
// so we print it here. We can't always check whether it was printed
|
||||
// because some tests need stdout to be a terminal (golang.org/issue/34791),
|
||||
// not a pipe.
|
||||
// TODO(golang.org/issue/29062): tests that exit with status 0 without
|
||||
// printing a final result should fail.
|
||||
fmt.Fprintf(cmd.Stdout, "FAIL\t%s\t%s\n", a.Package.ImportPath, t)
|
||||
}
|
||||
|
||||
|
||||
@@ -213,6 +213,9 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
|
||||
} else if cfg.BuildTrimpath && p.Module != nil {
|
||||
fmt.Fprintf(h, "module %s@%s\n", p.Module.Path, p.Module.Version)
|
||||
}
|
||||
if p.Module != nil {
|
||||
fmt.Fprintf(h, "go %s\n", p.Module.GoVersion)
|
||||
}
|
||||
fmt.Fprintf(h, "goos %s goarch %s\n", cfg.Goos, cfg.Goarch)
|
||||
fmt.Fprintf(h, "import %q\n", p.ImportPath)
|
||||
fmt.Fprintf(h, "omitdebug %v standard %v local %v prefix %q\n", p.Internal.OmitDebug, p.Standard, p.Internal.Local, p.Internal.LocalPrefix)
|
||||
|
||||
27
src/cmd/go/testdata/script/build_cache_arch_mode.txt
vendored
Normal file
27
src/cmd/go/testdata/script/build_cache_arch_mode.txt
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# Issue 9737: verify that GOARM and GO386 affect the computed build ID
|
||||
|
||||
[short] skip
|
||||
|
||||
# 386
|
||||
cd $GOPATH/src/mycmd
|
||||
env GOOS=linux
|
||||
env GOARCH=386
|
||||
env GO386=387
|
||||
go install mycmd
|
||||
env GO386=sse2
|
||||
stale mycmd
|
||||
|
||||
# arm
|
||||
cd $GOPATH/src/mycmd
|
||||
env GOOS=linux
|
||||
env GOARCH=arm
|
||||
env GOARM=5
|
||||
go install mycmd
|
||||
env GOARM=7
|
||||
stale mycmd
|
||||
|
||||
|
||||
-- mycmd/x.go --
|
||||
package main
|
||||
|
||||
func main() {}
|
||||
54
src/cmd/go/testdata/script/mod_download_partial.txt
vendored
Normal file
54
src/cmd/go/testdata/script/mod_download_partial.txt
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
# Download a module
|
||||
go mod download -modcacherw rsc.io/quote
|
||||
exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
|
||||
|
||||
# 'go mod verify' should fail if we delete a file.
|
||||
go mod verify
|
||||
rm $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
|
||||
! go mod verify
|
||||
|
||||
# Create a .partial file to simulate an failure extracting the zip file.
|
||||
cp empty $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.partial
|
||||
|
||||
# 'go mod verify' should not fail, since the module hasn't been completely
|
||||
# ingested into the cache.
|
||||
go mod verify
|
||||
|
||||
# 'go list' should not load packages from the directory.
|
||||
# NOTE: the message "directory $dir outside available modules" is reported
|
||||
# for directories not in the main module, active modules in the module cache,
|
||||
# or local replacements. In this case, the directory is in the right place,
|
||||
# but it's incomplete, so 'go list' acts as if it's not an active module.
|
||||
! go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.2
|
||||
stderr 'outside available modules'
|
||||
|
||||
# 'go list -m' should not print the directory.
|
||||
go list -m -f '{{.Dir}}' rsc.io/quote
|
||||
! stdout .
|
||||
|
||||
# 'go mod download' should re-extract the module and remove the .partial file.
|
||||
go mod download -modcacherw rsc.io/quote
|
||||
! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.partial
|
||||
exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
|
||||
|
||||
# 'go list' should succeed.
|
||||
go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.2
|
||||
stdout '^rsc.io/quote$'
|
||||
|
||||
# 'go list -m' should print the directory.
|
||||
go list -m -f '{{.Dir}}' rsc.io/quote
|
||||
stdout 'pkg[/\\]mod[/\\]rsc.io[/\\]quote@v1.5.2'
|
||||
|
||||
# go mod verify should fail if we delete a file.
|
||||
go mod verify
|
||||
rm $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
|
||||
! go mod verify
|
||||
|
||||
-- go.mod --
|
||||
module m
|
||||
|
||||
go 1.14
|
||||
|
||||
require rsc.io/quote v1.5.2
|
||||
|
||||
-- empty --
|
||||
7
src/cmd/go/testdata/script/mod_edit_go.txt
vendored
7
src/cmd/go/testdata/script/mod_edit_go.txt
vendored
@@ -7,6 +7,13 @@ go mod edit -go=1.9
|
||||
grep 'go 1.9' go.mod
|
||||
go build
|
||||
|
||||
# Reverting the version should force a rebuild and error instead of using
|
||||
# the cached 1.9 build. (https://golang.org/issue/37804)
|
||||
go mod edit -go=1.8
|
||||
! go build
|
||||
stderr 'type aliases only supported as of'
|
||||
|
||||
|
||||
-- go.mod --
|
||||
module m
|
||||
go 1.8
|
||||
|
||||
32
src/cmd/go/testdata/script/test_benchmark_chatty_fail.txt
vendored
Normal file
32
src/cmd/go/testdata/script/test_benchmark_chatty_fail.txt
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Run chatty tests. Assert on CONT lines.
|
||||
! go test chatty_test.go -v -bench . chatty_bench
|
||||
|
||||
# Sanity check that output occurs.
|
||||
stdout -count=2 'this is sub-0'
|
||||
stdout -count=2 'this is sub-1'
|
||||
stdout -count=2 'this is sub-2'
|
||||
stdout -count=1 'error from sub-0'
|
||||
stdout -count=1 'error from sub-1'
|
||||
stdout -count=1 'error from sub-2'
|
||||
|
||||
# Benchmarks should not print CONT.
|
||||
! stdout CONT
|
||||
|
||||
-- chatty_test.go --
|
||||
package chatty_bench
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func BenchmarkChatty(b *testing.B) {
|
||||
for i := 0; i < 3; i++ {
|
||||
b.Run(fmt.Sprintf("sub-%d", i), func(b *testing.B) {
|
||||
for j := 0; j < 2; j++ {
|
||||
b.Logf("this is sub-%d", i)
|
||||
}
|
||||
b.Errorf("error from sub-%d", i)
|
||||
})
|
||||
}
|
||||
}
|
||||
29
src/cmd/go/testdata/script/test_benchmark_chatty_success.txt
vendored
Normal file
29
src/cmd/go/testdata/script/test_benchmark_chatty_success.txt
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# Run chatty tests. Assert on CONT lines.
|
||||
go test chatty_test.go -v -bench . chatty_bench
|
||||
|
||||
# Sanity check that output happens. We don't provide -count because the amount
|
||||
# of output is variable.
|
||||
stdout 'this is sub-0'
|
||||
stdout 'this is sub-1'
|
||||
stdout 'this is sub-2'
|
||||
|
||||
# Benchmarks should not print CONT.
|
||||
! stdout CONT
|
||||
|
||||
-- chatty_test.go --
|
||||
package chatty_bench
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func BenchmarkChatty(b *testing.B) {
|
||||
for i := 0; i < 3; i++ {
|
||||
b.Run(fmt.Sprintf("sub-%d", i), func(b *testing.B) {
|
||||
for j := 0; j < 2; j++ {
|
||||
b.Logf("this is sub-%d", i)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
32
src/cmd/go/testdata/script/test_chatty_fail.txt
vendored
Normal file
32
src/cmd/go/testdata/script/test_chatty_fail.txt
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Run chatty tests. Assert on CONT lines.
|
||||
! go test chatty_test.go -v
|
||||
|
||||
# Sanity check that output occurs.
|
||||
stdout -count=2 'this is sub-0'
|
||||
stdout -count=2 'this is sub-1'
|
||||
stdout -count=2 'this is sub-2'
|
||||
stdout -count=1 'error from sub-0'
|
||||
stdout -count=1 'error from sub-1'
|
||||
stdout -count=1 'error from sub-2'
|
||||
|
||||
# Non-parallel tests should not print CONT.
|
||||
! stdout CONT
|
||||
|
||||
-- chatty_test.go --
|
||||
package chatty_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func TestChatty(t *testing.T) {
|
||||
for i := 0; i < 3; i++ {
|
||||
t.Run(fmt.Sprintf("sub-%d", i), func(t *testing.T) {
|
||||
for j := 0; j < 2; j++ {
|
||||
t.Logf("this is sub-%d", i)
|
||||
}
|
||||
t.Errorf("error from sub-%d", i)
|
||||
})
|
||||
}
|
||||
}
|
||||
58
src/cmd/go/testdata/script/test_chatty_parallel_fail.txt
vendored
Normal file
58
src/cmd/go/testdata/script/test_chatty_parallel_fail.txt
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
# Run parallel chatty tests. Assert on CONT lines. This test makes sure that
|
||||
# multiple parallel outputs have the appropriate CONT lines between them.
|
||||
! go test -parallel 3 chatty_parallel_test.go -v
|
||||
|
||||
stdout -count=1 '^=== CONT TestChattyParallel/sub-0\n chatty_parallel_test.go:38: error from sub-0$'
|
||||
stdout -count=1 '^=== CONT TestChattyParallel/sub-1\n chatty_parallel_test.go:38: error from sub-1$'
|
||||
stdout -count=1 '^=== CONT TestChattyParallel/sub-2\n chatty_parallel_test.go:38: error from sub-2$'
|
||||
|
||||
# Run parallel chatty tests with -json. Assert on CONT lines as above - make
|
||||
# sure there are CONT lines before each output line.
|
||||
! go test -json -parallel 3 chatty_parallel_test.go -v
|
||||
stdout -count=1 '{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-0","Output":"=== CONT TestChattyParallel/sub-0\\n"}\n{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-0","Output":" chatty_parallel_test.go:38: error from sub-0\\n"}'
|
||||
stdout -count=1 '{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-1","Output":"=== CONT TestChattyParallel/sub-1\\n"}\n{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-1","Output":" chatty_parallel_test.go:38: error from sub-1\\n"}'
|
||||
stdout -count=1 '{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-2","Output":"=== CONT TestChattyParallel/sub-2\\n"}\n{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-2","Output":" chatty_parallel_test.go:38: error from sub-2\\n"}'
|
||||
|
||||
-- chatty_parallel_test.go --
|
||||
package chatty_paralell_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"fmt"
|
||||
"flag"
|
||||
)
|
||||
|
||||
// This test ensures the the order of CONT lines in parallel chatty tests.
|
||||
func TestChattyParallel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// The number of concurrent tests running. This is closely tied to the
|
||||
// -parallel test flag, so we grab it from the flag rather than setting it
|
||||
// to some constant.
|
||||
parallel := flag.Lookup("test.parallel").Value.(flag.Getter).Get().(int)
|
||||
|
||||
// ready is a synchronization mechanism that causes subtests to execute
|
||||
// round robin.
|
||||
ready := make([]chan bool, parallel)
|
||||
for i := range ready {
|
||||
ready[i] = make(chan bool, 1)
|
||||
}
|
||||
ready[0] <- true
|
||||
|
||||
for i := range ready {
|
||||
i := i
|
||||
t.Run(fmt.Sprintf("sub-%d", i), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Some basic log output to precede the failures.
|
||||
<-ready[i]
|
||||
t.Logf("this is sub-%d", i)
|
||||
ready[(i+1)%len(ready)] <- true
|
||||
|
||||
// The actual failure messages we care about.
|
||||
<-ready[i]
|
||||
t.Errorf("error from sub-%d", i)
|
||||
ready[(i+1)%len(ready)] <- true
|
||||
})
|
||||
}
|
||||
}
|
||||
52
src/cmd/go/testdata/script/test_chatty_parallel_success.txt
vendored
Normal file
52
src/cmd/go/testdata/script/test_chatty_parallel_success.txt
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# Run parallel chatty tests. Assert on CONT lines. This test makes sure that
|
||||
# multiple parallel outputs have the appropriate CONT lines between them.
|
||||
go test -parallel 3 chatty_parallel_test.go -v
|
||||
stdout -count=2 '^=== CONT TestChattyParallel/sub-0\n chatty_parallel_test.go:32: this is sub-0$'
|
||||
stdout -count=2 '^=== CONT TestChattyParallel/sub-1\n chatty_parallel_test.go:32: this is sub-1$'
|
||||
stdout -count=2 '^=== CONT TestChattyParallel/sub-2\n chatty_parallel_test.go:32: this is sub-2$'
|
||||
|
||||
# Run parallel chatty tests with -json. Assert on CONT lines as above - make
|
||||
# sure there are CONT lines before each output line.
|
||||
go test -json -parallel 3 chatty_parallel_test.go -v
|
||||
stdout -count=2 '{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-0","Output":"=== CONT TestChattyParallel/sub-0\\n"}\n{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-0","Output":" chatty_parallel_test.go:32: this is sub-0\\n"}'
|
||||
stdout -count=2 '{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-1","Output":"=== CONT TestChattyParallel/sub-1\\n"}\n{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-1","Output":" chatty_parallel_test.go:32: this is sub-1\\n"}'
|
||||
stdout -count=2 '{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-2","Output":"=== CONT TestChattyParallel/sub-2\\n"}\n{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-2","Output":" chatty_parallel_test.go:32: this is sub-2\\n"}'
|
||||
|
||||
-- chatty_parallel_test.go --
|
||||
package chatty_paralell_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"fmt"
|
||||
"flag"
|
||||
)
|
||||
|
||||
// This test ensures the the order of CONT lines in parallel chatty tests.
|
||||
func TestChattyParallel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// The number of concurrent tests running. This is closely tied to the
|
||||
// -parallel test flag, so we grab it from the flag rather than setting it
|
||||
// to some constant.
|
||||
parallel := flag.Lookup("test.parallel").Value.(flag.Getter).Get().(int)
|
||||
|
||||
// ready is a synchronization mechanism that causes subtests to execute
|
||||
// round robin.
|
||||
ready := make([]chan bool, parallel)
|
||||
for i := range ready {
|
||||
ready[i] = make(chan bool, 1)
|
||||
}
|
||||
ready[0] <- true
|
||||
|
||||
for i := range ready {
|
||||
i := i
|
||||
t.Run(fmt.Sprintf("sub-%d", i), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
for j := 0; j < 2; j++ {
|
||||
<-ready[i]
|
||||
t.Logf("this is sub-%d", i)
|
||||
ready[(i+1)%len(ready)] <- true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
39
src/cmd/go/testdata/script/test_chatty_parallel_success_sleepy.txt
vendored
Normal file
39
src/cmd/go/testdata/script/test_chatty_parallel_success_sleepy.txt
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# Run parallel chatty tests. Assert on CONT lines. This test makes sure that
|
||||
# multiple parallel outputs have the appropriate CONT lines between them.
|
||||
go test -parallel 3 chatty_parallel_test.go -v
|
||||
|
||||
stdout '--- PASS: TestFast \([0-9.]{4}s\)\n=== CONT TestSlow\n chatty_parallel_test.go:31: this is the second TestSlow log\n--- PASS: TestSlow \([0-9.]{4}s\)'
|
||||
|
||||
-- chatty_parallel_test.go --
|
||||
package chatty_paralell_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
run = make(chan struct{})
|
||||
afterFirstLog = make(chan struct{})
|
||||
afterPass = make(chan struct{})
|
||||
)
|
||||
|
||||
func TestFast(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
<-afterFirstLog
|
||||
t.Cleanup(func() {
|
||||
close(afterPass)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSlow(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Logf("this is the first TestSlow log")
|
||||
close(afterFirstLog)
|
||||
|
||||
<-afterPass
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
t.Logf("this is the second TestSlow log")
|
||||
}
|
||||
27
src/cmd/go/testdata/script/test_chatty_success.txt
vendored
Normal file
27
src/cmd/go/testdata/script/test_chatty_success.txt
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# Run chatty tests. Assert on CONT lines.
|
||||
go test chatty_test.go -v
|
||||
|
||||
# Non-parallel tests should not print CONT.
|
||||
! stdout CONT
|
||||
|
||||
# The assertion is condensed into one line so that it precisely matches output,
|
||||
# rather than skipping lines and allow rogue CONT lines.
|
||||
stdout '=== RUN TestChatty\n=== RUN TestChatty/sub-0\n chatty_test.go:12: this is sub-0\n chatty_test.go:12: this is sub-0\n=== RUN TestChatty/sub-1\n chatty_test.go:12: this is sub-1\n chatty_test.go:12: this is sub-1\n=== RUN TestChatty/sub-2\n chatty_test.go:12: this is sub-2\n chatty_test.go:12: this is sub-2\n--- PASS: TestChatty'
|
||||
|
||||
-- chatty_test.go --
|
||||
package chatty_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func TestChatty(t *testing.T) {
|
||||
for i := 0; i < 3; i++ {
|
||||
t.Run(fmt.Sprintf("sub-%d", i), func(t *testing.T) {
|
||||
for j := 0; j < 2; j++ {
|
||||
t.Logf("this is sub-%d", i)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
69
src/cmd/go/testdata/script/test_json_panic_exit.txt
vendored
Normal file
69
src/cmd/go/testdata/script/test_json_panic_exit.txt
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
# Verifies golang.org/issue/37555.
|
||||
|
||||
[short] skip
|
||||
|
||||
# 'go test -json' should say a test passes if it says it passes.
|
||||
go test -json ./pass
|
||||
stdout '"Action":"pass".*\n\z'
|
||||
! stdout '"Test":.*\n\z'
|
||||
|
||||
# 'go test -json' should say a test passes if it exits 0 and prints nothing.
|
||||
# TODO(golang.org/issue/29062): this should fail in the future.
|
||||
go test -json ./exit0main
|
||||
stdout '"Action":"pass".*\n\z'
|
||||
! stdout '"Test":.*\n\z'
|
||||
|
||||
# 'go test -json' should say a test fails if it exits 1 and prints nothing.
|
||||
! go test -json ./exit1main
|
||||
stdout '"Action":"fail".*\n\z'
|
||||
! stdout '"Test":.*\n\z'
|
||||
|
||||
# 'go test -json' should say a test fails if it panics.
|
||||
! go test -json ./panic
|
||||
stdout '"Action":"fail".*\n\z'
|
||||
! stdout '"Test":.*\n\z'
|
||||
|
||||
-- go.mod --
|
||||
module example.com/test
|
||||
|
||||
go 1.14
|
||||
|
||||
-- pass/pass_test.go --
|
||||
package pass_test
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPass(t *testing.T) {}
|
||||
|
||||
-- exit0main/exit0main_test.go --
|
||||
package exit0_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
-- exit1main/exit1main_test.go --
|
||||
package exit1_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
-- panic/panic_test.go --
|
||||
package panic_test
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPanic(t *testing.T) {
|
||||
panic("oh no")
|
||||
}
|
||||
30
src/cmd/go/testdata/script/test_main_panic.txt
vendored
Normal file
30
src/cmd/go/testdata/script/test_main_panic.txt
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
[short] skip
|
||||
[!race] skip
|
||||
|
||||
! go test -v -race main_panic/testmain_parallel_sub_panic_test.go
|
||||
! stdout 'DATA RACE'
|
||||
-- main_panic/testmain_parallel_sub_panic_test.go --
|
||||
package testmain_parallel_sub_panic_test
|
||||
|
||||
import "testing"
|
||||
|
||||
func setup() { println("setup()") }
|
||||
func teardown() { println("teardown()") }
|
||||
func TestA(t *testing.T) {
|
||||
t.Run("1", func(t *testing.T) {
|
||||
t.Run("1", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
panic("A/1/1 panics")
|
||||
})
|
||||
t.Run("2", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
println("A/1/2 is ok")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
setup()
|
||||
defer teardown()
|
||||
m.Run()
|
||||
}
|
||||
14
src/cmd/go/testdata/script/test_regexps.txt
vendored
14
src/cmd/go/testdata/script/test_regexps.txt
vendored
@@ -4,28 +4,28 @@ go test -cpu=1 -run=X/Y -bench=X/Y -count=2 -v testregexp
|
||||
|
||||
# TestX is run, twice
|
||||
stdout -count=2 '^=== RUN TestX$'
|
||||
stdout -count=2 '^ TestX: x_test.go:6: LOG: X running$'
|
||||
stdout -count=2 '^ x_test.go:6: LOG: X running$'
|
||||
|
||||
# TestX/Y is run, twice
|
||||
stdout -count=2 '^=== RUN TestX/Y$'
|
||||
stdout -count=2 '^ TestX/Y: x_test.go:8: LOG: Y running$'
|
||||
stdout -count=2 '^ x_test.go:8: LOG: Y running$'
|
||||
|
||||
# TestXX is run, twice
|
||||
stdout -count=2 '^=== RUN TestXX$'
|
||||
stdout -count=2 '^ TestXX: z_test.go:10: LOG: XX running'
|
||||
stdout -count=2 '^ z_test.go:10: LOG: XX running'
|
||||
|
||||
# TestZ is not run
|
||||
! stdout '^=== RUN TestZ$'
|
||||
|
||||
# BenchmarkX is run with N=1 once, only to discover what sub-benchmarks it has,
|
||||
# and should not print a final summary line.
|
||||
stdout -count=1 '^\s+BenchmarkX: x_test.go:13: LOG: X running N=1$'
|
||||
stdout -count=1 '^ x_test.go:13: LOG: X running N=1$'
|
||||
! stdout '^\s+BenchmarkX: x_test.go:13: LOG: X running N=\d\d+'
|
||||
! stdout 'BenchmarkX\s+\d+'
|
||||
|
||||
# Same for BenchmarkXX.
|
||||
stdout -count=1 '^\s+BenchmarkXX: z_test.go:18: LOG: XX running N=1$'
|
||||
! stdout '^\s+BenchmarkXX: z_test.go:18: LOG: XX running N=\d\d+'
|
||||
stdout -count=1 '^ z_test.go:18: LOG: XX running N=1$'
|
||||
! stdout '^ z_test.go:18: LOG: XX running N=\d\d+'
|
||||
! stdout 'BenchmarkXX\s+\d+'
|
||||
|
||||
# BenchmarkX/Y is run in full twice due to -count=2.
|
||||
@@ -33,7 +33,7 @@ stdout -count=1 '^\s+BenchmarkXX: z_test.go:18: LOG: XX running N=1$'
|
||||
# but may cap out at N=1e9.
|
||||
# We don't actually care what the final iteration count is, but it should be
|
||||
# a large number, and the last iteration count prints right before the results.
|
||||
stdout -count=2 '^\s+BenchmarkX/Y: x_test.go:15: LOG: Y running N=[1-9]\d{4,}\nBenchmarkX/Y\s+\d+'
|
||||
stdout -count=2 '^ x_test.go:15: LOG: Y running N=[1-9]\d{4,}\nBenchmarkX/Y\s+\d+'
|
||||
|
||||
-- testregexp/x_test.go --
|
||||
package x
|
||||
|
||||
@@ -128,9 +128,16 @@ func (c *converter) Write(b []byte) (int, error) {
|
||||
}
|
||||
|
||||
var (
|
||||
// printed by test on successful run.
|
||||
bigPass = []byte("PASS\n")
|
||||
|
||||
// printed by test after a normal test failure.
|
||||
bigFail = []byte("FAIL\n")
|
||||
|
||||
// printed by 'go test' along with an error if the test binary terminates
|
||||
// with an error.
|
||||
bigFailErrorPrefix = []byte("FAIL\t")
|
||||
|
||||
updates = [][]byte{
|
||||
[]byte("=== RUN "),
|
||||
[]byte("=== PAUSE "),
|
||||
@@ -155,7 +162,7 @@ var (
|
||||
// before or after emitting other events.
|
||||
func (c *converter) handleInputLine(line []byte) {
|
||||
// Final PASS or FAIL.
|
||||
if bytes.Equal(line, bigPass) || bytes.Equal(line, bigFail) {
|
||||
if bytes.Equal(line, bigPass) || bytes.Equal(line, bigFail) || bytes.HasPrefix(line, bigFailErrorPrefix) {
|
||||
c.flushReport(0)
|
||||
c.output.write(line)
|
||||
if bytes.Equal(line, bigPass) {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
{"Action":"output","Test":"TestPanic","Output":"\tgo/src/testing/testing.go:909 +0xc9\n"}
|
||||
{"Action":"output","Test":"TestPanic","Output":"created by testing.(*T).Run\n"}
|
||||
{"Action":"output","Test":"TestPanic","Output":"\tgo/src/testing/testing.go:960 +0x350\n"}
|
||||
{"Action":"output","Test":"TestPanic","Output":"FAIL\tcommand-line-arguments\t0.042s\n"}
|
||||
{"Action":"fail","Test":"TestPanic"}
|
||||
{"Action":"output","Output":"FAIL\tcommand-line-arguments\t0.042s\n"}
|
||||
{"Action":"output","Output":"FAIL\n"}
|
||||
{"Action":"fail"}
|
||||
|
||||
@@ -470,8 +470,12 @@ func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) {
|
||||
offset := (signext24(r.Add&0xffffff) + 2) * 4
|
||||
var tramp *sym.Symbol
|
||||
for i := 0; ; i++ {
|
||||
name := r.Sym.Name + fmt.Sprintf("%+d-tramp%d", offset, i)
|
||||
oName := r.Sym.Name
|
||||
name := oName + fmt.Sprintf("%+d-tramp%d", offset, i)
|
||||
tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version))
|
||||
if oName == "runtime.deferreturn" {
|
||||
tramp.Attr.Set(sym.AttrDeferReturnTramp, true)
|
||||
}
|
||||
if tramp.Type == sym.SDYNIMPORT {
|
||||
// don't reuse trampoline defined in other module
|
||||
continue
|
||||
|
||||
@@ -276,7 +276,7 @@ func (ctxt *Link) pclntab() {
|
||||
// set the resumption point to PC_B.
|
||||
lastWasmAddr = uint32(r.Add)
|
||||
}
|
||||
if r.Type.IsDirectCall() && r.Sym != nil && r.Sym.Name == "runtime.deferreturn" {
|
||||
if r.Type.IsDirectCall() && r.Sym != nil && (r.Sym.Name == "runtime.deferreturn" || r.Sym.Attr.DeferReturnTramp()) {
|
||||
if ctxt.Arch.Family == sys.Wasm {
|
||||
deferreturn = lastWasmAddr - 1
|
||||
} else {
|
||||
|
||||
@@ -667,7 +667,8 @@ func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) {
|
||||
// target is at some offset within the function. Calls to duff+8 and duff+256 must appear as
|
||||
// distinct trampolines.
|
||||
|
||||
name := r.Sym.Name
|
||||
oName := r.Sym.Name
|
||||
name := oName
|
||||
if r.Add == 0 {
|
||||
name = name + fmt.Sprintf("-tramp%d", i)
|
||||
} else {
|
||||
@@ -677,6 +678,9 @@ func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) {
|
||||
// Look up the trampoline in case it already exists
|
||||
|
||||
tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version))
|
||||
if oName == "runtime.deferreturn" {
|
||||
tramp.Attr.Set(sym.AttrDeferReturnTramp, true)
|
||||
}
|
||||
if tramp.Value == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -81,7 +81,10 @@ const (
|
||||
// AttrReadOnly indicates whether the symbol's content (Symbol.P) is backed by
|
||||
// read-only memory.
|
||||
AttrReadOnly
|
||||
// 19 attributes defined so far.
|
||||
// AttrDeferReturnTramp indicates the symbol is a trampoline of a deferreturn
|
||||
// call.
|
||||
AttrDeferReturnTramp
|
||||
// 20 attributes defined so far.
|
||||
)
|
||||
|
||||
func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 }
|
||||
@@ -103,6 +106,7 @@ func (a Attribute) SubSymbol() bool { return a&AttrSubSymbol != 0 }
|
||||
func (a Attribute) Container() bool { return a&AttrContainer != 0 }
|
||||
func (a Attribute) TopFrame() bool { return a&AttrTopFrame != 0 }
|
||||
func (a Attribute) ReadOnly() bool { return a&AttrReadOnly != 0 }
|
||||
func (a Attribute) DeferReturnTramp() bool { return a&AttrDeferReturnTramp != 0 }
|
||||
|
||||
func (a Attribute) CgoExport() bool {
|
||||
return a.CgoExportDynamic() || a.CgoExportStatic()
|
||||
|
||||
@@ -447,3 +447,66 @@ func TestStrictDup(t *testing.T) {
|
||||
t.Errorf("unexpected output:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
const testTrampSrc = `
|
||||
package main
|
||||
import "fmt"
|
||||
func main() {
|
||||
fmt.Println("hello")
|
||||
|
||||
defer func(){
|
||||
if e := recover(); e == nil {
|
||||
panic("did not panic")
|
||||
}
|
||||
}()
|
||||
f1()
|
||||
}
|
||||
|
||||
// Test deferreturn trampolines. See issue #39049.
|
||||
func f1() { defer f2() }
|
||||
func f2() { panic("XXX") }
|
||||
`
|
||||
|
||||
func TestTrampoline(t *testing.T) {
|
||||
// Test that trampoline insertion works as expected.
|
||||
// For stress test, we set -debugtramp=2 flag, which sets a very low
|
||||
// threshold for trampoline generation, and essentially all cross-package
|
||||
// calls will use trampolines.
|
||||
switch runtime.GOARCH {
|
||||
case "arm", "ppc64", "ppc64le":
|
||||
default:
|
||||
t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
|
||||
}
|
||||
if runtime.GOOS == "aix" {
|
||||
t.Skip("trampolines on AIX doesn't work in Go 1.14") // fixed in Go 1.15
|
||||
}
|
||||
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
tmpdir, err := ioutil.TempDir("", "TestTrampoline")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
src := filepath.Join(tmpdir, "hello.go")
|
||||
err = ioutil.WriteFile(src, []byte(testTrampSrc), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
exe := filepath.Join(tmpdir, "hello.exe")
|
||||
|
||||
cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("build failed: %v\n%s", err, out)
|
||||
}
|
||||
cmd = exec.Command(exe)
|
||||
out, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("executable failed to run: %v\n%s", err, out)
|
||||
}
|
||||
if string(out) != "hello\n" {
|
||||
t.Errorf("unexpected output:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ func init() {
|
||||
http.HandleFunc("/trace", httpTrace)
|
||||
http.HandleFunc("/jsontrace", httpJsonTrace)
|
||||
http.HandleFunc("/trace_viewer_html", httpTraceViewerHTML)
|
||||
http.HandleFunc("/webcomponents.min.js", webcomponentsJS)
|
||||
}
|
||||
|
||||
// httpTrace serves either whole trace (goid==0) or trace for goid goroutine.
|
||||
@@ -43,14 +44,26 @@ func httpTrace(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
}
|
||||
|
||||
// See https://github.com/catapult-project/catapult/blob/master/tracing/docs/embedding-trace-viewer.md
|
||||
// This is almost verbatim copy of:
|
||||
// https://github.com/catapult-project/catapult/blob/master/tracing/bin/index.html
|
||||
// on revision 5f9e4c3eaa555bdef18218a89f38c768303b7b6e.
|
||||
// https://chromium.googlesource.com/catapult/+/9508452e18f130c98499cb4c4f1e1efaedee8962/tracing/docs/embedding-trace-viewer.md
|
||||
// This is almost verbatim copy of https://chromium-review.googlesource.com/c/catapult/+/2062938/2/tracing/bin/index.html
|
||||
var templTrace = `
|
||||
<html>
|
||||
<head>
|
||||
<link href="/trace_viewer_html" rel="import">
|
||||
<script src="/webcomponents.min.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function onTraceViewerImportFail() {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.body.textContent =
|
||||
'/trace_viewer_full.html is missing. File a bug in https://golang.org/issue';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<link rel="import" href="/trace_viewer_html"
|
||||
onerror="onTraceViewerImportFail(event)">
|
||||
|
||||
<style type="text/css">
|
||||
html, body {
|
||||
box-sizing: border-box;
|
||||
@@ -77,10 +90,10 @@ var templTrace = `
|
||||
|
||||
function load() {
|
||||
var req = new XMLHttpRequest();
|
||||
var is_binary = /[.]gz$/.test(url) || /[.]zip$/.test(url);
|
||||
var isBinary = /[.]gz$/.test(url) || /[.]zip$/.test(url);
|
||||
req.overrideMimeType('text/plain; charset=x-user-defined');
|
||||
req.open('GET', url, true);
|
||||
if (is_binary)
|
||||
if (isBinary)
|
||||
req.responseType = 'arraybuffer';
|
||||
|
||||
req.onreadystatechange = function(event) {
|
||||
@@ -89,7 +102,7 @@ var templTrace = `
|
||||
|
||||
window.setTimeout(function() {
|
||||
if (req.status === 200)
|
||||
onResult(is_binary ? req.response : req.responseText);
|
||||
onResult(isBinary ? req.response : req.responseText);
|
||||
else
|
||||
onResultFail(req.status);
|
||||
}, 0);
|
||||
@@ -136,17 +149,17 @@ var templTrace = `
|
||||
overlay.visible = true;
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.addEventListener('WebComponentsReady', function() {
|
||||
var container = document.createElement('track-view-container');
|
||||
container.id = 'track_view_container';
|
||||
|
||||
viewer = document.createElement('tr-ui-timeline-view');
|
||||
viewer.track_view_container = container;
|
||||
viewer.appendChild(container);
|
||||
Polymer.dom(viewer).appendChild(container);
|
||||
|
||||
viewer.id = 'trace-viewer';
|
||||
viewer.globalMode = true;
|
||||
document.body.appendChild(viewer);
|
||||
Polymer.dom(document.body).appendChild(viewer);
|
||||
|
||||
url = '/jsontrace?{{PARAMS}}';
|
||||
load();
|
||||
@@ -165,6 +178,10 @@ func httpTraceViewerHTML(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, filepath.Join(runtime.GOROOT(), "misc", "trace", "trace_viewer_full.html"))
|
||||
}
|
||||
|
||||
func webcomponentsJS(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, filepath.Join(runtime.GOROOT(), "misc", "trace", "webcomponents.min.js"))
|
||||
}
|
||||
|
||||
// httpJsonTrace serves json trace, requested from within templTrace HTML.
|
||||
func httpJsonTrace(w http.ResponseWriter, r *http.Request) {
|
||||
defer debug.FreeOSMemory()
|
||||
|
||||
1
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go
generated
vendored
1
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go
generated
vendored
@@ -801,6 +801,7 @@ var printVerbs = []printVerb{
|
||||
{'g', sharpNumFlag, argFloat | argComplex},
|
||||
{'G', sharpNumFlag, argFloat | argComplex},
|
||||
{'o', sharpNumFlag, argInt | argPointer},
|
||||
{'O', sharpNumFlag, argInt | argPointer},
|
||||
{'p', "-#", argPointer},
|
||||
{'q', " -+.0#", argRune | argInt | argString},
|
||||
{'s', " -+.0", argString},
|
||||
|
||||
2
src/cmd/vendor/modules.txt
vendored
2
src/cmd/vendor/modules.txt
vendored
@@ -44,7 +44,7 @@ golang.org/x/mod/zip
|
||||
## explicit
|
||||
golang.org/x/sys/unix
|
||||
golang.org/x/sys/windows
|
||||
# golang.org/x/tools v0.0.0-20200131233409-575de47986ce
|
||||
# golang.org/x/tools v0.0.0-20200602230032-c00d67ef29d0
|
||||
## explicit
|
||||
golang.org/x/tools/go/analysis
|
||||
golang.org/x/tools/go/analysis/internal/analysisflags
|
||||
|
||||
@@ -86,7 +86,8 @@ func NewGCM(cipher Block) (AEAD, error) {
|
||||
}
|
||||
|
||||
// NewGCMWithNonceSize returns the given 128-bit, block cipher wrapped in Galois
|
||||
// Counter Mode, which accepts nonces of the given length.
|
||||
// Counter Mode, which accepts nonces of the given length. The length must not
|
||||
// be zero.
|
||||
//
|
||||
// Only use this function if you require compatibility with an existing
|
||||
// cryptosystem that uses non-standard nonce lengths. All other users should use
|
||||
@@ -112,6 +113,10 @@ func newGCMWithNonceAndTagSize(cipher Block, nonceSize, tagSize int) (AEAD, erro
|
||||
return nil, errors.New("cipher: incorrect tag size given to GCM")
|
||||
}
|
||||
|
||||
if nonceSize <= 0 {
|
||||
return nil, errors.New("cipher: the nonce can't have zero length, or the security of the key will be immediately compromised")
|
||||
}
|
||||
|
||||
if cipher, ok := cipher.(gcmAble); ok {
|
||||
return cipher.NewGCM(nonceSize, tagSize)
|
||||
}
|
||||
|
||||
@@ -217,6 +217,13 @@ var aesGCMTests = []struct {
|
||||
"2b9680b886b3efb7c6354b38c63b5373",
|
||||
"e2b7e5ed5ff27fc8664148f5a628a46dcbf2015184fffb82f2651c36",
|
||||
},
|
||||
{
|
||||
"11754cd72aec309bf52f7687212e8957",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"250327c674aaf477aef2675748cf6971",
|
||||
},
|
||||
}
|
||||
|
||||
func TestAESGCM(t *testing.T) {
|
||||
@@ -234,14 +241,22 @@ func TestAESGCM(t *testing.T) {
|
||||
|
||||
var aesgcm cipher.AEAD
|
||||
switch {
|
||||
// Handle non-standard nonce sizes
|
||||
// Handle non-standard tag sizes
|
||||
case tagSize != 16:
|
||||
aesgcm, err = cipher.NewGCMWithTagSize(aes, tagSize)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Handle non-standard tag sizes
|
||||
// Handle 0 nonce size (expect error and continue)
|
||||
case len(nonce) == 0:
|
||||
aesgcm, err = cipher.NewGCMWithNonceSize(aes, 0)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for zero nonce size")
|
||||
}
|
||||
continue
|
||||
|
||||
// Handle non-standard nonce sizes
|
||||
case len(nonce) != 12:
|
||||
aesgcm, err = cipher.NewGCMWithNonceSize(aes, len(nonce))
|
||||
if err != nil {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -188,23 +188,32 @@ var errNotParsed = errors.New("x509: missing ASN.1 contents; use ParseCertificat
|
||||
// VerifyOptions contains parameters for Certificate.Verify. It's a structure
|
||||
// because other PKIX verification APIs have ended up needing many options.
|
||||
type VerifyOptions struct {
|
||||
DNSName string
|
||||
// DNSName, if set, is checked against the leaf certificate with
|
||||
// Certificate.VerifyHostname or the platform verifier.
|
||||
DNSName string
|
||||
|
||||
// Intermediates is an optional pool of certificates that are not trust
|
||||
// anchors, but can be used to form a chain from the leaf certificate to a
|
||||
// root certificate.
|
||||
Intermediates *CertPool
|
||||
Roots *CertPool // if nil, the system roots are used
|
||||
CurrentTime time.Time // if zero, the current time is used
|
||||
// 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.)
|
||||
// Roots is the set of trusted root certificates the leaf certificate needs
|
||||
// to chain up to. If nil, the system roots or the platform verifier are used.
|
||||
Roots *CertPool
|
||||
|
||||
// CurrentTime is used to check the validity of all certificates in the
|
||||
// chain. If zero, the current time is used.
|
||||
CurrentTime time.Time
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -717,8 +726,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
|
||||
@@ -726,9 +736,10 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
|
||||
// the name being validated. Note that DirectoryName constraints are not
|
||||
// supported.
|
||||
//
|
||||
// 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) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -255,12 +255,9 @@ type ConnBeginTx interface {
|
||||
// SessionResetter may be implemented by Conn to allow drivers to reset the
|
||||
// session state associated with the connection and to signal a bad connection.
|
||||
type SessionResetter interface {
|
||||
// ResetSession is called while a connection is in the connection
|
||||
// pool. No queries will run on this connection until this method returns.
|
||||
//
|
||||
// If the connection is bad this should return driver.ErrBadConn to prevent
|
||||
// the connection from being returned to the connection pool. Any other
|
||||
// error will be discarded.
|
||||
// ResetSession is called prior to executing a query on the connection
|
||||
// if the connection has been used before. If the driver returns ErrBadConn
|
||||
// the connection is discarded.
|
||||
ResetSession(ctx context.Context) error
|
||||
}
|
||||
|
||||
|
||||
@@ -390,12 +390,19 @@ func setStrictFakeConnClose(t *testing.T) {
|
||||
|
||||
func (c *fakeConn) ResetSession(ctx context.Context) error {
|
||||
c.dirtySession = false
|
||||
c.currTx = nil
|
||||
if c.isBad() {
|
||||
return driver.ErrBadConn
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ validator = (*fakeConn)(nil)
|
||||
|
||||
func (c *fakeConn) IsValid() bool {
|
||||
return !c.isBad()
|
||||
}
|
||||
|
||||
func (c *fakeConn) Close() (err error) {
|
||||
drv := fdriver.(*fakeDriver)
|
||||
defer func() {
|
||||
@@ -728,6 +735,9 @@ var hookExecBadConn func() bool
|
||||
func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||
panic("Using ExecContext")
|
||||
}
|
||||
|
||||
var errFakeConnSessionDirty = errors.New("fakedb: session is dirty")
|
||||
|
||||
func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
|
||||
if s.panic == "Exec" {
|
||||
panic(s.panic)
|
||||
@@ -740,7 +750,7 @@ func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (d
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
if s.c.isDirtyAndMark() {
|
||||
return nil, errors.New("fakedb: session is dirty")
|
||||
return nil, errFakeConnSessionDirty
|
||||
}
|
||||
|
||||
err := checkSubsetTypes(s.c.db.allowAny, args)
|
||||
@@ -854,7 +864,7 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
if s.c.isDirtyAndMark() {
|
||||
return nil, errors.New("fakedb: session is dirty")
|
||||
return nil, errFakeConnSessionDirty
|
||||
}
|
||||
|
||||
err := checkSubsetTypes(s.c.db.allowAny, args)
|
||||
@@ -887,6 +897,37 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.table == "tx_status" && s.colName[0] == "tx_status" {
|
||||
txStatus := "autocommit"
|
||||
if s.c.currTx != nil {
|
||||
txStatus = "transaction"
|
||||
}
|
||||
cursor := &rowsCursor{
|
||||
parentMem: s.c,
|
||||
posRow: -1,
|
||||
rows: [][]*row{
|
||||
[]*row{
|
||||
{
|
||||
cols: []interface{}{
|
||||
txStatus,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
cols: [][]string{
|
||||
[]string{
|
||||
"tx_status",
|
||||
},
|
||||
},
|
||||
colType: [][]string{
|
||||
[]string{
|
||||
"string",
|
||||
},
|
||||
},
|
||||
errPos: -1,
|
||||
}
|
||||
return cursor, nil
|
||||
}
|
||||
|
||||
t.mu.Lock()
|
||||
|
||||
|
||||
@@ -421,7 +421,6 @@ type DB struct {
|
||||
// It is closed during db.Close(). The close tells the connectionOpener
|
||||
// goroutine to exit.
|
||||
openerCh chan struct{}
|
||||
resetterCh chan *driverConn
|
||||
closed bool
|
||||
dep map[finalCloser]depSet
|
||||
lastPut map[*driverConn]string // stacktrace of last conn's put; debug only
|
||||
@@ -458,10 +457,10 @@ type driverConn struct {
|
||||
|
||||
sync.Mutex // guards following
|
||||
ci driver.Conn
|
||||
needReset bool // The connection session should be reset before use if true.
|
||||
closed bool
|
||||
finalClosed bool // ci.Close has been called
|
||||
openStmt map[*driverStmt]bool
|
||||
lastErr error // lastError captures the result of the session resetter.
|
||||
|
||||
// guarded by db.mu
|
||||
inUse bool
|
||||
@@ -486,6 +485,41 @@ func (dc *driverConn) expired(timeout time.Duration) bool {
|
||||
return dc.createdAt.Add(timeout).Before(nowFunc())
|
||||
}
|
||||
|
||||
// resetSession checks if the driver connection needs the
|
||||
// session to be reset and if required, resets it.
|
||||
func (dc *driverConn) resetSession(ctx context.Context) error {
|
||||
dc.Lock()
|
||||
defer dc.Unlock()
|
||||
|
||||
if !dc.needReset {
|
||||
return nil
|
||||
}
|
||||
if cr, ok := dc.ci.(driver.SessionResetter); ok {
|
||||
return cr.ResetSession(ctx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validator was introduced for Go1.15, but backported to Go1.14.
|
||||
type validator interface {
|
||||
IsValid() bool
|
||||
}
|
||||
|
||||
// validateConnection checks if the connection is valid and can
|
||||
// still be used. It also marks the session for reset if required.
|
||||
func (dc *driverConn) validateConnection(needsReset bool) bool {
|
||||
dc.Lock()
|
||||
defer dc.Unlock()
|
||||
|
||||
if needsReset {
|
||||
dc.needReset = true
|
||||
}
|
||||
if cv, ok := dc.ci.(validator); ok {
|
||||
return cv.IsValid()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// prepareLocked prepares the query on dc. When cg == nil the dc must keep track of
|
||||
// the prepared statements in a pool.
|
||||
func (dc *driverConn) prepareLocked(ctx context.Context, cg stmtConnGrabber, query string) (*driverStmt, error) {
|
||||
@@ -511,19 +545,6 @@ func (dc *driverConn) prepareLocked(ctx context.Context, cg stmtConnGrabber, que
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
// resetSession resets the connection session and sets the lastErr
|
||||
// that is checked before returning the connection to another query.
|
||||
//
|
||||
// resetSession assumes that the embedded mutex is locked when the connection
|
||||
// was returned to the pool. This unlocks the mutex.
|
||||
func (dc *driverConn) resetSession(ctx context.Context) {
|
||||
defer dc.Unlock() // In case of panic.
|
||||
if dc.closed { // Check if the database has been closed.
|
||||
return
|
||||
}
|
||||
dc.lastErr = dc.ci.(driver.SessionResetter).ResetSession(ctx)
|
||||
}
|
||||
|
||||
// the dc.db's Mutex is held.
|
||||
func (dc *driverConn) closeDBLocked() func() error {
|
||||
dc.Lock()
|
||||
@@ -713,14 +734,12 @@ func OpenDB(c driver.Connector) *DB {
|
||||
db := &DB{
|
||||
connector: c,
|
||||
openerCh: make(chan struct{}, connectionRequestQueueSize),
|
||||
resetterCh: make(chan *driverConn, 50),
|
||||
lastPut: make(map[*driverConn]string),
|
||||
connRequests: make(map[uint64]chan connRequest),
|
||||
stop: cancel,
|
||||
}
|
||||
|
||||
go db.connectionOpener(ctx)
|
||||
go db.connectionResetter(ctx)
|
||||
|
||||
return db
|
||||
}
|
||||
@@ -1058,23 +1077,6 @@ func (db *DB) connectionOpener(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// connectionResetter runs in a separate goroutine to reset connections async
|
||||
// to exported API.
|
||||
func (db *DB) connectionResetter(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
close(db.resetterCh)
|
||||
for dc := range db.resetterCh {
|
||||
dc.Unlock()
|
||||
}
|
||||
return
|
||||
case dc := <-db.resetterCh:
|
||||
dc.resetSession(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open one new connection
|
||||
func (db *DB) openNewConnection(ctx context.Context) {
|
||||
// maybeOpenNewConnctions has already executed db.numOpen++ before it sent
|
||||
@@ -1155,14 +1157,13 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn
|
||||
conn.Close()
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
// Lock around reading lastErr to ensure the session resetter finished.
|
||||
conn.Lock()
|
||||
err := conn.lastErr
|
||||
conn.Unlock()
|
||||
if err == driver.ErrBadConn {
|
||||
|
||||
// Reset the session if required.
|
||||
if err := conn.resetSession(ctx); err == driver.ErrBadConn {
|
||||
conn.Close()
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
@@ -1204,18 +1205,22 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn
|
||||
if !ok {
|
||||
return nil, errDBClosed
|
||||
}
|
||||
if ret.err == nil && ret.conn.expired(lifetime) {
|
||||
// Only check if the connection is expired if the strategy is cachedOrNewConns.
|
||||
// If we require a new connection, just re-use the connection without looking
|
||||
// at the expiry time. If it is expired, it will be checked when it is placed
|
||||
// back into the connection pool.
|
||||
// This prioritizes giving a valid connection to a client over the exact connection
|
||||
// lifetime, which could expire exactly after this point anyway.
|
||||
if strategy == cachedOrNewConn && ret.err == nil && ret.conn.expired(lifetime) {
|
||||
ret.conn.Close()
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
if ret.conn == nil {
|
||||
return nil, ret.err
|
||||
}
|
||||
// Lock around reading lastErr to ensure the session resetter finished.
|
||||
ret.conn.Lock()
|
||||
err := ret.conn.lastErr
|
||||
ret.conn.Unlock()
|
||||
if err == driver.ErrBadConn {
|
||||
|
||||
// Reset the session if required.
|
||||
if err := ret.conn.resetSession(ctx); err == driver.ErrBadConn {
|
||||
ret.conn.Close()
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
@@ -1275,13 +1280,23 @@ const debugGetPut = false
|
||||
// putConn adds a connection to the db's free pool.
|
||||
// err is optionally the last error that occurred on this connection.
|
||||
func (db *DB) putConn(dc *driverConn, err error, resetSession bool) {
|
||||
if err != driver.ErrBadConn {
|
||||
if !dc.validateConnection(resetSession) {
|
||||
err = driver.ErrBadConn
|
||||
}
|
||||
}
|
||||
db.mu.Lock()
|
||||
if !dc.inUse {
|
||||
db.mu.Unlock()
|
||||
if debugGetPut {
|
||||
fmt.Printf("putConn(%v) DUPLICATE was: %s\n\nPREVIOUS was: %s", dc, stack(), db.lastPut[dc])
|
||||
}
|
||||
panic("sql: connection returned that was never out")
|
||||
}
|
||||
|
||||
if err != driver.ErrBadConn && dc.expired(db.maxLifetime) {
|
||||
err = driver.ErrBadConn
|
||||
}
|
||||
if debugGetPut {
|
||||
db.lastPut[dc] = stack()
|
||||
}
|
||||
@@ -1305,41 +1320,13 @@ func (db *DB) putConn(dc *driverConn, err error, resetSession bool) {
|
||||
if putConnHook != nil {
|
||||
putConnHook(db, dc)
|
||||
}
|
||||
if db.closed {
|
||||
// Connections do not need to be reset if they will be closed.
|
||||
// Prevents writing to resetterCh after the DB has closed.
|
||||
resetSession = false
|
||||
}
|
||||
if resetSession {
|
||||
if _, resetSession = dc.ci.(driver.SessionResetter); resetSession {
|
||||
// Lock the driverConn here so it isn't released until
|
||||
// the connection is reset.
|
||||
// The lock must be taken before the connection is put into
|
||||
// the pool to prevent it from being taken out before it is reset.
|
||||
dc.Lock()
|
||||
}
|
||||
}
|
||||
added := db.putConnDBLocked(dc, nil)
|
||||
db.mu.Unlock()
|
||||
|
||||
if !added {
|
||||
if resetSession {
|
||||
dc.Unlock()
|
||||
}
|
||||
dc.Close()
|
||||
return
|
||||
}
|
||||
if !resetSession {
|
||||
return
|
||||
}
|
||||
select {
|
||||
default:
|
||||
// If the resetterCh is blocking then mark the connection
|
||||
// as bad and continue on.
|
||||
dc.lastErr = driver.ErrBadConn
|
||||
dc.Unlock()
|
||||
case db.resetterCh <- dc:
|
||||
}
|
||||
}
|
||||
|
||||
// Satisfy a connRequest or put the driverConn in the idle pool and return true
|
||||
@@ -1701,7 +1688,11 @@ func (db *DB) begin(ctx context.Context, opts *TxOptions, strategy connReuseStra
|
||||
// beginDC starts a transaction. The provided dc must be valid and ready to use.
|
||||
func (db *DB) beginDC(ctx context.Context, dc *driverConn, release func(error), opts *TxOptions) (tx *Tx, err error) {
|
||||
var txi driver.Tx
|
||||
keepConnOnRollback := false
|
||||
withLock(dc, func() {
|
||||
_, hasSessionResetter := dc.ci.(driver.SessionResetter)
|
||||
_, hasConnectionValidator := dc.ci.(validator)
|
||||
keepConnOnRollback = hasSessionResetter && hasConnectionValidator
|
||||
txi, err = ctxDriverBegin(ctx, opts, dc.ci)
|
||||
})
|
||||
if err != nil {
|
||||
@@ -1713,12 +1704,13 @@ func (db *DB) beginDC(ctx context.Context, dc *driverConn, release func(error),
|
||||
// The cancel function in Tx will be called after done is set to true.
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
tx = &Tx{
|
||||
db: db,
|
||||
dc: dc,
|
||||
releaseConn: release,
|
||||
txi: txi,
|
||||
cancel: cancel,
|
||||
ctx: ctx,
|
||||
db: db,
|
||||
dc: dc,
|
||||
releaseConn: release,
|
||||
txi: txi,
|
||||
cancel: cancel,
|
||||
keepConnOnRollback: keepConnOnRollback,
|
||||
ctx: ctx,
|
||||
}
|
||||
go tx.awaitDone()
|
||||
return tx, nil
|
||||
@@ -1980,6 +1972,11 @@ type Tx struct {
|
||||
// Use atomic operations on value when checking value.
|
||||
done int32
|
||||
|
||||
// keepConnOnRollback is true if the driver knows
|
||||
// how to reset the connection's session and if need be discard
|
||||
// the connection.
|
||||
keepConnOnRollback bool
|
||||
|
||||
// All Stmts prepared for this transaction. These will be closed after the
|
||||
// transaction has been committed or rolled back.
|
||||
stmts struct {
|
||||
@@ -2005,7 +2002,10 @@ func (tx *Tx) awaitDone() {
|
||||
// transaction is closed and the resources are released. This
|
||||
// rollback does nothing if the transaction has already been
|
||||
// committed or rolled back.
|
||||
tx.rollback(true)
|
||||
// Do not discard the connection if the connection knows
|
||||
// how to reset the session.
|
||||
discardConnection := !tx.keepConnOnRollback
|
||||
tx.rollback(discardConnection)
|
||||
}
|
||||
|
||||
func (tx *Tx) isDone() bool {
|
||||
@@ -2016,14 +2016,10 @@ func (tx *Tx) isDone() bool {
|
||||
// that has already been committed or rolled back.
|
||||
var ErrTxDone = errors.New("sql: transaction has already been committed or rolled back")
|
||||
|
||||
// close returns the connection to the pool and
|
||||
// must only be called by Tx.rollback or Tx.Commit.
|
||||
func (tx *Tx) close(err error) {
|
||||
tx.cancel()
|
||||
|
||||
tx.closemu.Lock()
|
||||
defer tx.closemu.Unlock()
|
||||
|
||||
// closeLocked returns the connection to the pool and
|
||||
// must only be called by Tx.rollback or Tx.Commit while
|
||||
// closemu is Locked and tx already canceled.
|
||||
func (tx *Tx) closeLocked(err error) {
|
||||
tx.releaseConn(err)
|
||||
tx.dc = nil
|
||||
tx.txi = nil
|
||||
@@ -2090,6 +2086,15 @@ func (tx *Tx) Commit() error {
|
||||
if !atomic.CompareAndSwapInt32(&tx.done, 0, 1) {
|
||||
return ErrTxDone
|
||||
}
|
||||
|
||||
// Cancel the Tx to release any active R-closemu locks.
|
||||
// This is safe to do because tx.done has already transitioned
|
||||
// from 0 to 1. Hold the W-closemu lock prior to rollback
|
||||
// to ensure no other connection has an active query.
|
||||
tx.cancel()
|
||||
tx.closemu.Lock()
|
||||
defer tx.closemu.Unlock()
|
||||
|
||||
var err error
|
||||
withLock(tx.dc, func() {
|
||||
err = tx.txi.Commit()
|
||||
@@ -2097,16 +2102,31 @@ func (tx *Tx) Commit() error {
|
||||
if err != driver.ErrBadConn {
|
||||
tx.closePrepared()
|
||||
}
|
||||
tx.close(err)
|
||||
tx.closeLocked(err)
|
||||
return err
|
||||
}
|
||||
|
||||
var rollbackHook func()
|
||||
|
||||
// rollback aborts the transaction and optionally forces the pool to discard
|
||||
// the connection.
|
||||
func (tx *Tx) rollback(discardConn bool) error {
|
||||
if !atomic.CompareAndSwapInt32(&tx.done, 0, 1) {
|
||||
return ErrTxDone
|
||||
}
|
||||
|
||||
if rollbackHook != nil {
|
||||
rollbackHook()
|
||||
}
|
||||
|
||||
// Cancel the Tx to release any active R-closemu locks.
|
||||
// This is safe to do because tx.done has already transitioned
|
||||
// from 0 to 1. Hold the W-closemu lock prior to rollback
|
||||
// to ensure no other connection has an active query.
|
||||
tx.cancel()
|
||||
tx.closemu.Lock()
|
||||
defer tx.closemu.Unlock()
|
||||
|
||||
var err error
|
||||
withLock(tx.dc, func() {
|
||||
err = tx.txi.Rollback()
|
||||
@@ -2117,7 +2137,7 @@ func (tx *Tx) rollback(discardConn bool) error {
|
||||
if discardConn {
|
||||
err = driver.ErrBadConn
|
||||
}
|
||||
tx.close(err)
|
||||
tx.closeLocked(err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,11 @@ func newTestDBConnector(t testing.TB, fc *fakeConnector, name string) *DB {
|
||||
exec(t, db, "CREATE|magicquery|op=string,millis=int32")
|
||||
exec(t, db, "INSERT|magicquery|op=sleep,millis=10")
|
||||
}
|
||||
if name == "tx_status" {
|
||||
// Magic table name and column, known by fakedb_test.go.
|
||||
exec(t, db, "CREATE|tx_status|tx_status=string")
|
||||
exec(t, db, "INSERT|tx_status|tx_status=invalid")
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
@@ -437,6 +442,7 @@ func TestTxContextWait(t *testing.T) {
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
tx.keepConnOnRollback = false
|
||||
|
||||
// This will trigger the *fakeConn.Prepare method which will take time
|
||||
// performing the query. The ctxDriverPrepare func will check the context
|
||||
@@ -449,6 +455,35 @@ func TestTxContextWait(t *testing.T) {
|
||||
waitForFree(t, db, 5*time.Second, 0)
|
||||
}
|
||||
|
||||
// TestTxContextWaitNoDiscard is the same as TestTxContextWait, but should not discard
|
||||
// the final connection.
|
||||
func TestTxContextWaitNoDiscard(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
// Guard against the context being canceled before BeginTx completes.
|
||||
if err == context.DeadlineExceeded {
|
||||
t.Skip("tx context canceled prior to first use")
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// This will trigger the *fakeConn.Prepare method which will take time
|
||||
// performing the query. The ctxDriverPrepare func will check the context
|
||||
// after this and close the rows and return an error.
|
||||
_, err = tx.QueryContext(ctx, "WAIT|1s|SELECT|people|age,name|")
|
||||
if err != context.DeadlineExceeded {
|
||||
t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
|
||||
}
|
||||
|
||||
waitForFree(t, db, 5*time.Second, 1)
|
||||
}
|
||||
|
||||
// TestUnsupportedOptions checks that the database fails when a driver that
|
||||
// doesn't implement ConnBeginTx is used with non-default options and an
|
||||
// un-cancellable context.
|
||||
@@ -1525,6 +1560,37 @@ func TestConnTx(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestConnIsValid verifies that a database connection that should be discarded,
|
||||
// is actually discarded and does not re-enter the connection pool.
|
||||
// If the IsValid method from *fakeConn is removed, this test will fail.
|
||||
func TestConnIsValid(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
|
||||
db.SetMaxOpenConns(1)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
c, err := db.Conn(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = c.Raw(func(raw interface{}) error {
|
||||
dc := raw.(*fakeConn)
|
||||
dc.stickyBad = true
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c.Close()
|
||||
|
||||
if len(db.freeConn) > 0 && db.freeConn[0].ci.(*fakeConn).stickyBad {
|
||||
t.Fatal("bad connection returned to pool; expected bad connection to be discarded")
|
||||
}
|
||||
}
|
||||
|
||||
// Tests fix for issue 2542, that we release a lock when querying on
|
||||
// a closed connection.
|
||||
func TestIssue2542Deadlock(t *testing.T) {
|
||||
@@ -2658,6 +2724,159 @@ func TestManyErrBadConn(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 34755: Ensure that a Tx cannot commit after a rollback.
|
||||
func TestTxCannotCommitAfterRollback(t *testing.T) {
|
||||
db := newTestDB(t, "tx_status")
|
||||
defer closeDB(t, db)
|
||||
|
||||
// First check query reporting is correct.
|
||||
var txStatus string
|
||||
err := db.QueryRow("SELECT|tx_status|tx_status|").Scan(&txStatus)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if g, w := txStatus, "autocommit"; g != w {
|
||||
t.Fatalf("tx_status=%q, wanted %q", g, w)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Ignore dirty session for this test.
|
||||
// A failing test should trigger the dirty session flag as well,
|
||||
// but that isn't exactly what this should test for.
|
||||
tx.txi.(*fakeTx).c.skipDirtySession = true
|
||||
|
||||
defer tx.Rollback()
|
||||
|
||||
err = tx.QueryRow("SELECT|tx_status|tx_status|").Scan(&txStatus)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if g, w := txStatus, "transaction"; g != w {
|
||||
t.Fatalf("tx_status=%q, wanted %q", g, w)
|
||||
}
|
||||
|
||||
// 1. Begin a transaction.
|
||||
// 2. (A) Start a query, (B) begin Tx rollback through a ctx cancel.
|
||||
// 3. Check if 2.A has committed in Tx (pass) or outside of Tx (fail).
|
||||
sendQuery := make(chan struct{})
|
||||
hookTxGrabConn = func() {
|
||||
cancel()
|
||||
<-sendQuery
|
||||
}
|
||||
rollbackHook = func() {
|
||||
close(sendQuery)
|
||||
}
|
||||
defer func() {
|
||||
hookTxGrabConn = nil
|
||||
rollbackHook = nil
|
||||
}()
|
||||
|
||||
err = tx.QueryRow("SELECT|tx_status|tx_status|").Scan(&txStatus)
|
||||
if err != nil {
|
||||
// A failure here would be expected if skipDirtySession was not set to true above.
|
||||
t.Fatal(err)
|
||||
}
|
||||
if g, w := txStatus, "transaction"; g != w {
|
||||
t.Fatalf("tx_status=%q, wanted %q", g, w)
|
||||
}
|
||||
}
|
||||
|
||||
// Issue32530 encounters an issue where a connection may
|
||||
// expire right after it comes out of a used connection pool
|
||||
// even when a new connection is requested.
|
||||
func TestConnExpiresFreshOutOfPool(t *testing.T) {
|
||||
execCases := []struct {
|
||||
expired bool
|
||||
badReset bool
|
||||
}{
|
||||
{false, false},
|
||||
{true, false},
|
||||
{false, true},
|
||||
}
|
||||
|
||||
t0 := time.Unix(1000000, 0)
|
||||
offset := time.Duration(0)
|
||||
offsetMu := sync.RWMutex{}
|
||||
|
||||
nowFunc = func() time.Time {
|
||||
offsetMu.RLock()
|
||||
defer offsetMu.RUnlock()
|
||||
return t0.Add(offset)
|
||||
}
|
||||
defer func() { nowFunc = time.Now }()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
db := newTestDB(t, "magicquery")
|
||||
defer closeDB(t, db)
|
||||
|
||||
db.SetMaxOpenConns(1)
|
||||
|
||||
for _, ec := range execCases {
|
||||
ec := ec
|
||||
name := fmt.Sprintf("expired=%t,badReset=%t", ec.expired, ec.badReset)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
db.clearAllConns(t)
|
||||
|
||||
db.SetMaxIdleConns(1)
|
||||
db.SetConnMaxLifetime(10 * time.Second)
|
||||
|
||||
conn, err := db.conn(ctx, alwaysNewConn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
afterPutConn := make(chan struct{})
|
||||
waitingForConn := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
conn, err := db.conn(ctx, alwaysNewConn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
db.putConn(conn, err, false)
|
||||
close(afterPutConn)
|
||||
}()
|
||||
go func() {
|
||||
for {
|
||||
db.mu.Lock()
|
||||
ct := len(db.connRequests)
|
||||
db.mu.Unlock()
|
||||
if ct > 0 {
|
||||
close(waitingForConn)
|
||||
return
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
<-waitingForConn
|
||||
|
||||
offsetMu.Lock()
|
||||
if ec.expired {
|
||||
offset = 11 * time.Second
|
||||
} else {
|
||||
offset = time.Duration(0)
|
||||
}
|
||||
offsetMu.Unlock()
|
||||
|
||||
conn.ci.(*fakeConn).stickyBad = ec.badReset
|
||||
|
||||
db.putConn(conn, err, true)
|
||||
|
||||
<-afterPutConn
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestIssue20575 ensures the Rows from query does not block
|
||||
// closing a transaction. Ensure Rows is closed while closing a trasaction.
|
||||
func TestIssue20575(t *testing.T) {
|
||||
|
||||
@@ -213,9 +213,6 @@ type decodeState struct {
|
||||
savedError error
|
||||
useNumber bool
|
||||
disallowUnknownFields bool
|
||||
// safeUnquote is the number of current string literal bytes that don't
|
||||
// need to be unquoted. When negative, no bytes need unquoting.
|
||||
safeUnquote int
|
||||
}
|
||||
|
||||
// readIndex returns the position of the last byte read.
|
||||
@@ -317,27 +314,13 @@ func (d *decodeState) rescanLiteral() {
|
||||
Switch:
|
||||
switch data[i-1] {
|
||||
case '"': // string
|
||||
// safeUnquote is initialized at -1, which means that all bytes
|
||||
// checked so far can be unquoted at a later time with no work
|
||||
// at all. When reaching the closing '"', if safeUnquote is
|
||||
// still -1, all bytes can be unquoted with no work. Otherwise,
|
||||
// only those bytes up until the first '\\' or non-ascii rune
|
||||
// can be safely unquoted.
|
||||
safeUnquote := -1
|
||||
for ; i < len(data); i++ {
|
||||
if c := data[i]; c == '\\' {
|
||||
if safeUnquote < 0 { // first unsafe byte
|
||||
safeUnquote = int(i - d.off)
|
||||
}
|
||||
switch data[i] {
|
||||
case '\\':
|
||||
i++ // escaped char
|
||||
} else if c == '"' {
|
||||
d.safeUnquote = safeUnquote
|
||||
case '"':
|
||||
i++ // tokenize the closing quote too
|
||||
break Switch
|
||||
} else if c >= utf8.RuneSelf {
|
||||
if safeUnquote < 0 { // first unsafe byte
|
||||
safeUnquote = int(i - d.off)
|
||||
}
|
||||
}
|
||||
}
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': // number
|
||||
@@ -691,7 +674,7 @@ func (d *decodeState) object(v reflect.Value) error {
|
||||
start := d.readIndex()
|
||||
d.rescanLiteral()
|
||||
item := d.data[start:d.readIndex()]
|
||||
key, ok := d.unquoteBytes(item)
|
||||
key, ok := unquoteBytes(item)
|
||||
if !ok {
|
||||
panic(phasePanicMsg)
|
||||
}
|
||||
@@ -892,7 +875,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
|
||||
d.saveError(&UnmarshalTypeError{Value: val, Type: v.Type(), Offset: int64(d.readIndex())})
|
||||
return nil
|
||||
}
|
||||
s, ok := d.unquoteBytes(item)
|
||||
s, ok := unquoteBytes(item)
|
||||
if !ok {
|
||||
if fromQuoted {
|
||||
return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())
|
||||
@@ -943,7 +926,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
|
||||
}
|
||||
|
||||
case '"': // string
|
||||
s, ok := d.unquoteBytes(item)
|
||||
s, ok := unquoteBytes(item)
|
||||
if !ok {
|
||||
if fromQuoted {
|
||||
return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())
|
||||
@@ -1103,7 +1086,7 @@ func (d *decodeState) objectInterface() map[string]interface{} {
|
||||
start := d.readIndex()
|
||||
d.rescanLiteral()
|
||||
item := d.data[start:d.readIndex()]
|
||||
key, ok := d.unquote(item)
|
||||
key, ok := unquote(item)
|
||||
if !ok {
|
||||
panic(phasePanicMsg)
|
||||
}
|
||||
@@ -1152,7 +1135,7 @@ func (d *decodeState) literalInterface() interface{} {
|
||||
return c == 't'
|
||||
|
||||
case '"': // string
|
||||
s, ok := d.unquote(item)
|
||||
s, ok := unquote(item)
|
||||
if !ok {
|
||||
panic(phasePanicMsg)
|
||||
}
|
||||
@@ -1195,26 +1178,38 @@ func getu4(s []byte) rune {
|
||||
|
||||
// unquote converts a quoted JSON string literal s into an actual string t.
|
||||
// The rules are different than for Go, so cannot use strconv.Unquote.
|
||||
// The first byte in s must be '"'.
|
||||
func (d *decodeState) unquote(s []byte) (t string, ok bool) {
|
||||
s, ok = d.unquoteBytes(s)
|
||||
func unquote(s []byte) (t string, ok bool) {
|
||||
s, ok = unquoteBytes(s)
|
||||
t = string(s)
|
||||
return
|
||||
}
|
||||
|
||||
func (d *decodeState) unquoteBytes(s []byte) (t []byte, ok bool) {
|
||||
// We already know that s[0] == '"'. However, we don't know that the
|
||||
// closing quote exists in all cases, such as when the string is nested
|
||||
// via the ",string" option.
|
||||
if len(s) < 2 || s[len(s)-1] != '"' {
|
||||
func unquoteBytes(s []byte) (t []byte, ok bool) {
|
||||
if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
|
||||
return
|
||||
}
|
||||
s = s[1 : len(s)-1]
|
||||
|
||||
// If there are no unusual characters, no unquoting is needed, so return
|
||||
// a slice of the original bytes.
|
||||
r := d.safeUnquote
|
||||
if r == -1 {
|
||||
// Check for unusual characters. If there are none,
|
||||
// then no unquoting is needed, so return a slice of the
|
||||
// original bytes.
|
||||
r := 0
|
||||
for r < len(s) {
|
||||
c := s[r]
|
||||
if c == '\\' || c == '"' || c < ' ' {
|
||||
break
|
||||
}
|
||||
if c < utf8.RuneSelf {
|
||||
r++
|
||||
continue
|
||||
}
|
||||
rr, size := utf8.DecodeRune(s[r:])
|
||||
if rr == utf8.RuneError && size == 1 {
|
||||
break
|
||||
}
|
||||
r += size
|
||||
}
|
||||
if r == len(s) {
|
||||
return s, true
|
||||
}
|
||||
|
||||
|
||||
@@ -2419,7 +2419,7 @@ func (m *textUnmarshalerString) UnmarshalText(text []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Test unmarshal to a map, with map key is a user defined type.
|
||||
// Test unmarshal to a map, where the map key is a user defined type.
|
||||
// See golang.org/issues/34437.
|
||||
func TestUnmarshalMapWithTextUnmarshalerStringKey(t *testing.T) {
|
||||
var p map[textUnmarshalerString]string
|
||||
@@ -2428,6 +2428,51 @@ func TestUnmarshalMapWithTextUnmarshalerStringKey(t *testing.T) {
|
||||
}
|
||||
|
||||
if _, ok := p["foo"]; !ok {
|
||||
t.Errorf(`Key "foo" is not existed in map: %v`, p)
|
||||
t.Errorf(`Key "foo" does not exist in map: %v`, p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalRescanLiteralMangledUnquote(t *testing.T) {
|
||||
// See golang.org/issues/38105.
|
||||
var p map[textUnmarshalerString]string
|
||||
if err := Unmarshal([]byte(`{"开源":"12345开源"}`), &p); err != nil {
|
||||
t.Fatalf("Unmarshal unexpected error: %v", err)
|
||||
}
|
||||
if _, ok := p["开源"]; !ok {
|
||||
t.Errorf(`Key "开源" does not exist in map: %v`, p)
|
||||
}
|
||||
|
||||
// See golang.org/issues/38126.
|
||||
type T struct {
|
||||
F1 string `json:"F1,string"`
|
||||
}
|
||||
t1 := T{"aaa\tbbb"}
|
||||
|
||||
b, err := Marshal(t1)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal unexpected error: %v", err)
|
||||
}
|
||||
var t2 T
|
||||
if err := Unmarshal(b, &t2); err != nil {
|
||||
t.Fatalf("Unmarshal unexpected error: %v", err)
|
||||
}
|
||||
if t1 != t2 {
|
||||
t.Errorf("Marshal and Unmarshal roundtrip mismatch: want %q got %q", t1, t2)
|
||||
}
|
||||
|
||||
// See golang.org/issues/39555.
|
||||
input := map[textUnmarshalerString]string{"FOO": "", `"`: ""}
|
||||
|
||||
encoded, err := Marshal(input)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal unexpected error: %v", err)
|
||||
}
|
||||
var got map[textUnmarshalerString]string
|
||||
if err := Unmarshal(encoded, &got); err != nil {
|
||||
t.Fatalf("Unmarshal unexpected error: %v", err)
|
||||
}
|
||||
want := map[textUnmarshalerString]string{"foo": "", `"`: ""}
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Fatalf("Unexpected roundtrip result:\nwant: %q\ngot: %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -635,11 +635,12 @@ func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) {
|
||||
return
|
||||
}
|
||||
if opts.quoted {
|
||||
b := make([]byte, 0, v.Len()+2)
|
||||
b = append(b, '"')
|
||||
b = append(b, []byte(v.String())...)
|
||||
b = append(b, '"')
|
||||
e.stringBytes(b, opts.escapeHTML)
|
||||
e2 := newEncodeState()
|
||||
// Since we encode the string twice, we only need to escape HTML
|
||||
// the first time.
|
||||
e2.string(v.String(), opts.escapeHTML)
|
||||
e.stringBytes(e2.Bytes(), false)
|
||||
encodeStatePool.Put(e2)
|
||||
} else {
|
||||
e.string(v.String(), opts.escapeHTML)
|
||||
}
|
||||
|
||||
@@ -79,37 +79,66 @@ type StringTag struct {
|
||||
NumberStr Number `json:",string"`
|
||||
}
|
||||
|
||||
var stringTagExpected = `{
|
||||
"BoolStr": "true",
|
||||
"IntStr": "42",
|
||||
"UintptrStr": "44",
|
||||
"StrStr": "\"xzbit\"",
|
||||
"NumberStr": "46"
|
||||
}`
|
||||
func TestRoundtripStringTag(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in StringTag
|
||||
want string // empty to just test that we roundtrip
|
||||
}{
|
||||
{
|
||||
name: "AllTypes",
|
||||
in: StringTag{
|
||||
BoolStr: true,
|
||||
IntStr: 42,
|
||||
UintptrStr: 44,
|
||||
StrStr: "xzbit",
|
||||
NumberStr: "46",
|
||||
},
|
||||
want: `{
|
||||
"BoolStr": "true",
|
||||
"IntStr": "42",
|
||||
"UintptrStr": "44",
|
||||
"StrStr": "\"xzbit\"",
|
||||
"NumberStr": "46"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
// See golang.org/issues/38173.
|
||||
name: "StringDoubleEscapes",
|
||||
in: StringTag{
|
||||
StrStr: "\b\f\n\r\t\"\\",
|
||||
NumberStr: "0", // just to satisfy the roundtrip
|
||||
},
|
||||
want: `{
|
||||
"BoolStr": "false",
|
||||
"IntStr": "0",
|
||||
"UintptrStr": "0",
|
||||
"StrStr": "\"\\u0008\\u000c\\n\\r\\t\\\"\\\\\"",
|
||||
"NumberStr": "0"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// Indent with a tab prefix to make the multi-line string
|
||||
// literals in the table nicer to read.
|
||||
got, err := MarshalIndent(&test.in, "\t\t\t", "\t")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := string(got); got != test.want {
|
||||
t.Fatalf(" got: %s\nwant: %s\n", got, test.want)
|
||||
}
|
||||
|
||||
func TestStringTag(t *testing.T) {
|
||||
var s StringTag
|
||||
s.BoolStr = true
|
||||
s.IntStr = 42
|
||||
s.UintptrStr = 44
|
||||
s.StrStr = "xzbit"
|
||||
s.NumberStr = "46"
|
||||
got, err := MarshalIndent(&s, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := string(got); got != stringTagExpected {
|
||||
t.Fatalf(" got: %s\nwant: %s\n", got, stringTagExpected)
|
||||
}
|
||||
|
||||
// Verify that it round-trips.
|
||||
var s2 StringTag
|
||||
err = NewDecoder(bytes.NewReader(got)).Decode(&s2)
|
||||
if err != nil {
|
||||
t.Fatalf("Decode: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(s, s2) {
|
||||
t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", s, string(got), s2)
|
||||
// Verify that it round-trips.
|
||||
var s2 StringTag
|
||||
if err := Unmarshal(got, &s2); err != nil {
|
||||
t.Fatalf("Decode: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(test.in, s2) {
|
||||
t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", test.in, string(got), s2)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,14 +144,15 @@ func TestEncoderSetEscapeHTML(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"stringOption", stringOption,
|
||||
`{"bar":"\"\u003chtml\u003efoobar\u003c/html\u003e\""}`,
|
||||
`{"bar":"\"\\u003chtml\\u003efoobar\\u003c/html\\u003e\""}`,
|
||||
`{"bar":"\"<html>foobar</html>\""}`,
|
||||
},
|
||||
} {
|
||||
var buf bytes.Buffer
|
||||
enc := NewEncoder(&buf)
|
||||
if err := enc.Encode(tt.v); err != nil {
|
||||
t.Fatalf("Encode(%s): %s", tt.name, err)
|
||||
t.Errorf("Encode(%s): %s", tt.name, err)
|
||||
continue
|
||||
}
|
||||
if got := strings.TrimSpace(buf.String()); got != tt.wantEscape {
|
||||
t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.wantEscape)
|
||||
@@ -159,7 +160,8 @@ func TestEncoderSetEscapeHTML(t *testing.T) {
|
||||
buf.Reset()
|
||||
enc.SetEscapeHTML(false)
|
||||
if err := enc.Encode(tt.v); err != nil {
|
||||
t.Fatalf("SetEscapeHTML(false) Encode(%s): %s", tt.name, err)
|
||||
t.Errorf("SetEscapeHTML(false) Encode(%s): %s", tt.name, err)
|
||||
continue
|
||||
}
|
||||
if got := strings.TrimSpace(buf.String()); got != tt.want {
|
||||
t.Errorf("SetEscapeHTML(false) Encode(%s) = %#q, want %#q",
|
||||
|
||||
@@ -153,6 +153,7 @@ var pkgDeps = map[string][]string{
|
||||
"internal/syscall/unix": {"L0", "syscall"},
|
||||
"internal/syscall/windows": {"L0", "syscall", "internal/syscall/windows/sysdll", "unicode/utf16"},
|
||||
"internal/syscall/windows/registry": {"L0", "syscall", "internal/syscall/windows/sysdll", "unicode/utf16"},
|
||||
"internal/syscall/execenv": {"L0", "syscall", "internal/syscall/windows", "unicode/utf16"},
|
||||
"time": {
|
||||
// "L0" without the "io" package:
|
||||
"errors",
|
||||
@@ -170,10 +171,10 @@ var pkgDeps = map[string][]string{
|
||||
"internal/cfg": {"L0"},
|
||||
"internal/poll": {"L0", "internal/oserror", "internal/race", "syscall", "time", "unicode/utf16", "unicode/utf8", "internal/syscall/windows", "internal/syscall/unix"},
|
||||
"internal/testlog": {"L0"},
|
||||
"os": {"L1", "os", "syscall", "time", "internal/oserror", "internal/poll", "internal/syscall/windows", "internal/syscall/unix", "internal/testlog"},
|
||||
"os": {"L1", "os", "syscall", "time", "internal/oserror", "internal/poll", "internal/syscall/windows", "internal/syscall/unix", "internal/syscall/execenv", "internal/testlog"},
|
||||
"path/filepath": {"L2", "os", "syscall", "internal/syscall/windows"},
|
||||
"io/ioutil": {"L2", "os", "path/filepath", "time"},
|
||||
"os/exec": {"L2", "os", "context", "path/filepath", "syscall"},
|
||||
"os/exec": {"L2", "os", "context", "path/filepath", "syscall", "internal/syscall/execenv"},
|
||||
"os/signal": {"L2", "os", "syscall"},
|
||||
|
||||
// OS enables basic operating system functionality,
|
||||
|
||||
@@ -62,9 +62,6 @@ func Examples(testFiles ...*ast.File) []*Example {
|
||||
if !ok || f.Recv != nil {
|
||||
continue
|
||||
}
|
||||
if params := f.Type.Params; len(params.List) != 0 {
|
||||
continue // function has params; not a valid example
|
||||
}
|
||||
numDecl++
|
||||
name := f.Name.Name
|
||||
if isTest(name, "Test") || isTest(name, "Benchmark") {
|
||||
@@ -74,6 +71,9 @@ func Examples(testFiles ...*ast.File) []*Example {
|
||||
if !isTest(name, "Example") {
|
||||
continue
|
||||
}
|
||||
if params := f.Type.Params; len(params.List) != 0 {
|
||||
continue // function has params; not a valid example
|
||||
}
|
||||
if f.Body == nil { // ast.File.Body nil dereference (see issue 28044)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -331,25 +331,65 @@ func main() {
|
||||
}
|
||||
`
|
||||
|
||||
const exampleWholeFileFunction = `package foo_test
|
||||
|
||||
func Foo(x int) {
|
||||
}
|
||||
|
||||
func Example() {
|
||||
fmt.Println("Hello, world!")
|
||||
// Output: Hello, world!
|
||||
}
|
||||
`
|
||||
|
||||
const exampleWholeFileFunctionOutput = `package main
|
||||
|
||||
func Foo(x int) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, world!")
|
||||
}
|
||||
`
|
||||
|
||||
var exampleWholeFileTestCases = []struct {
|
||||
Title, Source, Play, Output string
|
||||
}{
|
||||
{
|
||||
"Methods",
|
||||
exampleWholeFile,
|
||||
exampleWholeFileOutput,
|
||||
"Hello, world!\n",
|
||||
},
|
||||
{
|
||||
"Function",
|
||||
exampleWholeFileFunction,
|
||||
exampleWholeFileFunctionOutput,
|
||||
"Hello, world!\n",
|
||||
},
|
||||
}
|
||||
|
||||
func TestExamplesWholeFile(t *testing.T) {
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleWholeFile), parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
es := doc.Examples(file)
|
||||
if len(es) != 1 {
|
||||
t.Fatalf("wrong number of examples; got %d want 1", len(es))
|
||||
}
|
||||
e := es[0]
|
||||
if e.Name != "" {
|
||||
t.Errorf("got Name == %q, want %q", e.Name, "")
|
||||
}
|
||||
if g, w := formatFile(t, fset, e.Play), exampleWholeFileOutput; g != w {
|
||||
t.Errorf("got Play == %q, want %q", g, w)
|
||||
}
|
||||
if g, w := e.Output, "Hello, world!\n"; g != w {
|
||||
t.Errorf("got Output == %q, want %q", g, w)
|
||||
for _, c := range exampleWholeFileTestCases {
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", strings.NewReader(c.Source), parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
es := doc.Examples(file)
|
||||
if len(es) != 1 {
|
||||
t.Fatalf("%s: wrong number of examples; got %d want 1", c.Title, len(es))
|
||||
}
|
||||
e := es[0]
|
||||
if e.Name != "" {
|
||||
t.Errorf("%s: got Name == %q, want %q", c.Title, e.Name, "")
|
||||
}
|
||||
if g, w := formatFile(t, fset, e.Play), c.Play; g != w {
|
||||
t.Errorf("%s: got Play == %q, want %q", c.Title, g, w)
|
||||
}
|
||||
if g, w := e.Output, c.Output; g != w {
|
||||
t.Errorf("%s: got Output == %q, want %q", c.Title, g, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -133,13 +133,7 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode)
|
||||
// first error encountered are returned.
|
||||
//
|
||||
func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, mode Mode) (pkgs map[string]*ast.Package, first error) {
|
||||
fd, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
list, err := fd.Readdir(-1)
|
||||
list, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ type Hash struct {
|
||||
// which does call h.initSeed.)
|
||||
func (h *Hash) initSeed() {
|
||||
if h.seed.s == 0 {
|
||||
h.SetSeed(MakeSeed())
|
||||
h.setSeed(MakeSeed())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,12 +124,17 @@ func (h *Hash) Seed() Seed {
|
||||
// Two Hash objects with different seeds will very likely behave differently.
|
||||
// Any bytes added to h before this call will be discarded.
|
||||
func (h *Hash) SetSeed(seed Seed) {
|
||||
h.setSeed(seed)
|
||||
h.n = 0
|
||||
}
|
||||
|
||||
// setSeed sets seed without discarding accumulated data.
|
||||
func (h *Hash) setSeed(seed Seed) {
|
||||
if seed.s == 0 {
|
||||
panic("maphash: use of uninitialized Seed")
|
||||
}
|
||||
h.seed = seed
|
||||
h.state = seed
|
||||
h.n = 0
|
||||
}
|
||||
|
||||
// Reset discards all bytes added to h.
|
||||
|
||||
@@ -83,6 +83,29 @@ func TestHashHighBytes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepeat(t *testing.T) {
|
||||
h1 := new(Hash)
|
||||
h1.WriteString("testing")
|
||||
sum1 := h1.Sum64()
|
||||
|
||||
h1.Reset()
|
||||
h1.WriteString("testing")
|
||||
sum2 := h1.Sum64()
|
||||
|
||||
if sum1 != sum2 {
|
||||
t.Errorf("different sum after reseting: %#x != %#x", sum1, sum2)
|
||||
}
|
||||
|
||||
h2 := new(Hash)
|
||||
h2.SetSeed(h1.Seed())
|
||||
h2.WriteString("testing")
|
||||
sum3 := h2.Sum64()
|
||||
|
||||
if sum1 != sum3 {
|
||||
t.Errorf("different sum on the same seed: %#x != %#x", sum1, sum3)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure a Hash implements the hash.Hash and hash.Hash64 interfaces.
|
||||
var _ hash.Hash = &Hash{}
|
||||
var _ hash.Hash64 = &Hash{}
|
||||
|
||||
19
src/internal/syscall/execenv/execenv_default.go
Normal file
19
src/internal/syscall/execenv/execenv_default.go
Normal file
@@ -0,0 +1,19 @@
|
||||
// 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 !windows
|
||||
|
||||
package execenv
|
||||
|
||||
import "syscall"
|
||||
|
||||
// Default will return the default environment
|
||||
// variables based on the process attributes
|
||||
// provided.
|
||||
//
|
||||
// Defaults to syscall.Environ() on all platforms
|
||||
// other than Windows.
|
||||
func Default(sys *syscall.SysProcAttr) ([]string, error) {
|
||||
return syscall.Environ(), nil
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// 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 os
|
||||
// +build windows
|
||||
|
||||
package execenv
|
||||
|
||||
import (
|
||||
"internal/syscall/windows"
|
||||
@@ -11,9 +13,17 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func environForSysProcAttr(sys *syscall.SysProcAttr) (env []string, err error) {
|
||||
// Default will return the default environment
|
||||
// variables based on the process attributes
|
||||
// provided.
|
||||
//
|
||||
// If the process attributes contain a token, then
|
||||
// the environment variables will be sourced from
|
||||
// the defaults for that user token, otherwise they
|
||||
// will be sourced from syscall.Environ().
|
||||
func Default(sys *syscall.SysProcAttr) (env []string, err error) {
|
||||
if sys == nil || sys.Token == 0 {
|
||||
return Environ(), nil
|
||||
return syscall.Environ(), nil
|
||||
}
|
||||
var block *uint16
|
||||
err = windows.CreateEnvironmentBlock(&block, sys.Token, false)
|
||||
@@ -740,7 +740,8 @@ func (z nat) divLarge(u, uIn, vIn nat) (q, r nat) {
|
||||
// The remainder overwrites input u.
|
||||
//
|
||||
// Precondition:
|
||||
// - len(q) >= len(u)-len(v)
|
||||
// - q is large enough to hold the quotient u / v
|
||||
// which has a maximum length of len(u)-len(v)+1.
|
||||
func (q nat) divBasic(u, v nat) {
|
||||
n := len(v)
|
||||
m := len(u) - n
|
||||
@@ -779,6 +780,8 @@ func (q nat) divBasic(u, v nat) {
|
||||
}
|
||||
|
||||
// D4.
|
||||
// Compute the remainder u - (q̂*v) << (_W*j).
|
||||
// The subtraction may overflow if q̂ estimate was off by one.
|
||||
qhatv[n] = mulAddVWW(qhatv[0:n], v, qhat, 0)
|
||||
qhl := len(qhatv)
|
||||
if j+qhl > len(u) && qhatv[n] == 0 {
|
||||
@@ -787,7 +790,11 @@ func (q nat) divBasic(u, v nat) {
|
||||
c := subVV(u[j:j+qhl], u[j:], qhatv)
|
||||
if c != 0 {
|
||||
c := addVV(u[j:j+n], u[j:], v)
|
||||
u[j+n] += c
|
||||
// If n == qhl, the carry from subVV and the carry from addVV
|
||||
// cancel out and don't affect u[j+n].
|
||||
if n < qhl {
|
||||
u[j+n] += c
|
||||
}
|
||||
qhat--
|
||||
}
|
||||
|
||||
@@ -827,6 +834,10 @@ func (z nat) divRecursive(u, v nat) {
|
||||
putNat(tmp)
|
||||
}
|
||||
|
||||
// divRecursiveStep computes the division of u by v.
|
||||
// - z must be large enough to hold the quotient
|
||||
// - the quotient will overwrite z
|
||||
// - the remainder will overwrite u
|
||||
func (z nat) divRecursiveStep(u, v nat, depth int, tmp *nat, temps []*nat) {
|
||||
u = u.norm()
|
||||
v = v.norm()
|
||||
|
||||
@@ -786,3 +786,21 @@ func TestNatDiv(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestIssue37499 triggers the edge case of divBasic where
|
||||
// the inaccurate estimate of the first word's quotient
|
||||
// happens at the very beginning of the loop.
|
||||
func TestIssue37499(t *testing.T) {
|
||||
// Choose u and v such that v is slightly larger than u >> N.
|
||||
// This tricks divBasic into choosing 1 as the first word
|
||||
// of the quotient. This works in both 32-bit and 64-bit settings.
|
||||
u := natFromString("0x2b6c385a05be027f5c22005b63c42a1165b79ff510e1706b39f8489c1d28e57bb5ba4ef9fd9387a3e344402c0a453381")
|
||||
v := natFromString("0x2b6c385a05be027f5c22005b63c42a1165b79ff510e1706c")
|
||||
|
||||
q := nat(nil).make(8)
|
||||
q.divBasic(u, v)
|
||||
q = q.norm()
|
||||
if s := string(q.utoa(16)); s != "fffffffffffffffffffffffffffffffffffffffffffffffb" {
|
||||
t.Fatalf("incorrect quotient: %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,6 +411,7 @@ func checkIfNoneMatch(w ResponseWriter, r *Request) condResult {
|
||||
}
|
||||
if buf[0] == ',' {
|
||||
buf = buf[1:]
|
||||
continue
|
||||
}
|
||||
if buf[0] == '*' {
|
||||
return condFalse
|
||||
|
||||
@@ -849,6 +849,15 @@ func TestServeContent(t *testing.T) {
|
||||
wantStatus: 200,
|
||||
wantContentType: "text/css; charset=utf-8",
|
||||
},
|
||||
"if_none_match_malformed": {
|
||||
file: "testdata/style.css",
|
||||
serveETag: `"foo"`,
|
||||
reqHeader: map[string]string{
|
||||
"If-None-Match": `,`,
|
||||
},
|
||||
wantStatus: 200,
|
||||
wantContentType: "text/css; charset=utf-8",
|
||||
},
|
||||
"range_good": {
|
||||
file: "testdata/style.css",
|
||||
serveETag: `"A"`,
|
||||
|
||||
@@ -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
|
||||
@@ -877,21 +888,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
|
||||
}
|
||||
@@ -1310,7 +1327,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
|
||||
}
|
||||
|
||||
@@ -1560,6 +1577,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)
|
||||
}
|
||||
@@ -1871,6 +1899,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()
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package os
|
||||
|
||||
import "syscall"
|
||||
|
||||
func environForSysProcAttr(sys *syscall.SysProcAttr) ([]string, error) {
|
||||
return Environ(), nil
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"internal/syscall/execenv"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -222,11 +223,11 @@ func interfaceEqual(a, b interface{}) bool {
|
||||
return a == b
|
||||
}
|
||||
|
||||
func (c *Cmd) envv() []string {
|
||||
func (c *Cmd) envv() ([]string, error) {
|
||||
if c.Env != nil {
|
||||
return c.Env
|
||||
return c.Env, nil
|
||||
}
|
||||
return os.Environ()
|
||||
return execenv.Default(c.SysProcAttr)
|
||||
}
|
||||
|
||||
func (c *Cmd) argv() []string {
|
||||
@@ -413,11 +414,15 @@ func (c *Cmd) Start() error {
|
||||
}
|
||||
c.childFiles = append(c.childFiles, c.ExtraFiles...)
|
||||
|
||||
var err error
|
||||
envv, err := c.envv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{
|
||||
Dir: c.Dir,
|
||||
Files: c.childFiles,
|
||||
Env: addCriticalEnv(dedupEnv(c.envv())),
|
||||
Env: addCriticalEnv(dedupEnv(envv)),
|
||||
Sys: c.SysProcAttr,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
package os
|
||||
|
||||
import (
|
||||
"internal/syscall/execenv"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
@@ -39,7 +40,7 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
|
||||
Sys: attr.Sys,
|
||||
}
|
||||
if sysattr.Env == nil {
|
||||
sysattr.Env, err = environForSysProcAttr(sysattr.Sys)
|
||||
sysattr.Env, err = execenv.Default(sysattr.Sys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2448,3 +2448,38 @@ func TestDirSeek(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that opening a file does not change its permissions. Issue 38225.
|
||||
func TestOpenFileKeepsPermissions(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir, err := ioutil.TempDir("", "TestOpenFileKeepsPermissions")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer RemoveAll(dir)
|
||||
name := filepath.Join(dir, "x")
|
||||
f, err := Create(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
f, err = OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if fi, err := f.Stat(); err != nil {
|
||||
t.Error(err)
|
||||
} else if fi.Mode()&0222 == 0 {
|
||||
t.Errorf("f.Stat.Mode after OpenFile is %v, should be writable", fi.Mode())
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if fi, err := Stat(name); err != nil {
|
||||
t.Error(err)
|
||||
} else if fi.Mode()&0222 == 0 {
|
||||
t.Errorf("Stat after OpenFile is %v, should be writable", fi.Mode())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -789,6 +789,11 @@ var loop1, loop2 Loop
|
||||
var loopy1, loopy2 Loopy
|
||||
var cycleMap1, cycleMap2, cycleMap3 map[string]interface{}
|
||||
|
||||
type structWithSelfPtr struct {
|
||||
p *structWithSelfPtr
|
||||
s string
|
||||
}
|
||||
|
||||
func init() {
|
||||
loop1 = &loop2
|
||||
loop2 = &loop1
|
||||
@@ -845,6 +850,7 @@ var deepEqualTests = []DeepEqualTest{
|
||||
{[]float64{math.NaN()}, self{}, true},
|
||||
{map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false},
|
||||
{map[float64]float64{math.NaN(): 1}, self{}, true},
|
||||
{&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, false},
|
||||
|
||||
// Nil vs empty: not the same.
|
||||
{[]int{}, []int(nil), false},
|
||||
|
||||
@@ -45,8 +45,20 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
|
||||
}
|
||||
|
||||
if hard(v1, v2) {
|
||||
addr1 := v1.ptr
|
||||
addr2 := v2.ptr
|
||||
// For a Ptr or Map value, we need to check flagIndir,
|
||||
// which we do by calling the pointer method.
|
||||
// For Slice or Interface, flagIndir is always set,
|
||||
// and using v.ptr suffices.
|
||||
ptrval := func(v Value) unsafe.Pointer {
|
||||
switch v.Kind() {
|
||||
case Ptr, Map:
|
||||
return v.pointer()
|
||||
default:
|
||||
return v.ptr
|
||||
}
|
||||
}
|
||||
addr1 := ptrval(v1)
|
||||
addr2 := ptrval(v2)
|
||||
if uintptr(addr1) > uintptr(addr2) {
|
||||
// Canonicalize order to reduce number of entries in visited.
|
||||
// Assumes non-moving garbage collector.
|
||||
|
||||
@@ -3067,6 +3067,7 @@ func ifaceIndir(t *rtype) bool {
|
||||
return t.kind&kindDirectIface == 0
|
||||
}
|
||||
|
||||
// Note: this type must agree with runtime.bitvector.
|
||||
type bitVector struct {
|
||||
n uint32 // number of bits
|
||||
data []byte
|
||||
|
||||
@@ -574,6 +574,13 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool) {
|
||||
// Convert v to type typ if v is assignable to a variable
|
||||
// of type t in the language spec.
|
||||
// See issue 28761.
|
||||
if typ.Kind() == Interface {
|
||||
// We must clear the destination before calling assignTo,
|
||||
// in case assignTo writes (with memory barriers) to the
|
||||
// target location used as scratch space. See issue 39541.
|
||||
*(*uintptr)(addr) = 0
|
||||
*(*uintptr)(add(addr, ptrSize, "typ.size == 2*ptrSize")) = 0
|
||||
}
|
||||
v = v.assignTo("reflect.MakeFunc", typ, addr)
|
||||
|
||||
// We are writing to stack. No write barrier.
|
||||
@@ -2367,6 +2374,7 @@ func NewAt(typ Type, p unsafe.Pointer) Value {
|
||||
// assignTo returns a value v that can be assigned directly to typ.
|
||||
// It panics if v is not assignable to typ.
|
||||
// For a conversion to an interface type, target is a suggested scratch space to use.
|
||||
// target must be initialized memory (or nil).
|
||||
func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value {
|
||||
if v.flag&flagMethod != 0 {
|
||||
v = makeMethodValue(context, v)
|
||||
|
||||
@@ -158,9 +158,19 @@ func nilinterhash(p unsafe.Pointer, h uintptr) uintptr {
|
||||
// is slower but more general and is used for hashing interface types
|
||||
// (called from interhash or nilinterhash, above) or for hashing in
|
||||
// maps generated by reflect.MapOf (reflect_typehash, below).
|
||||
// Note: this function must match the compiler generated
|
||||
// functions exactly. See issue 37716.
|
||||
func typehash(t *_type, p unsafe.Pointer, h uintptr) uintptr {
|
||||
if t.tflag&tflagRegularMemory != 0 {
|
||||
return memhash(p, h, t.size)
|
||||
// Handle ptr sizes specially, see issue 37086.
|
||||
switch t.size {
|
||||
case 4:
|
||||
return memhash32(p, h)
|
||||
case 8:
|
||||
return memhash64(p, h)
|
||||
default:
|
||||
return memhash(p, h, t.size)
|
||||
}
|
||||
}
|
||||
switch t.kind & kindMask {
|
||||
case kindFloat32:
|
||||
@@ -187,12 +197,28 @@ func typehash(t *_type, p unsafe.Pointer, h uintptr) uintptr {
|
||||
return h
|
||||
case kindStruct:
|
||||
s := (*structtype)(unsafe.Pointer(t))
|
||||
memStart := uintptr(0)
|
||||
memEnd := uintptr(0)
|
||||
for _, f := range s.fields {
|
||||
// TODO: maybe we could hash several contiguous fields all at once.
|
||||
if memEnd > memStart && (f.name.isBlank() || f.offset() != memEnd || f.typ.tflag&tflagRegularMemory == 0) {
|
||||
// flush any pending regular memory hashing
|
||||
h = memhash(add(p, memStart), h, memEnd-memStart)
|
||||
memStart = memEnd
|
||||
}
|
||||
if f.name.isBlank() {
|
||||
continue
|
||||
}
|
||||
h = typehash(f.typ, add(p, f.offset()), h)
|
||||
if f.typ.tflag&tflagRegularMemory == 0 {
|
||||
h = typehash(f.typ, add(p, f.offset()), h)
|
||||
continue
|
||||
}
|
||||
if memStart == memEnd {
|
||||
memStart = f.offset()
|
||||
}
|
||||
memEnd = f.offset() + f.typ.size
|
||||
}
|
||||
if memEnd > memStart {
|
||||
h = memhash(add(p, memStart), h, memEnd-memStart)
|
||||
}
|
||||
return h
|
||||
default:
|
||||
|
||||
@@ -8,8 +8,10 @@ import "unsafe"
|
||||
|
||||
func checkptrAlignment(p unsafe.Pointer, elem *_type, n uintptr) {
|
||||
// Check that (*[n]elem)(p) is appropriately aligned.
|
||||
// Note that we allow unaligned pointers if the types they point to contain
|
||||
// no pointers themselves. See issue 37298.
|
||||
// TODO(mdempsky): What about fieldAlign?
|
||||
if uintptr(p)&(uintptr(elem.align)-1) != 0 {
|
||||
if elem.ptrdata != 0 && uintptr(p)&(uintptr(elem.align)-1) != 0 {
|
||||
throw("checkptr: unsafe pointer conversion")
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ func TestCheckPtr(t *testing.T) {
|
||||
cmd string
|
||||
want string
|
||||
}{
|
||||
{"CheckPtrAlignment", "fatal error: checkptr: unsafe pointer conversion\n"},
|
||||
{"CheckPtrAlignmentPtr", "fatal error: checkptr: unsafe pointer conversion\n"},
|
||||
{"CheckPtrAlignmentNoPtr", ""},
|
||||
{"CheckPtrArithmetic", "fatal error: checkptr: unsafe pointer arithmetic\n"},
|
||||
{"CheckPtrSize", "fatal error: checkptr: unsafe pointer conversion\n"},
|
||||
{"CheckPtrSmall", "fatal error: checkptr: unsafe pointer arithmetic\n"},
|
||||
@@ -38,6 +39,12 @@ func TestCheckPtr(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
if tc.want == "" {
|
||||
if len(got) > 0 {
|
||||
t.Errorf("output:\n%s\nwant no output", got)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !strings.HasPrefix(string(got), tc.want) {
|
||||
t.Errorf("output:\n%s\n\nwant output starting with: %s", got, tc.want)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
// Offsets into internal/cpu records for use in assembly.
|
||||
const (
|
||||
offsetX86HasAVX = unsafe.Offsetof(cpu.X86.HasAVX)
|
||||
offsetX86HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2)
|
||||
offsetX86HasERMS = unsafe.Offsetof(cpu.X86.HasERMS)
|
||||
offsetX86HasSSE2 = unsafe.Offsetof(cpu.X86.HasSSE2)
|
||||
|
||||
@@ -55,6 +55,16 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return runBuiltTestProg(t, exe, name, env...)
|
||||
}
|
||||
|
||||
func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
|
||||
if *flagQuick {
|
||||
t.Skip("-quick")
|
||||
}
|
||||
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
cmd := testenv.CleanCmdEnv(exec.Command(exe, name))
|
||||
cmd.Env = append(cmd.Env, env...)
|
||||
if testing.Short() {
|
||||
@@ -64,7 +74,7 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string {
|
||||
cmd.Stdout = &b
|
||||
cmd.Stderr = &b
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatalf("starting %s %s: %v", binary, name, err)
|
||||
t.Fatalf("starting %s %s: %v", exe, name, err)
|
||||
}
|
||||
|
||||
// If the process doesn't complete within 1 minute,
|
||||
@@ -92,7 +102,7 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string {
|
||||
}()
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
t.Logf("%s %s exit status: %v", binary, name, err)
|
||||
t.Logf("%s %s exit status: %v", exe, name, err)
|
||||
}
|
||||
close(done)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user